root/trunk/thirdparty/cl-vectors-0.1.3/paths.lisp

Revision 2190, 64.6 kB (checked in by hhubner, 1 year ago)

add more thirdparty libs

Line 
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.
12
13 ;;;; This file provides facilities to create and manipulate vectorial paths.
14 ;;;;
15 ;;;; Changelogs:
16 ;;;;
17 ;;;; 2007-02-20: first release
18
19 #+nil(error "This file assume that #+NIL is never defined.")
20
21 (in-package #:net.tuxee.paths)
22
23 (defvar *bezier-distance-tolerance* 0.5
24   "The default distance tolerance used when rendering Bezier
25 curves.")
26
27 (defvar *bezier-angle-tolerance* 0.05
28   "The default angle tolerance (in radian) used when rendering
29 Bezier curves")
30
31 (defvar *arc-length-tolerance* 1.0
32   "The maximum length of segment describing an arc.")
33
34 (defvar *miter-limit* 4.0
35   "Miter limit before reverting to bevel joint. Must be >=1.0.")
36
37 ;;;--[ Math utilities ]------------------------------------------------------
38
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)))))))
58
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)))))))
74
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))))))
84
85 (defun line-normal (x1 y1 x2 y2)
86   "Normalize the vector (X2-X1,Y2-Y1). See NORMALIZE."
87   (normalize (- x2 x1) (- y2 y1)))
88
89 ;;;--[ Points ]--------------------------------------------------------------
90
91 ;;; Points are supposed to be immutable
92
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))
97
98 ;;; Utility functions for points
99
100 (defun p+ (p1 p2)
101   (make-point (+ (point-x p1) (point-x p2))
102               (+ (point-y p1) (point-y p2))))
103
104 (defun p- (p1 p2)
105   (make-point (- (point-x p1) (point-x p2))
106               (- (point-y p1) (point-y p2))))
107
108 (defun p* (point scale &optional (scale-y scale))
109   (make-point (* (point-x point) scale)
110               (* (point-y point) scale-y)))
111
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))))))
118
119 (defun point-angle (point)
120   "Compute the angle of POINT relatively to the X axis."
121   (atan (point-y point) (point-x point)))
122
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))))
127
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))))
133
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)))
139
140 ;;;--[ Paths ]---------------------------------------------------------------
141
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)))
146
147 (defun create-path (type)
148   "Create a new path of the given type. The type must be one of
149 the following keyword:
150
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))
156
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))
161
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)))
167
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)))
172
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)))))
183
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))
188
189 (defun path-size (path)
190   "Return the number of knots on the path."
191   (length (path-knots path)))
192
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))))))
199
200 ;;; Iterators
201
202 (defgeneric path-iterator-reset (iterator)
203   (:documentation "Reset the iterator before the first knot."))
204
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."))
213
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))))))
222
223 ;;; Classic iterator
224
225 (defstruct path-iterator-state
226   path index)
227
228 (defun path-iterator (path)
229   (make-path-iterator-state :path path :index nil))
230
231 (defmethod path-iterator-reset ((iterator path-iterator-state))
232   (setf (path-iterator-state-index iterator) nil))
233
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))
242       (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))))))))
250
251 ;;; Segmented iterator
252 ;;;
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.)
259
260 ;;; Note: I use PI prefix instead of PATH-ITERATOR to shorten names.
261
262 (defstruct pi-segmented-state
263   path index predicate end-p queue)
264
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))
269
270 (defmethod path-iterator-reset ((iterator pi-segmented-state))
271   (setf (pi-segmented-state-index iterator) nil
272         (pi-segmented-state-queue iterator) nil))
273
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))
290       (t
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))
301            (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))))))))))
318
319 ;;; Iterate distinct
320
321 ;;; This iterator filter out identical knots. That is, the knots with
322 ;;; the same positions, with any interpolation. (All interpolations
323 ;;; currently implemented are empty when knot around them are not
324 ;;; distinct.)
325
326 ;;; When cyclic-p is true, the first knot of the iterator is the first
327 ;;; knot distinct from the first knot of the reference iterator.
328
329 ;;; When cyclic-p is false, the first knot of the iterator if the
330 ;;; first knot of the reference iterator, and if the path ends with a
331 ;;; knot which is not distinct from the first, it is kept.
332
333 (defclass filter-distinct-state ()
334   ((iterator :initarg :iterator)
335    (cyclic-p :initarg :cyclic-p)
336    (fixed :initarg :fixed)
337    (next :initarg :next)
338    (next-is-end-p)))
339
340 (defun filter-distinct (iterator &optional (preserve-cyclic-end-p nil))
341   (make-instance 'filter-distinct-state
342                  :iterator iterator
343                  :cyclic-p (not preserve-cyclic-end-p)
344                  :fixed nil
345                  :next nil))
346
347 (defmethod path-iterator-reset ((iterator filter-distinct-state))
348   (with-slots ((sub iterator) next next-is-end-p) iterator
349     (path-iterator-reset sub)
350     (setf next nil
351           next-is-end-p nil)))
352
353 (defmethod path-iterator-next ((iterator filter-distinct-state))
354   (with-slots ((sub iterator) cyclic-p fixed next next-is-end-p) iterator
355     (when fixed
356       ;; constant result cached
357       (return-from path-iterator-next (values-list fixed)))
358     (labels ((get-next ()
359                "Get the next knot information as a list (not as
360                multiple values)."
361                (multiple-value-list (path-iterator-next sub)))
362              (distinct-p (a b)
363                "Test if A and B have distinct knots."
364                (not (zerop (point-distance (second a) (second b)))))
365              (move-to-next (previous loop-p)
366                "Move iterator to find a knot distinct from the
367                PREVIOUS. Also indicate if the resulting knot is
368                the first of the sub iterator, and if end of path
369                was encountered. This is needed to compute the
370                effective END-P flag for the resulting iterator."
371                (loop
372                   with first-p = (third previous)
373                   with end-encountered-p = (third previous)
374                   for current = (get-next)
375                   until (or (distinct-p previous current)
376                             (and (not loop-p) first-p))
377                   do (setf first-p (third current))
378                   when (third current)
379                   do (setf end-encountered-p t)
380                   finally (return (values current first-p end-encountered-p)))))
381       (let (result)
382         (unless next
383           ;; First time we iterate.
384           (setf next-is-end-p nil)
385           (let ((first (get-next)))
386             (cond
387               ((or (not (second first))
388                    (third first))
389                ;; It was an empty path or a single knot path. Cache it
390                ;; and returns it for each further iterations.
391                (setf fixed first
392                      result first))
393               (cyclic-p
394                (multiple-value-bind (first-in-cycle first-p end-p) (move-to-next first nil)
395                  (declare (ignore first-p))
396                  (cond
397                    (end-p
398                     (setf (third first) t
399                           fixed first
400                           result first))
401                    (t
402                     (setf next first-in-cycle)))))
403               (t
404                (setf next first)))))
405         (unless result
406           ;; We copy NEXT because we need to modify RESULT, and since
407           ;; NEXT is kept for the next iteration, we take care of not
408           ;; modifying it.
409           (setf result (copy-seq next)
410                 (third result) next-is-end-p)
411           (multiple-value-bind (current first-p end-encountered-p) (move-to-next next cyclic-p)
412             (setf next current)
413             ;; Set end marker
414             (cond
415               (cyclic-p
416                (setf next-is-end-p first-p)
417                (when (and end-encountered-p (not first-p))
418                  (setf (third result) t)))
419               (t
420                (setf (third result) end-encountered-p)))))
421         (values-list result)))))
422
423 ;;; Misc
424
425 (defun path-clone (path)
426   (let ((new-interpolations (copy-seq (path-interpolations path))))
427     (loop for i below (length new-interpolations)
428        do (setf (aref new-interpolations i)
429                 (interpolation-clone (aref new-interpolations i))))
430     (let ((new-path (create-path (path-type path))))
431       (setf (path-knots new-path) (copy-seq (path-knots path))
432             (path-interpolations new-path) new-interpolations)
433       new-path)))
434
435 (defun path-reverse (path)
436   ;; reverse the order of knots
437   (setf (path-knots path) (nreverse (path-knots path)))
438   ;; reverse the order of interpolations 1..n (not the first one,
439   ;; which is the implicit straight line.)
440   (loop with interpolations = (path-interpolations path)
441      with length = (length interpolations)
442      for i from 1 upto (floor (1- length) 2)
443      do (rotatef (aref interpolations i)
444                  (aref interpolations (- length i))))
445   ;; reverse each interpolation
446   (loop for interpolation across (path-interpolations path)
447      do (interpolation-reverse interpolation)))
448
449 (defun path-reversed (path)
450   (let ((new-path (path-clone path)))
451     (path-reverse new-path)
452     new-path))
453
454 (defmacro do-path ((path interpolation knot) &body body)
455   (let ((path-sym (gensym))
456         (knots (gensym))
457         (interpolations (gensym))
458         (index (gensym)))
459     `(symbol-macrolet ((,interpolation (aref ,interpolations ,index))
460                        (,knot (aref ,knots ,index)))
461        (loop
462           with ,path-sym = ,path
463           with ,knots = (path-knots ,path-sym)
464           with ,interpolations = (path-interpolations ,path-sym)
465           for ,index below (length ,knots)
466           do (progn ,@body)))))
467
468 (defun path-translate (path vector)
469   "Translate the whole path accordingly to VECTOR."
470   (if (listp path)
471       (dolist (path-item path)
472         (path-translate path-item vector))
473       (unless (and (zerop (point-x vector))
474                    (zerop (point-y vector)))
475         (do-path (path interpolation knot)
476           (setf knot (p+ knot vector))
477           (interpolation-translate interpolation vector))))
478   path)
479
480 (defun path-rotate (path angle &optional center)
481   "Rotate the whole path by ANGLE radian around CENTER (which is
482 the origin if unspecified.)"
483   (if (listp path)
484       (dolist (path-item path)
485         (path-rotate path-item angle center))
486       (unless (zerop angle)
487         (when center
488           (path-translate path (p* center -1.0)))
489         (do-path (path interpolation knot)
490           (setf knot (point-rotate knot angle))
491           (interpolation-rotate interpolation angle))
492         (when center
493           (path-translate path center))))
494   path)
495
496 (defun path-scale (path scale-x scale-y &optional center)
497   "Scale the whole path by (SCALE-X,SCALE-Y) from CENTER (which
498 is the origin if unspecified.) Warning: not all interpolations
499 support non uniform scaling (when scale-x /= scale-y)."
500   (if (listp path)
501       (dolist (path-item path)
502         (path-scale path-item scale-x scale-y center))
503       (progn
504         (when center
505           (path-translate path (p* center -1.0)))
506         (do-path (path interpolation knot)
507           (setf knot (p* knot scale-x scale-y))
508           (interpolation-scale interpolation scale-x scale-y))
509         (when center
510           (path-translate path center))
511         (when (minusp (* scale-x scale-y))
512           (path-reverse path))))
513   path)
514
515 ;;;--[ Interpolations ]------------------------------------------------------
516
517 (defgeneric interpolation-segment (interpolation k1 k2 function)
518   (:documentation "Segment the path between K1 and K2 described
519 by the INTERPOLATION. Call FUNCTION for each generated point on
520 the interpolation path."))
521
522 (defgeneric interpolation-normal (interpolation k1 k2 side)
523   (:documentation "Compute the normal, going \"outside\" at
524 either K1 (if SIDE is false) or K2 (if SIDE is true). Return NIL
525 if the normal cannot be computed. Return a point otherwise."))
526
527 (defgeneric interpolation-clone (interpolation)
528   (:documentation "Duplicate INTERPOLATION."))
529
530 (defgeneric interpolation-reverse (interpolation)
531   (:documentation "Reverse the path described by INTERPOLATION
532 in-place."))
533
534 (defgeneric interpolation-reversed (interpolation)
535   (:method (interpolation)
536     (let ((cloned-interpolation (interpolation-clone interpolation)))
537       (interpolation-reversed cloned-interpolation)
538       cloned-interpolation))
539   (:documentation "Duplicate and reverse the INTERPOLATION."))
540
541 (defgeneric interpolation-translate (interpolation vector))
542
543 (defgeneric interpolation-rotate (interpolation angle))
544
545 (defgeneric interpolation-scale (interpolation scale-x scale-y))
546
547 ;;; Straight lines
548
549 (defun make-straight-line ()
550   :straight-line)
551
552 (defun straight-line-p (value)
553   (eq value :straight-line))
554
555 (defmethod interpolation-segment ((interpolation (eql :straight-line)) k1 k2 function)
556   (declare (ignore interpolation k1 k2 function)))
557
558 (defmethod interpolation-normal ((interpolation (eql :straight-line)) k1 k2 side)
559   (let* ((x1 (point-x k1))
560          (y1 (point-y k1))
561          (x2 (point-x k2))
562          (y2 (point-y k2))
563          (dx (- x2 x1))
564          (dy (- y2 y1))
565          (dist (sqrt (+ (expt dx 2) (expt dy 2)))))
566     (when (plusp dist)
567       (if side
568           (make-point (/ dx dist)
569                       (/ dy dist))
570           (make-point (- (/ dx dist))
571                       (- (/ dy dist)))))))
572
573 (defmethod interpolation-clone ((interpolation (eql :straight-line)))
574   (make-straight-line))
575
576 (defmethod interpolation-reverse ((interpolation (eql :straight-line)))
577   (declare (ignore interpolation)))
578
579 (defmethod interpolation-translate ((interpolation (eql :straight-line)) vector)
580   (declare (ignore interpolation vector)))
581
582 (defmethod interpolation-rotate ((interpolation (eql :straight-line)) angle)
583   (declare (ignore interpolation angle)))
584
585 (defmethod interpolation-scale ((interpolation (eql :straight-line)) scale-x scale-y)
586   (declare (ignore interpolation scale-x scale-y)))
587
588 ;;; Arc (SVG style)
589
590 (defclass arc ()
591   ((rx :initarg rx)
592    (ry :initarg ry)
593    (x-axis-rotation :initarg x-axis-rotation)
594    (large-arc-flag :initarg large-arc-flag) ; t = choose the longest arc, nil = choose the smallest arc
595    (sweep-flag :initarg sweep-flag))) ; t = arc on the right, nil = arc on the left
596
597 (defun make-arc (rx ry &key (x-axis-rotation 0.0) (large-arc-flag nil) (sweep-flag nil))
598   (make-instance 'arc
599                  'rx rx
600                  'ry ry
601                  'x-axis-rotation x-axis-rotation
602                  'large-arc-flag large-arc-flag
603                  'sweep-flag sweep-flag))
604
605 (defun svg-arc-parameters/reverse (center rx ry rotation start-angle delta-angle)
606   "Conversion from center to endpoint parameterization of SVG arc.
607
608 Returns values P1, P2, LARGE-ARC-FLAG-P, SWEEP-FLAG-P."
609   (let ((p1 (point-rotate (make-point rx 0) start-angle))
610         (p2 (point-rotate (make-point rx 0) (+ start-angle delta-angle))))
611     (flet ((transform (p)
612              (p+
613               (point-rotate
614                (p* p 1.0 (/ rx ry))
615                rotation)
616               center)))
617       (values (transform p1) (transform p2)
618               (> (abs delta-angle) pi)
619               (plusp delta-angle)))))
620
621 (defun svg-arc-parameters (p1 p2 rx ry rotation large-arc-flag-p sweep-flag-p)
622   "Conversion from endpoint to center parameterization of SVG arc.
623
624 Returns values RC, RX, RY, START-ANGLE and DELTA-ANGLE, where RC is
625 the center of the ellipse, RX and RY are the normalized
626 radii (needed if scaling was necessary)."
627   (when (and (/= rx 0)
628              (/= ry 0))
629     ;; [SVG] "If rX or rY have negative signs, these are dropped; the
630     ;; absolute value is used instead."
631     (setf rx (abs rx)
632           ry (abs ry))
633     ;; normalize boolean value to nil/t
634     (setf large-arc-flag-p (when large-arc-flag-p t)
635           sweep-flag-p (when sweep-flag-p t))
636     ;; rp1 and rp2 are p1 and p2 into the coordinate system such
637     ;; that rotation is cancelled and ellipse ratio is 1 (a circle.)
638     (let* ((rp1 (p* (point-rotate p1 (- rotation)) 1.0 (/ rx ry)))
639            (rp2 (p* (point-rotate p2 (- rotation)) 1.0 (/ rx ry)))
640            (rm (point-middle rp1 rp2))
641            (drp1 (p- rm rp1))
642            (dist (point-norm drp1)))
643       (when (plusp dist)
644         (let ((diff-sq (- (expt rx 2) (expt dist 2)))
645               rc)
646           (cond
647             ((not (plusp diff-sq))
648              ;; a/ scale the arc if it is too small to touch the points
649              (setf ry (* dist (/ ry rx))
650                    rx dist
651                    rc rm))
652             (t
653              ;; b/ otherwise compute the center of the circle
654              (let ((d (/ (sqrt diff-sq) dist)))
655                (unless (eq large-arc-flag-p sweep-flag-p)
656                  (setf d (- d)))
657                (setf rc (make-point (+ (point-x rm) (* (point-y drp1) d))
658                                     (- (point-y rm) (* (point-x drp1) d)))))))
659           (let* ((start-angle (point-angle (p- rp1 rc)))
660                  (end-angle (point-angle (p- rp2 rc)))
661                  (delta-angle (- end-angle start-angle)))
662             (when (minusp delta-angle)
663               (incf delta-angle (* 2 pi)))
664             (unless sweep-flag-p
665               (decf delta-angle (* 2 pi)))
666             (values (point-rotate (p* rc 1.0 (/ ry rx)) rotation) rx ry start-angle delta-angle)))))))
667
668 (defmethod interpolation-segment ((interpolation arc) k1 k2 function)
669   (let ((rotation (slot-value interpolation 'x-axis-rotation)))
670     (multiple-value-bind (rc rx ry start-angle delta-angle)
671         (svg-arc-parameters k1 k2
672                             (slot-value interpolation 'rx)
673                             (slot-value interpolation 'ry)
674                             rotation
675                             (slot-value interpolation 'large-arc-flag)
676                             (slot-value interpolation 'sweep-flag))
677       (when rc
678         (loop with n = (max 3 (* (max rx ry) (abs delta-angle)))
679            for i from 1 below n
680            for angle = (+ start-angle (/ (* delta-angle i) n))
681            for p = (p+ (point-rotate
682                         (p*
683                          (make-point (* rx (cos angle))
684                                      (* rx (sin angle)))
685                          1.0 (/ ry rx))
686                         rotation)
687                        rc)
688            do (funcall function p))))))
689
690 (defmethod interpolation-normal ((interpolation arc) k1 k2 side)
691   (let ((rotation (slot-value interpolation 'x-axis-rotation)))
692     (multiple-value-bind (rc rx ry start-angle delta-angle)
693         (svg-arc-parameters k1 k2
694                             (slot-value interpolation 'rx)
695                             (slot-value interpolation 'ry)
696                             rotation
697                             (slot-value interpolation 'large-arc-flag)
698                             (slot-value interpolation 'sweep-flag))
699       (flet ((adjust (normal)
700                (let* ((p (point-rotate (p* normal 1.0 (/ ry rx)) rotation))
701                       (d (point-norm p)))
702                  (when (plusp delta-angle)
703                    (setf d (- d)))
704                  (make-point (/ (point-x p) d) (/ (point-y p) d)))))
705         (when rc
706           (let ((end-angle (+ start-angle delta-angle)))
707             (adjust (if side
708                         (make-point (sin end-angle)
709                                     (- (cos end-angle)))
710                         (make-point (- (sin start-angle))
711                                     (cos start-angle))))))))))
712
713 (defmethod interpolation-clone ((interpolation arc))
714   (make-arc (slot-value interpolation 'rx)
715             (slot-value interpolation 'ry)
716             :x-axis-rotation (slot-value interpolation 'x-axis-rotation)
717             :large-arc-flag (slot-value interpolation 'large-arc-flag)
718             :sweep-flag (slot-value interpolation 'sweep-flag)))
719
720 (defmethod interpolation-reverse ((interpolation arc))
721   (setf (slot-value interpolation 'sweep-flag)
722         (not (slot-value interpolation 'sweep-flag))))
723
724 (defmethod interpolation-translate ((interpolation arc) vector)
725   (declare (ignore interpolation vector)))
726
727 (defmethod interpolation-rotate ((interpolation arc) angle)
728   (incf (slot-value interpolation 'x-axis-rotation) angle))
729
730 (defmethod interpolation-scale ((interpolation arc) scale-x scale-y)
731   ;; FIXME: Return :segment-me if scaling is not possible?
732   (assert (and (not (zerop scale-x))
733                (= scale-x scale-y)))
734   (with-slots (rx ry) interpolation
735     (setf rx (* rx scale-x)
736           ry (* ry scale-y))))
737
738 ;;; Catmull-Rom
739
740 (defclass catmull-rom ()
741   ((head
742     :initarg head)
743    (control-points
744     :initform (make-array 0)
745     :initarg control-points)
746    (queue
747     :initarg queue)))
748
749 (defun make-catmull-rom (head control-points queue)
750   (make-instance 'catmull-rom
751                  'head head
752                  'control-points (coerce control-points 'vector)
753                  'queue queue))
754
755 (defmethod interpolation-segment ((interpolation catmull-rom) k1 k2 function)
756   (let* ((control-points (slot-value interpolation 'control-points))
757          (points (make-array (+ (length control-points) 4))))
758     (replace points control-points :start1 2)
759     (setf (aref points 0) (slot-value interpolation 'head)
760           (aref points 1) k1
761           (aref points (- (length points) 2)) k2
762           (aref points (- (length points) 1)) (slot-value interpolation 'queue))
763     (labels ((eval-catmull-rom (a b c d p)
764                ;; http://www.mvps.org/directx/articles/catmull/
765                (* 0.5
766                   (+ (* 2 b)
767                      (* (+ (- a) c) p)
768                      (* (+ (* 2 a) (* -5 b) (* 4 c) (- d)) (expt p 2))
769                      (* (+ (- a) (* 3 b) (* -3 c) d) (expt p 3))))))
770       (loop for s below (- (length points) 3)
771          for a = (aref points (+ s 0)) then b
772          for b = (aref points (+ s 1)) then c
773          for c = (aref points (+ s 2)) then d
774          for d = (aref points (+ s 3))
775          do (funcall function b)
776          (loop with n = 32
777             for i from 1 below n
778             for p = (/ (coerce i 'float) n)
779             for x = (eval-catmull-rom (point-x a)
780                                       (point-x b)
781                                       (point-x c)
782                                       (point-x d)
783                                       p)
784             for y = (eval-catmull-rom (point-y a)
785                                       (point-y b)
786                                       (point-y c)
787                                       (point-y d)
788                                       p)
789             do (funcall function (make-point x y)))
790          (funcall function c)))))
791
792 (defmethod interpolation-normal ((interpolation catmull-rom) k1 k2 side)
793   (with-slots (head control-points queue) interpolation
794     (let (a b)
795       (if (zerop (length control-points))
796           (if side
797               (setf a k1
798                     b queue)
799               (setf a k2
800                     b head))
801           (if side
802               (setf a (aref control-points (1- (length control-points)))
803                     b queue)
804               (setf a (aref control-points 0)
805                     b head)))
806       (let* ((x1 (point-x a))
807              (y1 (point-y a))
808              (x2 (point-x b))
809              (y2 (point-y b))
810              (dx (- x2 x1))
811              (dy (- y2 y1))
812              (dist (sqrt (+ (expt dx 2) (expt dy 2)))))
813         (when (plusp dist)
814           (make-point (/ dx dist)
815                       (/ dy dist)))))))
816
817 (defmethod interpolation-clone ((interpolation catmull-rom))
818   (make-catmull-rom (slot-value interpolation 'head)
819                     (copy-seq (slot-value interpolation 'control-points))
820                     (slot-value interpolation 'queue)))
821
822 (defmethod interpolation-reverse ((interpolation catmull-rom))
823   (rotatef (slot-value interpolation 'head)
824            (slot-value interpolation 'queue))
825   (nreverse (slot-value interpolation 'control-points)))
826
827 (defmethod interpolation-translate ((interpolation catmull-rom) vector)
828   (with-slots (head control-points queue) interpolation
829     (setf head (p+ head vector)
830           queue (p+ queue vector))
831     (loop for i below (length control-points)
832        do (setf (aref control-points i) (p+ (aref control-points i) vector)))))
833
834 (defmethod interpolation-rotate ((interpolation catmull-rom) angle)
835   (with-slots (head control-points queue) interpolation
836     (setf head (point-rotate head angle)
837           queue (point-rotate queue angle))
838     (loop for i below (length control-points)
839        do (setf (aref control-points i) (point-rotate (aref control-points i) angle)))))
840
841 (defmethod interpolation-scale ((interpolation catmull-rom) scale-x scale-y)
842   (with-slots (head control-points queue) interpolation
843     (setf head (p* head scale-x scale-y)
844           queue (p* queue scale-x scale-y))
845     (loop for i below (length control-points)
846        do (setf (aref control-points i) (p* (aref control-points i)
847                                                      scale-x scale-y)))))
848
849 ;;; Bezier curves
850
851 ;;; [http://www.fho-emden.de/~hoffmann/bezier18122002.pdf]
852
853 (defclass bezier ()
854   ((control-points
855     :initform (make-array 0)
856     :initarg control-points)))
857
858 (defun make-bezier-curve (control-points)
859   (make-instance 'bezier
860                  'control-points (make-array (length control-points)
861                                              :initial-contents control-points)))
862
863 (defun split-bezier (points &optional (position 0.5))
864   "Split the Bezier curve described by POINTS at POSITION into
865 two Bezier curves of the same degree. Returns the curves as 2
866 values."
867   (let* ((size (length points))
868          (stack (make-array size))
869          (current points))
870     (setf (aref stack 0) points)
871     (loop for j from 1 below size
872        for next-size from (1- size) downto 1
873        do (let ((next (make-array next-size)))
874             (loop for i below next-size
875                for a = (aref current i)
876                for b = (aref current (1+ i))
877                do (setf (aref next i)
878                         (make-point (+ (* (- 1.0 position) (point-x a))
879                                        (* position (point-x b)))
880                                     (+ (* (- 1.0 position) (point-y a))
881                                        (* position (point-y b))))))
882             (setf (aref stack j) next
883                   current next)))
884     (let ((left (make-array (length points)))
885           (right (make-array (length points))))
886       (loop for i from 0 below size
887          for j from (1- size) downto 0
888          do (setf (aref left i) (aref (aref stack i) 0)
889                   (aref right i) (aref (aref stack j) i)))
890       (values left right))))
891
892 (defun evaluate-bezier (points position)
893   "Evaluate the point at POSITION on the Bezier curve described
894 by POINTS."
895   (let* ((size (length points))
896          (temp (make-array (1- size))))
897     (loop for current = points then temp
898        for i from (length temp) downto 1
899        do (loop for j below i
900              for a = (aref current j)
901              for b = (aref current (1+ j))
902              do (setf (aref temp j)
903                       (make-point (+ (* (- 1.0 position) (point-x a))
904                                      (* position (point-x b)))
905                                   (+ (* (- 1.0 position) (point-y a))
906                                      (* position (point-y b)))))))
907     (let ((p (aref temp 0)))
908       (values (point-x p) (point-y p)))))
909
910 (defun discrete-bezier-curve (points function
911                               &key
912                               (include-ends t)
913                               (min-subdivide nil)
914                               (max-subdivide 10)
915                               (distance-tolerance *bezier-distance-tolerance*)
916                               (angle-tolerance *bezier-angle-tolerance*))
917   "Subdivize Bezier curve up to certain criterions."
918   ;; FIXME: Handle cusps correctly!
919   (unless min-subdivide
920     (setf min-subdivide (floor (log (1+ (length points)) 2))))
921   (labels ((norm (a b)
922              (sqrt (+ (expt a 2) (expt b 2))))
923            (refine-bezier (points depth)
924              (let* ((a (aref points 0))
925                     (b (aref points (1- (length points))))
926                     (middle-straight (point-middle a b)))
927                (multiple-value-bind (bx by) (evaluate-bezier points 0.5)
928                  (when (or (< depth min-subdivide)
929                            (and (<= depth max-subdivide)
930                                 (or (> (norm (- bx (point-x middle-straight))
931                                              (- by (point-y middle-straight)))
932                                        distance-tolerance)
933                                     (> (abs (- (atan (- by (point-y a)) (- bx (point-x a)))
934                                                (atan (- (point-y b) by) (- (point-x b) bx))))
935                                        angle-tolerance))))
936                    (multiple-value-bind (a b) (split-bezier points 0.5)
937                      (refine-bezier a (1+ depth))
938                      (funcall function bx by)
939                      (refine-bezier b (1+ depth))))))))
940     (when include-ends
941       (let ((p (aref points 0)))
942         (funcall function (point-x p) (point-y p))))
943     (refine-bezier points 0)
944     (when include-ends
945       (let ((p (aref points (1- (length points)))))
946         (funcall function (point-x p) (point-y p)))))
947   (values))
948
949 (defmethod interpolation-segment ((interpolation bezier) k1 k2 function)
950   (with-slots (control-points) interpolation
951     (let ((points (make-array (+ 2 (length control-points)))))
952       (replace points control-points :start1 1)
953       (setf (aref points 0) k1
954             (aref points (1- (length points))) k2)
955       (discrete-bezier-curve points
956                              (lambda (x y) (funcall function (make-point x y)))
957                              :include-ends nil))))
958
959 (defmethod interpolation-normal ((interpolation bezier) k1 k2 side)
960   (let ((control-points (slot-value interpolation 'control-points))
961         a b)
962     (if (zerop (length control-points))
963         (if side
964             (setf a k1
965                   b k2)
966             (setf a k2
967                   b k1))
968         (if side
969             (setf a (aref control-points (1- (length control-points)))
970                   b k2)
971             (setf a (aref control-points 0)
972                   b k1)))
973     (let* ((x1 (point-x a))
974            (y1 (point-y a))
975            (x2 (point-x b))
976            (y2 (point-y b))
977            (dx (- x2 x1))
978            (dy (- y2 y1))
979            (dist (sqrt (+ (expt dx 2) (expt dy 2)))))
980       (when (plusp dist)
981         (make-point (/ dx dist)
982                     (/ dy dist))))))
983
984 (defmethod interpolation-clone ((interpolation bezier))
985   (let ((control-points (copy-seq (slot-value interpolation 'control-points))))
986     (loop for i below (length control-points)
987        do (setf (aref control-points i) (aref control-points i)))
988     (make-bezier-curve control-points)))
989
990 (defmethod interpolation-reverse ((interpolation bezier))
991   (nreverse (slot-value interpolation 'control-points)))
992
993 (defmethod interpolation-translate ((interpolation bezier) vector)
994   (with-slots (control-points) interpolation
995     (loop for i below (length control-points)
996        do (setf (aref control-points i) (p+ (aref control-points i) vector)))))
997
998 (defmethod interpolation-rotate ((interpolation bezier) angle)
999   (with-slots (control-points) interpolation
1000     (loop for i below (length control-points)
1001        do (setf (aref control-points i) (point-rotate (aref control-points i) angle)))))
1002
1003 (defmethod interpolation-scale ((interpolation bezier) scale-x scale-y)
1004   (with-slots (control-points) interpolation
1005     (loop for i below (length control-points)
1006        do (setf (aref control-points i) (p* (aref control-points i)
1007                                                      scale-x scale-y)))))
1008
1009 ;;;--[ Building paths ]------------------------------------------------------
1010
1011 (defun make-discrete-path (path)
1012   "Construct a path with only straight lines."
1013   (let ((result (create-path (path-type path)))
1014         (knots (path-knots path))
1015         (interpolations (path-interpolations path)))
1016     (when (plusp (length knots))
1017       ;; nicer, but slower too.. (But not profiled. Premature optimization?)
1018       #+nil(loop with iterator = (path-iterator-segmented path)
1019               for (interpolation knot end-p) = (multiple-value-list (path-iterator-next iterator))
1020               do (path-extend result interpolation knot)
1021               until end-p)
1022       (path-reset result (aref knots 0))
1023       (loop
1024          for i below (1- (length knots))
1025          for k1 = (aref knots i)
1026          for k2 = (aref knots (1+ i))
1027          for interpolation = (aref interpolations (1+ i))
1028          do (interpolation-segment interpolation k1 k2
1029                                    (lambda (knot)
1030                                      (path-extend result
1031                                                   (make-straight-line)
1032                                                   knot)))
1033          do (path-extend result (make-straight-line) k2)
1034          finally (unless (eq (path-type path) :open-polyline)
1035                    (interpolation-segment (aref interpolations 0) k2 (aref knots 0)
1036                                           (lambda (knot)
1037                                             (path-extend result
1038                                                          (make-straight-line)
1039