Code

lpe-vonkoch: updated to handle groups + changed parameter interpretation to allow...
[inkscape.git] / src / live_effects / lpe-spiro.cpp
1 #define INKSCAPE_LPE_SPIRO_C
3 /*
4  * Released under GNU GPL, read the file 'COPYING' for more information
5  */
7 #include "live_effects/lpe-spiro.h"
9 #include "display/curve.h"
10 #include "nodepath.h"
11 #include <typeinfo>
12 #include <2geom/pathvector.h>
13 #include <2geom/matrix.h>
14 #include <2geom/bezier-curve.h>
15 #include <2geom/hvlinesegment.h>
16 #include "helper/geom-nodetype.h"
17 #include "helper/geom-curves.h"
19 #include "live_effects/bezctx.h"
20 #include "live_effects/bezctx_intf.h"
21 #include "live_effects/spiro.h"
23 // For handling un-continuous paths:
24 #include "message-stack.h"
25 #include "inkscape.h"
26 #include "desktop.h"
28 typedef struct {
29     bezctx base;
30     SPCurve *curve;
31     int is_open;
32 } bezctx_ink;
34 void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
35 {
36     bezctx_ink *bi = (bezctx_ink *) bc;
37     bi->curve->moveto(x, y);
38 }
40 void bezctx_ink_lineto(bezctx *bc, double x, double y)
41 {
42     bezctx_ink *bi = (bezctx_ink *) bc;
43     bi->curve->lineto(x, y);
44 }
46 void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
47 {
48     bezctx_ink *bi = (bezctx_ink *) bc;
50     double x0, y0;
51     double x1, y1;
52     double x2, y2;
54     Geom::Point last = *(bi->curve->last_point());
55     x0 = last[Geom::X];
56     y0 = last[Geom::Y];
57     x1 = xm + (1./3) * (x0 - xm);
58     y1 = ym + (1./3) * (y0 - ym);
59     x2 = xm + (1./3) * (x3 - xm);
60     y2 = ym + (1./3) * (y3 - ym);
62     bi->curve->curveto(x1, y1, x2, y2, x3, y3);
63 }
65 void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
66                     double x3, double y3)
67 {
68     bezctx_ink *bi = (bezctx_ink *) bc;
69     bi->curve->curveto(x1, y1, x2, y2, x3, y3);
70 }
72 bezctx *
73 new_bezctx_ink(SPCurve *curve) {
74     bezctx_ink *result = g_new(bezctx_ink, 1);
75     result->base.moveto = bezctx_ink_moveto;
76     result->base.lineto = bezctx_ink_lineto;
77     result->base.quadto = bezctx_ink_quadto;
78     result->base.curveto = bezctx_ink_curveto;
79     result->base.mark_knot = NULL;
80     result->curve = curve;
81     return &result->base;
82 }
87 namespace Inkscape {
88 namespace LivePathEffect {
90 LPESpiro::LPESpiro(LivePathEffectObject *lpeobject) :
91     Effect(lpeobject)
92 {
93 }
95 LPESpiro::~LPESpiro()
96 {
97 }
99 void
100 LPESpiro::setup_nodepath(Inkscape::NodePath::Path *np)
102     sp_nodepath_show_handles(np, false);
103 //    sp_nodepath_show_helperpath(np, false);
106 void
107 LPESpiro::doEffect(SPCurve * curve)
109     using Geom::X;
110     using Geom::Y;
112     // Make copy of old path as it is changed during processing
113     Geom::PathVector const original_pathv = curve->get_pathvector();
114     guint len = curve->get_segment_count() + 2;
116     curve->reset();
117     bezctx *bc = new_bezctx_ink(curve);
118     spiro_cp *path = g_new (spiro_cp, len);
119     int ip = 0;
121     for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
122         if (path_it->empty())
123             continue;
125         // start of path
126         {
127             Geom::Point p = path_it->front().pointAt(0);
128             path[ip].x = p[X];
129             path[ip].y = p[Y];
130             path[ip].ty = '{' ;  // for closed paths, this is overwritten
131             ip++;
132         }
134         // midpoints
135         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
136         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());         // outgoing curve
138         Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
139         if (path_it->closed()) {
140             // if the path is closed, maybe we have to stop a bit earlier because the closing line segment has zerolength.
141             if (path_it->back_closed().isDegenerate()) {
142                 // the closing line segment has zero-length. So stop before that one!
143                 curve_endit = path_it->end_open();
144             }
145         }
147         while ( curve_it2 != curve_endit )
148         {
149             /* This deals with the node between curve_it1 and curve_it2.
150              * Loop to end_default (so without last segment), loop ends when curve_it2 hits the end
151              * and then curve_it1 points to end or closing segment */
152             Geom::Point p = curve_it1->finalPoint();
153             path[ip].x = p[X];
154             path[ip].y = p[Y];
156             // Determine type of spiro node this is, determined by the tangents (angles) of the curves
157             // TODO: see if this can be simplified by using /helpers/geom-nodetype.cpp:get_nodetype
158             bool this_is_line = is_straight_curve(*curve_it1);
159             bool next_is_line = is_straight_curve(*curve_it2);
161             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
163             if ( nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM )
164             {
165                 if (this_is_line && !next_is_line) {
166                     path[ip].ty = ']';
167                 } else if (next_is_line && !this_is_line) {
168                     path[ip].ty = '[';
169                 } else {
170                     path[ip].ty = 'c';
171                 }
172             } else {
173                 path[ip].ty = 'v';
174             }
176             ++curve_it1;
177             ++curve_it2;
178             ip++;
179         }
181         // add last point to the spiropath
182         Geom::Point p = curve_it1->finalPoint();
183         path[ip].x = p[X];
184         path[ip].y = p[Y];
185         if (path_it->closed()) {
186             // curve_it1 points to the (visually) closing segment. determine the match between first and this last segment (the closing node)
187             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front());
188             switch (nodetype) {
189                 case Geom::NODE_NONE: // can't happen! but if it does, it means the path isn't closed :-)
190                     path[ip].ty = '}';
191                     ip++;
192                     break;
193                 case Geom::NODE_CUSP:
194                     path[0].ty = path[ip].ty = 'v';
195                     break;
196                 case Geom::NODE_SMOOTH:
197                 case Geom::NODE_SYMM:
198                     path[0].ty = path[ip].ty = 'c';
199                     break;
200             }
201         } else {
202             // set type to path closer
203             path[ip].ty = '}';
204             ip++;
205         }
207         // run subpath through spiro
208         int sp_len = ip;
209         spiro_seg *s = run_spiro(path, sp_len);
210         spiro_to_bpath(s, sp_len, bc);
211         free(s);
212         ip = 0;
213     }
215     g_free (path);
217     // FIXME: refactor the spiro code such that it cannot generate non-continous paths!
218     // sometimes, the code above generates a path that is not continuous: caused by chaotic algorithm?
219     // The continuity error always happens after a lot of curveto calls (a big path probably that spins to infinity?)
220     // if so, undo the effect by resetting the original path.
221     try {
222         curve->get_pathvector() * Geom::identity(); // tests for continuity, this makes LPE Spiro slower of course :-(
223     }
224     catch (Geom::ContinuityError & e) {
225         g_warning("Exception during LPE Spiro execution. \n %s", e.what());
226         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
227             _("An exception occurred during execution of the Spiro Path Effect.") );
228         curve->set_pathvector(original_pathv);
229     }
232 }; //namespace LivePathEffect
233 }; /* namespace Inkscape */
235 /*
236   Local Variables:
237   mode:c++
238   c-file-style:"stroustrup"
239   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
240   indent-tabs-mode:nil
241   fill-column:99
242   End:
243 */
244 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :