1 ;;;; cl-vectors -- Rasterizer and paths manipulation library
2 ;;;; Copyright (C) 2007 Frédéric Jolliton <frederic@jolliton.com>
4 ;;;; This library is free software; you can redistribute it and/or
5 ;;;; modify it under the terms of the Lisp Lesser GNU Public License
6 ;;;; (http://opensource.franz.com/preamble.html), known as the LLGPL.
8 ;;;; This library is distributed in the hope that it will be useful, but
9 ;;;; WITHOUT ANY WARRANTY; without even the implied warranty of
10 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Lisp
11 ;;;; Lesser GNU Public License for more details.
13 (defpackage #:net.tuxee.vectors-doc
14 (:use
#:cl
#:aa
#:paths
)
17 (in-package #:net.tuxee.vectors-doc
)
19 (defvar *target
* "/home/fred/Devel/cl-vectors/doc-pictures/")
21 ;;;--[ Path annotation ]-----------------------------------------------------
23 (defun path-map-line (path function
)
24 "Iterate over all the line on the contour of the path."
25 (loop with iterator
= (path-iterator-segmented path
)
26 for previous-knot
= nil then knot
27 for
(interpolation knot end-p
) = (multiple-value-list (path-iterator-next iterator
))
30 do
(funcall function previous-knot knot
)
33 (funcall function knot
(nth-value 1 (path-iterator-next iterator
))))))
35 (defun rasterize-paths (paths image
&optional
(color #(0 0 0)) (opacity 1.0) (scale 1.0))
36 (let ((state (make-state)))
37 (flet ((do-line (p1 p2
)
39 (* scale
(point-x p1
)) (* scale
(point-y p1
))
40 (* scale
(point-x p2
)) (* scale
(point-y p2
)))))
41 (loop for path in
(flatten paths
)
42 do
(path-map-line path
#'do-line
)))
43 (cells-sweep state
(aa-misc:image-put-pixel image color opacity
))))
46 (if (not (listp path
))
48 (loop for item in path nconc
(flatten item
))))
50 (defun paths-bounding-box (paths &optional
(scale 1.0))
51 (let ((state (make-state))
54 (flet ((do-line (p1 p2
)
56 (* scale
(point-x p1
)) (* scale
(point-y p1
))
57 (* scale
(point-x p2
)) (* scale
(point-y p2
))))
59 (declare (ignore alpha
))
63 ((< x min-x
) (setf min-x x
))
64 ((> x max-x
) (setf max-x x
)))
66 ((< y min-y
) (setf min-y y
))
67 ((> y max-y
) (setf max-y y
))))
73 (loop for path in
(flatten paths
)
74 do
(path-map-line path
#'do-line
))
75 (cells-sweep state
#'do-cell
(lambda (&rest args
) (declare (ignore args
)))))
77 (values min-x min-y
(1+ max-x
) (1+ max-y
)))))
79 (defun show-paths (paths &key
(color #(0 0 0)) (opacity 1.0) (width 800) (height 600)
80 (background #(255 255 255)))
81 (let ((image (aa-misc:make-image width height background
)))
82 (rasterize-paths paths image color opacity
)
83 (aa-misc:show-image image
)))
85 (defun create-graph (graph &key subgraphs
(width 800) (height 600) (auto-size t
) (scale 1.0)
86 (background #(255 255 255)))
90 (flet ((update-limits (graph)
91 (loop for
(color . paths
) in graph
92 do
(multiple-value-bind (x1 y1 x2 y2
) (paths-bounding-box paths scale
)
94 (when (or (null min-x
) (< x1 min-x
)) (setf min-x x1
))
95 (when (or (null max-x
) (> x2 max-x
)) (setf max-x x2
))
96 (when (or (null min-y
) (< y1 min-y
)) (setf min-y y1
))
97 (when (or (null max-y
) (> y2 max-y
)) (setf max-y y2
)))))))
99 (update-limits graph
))
101 (mapcar #'update-limits subgraphs
)))
104 (setf width
(max 1 (+ (max 0 min-x
) max-x
))
105 height
(max 1 (+ (max 0 min-y
) max-y
))))
107 (setf width
(max 1 max-x
)
108 height
(max 1 max-y
))))))
109 (let ((image (aa-misc:make-image width height background
)))
111 (loop for
(color . paths
) in graph
112 do
(rasterize-paths paths image color
1.0 scale
)))
113 (dolist (subgraph subgraphs
)
114 (loop for
(color . paths
) in subgraph
115 do
(rasterize-paths paths image color
0.3 scale
)))
118 (defun generate-annotated-path (path &rest args
&key reference
&allow-other-keys
)
119 (apply #'create-graph
(when path
(path-annotated path
))
120 :subgraphs
(mapcar #'path-annotated
(if (listp reference
) reference
(list reference
)))
124 (defun show-annotated-path (&rest args
)
125 (aa-misc:show-image
(apply #'generate-annotated-path args
)))
127 (defun show-graph (graph)
128 (aa-misc:show-image
(create-graph graph
)))
130 (defun save-image* (filename image
)
131 (aa-misc:save-image
(merge-pathnames filename
*target
*) image
:pnm
))
133 (defun save-graph (filename graph
)
134 (aa-misc:save-image
(merge-pathnames filename
*target
*) (create-graph graph
) :pnm
))
136 (defun save-annotated-path (filename &rest args
)
137 (aa-misc:save-image
(merge-pathnames filename
*target
*) (apply #'generate-annotated-path args
) :pnm
))
139 ;;;--------------------------------------------------------------------------
142 (let ((path (create-path :polygon
)))
143 (path-reset path
(make-point 25 15))
144 (path-extend path
(make-straight-line) (make-point 250 25))
145 (path-extend path
(make-bezier-curve (list (make-point 300 40)
147 (make-point 200 100)))
148 (make-point 250 250))
149 (path-extend path
(make-arc 100 200 :x-axis-rotation -
0.8)
151 (path-extend path
(make-catmull-rom (make-point 10 270)
152 (list (make-point 10 200)
158 (show-annotated-path path
)))
164 (let ((path (make-simple-path '((125 .
20)
180 (let ((path (create-path :polygon
)))
181 (path-reset path
(make-point 25 15))
182 (path-extend path
(make-straight-line) (make-point 250 25))
183 (path-extend path
(make-bezier-curve (list (make-point 300 40)
185 (make-point 200 100)))
186 (make-point 250 250))
187 (path-extend path
(make-arc 100 200 :x-axis-rotation -
0.8)
189 (path-extend path
(make-catmull-rom (make-point 10 270)
190 (list (make-point 10 200)
197 "pic-interpolations.pnm"
201 ;; Discrete path - Before
203 (let ((path (make-simple-path '((80 .
80) (100 .
200) (250 .
80) (300 .
200)))))
205 "pic-before-discrete.pnm"
206 (paths:stroke-path path
100.0
213 ;; Discrete path - After
215 (let ((path (make-simple-path '((80 .
80) (100 .
200) (250 .
80) (300 .
200)))))
217 "pic-after-discrete.pnm"
218 (make-discrete-path (first (paths:stroke-path path
100.0
221 :inner-joint
:miter
)))
227 (let* ((path (make-simple-path '((50 .
50) (70 .
170) (190 .
90) (270 .
170) (300 .
40))))
228 (stroked (stroke-path path
40.0
232 :assume-type
:open-polyline
)))
234 "pic-stroke-open.pnm"
238 (let* ((path (make-simple-path '((50 .
50) (70 .
170) (190 .
90) (270 .
170) (300 .
40))))
239 (stroked (stroke-path path
40.0
243 :assume-type
:closed-polyline
)))
245 "pic-stroke-closed.pnm"
249 (let* ((path (make-simple-path '((50 .
50) (70 .
170) (190 .
90) (270 .
170) (300 .
40))))
250 (stroked (stroke-path path
40.0
254 :assume-type
:polygon
)))
256 "pic-stroke-polygon.pnm"
263 (let ((path (create-path :open-polyline
)))
264 (path-reset path
(make-point 30 30))
265 (path-extend path
(make-straight-line) (make-point 180 80))
266 (path-extend path
(make-arc 80 80 :large-arc-flag t
:sweep-flag t
) (make-point 150 150))
267 (path-extend path
(make-straight-line) (make-point 90 200))
270 (dash-path path
#(80 50))
276 (let ((path (make-simple-path '((50 .
50) (70 .
170) (190 .
30) (270 .
170))))
277 (clipping (make-rectangle-path/center
140 120 80 80)))
278 (paths::path-rotate clipping
0.3 (make-point 140 120))
279 (print (paths::clip-path
/path path clipping
))
282 (paths::clip-path
/path path clipping
)
283 :reference
(list path clipping
)
288 (let* ((paths (stroke-path (make-simple-path '((50 .
50) (70 .
170) (190 .
30) (270 .
170)))
289 40.0 :caps
:round
:inner-joint
:miter
:joint
:round
))
290 (paths-copy (mapcar #'path-clone paths
)))
292 (path-rotate path
0.4 (make-point 100 80)))
296 :reference
(list paths-copy
)
301 (let ((path (make-circle-path 100 50 90 40 0.2)))
309 (let ((path (make-rectangle-path 10 10 300 100 :round-x
20 :round-y
30)))
317 (let ((path (create-path :open-polyline
)))
318 (path-reset path
(make-point 20 300))
319 (path-extend path
(make-straight-line) (make-point 70 275))
320 (path-extend path
(make-arc 25 25 :x-axis-rotation -
0.5 :sweep-flag t
)
321 (make-point 120 250))
322 (path-extend path
(make-straight-line) (make-point 170 225))
323 (path-extend path
(make-arc 25 50 :x-axis-rotation -
0.5 :sweep-flag t
)
324 (make-point 220 200))
325 (path-extend path
(make-straight-line) (make-point 270 175))
326 (path-extend path
(make-arc 25 75 :x-axis-rotation -
0.5 :sweep-flag t
)
327 (make-point 320 150))
328 (path-extend path
(make-straight-line) (make-point 370 125))
329 (path-extend path
(make-arc 25 100 :x-axis-rotation -
0.5 :sweep-flag t
)
330 (make-point 420 100))
331 (path-extend path
(make-straight-line) (make-point 470 75))
332 (paths::path-scale path
0.7 0.7)
338 ;; Catmull-Rom example
340 (let ((path (create-path :open-polyline
)))
341 (path-reset path
(make-point 30 40))
342 (path-extend path
(make-catmull-rom (make-point 20 20)
343 (list (make-point 80 20)
350 "pic-catmull-rom.pnm"
356 (let ((path (create-path :open-polyline
)))
357 (path-reset path
(make-point 10 100))
358 (path-extend path
(make-bezier-curve (list (make-point 80 10)
361 (make-point 250 90)))
362 (make-point 300 100))
370 (let ((path (create-path :polygon
)))
371 (path-extend path
(make-arc 50 50) (make-point 0 0))
372 (path-extend path
(make-arc 34 34) (make-point 20 20))
373 (path-extend path
(make-arc 34 34) (make-point 0 40))
375 (save-graph "pic-list.pnm" (list (list #(120 120 120) (stroke-path path
2))
376 (list #(0 0 0) path
))))
378 ;; black triangle antialiased
380 (let ((state (aa:make-state
)))
381 (aa:line-f state
200 50 250 150)
382 (aa:line-f state
250 150 50 100)
383 (aa:line-f state
50 100 200 50)
384 (let* ((image (aa-misc:make-image
300 200 #(255 255 255)))
385 (put-pixel (aa-misc:image-put-pixel image
#(0 0 0))))
386 (aa:cells-sweep state put-pixel
)
387 (save-image* "pic-tut1.pnm" image
)))
388 ;; 2 overlapping triangles
389 (let ((state (aa:make-state
))) ; create the state
391 (aa:line-f state
200 50 250 150) ; describe the 3 sides
392 (aa:line-f state
250 150 50 100) ; of the first triangle
393 (aa:line-f state
50 100 200 50)
395 (aa:line-f state
75 25 10 75) ; describe the 3 sides
396 (aa:line-f state
10 75 175 100) ; of the second triangle
397 (aa:line-f state
175 100 75 25)
398 (let* ((image (aa-misc:make-image
300 200 #(255 255 255)))
399 (put-pixel (aa-misc:image-put-pixel image
#(0 0 0))))
400 (aa:cells-sweep state put-pixel
) ; render it
401 (save-image* "pic-tut2.pnm" image
)))
402 ;; 2 overlapping triangles red/blue
403 (let ((state1 (aa:make-state
))
404 (state2 (aa:make-state
)))
406 (aa:line-f state1
200 50 250 150) ; describe the 3 sides
407 (aa:line-f state1
250 150 50 100) ; of the first triangle
408 (aa:line-f state1
50 100 200 50)
410 (aa:line-f state2
75 25 10 75) ; describe the 3 sides
411 (aa:line-f state2
10 75 175 100) ; of the second triangle
412 (aa:line-f state2
175 100 75 25)
413 (let ((image (aa-misc:make-image
300 200 #(255 255 255))))
414 (aa:cells-sweep state1
(aa-misc:image-put-pixel image
#(255 0 0)))
415 (aa:cells-sweep state2
(aa-misc:image-put-pixel image
#(0 0 255)))
416 (save-image* "pic-tut3.pnm" image
)))
420 (defun path-extend-with-waves (path knot width frequency
)
421 ;; generate a serie of arc to represent a wave up to knot