Code

fix LPE Sketch with new 2geom
[inkscape.git] / src / live_effects / effect.cpp
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 "xml/node-event-vector.h"
12 #include "sp-object.h"
13 #include "attributes.h"
14 #include "message-stack.h"
15 #include "desktop.h"
16 #include "inkscape.h"
17 #include "document.h"
18 #include "document-private.h"
19 #include "xml/document.h"
20 #include <glibmm/i18n.h>
21 #include "pen-context.h"
22 #include "tools-switch.h"
23 #include "message-stack.h"
24 #include "desktop.h"
25 #include "nodepath.h"
27 #include "live_effects/lpeobject.h"
28 #include "live_effects/parameter/parameter.h"
29 #include <glibmm/ustring.h>
30 #include "display/curve.h"
31 #include <gtkmm.h>
33 #include <exception>
35 #include <2geom/sbasis-to-bezier.h>
36 #include <2geom/matrix.h>
37 #include <2geom/pathvector.h>
39 // include effects:
40 #include "live_effects/lpe-patternalongpath.h"
41 #include "live_effects/lpe-bendpath.h"
42 #include "live_effects/lpe-sketch.h"
43 #include "live_effects/lpe-vonkoch.h"
44 #include "live_effects/lpe-knot.h"
45 #include "live_effects/lpe-rough-hatches.h"
46 #include "live_effects/lpe-test-doEffect-stack.h"
47 #include "live_effects/lpe-gears.h"
48 #include "live_effects/lpe-curvestitch.h"
49 #include "live_effects/lpe-circle_with_radius.h"
50 #include "live_effects/lpe-perspective_path.h"
51 #include "live_effects/lpe-spiro.h"
52 #include "live_effects/lpe-lattice.h"
53 #include "live_effects/lpe-envelope.h"
54 #include "live_effects/lpe-constructgrid.h"
55 #include "live_effects/lpe-perp_bisector.h"
56 #include "live_effects/lpe-tangent_to_curve.h"
57 #include "live_effects/lpe-mirror_symmetry.h"
58 #include "live_effects/lpe-circle_3pts.h"
59 #include "live_effects/lpe-angle_bisector.h"
60 #include "live_effects/lpe-parallel.h"
61 #include "live_effects/lpe-copy_rotate.h"
62 #include "live_effects/lpe-offset.h"
63 #include "live_effects/lpe-ruler.h"
64 #include "live_effects/lpe-boolops.h"
65 #include "live_effects/lpe-interpolate.h"
66 #include "live_effects/lpe-text_label.h"
67 #include "live_effects/lpe-path_length.h"
68 #include "live_effects/lpe-line_segment.h"
69 // end of includes
71 namespace Inkscape {
73 namespace LivePathEffect {
75 const Util::EnumData<EffectType> LPETypeData[] = {
76     // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
77     {ANGLE_BISECTOR,        N_("Angle bisector"),          "angle_bisector"},
78     {BEND_PATH,             N_("Bend"),                     "bend_path"},
79     {BOOLOPS,               N_("Boolops"),                 "boolops"},
80     {CIRCLE_WITH_RADIUS,    N_("Circle (by center and radius)"),   "circle_with_radius"},
81     {CIRCLE_3PTS,           N_("Circle by 3 points"), "circle_3pts"},
82     {CONSTRUCT_GRID,        N_("Construct grid"),          "construct_grid"},
83 #ifdef LPE_ENABLE_TEST_EFFECTS
84     {DOEFFECTSTACK_TEST,    N_("doEffect stack test"),     "doeffectstacktest"},
85 #endif
86     {ENVELOPE,              N_("Envelope Deformation"),    "envelope"},
87     {FREEHAND_SHAPE,        N_("Freehand Shape"),          "freehand_shape"}, // this is actually a special type of PatternAlongPath, used to paste shapes in pen/pencil tool
88     {GEARS,                 N_("Gears"),                   "gears"},
89     {INTERPOLATE,           N_("Interpolate Sub-Paths"),   "interpolate"},
90     {KNOT,                  N_("Knot"),                    "knot"},
91     {LATTICE,               N_("Lattice Deformation"),     "lattice"},
92     {LINE_SEGMENT,          N_("Line Segment"),            "line_segment"},
93     {MIRROR_SYMMETRY,       N_("Mirror symmetry"),         "mirror_symmetry"},
94     {OFFSET,                N_("Offset"),                  "offset"},
95     {PARALLEL,              N_("Parallel"),                "parallel"},
96     {PATH_LENGTH,           N_("Path length"),             "path_length"},
97     {PATTERN_ALONG_PATH,    N_("Pattern Along Path"),      "skeletal"},   // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
98     {PERP_BISECTOR,         N_("Perpendicular bisector"),  "perp_bisector"},
99     {PERSPECTIVE_PATH,      N_("Perspective path"),        "perspective_path"},
100     {COPY_ROTATE,           N_("Rotate copies"),           "copy_rotate"},
101     {RULER,                 N_("Ruler"),                   "ruler"},
102     {SKETCH,                N_("Sketch"),                  "sketch"},
103     {ROUGH_HATCHES,         N_("Hatches (rough)"),            "rough_hatches"},
104     {SPIRO,                 N_("Spiro spline"),            "spiro"},
105     {CURVE_STITCH,          N_("Stitch Sub-Paths"),        "curvestitching"},
106     {TANGENT_TO_CURVE,      N_("Tangent to curve"),        "tangent_to_curve"},
107     {TEXT_LABEL,            N_("Text label"),              "text_label"},
108     {VONKOCH,               N_("VonKoch"),                 "vonkoch"},
109 };
110 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
112 int
113 Effect::acceptsNumClicks(EffectType type) {
114     switch (type) {
115         case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
116         case ANGLE_BISECTOR: return 3;
117         case CIRCLE_3PTS: return 3;
118         case CIRCLE_WITH_RADIUS: return 2;
119         case LINE_SEGMENT: return 2;
120         case PERP_BISECTOR: return 2;
121         default: return 0;
122     }
125 Effect*
126 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
128     Effect* neweffect = NULL;
129     switch (lpenr) {
130         case PATTERN_ALONG_PATH:
131             neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
132             break;
133         case FREEHAND_SHAPE:
134             neweffect = static_cast<Effect*> ( new LPEFreehandShape(lpeobj) );
135             break;
136         case BEND_PATH:
137             neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
138             break;
139         case SKETCH:
140             neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
141             break;
142         case ROUGH_HATCHES:
143             neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
144             break;
145         case VONKOCH:
146             neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
147             break;
148         case KNOT:
149             neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
150             break;
151 #ifdef LPE_ENABLE_TEST_EFFECTS
152         case DOEFFECTSTACK_TEST:
153             neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
154             break;
155 #endif
156         case GEARS:
157             neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
158             break;
159         case CURVE_STITCH:
160             neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
161             break;
162         case LATTICE:
163             neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
164             break;
165         case ENVELOPE:
166             neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
167             break;
168         case CIRCLE_WITH_RADIUS:
169             neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
170             break;
171         case PERSPECTIVE_PATH:
172             neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
173             break;
174         case SPIRO:
175             neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
176             break;
177         case CONSTRUCT_GRID:
178             neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
179             break;
180         case PERP_BISECTOR:
181             neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
182             break;
183         case TANGENT_TO_CURVE:
184             neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
185             break;
186         case MIRROR_SYMMETRY:
187             neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
188             break;
189         case CIRCLE_3PTS:
190             neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
191             break;
192         case ANGLE_BISECTOR:
193             neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
194             break;
195         case PARALLEL:
196             neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
197             break;
198         case COPY_ROTATE:
199             neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
200             break;
201         case OFFSET:
202             neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
203             break;
204         case RULER:
205             neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
206             break;
207         case BOOLOPS:
208             neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
209             break;
210         case INTERPOLATE:
211             neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
212             break;
213         case TEXT_LABEL:
214             neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
215             break;
216         case PATH_LENGTH:
217             neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
218             break;
219         case LINE_SEGMENT:
220             neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
221             break;
222         default:
223             g_warning("LivePathEffect::Effect::New   called with invalid patheffect type (%d)", lpenr);
224             neweffect = NULL;
225             break;
226     }
228     if (neweffect) {
229         neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
230     }
232     return neweffect;
235 void
236 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
238     // Path effect definition
239     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
240     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
241     repr->setAttribute("effect", name);
243     SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
244     const gchar * repr_id = repr->attribute("id");
245     Inkscape::GC::release(repr);
247     gchar *href = g_strdup_printf("#%s", repr_id);
248     sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
249     g_free(href);
252 void
253 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
255     createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
258 Effect::Effect(LivePathEffectObject *lpeobject)
259     : oncanvasedit_it(0),
260       is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
261       deactivate_knotholder(_("Deactivate knotholder?"), _("Check this to deactivate knotholder handles (useful if they interfere with node handles during editing)"), "deactivate_knotholder", &wr, this, false),
262       show_orig_path(false),
263       lpeobj(lpeobject),
264       concatenate_before_pwd2(false),
265       provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
266       is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
268     registerParameter( dynamic_cast<Parameter *>(&is_visible) );
269     registerParameter( dynamic_cast<Parameter *>(&deactivate_knotholder) );
272 Effect::~Effect()
276 Glib::ustring
277 Effect::getName()
279     if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
280         return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
281     else
282         return Glib::ustring( _("No effect") );
285 EffectType
286 Effect::effectType() {
287     return lpeobj->effecttype;
290 /**
291  * Is performed a single time when the effect is freshly applied to a path
292  */
293 void
294 Effect::doOnApply (SPLPEItem */*lpeitem*/)
298 /**
299  * Is performed each time before the effect is updated.
300  */
301 void
302 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
304     //Do nothing for simple effects
307 /**
308  * Effects can have a parameter path set before they are applied by accepting a nonzero number of
309  * mouse clicks. This method activates the pen context, which waits for the specified number of
310  * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
311  */
312 void
313 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
315     // switch to pen context
316     SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
317     if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
318         tools_switch(desktop, TOOLS_FREEHAND_PEN);
319     }
321     SPEventContext *ec = desktop->event_context;
322     SPPenContext *pc = SP_PEN_CONTEXT(ec);
323     pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
324     pc->waiting_LPE = this;
325     pc->waiting_item = lpeitem;
326     pc->polylines_only = true;
328     ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
329         g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
330                         getName().c_str(), acceptsNumClicks()));
333 void
334 Effect::writeParamsToSVG() {
335     std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
336     for (p = param_vector.begin(); p != param_vector.end(); ++p) {
337         (*p)->write_to_SVG();
338     }
341 /**
342  * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
343  * applied, this is the method that processes the resulting path. Override it to customize it for
344  * your LPE. But don't forget to call the parent method so that is_ready is set to true!
345  */
346 void
347 Effect::acceptParamPath (SPPath */*param_path*/) {
348     setReady();
351 /*
352  *  Here be the doEffect function chain:
353  */
354 void
355 Effect::doEffect (SPCurve * curve)
357     std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
359     std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
361     curve->set_pathvector(result_pathv);
364 std::vector<Geom::Path>
365 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
367     std::vector<Geom::Path> path_out;
369     if ( !concatenate_before_pwd2 ) {
370         // default behavior
371         for (unsigned int i=0; i < path_in.size(); i++) {
372             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
373             Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
374             std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
375             // add the output path vector to the already accumulated vector:
376             for (unsigned int j=0; j < path.size(); j++) {
377                 path_out.push_back(path[j]);
378             }
379         }
380     } else {
381       // concatenate the path into possibly discontinuous pwd2
382         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
383         for (unsigned int i=0; i < path_in.size(); i++) {
384             pwd2_in.concat( path_in[i].toPwSb() );
385         }
386         Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
387         path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
388     }
390     return path_out;
393 Geom::Piecewise<Geom::D2<Geom::SBasis> >
394 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
396     g_warning("Effect has no doEffect implementation");
397     return pwd2_in;
400 void
401 Effect::readallParameters(Inkscape::XML::Node * repr)
403     std::vector<Parameter *>::iterator it = param_vector.begin();
404     while (it != param_vector.end()) {
405         Parameter * param = *it;
406         const gchar * key = param->param_key.c_str();
407         const gchar * value = repr->attribute(key);
408         if (value) {
409             bool accepted = param->param_readSVGValue(value);
410             if (!accepted) {
411                 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
412             }
413         } else {
414             // set default value
415             param->param_set_default();
416         }
418         it++;
419     }
422 /* This function does not and SHOULD NOT write to XML */
423 void
424 Effect::setParameter(const gchar * key, const gchar * new_value)
426     Parameter * param = getParameter(key);
427     if (param) {
428         if (new_value) {
429             bool accepted = param->param_readSVGValue(new_value);
430             if (!accepted) {
431                 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
432             }
433         } else {
434             // set default value
435             param->param_set_default();
436         }
437     }
440 void
441 Effect::registerParameter(Parameter * param)
443     param_vector.push_back(param);
446 // TODO: should we provide a way to alter the handle's appearance?
447 void
448 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
450     kh_entity_vector.push_back(std::make_pair(entity, descr));
453 /**
454  * Add all registered LPE knotholder handles to the knotholder
455  */
456 void
457 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
458     using namespace Inkscape::LivePathEffect;
460     if (deactivate_knotholder)
461         return;
463     // add handles provided by the effect itself
464     addKnotHolderEntities(knotholder, desktop, item);
466     // add handles provided by the effect's parameters (if any)
467     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
468         (*p)->addKnotHolderEntities(knotholder, desktop, item);
469     }
472 void
473 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
474     // TODO: The entities in kh_entity_vector are already instantiated during the call
475     //       to registerKnotHolderHandle(), but they are recreated here. Also, we must not
476     //       delete them when the knotholder is destroyed, whence the clumsy function
477     //       isDeletable(). If we could create entities of different classes dynamically,
478     //       this would be much nicer. How to do this?
479     std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
480     for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
481         KnotHolderEntity *entity = i->first;
482         const char *descr = i->second;
484         entity->create(desktop, item, knotholder, descr);
485         knotholder->add(entity);
486     }
489 /**
490  * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
491  * This is the function called by external code like SPLPEItem.
492  */
493 std::vector<Geom::PathVector>
494 Effect::getHelperPaths(SPLPEItem *lpeitem)
496     std::vector<Geom::PathVector> hp_vec;
498     if (!SP_IS_SHAPE(lpeitem)) {
499         g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
500         return hp_vec;
501     }
503     // TODO: we can probably optimize this by using a lot more references
504     //       rather than copying PathVectors all over the place
505     if (show_orig_path) {
506         // add original path to helperpaths
507         SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
508         hp_vec.push_back(curve->get_pathvector());
509     }
511     // add other helperpaths provided by the effect itself
512     addCanvasIndicators(lpeitem, hp_vec);
514     // add helperpaths provided by the effect's parameters
515     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
516         (*p)->addCanvasIndicators(lpeitem, hp_vec);
517     }
519     return hp_vec;
522 /**
523  * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
524  * This function should be overwritten by derived effects if they want to provide their own helperpaths.
525  */
526 void
527 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
532 /**
533  * This *creates* a new widget, management of deletion should be done by the caller
534  */
535 Gtk::Widget *
536 Effect::newWidget(Gtk::Tooltips * tooltips)
538     // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
539     Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
541     vbox->set_border_width(5);
543     std::vector<Parameter *>::iterator it = param_vector.begin();
544     while (it != param_vector.end()) {
545         Parameter * param = *it;
546         Gtk::Widget * widg = param->param_newWidget(tooltips);
547         Glib::ustring * tip = param->param_getTooltip();
548         if (widg) {
549            vbox->pack_start(*widg, true, true, 2);
550             if (tip != NULL) {
551                 tooltips->set_tip(*widg, *tip);
552             }
553         }
555         it++;
556     }
558     return dynamic_cast<Gtk::Widget *>(vbox);
562 Inkscape::XML::Node *
563 Effect::getRepr()
565     return SP_OBJECT_REPR(lpeobj);
568 SPDocument *
569 Effect::getSPDoc()
571     if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
572     return SP_OBJECT_DOCUMENT(lpeobj);
575 Parameter *
576 Effect::getParameter(const char * key)
578     Glib::ustring stringkey(key);
580     std::vector<Parameter *>::iterator it = param_vector.begin();
581     while (it != param_vector.end()) {
582         Parameter * param = *it;
583         if ( param->param_key == key) {
584             return param;
585         }
587         it++;
588     }
590     return NULL;
593 Parameter *
594 Effect::getNextOncanvasEditableParam()
596     if (param_vector.size() == 0) // no parameters
597         return NULL;
599     oncanvasedit_it++;
600     if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
601         oncanvasedit_it = 0;
602     }
603     int old_it = oncanvasedit_it;
605     do {
606         Parameter * param = param_vector[oncanvasedit_it];
607         if(param && param->oncanvas_editable) {
608             return param;
609         } else {
610             oncanvasedit_it++;
611             if (oncanvasedit_it == static_cast<int>(param_vector.size())) {  // loop round the map
612                 oncanvasedit_it = 0;
613             }
614         }
615     } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
617     return NULL;
620 void
621 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
623     if (!desktop) return;
625     Parameter * param = getNextOncanvasEditableParam();
626     if (param) {
627         param->param_editOncanvas(item, desktop);
628         gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
629         desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
630         g_free(message);
631     } else {
632         desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
633                                         _("None of the applied path effect's parameters can be edited on-canvas.") );
634     }
637 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
638 * 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!
639 */
640 void
641 Effect::resetDefaults(SPItem * /*item*/)
643     // do nothing for simple effects
646 void
647 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
649     np->helperpath_rgba = 0xff0000ff;
650     np->helperpath_width = 1.0;
653 void
654 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
656     // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
657     for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
658         Parameter * param = *it;
659         param->param_transform_multiply(postmul, set);
660     }
663 // TODO: take _all_ parameters into account, not only PointParams
664 bool
665 Effect::providesKnotholder()
667     // does the effect actively provide any knotholder entities of its own?
668     if (kh_entity_vector.size() > 0)
669         return true;
671     // otherwise: are there any parameters that have knotholderentities?
672     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
673         if ((*p)->providesKnotHolderEntities()) {
674             return true;
675         }
676     }
678     return false;
681 } /* namespace LivePathEffect */
683 } /* namespace Inkscape */
685 /*
686   Local Variables:
687   mode:c++
688   c-file-style:"stroustrup"
689   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
690   indent-tabs-mode:nil
691   fill-column:99
692   End:
693 */
694 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :