File: plot.lsp

package info (click to toggle)
newlisp 10.7.5-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,292 kB
  • sloc: ansic: 33,280; lisp: 4,181; sh: 609; makefile: 215
file content (463 lines) | stat: -rw-r--r-- 14,555 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
;; @module plot.lsp
;; @description Routines for creating data plots.
;; @version 1.1 initial release
;; @version 1.2 allow different length data vectors
;; @version 2.0 added plotXY for plotting data points -> (x, y)
;; @version 2.1 added plot:reset for resetting optional labels and settings
;; @version 2.2 option plot:data-min, plot:data-max did not work
;; @author Lutz Mueller, September 2011, April 2012
;;
;; In its initial release the <tt>plot.lsp</tt> module can draw
;; simple line plots from one to five data sets. In its simplest
;; form only the <tt>plot</tt> command is necessary. A group of
;; parameters can be set to further customize the plot. Plots can
;; be save to a graphics file.
;;
;; Load the module using:
;; <pre>(load "plot.lsp")</pre>
;; This module runs the newLISP Java based Guiserver. The file
;; <tt>guiserver.jar</tt> is installed by one of the newLISP binary
;; installers in a standard location. Executing <tt>(test-plot)</tt>
;; will generate a test plots which can be seen here:
;; @link http://newlisp.org/code/example-line-plot.png line-plot
;; and 
;; @link http://newlisp.org/code/example-xy-plot.png XY-plot  .
;; 
;; Several variables can be set optionally to change the size,
;; and positioning of the plot area in the image.
;; Other variables can be set to control the partioning of the grid,
;; labeling of the horizontal axis and legend.
;; The following list shows all parameters with their default
;; values:
;; <pre>
;; ; mandatory and preset with default values
;; plot:wwidth        640 ; window width in pixels
;; plot:wheight       420 ; window height in pixels
;; plot:origin-x       64 ; top left x of plot area
;; plot:origin-y       64 ; top left y of plot area
;; plot:pwidth        520 ; width of plot area
;; plot:pheight       280 ; height of plot area<br/>
;; ; optional
;; plot:data-min      nil ; minimum value on the Y axis 
;; plot:data-max      nil ; maximum value on the Y axis 
;; plot:title         nil ; top centered main title
;; plot:sub-title     nil ; sub title under main title
;; plot:unit-y        nil ; a unit label centered left to the Y axis
;; plot:labels        nil ; a list of string labels for vertical grid
;; plot:legend        nil ; a list of string labels<br/>
;; ; optional for plotXY only
;; plot:data-x-min    nil ; minimum value on the X axis 
;; plot:data-x-max    nil ; maximum value on the X axis 
;; plot:data-y-min    nil ; minimum value on the Y axis 
;; plot:data-y-max    nil ; maximum value on the Y axis 
;; plot:unit-x        nil ; a unit label centered under the X axis
;; </pre>
;;
;; Only the the first group of variables is mandatory and preset to
;; the values shown above. Options in the second group will be either
;; suppressed or set automatically.

;; @syntax (plot <list-data> [<list-data> . . . ])
;; @param <list-data> One to five lists of data points.
;; <br>
;; The function draws one or more horizontal data lines from up
;; to five data sets in <list-data>. Colors are chosen in the sequence
;; red, green, blue, yellow and purple.
;; <br><br>
;; The following example doesn't set any extra options and plots to random
;; data sets. Scale and labels on the vertical and horizontal axis
;; will be set automatically. No title, sub-title and legends will
;; be printed. 
;;
;; @example
;; (plot (random 10 5 50) (normal 10 0.5 50))

;; The following example sets several options then plots and 
;; exports the image to a PNG graphics file:
;;
;; @example
;; (set 'plot:title "The Example Plot")
;; (set 'plot:sub-title "several random data sets")
;; (set 'plot:labels '("1" "" "20" "" "40" "" "60" "" "80" "" "100"))
;; (set 'plot:legend  '("first" "second" "third" "fourth"))
;;
;; ; display plot image
;; (plot (random 10 5 100) 
;;       (normal 10 0.5 100)
;;       (normal 8 2 100) 
;;       (normal 5 1 100) )
;;
;; ; save the displayed image to a file
;; (plot:export "example-plot.png")
;; 

;; @syntax (plot:XY <list-data-X> <list-data-Y>)
;; @param <list-data-X> List of X coordinates of data points.
;; @param <list-data-X> List of Y coordinates of data points.
;; <br>
;; Draws data points of data in <list-data-X> and <list-data-Y>.
;;

;; @syntax (plot:export <str-file-name>)
;; @param <str-file-name> The name of the file.
;;
;; Exports the current plot shown to a file in PNG format.
;;
;; @example
;; (plot:export "example-plot.png")

;; See the example plot
;; @link http://newlisp.org/code/example-plot.png here .

;; @syntax (plot:reset)
;; <br>
;; Resets all optional labels and sizes to 'nil'.

(define (plot:export file-name)
    (gs:export file-name))


(set-locale "C")
(load (append (env "NEWLISPDIR") "/guiserver.lsp")) 

(context 'plot)

(set 'wwidth 640)
(set 'wheight 420)

(set 'origin-x 64)
(set 'origin-y 64)

(set 'pwidth 520)
(set 'pheight 280)

(set 'title nil)
(set 'sub-title nil)

(set 'unit-x nil)
(set 'unit-y nil)
(set 'data-min nil)
(set 'data-max nil)
(set 'labels nil)
(set 'legend nil)

; colors
(set 'background-color '(0.3 0.3 0.3))
(set 'text-color '(0.9 0.9 0.9))
(set 'grid-color '(0.5 0.5 0.5))
(set 'frame-color '(0.7 0.7 0.7))

(set 'line-color (array 5 '(
    (1.0 0.35 0.35) ; 0 red
    (0.0 0.9 0.0)   ; 1 green
    (0.6 0.6 1.0)   ; 2 blue
    (0.9 0.7 0.0)   ; 3 orange
    (0.9 0.3 0.9)   ; 4 purple
)))


; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; auxiliary functions ;;;;;;;;;;;;;;;;;;;;;;

; round to 3 digits precision
; calculate rounding digits from (sub max min)
; then apply to all numbers
(define (rnd num)
     (round num (- (log num 10) 2)))

; setup the main window and plot area
(define (setup-window)
    (gs:init) 
    ;; describe the GUI
    (gs:frame 'PlotWindow 100 100 wwidth wheight "Plot")
    (gs:set-resizable 'PlotWindow nil)
    (gs:canvas 'PlotCanvas)
    (gs:set-background 'PlotCanvas background-color)
    (gs:add-to 'PlotWindow 'PlotCanvas)
    (gs:set-translation  origin-x origin-y)

    (gs:set-visible 'PlotWindow true)
    ; draw title
    (when title 
        (gs:set-font 'PlotCanvas "Lucida Sans Typewriter Regular" 18 "bold")        
        (gs:draw-text 'title title 
            (/ (- pwidth (* (length title) 11)) 2) -34 text-color)
    )
    ; draw sub title
    (when sub-title
        (gs:set-font 'PlotCanvas "Lucida Sans Typewriter Regular" 12 "plain")       
        (gs:draw-text 'title sub-title 
            (/ (- pwidth (* (length sub-title) 7)) 2) -18 text-color)
    )
)

(define (draw-grid L)
    ; draw horizontal grid lines
    (gs:set-stroke 1.0)
    (for (i 1 4)
        (let (y (/ (* i pheight) 5) )
            (gs:draw-line 'Grid 0 y pwidth y grid-color))
    )

    (if labels
        (set 'L (- (length labels) 1))
        (unless L 
            (set 'L (if (< N 50) (- N 1) 10)))
    )

    ; draw vertical grid lines
    (gs:set-stroke 1.0)
    (for (i 1 (- L 1))
        (let (x (/ (* i pwidth) L) )
             (gs:draw-line 'Grid x 0 x pheight grid-color)
        )
    )

)

(define (draw-grid-frame)
    (gs:set-stroke 1.0)
    (gs:draw-rect 'GridFrame 0 0 pwidth pheight)
    (gs:color-tag 'GridFrame frame-color)
    (gs:set-stroke 5.0)
    (gs:draw-rect 'GridMask -3 -3 (+ pwidth 6) (+ pheight 6))
    (gs:color-tag 'GridMask background-color)
)


(define (draw-labels) ; only used for line plot
    ; draw labels for x-range under vertical grid lines
    (gs:set-font 'PlotCanvas "Monospaced" 12 "plain")
    (if labels
      (let (step (div pwidth (- (length labels) 1))
          cnt 0 )
         (dolist (t labels)
           (unless (empty? t)
             (gs:draw-text 'Grid t (int (sub (mul step cnt) (mul (length t) 3.5)))  
                (+ pheight 14) text-color))
           (inc cnt))
      )
      ; else if no labels
      (begin
        (gs:draw-text 'Grid (format "%5d" 1) -32 (+ pheight 15) text-color)
        (gs:draw-text 'Grid (format "%5d" N) (- pwidth 26) (+ pheight 15) text-color)
      )
    )
)

(define (draw-y-numbers range, y-format step)
    ; draw y labels as of y-range left to horizontal grid lines
    (gs:set-font 'PlotCanvas "Monospaced" 12 "plain")
    (set 'y-format (if 
            (< range 1)   "%8.3f"
            (< range 10)  "%8.2f"  
            (< range 100) "%8.1f"
            true            "%8.0f"))

    (set 'step (rnd (div range 5)))
    (dotimes (i 6)
        (let (y (- pheight -4 (* i (/ pheight 5))))
            (gs:draw-text 'Grid 
            (format y-format (add ymin (mul i step))) 
                   -64 y text-color))
    )
 
    ; draw unit label to left center of grid
    (when unit-y
        (gs:draw-text 'Grid unit-y -50 (+ (/ pheight 2) 4) text-color))
)

(define (draw-x-numbers n range xmin, x-format x-offset L step)
    ; draw numbers for x-range under vertical grid lines
    (gs:set-font 'PlotCanvas "Monospaced" 12 "plain")

    (set 'x-format (if 
            (< range 1)   "%8.3f"
            (< range 10)  "%8.2f"  
            (< range 100) "%8.1f"
            true            "%8.0f"))

    (set 'x-offset (if 
            (< range 1)   20
            (< range 10)  14  
            (< range 100)  8 
            true             2 ))


    (set 'L 10)
    (set 'step (rnd (div range L)))
    
    (for (i 0 (- L 1))
        (let ( x (+ (* i  (/ pwidth L)) x-offset) )
             (gs:draw-text 'Grid  
                (format x-format (add xmin (mul (+ i 1) step)))
                x (+ pheight 15) text-color)
        )
    )
    ; unit label
    (when unit-x
        (gs:draw-text 'Grid unit-x (- (/ pwidth 2) 30) (+ pheight 30) text-color))
)

; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; user functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; take one or more data vectors (lists) as arguments
(define (plot:plot)
    (setup-window)
    ; set data min, max and range over all plot vectors
    (set 'M (length (args)))
    (set 'N 0)
    (dotimes (m M)
        (set 'data (args m) )
        (set 'N (max N (length data)))
        (unless (and data-min data-max)
            (if (= 0 m)
                (begin
                    (set 'ymin (apply min data))
                    (set 'ymax (apply max data)) )
                (begin
                    (set 'ymin (min ymin (apply min data)))
                    (set 'ymax (max ymax (apply max data))))
            )
        )
    )

    ; overwrite ymin and ymax if data-min/mx are defined
    (when (and data-min data-max)
        (set 'ymin data-min)
        (set 'ymax data-max))

    (set 'y-range (sub ymax ymin))
    (draw-grid)
    (draw-labels)
    (draw-y-numbers y-range)

    ; for each data vector draw data
    (gs:set-stroke 1.5)
    (dotimes (m M)
        (set 'data (args m))
        ; draw data
        (set 'last-x nil 'last-y nil)
        (dotimes (i (length data))
            (let (x (div (mul i pwidth) (- N 1)) 
                  y (div (mul (sub (pop data) ymin) pheight) y-range))
                (when last-x 
                    (gs:draw-line 'seg last-x (- pheight last-y) 
                        (int x) (int (- pheight y)) (line-color m)))
                (set 'last-x (int x) 'last-y (int y))
            )
        )
    )

    ; draw legend depending on the number of plot lines
    ; style below 
    (dotimes (m M)
        (let (x (* m (/ pwidth 5)))
            (when (and (list? legend) (= M (length  legend))) 
                (gs:draw-line 'legend x (+ pheight 30) (+ x 25) (+ pheight 30) (line-color m))
                (gs:draw-text 'legend (legend m) (+ x 1 30) (+ pheight 33) text-color)
            )
        )
    )

    (draw-grid-frame)
    (gs:set-visible 'PlotWindow true)
)

(define (plot:XY data-x data-y (idx-color 3) , N, xmin, xmax, ymin, ymax)
    ; set data min, max and range over the two data vectors
    (set 'N (length data-x))
    (when (!= N (length data-y))
        (throw-error "X vecor and Y vector must be of equal length"))

    (setup-window)

    (set 'xmin (apply min data-x))
    (set 'xmax (apply max data-x)) 
    (set 'ymin (apply min data-y))
    (set 'ymax (apply max data-y)) 

    (when (and data-x-min data-x-max)
        (set 'xmin data-x-min)
        (set 'xmax data-x-max))
    
    (when (and data-y-min data-y-max)
        (set 'ymin data-y-min)
        (set 'ymax data-y-max))

    (set 'x-range (sub xmax xmin))
    (set 'y-range (sub ymax ymin))

    (draw-grid 10)
    (draw-y-numbers y-range)
    (draw-x-numbers N x-range xmin)

    ; draw XY data points in (line-color idx-color)
    (gs:set-stroke 1.5)
    (dotimes (i N)
        (let (x (div (mul (sub (pop data-x) xmin) pwidth) x-range)
              y (div (mul (sub (pop data-y) ymin) pheight) y-range))
            (if (and (< x pwidth) (< y pheight) (>= x 0) (>= y 0))
                (gs:draw-circle 'xy-points (int x) (int (- pheight y)) 3 (line-color idx-color)))
        )
    )

    (draw-grid-frame)
    (gs:set-visible 'PlotWindow true)
)

(define (plot:reset)
    (setq
        data-min      nil ; minimum value on the Y axis 
        data-max      nil ; maximum value on the Y axis 
        title         nil ; top centered main title
        sub-title     nil ; sub title under main title<br>
        unit-y        nil ; a unit label centered left to the Y axis
        labels        nil ; a list of string labels for vertical grid
        legend        nil ; a list of string labels
        data-x-min    nil ; minimum value on the X axis 
        data-x-max    nil ; maximum value on the X axis 
        data-y-min    nil ; minimum value on the Y axis 
        data-y-max    nil ; maximum value on the Y axis 
        unit-x        nil ; a unit label centered under the X axis
    )
)
;; </pre>

(context MAIN)

; test
(define (test-plot)
    ; optional title, sub-title, labels and legend, data min/max for Y
    (set 'plot:title "The Example Line Plot")
    (set 'plot:sub-title "several random data sets")
    (set 'plot:labels '("1" "" "20" "" "40" "" "60" "" "80" "" "100"))
    (set 'plot:legend  '("first" "second" "third" "fourth"))
    ;(set 'plot:data-min 0 'plot:data-max 20)

    (set 'plot:unit-y "unit y")
    ; display plot - only required statement
    (plot (random 10 5 100) 
          (normal 10 0.5 100)
          (normal 8 2 100) 
          (normal 5 1 100) )

    ; optionally save the display to a file
    (plot:export "example-line-plot.png")

    ; test XY plot
    (plot:reset)
    (set 'plot:title "The Example XY Plot")
    (set 'plot:sub-title "1000 normal distributed data points")
    (set 'plot:unit-x "unit x")

    (plot:XY (normal 10 3 1000) (normal 0 1 1000) )
    (plot:export "example-xy-plot.png")

)


;(plot (random 10 5 50) (normal 10 0.5 50))

;(test-plot)
;(exit)