effect.cpp revision 4afe3fc6b9c122bc5c02b27aea3845ba41384d2a
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Released under GNU GPL, read the file 'COPYING' for more information
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster// include effects:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "live_effects/lpe-test-doEffect-stack.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster#include "live_effects/lpe-circle_with_radius.h"
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster// end of includes
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterconst Util::EnumData<EffectType> LPETypeData[] = {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {CIRCLE_WITH_RADIUS, N_("Circle (center+radius)"), "circle_with_radius"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {CIRCLE_3PTS, N_("Circle through 3 points"), "circle_3pts"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {ENVELOPE, N_("Envelope Deformation"), "envelope"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {FREEHAND_SHAPE, N_("Freehand Shape"), "freehand_shape"}, // this is actually a special type of PatternAlongPath, used to paste shapes in pen/pencil tool
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {INTERPOLATE, N_("Interpolate Sub-Paths"), "interpolate"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {LATTICE, N_("Lattice Deformation"), "lattice"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {MIRROR_SYMMETRY, N_("Mirror symmetry"), "mirror_symmetry"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {PATTERN_ALONG_PATH, N_("Pattern Along Path"), "skeletal"}, // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {PERP_BISECTOR, N_("Perpendicular bisector"), "perp_bisector"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {PERSPECTIVE_PATH, N_("Perspective path"), "perspective_path"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {COPY_ROTATE, N_("Rotate copies"), "copy_rotate"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"},
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Fosterconst Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterEffect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEPatternAlongPath(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEFreehandShape(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEBendPath(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPECurveStitch(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPELattice(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEEnvelope(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPECircleWithRadius(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEPerspectivePath(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPESpiro(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEConstructGrid(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEPerpBisector(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster neweffect->readallParameters(SP_OBJECT_REPR(lpeobj));
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterEffect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // Path effect definition
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SP_OBJECT_REPR(SP_DOCUMENT_DEFS(doc))->addChild(repr, NULL); // adds to <defs> and assigns the 'id' attribute
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterEffect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster createAndApply(LPETypeConverter.get_key(type).c_str(), doc, item);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster provides_own_flash_paths(true) // is automatically set to false if providesOwnFlashPaths() is not overridden
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster registerParameter( dynamic_cast<Parameter *>(&is_visible) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Is performed a single time when the effect is freshly applied to a path
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Is performed each time before the effect is updated.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster //Do nothing for simple effects
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Effects can have a parameter path set before they are applied by accepting a nonzero number of
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * mouse clicks. This method activates the pen context, which waits for the specified number of
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * clicks. Override Effect::acceptsNumParams() to return the number of expected mouse clicks.
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterEffect::doAcceptPathPreparations(SPLPEItem *lpeitem)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // switch to pen context
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster pc->expecting_clicks_for_LPE = this->acceptsNumParams();
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster for (p = param_vector.begin(); p != param_vector.end(); ++p) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * applied, this is the method that processes the resulting path. Override it to customize it for
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * your LPE. But don't forget to call the parent method so that done_pathparam_set is set to true!
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterEffect::acceptParamPath (SPPath */*param_path*/) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster * Here be the doEffect function chain:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterEffect::doEffect_path (std::vector<Geom::Path> const & path_in)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // default behavior
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster for (unsigned int i=0; i < path_in.size(); i++) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in = path_in[i].toPwSb();
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster std::vector<Geom::Path> path = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // add the output path vector to the already accumulated vector:
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster // concatenate the path into possibly discontinuous pwd2
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster for (unsigned int i=0; i < path_in.size(); i++) {
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect_pwd2(pwd2_in);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterEffect::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster g_warning("Effect has no doEffect implementation");
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan FosterEffect::readallParameters(Inkscape::XML::Node * repr)
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster std::vector<Parameter *>::iterator it = param_vector.begin();
a688bcbb4bcff5398fdd29b86f83450257dc0df4Allan Foster bool accepted = param->param_readSVGValue(value);
if (!accepted) {
it++;
if (param) {
if (new_value) {
if (!accepted) {
return hp_vec;
if (show_orig_path) {
return hp_vec;
* This function should be overwritten by derived effects if they want to provide their own helperpaths.
// use manage here, because after deletion of Effect object, others might still be pointing to this widget.
if (widg) {
it++;
return param;
it++;
return NULL;
return NULL;
oncanvasedit_it = 0;
return param;
oncanvasedit_it = 0;
return NULL;
if (!desktop) return;
if (param) {
/* This function should reset the defaults and is used for example to initialize an effect right after it has been applied to a path
* 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!
// cycle through all parameters. Most parameters will not need transformation, but path and point params do.
for (std::vector<Parameter *>::iterator it = param_vector.begin(); it != param_vector.end(); it++) {