Updated testsuite with an expected GCL error in to_poly_share
[maxima.git] / share / draw / picture.lisp
blob10ebb0f74620a4c76d2da5bbb8180474d48708f1
1 ;; COPYRIGHT NOTICE
2 ;;
3 ;; Copyright (C) 2007-2016 Mario Rodriguez Riotorto
4 ;;
5 ;; This program is free software; you can redistribute
6 ;; it and/or modify it under the terms of the
7 ;; GNU General Public License as published by
8 ;; the Free Software Foundation; either version 2
9 ;; of the License, or (at your option) any later version.
10 ;;
11 ;; This program is distributed in the hope that it
12 ;; will be useful, but WITHOUT ANY WARRANTY;
13 ;; without even the implied warranty of MERCHANTABILITY
14 ;; or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;; GNU General Public License for more details at
16 ;; http://www.gnu.org/copyleft/gpl.html
18 ;; The picture package. UNSTABLE !!
20 ;; For questions, suggestions, bugs and the like, feel free
21 ;; to contact me at
22 ;; mario @@@ edu DOT xunta DOT es
23 ;; http://tecnostats.net/Maxima/gnuplot
27 (defun cut-and-round (seq)
28 (map 'list
29 #'(lambda (z)
30 (let ((fz (meval `($float ,z))))
31 (cond
32 ((< fz 0) 0)
33 ((> fz 255) 255)
34 (t (round fz)))))
35 seq) )
39 ;; Constructs a levels picture. This object contains four parts:
40 ;; 1) symbol 'level
41 ;; 2) image width
42 ;; 3) image height
43 ;; 4) an integer array with pixel data ranging from 0 to 255.
44 ;; Argument data must contain only numbers ranged from 0 to 255;
45 ;; negative numbers are substituted by 0, and those which are
46 ;; greater than 255 are set to 255. If data is a Maxima list,
47 ;; width and height must be given.
48 (defun $make_level_picture (data &optional (wi nil) (he nil))
49 (let (width height picarray)
50 (cond
51 (($matrixp data)
52 (setf width (length (cdadr data))
53 height (length (cdr data)))
54 (setf picarray (make-array (* height width)
55 :element-type 'integer
56 :initial-contents (cut-and-round (rest ($flatten ($args data)))))) )
57 ((and ($listp data)
58 (integerp wi)
59 (integerp he)
60 (= (* wi he) ($length data)))
61 (setf width wi
62 height he
63 picarray (make-array (* wi he)
64 :element-type 'integer
65 :initial-contents (cut-and-round (rest data)))))
67 (merror "Argument should be a matrix or a list of numbers")))
68 (list '(picture simp) '$level width height picarray) ))
72 ;; Returns true if the argument is a well formed image,
73 ;; and false otherwise
74 (defun $picturep (im)
75 (cond ((atom im)
76 nil)
77 ((and (= (length im) 5)
78 (equal (car im) '(picture simp)))
81 (and (equal (length im) 5)
82 (equal (car im) '(picture ))
83 (or (member (cadr im) '($level $rgb)))
84 (arrayp (nth 4 im))
85 (cond ((equal (nth 1 im) '$level)
86 (= (array-dimension (nth 4 im) 0)
87 (* (nth 2 im) (nth 3 im))))
88 (t ; rgb image
89 (= (array-dimension (nth 4 im) 0)
90 (* 3 (nth 2 im) (nth 3 im)))))
91 (every #'(lambda (z) (and (integerp z) (>= z 0) (<= z 255))) (nth 4 im)) ))))
95 ;; Returns true in case of equal pictures, and false otherwise.
96 (defun $picture_equalp (pic1 pic2)
97 (if (and ($picturep pic1) ($picturep pic2))
98 (equalp pic1 pic2)
99 (merror "Two picture objects are required")))
103 ;; Constructs a coloured rgb picture. This object contains four parts:
104 ;; 1) symbol 'rgb
105 ;; 2) image width
106 ;; 3) image height
107 ;; 4) an integer array of length 3*width*height with pixel data ranging
108 ;; from 0 to 255. Each pixel is represented by three consecutive numbers
109 ;; (red, green, blue). Arguments must contain the three channels in
110 ;; level_picture.
111 (defun $make_rgb_picture (redlevel greenlevel bluelevel)
112 (when (not (and ($picturep redlevel)
113 (equal (cadr redlevel) '$level)
114 ($picturep greenlevel)
115 (equal (cadr greenlevel) '$level)
116 ($picturep bluelevel)
117 (equal (cadr bluelevel) '$level)))
118 (merror "Color channel is not a levels picture object"))
119 (when (not (and (= (caddr redlevel) (caddr greenlevel) (caddr bluelevel))
120 (= (cadddr redlevel) (cadddr greenlevel) (cadddr bluelevel)) ))
121 (merror "Color channels are not of equal dimensions"))
122 (let (width height leng picarray i3)
123 (setf width (caddr redlevel)
124 height (cadddr redlevel))
125 (setf leng (* width height))
126 (setf picarray (make-array (* 3 leng) :element-type 'integer))
127 (loop for i from 0 below leng do
128 (setf i3 (* 3 i))
129 (setf (aref picarray i3) (aref (nth 4 redlevel) i))
130 (setf (aref picarray (incf i3)) (aref (nth 4 greenlevel) i))
131 (setf (aref picarray (incf i3)) (aref (nth 4 bluelevel) i)))
132 (list '(picture simp) '$rgb width height picarray) ))
136 ;; Extracts color channel ('red, 'green or 'blue) from a coloured picture.
137 ;; Returns a levels picture.
138 (defun $take_channel (pic chn)
139 (when (not (and ($picturep pic)
140 (equal (cadr pic) '$rgb)))
141 (merror "Argument is not a coloured picture"))
142 (when (not (member chn '($red $green $blue)))
143 (merror "Incorrect colour channel"))
144 (let* ((width (caddr pic))
145 (height (cadddr pic))
146 (dim (* width height))
147 (img (make-array dim :element-type 'integer))
148 idx)
149 (setf idx
150 (case chn
151 ($red 0)
152 ($green 1)
153 ($blue 2)))
154 (loop for i from 0 below dim do
155 (setf (aref img i) (aref (nth 4 pic) (+ (* 3 i) idx))))
156 (list '(picture simp) '$level width height img) ))
160 ;; Returns the negative of a (level or rgb) picture
161 (defun $negative_picture (pic)
162 (if (not ($picturep pic))
163 (merror "Argument is not a picture"))
164 (let ((dim (array-dimension (nth 4 pic) 0))
165 (arr (make-array (array-dimension (nth 4 pic) 0) :element-type 'integer)))
166 (loop for i from 0 below dim do
167 (setf (aref arr i) (- 255 (aref (nth 4 pic) i))))
168 (list '(picture simp)
169 (nth 1 pic)
170 (nth 2 pic)
171 (nth 3 pic)
172 arr)))
176 ;; Transforms an rgb picture into a level one by
177 ;; averaging the red, green and blue values.
178 (defun $rgb2level (pic)
179 (if (or (not ($picturep pic))
180 (not (equal (nth 1 pic) '$rgb)))
181 (merror "Argument is not an rgb picture"))
182 (let* ((dim (* (nth 2 pic) (nth 3 pic)))
183 (arr (make-array dim :element-type 'integer))
184 (k -1))
185 (loop for i from 0 below dim do
186 (setf (aref arr i) (round (/ (+ (aref (nth 4 pic) (incf k))
187 (aref (nth 4 pic) (incf k))
188 (aref (nth 4 pic) (incf k)))
189 3))))
190 (list '(picture simp)
191 '$level
192 (nth 2 pic)
193 (nth 3 pic)
194 arr)))
198 ;; Returns pixel from picture. Coordinates x and y range from 0 to
199 ;; (width-1) and (height-1), respectively. We are working
200 ;; with arrays, not with lists.
201 (defun $get_pixel (pic x y)
202 (when (not ($picturep pic))
203 (merror "Argument is not a well formed picture"))
204 (when (not (and (integerp x) (integerp y)))
205 (merror "Pixel coordinates must be positive integers"))
206 (when (not (and (> x -1)
207 (< x (nth 2 pic))
208 (> y -1)
209 (< y (nth 3 pic))))
210 (merror "Pixel coordinates out of range"))
211 (case (nth 1 pic)
212 ($level (aref (nth 4 pic) (+ x (* y (nth 2 pic)))))
213 ($rgb (let ((pos (* 3 (+ x (* y (nth 2 pic))))))
214 (list
215 '(mlist simp)
216 (aref (nth 4 pic) pos)
217 (aref (nth 4 pic) (incf pos))
218 (aref (nth 4 pic) (incf pos)))))))
227 ;;; XPM I M A G E F O R M A T S U P P O R T
229 ;; The following functions have been taken from
230 ;; http://common-lisp.net/project/gamelib/ (MIT license)
231 ;; Changes have been made to fit the Maxima environment.
234 (defvar *xpm-readtable* nil)
237 (defun init-readtable ()
238 (unless *xpm-readtable*
239 (setf *xpm-readtable* (copy-readtable))
240 (set-syntax-from-char #\, #\Space *xpm-readtable*)))
243 (defun skip-whitespace (f)
244 (loop for c = (read-char f)
245 while (member c '(#\space #\tab) :test #'char=)
246 finally (unread-char c f)
250 (defun read-colour (f)
251 (let ((ctype (read-char f)))
252 (case ctype
254 ; color in hexadecimal format
255 (#\#
256 (let ((*read-base* 16))
257 (read f)))
259 ; color name:
260 ; 0. read the rest of the name and append the first letter
261 ; 1. get the hexadecimal code from *color-table* defined in grcommon.lisp
262 ; 2. remove # and transform the code to an integer in base 10
263 (otherwise
264 (parse-integer
265 (subseq (gethash (atom-to-downcased-string (format nil "~a~a" ctype (read f))) *color-table*) 1)
266 :radix 16)) )))
269 (defun read-charspec (f cnt)
270 (format nil "~{~c~}"
271 (loop for n from 0 below cnt
272 collect (read-char f))))
275 (defun read-cspec (str cnt hash)
276 (with-input-from-string (cs str)
277 (let ((chars (read-charspec cs cnt)))
278 (skip-whitespace cs)
279 (let ((c (read-char cs)))
280 (if (char= c #\c)
281 (let ((col (progn
282 (skip-whitespace cs)
283 (read-colour cs))))
284 (setf (gethash chars hash) col))
285 (merror "Unknown colourspec"))))))
288 (defun $read_xpm (mfspec)
289 (init-readtable)
290 (let ((*readtable* *xpm-readtable*)
291 (fspec (string-trim "\"" (coerce (mstring mfspec) 'string))) )
292 (with-open-file (image fspec :direction :input)
293 (read-line image) ; Skip initial comment
294 (read-line image) ; Skip C code
295 (let ((colspec (read image))
296 width
297 height
298 (chartab (make-hash-table :test #'equal))
299 img)
300 (with-input-from-string (cspec colspec)
301 (setf width (read cspec))
302 (setf height (read cspec))
303 (let ((colours (read cspec))
304 (cpp (read cspec))
305 rgb (counter -1))
306 (loop for cix from 0 below colours
307 for c = (read image)
308 do (read-cspec c cpp chartab))
309 (setf img (make-array (* 3 width height) :element-type 'integer))
310 (loop for y from 0 below height
311 for line = (read image)
312 do (progn
313 (with-input-from-string (data line)
314 (loop for x from 0 below width
315 for cs = (read-charspec data cpp) do
316 (setf rgb (gethash cs chartab))
317 (setf (aref img (incf counter)) (/ (logand rgb 16711680) 65536))
318 (setf (aref img (incf counter)) (/ (logand rgb 65280) 256))
319 (setf (aref img (incf counter)) (logand rgb 255))))) )
320 (list '(picture simp) '$rgb width height img)))))))