[CM] gnuplot (was: Pest Question?)

Torsten Anders t.anders at nici.kun.nl
Fri Jun 14 08:14:47 PDT 2002


Attached is a somewhat evolved version of my interface to gnuplot. I also 
added examples and explaination.

I just found out: there exists a more elaborated lisp->gnuplot as a part of 
CLLIB (which is now part of CLOCC) at http://clocc.sourceforge.net 

Anyway, I assume my attempt is more easy to use, partly because it is so 
tiny. But I also simplify matters: the user does not need to know the details 
of gnuplot (e.g. specifying a 'z' coordinate always results in a 3D plot). 
Although, therefore some gnuplot features can not be used (e.g. certain line 
styles need more parameter and therefore can no be used).

On Thursday 13 June 2002 18:33, you wrote:
> also cm should have a SHELL call defined
> for all lisps, ill check

Sure the definition of SHELL should go somewhere else. I only placed it in 
the file for easy testing (I wrote the core of this years ago, didn't check 
whether CM2 also includes such a function...).

> > BTW, there is a gnuplot port for the Mac + Win too.
> please send me urls to these!

main gnuplot site:
http://www.gnuplot.info/

at sourceforge:
http://sourceforge.net/projects/gnuplot/

port for Macintosh:
http://homepage.mac.com/gnuplot/

port for dos and windows (probably not the only one...):
http://archives.math.utk.edu/software/multi-platform/gnuplot/msdos/

front end (surely not the only one):
http://www.flash.net/~dmishee/xgfe/xgfe.html

emacs mode (!?!):
http://feff.phys.washington.edu/~ravel/software/gnuplot-mode/Welcome.html


-------------- next part --------------
;;;
;;; The function 'plot' is an interface to gnuplot.
;;; Very little error checking (would cost performance, esp. for large lists)
;;;
;;; No performance enhancement, but seems to be OK.
;;; (take 1-1.5 sec for 2000 points to compute, scales linear,
;;; messured on a Celeron 333 running CMU in SuSE 7.3)
;;;
;;; copyright 2002 by Torsten Anders
;;; anders at uni-weimar.de
;;;

#| ; tests / doc

(plot '(0 5 3 4))			; simple plot 
(plot '(1 2 3 4) :style "impulses")	; line style 
(plot '(1 2 3 4) :x '(0.1 0.5 2 2.1))	; x and y given
(plot '(1 2 3 4)			; 3D plot
      :x '(0.1 0.5 2 2.1)
      :z '(0.5 3 2 -1)
      :style "lines")
(plot '((1 5 3) (4 6 0)))		; multiple plots
(plot '((1 5 3) (4 6 0))		; multiple line styles
      :style '("points" "linespoints")) 
(plot '((1 5 3) (4 6 0))		; multiple 3D plots
      :x '((1 2 1.5) (1 7 6))
      :z '((0 1 4) (0 3 2)))
(plot '(0 5 3 4)			; additional settings (out to ps file)
      :set '("title 'test'"
	     "output '/home/to/plot.ps'"
	     "terminal postscript"))

--> all doc following is quoted from doc of gnuplot, see that for (much) more info <--

;; some styles (all styles using 2 (2D) or 3 (3D) parameters) can be used)
"lines" The `lines` style connects adjacent points with straight line segments.
"points" The `points` style displays a small symbol at each point. (:set "pointsize <number>" may be used to change the size of the points)
"linespoints" The `linespoints` style does both `lines` and  `points`, that is, it draws a small symbol at each point and then connects adjacent points with straight line segments. `linespoints` may be abbreviated `lp`. (:set "pointsize <number>" may be used to change the size of the points). 
"impulses"  The `impulses` style displays a vertical line from the x axis (not the graph border), or from the grid base in a 3D plot, to each point.
"dots" The `dots` style plots a tiny dot at each point -- this is useful for scatter plots with many points.
"steps" The `steps` style is only relevant to 2-d plotting.  It connects consecutive points with two line segments: the first from (x1,y1) to (x2,y1) and the
 second from (x2,y1) to (x2,y2).
"boxes" The `boxes` style is only relevant to 2-d plotting.  It draws a box centered about the given x coordinate from the x axis (not the graph border) to the given y coordinate.

;; additional style settings
(plot '(1 2 3 4) :style "linespoints linetype 1 linewidth 1.5 pointtype 3 pointsize 3")
;; same with short hands
(plot '(1 2 3 4) :style "linesp lt 1 lw 1.5 pt 3 ps 3")

;; title for graphs
(plot '(1 2 3 4) :title "test")
(plot '((1 2 3 4) (2 3 4 5)) :title '("test1" "test2"))

;; further settings
The `set` command of gnuplot can be used to sets _lots_ of options.

;; output type
"terminal <val>" sets what kind of output to generate, many formats are supported, e.g.:
x11 X11 Window System 
aifm  Adobe Illustrator 3.0 Format
corel  EPS format for CorelDRAW
fig  FIG 3.1 graphics language: can be edited by xfig graphics editor
mif  Frame maker MIF 3.00 format
postscript  PostScript graphics language
latex  LaTeX picture environment
[further special Tex/LaTeX environments]
[many special printer formats]

;; output file
"output '<filename>'" output file

(plot '(0 5 3 4)		   
      :set '("output '/home/to/plot.ps'"
	     "terminal postscript"))

;; title
"title '<title>'" title of whole plot 

(plot '(0 5 3 4) :set '("title 'test'"))

;; view
"view <rot_x> {,{<rot_z>}{,{<scale>}{,<scale_z>}}}" sets the viewing angle for 3D plots, where <rot_x> and <rot_z> control the rotation angles (in degrees). <rot_x> is bounded to the [0:180] range with a default of 60 degrees, while <rot_z> is bounded to the [0:360] range with a default of 30 degrees. <scale> controls the scaling of the entire `splot`, while <scale_z> scales the z axis only.  Both scales default to 1.0.

(plot '(0 1 5) :x '(0 1 2) :z '(0 5 1) :set '("view 120, 30, 1, 1"))

|#


(defun plot (y &key x z (style "linespoints") set title
	       (data-file "/tmp/gnuplot_daten")
	       (command-file "/tmp/gnuplot_command"))
  "Generates script and data file for gnuplot and calls gnuplot with these files.
Coordinates (i.e. x, y, and z) may be either a list of numbers (for a single plot) or a list of lists (for multiple plots) -- the nesting of all lists should be the same. Specifying z results in a 3D plot. style (sets the lines style) expects either a single string for all or a list of styles with a new value for every plot. set expects a list of strings describing arbitrary additional settings to gnuplot (without the leading 'set').
data-file gives the beginning of the data file names (every plot is written to an own data file). command-file is the name of the command file."
  (let* ((plot-cmd (if z "splot" "plot")) ; 2D or 3D ?
	 (data-files   ; write-string-to-file returns filename
	  (loop for d in (combine-coordinates x y z)
		for i from 1
		collect (write-string-to-file
			 (make-lines d)
			 (format nil "~a~a" data-file i))))
	 (l (length data-files))
	 (titles (if title
		     (listify title l)
		   (loop for i from 1 to l
			 collect (format NIL "data~A" i))))
	 (styles (listify style l))
	 (plots	       ; strings for every plot
	  (mapcar #'(lambda (data title style)
		      (format NIL " '~A' title '~A' with ~A"
			      data title style))
		  data-files titles styles))
	 (settings (format NIL "~{set ~a~%~}" set)))
    (write-string-to-file
     (format nil "~a~a ~a ~{, ~a~} ; pause -1 'Hit return to continue' ~%"
	     settings plot-cmd
	     (first plots) (rest plots)) ; to put commata at correct position
     command-file))
  (shell (format nil "xterm -e gnuplot ~a &" command-file)))


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; utils
;;;

(defun make-lines (data)
  "Writes each sublist of data in an own line"
  (format NIL "~{~{~,7F ~}~%~}" data))

; (make-lines '((0 1) (0.2 2) (1 3) (5 4)))

(defun combine-coordinates (x y z)
  "Combines the lists of x (and possibly y and z) coordinates into a list of the form (((x1 y1 z1) ...) [(<coors for second plot>) ...]). Both x or z may be NIL."
  (when (and z (or (not x) (not y)))
    (error "z can not be given without x or y."))
  (flet ((prepare (x y z) (remove NIL (list x y z))))
  (if (listp (first y))			; checks only first element !!
      (apply #'mapcar #'(lambda (xx &optional yy zz)
			  ;(print (list xx yy zz))
			  (mat-trans (prepare xx yy zz)))
	     (prepare x y z))
    (list (mat-trans (prepare x y z))))))

; (combine-coordinates '(1 2 3) NIL NIL)
; (combine-coordinates '(1 2 3) '(a b c) NIL)
; (combine-coordinates '((1 2 3) (5 6 7)) '((a b c) (x y z)) NIL)

(defun write-string-to-file (string out-file)
  "Writes (or overwrites) string into out-file and returns out-file."
  (with-open-file (out-stream out-file
		   :direction :output
		   :if-exists :supersede)
     (format out-stream string))
  out-file)

;; idea for mat-trans descends to PatchWork (but not code)
(defun mat-trans (in-list)
  "Transforms a list of form ((a1 a2 a3) (b1 b2 b3) (c1 c2 c3) ...) into ((a1 b1 c1 ...) (a2 b2 c2 ...) (a3 b3 c3 ...))."
  (apply #'mapcar #'(lambda (&rest all) all) 
	 in-list))

(defun listify (x l)
  (if (listp x)	
      x
    (make-list l :initial-element x)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; quote (should be defined elsewhere for [hopefully] every platform)

;;; modified version of 'shell' in CM1 by Heinrich Taube
;;; for support of other Lisp platforms see "impl.lisp" 
#+CMU
(defun shell (string &key (wait T) (output T))
  "'Evaluates' string in /bin/sh and outputs result on *standard-output*. Start a child process and does not wait until process is finished. Therefore output of process may be mixed in outputs of other programm..."
  (unwind-protect 
      (let* ((process  (ext:run-program "/bin/sh" (list "-c" string)
					:output output
					:wait wait)))
	(ext:process-close process)
	process)))


More information about the Cmdist mailing list