X Axis Tic Marks (Programming in Emacs Lisp)

Previous: , Up: The print-X-axis Function [Contents][Index]


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, 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

Previous: Similarities and differences, Up: The print-X-axis Function [Contents][Index]

AltStyle によって変換されたページ (->オリジナル) /