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