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 }
123 }
125 Effect*
126 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
127 {
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;
233 }
235 void
236 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
237 {
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);
250 }
252 void
253 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
254 {
255 createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
256 }
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
267 {
268 registerParameter( dynamic_cast<Parameter *>(&is_visible) );
269 registerParameter( dynamic_cast<Parameter *>(&deactivate_knotholder) );
270 }
272 Effect::~Effect()
273 {
274 }
276 Glib::ustring
277 Effect::getName()
278 {
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") );
283 }
285 EffectType
286 Effect::effectType() {
287 return lpeobj->effecttype;
288 }
290 /**
291 * Is performed a single time when the effect is freshly applied to a path
292 */
293 void
294 Effect::doOnApply (SPLPEItem */*lpeitem*/)
295 {
296 }
298 /**
299 * Is performed each time before the effect is updated.
300 */
301 void
302 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
303 {
304 //Do nothing for simple effects
305 }
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)
314 {
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()));
331 }
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 }
339 }
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();
349 }
351 /*
352 * Here be the doEffect function chain:
353 */
354 void
355 Effect::doEffect (SPCurve * curve)
356 {
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);
362 }
364 std::vector<Geom::Path>
365 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
366 {
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;
391 }
393 Geom::Piecewise<Geom::D2<Geom::SBasis> >
394 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
395 {
396 g_warning("Effect has no doEffect implementation");
397 return pwd2_in;
398 }
400 void
401 Effect::readallParameters(Inkscape::XML::Node * repr)
402 {
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 }
420 }
422 /* This function does not and SHOULD NOT write to XML */
423 void
424 Effect::setParameter(const gchar * key, const gchar * new_value)
425 {
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 }
438 }
440 void
441 Effect::registerParameter(Parameter * param)
442 {
443 param_vector.push_back(param);
444 }
446 // TODO: should we provide a way to alter the handle's appearance?
447 void
448 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
449 {
450 kh_entity_vector.push_back(std::make_pair(entity, descr));
451 }
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 }
470 }
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 }
487 }
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)
495 {
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;
520 }
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*/)
528 {
529 }
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)
537 {
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);
559 }
562 Inkscape::XML::Node *
563 Effect::getRepr()
564 {
565 return SP_OBJECT_REPR(lpeobj);
566 }
568 SPDocument *
569 Effect::getSPDoc()
570 {
571 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
572 return SP_OBJECT_DOCUMENT(lpeobj);
573 }
575 Parameter *
576 Effect::getParameter(const char * key)
577 {
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;
591 }
593 Parameter *
594 Effect::getNextOncanvasEditableParam()
595 {
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;
618 }
620 void
621 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
622 {
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 }
635 }
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*/)
642 {
643 // do nothing for simple effects
644 }
646 void
647 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
648 {
649 np->helperpath_rgba = 0xff0000ff;
650 np->helperpath_width = 1.0;
651 }
653 void
654 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
655 {
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 }
661 }
663 // TODO: take _all_ parameters into account, not only PointParams
664 bool
665 Effect::providesKnotholder()
666 {
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;
679 }
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 :