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