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 <2geom/isnan.h>
17 #include "helper/geom-nodetype.h"
18 #include "helper/geom-curves.h"
20 #include "live_effects/bezctx.h"
21 #include "live_effects/bezctx_intf.h"
22 #include "live_effects/spiro.h"
24 // For handling un-continuous paths:
25 #include "message-stack.h"
26 #include "inkscape.h"
27 #include "desktop.h"
29 #define SPIRO_SHOW_INFINITE_COORDINATE_CALLS
31 typedef struct {
32 bezctx base;
33 SPCurve *curve;
34 int is_open;
35 } bezctx_ink;
37 void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
38 {
39 bezctx_ink *bi = (bezctx_ink *) bc;
40 if ( IS_FINITE(x) && IS_FINITE(y) ) {
41 bi->curve->moveto(x, y);
42 }
43 #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
44 else {
45 g_message("lpe moveto not finite");
46 }
47 #endif
48 }
50 void bezctx_ink_lineto(bezctx *bc, double x, double y)
51 {
52 bezctx_ink *bi = (bezctx_ink *) bc;
53 if ( IS_FINITE(x) && IS_FINITE(y) ) {
54 bi->curve->lineto(x, y);
55 }
56 #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
57 else {
58 g_message("lpe lineto not finite");
59 }
60 #endif
61 }
63 void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
64 {
65 bezctx_ink *bi = (bezctx_ink *) bc;
67 if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
68 bi->curve->quadto(xm, ym, x3, y3);
69 }
70 #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
71 else {
72 g_message("lpe quadto not finite");
73 }
74 #endif
75 }
77 void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
78 double x3, double y3)
79 {
80 bezctx_ink *bi = (bezctx_ink *) bc;
81 if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
82 bi->curve->curveto(x1, y1, x2, y2, x3, y3);
83 }
84 #ifdef SPIRO_SHOW_INFINITE_COORDINATE_CALLS
85 else {
86 g_message("lpe curveto not finite");
87 }
88 #endif
89 }
91 bezctx *
92 new_bezctx_ink(SPCurve *curve) {
93 bezctx_ink *result = g_new(bezctx_ink, 1);
94 result->base.moveto = bezctx_ink_moveto;
95 result->base.lineto = bezctx_ink_lineto;
96 result->base.quadto = bezctx_ink_quadto;
97 result->base.curveto = bezctx_ink_curveto;
98 result->base.mark_knot = NULL;
99 result->curve = curve;
100 return &result->base;
101 }
106 namespace Inkscape {
107 namespace LivePathEffect {
109 LPESpiro::LPESpiro(LivePathEffectObject *lpeobject) :
110 Effect(lpeobject)
111 {
112 }
114 LPESpiro::~LPESpiro()
115 {
116 }
118 void
119 LPESpiro::setup_nodepath(Inkscape::NodePath::Path *np)
120 {
121 Effect::setup_nodepath(np);
122 sp_nodepath_show_handles(np, false);
123 // sp_nodepath_show_helperpath(np, false);
124 }
126 void
127 LPESpiro::doEffect(SPCurve * curve)
128 {
129 using Geom::X;
130 using Geom::Y;
132 // Make copy of old path as it is changed during processing
133 Geom::PathVector const original_pathv = curve->get_pathvector();
134 guint len = curve->get_segment_count() + 2;
136 curve->reset();
137 bezctx *bc = new_bezctx_ink(curve);
138 spiro_cp *path = g_new (spiro_cp, len);
139 int ip = 0;
141 for(Geom::PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
142 if (path_it->empty())
143 continue;
145 // start of path
146 {
147 Geom::Point p = path_it->front().pointAt(0);
148 path[ip].x = p[X];
149 path[ip].y = p[Y];
150 path[ip].ty = '{' ; // for closed paths, this is overwritten
151 ip++;
152 }
154 // midpoints
155 Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
156 Geom::Path::const_iterator curve_it2 = ++(path_it->begin()); // outgoing curve
158 Geom::Path::const_iterator curve_endit = path_it->end_default(); // this determines when the loop has to stop
159 if (path_it->closed()) {
160 // if the path is closed, maybe we have to stop a bit earlier because the closing line segment has zerolength.
161 const Geom::Curve &closingline = path_it->back_closed(); // the closing line segment is always of type Geom::LineSegment.
162 if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
163 // closingline.isDegenerate() did not work, because it only checks for *exact* zero length, which goes wrong for relative coordinates and rounding errors...
164 // the closing line segment has zero-length. So stop before that one!
165 curve_endit = path_it->end_open();
166 }
167 }
169 while ( curve_it2 != curve_endit )
170 {
171 /* This deals with the node between curve_it1 and curve_it2.
172 * Loop to end_default (so without last segment), loop ends when curve_it2 hits the end
173 * and then curve_it1 points to end or closing segment */
174 Geom::Point p = curve_it1->finalPoint();
175 path[ip].x = p[X];
176 path[ip].y = p[Y];
178 // Determine type of spiro node this is, determined by the tangents (angles) of the curves
179 // TODO: see if this can be simplified by using /helpers/geom-nodetype.cpp:get_nodetype
180 bool this_is_line = is_straight_curve(*curve_it1);
181 bool next_is_line = is_straight_curve(*curve_it2);
183 Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, *curve_it2);
185 if ( nodetype == Geom::NODE_SMOOTH || nodetype == Geom::NODE_SYMM )
186 {
187 if (this_is_line && !next_is_line) {
188 path[ip].ty = ']';
189 } else if (next_is_line && !this_is_line) {
190 path[ip].ty = '[';
191 } else {
192 path[ip].ty = 'c';
193 }
194 } else {
195 path[ip].ty = 'v';
196 }
198 ++curve_it1;
199 ++curve_it2;
200 ip++;
201 }
203 // add last point to the spiropath
204 Geom::Point p = curve_it1->finalPoint();
205 path[ip].x = p[X];
206 path[ip].y = p[Y];
207 if (path_it->closed()) {
208 // curve_it1 points to the (visually) closing segment. determine the match between first and this last segment (the closing node)
209 Geom::NodeType nodetype = Geom::get_nodetype(*curve_it1, path_it->front());
210 switch (nodetype) {
211 case Geom::NODE_NONE: // can't happen! but if it does, it means the path isn't closed :-)
212 path[ip].ty = '}';
213 ip++;
214 break;
215 case Geom::NODE_CUSP:
216 path[0].ty = path[ip].ty = 'v';
217 break;
218 case Geom::NODE_SMOOTH:
219 case Geom::NODE_SYMM:
220 path[0].ty = path[ip].ty = 'c';
221 break;
222 }
223 } else {
224 // set type to path closer
225 path[ip].ty = '}';
226 ip++;
227 }
229 // run subpath through spiro
230 int sp_len = ip;
231 spiro_seg *s = run_spiro(path, sp_len);
232 spiro_to_bpath(s, sp_len, bc);
233 free(s);
234 ip = 0;
235 }
237 g_free (path);
238 }
240 }; //namespace LivePathEffect
241 }; /* namespace Inkscape */
243 /*
244 Local Variables:
245 mode:c++
246 c-file-style:"stroustrup"
247 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
248 indent-tabs-mode:nil
249 fill-column:99
250 End:
251 */
252 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :