1 
2 #include "svg.h"
3 
SVGpath(path_t path,bool normalize,int curve_segments)4 SVGpath::SVGpath(path_t path,bool normalize, int curve_segments) :
5   m_curve_segments(curve_segments),m_pathset()
6 {
7   m_pathset = path_to_vertices(path);
8   if (normalize)
9     normalize_pathset();
10 }
11 
path_to_vertices(path_t path)12 pathset_t SVGpath::path_to_vertices(path_t path)
13 {
14   vec2 cur_point;
15   vec2 start;
16 
17   vec2_ary_t verts;
18   pathset_t curves;
19 
20   vec2 last_cp;
21   bool last_cp_set = false;
22 
23   for(path_t::const_iterator it=path.begin();it<path.end();++it) {
24     path_token_t token = *it;
25     char command = token.first;
26     vec2_ary_t data = token.second;
27 
28     // for "T" shorthand Q curves use last control point, otherwise reset it to NULL
29     switch(command) {
30       case 't':
31       case 'T':
32       case 'q':
33       case 'Q':
34         // nop
35         break;
36       default:
37         last_cp_set = false;
38         break;
39     }
40 
41     // main switch, SVG path ouline => outline vertices list,
42     //  curves are broken into [m_curve_segments] parts
43     switch(command) {
44       case 'M': // moveto - start of curve/contour
45       case 'm': // moveto in relative coords (no change from 'M')
46         if (!verts.empty())
47           curves.push_back(verts);
48         verts.clear();
49         verts.push_back(data[0]);
50         cur_point = data[0];
51         start = cur_point;
52         break;
53       case 'L': // line to
54         verts.push_back(data[0]);
55         cur_point = data[0];
56         break;
57       case 'l': // line to, in relative coordinates
58         verts.push_back(data[0]);
59         cur_point = data[0] + cur_point;
60         break;
61       case 'V': // vertical line from current point
62         {
63           vec2 v(cur_point[0],data[0][0]);
64           verts.push_back(v);
65           cur_point = v;
66         }
67         break;
68       case 'H': // horizontal line from current point
69         {
70           vec2 v(data[0][0],cur_point[1]);
71           verts.push_back(v);
72           cur_point = v;
73         }
74         break;
75       case 'C': // cubic curve
76         {
77           vec2_ary_t curve = Procedural::bezier_cubic_curve(cur_point,data[0],data[1],data[2],m_curve_segments);
78           cur_point = curve.back();
79           copy(curve.begin(), curve.end(), back_inserter(verts));
80         }
81         break;
82       case 'Q': // quadratic curve
83         {
84           vec2_ary_t curve = Procedural::bezier_quadratic_curve(cur_point,data[0],data[1],m_curve_segments);
85           cur_point = curve.back();
86           last_cp = data[0];
87           last_cp_set = true;
88           copy(curve.begin(), curve.end(), back_inserter(verts));
89         }
90         break;
91       case 'T': // continue quadratic curve, with control point being reflection of the last CP
92         {
93           vec2 new_cp;
94           if (last_cp_set) {
95             // reflect the last CP
96             new_cp = cur_point + (cur_point - last_cp);
97           } else {
98             new_cp = cur_point;
99           }
100           vec2_ary_t curve = Procedural::bezier_quadratic_curve(cur_point,new_cp,data[0],m_curve_segments);
101           cur_point = curve.back();
102           copy(curve.begin(), curve.end(), back_inserter(verts));
103           last_cp = new_cp;
104         }
105         break;
106       case 'Z':
107       case 'z':
108         cur_point = start;
109         break;
110       default:
111         printf("unhandled SVG path command '%c'",command);
112         break;
113     }
114   }
115 
116   // collect the last pass, if any
117   if (!verts.empty())
118     curves.push_back(verts);
119 
120   return curves;
121 }
122 
123 // whole path centered to 0,0 and mapped into unit square
normalize_pathset(void)124 void SVGpath::normalize_pathset(void)
125 {
126   vec2 min_p = m_pathset[0][0];
127   vec2 max_p = m_pathset[0][0];
128 
129   for(pathset_t::const_iterator it=m_pathset.begin();it!=m_pathset.end();++it) {
130     vec2_ary_t path = *it;
131     for(vec2_ary_t::const_iterator p_it=path.begin();p_it!=path.end();++p_it) {
132       vec2 p = *p_it;
133       if (p[0] < min_p[0]) min_p[0] = p[0];
134       if (p[0] > max_p[0]) max_p[0] = p[0];
135       if (p[1] < min_p[1]) min_p[1] = p[1];
136       if (p[1] > max_p[1]) max_p[1] = p[1];
137     }
138   }
139 
140   vec2 mid_p = (min_p + max_p) / 2.0;
141 
142   float xsize = fabs(max_p[0] - min_p[0]);
143   float ysize = fabs(max_p[1] - min_p[1]);
144 
145   float divisor = (xsize>ysize) ? xsize : ysize;
146 
147   pathset_t pathset_new;
148   // normalize points
149   for(pathset_t::const_iterator it=m_pathset.begin();it!=m_pathset.end();++it) {
150     vec2_ary_t path = *it;
151     for(vec2_ary_t::iterator p_it=path.begin();p_it!=path.end();++p_it) {
152       vec2 p = *p_it;
153       *p_it = (p - mid_p) / divisor;
154     }
155     pathset_new.push_back(path);
156   }
157   m_pathset.swap(pathset_new);
158 }
159 
get_pathset(void)160 pathset_t SVGpath::get_pathset(void)
161 {
162   return m_pathset;
163 }
164