Info Node: (emacs-lisp-intro.info)X Axis Tic Marks
(emacs-lisp-intro.info)X Axis Tic Marks
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 GNU Emacs 21, 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 tics in the horizontal
axis:
;; X-length
...
(length numbers-list)
;; tic-width
...
(* symbol-width X-axis-label-spacing)
;; number-of-X-tics
(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 tics 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 tics.
(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