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