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