Code

Fix for PathString to prevent it from getting into a very, very long copying run...
[inkscape.git] / src / svg / path-string.h
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 "libnr/nr-point.h"
23 #include "libnr/nr-point-ops.h"
25 namespace Inkscape {
27 namespace SVG {
29 class PathString {
30 public:
31     PathString();
33     // default copy
34     // default assign
36     std::string const &string() {
37         std::string const &t = tail();
38         final.reserve(commonbase.size()+t.size());
39         final = commonbase;
40         final += tail();
41         return final;
42     }
44     operator std::string const &() {
45         return string();
46     }
48     operator Glib::ustring const () const {
49         return commonbase + tail();
50     }
52     char const *c_str() {
53         return string().c_str();
54     }
56     PathString &moveTo(NR::Coord x, NR::Coord y) {
57         return moveTo(NR::Point(x, y));
58     }
60     PathString &moveTo(NR::Point p) {
61         _appendOp('M','m');
62         _appendPoint(p, true);
64         _initial_point = _current_point;
65         return *this;
66     }
68     PathString &lineTo(NR::Coord x, NR::Coord y) {
69         return lineTo(NR::Point(x, y));
70     }
72     PathString &lineTo(NR::Point p) {
73         _appendOp('L','l');
74         _appendPoint(p, true);
75         return *this;
76     }
78     PathString &horizontalLineTo(NR::Coord x) {
79         _appendOp('H','h');
80         _appendX(x, true);
81         return *this;
82     }
84     PathString &verticalLineTo(NR::Coord y) {
85         _appendOp('V','v');
86         _appendY(y, true);
87         return *this;
88     }
90     PathString &quadTo(NR::Coord cx, NR::Coord cy, NR::Coord x, NR::Coord y) {
91         return quadTo(NR::Point(cx, cy), NR::Point(x, y));
92     }
94     PathString &quadTo(NR::Point c, NR::Point p) {
95         _appendOp('Q','q');
96         _appendPoint(c, false);
97         _appendPoint(p, true);
98         return *this;
99     }
101     PathString &curveTo(NR::Coord x0, NR::Coord y0,
102                         NR::Coord x1, NR::Coord y1,
103                         NR::Coord x,  NR::Coord y)
104     {
105         return curveTo(NR::Point(x0, y0), NR::Point(x1, y1), NR::Point(x, y));
106     }
108     PathString &curveTo(NR::Point c0, NR::Point c1, NR::Point p) {
109         _appendOp('C','c');
110         _appendPoint(c0, false);
111         _appendPoint(c1, false);
112         _appendPoint(p, true);
113         return *this;
114     }
116     PathString &arcTo(NR::Coord rx, NR::Coord ry, NR::Coord rot,
117                       bool large_arc, bool sweep,
118                       NR::Point p)
119     {
120         _appendOp('A','a');
121         _appendValue(NR::Point(rx,ry));
122         _appendValue(rot);
123         _appendFlag(large_arc);
124         _appendFlag(sweep);
125         _appendPoint(p, true);
126         return *this;
127     }
129     PathString &closePath() {
130         _abs_state.appendOp('z');
131         _rel_state.appendOp('z');
132         _current_point = _initial_point;
133         return *this;
134     }
136 private:
138     void _appendOp(char abs_op, char rel_op);
140     void _appendFlag(bool flag) {
141         _abs_state.append(flag);
142         _rel_state.append(flag);
143     }
145     void _appendValue(NR::Coord v) {
146         _abs_state.append(v);
147         _rel_state.append(v);
148     }
150     void _appendValue(NR::Point p) {
151         _abs_state.append(p);
152         _rel_state.append(p);
153     }
155     void _appendX(NR::Coord x, bool sc) {
156         double rx;
157         _abs_state.append(x, rx);
158         _rel_state.appendRelative(rx, _current_point[NR::X]);
159         if (sc) _current_point[NR::X] = rx;
160     }
162     void _appendY(NR::Coord y, bool sc) {
163         double ry;
164         _abs_state.append(y, ry);
165         _rel_state.appendRelative(ry, _current_point[NR::Y]);
166         if (sc) _current_point[NR::Y] = ry;
167     }
169     void _appendPoint(NR::Point p, bool sc) {
170         NR::Point rp;
171         _abs_state.append(p, rp);
172         _rel_state.appendRelative(rp, _current_point);
173         if (sc) _current_point = rp;
174     }
176     struct State {
177         State() { prevop = 0; switches = 0; }
179         void appendOp(char op) {
180             if (prevop != 0) str += ' ';
181             str += op;
182             prevop = ( op == 'M' ? 'L' : op == 'm' ? 'l' : op );
183         }
185         void append(bool flag) {
186             str += ' ';
187             str += ( flag ? '1' : '0' );
188         }
190         void append(NR::Coord v);
191         void append(NR::Point v);
192         void append(NR::Coord v, NR::Coord& rv);
193         void append(NR::Point p, NR::Point& rp);
194         void appendRelative(NR::Coord v, NR::Coord r);
195         void appendRelative(NR::Point p, NR::Point r);
197         bool operator<=(const State& s) const {
198             if ( str.size() < s.str.size() ) return true;
199             if ( str.size() > s.str.size() ) return false;
200             if ( switches < s.switches ) return true;
201             if ( switches > s.switches ) return false;
202             return true;
203         }
205         // Note: changing this to Glib::ustring might cause problems in path-string.cpp because it assumes that
206         //       size() returns the size of the string in BYTES (and Glib::ustring::resize is terribly slow)
207         std::string str;
208         unsigned int switches;
209         char prevop;
211     private:
212         void appendNumber(double v, int precision=numericprecision, int minexp=minimumexponent);
213         void appendNumber(double v, double &rv, int precision=numericprecision, int minexp=minimumexponent);
214         void appendRelativeCoord(NR::Coord v, NR::Coord r);
215     } _abs_state, _rel_state; // State with the last operator being an absolute/relative operator
217     NR::Point _initial_point;
218     NR::Point _current_point;
220     // If both states have a common prefix it is stored here.
221     // Separating out the common prefix prevents repeated copying between the states
222     // to cause a quadratic time complexity (in the number of characters/operators)
223     std::string commonbase;
224     std::string final;
225     std::string const &tail() const { return ((_abs_state <= _rel_state || !allow_relative_coordinates) ? _abs_state.str : _rel_state.str); }
227     bool const allow_relative_coordinates;
228     bool const force_repeat_commands;
229     static int numericprecision;
230     static int minimumexponent;
231 };
237 #endif
238 /*
239   Local Variables:
240   mode:c++
241   c-file-style:"stroustrup"
242   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
243   indent-tabs-mode:nil
244   fill-column:99
245   End:
246 */
247 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :