Code

72d4a69176da785cecd87f80d89a945d81249922
[inkscape.git] / src / live_effects / n-art-bpath-2geom.cpp
1 #define SEEN_LIBNR_N_ART_BPATH_2GEOM_CPP
3 /** \file
4  * Contains functions to convert from NArtBpath to 2geom's Path
5  *
6  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
7  *
8  * Released under GNU GPL, read the file 'COPYING' for more information
9  */
10  
12 #include "live_effects/n-art-bpath-2geom.h"
13 #include "svg/svg.h"
14 #include <glib.h>
15 #include <2geom/path.h>
16 #include <2geom/svg-path.h>
17 #include <2geom/svg-path-parser.h>
18 #include <2geom/sbasis-to-bezier.h>
20 #define LPE_USE_2GEOM_CONVERSION
22 //##########################################################
24 #include <iostream>
25 #include <sstream>
26 #include <string>
27 #include <boost/format.hpp>
29 static void curve_to_svgd(std::ostream & f, Geom::Curve const* c) {
30     if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const  *>(c)) {
31         f << boost::format("L %g,%g ") % (*line_segment)[1][0] % (*line_segment)[1][1];
32     }
33     else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const  *>(c)) {
34         f << boost::format("Q %g,%g %g,%g ") % (*quadratic_bezier)[1][0] % (*quadratic_bezier)[1][0]  
35                 % (*quadratic_bezier)[2][0] % (*quadratic_bezier)[2][1];
36     }
37     else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const  *>(c)) {
38         f << boost::format("C %g,%g %g,%g %g,%g ") 
39                 % (*cubic_bezier)[1][0] % (*cubic_bezier)[1][1] 
40                 % (*cubic_bezier)[2][0] % (*cubic_bezier)[2][1] 
41                 % (*cubic_bezier)[3][0] % (*cubic_bezier)[3][1];
42     }
43 //    else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc *>(c)) {
44 //        //get at the innards and spit them out as svgd
45 //    }
46     else { 
47         //this case handles sbasis as well as all other curve types
48         Geom::Path sbasis_path = Geom::path_from_sbasis(c->toSBasis(), 0.1);
50         //recurse to convert the new path resulting from the sbasis to svgd
51         for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
52             curve_to_svgd(f, &(*iter));
53         }
54     }
55 }
57 static void write_svgd(std::ostream & f, Geom::Path const &p) {
58     if(f == NULL) {
59         f << "ERRRRRRORRRRR";
60         return;
61     }
63     f << boost::format("M %g,%g ") % p.initialPoint()[0] % p.initialPoint()[1];
64     
65     for(Geom::Path::const_iterator iter(p.begin()), end(p.end()); iter != end; ++iter) {
66         curve_to_svgd(f, &(*iter));
67     }
68     if(p.closed())
69         f << "Z ";
70 }
72 static void write_svgd(std::ostream & f, std::vector<Geom::Path> const &p) {
73     std::vector<Geom::Path>::const_iterator it(p.begin());
74     for(; it != p.end(); it++) {
75         write_svgd(f, *it);
76     }
77 }
79 //##########################################################
80 #ifndef LPE_USE_2GEOM_CONVERSION
82 static
83 Geom::Point point(double *nums, int ix) {
84     return Geom::Point(nums[ix], nums[ix + 1]);
85 }
87 using namespace Geom;
89 class OldPathBuilder {
90 public:
91     OldPathBuilder(double const &c = Geom_EPSILON) : _current_path(NULL) {
92         _continuity_tollerance = c;
93     }
95     void startPathRel(Point const &p0) { startPath(p0 + _current_point); }
96     void startPath(Point const &p0) {
97         _pathset.push_back(Geom::Path());
98         _current_path = &_pathset.back();
99         _initial_point = _current_point = p0;
100     }
102     void pushLineRel(Point const &p0) { pushLine(p0 + _current_point); }
103     void pushLine(Point const &p1) {
104         if (!_current_path) startPath(_current_point);
105         _current_path->appendNew<LineSegment>(p1);
106         _current_point = p1;
107     }
109     void pushLineRel(Point const &p0, Point const &p1) { pushLine(p0 + _current_point, p1 + _current_point); }
110     void pushLine(Point const &p0, Point const &p1) {
111         if(p0 != _current_point) startPath(p0);
112         pushLine(p1);
113     }
115     void pushHorizontalRel(Coord y) { pushHorizontal(y + _current_point[1]); }
116     void pushHorizontal(Coord y) {
117         if (!_current_path) startPath(_current_point);
118         pushLine(Point(_current_point[0], y));
119     }
121     void pushVerticalRel(Coord x) { pushVertical(x + _current_point[0]); }
122     void pushVertical(Coord x) {
123         if (!_current_path) startPath(_current_point);
124         pushLine(Point(x, _current_point[1]));
125     }
127     void pushQuadraticRel(Point const &p1, Point const &p2) { pushQuadratic(p1 + _current_point, p2 + _current_point); }
128     void pushQuadratic(Point const &p1, Point const &p2) {
129         if (!_current_path) startPath(_current_point);
130         _current_path->appendNew<QuadraticBezier>(p1, p2);
131         _current_point = p2;
132     }
134     void pushQuadraticRel(Point const &p0, Point const &p1, Point const &p2) {
135         pushQuadratic(p0 + _current_point, p1 + _current_point, p2 + _current_point);
136     }
137     void pushQuadratic(Point const &p0, Point const &p1, Point const &p2) {
138         if(p0 != _current_point) startPath(p0);
139         pushQuadratic(p1, p2);
140     }
142     void pushCubicRel(Point const &p1, Point const &p2, Point const &p3) {
143         pushCubic(p1 + _current_point, p2 + _current_point, p3 + _current_point);
144     }
145     void pushCubic(Point const &p1, Point const &p2, Point const &p3) {
146         if (!_current_path) startPath(_current_point);
147         _current_path->appendNew<CubicBezier>(p1, p2, p3);
148         _current_point = p3;
149     }
151     void pushCubicRel(Point const &p0, Point const &p1, Point const &p2, Point const &p3) {
152         pushCubic(p0 + _current_point, p1 + _current_point, p2 + _current_point, p3 + _current_point);
153     }
154     void pushCubic(Point const &p0, Point const &p1, Point const &p2, Point const &p3) {
155         if(p0 != _current_point) startPath(p0);
156         pushCubic(p1, p2, p3);
157     }
158 /*
159     void pushEllipseRel(Point const &radii, double rotation, bool large, bool sweep, Point const &end) {
160         pushEllipse(radii, rotation, large, sweep, end + _current_point);
161     }
162     void pushEllipse(Point const &radii, double rotation, bool large, bool sweep, Point const &end) {
163         if (!_current_path) startPath(_current_point);
164         _current_path->append(SVGEllipticalArc(_current_point, radii[0], radii[1], rotation, large, sweep, end));
165         _current_point = end;
166     }
168     void pushEllipseRel(Point const &initial, Point const &radii, double rotation, bool large, bool sweep, Point const &end) {
169         pushEllipse(initial + _current_point, radii, rotation, large, sweep, end + _current_point);
170     }
171     void pushEllipse(Point const &initial, Point const &radii, double rotation, bool large, bool sweep, Point const &end) {
172         if(initial != _current_point) startPath(initial);
173         pushEllipse(radii, rotation, large, sweep, end);
174     }*/
175     
176     void pushSBasis(SBasisCurve &sb) {
177         pushSBasis(sb.sbasis());
178     }
179     void pushSBasis(D2<SBasis> sb) {
180         Point initial = Point(sb[X][0][0], sb[Y][0][0]);
181         if (!_current_path) startPath(_current_point);
182         if (distance(initial, _current_point) > _continuity_tollerance) {
183             startPath(initial);
184         } else if (_current_point != initial) {
185             /* in this case there are three possible options
186                1. connect the points with tiny line segments
187                   this may well translate into bug reports from
188                   users claiming "duplicate or extraneous nodes"
189                2. fudge the initial point of the multidimsb
190                   we've chosen to do this here but question the 
191                   numerical stability of this decision
192                3. translate the whole sbasis so that initial is coincident
193                   with _current_point. this could very well lead
194                   to an accumulation of error for paths that expect 
195                   to meet in the end.
196                perhaps someday an option could be made to allow 
197                the user to choose between these alternatives
198                if the need arises
199             */
200             sb[X][0][0] = _current_point[X];
201             sb[Y][0][0] = _current_point[Y]; 
202         }
203         _current_path->append(sb);
204     }
205     
206     void closePath() {
207         if (_current_path) {
208             _current_path->close(true);
209             _current_path = NULL;
210         }
211         _current_point = _initial_point = Point();
212     }
214     std::vector<Path> const &peek() const { return _pathset; }
216 private:
217     std::vector<Path> _pathset;
218     Path *_current_path;
219     Point _current_point;
220     Point _initial_point;
221     double _continuity_tollerance;
222 };
224 static
225 std::vector<Geom::Path>
226 read_svgd(std::istringstream & s) {
227     assert(s);
229     OldPathBuilder builder;
231     char mode = 0;
233     double nums[7];
234     int cur = 0;
235     while(!s.eof()) {
236         char ch;
237         s >> ch;
238         if((ch >= 'A' and ch <= 'Z') or (ch >= 'a' and ch <= 'z')) {
239             mode = ch;
240             cur = 0;
241         } else if (ch == ' ' or ch == '\t' or ch == '\n' or ch == '\r' or ch == ',')
242             continue;
243         else if ((ch >= '0' and ch <= '9') or ch == '-' or ch == '.' or ch == '+') {
244             s.unget();
245             //TODO: use something else, perhaps.  Unless the svg path number spec matches scan.
246             s >> nums[cur];
247             cur++;
248         }
249         
250         switch(mode) {
251         //FIXME: "If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit lineto commands."
252         case 'm':
253             if(cur >= 2) {
254                 builder.startPathRel(point(nums, 0));
255                 cur = 0;
256             }
257             break;
258         case 'M':
259             if(cur >= 2) {
260                 builder.startPath(point(nums, 0));
261                 cur = 0;
262             }
263             break;
264         case 'l':
265             if(cur >= 2) {
266                 builder.pushLineRel(point(nums, 0));
267                 cur = 0;
268             }
269             break;
270         case 'L':
271             if(cur >= 2) {
272                 builder.pushLine(point(nums, 0));
273                 cur = 0;
274             }
275             break;
276         case 'h':
277             if(cur >= 1) {
278                 builder.pushHorizontalRel(nums[0]);
279                 cur = 0;
280             }
281             break;
282         case 'H':
283             if(cur >= 1) {
284                 builder.pushHorizontal(nums[0]);
285                 cur = 0;
286             }
287             break;
288         case 'v':
289             if(cur >= 1) {
290                 builder.pushVerticalRel(nums[0]);
291                 cur = 0;
292             }
293             break;
294         case 'V':
295             if(cur >= 1) {
296                 builder.pushVertical(nums[0]);
297                 cur = 0;
298             }
299             break;
300         case 'c':
301             if(cur >= 6) {
302                 builder.pushCubicRel(point(nums, 0), point(nums, 2), point(nums, 4));
303                 cur = 0;
304             }
305             break;
306         case 'C':
307             if(cur >= 6) {
308                 builder.pushCubic(point(nums, 0), point(nums, 2), point(nums, 4));
309                 cur = 0;
310             }
311             break;
312         case 'q':
313             if(cur >= 4) {
314                 builder.pushQuadraticRel(point(nums, 0), point(nums, 2));
315                 cur = 0;
316             }
317             break;
318         case 'Q':
319             if(cur >= 4) {
320                 builder.pushQuadratic(point(nums, 0), point(nums, 2));
321                 cur = 0;
322             }
323             break;
324         case 'a':
325             if(cur >= 7) {
326                 //builder.pushEllipseRel(point(nums, 0), nums[2], nums[3] > 0, nums[4] > 0, point(nums, 5));
327                 cur = 0;
328             }
329             break;
330         case 'A':
331             if(cur >= 7) {
332                 //builder.pushEllipse(point(nums, 0), nums[2], nums[3] > 0, nums[4] > 0, point(nums, 5));
333                 cur = 0;
334             }
335             break;
336         case 'z':
337         case 'Z':
338             builder.closePath();
339             break;
340         }
341     }
342     return builder.peek();
346 #endif 
347 //##########################################################
349 std::vector<Geom::Path>  
350 SVGD_to_2GeomPath (char const *svgd)
352     std::vector<Geom::Path> pathv;
353 #ifdef LPE_USE_2GEOM_CONVERSION
354     try {
355         pathv = Geom::parse_svg_path(svgd);
356     }
357     catch (std::runtime_error e) {
358         g_warning("SVGPathParseError: %s", e.what());
359     }
360 #else
361     std::istringstream ss;
362     std::string svgd_string = svgd;
363     ss.str(svgd_string);
364     pathv = read_svgd(ss);
365 #endif
366     return pathv;
370 std::vector<Geom::Path>
371 BPath_to_2GeomPath(NArtBpath const * bpath)
373     std::vector<Geom::Path> pathv;
374     char *svgpath = sp_svg_write_path(bpath);
375     if (!svgpath) {
376         g_warning("BPath_to_2GeomPath - empty path returned");
377         return pathv;
378     }
379     pathv = SVGD_to_2GeomPath(svgpath);
380     g_free(svgpath);
381     return pathv;
384 char *
385 SVGD_from_2GeomPath(std::vector<Geom::Path> const & path)
387     std::ostringstream ss;
388     write_svgd(ss, path);
389     ss.flush();
390     std::string str = ss.str();
391     char * svgd = g_strdup(str.c_str());
392     return svgd;
395 NArtBpath *
396 BPath_from_2GeomPath(std::vector<Geom::Path> const & path)
398     char * svgd = SVGD_from_2GeomPath(path);
399     NArtBpath *bpath = sp_svg_read_path(svgd);
400     g_free(svgd);
401     return bpath;
406 /*
407   Local Variables:
408   mode:c++
409   c-file-style:"stroustrup"
410   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
411   indent-tabs-mode:nil
412   fill-column:99
413   End:
414 */
415 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :