| 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 |
  |
|---|