1 #define INKSCAPE_LIVEPATHEFFECT_CPP
3 /*
4 * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
5 *
6 * Released under GNU GPL, read the file 'COPYING' for more information
7 */
9 #include "live_effects/effect.h"
11 #include "display/display-forward.h"
12 #include "xml/node-event-vector.h"
13 #include "sp-object.h"
14 #include "attributes.h"
15 #include "message-stack.h"
16 #include "desktop.h"
17 #include "inkscape.h"
18 #include "document.h"
19 #include <glibmm/i18n.h>
21 #include "live_effects/lpeobject.h"
22 #include "live_effects/parameter/parameter.h"
23 #include <glibmm/ustring.h>
24 #include "live_effects/n-art-bpath-2geom.h"
25 #include "display/curve.h"
26 #include <gtkmm.h>
28 #include <exception>
30 #include <2geom/sbasis-to-bezier.h>
31 #include <2geom/matrix.h>
34 // include effects:
35 #include "live_effects/lpe-skeletalstrokes.h"
36 #include "live_effects/lpe-pathalongpath.h"
37 //here!!
38 #include "live_effects/lpe-sketch.h"
39 #include "live_effects/lpe-vonkoch.h"
40 #include "live_effects/lpe-knot.h"
41 #include "live_effects/lpe-slant.h"
42 #include "live_effects/lpe-test-doEffect-stack.h"
43 #include "live_effects/lpe-gears.h"
44 #include "live_effects/lpe-curvestitch.h"
46 #include "nodepath.h"
48 namespace Inkscape {
50 namespace LivePathEffect {
52 const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
53 // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
54 {PATH_ALONG_PATH, N_("Bend Path"), "bend_path"},
55 {SKELETAL_STROKES, N_("Pattern Along Path"), "skeletal"},
56 {SKETCH, N_("Sketch"), "sketch"},
57 {VONKOCH, N_("VonKoch"), "vonkoch"},
58 {KNOT, N_("Knot"), "knot"},
59 #ifdef LPE_ENABLE_TEST_EFFECTS
60 {SLANT, N_("Slant"), "slant"},
61 {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
62 #endif
63 {GEARS, N_("Gears"), "gears"},
64 {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
65 };
66 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
68 Effect*
69 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
70 {
71 Effect* neweffect = NULL;
72 switch (lpenr) {
73 case SKELETAL_STROKES:
74 neweffect = (Effect*) new LPESkeletalStrokes(lpeobj);
75 break;
76 case PATH_ALONG_PATH:
77 neweffect = (Effect*) new LPEPathAlongPath(lpeobj);
78 break;
79 //here!!
80 case SKETCH:
81 neweffect = (Effect*) new LPESketch(lpeobj);
82 break;
83 case VONKOCH:
84 neweffect = (Effect*) new LPEVonKoch(lpeobj);
85 break;
86 case KNOT:
87 neweffect = (Effect*) new LPEKnot(lpeobj);
88 break;
89 #ifdef LPE_ENABLE_TEST_EFFECTS
90 case SLANT:
91 neweffect = (Effect*) new LPESlant(lpeobj);
92 break;
93 case DOEFFECTSTACK_TEST:
94 neweffect = (Effect*) new LPEdoEffectStackTest(lpeobj);
95 break;
96 #endif
97 case GEARS:
98 neweffect = (Effect*) new LPEGears(lpeobj);
99 break;
100 case CURVE_STITCH:
101 neweffect = (Effect*) new LPECurveStitch(lpeobj);
102 break;
103 default:
104 g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
105 neweffect = NULL;
106 break;
107 }
109 if (neweffect) {
110 neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
111 }
113 return neweffect;
114 }
116 Effect::Effect(LivePathEffectObject *lpeobject)
117 {
118 lpeobj = lpeobject;
119 oncanvasedit_it = 0;
120 }
122 Effect::~Effect()
123 {
124 }
126 Glib::ustring
127 Effect::getName()
128 {
129 if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
130 return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
131 else
132 return Glib::ustring( _("No effect") );
133 }
135 /*
136 * Here be the doEffect function chain:
137 */
138 void
139 Effect::doEffect (SPCurve * curve)
140 {
141 NArtBpath *new_bpath = doEffect_nartbpath(SP_CURVE_BPATH(curve));
143 if (new_bpath && new_bpath != SP_CURVE_BPATH(curve)) { // FIXME, add function to SPCurve to change bpath? or a copy function?
144 if (curve->_bpath) {
145 g_free(curve->_bpath); //delete old bpath
146 }
147 curve->_bpath = new_bpath;
148 }
149 }
151 NArtBpath *
152 Effect::doEffect_nartbpath (NArtBpath * path_in)
153 {
154 try {
155 std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
157 std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
159 NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
161 return new_bpath;
162 }
163 catch (std::exception & e) {
164 g_warning("Exception during LPE %s execution. \n %s", getName().c_str(), e.what());
165 SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
166 _("An exception occurred during execution of the Path Effect.") );
168 NArtBpath *path_out;
170 unsigned ret = 0;
171 while ( path_in[ret].code != NR_END ) {
172 ++ret;
173 }
174 unsigned len = ++ret;
176 path_out = g_new(NArtBpath, len);
177 memcpy(path_out, path_in, len * sizeof(NArtBpath));
178 return path_out;
179 }
180 }
182 std::vector<Geom::Path>
183 Effect::doEffect_path (std::vector<Geom::Path> & path_in)
184 {
185 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
187 for (unsigned int i=0; i < path_in.size(); i++) {
188 pwd2_in.concat( path_in[i].toPwSb() );
189 }
191 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
193 std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
195 return path_out;
196 }
198 Geom::Piecewise<Geom::D2<Geom::SBasis> >
199 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
200 {
201 g_warning("Effect has no doEffect implementation");
202 return pwd2_in;
203 }
205 void
206 Effect::readallParameters(Inkscape::XML::Node * repr)
207 {
208 std::vector<Parameter *>::iterator it = param_vector.begin();
209 while (it != param_vector.end()) {
210 Parameter * param = *it;
211 const gchar * key = param->param_key.c_str();
212 const gchar * value = repr->attribute(key);
213 if (value) {
214 bool accepted = param->param_readSVGValue(value);
215 if (!accepted) {
216 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
217 }
218 } else {
219 // set default value
220 param->param_set_default();
221 }
223 it++;
224 }
225 }
227 /* This function does not and SHOULD NOT write to XML */
228 void
229 Effect::setParameter(const gchar * key, const gchar * new_value)
230 {
231 Parameter * param = getParameter(key);
232 if (param) {
233 if (new_value) {
234 bool accepted = param->param_readSVGValue(new_value);
235 if (!accepted) {
236 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
237 }
238 } else {
239 // set default value
240 param->param_set_default();
241 }
242 }
243 }
245 void
246 Effect::registerParameter(Parameter * param)
247 {
248 param_vector.push_back(param);
249 }
251 /**
252 * This *creates* a new widget, management of deletion should be done by the caller
253 */
254 Gtk::Widget *
255 Effect::newWidget(Gtk::Tooltips * tooltips)
256 {
257 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
258 Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
260 vbox->set_border_width(5);
262 std::vector<Parameter *>::iterator it = param_vector.begin();
263 while (it != param_vector.end()) {
264 Parameter * param = *it;
265 Gtk::Widget * widg = param->param_newWidget(tooltips);
266 Glib::ustring * tip = param->param_getTooltip();
267 if (widg) {
268 vbox->pack_start(*widg, true, true, 2);
269 if (tip != NULL) {
270 tooltips->set_tip(*widg, *tip);
271 }
272 }
274 it++;
275 }
277 return dynamic_cast<Gtk::Widget *>(vbox);
278 }
281 Inkscape::XML::Node *
282 Effect::getRepr()
283 {
284 return SP_OBJECT_REPR(lpeobj);
285 }
287 SPDocument *
288 Effect::getSPDoc()
289 {
290 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
291 return SP_OBJECT_DOCUMENT(lpeobj);
292 }
294 Parameter *
295 Effect::getParameter(const char * key)
296 {
297 Glib::ustring stringkey(key);
299 std::vector<Parameter *>::iterator it = param_vector.begin();
300 while (it != param_vector.end()) {
301 Parameter * param = *it;
302 if ( param->param_key == key) {
303 return param;
304 }
306 it++;
307 }
309 return NULL;
310 }
312 Parameter *
313 Effect::getNextOncanvasEditableParam()
314 {
315 oncanvasedit_it++;
316 if (oncanvasedit_it == static_cast<int>(param_vector.size())) {
317 oncanvasedit_it = 0;
318 }
319 int old_it = oncanvasedit_it;
321 do {
322 Parameter * param = param_vector[oncanvasedit_it];
323 if(param && param->oncanvas_editable) {
324 return param;
325 } else {
326 oncanvasedit_it++;
327 if (oncanvasedit_it == static_cast<int>(param_vector.size())) { // loop round the map
328 oncanvasedit_it = 0;
329 }
330 }
331 } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
333 return NULL;
334 }
336 void
337 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
338 {
339 if (!desktop) return;
341 Parameter * param = getNextOncanvasEditableParam();
342 if (param) {
343 param->param_editOncanvas(item, desktop);
344 gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
345 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
346 g_free(message);
347 } else {
348 desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
349 _("None of the applied path effect's parameters can be edited on-canvas.") );
350 }
351 }
353 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
354 * The nice thing about this is that this function can use knowledge of the original path and set things accordingly for example to the size or origin of the original path!
355 */
356 void
357 Effect::resetDefaults(SPItem * /*item*/)
358 {
359 // do nothing for simple effects
360 }
362 void
363 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
364 {
365 np->show_helperpath = true;
366 np->helperpath_rgba = 0xff0000ff;
367 np->helperpath_width = 1.0;
368 }
370 void
371 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
372 {
373 // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
374 for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
375 Parameter * param = *it;
376 param->param_transform_multiply(postmul, set);
377 }
378 }
380 } /* namespace LivePathEffect */
382 } /* namespace Inkscape */
384 /*
385 Local Variables:
386 mode:c++
387 c-file-style:"stroustrup"
388 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
389 indent-tabs-mode:nil
390 fill-column:99
391 End:
392 */
393 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :