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