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 //#define LPE_ENABLE_TEST_EFFECTS
78 namespace Inkscape {
80 namespace LivePathEffect {
82 const Util::EnumData<EffectType> LPETypeData[] = {
83 // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
84 #ifdef LPE_ENABLE_TEST_EFFECTS
85 {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
86 {ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"},
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 {DYNASTROKE, N_("Dynamic stroke"), "dynastroke"},
91 {ENVELOPE, N_("Envelope Deformation"), "envelope"},
92 {ROUGH_HATCHES, N_("Hatches (rough)"), "rough_hatches"},
93 {INTERPOLATE, N_("Interpolate Sub-Paths"), "interpolate"},
94 {LATTICE, N_("Lattice Deformation"), "lattice"},
95 {LINE_SEGMENT, N_("Line Segment"), "line_segment"},
96 {MIRROR_SYMMETRY, N_("Mirror symmetry"), "mirror_symmetry"},
97 {OFFSET, N_("Offset"), "offset"},
98 {PARALLEL, N_("Parallel"), "parallel"},
99 {PATH_LENGTH, N_("Path length"), "path_length"},
100 {PERP_BISECTOR, N_("Perpendicular bisector"), "perp_bisector"},
101 {PERSPECTIVE_PATH, N_("Perspective path"), "perspective_path"},
102 {COPY_ROTATE, N_("Rotate copies"), "copy_rotate"},
103 {RULER, N_("Ruler"), "ruler"},
104 {SKETCH, N_("Sketch"), "sketch"},
105 {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"},
106 {TEXT_LABEL, N_("Text label"), "text_label"},
107 {VONKOCH, N_("VonKoch"), "vonkoch"},
108 #endif
109 /* 0.46 */
110 {BEND_PATH, N_("Bend"), "bend_path"},
111 {GEARS, N_("Gears"), "gears"},
112 {PATTERN_ALONG_PATH, N_("Pattern Along Path"), "skeletal"}, // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
113 {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
114 /* 0.47 */
115 {FREEHAND_SHAPE, N_("Freehand Shape"), "freehand_shape"}, // this is actually a special type of PatternAlongPath, used to paste shapes in pen/pencil tool
116 {KNOT, N_("Knot"), "knot"},
117 {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"},
118 {SPIRO, N_("Spiro spline"), "spiro"},
119 };
120 const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
122 int
123 Effect::acceptsNumClicks(EffectType type) {
124 switch (type) {
125 case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
126 case ANGLE_BISECTOR: return 3;
127 case CIRCLE_3PTS: return 3;
128 case CIRCLE_WITH_RADIUS: return 2;
129 case LINE_SEGMENT: return 2;
130 case PERP_BISECTOR: return 2;
131 default: return 0;
132 }
133 }
135 Effect*
136 Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
137 {
138 Effect* neweffect = NULL;
139 switch (lpenr) {
140 case PATTERN_ALONG_PATH:
141 neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
142 break;
143 case FREEHAND_SHAPE:
144 neweffect = static_cast<Effect*> ( new LPEFreehandShape(lpeobj) );
145 break;
146 case BEND_PATH:
147 neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
148 break;
149 case SKETCH:
150 neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
151 break;
152 case ROUGH_HATCHES:
153 neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
154 break;
155 case VONKOCH:
156 neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
157 break;
158 case KNOT:
159 neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
160 break;
161 case GEARS:
162 neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
163 break;
164 case CURVE_STITCH:
165 neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
166 break;
167 case LATTICE:
168 neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
169 break;
170 case ENVELOPE:
171 neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
172 break;
173 case CIRCLE_WITH_RADIUS:
174 neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
175 break;
176 case PERSPECTIVE_PATH:
177 neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
178 break;
179 case SPIRO:
180 neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
181 break;
182 case CONSTRUCT_GRID:
183 neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
184 break;
185 case PERP_BISECTOR:
186 neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
187 break;
188 case TANGENT_TO_CURVE:
189 neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
190 break;
191 case MIRROR_SYMMETRY:
192 neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
193 break;
194 case CIRCLE_3PTS:
195 neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
196 break;
197 case ANGLE_BISECTOR:
198 neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
199 break;
200 case PARALLEL:
201 neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
202 break;
203 case COPY_ROTATE:
204 neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
205 break;
206 case OFFSET:
207 neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
208 break;
209 case RULER:
210 neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
211 break;
212 case BOOLOPS:
213 neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
214 break;
215 case INTERPOLATE:
216 neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
217 break;
218 case TEXT_LABEL:
219 neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
220 break;
221 case PATH_LENGTH:
222 neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
223 break;
224 case LINE_SEGMENT:
225 neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
226 break;
227 case DOEFFECTSTACK_TEST:
228 neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
229 break;
230 case DYNASTROKE:
231 neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
232 break;
233 default:
234 g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
235 neweffect = NULL;
236 break;
237 }
239 if (neweffect) {
240 neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
241 }
243 return neweffect;
244 }
246 void
247 Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
248 {
249 // Path effect definition
250 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
251 Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
252 repr->setAttribute("effect", name);
254 SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
255 const gchar * repr_id = repr->attribute("id");
256 Inkscape::GC::release(repr);
258 gchar *href = g_strdup_printf("#%s", repr_id);
259 sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
260 g_free(href);
261 }
263 void
264 Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
265 {
266 createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
267 }
269 Effect::Effect(LivePathEffectObject *lpeobject)
270 : oncanvasedit_it(0),
271 is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
272 deactivate_knotholder(_("Deactivate knotholder?"), _("Check this to deactivate knotholder handles (useful if they interfere with node handles during editing)"), "deactivate_knotholder", &wr, this, false),
273 show_orig_path(false),
274 lpeobj(lpeobject),
275 concatenate_before_pwd2(false),
276 provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
277 is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
278 {
279 registerParameter( dynamic_cast<Parameter *>(&is_visible) );
280 registerParameter( dynamic_cast<Parameter *>(&deactivate_knotholder) );
281 }
283 Effect::~Effect()
284 {
285 }
287 Glib::ustring
288 Effect::getName()
289 {
290 if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
291 return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
292 else
293 return Glib::ustring( _("No effect") );
294 }
296 EffectType
297 Effect::effectType() {
298 return lpeobj->effecttype;
299 }
301 /**
302 * Is performed a single time when the effect is freshly applied to a path
303 */
304 void
305 Effect::doOnApply (SPLPEItem */*lpeitem*/)
306 {
307 }
309 /**
310 * Is performed each time before the effect is updated.
311 */
312 void
313 Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
314 {
315 //Do nothing for simple effects
316 }
318 /**
319 * Effects can have a parameter path set before they are applied by accepting a nonzero number of
320 * mouse clicks. This method activates the pen context, which waits for the specified number of
321 * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
322 */
323 void
324 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
325 {
326 // switch to pen context
327 SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
328 if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
329 tools_switch(desktop, TOOLS_FREEHAND_PEN);
330 }
332 SPEventContext *ec = desktop->event_context;
333 SPPenContext *pc = SP_PEN_CONTEXT(ec);
334 pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
335 pc->waiting_LPE = this;
336 pc->waiting_item = lpeitem;
337 pc->polylines_only = true;
339 ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
340 g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
341 getName().c_str(), acceptsNumClicks()));
342 }
344 void
345 Effect::writeParamsToSVG() {
346 std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
347 for (p = param_vector.begin(); p != param_vector.end(); ++p) {
348 (*p)->write_to_SVG();
349 }
350 }
352 /**
353 * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
354 * applied, this is the method that processes the resulting path. Override it to customize it for
355 * your LPE. But don't forget to call the parent method so that is_ready is set to true!
356 */
357 void
358 Effect::acceptParamPath (SPPath */*param_path*/) {
359 setReady();
360 }
362 /*
363 * Here be the doEffect function chain:
364 */
365 void
366 Effect::doEffect (SPCurve * curve)
367 {
368 std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
370 std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
372 curve->set_pathvector(result_pathv);
373 }
375 std::vector<Geom::Path>
376 Effect::doEffect_path (std::vector<Geom::Path> const & path_in)
377 {
378 std::vector<Geom::Path> path_out;
380 if ( !concatenate_before_pwd2 ) {
381 // default behavior
382 for (unsigned int i=0; i < path_in.size(); i++) {
383 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
384 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
385 std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
386 // add the output path vector to the already accumulated vector:
387 for (unsigned int j=0; j < path.size(); j++) {
388 path_out.push_back(path[j]);
389 }
390 }
391 } else {
392 // concatenate the path into possibly discontinuous pwd2
393 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
394 for (unsigned int i=0; i < path_in.size(); i++) {
395 pwd2_in.concat( path_in[i].toPwSb() );
396 }
397 Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
398 path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
399 }
401 return path_out;
402 }
404 Geom::Piecewise<Geom::D2<Geom::SBasis> >
405 Effect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
406 {
407 g_warning("Effect has no doEffect implementation");
408 return pwd2_in;
409 }
411 void
412 Effect::readallParameters(Inkscape::XML::Node * repr)
413 {
414 std::vector<Parameter *>::iterator it = param_vector.begin();
415 while (it != param_vector.end()) {
416 Parameter * param = *it;
417 const gchar * key = param->param_key.c_str();
418 const gchar * value = repr->attribute(key);
419 if (value) {
420 bool accepted = param->param_readSVGValue(value);
421 if (!accepted) {
422 g_warning("Effect::readallParameters - '%s' not accepted for %s", value, key);
423 }
424 } else {
425 // set default value
426 param->param_set_default();
427 }
429 it++;
430 }
431 }
433 /* This function does not and SHOULD NOT write to XML */
434 void
435 Effect::setParameter(const gchar * key, const gchar * new_value)
436 {
437 Parameter * param = getParameter(key);
438 if (param) {
439 if (new_value) {
440 bool accepted = param->param_readSVGValue(new_value);
441 if (!accepted) {
442 g_warning("Effect::setParameter - '%s' not accepted for %s", new_value, key);
443 }
444 } else {
445 // set default value
446 param->param_set_default();
447 }
448 }
449 }
451 void
452 Effect::registerParameter(Parameter * param)
453 {
454 param_vector.push_back(param);
455 }
457 // TODO: should we provide a way to alter the handle's appearance?
458 void
459 Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
460 {
461 kh_entity_vector.push_back(std::make_pair(entity, descr));
462 }
464 /**
465 * Add all registered LPE knotholder handles to the knotholder
466 */
467 void
468 Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
469 using namespace Inkscape::LivePathEffect;
471 if (deactivate_knotholder)
472 return;
474 // add handles provided by the effect itself
475 addKnotHolderEntities(knotholder, desktop, item);
477 // add handles provided by the effect's parameters (if any)
478 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
479 (*p)->addKnotHolderEntities(knotholder, desktop, item);
480 }
481 }
483 void
484 Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
485 // TODO: The entities in kh_entity_vector are already instantiated during the call
486 // to registerKnotHolderHandle(), but they are recreated here. Also, we must not
487 // delete them when the knotholder is destroyed, whence the clumsy function
488 // isDeletable(). If we could create entities of different classes dynamically,
489 // this would be much nicer. How to do this?
490 std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
491 for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
492 KnotHolderEntity *entity = i->first;
493 const char *descr = i->second;
495 entity->create(desktop, item, knotholder, descr);
496 knotholder->add(entity);
497 }
498 }
500 /**
501 * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
502 * This is the function called by external code like SPLPEItem.
503 */
504 std::vector<Geom::PathVector>
505 Effect::getHelperPaths(SPLPEItem *lpeitem)
506 {
507 std::vector<Geom::PathVector> hp_vec;
509 if (!SP_IS_SHAPE(lpeitem)) {
510 g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
511 return hp_vec;
512 }
514 // TODO: we can probably optimize this by using a lot more references
515 // rather than copying PathVectors all over the place
516 if (show_orig_path) {
517 // add original path to helperpaths
518 SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
519 hp_vec.push_back(curve->get_pathvector());
520 }
522 // add other helperpaths provided by the effect itself
523 addCanvasIndicators(lpeitem, hp_vec);
525 // add helperpaths provided by the effect's parameters
526 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
527 (*p)->addCanvasIndicators(lpeitem, hp_vec);
528 }
530 return hp_vec;
531 }
533 /**
534 * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
535 * This function should be overwritten by derived effects if they want to provide their own helperpaths.
536 */
537 void
538 Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
539 {
540 }
543 /**
544 * This *creates* a new widget, management of deletion should be done by the caller
545 */
546 Gtk::Widget *
547 Effect::newWidget(Gtk::Tooltips * tooltips)
548 {
549 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
550 Gtk::VBox * vbox = Gtk::manage( new Gtk::VBox() );
552 vbox->set_border_width(5);
554 std::vector<Parameter *>::iterator it = param_vector.begin();
555 while (it != param_vector.end()) {
556 Parameter * param = *it;
557 Gtk::Widget * widg = param->param_newWidget(tooltips);
558 Glib::ustring * tip = param->param_getTooltip();
559 if (widg) {
560 vbox->pack_start(*widg, true, true, 2);
561 if (tip != NULL) {
562 tooltips->set_tip(*widg, *tip);
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 SPDocument *
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 // do nothing for simple effects
655 }
657 void
658 Effect::setup_nodepath(Inkscape::NodePath::Path *np)
659 {
660 np->helperpath_rgba = 0xff0000ff;
661 np->helperpath_width = 1.0;
662 }
664 void
665 Effect::transform_multiply(Geom::Matrix const& postmul, bool set)
666 {
667 // cycle through all parameters. Most parameters will not need transformation, but path and point params do.
668 for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {
669 Parameter * param = *it;
670 param->param_transform_multiply(postmul, set);
671 }
672 }
674 // TODO: take _all_ parameters into account, not only PointParams
675 bool
676 Effect::providesKnotholder()
677 {
678 // does the effect actively provide any knotholder entities of its own?
679 if (kh_entity_vector.size() > 0)
680 return true;
682 // otherwise: are there any parameters that have knotholderentities?
683 for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
684 if ((*p)->providesKnotHolderEntities()) {
685 return true;
686 }
687 }
689 return false;
690 }
692 } /* namespace LivePathEffect */
694 } /* namespace Inkscape */
696 /*
697 Local Variables:
698 mode:c++
699 c-file-style:"stroustrup"
700 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
701 indent-tabs-mode:nil
702 fill-column:99
703 End:
704 */
705 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :