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 // TRANSLATORS: boolean operations
89 {BOOLOPS, N_("Boolops"), "boolops"},
90 {CIRCLE_WITH_RADIUS, N_("Circle (by center and radius)"), "circle_with_radius"},
91 {CIRCLE_3PTS, N_("Circle by 3 points"), "circle_3pts"},
92 {DYNASTROKE, N_("Dynamic stroke"), "dynastroke"},
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 {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"},
104 {TEXT_LABEL, N_("Text label"), "text_label"},
105 #endif
106 /* 0.46 */
107 {BEND_PATH, N_("Bend"), "bend_path"},
108 {GEARS, N_("Gears"), "gears"},
109 {PATTERN_ALONG_PATH, N_("Pattern Along Path"), "skeletal"}, // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
110 {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
111 /* 0.47 */
112 {VONKOCH, N_("VonKoch"), "vonkoch"},
113 {KNOT, N_("Knot"), "knot"},
114 {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"},
115 {SPIRO, N_("Spiro spline"), "spiro"},
116 {ENVELOPE, N_("Envelope Deformation"), "envelope"},
117 {INTERPOLATE, N_("Interpolate Sub-Paths"), "interpolate"},
118 {ROUGH_HATCHES, N_("Hatches (rough)"), "rough_hatches"},
119 {SKETCH, N_("Sketch"), "sketch"},
120 {RULER, N_("Ruler"), "ruler"},
121 };
122 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
124 int
125 Effect::acceptsNumClicks(EffectType type) {
126 switch (type) {
127 case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
128 case ANGLE_BISECTOR: return 3;
129 case CIRCLE_3PTS: return 3;
130 case CIRCLE_WITH_RADIUS: return 2;
131 case LINE_SEGMENT: return 2;
132 case PERP_BISECTOR: return 2;
133 default: return 0;
134 }
135 }
137 Effect*
138 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
139 {
140 Effect* neweffect = NULL;
141 switch (lpenr) {
142 case PATTERN_ALONG_PATH:
143 neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
144 break;
145 case BEND_PATH:
146 neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
147 break;
148 case SKETCH:
149 neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
150 break;
151 case ROUGH_HATCHES:
152 neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
153 break;
154 case VONKOCH:
155 neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
156 break;
157 case KNOT:
158 neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
159 break;
160 case GEARS:
161 neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
162 break;
163 case CURVE_STITCH:
164 neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
165 break;
166 case LATTICE:
167 neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
168 break;
169 case ENVELOPE:
170 neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
171 break;
172 case CIRCLE_WITH_RADIUS:
173 neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
174 break;
175 case PERSPECTIVE_PATH:
176 neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
177 break;
178 case SPIRO:
179 neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
180 break;
181 case CONSTRUCT_GRID:
182 neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
183 break;
184 case PERP_BISECTOR:
185 neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
186 break;
187 case TANGENT_TO_CURVE:
188 neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
189 break;
190 case MIRROR_SYMMETRY:
191 neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
192 break;
193 case CIRCLE_3PTS:
194 neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
195 break;
196 case ANGLE_BISECTOR:
197 neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
198 break;
199 case PARALLEL:
200 neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
201 break;
202 case COPY_ROTATE:
203 neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
204 break;
205 case OFFSET:
206 neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
207 break;
208 case RULER:
209 neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
210 break;
211 case BOOLOPS:
212 neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
213 break;
214 case INTERPOLATE:
215 neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
216 break;
217 case TEXT_LABEL:
218 neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
219 break;
220 case PATH_LENGTH:
221 neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
222 break;
223 case LINE_SEGMENT:
224 neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
225 break;
226 case DOEFFECTSTACK_TEST:
227 neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
228 break;
229 case DYNASTROKE:
230 neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
231 break;
232 case RECURSIVE_SKELETON:
233 neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
234 break;
235 default:
236 g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
237 neweffect = NULL;
238 break;
239 }
241 if (neweffect) {
242 neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
243 }
245 return neweffect;
246 }
248 void
249 Effect::createAndApply(const char* name, Document *doc, SPItem *item)
250 {
251 // Path effect definition
252 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
253 Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
254 repr->setAttribute("effect", name);
256 SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
257 const gchar * repr_id = repr->attribute("id");
258 Inkscape::GC::release(repr);
260 gchar *href = g_strdup_printf("#%s", repr_id);
261 sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
262 g_free(href);
263 }
265 void
266 Effect::createAndApply(EffectType type, Document *doc, SPItem *item)
267 {
268 createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
269 }
271 Effect::Effect(LivePathEffectObject *lpeobject)
272 : oncanvasedit_it(0),
273 is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
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
279 {
280 registerParameter( dynamic_cast<Parameter *>(&is_visible) );
281 is_visible.widget_is_visible = false;
282 }
284 Effect::~Effect()
285 {
286 }
288 Glib::ustring
289 Effect::getName()
290 {
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") );
295 }
297 EffectType
298 Effect::effectType() {
299 return lpeobj->effecttype;
300 }
302 /**
303 * Is performed a single time when the effect is freshly applied to a path
304 */
305 void
306 Effect::doOnApply (SPLPEItem */*lpeitem*/)
307 {
308 }
310 /**
311 * Is performed each time before the effect is updated.
312 */
313 void
314 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
315 {
316 //Do nothing for simple effects
317 }
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)
326 {
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()));
343 }
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 }
351 }
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();
361 }
363 /*
364 * Here be the doEffect function chain:
365 */
366 void
367 Effect::doEffect (SPCurve * curve)
368 {
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);
374 }
376 std::vector<Geom::Path>
377 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
378 {
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;
403 }
405 Geom::Piecewise<Geom::D2<Geom::SBasis> >
406 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
407 {
408 g_warning("Effect has no doEffect implementation");
409 return pwd2_in;
410 }
412 void
413 Effect::readallParameters(Inkscape::XML::Node * repr)
414 {
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 }
432 }
434 /* This function does not and SHOULD NOT write to XML */
435 void
436 Effect::setParameter(const gchar * key, const gchar * new_value)
437 {
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 }
450 }
452 void
453 Effect::registerParameter(Parameter * param)
454 {
455 param_vector.push_back(param);
456 }
458 // TODO: should we provide a way to alter the handle's appearance?
459 void
460 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
461 {
462 kh_entity_vector.push_back(std::make_pair(entity, descr));
463 }
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 // add handles provided by the effect itself
473 addKnotHolderEntities(knotholder, desktop, item);
475 // add handles provided by the effect's parameters (if any)
476 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
477 (*p)->addKnotHolderEntities(knotholder, desktop, item);
478 }
479 }
481 void
482 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
483 // TODO: The entities in kh_entity_vector are already instantiated during the call
484 // to registerKnotHolderHandle(), but they are recreated here. Also, we must not
485 // delete them when the knotholder is destroyed, whence the clumsy function
486 // isDeletable(). If we could create entities of different classes dynamically,
487 // this would be much nicer. How to do this?
488 std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
489 for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
490 KnotHolderEntity *entity = i->first;
491 const char *descr = i->second;
493 entity->create(desktop, item, knotholder, descr);
494 knotholder->add(entity);
495 }
496 }
498 /**
499 * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
500 * This is the function called by external code like SPLPEItem.
501 */
502 std::vector<Geom::PathVector>
503 Effect::getHelperPaths(SPLPEItem *lpeitem)
504 {
505 std::vector<Geom::PathVector> hp_vec;
507 if (!SP_IS_SHAPE(lpeitem)) {
508 g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
509 return hp_vec;
510 }
512 // TODO: we can probably optimize this by using a lot more references
513 // rather than copying PathVectors all over the place
514 if (show_orig_path) {
515 // add original path to helperpaths
516 SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
517 hp_vec.push_back(curve->get_pathvector());
518 }
520 // add other helperpaths provided by the effect itself
521 addCanvasIndicators(lpeitem, hp_vec);
523 // add helperpaths provided by the effect's parameters
524 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
525 (*p)->addCanvasIndicators(lpeitem, hp_vec);
526 }
528 return hp_vec;
529 }
531 /**
532 * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
533 * This function should be overwritten by derived effects if they want to provide their own helperpaths.
534 */
535 void
536 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
537 {
538 }
541 /**
542 * This *creates* a new widget, management of deletion should be done by the caller
543 */
544 Gtk::Widget *
545 Effect::newWidget(Gtk::Tooltips * tooltips)
546 {
547 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
548 Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
550 vbox->set_border_width(5);
552 std::vector<Parameter *>::iterator it = param_vector.begin();
553 while (it != param_vector.end()) {
554 if ((*it)->widget_is_visible) {
555 Parameter * param = *it;
556 Gtk::Widget * widg = param->param_newWidget(tooltips);
557 Glib::ustring * tip = param->param_getTooltip();
558 if (widg) {
559 vbox->pack_start(*widg, true, true, 2);
560 if (tip != NULL) {
561 tooltips->set_tip(*widg, *tip);
562 }
563 }
564 }
566 it++;
567 }
569 return dynamic_cast<Gtk::Widget *>(vbox);
570 }
573 Inkscape::XML::Node *
574 Effect::getRepr()
575 {
576 return SP_OBJECT_REPR(lpeobj);
577 }
579 Document *
580 Effect::getSPDoc()
581 {
582 if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("Effect::getSPDoc() returns NULL");
583 return SP_OBJECT_DOCUMENT(lpeobj);
584 }
586 Parameter *
587 Effect::getParameter(const char * key)
588 {
589 Glib::ustring stringkey(key);
591 std::vector<Parameter *>::iterator it = param_vector.begin();
592 while (it != param_vector.end()) {
593 Parameter * param = *it;
594 if ( param->param_key == key) {
595 return param;
596 }
598 it++;
599 }
601 return NULL;
602 }
604 Parameter *
605 Effect::getNextOncanvasEditableParam()
606 {
607 if (param_vector.size() == 0) // no parameters
608 return NULL;
610 oncanvasedit_it++;
611 if (oncanvasedit_it >= static_cast<int>(param_vector.size())) {
612 oncanvasedit_it = 0;
613 }
614 int old_it = oncanvasedit_it;
616 do {
617 Parameter * param = param_vector[oncanvasedit_it];
618 if(param && param->oncanvas_editable) {
619 return param;
620 } else {
621 oncanvasedit_it++;
622 if (oncanvasedit_it == static_cast<int>(param_vector.size())) { // loop round the map
623 oncanvasedit_it = 0;
624 }
625 }
626 } while (oncanvasedit_it != old_it); // iterate until complete loop through map has been made
628 return NULL;
629 }
631 void
632 Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
633 {
634 if (!desktop) return;
636 Parameter * param = getNextOncanvasEditableParam();
637 if (param) {
638 param->param_editOncanvas(item, desktop);
639 gchar *message = g_strdup_printf(_("Editing parameter <b>%s</b>."), param->param_label.c_str());
640 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, message);
641 g_free(message);
642 } else {
643 desktop->messageStack()->flash( Inkscape::WARNING_MESSAGE,
644 _("None of the applied path effect's parameters can be edited on-canvas.") );
645 }
646 }
648 /* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
649 * 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!
650 */
651 void
652 Effect::resetDefaults(SPItem * /*item*/)
653 {
654 std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
655 for (p = param_vector.begin(); p != param_vector.end(); ++p) {
656 (*p)->param_set_default();
657 (*p)->write_to_SVG();
658 }
659 }
661 void
662 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
663 {
664 np->helperpath_rgba = 0xff0000ff;
665 np->helperpath_width = 1.0;
666 }
668 void
669 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
670 {
671 // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
672 for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
673 Parameter * param = *it;
674 param->param_transform_multiply(postmul, set);
675 }
676 }
678 // TODO: take _all_ parameters into account, not only PointParams
679 bool
680 Effect::providesKnotholder()
681 {
682 // does the effect actively provide any knotholder entities of its own?
683 if (kh_entity_vector.size() > 0)
684 return true;
686 // otherwise: are there any parameters that have knotholderentities?
687 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
688 if ((*p)->providesKnotHolderEntities()) {
689 return true;
690 }
691 }
693 return false;
694 }
696 } /* namespace LivePathEffect */
698 } /* namespace Inkscape */
700 /*
701 Local Variables:
702 mode:c++
703 c-file-style:"stroustrup"
704 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
705 indent-tabs-mode:nil
706 fill-column:99
707 End:
708 */
709 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :