Initial import.
[cl-vectors.git] / paths.lisp
blob9751983970944d11cbe8b00d9f6a3bf5878df32c
1 ;;;; cl-vectors -- Rasterizer and paths manipulation library
2 ;;;; Copyright (C) 2007 Frédéric Jolliton <frederic@jolliton.com>
3 ;;;;
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.
7 ;;;;
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 ;;;; This file provides facilities to create and manipulate vectorial paths.
14 ;;;;
15 ;;;; Changelogs:
16 ;;;;
17 ;;;; 2007-02-20: first release
19 #+nil(error "This file assume that #+NIL is never defined.")
21 (in-package #:net.tuxee.paths)
23 (defvar *bezier-distance-tolerance* 0.5
24 "The default distance tolerance used when rendering Bezier
25 curves.")
27 (defvar *bezier-angle-tolerance* 0.05
28 "The default angle tolerance (in radian) used when rendering
29 Bezier curves")
31 (defvar *arc-length-tolerance* 1.0
32 "The maximum length of segment describing an arc.")
34 (defvar *miter-limit* 4.0
35 "Miter limit before reverting to bevel joint. Must be >=1.0.")
37 ;;;--[ Math utilities ]------------------------------------------------------
39 ;;; http://mathworld.wolfram.com/Line-LineIntersection.html
40 (defun line-intersection (x1 y1 x2 y2
41 x3 y3 x4 y4)
42 "Compute the intersection between 2 lines (x1,y1)-(x2,y2)
43 and (x3,y3)-(x4,y4). Return the coordinates of the intersection
44 points as 2 values. If the 2 lines are colinears, return NIL."
45 (flet ((det (a b c d)
46 (- (* a d)
47 (* b c))))
48 (let* ((dx1 (- x2 x1))
49 (dy1 (- y2 y1))
50 (dx2 (- x4 x3))
51 (dy2 (- y4 y3))
52 (d (det dx2 dy2 dx1 dy1)))
53 (unless (zerop d)
54 (let ((a (det x1 y1 x2 y2))
55 (b (det x3 y3 x4 y4)))
56 (values (/ (det a dx1 b dx2) d)
57 (/ (det a dy1 b dy2) d)))))))
59 (defun line-intersection/delta (x1 y1 dx1 dy1
60 x2 y2 dx2 dy2)
61 "Compute the intersection between the line by (x1,y1) and
62 direction (dx1,dy1) and the line by (x2,y2) and
63 direction (dx2,dy2). Return the coordinates of the intersection
64 points as 2 values. If the 2 lines are colinears, return NIL."
65 (flet ((det (a b c d)
66 (- (* a d)
67 (* b c))))
68 (let ((d (det dx2 dy2 dx1 dy1)))
69 (unless (zerop d)
70 (let ((a (det x1 y1 (+ x1 dx1) (+ y1 dy1)))
71 (b (det x2 y2 (+ x2 dx2) (+ y2 dy2))))
72 (values (/ (det a dx1 b dx2) d)
73 (/ (det a dy1 b dy2) d)))))))
75 (defun normalize (x y &optional (length 1.0))
76 "Normalize the vector (X,Y) such that its length is LENGTH (or
77 1.0 if unspecified.) Return the component of the resulting vector
78 as 2 values. Return NIL if the input vector had a null length."
79 (if (zerop length)
80 (values 0.0 0.0)
81 (let ((norm (/ (sqrt (+ (* x x) (* y y))) length)))
82 (unless (zerop norm)
83 (values (/ x norm) (/ y norm))))))
85 (defun line-normal (x1 y1 x2 y2)
86 "Normalize the vector (X2-X1,Y2-Y1). See NORMALIZE."
87 (normalize (- x2 x1) (- y2 y1)))
89 ;;;--[ Points ]--------------------------------------------------------------
91 ;;; Points are supposed to be immutable
93 (declaim (inline make-point point-x point-y))
94 (defun make-point (x y) (cons x y))
95 (defun point-x (point) (car point))
96 (defun point-y (point) (cdr point))
98 ;;; Utility functions for points
100 (defun p+ (p1 p2)
101 (make-point (+ (point-x p1) (point-x p2))
102 (+ (point-y p1) (point-y p2))))
104 (defun p- (p1 p2)
105 (make-point (- (point-x p1) (point-x p2))
106 (- (point-y p1) (point-y p2))))
108 (defun p* (point scale &optional (scale-y scale))
109 (make-point (* (point-x point) scale)
110 (* (point-y point) scale-y)))
112 (defun point-rotate (point angle)
113 "Rotate POINT by ANGLE radian around the origin."
114 (let ((x (point-x point))
115 (y (point-y point)))
116 (make-point (- (* x (cos angle)) (* y (sin angle)))
117 (+ (* y (cos angle)) (* x (sin angle))))))
119 (defun point-angle (point)
120 "Compute the angle of POINT relatively to the X axis."
121 (atan (point-y point) (point-x point)))
123 (defun point-norm (point)
124 "Compute the distance of POINT from origin."
125 (sqrt (+ (expt (point-x point) 2)
126 (expt (point-y point) 2))))
128 ;; (point-norm (p- p2 p1))
129 (defun point-distance (p1 p2)
130 "Compute the distance between P1 and P2."
131 (sqrt (+ (expt (- (point-x p2) (point-x p1)) 2)
132 (expt (- (point-y p2) (point-y p1)) 2))))
134 ;; (p* (p+ p1 p2) 0.5)
135 (defun point-middle (p1 p2)
136 "Compute the point between P1 and P2."
137 (make-point (/ (+ (point-x p1) (point-x p2)) 2.0)
138 (/ (+ (point-y p1) (point-y p2)) 2.0)))
140 ;;;--[ Paths ]---------------------------------------------------------------
142 (defstruct path
143 (type :open-polyline :type (member :open-polyline :closed-polyline :polygon))
144 (knots (make-array 0 :adjustable t :fill-pointer 0))
145 (interpolations (make-array 0 :adjustable t :fill-pointer 0)))
147 (defun create-path (type)
148 "Create a new path of the given type. The type must be one of
149 the following keyword:
151 :open-polyline -- An open polyline path,
152 :closed-polyline -- A closed polyline path,
153 :polygon -- Like :closed-polyline, but implicitly filled."
154 (assert (member type '(:open-polyline :closed-polyline :polygon)))
155 (make-path :type type))
157 (defun path-clear (path)
158 "Clear the path such that it is empty."
159 (setf (fill-pointer (path-knots path)) 0
160 (fill-pointer (path-interpolations path)) 0))
162 (defun path-reset (path knot)
163 "Reset the path such that it is a single knot."
164 (path-clear path)
165 (vector-push-extend knot (path-knots path))
166 (vector-push-extend (make-straight-line) (path-interpolations path)))
168 (defun path-extend (path interpolation knot)
169 "Extend the path to KNOT, with INTERPOLATION."
170 (vector-push-extend interpolation (path-interpolations path))
171 (vector-push-extend knot (path-knots path)))
173 (defun path-concatenate (path interpolation other-path)
174 "Append OTHER-PATH to PATH, joined by INTERPOLATION."
175 (let ((interpolations (path-interpolations other-path))
176 (knots (path-knots other-path)))
177 (loop for i below (length knots)
178 do (path-extend path
179 (interpolation-clone (if (and (zerop i) interpolation)
180 interpolation
181 (aref interpolations i)))
182 (aref knots i)))))
184 (defun path-replace (path other-path)
185 "Replace PATH with contents of OTHER-PATH."
186 (path-clear path)
187 (path-concatenate path nil other-path))
189 (defun path-size (path)
190 "Return the number of knots on the path."
191 (length (path-knots path)))
193 (defun path-last-knot (path)
194 "Return the last knot of the path. Return NIL if the path is
195 empty."
196 (let ((knots (path-knots path)))
197 (when (plusp (length knots))
198 (aref knots (1- (length knots))))))
200 ;;; Iterators
202 (defgeneric path-iterator-reset (iterator)
203 (:documentation "Reset the iterator before the first knot."))
205 (defgeneric path-iterator-next (iterator)
206 (:documentation "Move the iterator to the next knot, and return
207 3 values: INTERPOLATION, KNOT and END-P. INTERPOLATION is the
208 interpolation between the previous knot and the current one. For
209 the first iteration, INTERPOLATION is usually the implicit
210 straight line between the last knot and the first knot. KNOT and
211 INTERPOLATION are null if the path is empty. END-P is true if the
212 knot is the last on the path or if the path is empty."))
214 (defun path-from-iterator (iterator type)
215 "Construct a new path from the given iterator."
216 (let ((path (create-path type)))
217 (loop
218 (multiple-value-bind (iterator knot end-p) (path-iterator-next iterator)
219 (path-extend path iterator knot)
220 (when end-p
221 (return path))))))
223 ;;; Classic iterator
225 (defstruct path-iterator-state
226 path index)
228 (defun path-iterator (path)
229 (make-path-iterator-state :path path :index nil))
231 (defmethod path-iterator-reset ((iterator path-iterator-state))
232 (setf (path-iterator-state-index iterator) nil))
234 (defmethod path-iterator-next ((iterator path-iterator-state))
235 (let* ((index (path-iterator-state-index iterator))
236 (path (path-iterator-state-path iterator))
237 (knots (path-knots path))
238 (interpolations (path-interpolations path)))
239 (cond
240 ((zerop (length knots))
241 (values nil nil t))
243 ;; Update index to the next place
244 (setf index
245 (setf (path-iterator-state-index iterator)
246 (if (null index) 0 (mod (1+ index) (length knots)))))
247 (values (aref interpolations index)
248 (aref knots index)
249 (= index (1- (length knots))))))))
251 ;;; Segmented iterator
253 ;;; This iterator iterate over segmented interpolation, if the
254 ;;; interpolation is matched by the predicate. This is useful for
255 ;;; algorithms that doesn't handle certain type of interpolations.
256 ;;; The predicate could test the type, but also certain type of
257 ;;; interpolation (such as arc of circle vs arc of ellipse, or degree
258 ;;; of the Bezier curves.)
260 ;;; Note: I use PI prefix instead of PATH-ITERATOR to shorten names.
262 (defstruct pi-segmented-state
263 path index predicate end-p queue)
265 (defun path-iterator-segmented (path &optional (predicate (constantly t)))
266 (make-pi-segmented-state :path path :index nil
267 :predicate predicate
268 :end-p nil :queue nil))
270 (defmethod path-iterator-reset ((iterator pi-segmented-state))
271 (setf (pi-segmented-state-index iterator) nil
272 (pi-segmented-state-queue iterator) nil))
274 (defmethod path-iterator-next ((iterator pi-segmented-state))
275 (flet ((update-queue (interpolation k1 k2 last-p)
276 (let (new-queue)
277 (interpolation-segment interpolation k1 k2 (lambda (p) (push p new-queue)))
278 (push k2 new-queue)
279 (setf (pi-segmented-state-end-p iterator) last-p
280 (pi-segmented-state-queue iterator) (nreverse new-queue))))
281 (dequeue ()
282 (let* ((knot (pop (pi-segmented-state-queue iterator)))
283 (end-p (and (pi-segmented-state-end-p iterator)
284 (null (pi-segmented-state-queue iterator)))))
285 (values (make-straight-line) knot (when end-p t)))))
286 (cond
287 ((pi-segmented-state-queue iterator)
288 ;; Queue is not empty, process it first.
289 (dequeue))
291 ;; Either refill the queue, or return the next straight line
292 ;; from the sub iterator.
293 (let* ((index (pi-segmented-state-index iterator))
294 (path (pi-segmented-state-path iterator))
295 (knots (path-knots path))
296 (interpolations (path-interpolations path)))
297 (cond
298 ((zerop (length knots))
299 ;; Empty path.
300 (values nil nil t))
302 ;; Update index to the next place
303 (setf index
304 (setf (pi-segmented-state-index iterator)
305 (if (null index) 0 (mod (1+ index) (length knots)))))
306 (let ((interpolation (aref interpolations index))
307 (knot (aref knots index))
308 (end-p (= index (1- (length knots)))))
309 ;; Check if we have to segment the next interpolation
310 (if (funcall (pi-segmented-state-predicate iterator)
311 interpolation)
312 (let ((previous-index (mod (1- index) (length knots))))
313 (update-queue interpolation
314 (aref knots previous-index)
315 knot end-p)
316 (dequeue))
317 (values interpolation knot end-p))))))))))
319 ;;; Iterate distinct
321 ;;; This iterator filter out identical knots. That is, the knots with
322 ;;; the same positions, with any interpolation.
324 ;;; Note about end marker: Consider the input path with knots A, A, B,
325 ;;; C, C, A. The last knot A is marked as the last knot on the
326 ;;; path. But the "distinct" filter will keep only the knot between
327 ;;; brackets: A, [A], [B], C, [C], A (the last of each series of
328 ;;; identical knots.) The resulting sequence is thus: A, B, C. And C
329 ;;; is the last knot in this case.
331 (defstruct filter-distinct-state
332 iterator current next cyclic-p)
334 (defun filter-distinct (iterator &optional (preserve-cyclic-end-p nil))
335 (make-filter-distinct-state :iterator iterator
336 :current nil :next nil
337 :cyclic-p (not preserve-cyclic-end-p)))
339 (defmethod path-iterator-reset ((iterator filter-distinct-state))
340 (path-iterator-reset (filter-distinct-state-iterator iterator))
341 (setf (filter-distinct-state-current iterator) nil
342 (filter-distinct-state-next iterator) nil))
344 (defmethod path-iterator-next ((iterator filter-distinct-state))
345 (let ((sub (filter-distinct-state-iterator iterator))
346 (current (filter-distinct-state-current iterator))
347 (next (filter-distinct-state-next iterator))
348 (cyclic-p (filter-distinct-state-cyclic-p iterator))
349 result)
350 ;; FIXME: Both LOOP can be factorized
352 ;; When we start, we have 2 things:
353 ;; - the state to return, possibly modified below for end-p (aka current)
354 ;; - the state that followed current (aka next)
355 (unless current
356 ;; Take the first knot
357 (setf current (multiple-value-list (path-iterator-next sub)))
358 (when (or (null (second current)) (third current))
359 ;; The path was empty or is composed of a single knot. Stop
360 ;; here.
361 (return-from path-iterator-next (values-list current)))
362 ;; Advance current until the following knot is distinct
363 (loop do (setf next (multiple-value-list (path-iterator-next sub)))
364 while (zerop (point-distance (second current) (second next)))
365 ;; Is the path made of identical knots?
366 when (third next)
367 do (return-from path-iterator-next (values-list next))
368 ;; Advance current
369 do (setf current next)))
370 ;; CURRENT will be the result knot
371 ;; NEXT become the current knot
372 (setf result current
373 current next)
374 (cond
375 ((and (third current) (not cyclic-p))
376 (setf next (multiple-value-list (path-iterator-next sub))))
378 ;; Advance current until the following knot is distinct
379 (loop do (setf next (multiple-value-list (path-iterator-next sub)))
380 until (and (third current) (not cyclic-p))
381 while (zerop (point-distance (second current) (second next)))
382 ;; Have we reached the end of the path?
383 when (third current)
384 do (setf (third result) t)
385 ;; Advance current
386 do (setf current next))))
387 ;; Keep the state for the next iteration
388 (setf (filter-distinct-state-current iterator) current
389 (filter-distinct-state-next iterator) next)
390 ;; at the end, we must have 3 things:
391 ;; - the state to return (aka result)
392 ;; - the next state to return (aka current), possibly with invalid end info,
393 ;; - the unprocessed state, the one after the "current" (aka next)
394 (values-list result)))
396 ;;; Misc
398 (defun path-clone (path)
399 (let ((new-interpolations (copy-seq (path-interpolations path))))
400 (loop for i below (length new-interpolations)
401 do (setf (aref new-interpolations i)
402 (interpolation-clone (aref new-interpolations i))))
403 (let ((new-path (create-path (path-type path))))
404 (setf (path-knots new-path) (copy-seq (path-knots path))
405 (path-interpolations new-path) new-interpolations)
406 new-path)))
408 (defun path-reverse (path)
409 ;; reverse the order of knots
410 (setf (path-knots path) (nreverse (path-knots path)))
411 ;; reverse the order of interpolations 1..n (not the first one,
412 ;; which is the implicit straight line.)
413 (loop with interpolations = (path-interpolations path)
414 with length = (length interpolations)
415 for i from 1 upto (floor (1- length) 2)
416 do (rotatef (aref interpolations i)
417 (aref interpolations (- length i))))
418 ;; reverse each interpolation
419 (loop for interpolation across (path-interpolations path)
420 do (interpolation-reverse interpolation)))
422 (defun path-reversed (path)
423 (let ((new-path (path-clone path)))
424 (path-reverse new-path)
425 new-path))
427 (defmacro do-path ((path interpolation knot) &body body)
428 (let ((path-sym (gensym))
429 (knots (gensym))
430 (interpolations (gensym))
431 (index (gensym)))
432 `(symbol-macrolet ((,interpolation (aref ,interpolations ,index))
433 (,knot (aref ,knots ,index)))
434 (loop
435 with ,path-sym = ,path
436 with ,knots = (path-knots ,path-sym)
437 with ,interpolations = (path-interpolations ,path-sym)
438 for ,index below (length ,knots)
439 do (progn ,@body)))))
441 (defun path-translate (path vector)
442 "Translate the whole path accordingly to VECTOR."
443 (unless (and (zerop (point-x vector))
444 (zerop (point-y vector)))
445 (do-path (path interpolation knot)
446 (setf knot (p+ knot vector))
447 (interpolation-translate interpolation vector)))
448 path)
450 (defun path-rotate (path angle &optional center)
451 "Rotate the whole path by ANGLE radian around CENTER (which is
452 the origin if unspecified.)"
453 (unless (zerop angle)
454 (when center
455 (path-translate path (p* center -1.0)))
456 (do-path (path interpolation knot)
457 (setf knot (point-rotate knot angle))
458 (interpolation-rotate interpolation angle))
459 (when center
460 (path-translate path center)))
461 path)
463 (defun path-scale (path scale-x scale-y &optional center)
464 "Scale the whole path by (SCALE-X,SCALE-Y) from CENTER (which
465 is the origin if unspecified.) Warning: not all interpolations
466 support non uniform scaling (when scale-x /= scale-y)."
467 (when center
468 (path-translate path (p* center -1.0)))
469 (do-path (path interpolation knot)
470 (setf knot (p* knot scale-x scale-y))
471 (interpolation-scale interpolation scale-x scale-y))
472 (when center
473 (path-translate path center))
474 (when (minusp (* scale-x scale-y))
475 (path-reverse path))
476 path)
478 ;;;--[ Interpolations ]------------------------------------------------------
480 (defgeneric interpolation-segment (interpolation k1 k2 function)
481 (:documentation "Segment the path between K1 and K2 described
482 by the INTERPOLATION. Call FUNCTION for each generated point on
483 the interpolation path."))
485 (defgeneric interpolation-normal (interpolation k1 k2 side)
486 (:documentation "Compute the normal, going \"outside\" at
487 either K1 (if SIDE is false) or K2 (if SIDE is true). Return NIL
488 if the normal cannot be computed. Return a point otherwise."))
490 (defgeneric interpolation-clone (interpolation)
491 (:documentation "Duplicate INTERPOLATION."))
493 (defgeneric interpolation-reverse (interpolation)
494 (:documentation "Reverse the path described by INTERPOLATION
495 in-place."))
497 (defgeneric interpolation-reversed (interpolation)
498 (:method (interpolation)
499 (let ((cloned-interpolation (interpolation-clone interpolation)))
500 (interpolation-reversed cloned-interpolation)
501 cloned-interpolation))
502 (:documentation "Duplicate and reverse the INTERPOLATION."))
504 (defgeneric interpolation-translate (interpolation vector))
506 (defgeneric interpolation-rotate (interpolation angle))
508 (defgeneric interpolation-scale (interpolation scale-x scale-y))
510 ;;; Straight lines
512 (defun make-straight-line ()
513 :straight-line)
515 (defun straight-line-p (value)
516 (eq value :straight-line))
518 (defmethod interpolation-segment ((interpolation (eql :straight-line)) k1 k2 function)
519 (declare (ignore interpolation k1 k2 function)))
521 (defmethod interpolation-normal ((interpolation (eql :straight-line)) k1 k2 side)
522 (let* ((x1 (point-x k1))
523 (y1 (point-y k1))
524 (x2 (point-x k2))
525 (y2 (point-y k2))
526 (dx (- x2 x1))
527 (dy (- y2 y1))
528 (dist (sqrt (+ (expt dx 2) (expt dy 2)))))
529 (when (plusp dist)
530 (if side
531 (make-point (/ dx dist)
532 (/ dy dist))
533 (make-point (- (/ dx dist))
534 (- (/ dy dist)))))))
536 (defmethod interpolation-clone ((interpolation (eql :straight-line)))
537 (make-straight-line))
539 (defmethod interpolation-reverse ((interpolation (eql :straight-line)))
540 (declare (ignore interpolation)))
542 (defmethod interpolation-translate ((interpolation (eql :straight-line)) vector)
543 (declare (ignore interpolation vector)))
545 (defmethod interpolation-rotate ((interpolation (eql :straight-line)) angle)
546 (declare (ignore interpolation angle)))
548 (defmethod interpolation-scale ((interpolation (eql :straight-line)) scale-x scale-y)
549 (declare (ignore interpolation scale-x scale-y)))
551 ;;; Arc (SVG style)
553 (defclass arc ()
554 ((rx :initarg rx)
555 (ry :initarg ry)
556 (x-axis-rotation :initarg x-axis-rotation)
557 (large-arc-flag :initarg large-arc-flag) ; t = choose the longest arc, nil = choose the smallest arc
558 (sweep-flag :initarg sweep-flag))) ; t = arc on the right, nil = arc on the left
560 (defun make-arc (rx ry &key (x-axis-rotation 0.0) (large-arc-flag nil) (sweep-flag nil))
561 (make-instance 'arc
562 'rx rx
563 'ry ry
564 'x-axis-rotation x-axis-rotation
565 'large-arc-flag large-arc-flag
566 'sweep-flag sweep-flag))
568 (defun svg-arc-parameters/reverse (center rx ry rotation start-angle delta-angle)
569 "Conversion from center to endpoint parameterization of SVG arc.
571 Returns values P1, P2, LARGE-ARC-FLAG-P, SWEEP-FLAG-P."
572 (let ((p1 (point-rotate (make-point rx 0) start-angle))
573 (p2 (point-rotate (make-point rx 0) (+ start-angle delta-angle))))
574 (flet ((transform (p)
576 (point-rotate
577 (p* p 1.0 (/ rx ry))
578 rotation)
579 center)))
580 (values (transform p1) (transform p2)
581 (> (abs delta-angle) pi)
582 (plusp delta-angle)))))
584 (defun svg-arc-parameters (p1 p2 rx ry rotation large-arc-flag-p sweep-flag-p)
585 "Conversion from endpoint to center parameterization of SVG arc.
587 Returns values RC, RX, RY, START-ANGLE and DELTA-ANGLE, where RC is
588 the center of the ellipse, RX and RY are the normalized
589 radii (needed if scaling was necessary)."
590 (when (and (/= rx 0)
591 (/= ry 0))
592 ;; [SVG] "If rX or rY have negative signs, these are dropped; the
593 ;; absolute value is used instead."
594 (setf rx (abs rx)
595 ry (abs ry))
596 ;; normalize boolean value to nil/t
597 (setf large-arc-flag-p (when large-arc-flag-p t)
598 sweep-flag-p (when sweep-flag-p t))
599 ;; rp1 and rp2 are p1 and p2 into the coordinate system such
600 ;; that rotation is cancelled and ellipse ratio is 1 (a circle.)
601 (let* ((rp1 (p* (point-rotate p1 (- rotation)) 1.0 (/ rx ry)))
602 (rp2 (p* (point-rotate p2 (- rotation)) 1.0 (/ rx ry)))
603 (rm (point-middle rp1 rp2))
604 (drp1 (p- rm rp1))
605 (dist (point-norm drp1)))
606 (when (plusp dist)
607 (let ((diff-sq (- (expt rx 2) (expt dist 2)))
609 (cond
610 ((not (plusp diff-sq))
611 ;; a/ scale the arc if it is too small to touch the points
612 (setf ry (* dist (/ ry rx))
613 rx dist
614 rc rm))
616 ;; b/ otherwise compute the center of the circle
617 (let ((d (/ (sqrt diff-sq) dist)))
618 (unless (eq large-arc-flag-p sweep-flag-p)
619 (setf d (- d)))
620 (setf rc (make-point (+ (point-x rm) (* (point-y drp1) d))
621 (- (point-y rm) (* (point-x drp1) d)))))))
622 (let* ((start-angle (point-angle (p- rp1 rc)))
623 (end-angle (point-angle (p- rp2 rc)))
624 (delta-angle (- end-angle start-angle)))
625 (when (minusp delta-angle)
626 (incf delta-angle (* 2 pi)))
627 (unless sweep-flag-p
628 (decf delta-angle (* 2 pi)))
629 (values (point-rotate (p* rc 1.0 (/ ry rx)) rotation) rx ry start-angle delta-angle)))))))
631 (defmethod interpolation-segment ((interpolation arc) k1 k2 function)
632 (let ((rotation (slot-value interpolation 'x-axis-rotation)))
633 (multiple-value-bind (rc rx ry start-angle delta-angle)
634 (svg-arc-parameters k1 k2
635 (slot-value interpolation 'rx)
636 (slot-value interpolation 'ry)
637 rotation
638 (slot-value interpolation 'large-arc-flag)
639 (slot-value interpolation 'sweep-flag))
640 (when rc
641 (loop with n = (max 3 (* (max rx ry) (abs delta-angle)))
642 for i from 1 below n
643 for angle = (+ start-angle (/ (* delta-angle i) n))
644 for p = (p+ (point-rotate
646 (make-point (* rx (cos angle))
647 (* rx (sin angle)))
648 1.0 (/ ry rx))
649 rotation)
651 do (funcall function p))))))
653 (defmethod interpolation-normal ((interpolation arc) k1 k2 side)
654 (let ((rotation (slot-value interpolation 'x-axis-rotation)))
655 (multiple-value-bind (rc rx ry start-angle delta-angle)
656 (svg-arc-parameters k1 k2
657 (slot-value interpolation 'rx)
658 (slot-value interpolation 'ry)
659 rotation
660 (slot-value interpolation 'large-arc-flag)
661 (slot-value interpolation 'sweep-flag))
662 (flet ((adjust (normal)
663 (let* ((p (point-rotate (p* normal 1.0 (/ ry rx)) rotation))
664 (d (point-norm p)))
665 (when (plusp delta-angle)
666 (setf d (- d)))
667 (make-point (/ (point-x p) d) (/ (point-y p) d)))))
668 (when rc
669 (let ((end-angle (+ start-angle delta-angle)))
670 (adjust (if side
671 (make-point (sin end-angle)
672 (- (cos end-angle)))
673 (make-point (- (sin start-angle))
674 (cos start-angle))))))))))
676 (defmethod interpolation-clone ((interpolation arc))
677 (make-arc (slot-value interpolation 'rx)
678 (slot-value interpolation 'ry)
679 :x-axis-rotation (slot-value interpolation 'x-axis-rotation)
680 :large-arc-flag (slot-value interpolation 'large-arc-flag)
681 :sweep-flag (slot-value interpolation 'sweep-flag)))
683 (defmethod interpolation-reverse ((interpolation arc))
684 (setf (slot-value interpolation 'sweep-flag)
685 (not (slot-value interpolation 'sweep-flag))))
687 (defmethod interpolation-translate ((interpolation arc) vector)
688 (declare (ignore interpolation vector)))
690 (defmethod interpolation-rotate ((interpolation arc) angle)
691 (incf (slot-value interpolation 'x-axis-rotation) angle))
693 (defmethod interpolation-scale ((interpolation arc) scale-x scale-y)
694 ;; FIXME: Return :segment-me if scaling is not possible?
695 (assert (and (not (zerop scale-x))
696 (= scale-x scale-y)))
697 (with-slots (rx ry) interpolation
698 (setf rx (* rx scale-x)
699 ry (* ry scale-y))))
701 ;;; Catmull-Rom
703 (defclass catmull-rom ()
704 ((head
705 :initarg head)
706 (control-points
707 :initform (make-array 0)
708 :initarg control-points)
709 (queue
710 :initarg queue)))
712 (defun make-catmull-rom (head control-points queue)
713 (make-instance 'catmull-rom
714 'head head
715 'control-points (coerce control-points 'vector)
716 'queue queue))
718 (defmethod interpolation-segment ((interpolation catmull-rom) k1 k2 function)
719 (let* ((control-points (slot-value interpolation 'control-points))
720 (points (make-array (+ (length control-points) 4))))
721 (replace points control-points :start1 2)
722 (setf (aref points 0) (slot-value interpolation 'head)
723 (aref points 1) k1
724 (aref points (- (length points) 2)) k2
725 (aref points (- (length points) 1)) (slot-value interpolation 'queue))
726 (labels ((eval-catmull-rom (a b c d p)
727 ;; http://www.mvps.org/directx/articles/catmull/
728 (* 0.5
729 (+ (* 2 b)
730 (* (+ (- a) c) p)
731 (* (+ (* 2 a) (* -5 b) (* 4 c) (- d)) (expt p 2))
732 (* (+ (- a) (* 3 b) (* -3 c) d) (expt p 3))))))
733 (loop for s below (- (length points) 3)
734 for a = (aref points (+ s 0)) then b
735 for b = (aref points (+ s 1)) then c
736 for c = (aref points (+ s 2)) then d
737 for d = (aref points (+ s 3))
738 do (funcall function b)
739 (loop with n = 32
740 for i from 1 below n
741 for p = (/ (coerce i 'float) n)
742 for x = (eval-catmull-rom (point-x a)
743 (point-x b)
744 (point-x c)
745 (point-x d)
747 for y = (eval-catmull-rom (point-y a)
748 (point-y b)
749 (point-y c)
750 (point-y d)
752 do (funcall function (make-point x y)))
753 (funcall function c)))))
755 (defmethod interpolation-normal ((interpolation catmull-rom) k1 k2 side)
756 (with-slots (head control-points queue) interpolation
757 (let (a b)
758 (if (zerop (length control-points))
759 (if side
760 (setf a k1
761 b queue)
762 (setf a k2
763 b head))
764 (if side
765 (setf a (aref control-points (1- (length control-points)))
766 b queue)
767 (setf a (aref control-points 0)
768 b head)))
769 (let* ((x1 (point-x a))
770 (y1 (point-y a))
771 (x2 (point-x b))
772 (y2 (point-y b))
773 (dx (- x2 x1))
774 (dy (- y2 y1))
775 (dist (sqrt (+ (expt dx 2) (expt dy 2)))))
776 (when (plusp dist)
777 (make-point (/ dx dist)
778 (/ dy dist)))))))
780 (defmethod interpolation-clone ((interpolation catmull-rom))
781 (make-catmull-rom (slot-value interpolation 'head)
782 (copy-seq (slot-value interpolation 'control-points))
783 (slot-value interpolation 'queue)))
785 (defmethod interpolation-reverse ((interpolation catmull-rom))
786 (rotatef (slot-value interpolation 'head)
787 (slot-value interpolation 'queue))
788 (nreverse (slot-value interpolation 'control-points)))
790 (defmethod interpolation-translate ((interpolation catmull-rom) vector)
791 (with-slots (head control-points queue) interpolation
792 (setf head (p+ head vector)
793 queue (p+ queue vector))
794 (loop for i below (length control-points)
795 do (setf (aref control-points i) (p+ (aref control-points i) vector)))))
797 (defmethod interpolation-rotate ((interpolation catmull-rom) angle)
798 (with-slots (head control-points queue) interpolation
799 (setf head (point-rotate head angle)
800 queue (point-rotate queue angle))
801 (loop for i below (length control-points)
802 do (setf (aref control-points i) (point-rotate (aref control-points i) angle)))))
804 (defmethod interpolation-scale ((interpolation catmull-rom) scale-x scale-y)
805 (with-slots (head control-points queue) interpolation
806 (setf head (p* head scale-x scale-y)
807 queue (p* queue scale-x scale-y))
808 (loop for i below (length control-points)
809 do (setf (aref control-points i) (p* (aref control-points i)
810 scale-x scale-y)))))
812 ;;; Bezier curves
814 ;;; [http://www.fho-emden.de/~hoffmann/bezier18122002.pdf]
816 (defclass bezier ()
817 ((control-points
818 :initform (make-array 0)
819 :initarg control-points)))
821 (defun make-bezier-curve (control-points)
822 (make-instance 'bezier
823 'control-points (make-array (length control-points)
824 :initial-contents control-points)))
826 (defun split-bezier (points &optional (position 0.5))
827 "Split the Bezier curve described by POINTS at POSITION into
828 two Bezier curves of the same degree. Returns the curves as 2
829 values."
830 (let* ((size (length points))
831 (stack (make-array size))
832 (current points))
833 (setf (aref stack 0) points)
834 (loop for j from 1 below size
835 for next-size from (1- size) downto 1
836 do (let ((next (make-array next-size)))
837 (loop for i below next-size
838 for a = (aref current i)
839 for b = (aref current (1+ i))
840 do (setf (aref next i)
841 (make-point (+ (* (- 1.0 position) (point-x a))
842 (* position (point-x b)))
843 (+ (* (- 1.0 position) (point-y a))
844 (* position (point-y b))))))
845 (setf (aref stack j) next
846 current next)))
847 (let ((left (make-array (length points)))
848 (right (make-array (length points))))
849 (loop for i from 0 below size
850 for j from (1- size) downto 0
851 do (setf (aref left i) (aref (aref stack i) 0)
852 (aref right i) (aref (aref stack j) i)))
853 (values left right))))
855 (defun evaluate-bezier (points position)
856 "Evaluate the point at POSITION on the Bezier curve described
857 by POINTS."
858 (let* ((size (length points))
859 (temp (make-array (1- size))))
860 (loop for current = points then temp
861 for i from (length temp) downto 1
862 do (loop for j below i
863 for a = (aref current j)
864 for b = (aref current (1+ j))
865 do (setf (aref temp j)
866 (make-point (+ (* (- 1.0 position) (point-x a))
867 (* position (point-x b)))
868 (+ (* (- 1.0 position) (point-y a))
869 (* position (point-y b)))))))
870 (let ((p (aref temp 0)))
871 (values (point-x p) (point-y p)))))
873 (defun discrete-bezier-curve (points function
874 &key
875 (include-ends t)
876 (min-subdivide nil)
877 (max-subdivide 10)
878 (distance-tolerance *bezier-distance-tolerance*)
879 (angle-tolerance *bezier-angle-tolerance*))
880 "Subdivize Bezier curve up to certain criterions."
881 ;; FIXME: Handle cusps correctly!
882 (unless min-subdivide
883 (setf min-subdivide (floor (log (1+ (length points)) 2))))
884 (labels ((norm (a b)
885 (sqrt (+ (expt a 2) (expt b 2))))
886 (refine-bezier (points depth)
887 (let* ((a (aref points 0))
888 (b (aref points (1- (length points))))
889 (middle-straight (point-middle a b)))
890 (multiple-value-bind (bx by) (evaluate-bezier points 0.5)
891 (when (or (< depth min-subdivide)
892 (and (<= depth max-subdivide)
893 (or (> (norm (- bx (point-x middle-straight))
894 (- by (point-y middle-straight)))
895 distance-tolerance)
896 (> (abs (- (atan (- by (point-y a)) (- bx (point-x a)))
897 (atan (- (point-y b) by) (- (point-x b) bx))))
898 angle-tolerance))))
899 (multiple-value-bind (a b) (split-bezier points 0.5)
900 (refine-bezier a (1+ depth))
901 (funcall function bx by)
902 (refine-bezier b (1+ depth))))))))
903 (when include-ends
904 (let ((p (aref points 0)))
905 (funcall function (point-x p) (point-y p))))
906 (refine-bezier points 0)
907 (when include-ends
908 (let ((p (aref points (1- (length points)))))
909 (funcall function (point-x p) (point-y p)))))
910 (values))
912 (defmethod interpolation-segment ((interpolation bezier) k1 k2 function)
913 (with-slots (control-points) interpolation
914 (let ((points (make-array (+ 2 (length control-points)))))
915 (replace points control-points :start1 1)
916 (setf (aref points 0) k1
917 (aref points (1- (length points))) k2)
918 (discrete-bezier-curve points
919 (lambda (x y) (funcall function (make-point x y)))
920 :include-ends nil))))
922 (defmethod interpolation-normal ((interpolation bezier) k1 k2 side)
923 (let ((control-points (slot-value interpolation 'control-points))
924 a b)
925 (if (zerop (length control-points))
926 (if side
927 (setf a k1
928 b k2)
929 (setf a k2
930 b k1))
931 (if side
932 (setf a (aref control-points (1- (length control-points)))
933 b k2)
934 (setf a (aref control-points 0)
935 b k1)))
936 (let* ((x1 (point-x a))
937 (y1 (point-y a))
938 (x2 (point-x b))
939 (y2 (point-y b))
940 (dx (- x2 x1))
941 (dy (- y2 y1))
942 (dist (sqrt (+ (expt dx 2) (expt dy 2)))))
943 (when (plusp dist)
944 (make-point (/ dx dist)
945 (/ dy dist))))))
947 (defmethod interpolation-clone ((interpolation bezier))
948 (let ((control-points (copy-seq (slot-value interpolation 'control-points))))
949 (loop for i below (length control-points)
950 do (setf (aref control-points i) (aref control-points i)))
951 (make-bezier-curve control-points)))
953 (defmethod interpolation-reverse ((interpolation bezier))
954 (nreverse (slot-value interpolation 'control-points)))
956 (defmethod interpolation-translate ((interpolation bezier) vector)
957 (with-slots (control-points) interpolation
958 (loop for i below (length control-points)
959 do (setf (aref control-points i) (p+ (aref control-points i) vector)))))
961 (defmethod interpolation-rotate ((interpolation bezier) angle)
962 (with-slots (control-points) interpolation
963 (loop for i below (length control-points)
964 do (setf (aref control-points i) (point-rotate (aref control-points i) angle)))))
966 (defmethod interpolation-scale ((interpolation bezier) scale-x scale-y)
967 (with-slots (control-points) interpolation
968 (loop for i below (length control-points)
969 do (setf (aref control-points i) (p* (aref control-points i)
970 scale-x scale-y)))))
972 ;;;--[ Building paths ]------------------------------------------------------
974 (defun make-discrete-path (path)
975 "Construct a path with only straight lines."
976 (let ((result (create-path (path-type path)))
977 (knots (path-knots path))
978 (interpolations (path-interpolations path)))
979 (when (plusp (length knots))
980 ;; nicer, but slower too.. (But not profiled. Premature optimization?)
981 #+nil(loop with iterator = (path-iterator-segmented path)
982 for (interpolation knot end-p) = (multiple-value-list (path-iterator-next iterator))
983 do (path-extend result interpolation knot)
984 until end-p)
985 (path-reset result (aref knots 0))
986 (loop
987 for i below (1- (length knots))
988 for k1 = (aref knots i)
989 for k2 = (aref knots (1+ i))
990 for interpolation = (aref interpolations (1+ i))
991 do (interpolation-segment interpolation k1 k2
992 (lambda (knot)
993 (path-extend result
994 (make-straight-line)
995 knot)))
996 do (path-extend result (make-straight-line) k2)
997 finally (unless (eq (path-type path) :open-polyline)
998 (interpolation-segment (aref interpolations 0) k2 (aref knots 0)
999 (lambda (knot)
1000 (path-extend result
1001 (make-straight-line)
1002 knot))))))
1003 result))
1005 (defun make-circle-path (cx cy radius &optional (radius-y radius) (x-axis-rotation 0.0))
1006 "Construct a path to represent a circle centered at CX,CY of
1007 the specified RADIUS."
1008 ;; Note: We represent the circle with 2 arcs
1009 (let ((path (create-path :polygon)))
1010 (setf radius (abs radius)
1011 radius-y (abs radius-y))
1012 (when (= radius radius-y)
1013 (setf x-axis-rotation 0.0))
1014 (when (and (plusp cx) (plusp cy))
1015 (let* ((center (make-point cx cy))
1016 (p (point-rotate (make-point radius 0) x-axis-rotation))
1017 (left (p+ center p))
1018 (right (p- center p)))
1019 (path-reset path right)
1020 (path-extend path (make-arc radius radius-y :x-axis-rotation x-axis-rotation) left)
1021 (path-extend path (make-arc radius radius-y :x-axis-rotation x-axis-rotation) right)))
1022 path))
1024 (defun make-rectangle-path (x1 y1 x2 y2
1025 &key (round nil) (round-x nil) (round-y nil))
1026 ;; FIXME: Instead: center + width + height + rotation ?
1027 ;; FIXME: Round corners? (rx, ry)
1028 (when (> x1 x2)
1029 (rotatef x1 x2))
1030 (when (> y1 y2)
1031 (rotatef y1 y2))
1032 (let ((path (create-path :closed-polyline))
1033 (round-x (or round-x round))
1034 (round-y (or round-y round)))
1035 (cond
1036 ((and round-x (plusp round-x)
1037 round-y (plusp round-y))
1038 (path-reset path (make-point (+ x1 round-x) y1))
1039 (path-extend path (make-arc round-x round-y) (make-point x1 (+ y1 round-y)))
1040 (path-extend path (make-straight-line) (make-point x1 (- y2 round-y)))
1041 (path-extend path (make-arc round-x round-y) (make-point (+ x1 round-x) y2))
1042 (path-extend path (make-straight-line) (make-point (- x2 round-x) y2))
1043 (path-extend path (make-arc round-x round-y) (make-point x2 (- y2 round-y)))
1044 (path-extend path (make-straight-line) (make-point x2 (+ y1 round-y)))
1045 (path-extend path (make-arc round-x round-y) (make-point (- x2 round-x) y1)))
1047 (path-reset path (make-point x1 y1))
1048 (path-extend path (make-straight-line) (make-point x1 y2))
1049 (path-extend path (make-straight-line) (make-point x2 y2))
1050 (path-extend path (make-straight-line) (make-point x2 y1))))
1051 path))
1053 (defun make-rectangle-path/center (x y dx dy &rest args)
1054 (apply #'make-rectangle-path (- x dx) (- y dy) (+ x dx) (+ y dy) args))
1056 (defun make-regular-polygon-path (x y radius sides &optional (start-angle 0.0))
1057 (let ((path (create-path :closed-polyline)))
1058 (loop for i below sides
1059 for angle = (+ start-angle (/ (* i 2 pi) sides))
1060 do (path-extend path (make-straight-line)
1061 (make-point (+ x (* (cos angle) radius))
1062 (- y (* (sin angle) radius)))))
1063 path))
1065 (defun make-simple-path (points &optional (type :open-polyline))
1066 "Create a path with only straight line, by specifying only knots."
1067 (let ((path (create-path type)))
1068 (dolist (point points)
1069 (path-extend path (make-straight-line) point))
1070 path))
1072 ;;;--[ Transformations ]-----------------------------------------------------
1074 (defmacro define-for-multiple-paths (name-multiple name-single &optional documentation)
1075 "Define a new function named by NAME-MULTIPLE which accepts
1076 multiple paths as input from a function accepting a single path
1077 and producing a list of path named by NAME-SINGLE."
1078 `(defun ,name-multiple (paths &rest args)
1079 ,@(when documentation (list documentation))
1080 (loop for path in (if (listp paths) paths (list paths))
1081 nconc (apply #',name-single path args))))
1083 ;;; Stroke
1085 (defun stroke-path/1 (path thickness
1086 &key (caps :butt) (joint :none) (inner-joint :none)
1087 assume-type)
1088 "Stroke the path."
1089 (setf thickness (abs thickness))
1090 (let ((half-thickness (/ thickness 2.0))
1091 target)
1092 ;; TARGET is the path updated by the function LINE-TO and
1093 ;; EXTEND-TO below.
1094 (labels ((filter-interpolation (interpolation)
1095 ;; We handle only straight-line and arc of circle. The
1096 ;; rest will be segmented.
1097 (not (or (straight-line-p interpolation)
1098 (and (typep interpolation 'arc)
1099 (= (slot-value interpolation 'rx)
1100 (slot-value interpolation 'ry))))))
1101 (det (a b c d)
1102 (- (* a d) (* b c)))
1103 (arc (model)
1104 "Make a new arc similar to MODEL but with a radius
1105 updated to match the stroke."
1106 (assert (= (slot-value model 'rx)
1107 (slot-value model 'ry)))
1108 (let ((shift (if (slot-value model 'sweep-flag)
1109 (- half-thickness)
1110 half-thickness)))
1111 (make-arc (+ (slot-value model 'rx) shift)
1112 (+ (slot-value model 'ry) shift)
1113 :sweep-flag (slot-value model 'sweep-flag)
1114 :large-arc-flag (slot-value model 'large-arc-flag))))
1115 (line-to (p)
1116 "Extend the path to knot P with a straight line."
1117 (path-extend target (make-straight-line) p))
1118 (extend-to (i p)
1119 "EXtend the path to knot P with the given interpolation."
1120 (path-extend target i p))
1121 (do-single (k1)
1122 "Produce the resulting path when the input path
1123 contains a single knot."
1124 (ecase caps
1125 (:butt
1126 nil)
1127 (:square
1128 (path-replace target
1129 (make-rectangle-path/center (point-x k1)
1130 (point-y k1)
1131 half-thickness
1132 half-thickness)))
1133 (:round
1134 (path-replace target
1135 (make-circle-path (point-x k1)
1136 (point-y k1)
1137 half-thickness)))))
1138 (do-first (k1 i2 k2)
1139 "Process the first interpolation."
1140 (let* ((normal (interpolation-normal i2 k1 k2 nil))
1141 (n (p* normal half-thickness))
1142 (d (point-rotate n (/ pi 2))))
1143 (ecase caps
1144 (:butt
1145 (line-to (p- k1 d)))
1146 (:square
1147 (line-to (p+ (p+ k1 d) n))
1148 (line-to (p+ (p- k1 d) n))
1149 (unless (straight-line-p i2)
1150 (line-to (p- k1 d))))
1151 (:round
1152 (extend-to (make-arc half-thickness half-thickness) (p- k1 d))))))
1153 (do-last (k1 i2 k2)
1154 "Process the last interpolation."
1155 (let* ((normal (interpolation-normal i2 k1 k2 t))
1156 (d (p* (point-rotate normal (/ pi 2)) half-thickness)))
1157 (cond
1158 ((typep i2 'arc)
1159 (extend-to (arc i2) (p+ k2 d)))
1160 ((straight-line-p i2)
1161 (unless (eq caps :square)
1162 (line-to (p+ k2 d))))
1164 (error "unexpected interpolation")))))
1165 (do-segment (k1 i2 k2 i3 k3)
1166 "Process intermediate interpolation."
1167 (let* ((normal-a (interpolation-normal i2 k1 k2 t))
1168 (normal-b (interpolation-normal i3 k2 k3 nil))
1169 (outer-p (plusp (det (point-x normal-a) (point-y normal-a)
1170 (point-x normal-b) (point-y normal-b))))
1171 (d-a (p* (point-rotate normal-a (/ pi 2)) half-thickness))
1172 (d-b (p* (point-rotate normal-b (/ pi -2)) half-thickness)))
1173 (cond
1174 ((and (not outer-p)
1175 (eq inner-joint :miter)
1176 (straight-line-p i2)
1177 (straight-line-p i3))
1178 ;; Miter inner joint between 2 straight lines
1179 (multiple-value-bind (xi yi)
1180 (line-intersection/delta
1181 (point-x (p+ k2 d-a)) (point-y (p+ k2 d-a))
1182 (point-x normal-a) (point-y normal-a)
1183 (point-x (p+ k2 d-b)) (point-y (p+ k2 d-b))
1184 (point-x normal-b) (point-y normal-b))
1185 (cond
1186 ((and xi
1187 (plusp (+ (* (- xi (point-x k1))
1188 (point-x normal-a))
1189 (* (- yi (point-y k1))
1190 (point-y normal-a))))
1191 (plusp (+ (* (- xi (point-x k3))
1192 (point-x normal-b))
1193 (* (- yi (point-y k3))
1194 (point-y normal-b)))))
1195 ;; ok, intersection point
1196 ;; is behind segments
1197 ;; ends
1198 (extend-to (make-straight-line) (make-point xi yi)))
1200 ;; revert to basic joint
1201 (line-to (p+ k2 d-a))
1202 (line-to (p+ k2 d-b))))))
1203 ((and outer-p
1204 (eq joint :miter)
1205 (straight-line-p i2)
1206 (straight-line-p i3))
1207 ;; Miter outer joint between 2 straight lines
1208 (multiple-value-bind (xi yi)
1209 (line-intersection/delta
1210 (point-x (p+ k2 d-a)) (point-y (p+ k2 d-a))
1211 (point-x normal-a) (point-y normal-a)
1212 (point-x (p+ k2 d-b)) (point-y (p+ k2 d-b))
1213 (point-x normal-b) (point-y normal-b))
1214 (let ((i (make-point xi yi)))
1215 (cond
1216 ((and xi
1217 (<= (point-distance i k2)
1218 (* half-thickness *miter-limit*)))
1219 (line-to (make-point xi yi)))
1221 ;; FIXME: Ugh. My math skill show its
1222 ;; limits. This is probably possible to
1223 ;; compute the same thing with less steps.
1224 (let* ((p (p+ k2 (point-middle d-a d-b)))
1225 (a (point-distance (p+ k2 d-a) i))
1226 (b (- (* half-thickness *miter-limit*)
1227 (point-distance k2 p)))
1228 (c (point-distance p i))
1229 (d (/ (* a b) c))
1230 (p1 (p+ (p+ k2 d-a) (p* normal-a d)))
1231 (p2 (p+ (p+ k2 d-b) (p* normal-b d))))
1232 (line-to p1)
1233 (line-to p2)))))))
1235 (extend-to (if (typep i2 'arc)
1236 (arc i2)
1237 (make-straight-line))
1238 (p+ k2 d-a))
1239 ;; joint
1240 (if outer-p
1241 (ecase joint
1242 ((:none :miter)
1243 (line-to (p+ k2 d-b)))
1244 (:round
1245 (extend-to (make-arc half-thickness half-thickness
1246 :sweep-flag nil)
1247 (p+ k2 d-b))))
1248 (ecase inner-joint
1249 ((:none :miter)
1250 (line-to (p+ k2 d-b)))
1251 (:round
1252 (extend-to (make-arc half-thickness half-thickness
1253 :sweep-flag t)
1254 (p+ k2 d-b)))))))))
1255 (do-contour-half (path new-target first-half-p)
1256 (setf target new-target)
1257 (let ((iterator (filter-distinct (path-iterator-segmented path #'filter-interpolation)
1258 t)))
1259 (flet ((next ()
1260 (path-iterator-next iterator)))
1261 (multiple-value-bind (i1 k1 e1) (next)
1262 (when k1
1263 (cond
1265 (when first-half-p
1266 (do-single k1)))
1268 ;; at least 2 knots
1269 (multiple-value-bind (i2 k2 e2) (next)
1270 (do-first k1 i2 k2)
1271 ;; rest of the path
1272 (unless e2
1273 (loop
1274 (multiple-value-bind (i3 k3 e3) (next)
1275 (do-segment k1 i2 k2 i3 k3)
1276 (shiftf i1 i2 i3)
1277 (shiftf k1 k2 k3)
1278 (when e3
1279 (return)))))
1280 (do-last k1 i2 k2)))))))))
1281 (do-contour-polygon (path new-target first-p)
1282 (setf target new-target)
1283 (let ((iterator (filter-distinct (path-iterator-segmented path #'filter-interpolation))))
1284 (flet ((next ()
1285 (path-iterator-next iterator)))
1286 (multiple-value-bind (i1 k1 e1) (next)
1287 (when k1
1288 (cond
1290 (when first-p
1291 (do-single k1)))
1293 ;; at least 2 knots
1294 (multiple-value-bind (i2 k2 e2) (next)
1295 ;; rest of the path
1296 (let (extra-iteration)
1297 (when e2
1298 (setf extra-iteration 2))
1299 (loop
1300 (multiple-value-bind (i3 k3 e3) (next)
1301 (when (and extra-iteration (zerop extra-iteration))
1302 (return))
1303 (do-segment k1 i2 k2 i3 k3)
1304 (shiftf i1 i2 i3)
1305 (shiftf k1 k2 k3)
1306 (cond
1307 (extra-iteration
1308 (decf extra-iteration))
1310 (setf extra-iteration 2)))))))))))))))
1311 (when (plusp half-thickness)
1312 (ecase (or assume-type (path-type path))
1313 (:open-polyline
1314 (let ((result (create-path :polygon)))
1315 (do-contour-half path result t)
1316 (do-contour-half (path-reversed path) result nil)
1317 (list result)))
1318 (:closed-polyline
1319 (let ((result-a (create-path :polygon))
1320 (result-b (create-path :polygon)))
1321 ;; FIXME: What happen for single knot path?
1322 (do-contour-polygon path result-a t)
1323 (do-contour-polygon (path-reversed path) result-b nil)
1324 (list result-a result-b)))
1325 (:polygon
1326 (let ((result (create-path :polygon)))
1327 (do-contour-polygon path result t)
1328 (list result))))))))
1330 (define-for-multiple-paths stroke-path stroke-path/1)
1332 ;;; Dash
1334 (defun dash-path/1 (path sizes &key (toggle-p nil) (cycle-index 0))
1335 "Dash path. If TOGGLE-P is true, segments of odd indices are
1336 kept, while if TOGGLE-P is false, segments of even indices are
1337 kept. CYCLE indicate where to cycle the SIZES once the end is
1338 reached."
1339 (assert (<= 0 cycle-index (1- (length sizes)))
1340 (cycle-index) "Invalid cycle index")
1341 (assert (loop for size across sizes never (minusp size))
1342 (sizes) "All sizes must be non-negative.")
1343 (assert (loop for size across sizes thereis (plusp size))
1344 (sizes) "At least one size must be positive.")
1345 (flet ((interpolation-filter (interpolation)
1346 (or (not (typep interpolation 'arc))
1347 (/= (slot-value interpolation 'rx)
1348 (slot-value interpolation 'ry)))))
1349 (let (result
1350 (current (create-path :open-polyline))
1351 (current-length 0.0)
1352 (toggle (not toggle-p))
1353 (index 0)
1354 (size (aref sizes 0))
1355 (iterator (path-iterator-segmented path #'interpolation-filter)))
1356 (flet ((flush ()
1357 (when toggle
1358 (push current result))
1359 (setf toggle (not toggle))
1360 (setf current (create-path :open-polyline)
1361 current-length 0.0)
1362 (incf index)
1363 (when (= index (length sizes))
1364 (setf index cycle-index))
1365 (setf size (aref sizes index)))
1366 (extend (interpolation knot length)
1367 (path-extend current interpolation knot)
1368 (incf current-length length)))
1369 (loop
1370 for previous-knot = nil then knot
1371 for stop-p = nil then end-p
1372 for (interpolation knot end-p) = (multiple-value-list (path-iterator-next iterator))
1373 if (not previous-knot)
1374 do (path-reset current knot)
1375 else
1376 do (etypecase interpolation
1377 ((eql :straight-line)
1378 (let* ((delta (p- knot previous-knot))
1379 (length (point-norm delta))
1380 (pos 0.0))
1381 (loop
1382 (let ((missing (- size current-length))
1383 (available (- length pos)))
1384 (when (> missing available)
1385 (extend (make-straight-line) knot available)
1386 (return))
1387 (incf pos missing)
1388 (let ((end (p+ previous-knot (p* delta (/ pos length)))))
1389 (extend (make-straight-line) end missing)
1390 (flush)
1391 (path-reset current end))))))
1392 (arc
1393 (with-slots (rx ry x-axis-rotation large-arc-flag sweep-flag) interpolation
1394 (assert (= rx ry))
1395 (multiple-value-bind (rc nrx nry start-angle delta-angle)
1396 (svg-arc-parameters previous-knot knot rx ry
1397 x-axis-rotation
1398 large-arc-flag
1399 sweep-flag)
1400 (let* ((length (* (abs delta-angle) nrx))
1401 (pos 0.0))
1402 (loop
1403 (let ((missing (- size current-length))
1404 (available (- length pos)))
1405 (when (> missing available)
1406 ;; FIXME: large-arc-flag must be
1407 ;; computed accordingly to the new ends
1408 ;; of the arc!
1409 (extend (make-arc nrx nry
1410 :x-axis-rotation x-axis-rotation
1411 :large-arc-flag nil
1412 :sweep-flag sweep-flag)
1413 knot
1414 available)
1415 (return))
1416 (incf pos missing)
1417 (let ((end (p+
1418 (point-rotate (make-point nrx 0)
1419 (+ x-axis-rotation
1420 (if (plusp delta-angle)
1421 (+ start-angle (/ pos nrx))
1422 (- start-angle (/ pos nrx)))))
1423 rc)))
1424 ;; FIXME: large-arc-flag must be
1425 ;; computed accordingly to the new ends
1426 ;; of the arc!
1427 (extend (make-arc nrx nry
1428 :x-axis-rotation x-axis-rotation
1429 :large-arc-flag nil
1430 :sweep-flag sweep-flag)
1432 missing)
1433 (flush)
1434 (path-reset current end)))))))))
1435 until (if (eq (path-type path) :open-polyline) end-p stop-p))
1436 (flush))
1437 (nreverse result))))
1439 (define-for-multiple-paths dash-path dash-path/1)
1441 ;;; Clip path
1443 (defun clip-path/1 (path x y dx dy)
1444 (let (result
1445 (current (create-path (path-type path)))
1446 (iterator (path-iterator-segmented path)))
1447 (labels ((next ()
1448 (path-iterator-next iterator))
1449 (det (a b c d)
1450 (- (* a d) (* b c)))
1451 (inside-p (p)
1452 (plusp (det (- (point-x p) x)
1453 (- (point-y p) y)
1454 dx dy)))
1455 (clip-left (k1 k2)
1456 (let ((k1-inside-p (when (inside-p k1) t))
1457 (k2-inside-p (when (inside-p k2) t)))
1458 (when k1-inside-p
1459 (path-extend current (make-straight-line) k1))
1460 (when (not (eq k1-inside-p k2-inside-p))
1461 (multiple-value-bind (xi yi)
1462 (line-intersection/delta x y dx dy
1463 (point-x k1) (point-y k1)
1464 (- (point-x k2) (point-x k1))
1465 (- (point-y k2) (point-y k1)))
1466 (when xi
1467 (path-extend current (make-straight-line) (make-point xi yi))))))))
1468 (multiple-value-bind (i1 k1 e1) (next)
1469 (let ((first-knot k1))
1470 (when k1
1471 (cond
1473 (when (inside-p k1)
1474 (path-reset current k1)))
1476 (loop
1477 (multiple-value-bind (i2 k2 e2) (next)
1478 (clip-left k1 k2)
1479 (when e2
1480 (if (eq (path-type path) :open-polyline)
1481 (when (inside-p k2)
1482 (path-extend current (make-straight-line) k2))
1483 (clip-left k2 first-knot))
1484 (return))
1485 (setf i1 i2)
1486 (setf k1 k2)))))))))
1487 (push current result)
1488 result))
1490 (define-for-multiple-paths clip-path clip-path/1)
1492 (defun clip-path/path/1 (path limit)
1493 (let ((iterator (filter-distinct (path-iterator-segmented limit)))
1494 (result (list path)))
1495 (multiple-value-bind (i1 k1 e1) (path-iterator-next iterator)
1496 (declare (ignore i1))
1497 (when (and k1 (not e1))
1498 (let ((stop-p nil))
1499 (loop
1500 (multiple-value-bind (i2 k2 e2) (path-iterator-next iterator)
1501 (declare (ignore i2))
1502 (setq result (loop for path in result
1503 nconc (clip-path path
1504 (point-x k1) (point-y k1)
1505 (point-x (p- k2 k1)) (point-y (p- k2 k1)))))
1506 (when stop-p
1507 (return result))
1508 (when e2
1509 (setf stop-p t))
1510 (setf k1 k2))))))))
1512 (define-for-multiple-paths clip-path/path clip-path/path/1)
1515 ;;; Round path
1517 (defun round-path/1 (path &optional max-radius)
1518 (declare (ignore max-radius))
1519 (list path))
1521 (define-for-multiple-paths round-path round-path/1)