Code

noop: be more consistent with function names (get_pathvector => set_pathvector)
[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"
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)
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     }
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 :