Code

remove more NR:: from live_effects code
[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"
18 #include "live_effects/bezctx.h"
19 #include "live_effects/bezctx_intf.h"
20 #include "live_effects/spiro.h"
22 // For handling un-continuous paths:
23 #include "message-stack.h"
24 #include "inkscape.h"
25 #include "desktop.h"
27 typedef struct {
28     bezctx base;
29     SPCurve *curve;
30     int is_open;
31 } bezctx_ink;
33 void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
34 {
35     bezctx_ink *bi = (bezctx_ink *) bc;
36     bi->curve->moveto(x, y);
37 }
39 void bezctx_ink_lineto(bezctx *bc, double x, double y)
40 {
41     bezctx_ink *bi = (bezctx_ink *) bc;
42     bi->curve->lineto(x, y);
43 }
45 void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
46 {
47     bezctx_ink *bi = (bezctx_ink *) bc;
49     double x0, y0;
50     double x1, y1;
51     double x2, y2;
53     Geom::Point last = bi->curve->last_point();
54     x0 = last[Geom::X];
55     y0 = last[Geom::Y];
56     x1 = xm + (1./3) * (x0 - xm);
57     y1 = ym + (1./3) * (y0 - ym);
58     x2 = xm + (1./3) * (x3 - xm);
59     y2 = ym + (1./3) * (y3 - ym);
61     bi->curve->curveto(x1, y1, x2, y2, x3, y3);
62 }
64 void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
65                     double x3, double y3)
66 {
67     bezctx_ink *bi = (bezctx_ink *) bc;
68     bi->curve->curveto(x1, y1, x2, y2, x3, y3);
69 }
71 bezctx *
72 new_bezctx_ink(SPCurve *curve) {
73     bezctx_ink *result = g_new(bezctx_ink, 1);
74     result->base.moveto = bezctx_ink_moveto;
75     result->base.lineto = bezctx_ink_lineto;
76     result->base.quadto = bezctx_ink_quadto;
77     result->base.curveto = bezctx_ink_curveto;
78     result->base.mark_knot = NULL;
79     result->curve = curve;
80     return &result->base;
81 }
86 namespace Inkscape {
87 namespace LivePathEffect {
89 LPESpiro::LPESpiro(LivePathEffectObject *lpeobject) :
90     Effect(lpeobject)
91 {
92 }
94 LPESpiro::~LPESpiro()
95 {
96 }
98 void
99 LPESpiro::setup_nodepath(Inkscape::NodePath::Path *np)
101     sp_nodepath_show_handles(np, false);
102 //    sp_nodepath_show_helperpath(np, false);
105 void
106 LPESpiro::doEffect(SPCurve * curve)
108     using Geom::X;
109     using Geom::Y;
111     // Make copy of old path as it is changed during processing
112     Geom::PathVector const original_pathv = curve->get_pathvector();
113     guint len = curve->get_segment_count() + 2;
115     curve->reset();
116     bezctx *bc = new_bezctx_ink(curve);
117     spiro_cp *path = g_new (spiro_cp, len);
118     int ip = 0;
120     for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
121         if (path_it->empty())
122             continue;
124         // start of path
125         {
126             Geom::Point p = path_it->front().pointAt(0);
127             path[ip].x = p[X];
128             path[ip].y = p[Y];
129             path[ip].ty = path_it->closed() ? 'c' : '{' ;  // for closed paths, this might change depending on whether the closing is smooth or not
130             ip++;
131         }
133         // midpoints
134         Geom::Path::const_iterator curve_it1 = path_it->begin();      // incoming curve
135         Geom::Path::const_iterator curve_it2 = ++(path_it->begin());         // outgoing curve
137         Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
138         if (path_it->closed()) {
139             // if the path is closed, maybe we have to stop a bit earlier because the closing line segment has zerolength.
140             if (path_it->back_closed().isDegenerate()) {
141                 // the closing line segment has zero-length. So stop before that one!
142                 curve_endit = path_it->end_open();
143             }
144         }
146         while ( curve_it2 != curve_endit )
147         {
148             /* This deals with the node between curve_it1 and curve_it2.
149              * Loop to end_default (so without last segment), loop ends when curve_it2 hits the end
150              * and then curve_it1 points to end or closing segment */
151             Geom::Point p = curve_it1->finalPoint();
152             path[ip].x = p[X];
153             path[ip].y = p[Y];
155             // Determine type of spiro node this is, determined by the tangents (angles) of the curves
156             // TODO: see if this can be simplified by using /helpers/geom-nodetype.cpp:get_nodetype
157             bool this_is_line = ( dynamic_cast<Geom::LineSegment const *>(&*curve_it1)  ||
158                                   dynamic_cast<Geom::HLineSegment const *>(&*curve_it1) ||
159                                   dynamic_cast<Geom::VLineSegment const *>(&*curve_it1) );
160             bool next_is_line = ( dynamic_cast<Geom::LineSegment const *>(&*curve_it2)  ||
161                                   dynamic_cast<Geom::HLineSegment const *>(&*curve_it2) ||
162                                   dynamic_cast<Geom::VLineSegment const *>(&*curve_it2) );
163             Geom::Point deriv_1 = curve_it1->unitTangentAt(1);
164             Geom::Point deriv_2 = curve_it2->unitTangentAt(0);
165             double this_angle_L2 = Geom::L2(deriv_1);
166             double next_angle_L2 = Geom::L2(deriv_2);
167             double both_angles_L2 = Geom::L2(deriv_1 + deriv_2);
168             if ( (this_angle_L2 > 1e-6) &&
169                  (next_angle_L2 > 1e-6) &&
170                  ((this_angle_L2 + next_angle_L2 - both_angles_L2) < 1e-3) )
171             {
172                 if (this_is_line && !next_is_line) {
173                     path[ip].ty = ']';
174                 } else if (next_is_line && !this_is_line) {
175                     path[ip].ty = '[';
176                 } else {
177                     path[ip].ty = 'c';
178                 }
179             } else {
180                 path[ip].ty = 'v';
181             }
183             ++curve_it1;
184             ++curve_it2;
185             ip++;
186         }
188         // add last point to the spiropath
189         Geom::Point p = curve_it1->finalPoint();
190         path[ip].x = p[X];
191         path[ip].y = p[Y];
192         if (path_it->closed()) {
193             // curve_it1 points to the (visually) closing segment. determine the match between first and this last segment (the closing node)
194             Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front());
195             switch (nodetype) {
196                 case Geom::NODE_NONE: // can't happen! but if it does, it means the path isn't closed :-)
197                     path[ip].ty = '}';
198                     ip++;
199                     break;
200                 case Geom::NODE_CUSP:
201                     path[0].ty = path[ip].ty = 'v';
202                     break;
203                 case Geom::NODE_SMOOTH:
204                 case Geom::NODE_SYMM:
205                     path[0].ty = path[ip].ty = 'c';
206                     break;
207             }
208         } else {
209             // set type to path closer
210             path[ip].ty = '}';
211             ip++;
212         }
214         // run subpath through spiro
215         int sp_len = ip;
216         spiro_seg *s = run_spiro(path, sp_len);
217         spiro_to_bpath(s, sp_len, bc);
218         free(s);
219         ip = 0;
220     }
222     g_free (path);
224     // FIXME: refactor the spiro code such that it cannot generate non-continous paths!
225     // sometimes, the code above generates a path that is not continuous: caused by chaotic algorithm?
226     // The continuity error always happens after a lot of curveto calls (a big path probably that spins to infinity?)
227     // if so, undo the effect by resetting the original path.
228     try {
229         curve->get_pathvector() * Geom::identity(); // tests for continuity, this makes LPE Spiro slower of course :-(
230     }
231     catch (Geom::ContinuityError & e) {
232         g_warning("Exception during LPE Spiro execution. \n %s", e.what());
233         SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
234             _("An exception occurred during execution of the Spiro Path Effect.") );
235         curve->set_pathvector(original_pathv);
236     }
239 }; //namespace LivePathEffect
240 }; /* namespace Inkscape */
242 /*
243   Local Variables:
244   mode:c++
245   c-file-style:"stroustrup"
246   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
247   indent-tabs-mode:nil
248   fill-column:99
249   End:
250 */
251 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :