Code

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