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