eintr: X Axis Tic Marks

 
 C.3.1 X Axis Tic Marks
 ----------------------
 
 The first function should print the X axis tic marks.  We must specify
 the tic marks themselves and their spacing:
 
      (defvar X-axis-label-spacing
        (if (boundp 'graph-blank)
            (* 5 (length graph-blank)) 5)
        "Number of units from one X axis label to next.")
 
 (Note that the value of ‘graph-blank’ is set by another ‘defvar’.  The
 ‘boundp’ predicate checks whether it has already been set; ‘boundp’
 returns ‘nil’ if it has not.  If ‘graph-blank’ were unbound and we did
 not use this conditional construction, in a recent GNU Emacs, we would
 enter the debugger and see an error message saying
 ‘Debugger entered--Lisp error: (void-variable graph-blank)’.)
 
    Here is the ‘defvar’ for ‘X-axis-tic-symbol’:
 
      (defvar X-axis-tic-symbol "|"
        "String to insert to point to a column in X axis.")
 
    The goal is to make a line that looks like this:
 
             |   |    |    |
 
    The first tic is indented so that it is under the first column, which
 is indented to provide space for the Y axis labels.
 
    A tic element consists of the blank spaces that stretch from one tic
 to the next plus a tic symbol.  The number of blanks is determined by
 the width of the tic symbol and the ‘X-axis-label-spacing’.
 
    The code looks like this:
 
      ;;; X-axis-tic-element
      ...
      (concat
       (make-string
        ;; Make a string of blanks.
        (-  (* symbol-width X-axis-label-spacing)
            (length X-axis-tic-symbol))
        ? )
       ;; Concatenate blanks with tic symbol.
       X-axis-tic-symbol)
      ...
 
    Next, we determine how many blanks are needed to indent the first tic
 mark to the first column of the graph.  This uses the value of
 ‘full-Y-label-width’ passed it by the ‘print-graph’ function.
 
    The code to make ‘X-axis-leading-spaces’ looks like this:
 
      ;; X-axis-leading-spaces
      ...
      (make-string full-Y-label-width ? )
      ...
 
    We also need to determine the length of the horizontal axis, which is
 the length of the numbers list, and the number of ticks in the
 horizontal axis:
 
      ;; X-length
      ...
      (length numbers-list)
 
      ;; tic-width
      ...
      (* symbol-width X-axis-label-spacing)
 
      ;; number-of-X-ticks
      (if (zerop (% (X-length tic-width)))
          (/ (X-length tic-width))
        (1+ (/ (X-length tic-width))))
 
    All this leads us directly to the function for printing the X axis
 tic line:
 
      (defun print-X-axis-tic-line
        (number-of-X-tics X-axis-leading-spaces X-axis-tic-element)
        "Print ticks for X axis."
          (insert X-axis-leading-spaces)
          (insert X-axis-tic-symbol)  ; Under first column.
          ;; Insert second tic in the right spot.
          (insert (concat
                   (make-string
                    (-  (* symbol-width X-axis-label-spacing)
                        ;; Insert white space up to second tic symbol.
                        (* 2 (length X-axis-tic-symbol)))
                    ? )
                   X-axis-tic-symbol))
          ;; Insert remaining ticks.
          (while (> number-of-X-tics 1)
            (insert X-axis-tic-element)
            (setq number-of-X-tics (1- number-of-X-tics))))
 
    The line of numbers is equally straightforward:
 
    First, we create a numbered element with blank spaces before each
 number:
 
      (defun X-axis-element (number)
        "Construct a numbered X axis element."
        (let ((leading-spaces
               (-  (* symbol-width X-axis-label-spacing)
                   (length (number-to-string number)))))
          (concat (make-string leading-spaces ? )
                  (number-to-string number))))
 
    Next, we create the function to print the numbered line, starting
 with the number 1 under the first column:
 
      (defun print-X-axis-numbered-line
        (number-of-X-tics X-axis-leading-spaces)
        "Print line of X-axis numbers"
        (let ((number X-axis-label-spacing))
          (insert X-axis-leading-spaces)
          (insert "1")
          (insert (concat
                   (make-string
                    ;; Insert white space up to next number.
                    (-  (* symbol-width X-axis-label-spacing) 2)
                    ? )
                   (number-to-string number)))
          ;; Insert remaining numbers.
          (setq number (+ number X-axis-label-spacing))
          (while (> number-of-X-tics 1)
            (insert (X-axis-element number))
            (setq number (+ number X-axis-label-spacing))
            (setq number-of-X-tics (1- number-of-X-tics)))))
 
    Finally, we need to write the ‘print-X-axis’ that uses
 ‘print-X-axis-tic-line’ and ‘print-X-axis-numbered-line’.
 
    The function must determine the local values of the variables used by
 both ‘print-X-axis-tic-line’ and ‘print-X-axis-numbered-line’, and then
 it must call them.  Also, it must print the carriage return that
 separates the two lines.
 
    The function consists of a varlist that specifies five local
 variables, and calls to each of the two line printing functions:
 
      (defun print-X-axis (numbers-list)
        "Print X axis labels to length of NUMBERS-LIST."
        (let* ((leading-spaces
                (make-string full-Y-label-width ? ))
             ;; symbol-width is provided by graph-body-print
             (tic-width (* symbol-width X-axis-label-spacing))
             (X-length (length numbers-list))
             (X-tic
              (concat
               (make-string
                ;; Make a string of blanks.
                (-  (* symbol-width X-axis-label-spacing)
                    (length X-axis-tic-symbol))
                ? )
               ;; Concatenate blanks with tic symbol.
               X-axis-tic-symbol))
             (tic-number
              (if (zerop (% X-length tic-width))
                  (/ X-length tic-width)
                (1+ (/ X-length tic-width)))))
          (print-X-axis-tic-line tic-number leading-spaces X-tic)
          (insert "\n")
          (print-X-axis-numbered-line tic-number leading-spaces)))
 
    You can test ‘print-X-axis’:
 
   1. Install ‘X-axis-tic-symbol’, ‘X-axis-label-spacing’,
      ‘print-X-axis-tic-line’, as well as ‘X-axis-element’,
      ‘print-X-axis-numbered-line’, and ‘print-X-axis’.
 
   2. Copy the following expression:
 
           (progn
            (let ((full-Y-label-width 5)
                  (symbol-width 1))
              (print-X-axis
               '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16))))
 
   3. Switch to the ‘*scratch*’ buffer and place the cursor where you
      want the axis labels to start.
 
   4. Type ‘M-:’ (‘eval-expression’).
 
   5. Yank the test expression into the minibuffer with ‘C-y’ (‘yank)’.
 
   6. Press <RET> to evaluate the expression.
 
    Emacs will print the horizontal axis like this:
 
           |   |    |    |    |
           1   5   10   15   20