changelog shortlog graph tags branches changeset files file revisions raw help

Mercurial > core / annotate lisp/lib/dat/xml/svg.lisp

changeset 698: 96958d3eb5b0
parent: 2db3aa3a1b31
author: Richard Westhaver <ellis@rwest.io>
date: Fri, 04 Oct 2024 22:04:59 -0400
permissions: -rw-r--r--
description: fixes
231
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
1
 ;;; dat/xml/svg.lisp --- SVG data encoders
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
2
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
3
 ;; based on https://github.com/orthecreedence/cl-svg-polygon
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
4
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
5
 ;;; Code:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
6
 (in-package :dat/svg)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
7
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
8
 ;;; MATRIX
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
9
 (defun id-matrix (dims)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
10
   "Return a square identity matrix with the specified "
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
11
   (let ((array (make-array (* dims dims) :initial-element 0.0 :element-type 'single-float)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
12
     (dotimes (d dims)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
13
       (setf (aref array (* d (1+ dims))) 1.0))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
14
     array))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
15
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
16
 (defun mat* (m1 m2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
17
   "Multiply 3x3 matrices m1 by m2."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
18
   (let ((new (make-array 9 :initial-element 0.0 :element-type 'single-float)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
19
     (dotimes (x 3)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
20
       (dotimes (y 3)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
21
         (let ((prod (+ (* (aref m1 (* x 3)) (aref m2 y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
22
                        (* (aref m1 (+ (* x 3) 1)) (aref m2 (+ y 3)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
23
                        (* (aref m1 (+ (* x 3) 2)) (aref m2 (+ y 6))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
24
           (setf (aref new (+ y (* x 3))) (coerce prod 'single-float)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
25
     new))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
26
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
27
 (defun matv* (m v)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
28
   "Multiple a matrix by a vector, return the resulting vector."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
29
   (let ((new (make-list 3))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
30
         (vx (car v))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
31
         (vy (cadr v))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
32
         (vz 1))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
33
     (dotimes (i 3)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
34
       (setf (nth i new) (+ (* vx (aref m (* i 3)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
35
                            (* vy (aref m (+ (* i 3) 1)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
36
                            (* vz (aref m (+ (* i 3) 2))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
37
     new))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
38
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
39
 (defun m-rotate (degrees &key reverse)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
40
   "Generate a rotation matrix."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
41
   (let* ((matrix (id-matrix 3))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
42
          (angle-rad (* (mod degrees 360) (/ PI 180)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
43
          (cos (coerce (cos angle-rad) 'single-float))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
44
          (sin (coerce (sin angle-rad) 'single-float)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
45
     (setf (aref matrix 0) cos
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
46
           (aref matrix 1) (if reverse sin (- sin))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
47
           (aref matrix 3) (if reverse (- sin) sin)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
48
           (aref matrix 4) cos)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
49
     matrix))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
50
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
51
 (defun m-scale (x y)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
52
   "Generate a scaling matrix."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
53
   (let ((matrix (id-matrix 3)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
54
     (setf (aref matrix 0)  (coerce x 'single-float)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
55
           (aref matrix 4)  (coerce y 'single-float))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
56
     matrix))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
57
   
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
58
 (defun m-translate (x y)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
59
   "Generate a translation matrix."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
60
   (let ((translatrix (id-matrix 3)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
61
     (setf (aref translatrix 2) (coerce x 'single-float)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
62
           (aref translatrix 5) (coerce y 'single-float))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
63
     translatrix))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
64
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
65
 (defun m-skew (degrees &key (axis :x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
66
   "Generate a skew matrix along the :axis axis (:x or :y)."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
67
   (let ((matrix (id-matrix 3))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
68
         (angle-rad (* (mod degrees 360) (/ PI 180)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
69
         (idx (if (equal axis :x) 1 3)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
70
     (setf (aref matrix idx) (coerce (tan angle-rad) 'single-float))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
71
     matrix))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
72
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
73
 ;;; VECTOR
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
74
 (defun norm (v)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
75
   "Calculate a vector norm."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
76
   (expt (loop for x in v sum (expt x 2)) .5))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
77
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
78
 (defun normalize (v)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
79
   "Normalize a 2D vector"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
80
   (let ((x (car v))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
81
         (y (cadr v)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
82
     (let ((norm (norm v)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
83
       (list (/ x norm) (/ y norm)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
84
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
85
 (defun dot-prod (v1 v2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
86
   "Give the dot product of two 2D vectors."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
87
   (+ (* (car v1) (car v2))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
88
      (* (cadr v1) (cadr v2))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
89
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
90
 ;;; TRANSFORMATIONS
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
91
 (defun parse-transform (transform)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
92
   "Turn a transform(...) into an easily-parsable list structure."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
93
   ;; convert "translate(-10,-20) scale(2) rotate(45) translate(5,10)" into
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
94
   ;; "(translate -10 -20) (scale 2) (rotate 45) (translate 5 10)"
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
95
   ;; (ie read-from-string'able)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
96
   (let* ((transform (cl-ppcre::regex-replace-all "([a-z]+)\\(" transform "(\\1 "))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
97
          (transform (cl-ppcre::regex-replace-all "," transform " ")))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
98
     (read-from-string (format nil "( ~a )" transform))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
99
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
100
 (defun get-transformations (object groups)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
101
   "Given an SVG object and a tree of groups, grab all transformations, starting
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
102
    from the top down, into a flat list so they can be applied sequentially."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
103
   (let ((object-transform (getf object :transform))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
104
         (object-group (getf object :group))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
105
         (transformations nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
106
     (labels ((traverse-groups (path groups)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
107
                (dolist (group groups)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
108
                  (when (eql (car (getf group :group)) (car path))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
109
                    (let* ((groups (getf group :groups))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
110
                           (transform (getf group :transform))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
111
                           (transform (if (listp transform) (car transform) transform)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
112
                      (when transform
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
113
                        (push transform transformations))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
114
                      (when groups
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
115
                        (traverse-groups (cdr path) groups)))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
116
       (traverse-groups object-group groups))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
117
     (when object-transform
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
118
       (push object-transform transformations))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
119
     transformations))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
120
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
121
 (defun get-matrix-from-transformation (transformation)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
122
   "Given a transformation in list form (FN ARG1 ARG2 ...), turn it into a matrix
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
123
   which can be multipled to give the overall transformation for an object."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
124
   (macrolet ((idx (var idx default)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
125
                (let ((name (gensym)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
126
                  `(let ((,name (nth ,idx ,var)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
127
                     (if ,name ,name ,default)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
128
     (let ((transformation (if (listp (car transformation))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
129
                               (car transformation)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
130
                               transformation)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
131
       (case (intern (write-to-string (car transformation)) :dat/svg)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
132
         (matrix (vector (nth 1 transformation) (nth 3 transformation) (nth 5 transformation)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
133
                         (nth 2 transformation) (nth 4 transformation) (nth 6 transformation)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
134
                         0 0 1))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
135
         (translate (m-translate (nth 1 transformation) (idx transformation 2 0)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
136
         (scale (m-scale (nth 1 transformation) (idx transformation 2 0)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
137
         (rotate (let ((angle (nth 1 transformation))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
138
                       (center-x (idx transformation 2 0))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
139
                       (center-y (idx transformation 3 0)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
140
                   (if (and (eq 0 center-x) (eq 0 center-y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
141
                       ;; just rotate, no offset funny business
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
142
                       (m-rotate angle)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
143
                       (mat* (mat* (m-translate center-x center-y) (m-rotate angle)) (m-translate (- center-x) (- center-y))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
144
         (skewx (m-skew (nth 1 transformation) :axis :x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
145
         (skewy (m-skew (nth 1 transformation) :axis :y))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
146
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
147
 (defun apply-transformations (points object groups &key scale)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
148
   "Apply all transformations for an object, starting from its top-level group
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
149
   and working down to the object itself."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
150
   (let ((transformations (get-transformations object groups))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
151
         (matrix (id-matrix 3))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
152
         (trans-points nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
153
     (dolist (transform transformations)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
154
       (setf matrix (mat* (get-matrix-from-transformation transform) matrix)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
155
     (when scale
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
156
       (setf matrix (mat* (m-scale (car scale) (cadr scale)) matrix)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
157
     (loop for p across points do
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
158
       (push (butlast (matv* matrix (append p '(1)))) trans-points))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
159
     (values (reverse trans-points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
160
             matrix)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
161
 ;;; PATHS
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
162
 (define-condition unsupported-path-command (error)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
163
   ((text :initarg :text :reader text))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
164
   (:documentation "Thrown when an unsupported action/feature is parsed in a path."))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
165
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
166
 (defun points-close-equal-p (point1 point2 &key (precision 10))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
167
   "Determine if two points are (about) the same. Yes, this is open to
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
168
    interpretation, which is why it takes a precision argument =]."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
169
   (flet ((round-point (point)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
170
            (mapcar (lambda (x) (/ (floor (* x precision)) precision)) point)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
171
     (equal (round-point point1) (round-point point2))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
172
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
173
 (defun replace-char (char rep str)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
174
   "Replace all instances of char with rep in str (non-destructive)."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
175
   (let ((new-str (make-string (length str))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
176
     (loop for i from 0
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
177
           for c across str do
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
178
       (setf (aref new-str i) (if (eq c char)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
179
                                  rep
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
180
                                  c)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
181
     new-str))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
182
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
183
 (defmacro cmd-repeat (args-and-count &body body)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
184
   "Some commands can repeat values with the command, namely the curve commands:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
185
        c,1,2,4,4,5,5 c,8,8,3,4,3,1
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
186
     can be written as
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
187
        c,1,2,4,4,5,5,8,8,3,4,3,1
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
188
   yay. This macro helps alleviate some of the issues caused by this wonderful
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
189
   feature in the get-points-from-path function."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
190
   (let ((i (gensym))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
191
         (a (gensym))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
192
         (args (car args-and-count))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
193
         (count (cadr args-and-count)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
194
     `(dotimes (,i (floor (/ (length ,args) ,count)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
195
        ,@body
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
196
        (setf cur-x (car cur-point)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
197
              cur-y (cadr cur-point))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
198
        (dotimes (,a ,count)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
199
          (setf ,args (cdr ,args))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
200
 (defun get-points-from-path (str-data &key (curve-resolution 10))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
201
   "Given a string describing an SVG path, do our best to retrieve points along
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
202
   that path. Bezier curves are approximated as accurately as needed (defined by
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
203
   :curve-resolution).
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
204
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
205
   If the path generates an arc between x1,y1 and x2,y2, we just ignore the whole
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
206
   arc thing and set x2,y2 as the next point in the path.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
207
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
208
   If Z/z ends the path in the middle, we silently return the current set of 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
209
   points without continuing the path. The idea here is we are generating
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
210
   polygons so breaks or cutouts are not acceptable."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
211
     (let ((commands (print (split "(?=[a-zA-Z])" str-data)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
212
           (scanner-empty-p (cl-ppcre:create-scanner (concatenate 'string "[" *whitespaces* "]") :multi-line-mode t))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
213
           (points nil)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
214
         (parts nil)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
215
         (first-point nil)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
216
         (cur-point '(0 0))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
217
         (last-anchor nil)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
218
         (disconnected nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
219
     (dolist (cmd-str commands)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
220
       ;; this (let) splits the command from "M-113-20" to
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
221
       ;; ("M" "-113" "-20")
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
222
       (let* ((cmd-parts (cl-ppcre:split "( |,|(?<=[A-Za-z])|(?=\-))" cmd-str))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
223
              (cmd (aref (car cmd-parts) 0))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
224
              ;(forget (format t "cmd: ~s~%" cmd-parts))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
225
              (args (remove-if #'null (mapcar (lambda (a)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
226
                                                (if (cl-ppcre:scan scanner-empty-p a)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
227
                                                    nil
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
228
                                                    (read-from-string a)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
229
                                              (cdr cmd-parts))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
230
              (cur-x (car cur-point))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
231
              (cur-y (cadr cur-point)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
232
         ;; process the commands (http://www.w3.org/TR/SVG/paths.html)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
233
         (case (if (eq cmd #\z)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
234
                   (aref (string-upcase cmd) 0)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
235
                   cmd)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
236
           (#\M
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
237
            (cmd-repeat (args 2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
238
              (setf cur-point args)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
239
              (push cur-point points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
240
           (#\m
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
241
            (cmd-repeat (args 2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
242
              (setf cur-point (list (+ cur-x (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
243
                                    (+ cur-y (cadr args))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
244
              (push cur-point points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
245
           (#\L
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
246
            (cmd-repeat (args 2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
247
              (setf cur-point args)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
248
              (push cur-point points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
249
           (#\l
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
250
            (cmd-repeat (args 2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
251
              (setf cur-point (list (+ cur-x (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
252
                                    (+ cur-y (cadr args))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
253
              (push cur-point points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
254
           (#\H
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
255
            (cmd-repeat (args 1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
256
              (setf (car cur-point) (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
257
              (push cur-point points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
258
           (#\h
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
259
            (cmd-repeat (args 1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
260
              (setf (car cur-point) (+ cur-x (car args)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
261
              (push cur-point points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
262
           (#\V
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
263
            (cmd-repeat (args 1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
264
              (setf (cadr cur-point) (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
265
              (push cur-point points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
266
           (#\v
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
267
            (cmd-repeat (args 1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
268
              (setf (cadr cur-point) (+ cur-y (car args)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
269
              (push cur-point points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
270
           (#\C
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
271
            (cmd-repeat (args 6)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
272
              (let ((x1 (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
273
                    (y1 (cadr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
274
                    (x2 (nth 2 args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
275
                    (y2 (nth 3 args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
276
                    (x (nth 4 args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
277
                    (y (nth 5 args)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
278
                (setf points (append (bezier-cubic cur-x cur-y x y x1 y1 x2 y2 :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
279
                      last-anchor (list x2 y2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
280
                      cur-point (list x y)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
281
           (#\c
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
282
            (cmd-repeat (args 6)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
283
              (let ((x1 (+ (car args) cur-x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
284
                    (y1 (+ (cadr args) cur-y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
285
                    (x2 (+ (nth 2 args) cur-x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
286
                    (y2 (+ (nth 3 args) cur-y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
287
                    (x (+ (nth 4 args) cur-x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
288
                    (y (+ (nth 5 args) cur-y)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
289
                (setf points (append (bezier-cubic cur-x cur-y x y x1 y1 x2 y2 :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
290
                      last-anchor (list x2 y2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
291
                      cur-point (list x y)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
292
           (#\S
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
293
            (cmd-repeat (args 4)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
294
              (let ((x1 (+ cur-x (- cur-x (car last-anchor))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
295
                    (y1 (+ cur-y (- cur-y (cadr last-anchor))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
296
                    (x2 (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
297
                    (y2 (cadr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
298
                    (x (nth 2 args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
299
                    (y (nth 3 args)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
300
                (setf points (append (bezier-cubic cur-x cur-y x y x1 y1 x2 y2 :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
301
                      last-anchor (list x2 y2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
302
                      cur-point (list x y)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
303
           (#\s
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
304
            (cmd-repeat (args 4)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
305
              (let ((x1 (+ cur-x (- cur-x (car last-anchor))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
306
                    (y1 (+ cur-y (- cur-y (cadr last-anchor))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
307
                    (x2 (+ (car args) cur-x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
308
                    (y2 (+ (cadr args) cur-y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
309
                    (x (+ (nth 2 args) cur-x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
310
                    (y (+ (nth 3 args) cur-y)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
311
                (setf points (append (bezier-cubic cur-x cur-y x y x1 y1 x2 y2 :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
312
                      last-anchor (list x2 y2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
313
                      cur-point (list x y)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
314
           (#\Q
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
315
            (cmd-repeat (args 4)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
316
              (let ((x1 (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
317
                    (y1 (cadr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
318
                    (x (nth 2 args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
319
                    (y (nth 3 args)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
320
                (setf points (append (bezier-quadratic cur-x cur-y x y x1 y1 :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
321
                      last-anchor (list x1 y1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
322
                      cur-point (list x y)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
323
           (#\q
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
324
            (cmd-repeat (args 4)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
325
              (let ((x1 (+ (car args) cur-x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
326
                    (y1 (+ (cadr args) cur-y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
327
                    (x (+ (nth 2 args) cur-x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
328
                    (y (+ (nth 3 args) cur-y)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
329
                (setf points (append (bezier-quadratic cur-x cur-y x y x1 y1 :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
330
                      last-anchor (list x1 y1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
331
                      cur-point (list x y)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
332
           (#\T
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
333
            (cmd-repeat (args 2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
334
              (let ((x1 (+ cur-x (- cur-x (car last-anchor))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
335
                    (y1 (+ cur-y (- cur-y (cadr last-anchor))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
336
                    (x (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
337
                    (y (cadr args)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
338
                (setf points (append (bezier-quadratic cur-x cur-y x y x1 y1 :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
339
                      last-anchor (list x1 y1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
340
                      cur-point (list x y)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
341
           (#\t
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
342
            (cmd-repeat (args 2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
343
              (let ((x1 (+ cur-x (- cur-x (car last-anchor))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
344
                    (y1 (+ cur-y (- cur-y (cadr last-anchor))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
345
                    (x (+ (car args) cur-x))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
346
                    (y (+ (cadr args) cur-y)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
347
                (setf points (append (bezier-quadratic cur-x cur-y x y x1 y1 :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
348
                      last-anchor (list x1 y1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
349
                      cur-point (list x y)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
350
           (#\A
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
351
            (cmd-repeat (args 7)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
352
              (let ((rx (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
353
                    (ry (cadr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
354
                    (x-rot (caddr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
355
                    (large-arc (cadddr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
356
                    (sweep-flag (cadr (cdddr args)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
357
                    (x1 (car cur-point))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
358
                    (y1 (cadr cur-point))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
359
                    (x2 (+ (caddr (cdddr args)) (car cur-point)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
360
                    (y2 (+ (cadddr (cdddr args)) (cadr cur-point))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
361
                (setf points (append (elliptical-arc x1 y1 x2 y2 rx ry x-rot large-arc sweep-flag :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
362
                      cur-point (list x2 y2)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
363
           (#\a
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
364
            (cmd-repeat (args 7)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
365
              (let ((rx (car args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
366
                    (ry (cadr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
367
                    (x-rot (caddr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
368
                    (large-arc (cadddr args))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
369
                    (sweep-flag (cadr (cdddr args)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
370
                    (x1 (car cur-point))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
371
                    (y1 (cadr cur-point))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
372
                    (x2 (+ (caddr (cdddr args)) (car cur-point)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
373
                    (y2 (+ (cadddr (cdddr args)) (cadr cur-point))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
374
                (setf points (append (elliptical-arc x1 y1 x2 y2 rx ry x-rot large-arc sweep-flag :resolution curve-resolution) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
375
                      cur-point (list x2 y2)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
376
           (#\Z
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
377
            (push (coerce (reverse (if (points-close-equal-p (car points) first-point)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
378
                                       (cdr points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
379
                                       points)) 'vector) parts)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
380
            (setf points nil))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
381
       (when (= (length points) 1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
382
         (setf first-point (car points))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
383
     (when (not (zerop (length points)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
384
       ;; we have unfinished points. add them to the part list
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
385
       (setf disconnected t)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
386
       (push (coerce (reverse (if (points-close-equal-p (car points) first-point)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
387
                                  (cdr points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
388
                                  points)) 'vector) parts))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
389
     (values (reverse parts) disconnected)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
390
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
391
 (defun bezier-cubic (x1 y1 x2 y2 ax1 ay1 ax2 ay2 &key (resolution 10))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
392
   "Sample resolution points off of a cubic bezier curve from (x1,y1) to (x2,y2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
393
   using anchor points (ax1,ay1) (ax2,ay2)."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
394
   (let ((points nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
395
     (flet ((cubic (t-val p0 p1 p2 p3)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
396
              (+ (* (expt (- 1 t-val) 3) p0)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
397
                 (* 3 (expt (- 1 t-val) 2) t-val p1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
398
                 (* 3 (- 1 t-val) (expt t-val 2) p2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
399
                 (* (expt t-val 3) p3))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
400
       (dotimes (i resolution)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
401
         (let ((t-val (* (1+ i) (/ 1 resolution))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
402
           (push (list (cubic t-val x1 ax1 ax2 x2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
403
                       (cubic t-val y1 ay1 ay2 y2))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
404
                 points))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
405
     points))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
406
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
407
 (defun bezier-quadratic (x1 y1 x2 y2 ax1 ay1 &key (resolution 10))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
408
   "Sample resolution points off of a quadratic bezier curve from (x1,y1) to
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
409
   (x2,y2) using anchor points (ax1,ay1) (ax2,ay2)."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
410
   (let ((points nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
411
     (flet ((quadratic (t-val p0 p1 p2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
412
              (+ (* (expt (- 1 t-val) 2) p0)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
413
                 (* 2 (- 1 t-val) t-val p1)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
414
                 (* (expt t-val 2) p2))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
415
       (dotimes (i resolution)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
416
         (let ((t-val (* (1+ i) (/ 1 resolution))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
417
           (push (list (quadratic t-val x1 ax1 x2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
418
                       (quadratic t-val y1 ay1 y2)) points))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
419
     points))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
420
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
421
 (defun elliptical-arc (x1 y1 x2 y2 rx ry x-rotation large-arc-flag sweep-flag &key (resolution 10))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
422
   "Calculate an arc in a path. Yuck."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
423
   (let ((rot-mat-i (m-rotate x-rotation :reverse t))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
424
         (rot-mat (m-rotate x-rotation)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
425
     ;; calculate a bunch of crap, mainly ellipse center x,y
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
426
     (let* ((xy-i (matv* rot-mat-i (list (/ (- x1 x2) 2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
427
                                         (/ (- y1 y2) 2))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
428
            (x-i (car xy-i))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
429
            (y-i (cadr xy-i))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
430
            (rx2 (expt rx 2))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
431
            (ry2 (expt ry 2))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
432
            (x-i2 (expt x-i 2))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
433
            (y-i2 (expt y-i 2))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
434
            (cxy-m (expt (/ (- (* rx2 ry2) (* rx2 y-i2) (* ry2 x-i2))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
435
                            (+ (* rx2 y-i2) (* rx2 x-i2)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
436
                         .5))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
437
            (cxy-m (if (eq large-arc-flag sweep-flag)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
438
                       (- cxy-m)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
439
                       cxy-m))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
440
            (cx-i (* cxy-m (/ (* rx y-i) ry)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
441
            (cy-i (* cxy-m (/ (* ry x-i) (- rx))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
442
            (cxy (matv* rot-mat (list cx-i cy-i)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
443
            (cx (+ (car cxy) (/ (+ x1 x2) 2)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
444
            (cy (+ (cadr cxy) (/ (+ y1 y2) 2))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
445
       (flet ((angle (v1 v2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
446
                (let ((x1 (car v1))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
447
                      (y1 (cadr v1))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
448
                      (x2 (car v2))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
449
                      (y2 (cadr v2)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
450
                  (let ((sign (if (< 0 (- (* x1 y2) (* y1 x2)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
451
                                  1
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
452
                                  -1)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
453
                    (* sign (acos (/ (dot-prod v1 v2)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
454
                                     (* (norm v1) (norm v2)))))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
455
         ;; calculate the start/delta angles
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
456
         (let ((theta-1 (angle (list 1 0) (list (/ (- x-i cx-i) rx)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
457
                                                (/ (- y-i cy-i) ry))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
458
               (theta-delta (angle (list (/ (- x-i cx-i) rx)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
459
                                         (/ (- y-i cy-i) ry))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
460
                                   (list (/ (- (- x-i) cx-i) rx)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
461
                                         (/ (- (- y-i) cy-i) ry)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
462
           (let ((theta-step (/ theta-delta resolution))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
463
                 (points nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
464
             ;; create our points for the ellipse. if this were a true
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
465
             ;; implementation, we'd do radii correction such that x2,y2 always
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
466
             ;; fall ON the ellipse path, but i truly do not care enough to
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
467
             ;; bother. if your SVG generator sucks, take it up with them, or
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
468
             ;; better yet do the proper calculations and issue a pull request.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
469
             (dotimes (i resolution)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
470
               (let ((angle (+ theta-1 (* theta-step i))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
471
                 (let ((xy (matv* rot-mat (list (* rx (cos angle))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
472
                                                (* ry (sin angle))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
473
                   (push (list (+ (car xy) cx)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
474
                               (+ (cadr xy) cy)) points))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
475
             ;; get the last point on there.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
476
             (push (list x2 y2) points)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
477
             (reverse points)))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
478
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
479
 ;;; SVG
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
480
 (define-condition not-an-object (simple-condition) ())
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
481
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
482
 (defun get-points-from-ellipse (x y rx ry &key (curve-resolution 20))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
483
   "Calculate curve-resolution points along an ellipse. Can be used for circles
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
484
   too (when rx == ry)."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
485
   (let ((points (make-array curve-resolution)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
486
     (dotimes (i curve-resolution)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
487
       (let ((rad (* i (/ (* 2 PI) curve-resolution))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
488
         (setf (aref points i)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
489
               (list (coerce (+ x (* (cos rad) rx)) 'single-float)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
490
                     (coerce (+ y (* (sin rad) ry)) 'single-float)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
491
     points))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
492
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
493
 (defmacro with-plist-string-reads (plist bindings &body body)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
494
   "Helper macro to make convert-to-points much more readable. Basically wraps
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
495
   around reading values from a string in a plist and binding the result to a
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
496
   variable:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
497
   
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
498
     (with-plist-string-reads my-plist ((x :x) (y :y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
499
       (+ x y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
500
   
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
501
   Expands to:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
502
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
503
     (let ((x (read-from-string (getf my-plist :x)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
504
           (y (read-from-string (getf my-plist :y))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
505
       (+ x y))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
506
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
507
   Much cleaner."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
508
   `(let ,(loop for binding in bindings collect
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
509
           (list (car binding) `(read-from-string (getf ,plist ,(cadr binding)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
510
      ,@body))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
511
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
512
 (defun convert-to-points (obj &key (curve-resolution 10))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
513
   "Take an object loaded from and SVG file (most likely using parse-svg-nodes)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
514
   and turn it into a set of points describing a polygon. Curves are
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
515
   approximated using :curve-resolution. The higher the resolution, the more
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
516
   accurate the curve will be. This works for paths with bezier curves as well
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
517
   as ellipses and circles."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
518
   (case (intern (string-upcase (getf obj :type)) :dat/svg)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
519
     (rect
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
520
       (with-plist-string-reads obj ((x :x) (y :y) (w :width) (h :height)) 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
521
         (list :points (list (vector (list x y)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
522
                                     (list (+ x w) y)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
523
                                     (list (+ x w) (+ y h))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
524
                                     (list x (+ y h)))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
525
     (polygon
232
2db3aa3a1b31 no need split-sequence
Richard Westhaver <ellis@rwest.io>
parents: 231
diff changeset
526
       (let* ((pairs (uiop:split-string (getf obj :points)))
231
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
527
              (points (loop for pair in pairs
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
528
                            if (find #\, pair) collect (progn (setf (aref pair (search "," pair)) #\space)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
529
                                                              (read-from-string (format nil "(~a)" pair))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
530
         (list :points (list (coerce points 'vector)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
531
     (path
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
532
       (multiple-value-bind (parts disconnected)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
533
           (get-points-from-path (getf obj :d) :curve-resolution curve-resolution)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
534
         (list :points parts :meta (list :disconnected disconnected))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
535
     (ellipse 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
536
       (with-plist-string-reads obj ((x :cx) (y :cy) (rx :rx) (ry :ry))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
537
         (list :points (list (get-points-from-ellipse x y rx ry :curve-resolution curve-resolution)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
538
     (circle
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
539
       (with-plist-string-reads obj ((x :cx) (y :cy) (r :r))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
540
         (list :points (list (get-points-from-ellipse x y r r :curve-resolution curve-resolution)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
541
     (t
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
542
       (error 'not-an-object))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
543
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
544
 (defun get-node-attr (node attr-name)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
545
   "Given a node, get the attribute stored under attr-name."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
546
   (cadr (dat/xml::find-attrib attr-name node)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
547
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
548
 (defun parse-svg-nodes (nodes &key parent-group (next-id 0) save-attributes (group-id-attribute-name "id"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
549
   "Given an SVG doc read via dat/xml:parse, return two things:
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
550
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
551
     1. A list of plist objects describing ALL the objects found in the SVG file.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
552
        Each object stores the group it's part of along with its attributes and
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
553
        transformations.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
554
     2. A list of plist objects describing ALL the groups found, each storing its
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
555
        group id (created if not explicit) and any transformations that group has.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
556
   
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
557
   The idea is that given this data, we can easily generate polygons for each
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
558
   object and then apply transformations to it starting with its top-level group
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
559
   and working down to the object's transformations itself."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
560
   (let ((objs nil)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
561
         (groups nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
562
     (loop for node in (xml-node-children nodes)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
563
           do (let ((tag (xml-node-name node)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
564
                (if (equal tag "g")
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
565
                    (let* ((gid (get-node-attr node group-id-attribute-name))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
566
                           (gid (if gid gid (get-node-attr node "id")))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
567
                           (gid (list (if gid gid (incf next-id))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
568
                           (full-gid (if parent-group
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
569
                                         (append parent-group gid)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
570
                                         gid)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
571
                      (multiple-value-bind (sub-nodes sub-groups) (parse-svg-nodes node
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
572
                                                                                   :parent-group full-gid
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
573
                                                                                   :next-id next-id
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
574
                                                                                   :save-attributes save-attributes
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
575
                                                                                   :group-id-attribute-name group-id-attribute-name)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
576
                        (setf objs (append sub-nodes objs))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
577
                        (push (list :group gid :transform (parse-transform (get-node-attr node "transform")) :groups sub-groups) groups)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
578
                    (let* ((gid parent-group)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
579
                           (obj (list :type tag :group gid))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
580
                           (tagsym (intern (string-upcase tag) :dat/svg))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
581
                           (attrs (append (case tagsym
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
582
                                            (rect (list "x" "y" "width" "height"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
583
                                            (polygon (list "points"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
584
                                            (path (list "d"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
585
                                            (ellipse (list "cx" "cy" "rx" "ry"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
586
                                            (circle (list "cx" "cy" "r"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
587
                                            (t nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
588
                                          save-attributes)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
589
                      (when attrs
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
590
                        (push (append obj (loop for attr in (append attrs (list "transform" "fill" "style" "opacity"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
591
                                                for val = (get-node-attr node attr)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
592
                                                for parsed = (if (and val (equal attr "transform")) (parse-transform val) val)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
593
                                                if parsed append (list (read-from-string (format nil ":~a" attr)) parsed)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
594
                              objs))))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
595
     (values objs groups)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
596
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
597
 (defun file-contents (path)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
598
   "Sucks up an entire file from PATH into a freshly-allocated string,
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
599
   returning two values: the string and the number of bytes read."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
600
   (with-open-file (s path)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
601
     (let* ((len (file-length s))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
602
            (data (make-string len)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
603
       (values data (read-sequence data s)))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
604
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
605
 (defun parse-svg-string (svg-str &key (curve-resolution 10) scale save-attributes (group-id-attribute-name "id"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
606
   "Parses an SVG string, creating the nodes and groups from the SVG, then
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
607
   converts each object into a set of points using the data in that object and
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
608
   the transformations from the groups the object belongs to (and the object's
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
609
   own transformations).
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
610
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
611
   SVG object curve resolutions can be set via :curve-resolution (the higher the
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
612
   value, the more accurate curves are)."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
613
   (multiple-value-bind (nodes groups)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
614
       (parse-svg-nodes (xml-parse svg-str :quash-errors nil) :save-attributes save-attributes :group-id-attribute-name group-id-attribute-name)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
615
     (remove-if
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
616
       'null
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
617
       (mapcar (lambda (node)
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
618
                 (handler-case
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
619
                   (let* ((points-and-meta (convert-to-points node :curve-resolution curve-resolution))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
620
                          (points-and-holes (getf points-and-meta :points))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
621
                          (points (apply-transformations (car points-and-holes) node groups :scale scale))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
622
                          (holes nil))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
623
                     (dolist (hole (cdr points-and-holes))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
624
                       (push (coerce (apply-transformations hole node groups :scale scale) 'vector) holes))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
625
                     (append node (list :point-data (coerce points 'vector) :holes holes :meta (getf points-and-meta :meta))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
626
                   (not-an-object ()
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
627
                     nil)))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
628
               nodes))))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
629
 
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
630
 (defun parse-svg-file (filename &key (curve-resolution 10) scale save-attributes (group-id-attribute-name "id"))
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
631
   "Simple wrapper around parse-svg-string.
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
632
   
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
633
   SVG object curve resolutions can be set via :curve-resolution (the higher the
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
634
   value, the more accurate curves are)."
Richard Westhaver <ellis@rwest.io>
parents:
diff changeset
635
   (parse-svg-string (file-contents filename) :curve-resolution curve-resolution :scale scale :save-attributes save-attributes :group-id-attribute-name group-id-attribute-name))