]> gitweb.factorcode.org Git - factor.git/blob - extra/svg/svg.factor
math.affine-transforms, sequences.squish, and svg vocabs
[factor.git] / extra / svg / svg.factor
1 USING: accessors arrays assocs fry kernel math math.affine-transforms math.constants
2 math.functions math.parser math.vectors memoize peg.ebnf sequences sequences.squish
3 splitting strings xml.data xml.utilities ;
4 IN: svg
5
6 XML-NS: svg-name http://www.w3.org/2000/svg
7 XML-NS: xlink-name http://www.w3.org/1999/xlink
8 XML-NS: sodipodi-name http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd
9 XML-NS: inkscape-name http://www.inkscape.org/namespaces/inkscape
10
11 : svg-string>number ( string -- number )
12     { { CHAR: E CHAR: e } } substitute "e" split1
13     [ string>number ] [ [ string>number 10 swap ^ ] [ 1 ] if* ] bi* *
14     >float ;
15
16 : degrees ( deg -- rad ) pi * 180.0 / ;
17
18 EBNF: svg-transform>affine-transform
19
20 transforms =
21     transform:m comma-wsp+ transforms:n => [[ m n a. ]]
22     | transform
23 transform =
24     matrix
25     | translate
26     | scale
27     | rotate
28     | skewX
29     | skewY
30 matrix =
31     "matrix" wsp* "(" wsp*
32        number:xx comma-wsp
33        number:xy comma-wsp
34        number:yx comma-wsp
35        number:yy comma-wsp
36        number:ox comma-wsp
37        number:oy wsp* ")"
38         => [[ { xx xy } { yx yy } { ox oy } <affine-transform> ]]
39 translate =
40     "translate" wsp* "(" wsp* number:tx ( comma-wsp number:ty => [[ ty ]] )?:ty wsp* ")"
41         => [[ tx ty 0.0 or 2array <translation> ]]
42 scale =
43     "scale" wsp* "(" wsp* number:sx ( comma-wsp number:sy => [[ sy ]] )?:sy wsp* ")"
44         => [[ sx sy sx or <scale> ]]
45 rotate =
46     "rotate" wsp* "(" wsp* number:a ( comma-wsp number:cx comma-wsp number:cy => [[ cx cy 2array ]])?:c wsp* ")"
47         => [[ a degrees <rotation> c [ center-rotation ] when* ]]
48 skewX =
49     "skewX" wsp* "(" wsp* number:a wsp* ")"
50         => [[ { 1.0 0.0 } a degrees tan 1.0 2array { 0.0 0.0 } <affine-transform> ]]
51 skewY =
52     "skewY" wsp* "(" wsp* number:a wsp* ")"
53         => [[ 1.0 a degrees tan 2array { 0.0 1.0 } { 0.0 0.0 } <affine-transform> ]]
54 number =
55     sign? (floating-point-constant | integer-constant) => [[ squish-strings svg-string>number ]]
56 comma-wsp =
57     (wsp+ comma? wsp*) | (comma wsp*)
58 comma =
59     ","
60 integer-constant =
61     digit-sequence
62 floating-point-constant =
63     fractional-constant exponent?
64     | digit-sequence exponent
65 fractional-constant =
66     digit-sequence? "." digit-sequence
67     | digit-sequence "."
68 exponent =
69     ( "e" | "E" ) sign? digit-sequence
70 sign =
71     "+" => [[ f ]] | "-"
72 digit-sequence = [0-9]+ => [[ >string ]]
73 wsp = (" " | "\t" | "\r" | "\n")
74
75 transform-list = wsp* transforms?:t wsp*
76     => [[ t [ identity-transform ] unless* ]]
77
78 ;EBNF
79
80 : tag-transform ( tag -- transform )
81     "transform" svg-name swap at svg-transform>affine-transform ;
82
83 TUPLE: moveto p relative? ;
84 TUPLE: closepath ;
85 TUPLE: lineto p relative? ;
86 TUPLE: horizontal-lineto x relative? ;
87 TUPLE: vertical-lineto y relative? ;
88 TUPLE: curveto p1 p2 p relative? ;
89 TUPLE: smooth-curveto p2 p relative? ;
90 TUPLE: quadratic-bezier-curveto p1 p relative? ;
91 TUPLE: smooth-quadratic-bezier-curveto p relative? ;
92 TUPLE: elliptical-arc radii x-axis-rotation large-arc? sweep? p relative? ;
93
94 : (set-relative) ( args rel -- args )
95     '[ [ _ >>relative? drop ] each ] keep ;
96
97 EBNF: svg-path>array
98
99 moveto-drawto-command-groups =
100     moveto-drawto-command-group:first wsp* moveto-drawto-command-groups:rest
101         => [[ first rest append ]]
102     | moveto-drawto-command-group
103 moveto-drawto-command-group =
104     moveto:m wsp* drawto-commands?:d => [[ m d append ]]
105 drawto-commands =
106     drawto-command:first wsp* drawto-commands:rest => [[ first rest append ]]
107     | drawto-command
108 drawto-command =
109     closepath
110     | lineto
111     | horizontal-lineto
112     | vertical-lineto
113     | curveto
114     | smooth-curveto
115     | quadratic-bezier-curveto
116     | smooth-quadratic-bezier-curveto
117     | elliptical-arc
118 moveto =
119     ("M" => [[ f ]] | "m" => [[ t ]]):rel wsp* moveto-argument-sequence:args
120         => [[ args rel (set-relative) ]]
121 moveto-argument = coordinate-pair => [[ f moveto boa ]]
122 moveto-argument-sequence =
123     moveto-argument:first comma-wsp? lineto-argument-sequence:rest
124         => [[ rest first prefix ]]
125     | moveto-argument => [[ 1array ]]
126 closepath =
127     ("Z" | "z") => [[ drop closepath boa 1array ]]
128 lineto =
129     ("L" => [[ f ]] | "l" => [[ t ]]):rel wsp* lineto-argument-sequence:args
130         => [[ args rel (set-relative) ]]
131 lineto-argument = coordinate-pair => [[ f lineto boa ]]
132 lineto-argument-sequence =
133     lineto-argument:first comma-wsp? lineto-argument-sequence:rest
134         => [[ rest first prefix ]]
135     | lineto-argument => [[ 1array ]]
136 horizontal-lineto =
137     ( "H" => [[ f ]] | "h" => [[ t ]]):rel wsp* horizontal-lineto-argument-sequence:args
138         => [[ args rel (set-relative) ]]
139 horizontal-lineto-argument = coordinate => [[ f horizontal-lineto boa ]]
140 horizontal-lineto-argument-sequence =
141     horizontal-lineto-argument:first comma-wsp? horizontal-lineto-argument-sequence:rest
142         => [[ rest first prefix ]]
143     | horizontal-lineto-argument => [[ 1array ]]
144 vertical-lineto =
145     ( "V" => [[ f ]] | "v" => [[ t ]]):rel wsp* vertical-lineto-argument-sequence:args
146         => [[ args rel (set-relative) ]]
147 vertical-lineto-argument = coordinate => [[ f vertical-lineto boa ]]
148 vertical-lineto-argument-sequence =
149     vertical-lineto-argument:first comma-wsp? vertical-lineto-argument-sequence:rest
150         => [[ rest first prefix ]]
151     | vertical-lineto-argument => [[ 1array ]]
152 curveto =
153     ( "C" => [[ f ]] | "c" => [[ t ]]):rel wsp* curveto-argument-sequence:args
154         => [[ args rel (set-relative) ]]
155 curveto-argument-sequence =
156     curveto-argument:first comma-wsp? curveto-argument-sequence:rest
157         => [[ rest first prefix ]]
158     | curveto-argument => [[ 1array ]]
159 curveto-argument =
160     coordinate-pair:pone comma-wsp? coordinate-pair:ptwo comma-wsp? coordinate-pair:p
161         => [[ pone ptwo p f curveto boa ]]
162 smooth-curveto =
163     ( "S" => [[ f ]] | "s" => [[ t ]] ):rel wsp* smooth-curveto-argument-sequence:args
164         => [[ args rel (set-relative) ]]
165 smooth-curveto-argument-sequence =
166     smooth-curveto-argument:first comma-wsp? smooth-curveto-argument-sequence:rest
167         => [[ rest first prefix ]]
168     | smooth-curveto-argument => [[ 1array ]]
169 smooth-curveto-argument =
170     coordinate-pair:ptwo comma-wsp? coordinate-pair:p
171         => [[ ptwo p f smooth-curveto boa ]]
172 quadratic-bezier-curveto =
173     ( "Q" => [[ f ]] | "q" => [[ t ]] ):rel wsp* quadratic-bezier-curveto-argument-sequence:args
174         => [[ args rel (set-relative) ]]
175 quadratic-bezier-curveto-argument-sequence =
176     quadratic-bezier-curveto-argument:first comma-wsp? 
177         quadratic-bezier-curveto-argument-sequence:rest
178         => [[ rest first prefix ]]
179     | quadratic-bezier-curveto-argument => [[ 1array ]]
180 quadratic-bezier-curveto-argument =
181     coordinate-pair:pone comma-wsp? coordinate-pair:p
182         => [[ pone p f quadratic-bezier-curveto boa ]]
183 smooth-quadratic-bezier-curveto =
184     ( "T" => [[ f ]] | "t" => [[ t ]] ):rel wsp* smooth-quadratic-bezier-curveto-argument-sequence:args
185         => [[ args rel (set-relative) ]]
186 smooth-quadratic-bezier-curveto-argument-sequence =
187     smooth-quadratic-bezier-curveto-argument:first comma-wsp? smooth-quadratic-bezier-curveto-argument-sequence:rest
188         => [[ rest first prefix ]]
189     | smooth-quadratic-bezier-curveto-argument => [[ 1array ]]
190 smooth-quadratic-bezier-curveto-argument = coordinate-pair => [[ f smooth-quadratic-bezier-curveto boa ]]
191 elliptical-arc =
192     ( "A" => [[ f ]] | "a" => [[ t ]] ):rel wsp* elliptical-arc-argument-sequence:args
193         => [[ args rel (set-relative) ]]
194 elliptical-arc-argument-sequence =
195     elliptical-arc-argument:first comma-wsp? elliptical-arc-argument-sequence:rest
196         => [[ rest first prefix ]]
197     | elliptical-arc-argument => [[ 1array ]]
198 elliptical-arc-argument =
199     nonnegative-number:radiix comma-wsp? nonnegative-number:radiiy comma-wsp? 
200         number:xrot comma-wsp flag:large comma-wsp flag:sweep
201         comma-wsp coordinate-pair:p
202         => [[ radiix radiiy 2array xrot large sweep p f elliptical-arc boa ]]
203 coordinate-pair = coordinate:x comma-wsp? coordinate:y => [[ x y 2array ]]
204 coordinate = number
205 nonnegative-number = (floating-point-constant | integer-constant) => [[ squish-strings svg-string>number ]]
206 number = sign? (floating-point-constant | integer-constant) => [[ squish-strings svg-string>number ]]
207 flag = "0" => [[ f ]] | "1" => [[ t ]]
208 comma-wsp = (wsp+ comma? wsp*) | (comma wsp*)
209 comma = ","
210 integer-constant = digit-sequence
211 floating-point-constant = fractional-constant exponent?  | digit-sequence exponent
212 fractional-constant = digit-sequence? "." digit-sequence | digit-sequence "."
213 exponent = ( "e" | "E" ) sign? digit-sequence
214 sign = "+" => [[ drop f ]] | "-"
215 digit-sequence = [0-9]+ => [[ >string ]]
216 wsp = (" " | "\t" | "\r" | "\n")
217
218 svg-path = wsp* moveto-drawto-command-groups?:x wsp* => [[ x ]]
219
220 ;EBNF
221
222 : tag-d ( tag -- d )
223     "d" svg-name swap at svg-path>array ;