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"
8 #include "display/curve.h"
9 #include <libnr/n-art-bpath.h>
10 #include "nodepath.h"
12 #include "live_effects/bezctx.h"
13 #include "live_effects/bezctx_intf.h"
14 #include "live_effects/spiro.h"
16 // For handling un-continuous paths:
17 #include <2geom/pathvector.h>
18 #include <2geom/matrix.h>
19 #include "message-stack.h"
20 #include "inkscape.h"
21 #include "desktop.h"
23 typedef struct {
24 bezctx base;
25 SPCurve *curve;
26 int is_open;
27 } bezctx_ink;
29 void bezctx_ink_moveto(bezctx *bc, double x, double y, int /*is_open*/)
30 {
31 bezctx_ink *bi = (bezctx_ink *) bc;
32 bi->curve->moveto(x, y);
33 }
35 void bezctx_ink_lineto(bezctx *bc, double x, double y)
36 {
37 bezctx_ink *bi = (bezctx_ink *) bc;
38 bi->curve->lineto(x, y);
39 }
41 void bezctx_ink_quadto(bezctx *bc, double xm, double ym, double x3, double y3)
42 {
43 bezctx_ink *bi = (bezctx_ink *) bc;
45 double x0, y0;
46 double x1, y1;
47 double x2, y2;
49 NR::Point last = bi->curve->last_point();
50 x0 = last[NR::X];
51 y0 = last[NR::Y];
52 x1 = xm + (1./3) * (x0 - xm);
53 y1 = ym + (1./3) * (y0 - ym);
54 x2 = xm + (1./3) * (x3 - xm);
55 y2 = ym + (1./3) * (y3 - ym);
57 bi->curve->curveto(x1, y1, x2, y2, x3, y3);
58 }
60 void bezctx_ink_curveto(bezctx *bc, double x1, double y1, double x2, double y2,
61 double x3, double y3)
62 {
63 bezctx_ink *bi = (bezctx_ink *) bc;
64 bi->curve->curveto(x1, y1, x2, y2, x3, y3);
65 }
67 bezctx *
68 new_bezctx_ink(SPCurve *curve) {
69 bezctx_ink *result = g_new(bezctx_ink, 1);
70 result->base.moveto = bezctx_ink_moveto;
71 result->base.lineto = bezctx_ink_lineto;
72 result->base.quadto = bezctx_ink_quadto;
73 result->base.curveto = bezctx_ink_curveto;
74 result->base.mark_knot = NULL;
75 result->curve = curve;
76 return &result->base;
77 }
82 namespace Inkscape {
83 namespace LivePathEffect {
85 LPESpiro::LPESpiro(LivePathEffectObject *lpeobject) :
86 Effect(lpeobject)
87 {
88 }
90 LPESpiro::~LPESpiro()
91 {
92 }
94 void
95 LPESpiro::setup_nodepath(Inkscape::NodePath::Path *np)
96 {
97 sp_nodepath_show_handles(np, false);
98 // sp_nodepath_show_helperpath(np, false);
99 }
101 void
102 LPESpiro::doEffect(SPCurve * curve)
103 {
104 Geom::PathVector original_pathv = curve->get_pathvector();
106 SPCurve *csrc = curve->copy();
107 curve->reset();
108 bezctx *bc = new_bezctx_ink(curve);
109 int len = SP_CURVE_LENGTH(csrc);
110 spiro_cp *path = g_new (spiro_cp, len + 1);
111 NArtBpath const *bpath = csrc->get_bpath();
112 int ib = 0;
113 int ip = 0;
114 bool closed = false;
115 NR::Point pt(0, 0);
116 NArtBpath const *first_in_subpath = NULL;
117 while(ib <= len) {
118 path [ip].x = bpath[ib].x3;
119 path [ip].y = bpath[ib].y3;
120 // std::cout << "==" << bpath[ib].code << " ip" << ip << " ib" << ib << "\n";
121 if (bpath[ib].code == NR_END || bpath[ib].code == NR_MOVETO_OPEN || bpath[ib].code == NR_MOVETO) {
122 if (ip != 0) { // run prev subpath
123 int sp_len = 0;
124 if (!closed) {
125 path[ip - 1].ty = '}';
126 sp_len = ip;
127 } else {
128 sp_len = ip - 1;
129 }
130 spiro_seg *s = NULL;
131 //for (int j = 0; j <= sp_len; j ++) printf ("%c\n", path[j].ty);
132 s = run_spiro(path, sp_len);
133 spiro_to_bpath(s, sp_len, bc);
134 free(s);
135 path[0].x = path[ip].x;
136 path[0].y = path[ip].y;
137 ip = 0;
138 }
139 if (bpath[ib].code == NR_MOVETO_OPEN) {
140 closed = false;
141 path[ip].ty = '{';
142 } else {
143 closed = true;
144 if (ib < len)
145 first_in_subpath = &(bpath[ib + 1]);
146 path[ip].ty = 'c';
147 }
148 } else {
149 // this point is not last, so makes sense to find a proper type for it
150 NArtBpath const *next = NULL;
151 if (ib < len && (bpath[ib+1].code == NR_END || bpath[ib+1].code == NR_MOVETO_OPEN || bpath[ib+1].code == NR_MOVETO)) { // end of subpath
152 if (closed)
153 next = first_in_subpath;
154 } else {
155 if (ib < len)
156 next = &(bpath[ib+1]);
157 else if (closed)
158 next = first_in_subpath;
159 }
160 if (next) {
161 bool this_is_line = bpath[ib].code == NR_LINETO ||
162 (NR::L2(NR::Point(bpath[ib].x3, bpath[ib].y3) - NR::Point(bpath[ib].x2, bpath[ib].y2)) < 1e-6);
163 bool next_is_line = next->code == NR_LINETO ||
164 (NR::L2(NR::Point(bpath[ib].x3, bpath[ib].y3) - NR::Point(next->x1, next->y1)) < 1e-6);
165 NR::Point this_angle (0, 0);
166 if (this_is_line) {
167 this_angle = NR::Point (bpath[ib].x3 - pt[NR::X], bpath[ib].y3 - pt[NR::Y]);
168 } else if (bpath[ib].code == NR_CURVETO) {
169 this_angle = NR::Point (bpath[ib].x3 - bpath[ib].x2, bpath[ib].y3 - bpath[ib].y2);
170 }
171 NR::Point next_angle (0, 0);
172 if (next_is_line) {
173 next_angle = NR::Point (next->x3 - bpath[ib].x3, next->y3 - bpath[ib].y3);
174 } else if (next->code == NR_CURVETO) {
175 next_angle = NR::Point (next->x1 - bpath[ib].x3, next->y1 - bpath[ib].y3);
176 }
177 double this_angle_L2 = NR::L2(this_angle);
178 double next_angle_L2 = NR::L2(next_angle);
179 double both_angles_L2 = NR::L2(this_angle + next_angle);
180 if (this_angle_L2 > 1e-6 &&
181 next_angle_L2 > 1e-6 &&
182 this_angle_L2 + next_angle_L2 - both_angles_L2 < 1e-3) {
183 if (this_is_line && !next_is_line) {
184 path[ip].ty = ']';
185 } else if (next_is_line && !this_is_line) {
186 path[ip].ty = '[';
187 } else {
188 path[ip].ty = 'c';
189 }
190 } else {
191 path[ip].ty = 'v';
193 }
194 if (closed && next == first_in_subpath) {
195 path[0].ty = path[ip].ty;
196 }
197 }
198 }
199 pt = NR::Point(bpath[ib].x3, bpath[ib].y3);
200 ip++;
201 ib++;
202 }
203 g_free (path);
205 // FIXME: refactor the spiro code such that it cannot generate non-continous paths!
206 // sometimes, the code above generates a path that is not continuous. if so, undo the effect by resetting the original path.
207 try {
208 curve->get_pathvector() * Geom::identity(); // tests for continuity, this makes LPE Spiro slower of course :-(
209 }
210 catch (Geom::ContinuityError & e) {
211 g_warning("Exception during LPE Spiro execution. \n %s", e.what());
212 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
213 _("An exception occurred during execution of the Spiro Path Effect.") );
214 curve->set_pathvector(original_pathv);
215 }
216 }
218 }; //namespace LivePathEffect
219 }; /* namespace Inkscape */
221 /*
222 Local Variables:
223 mode:c++
224 c-file-style:"stroustrup"
225 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
226 indent-tabs-mode:nil
227 fill-column:99
228 End:
229 */
230 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :