1 /**
2 * Inkscape::SVG::PathString - builder for SVG path strings
3 *
4 * Copyright 2007 MenTaLguY <mental@rydia.net>
5 * Copyright 2008 Jasper van de Gronde <th.v.d.gronde@hccnet.nl>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * See the file COPYING for details.
13 *
14 */
16 #ifndef SEEN_INKSCAPE_SVG_PATH_STRING_H
17 #define SEEN_INKSCAPE_SVG_PATH_STRING_H
19 #include <glibmm/ustring.h>
20 #include <string>
21 #include <stdio.h>
22 #include <2geom/point.h>
24 namespace Inkscape {
26 namespace SVG {
28 class PathString {
29 public:
30 PathString();
32 // default copy
33 // default assign
35 std::string const &string() {
36 std::string const &t = tail();
37 final.reserve(commonbase.size()+t.size());
38 final = commonbase;
39 final += tail();
40 return final;
41 }
43 operator std::string const &() {
44 return string();
45 }
47 operator Glib::ustring const () const {
48 return commonbase + tail();
49 }
51 char const *c_str() {
52 return string().c_str();
53 }
55 PathString &moveTo(Geom::Coord x, Geom::Coord y) {
56 return moveTo(Geom::Point(x, y));
57 }
59 PathString &moveTo(Geom::Point p) {
60 _appendOp('M','m');
61 _appendPoint(p, true);
63 _initial_point = _current_point;
64 return *this;
65 }
67 PathString &lineTo(Geom::Coord x, Geom::Coord y) {
68 return lineTo(Geom::Point(x, y));
69 }
71 PathString &lineTo(Geom::Point p) {
72 _appendOp('L','l');
73 _appendPoint(p, true);
74 return *this;
75 }
77 PathString &horizontalLineTo(Geom::Coord x) {
78 _appendOp('H','h');
79 _appendX(x, true);
80 return *this;
81 }
83 PathString &verticalLineTo(Geom::Coord y) {
84 _appendOp('V','v');
85 _appendY(y, true);
86 return *this;
87 }
89 PathString &quadTo(Geom::Coord cx, Geom::Coord cy, Geom::Coord x, Geom::Coord y) {
90 return quadTo(Geom::Point(cx, cy), Geom::Point(x, y));
91 }
93 PathString &quadTo(Geom::Point c, Geom::Point p) {
94 _appendOp('Q','q');
95 _appendPoint(c, false);
96 _appendPoint(p, true);
97 return *this;
98 }
100 PathString &curveTo(Geom::Coord x0, Geom::Coord y0,
101 Geom::Coord x1, Geom::Coord y1,
102 Geom::Coord x, Geom::Coord y)
103 {
104 return curveTo(Geom::Point(x0, y0), Geom::Point(x1, y1), Geom::Point(x, y));
105 }
107 PathString &curveTo(Geom::Point c0, Geom::Point c1, Geom::Point p) {
108 _appendOp('C','c');
109 _appendPoint(c0, false);
110 _appendPoint(c1, false);
111 _appendPoint(p, true);
112 return *this;
113 }
115 /**
116 * \param rot the angle in degrees
117 */
118 PathString &arcTo(Geom::Coord rx, Geom::Coord ry, Geom::Coord rot,
119 bool large_arc, bool sweep,
120 Geom::Point p)
121 {
122 _appendOp('A','a');
123 _appendValue(Geom::Point(rx,ry));
124 _appendValue(rot);
125 _appendFlag(large_arc);
126 _appendFlag(sweep);
127 _appendPoint(p, true);
128 return *this;
129 }
131 PathString &closePath() {
132 _abs_state.appendOp('z');
133 _rel_state.appendOp('z');
134 _current_point = _initial_point;
135 return *this;
136 }
138 private:
140 void _appendOp(char abs_op, char rel_op);
142 void _appendFlag(bool flag) {
143 _abs_state.append(flag);
144 _rel_state.append(flag);
145 }
147 void _appendValue(Geom::Coord v) {
148 _abs_state.append(v);
149 _rel_state.append(v);
150 }
152 void _appendValue(Geom::Point p) {
153 _abs_state.append(p);
154 _rel_state.append(p);
155 }
157 void _appendX(Geom::Coord x, bool sc) {
158 double rx;
159 _abs_state.append(x, rx);
160 _rel_state.appendRelative(rx, _current_point[Geom::X]);
161 if (sc) _current_point[Geom::X] = rx;
162 }
164 void _appendY(Geom::Coord y, bool sc) {
165 double ry;
166 _abs_state.append(y, ry);
167 _rel_state.appendRelative(ry, _current_point[Geom::Y]);
168 if (sc) _current_point[Geom::Y] = ry;
169 }
171 void _appendPoint(Geom::Point p, bool sc) {
172 Geom::Point rp;
173 _abs_state.append(p, rp);
174 _rel_state.appendRelative(rp, _current_point);
175 if (sc) _current_point = rp;
176 }
178 struct State {
179 State() { prevop = 0; switches = 0; }
181 void appendOp(char op) {
182 if (prevop != 0) str += ' ';
183 str += op;
184 prevop = ( op == 'M' ? 'L' : op == 'm' ? 'l' : op );
185 }
187 void append(bool flag) {
188 str += ' ';
189 str += ( flag ? '1' : '0' );
190 }
192 void append(Geom::Coord v);
193 void append(Geom::Point v);
194 void append(Geom::Coord v, Geom::Coord& rv);
195 void append(Geom::Point p, Geom::Point& rp);
196 void appendRelative(Geom::Coord v, Geom::Coord r);
197 void appendRelative(Geom::Point p, Geom::Point r);
199 bool operator<=(const State& s) const {
200 if ( str.size() < s.str.size() ) return true;
201 if ( str.size() > s.str.size() ) return false;
202 if ( switches < s.switches ) return true;
203 if ( switches > s.switches ) return false;
204 return true;
205 }
207 // Note: changing this to Glib::ustring might cause problems in path-string.cpp because it assumes that
208 // size() returns the size of the string in BYTES (and Glib::ustring::resize is terribly slow)
209 std::string str;
210 unsigned int switches;
211 char prevop;
213 private:
214 void appendNumber(double v, int precision=numericprecision, int minexp=minimumexponent);
215 void appendNumber(double v, double &rv, int precision=numericprecision, int minexp=minimumexponent);
216 void appendRelativeCoord(Geom::Coord v, Geom::Coord r);
217 } _abs_state, _rel_state; // State with the last operator being an absolute/relative operator
219 Geom::Point _initial_point;
220 Geom::Point _current_point;
222 // If both states have a common prefix it is stored here.
223 // Separating out the common prefix prevents repeated copying between the states
224 // to cause a quadratic time complexity (in the number of characters/operators)
225 std::string commonbase;
226 std::string final;
227 std::string const &tail() const { return ((_abs_state <= _rel_state || !allow_relative_coordinates) ? _abs_state.str : _rel_state.str); }
229 bool const allow_relative_coordinates;
230 bool const force_repeat_commands;
231 static int numericprecision;
232 static int minimumexponent;
233 };
235 }
237 }
239 #endif
240 /*
241 Local Variables:
242 mode:c++
243 c-file-style:"stroustrup"
244 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
245 indent-tabs-mode:nil
246 fill-column:99
247 End:
248 */
249 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :