index c46a9dd2397dc864fc46f382a596866e4fcaa63a..9dbd27b5041383c10ff45e94ed130433e367f429 100644 (file)
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+//#define LPE_ENABLE_TEST_EFFECTS
+
#include "live_effects/effect.h"
-#include "display/display-forward.h"
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
#include "xml/node-event-vector.h"
#include "sp-object.h"
#include "attributes.h"
#include "document-private.h"
#include "xml/document.h"
#include <glibmm/i18n.h>
-
+#include "pen-context.h"
+#include "tools-switch.h"
+#include "message-stack.h"
+#include "desktop.h"
+#include "knotholder.h"
+#include "sp-lpe-item.h"
#include "live_effects/lpeobject.h"
#include "live_effects/parameter/parameter.h"
#include <glibmm/ustring.h>
-#include "libnr/n-art-bpath-2geom.h"
#include "display/curve.h"
#include <gtkmm.h>
#include <2geom/sbasis-to-bezier.h>
#include <2geom/matrix.h>
-
+#include <2geom/pathvector.h>
// include effects:
#include "live_effects/lpe-patternalongpath.h"
#include "live_effects/lpe-sketch.h"
#include "live_effects/lpe-vonkoch.h"
#include "live_effects/lpe-knot.h"
+#include "live_effects/lpe-rough-hatches.h"
+#include "live_effects/lpe-dynastroke.h"
#include "live_effects/lpe-test-doEffect-stack.h"
#include "live_effects/lpe-gears.h"
#include "live_effects/lpe-curvestitch.h"
#include "live_effects/lpe-constructgrid.h"
#include "live_effects/lpe-perp_bisector.h"
#include "live_effects/lpe-tangent_to_curve.h"
-// end of includes
+#include "live_effects/lpe-mirror_symmetry.h"
+#include "live_effects/lpe-circle_3pts.h"
+#include "live_effects/lpe-angle_bisector.h"
+#include "live_effects/lpe-parallel.h"
+#include "live_effects/lpe-copy_rotate.h"
+#include "live_effects/lpe-offset.h"
+#include "live_effects/lpe-ruler.h"
+#include "live_effects/lpe-boolops.h"
+#include "live_effects/lpe-interpolate.h"
+#include "live_effects/lpe-text_label.h"
+#include "live_effects/lpe-path_length.h"
+#include "live_effects/lpe-line_segment.h"
+#include "live_effects/lpe-recursiveskeleton.h"
+#include "live_effects/lpe-extrude.h"
+#include "live_effects/lpe-powerstroke.h"
-#include "nodepath.h"
namespace Inkscape {
namespace LivePathEffect {
-const Util::EnumData<EffectType> LPETypeData[INVALID_LPE] = {
- // {constant defined in effect.h, N_("name of your effect"), "name of your effect in SVG"}
- {BEND_PATH, N_("Bend"), "bend_path"},
- {PATTERN_ALONG_PATH, N_("Pattern Along Path"), "skeletal"}, // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
- {SKETCH, N_("Sketch"), "sketch"},
- {VONKOCH, N_("VonKoch"), "vonkoch"},
- {KNOT, N_("Knot"), "knot"},
+const Util::EnumData<EffectType> LPETypeData[] = {
+ // {constant defined in effect-enum.h, N_("name of your effect"), "name of your effect in SVG"}
#ifdef LPE_ENABLE_TEST_EFFECTS
- {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
+ {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"},
+ {ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"},
+ // TRANSLATORS: boolean operations
+ {BOOLOPS, N_("Boolops"), "boolops"},
+ {CIRCLE_WITH_RADIUS, N_("Circle (by center and radius)"), "circle_with_radius"},
+ {CIRCLE_3PTS, N_("Circle by 3 points"), "circle_3pts"},
+ {DYNASTROKE, N_("Dynamic stroke"), "dynastroke"},
+ {EXTRUDE, N_("Extrude"), "extrude"},
+ {LATTICE, N_("Lattice Deformation"), "lattice"},
+ {LINE_SEGMENT, N_("Line Segment"), "line_segment"},
+ {MIRROR_SYMMETRY, N_("Mirror symmetry"), "mirror_symmetry"},
+ {OFFSET, N_("Offset"), "offset"},
+ {PARALLEL, N_("Parallel"), "parallel"},
+ {PATH_LENGTH, N_("Path length"), "path_length"},
+ {PERP_BISECTOR, N_("Perpendicular bisector"), "perp_bisector"},
+ {PERSPECTIVE_PATH, N_("Perspective path"), "perspective_path"},
+ {POWERSTROKE, N_("Power stroke"), "powerstroke"},
+ {COPY_ROTATE, N_("Rotate copies"), "copy_rotate"},
+ {RECURSIVE_SKELETON, N_("Recursive skeleton"), "recursive_skeleton"},
+ {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"},
+ {TEXT_LABEL, N_("Text label"), "text_label"},
#endif
- {GEARS, N_("Gears"), "gears"},
- {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
- {CIRCLE_WITH_RADIUS, N_("Circle (center+radius)"), "circle_with_radius"},
- {PERSPECTIVE_PATH, N_("Perspective path"), "perspective_path"},
- {SPIRO, N_("Spiro spline"), "spiro"},
- {LATTICE, N_("Lattice Deformation"), "lattice"},
- {ENVELOPE, N_("Envelope Deformation"), "envelope"},
- {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"},
- {PERP_BISECTOR, N_("Perpendicular bisector"), "perp_bisector"},
- {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"}
+/* 0.46 */
+ {BEND_PATH, N_("Bend"), "bend_path"},
+ {GEARS, N_("Gears"), "gears"},
+ {PATTERN_ALONG_PATH, N_("Pattern Along Path"), "skeletal"}, // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
+ {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
+/* 0.47 */
+ {VONKOCH, N_("VonKoch"), "vonkoch"},
+ {KNOT, N_("Knot"), "knot"},
+ {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"},
+ {SPIRO, N_("Spiro spline"), "spiro"},
+ {ENVELOPE, N_("Envelope Deformation"), "envelope"},
+ {INTERPOLATE, N_("Interpolate Sub-Paths"), "interpolate"},
+ {ROUGH_HATCHES, N_("Hatches (rough)"), "rough_hatches"},
+ {SKETCH, N_("Sketch"), "sketch"},
+ {RULER, N_("Ruler"), "ruler"},
+/* 0.49 */
};
-const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, INVALID_LPE);
+const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
+
+int
+Effect::acceptsNumClicks(EffectType type) {
+ switch (type) {
+ case INVALID_LPE: return -1; // in case we want to distinguish between invalid LPE and valid ones that expect zero clicks
+ case ANGLE_BISECTOR: return 3;
+ case CIRCLE_3PTS: return 3;
+ case CIRCLE_WITH_RADIUS: return 2;
+ case LINE_SEGMENT: return 2;
+ case PERP_BISECTOR: return 2;
+ default: return 0;
+ }
+}
Effect*
Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
case SKETCH:
neweffect = static_cast<Effect*> ( new LPESketch(lpeobj) );
break;
+ case ROUGH_HATCHES:
+ neweffect = static_cast<Effect*> ( new LPERoughHatches(lpeobj) );
+ break;
case VONKOCH:
neweffect = static_cast<Effect*> ( new LPEVonKoch(lpeobj) );
break;
case KNOT:
neweffect = static_cast<Effect*> ( new LPEKnot(lpeobj) );
break;
-#ifdef LPE_ENABLE_TEST_EFFECTS
- case DOEFFECTSTACK_TEST:
- neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
- break;
-#endif
case GEARS:
neweffect = static_cast<Effect*> ( new LPEGears(lpeobj) );
break;
case TANGENT_TO_CURVE:
neweffect = static_cast<Effect*> ( new LPETangentToCurve(lpeobj) );
break;
+ case MIRROR_SYMMETRY:
+ neweffect = static_cast<Effect*> ( new LPEMirrorSymmetry(lpeobj) );
+ break;
+ case CIRCLE_3PTS:
+ neweffect = static_cast<Effect*> ( new LPECircle3Pts(lpeobj) );
+ break;
+ case ANGLE_BISECTOR:
+ neweffect = static_cast<Effect*> ( new LPEAngleBisector(lpeobj) );
+ break;
+ case PARALLEL:
+ neweffect = static_cast<Effect*> ( new LPEParallel(lpeobj) );
+ break;
+ case COPY_ROTATE:
+ neweffect = static_cast<Effect*> ( new LPECopyRotate(lpeobj) );
+ break;
+ case OFFSET:
+ neweffect = static_cast<Effect*> ( new LPEOffset(lpeobj) );
+ break;
+ case RULER:
+ neweffect = static_cast<Effect*> ( new LPERuler(lpeobj) );
+ break;
+ case BOOLOPS:
+ neweffect = static_cast<Effect*> ( new LPEBoolops(lpeobj) );
+ break;
+ case INTERPOLATE:
+ neweffect = static_cast<Effect*> ( new LPEInterpolate(lpeobj) );
+ break;
+ case TEXT_LABEL:
+ neweffect = static_cast<Effect*> ( new LPETextLabel(lpeobj) );
+ break;
+ case PATH_LENGTH:
+ neweffect = static_cast<Effect*> ( new LPEPathLength(lpeobj) );
+ break;
+ case LINE_SEGMENT:
+ neweffect = static_cast<Effect*> ( new LPELineSegment(lpeobj) );
+ break;
+ case DOEFFECTSTACK_TEST:
+ neweffect = static_cast<Effect*> ( new LPEdoEffectStackTest(lpeobj) );
+ break;
+ case DYNASTROKE:
+ neweffect = static_cast<Effect*> ( new LPEDynastroke(lpeobj) );
+ break;
+ case RECURSIVE_SKELETON:
+ neweffect = static_cast<Effect*> ( new LPERecursiveSkeleton(lpeobj) );
+ break;
+ case EXTRUDE:
+ neweffect = static_cast<Effect*> ( new LPEExtrude(lpeobj) );
+ break;
+ case POWERSTROKE:
+ neweffect = static_cast<Effect*> ( new LPEPowerStroke(lpeobj) );
+ break;
default:
g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
neweffect = NULL;
gchar *href = g_strdup_printf("#%s", repr_id);
sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true);
g_free(href);
-
- sp_document_done(doc, SP_VERB_DIALOG_LIVE_PATH_EFFECT,
- _("Create and apply path effect"));
}
void
Effect::Effect(LivePathEffectObject *lpeobject)
: oncanvasedit_it(0),
is_visible(_("Is visible?"), _("If unchecked, the effect remains applied to the object but is temporarily disabled on canvas"), "is_visible", &wr, this, true),
+ show_orig_path(false),
lpeobj(lpeobject),
- concatenate_before_pwd2(false)
+ concatenate_before_pwd2(false),
+ provides_own_flash_paths(true), // is automatically set to false if providesOwnFlashPaths() is not overridden
+ is_ready(false) // is automatically set to false if providesOwnFlashPaths() is not overridden
{
registerParameter( dynamic_cast<Parameter *>(&is_visible) );
+ is_visible.widget_is_visible = false;
}
Effect::~Effect()
Glib::ustring
Effect::getName()
{
- if (lpeobj->effecttype_set && lpeobj->effecttype < INVALID_LPE)
+ if (lpeobj->effecttype_set && LPETypeConverter.is_valid_id(lpeobj->effecttype) )
return Glib::ustring( _(LPETypeConverter.get_label(lpeobj->effecttype).c_str()) );
else
return Glib::ustring( _("No effect") );
return lpeobj->effecttype;
}
+/**
+ * Is performed a single time when the effect is freshly applied to a path
+ */
void
Effect::doOnApply (SPLPEItem */*lpeitem*/)
{
- // This is performed once when the effect is freshly applied to a path
}
+/**
+ * Is performed each time before the effect is updated.
+ */
void
Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
{
//Do nothing for simple effects
}
+/**
+ * Effects can have a parameter path set before they are applied by accepting a nonzero number of
+ * mouse clicks. This method activates the pen context, which waits for the specified number of
+ * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
+ */
+void
+Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
+{
+ // switch to pen context
+ SPDesktop *desktop = inkscape_active_desktop(); // TODO: Is there a better method to find the item's desktop?
+ if (!tools_isactive(desktop, TOOLS_FREEHAND_PEN)) {
+ tools_switch(desktop, TOOLS_FREEHAND_PEN);
+ }
+
+ SPEventContext *ec = desktop->event_context;
+ SPPenContext *pc = SP_PEN_CONTEXT(ec);
+ pc->expecting_clicks_for_LPE = this->acceptsNumClicks();
+ pc->waiting_LPE = this;
+ pc->waiting_item = lpeitem;
+ pc->polylines_only = true;
+
+ ec->desktop->messageStack()->flash(Inkscape::INFORMATION_MESSAGE,
+ g_strdup_printf(_("Please specify a parameter path for the LPE '%s' with %d mouse clicks"),
+ getName().c_str(), acceptsNumClicks()));
+}
void
Effect::writeParamsToSVG() {
}
}
+/**
+ * If the effect expects a path parameter (specified by a number of mouse clicks) before it is
+ * applied, this is the method that processes the resulting path. Override it to customize it for
+ * your LPE. But don't forget to call the parent method so that is_ready is set to true!
+ */
+void
+Effect::acceptParamPath (SPPath */*param_path*/) {
+ setReady();
+}
+
/*
* Here be the doEffect function chain:
*/
void
Effect::doEffect (SPCurve * curve)
{
- NArtBpath const *bpath_in = curve->get_bpath();
+ std::vector<Geom::Path> orig_pathv = curve->get_pathvector();
- std::vector<Geom::Path> result_pathv;
+ std::vector<Geom::Path> result_pathv = doEffect_path(orig_pathv);
- try {
- std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(bpath_in);
-
- result_pathv = doEffect_path(orig_pathv);
- }
- catch (std::exception & e) {
- g_warning("Exception during LPE %s execution. \n %s", getName().c_str(), e.what());
- SP_ACTIVE_DESKTOP->messageStack()->flash( Inkscape::WARNING_MESSAGE,
- _("An exception occurred during execution of the Path Effect.") );
- }
-
- curve->set_pathv(result_pathv);
+ curve->set_pathvector(result_pathv);
}
std::vector<Geom::Path>
*/
void
Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
+ using namespace Inkscape::LivePathEffect;
+
+ // add handles provided by the effect itself
+ addKnotHolderEntities(knotholder, desktop, item);
+
+ // add handles provided by the effect's parameters (if any)
+ for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
+ (*p)->addKnotHolderEntities(knotholder, desktop, item);
+ }
+}
+
+void
+Effect::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
+ // TODO: The entities in kh_entity_vector are already instantiated during the call
+ // to registerKnotHolderHandle(), but they are recreated here. Also, we must not
+ // delete them when the knotholder is destroyed, whence the clumsy function
+ // isDeletable(). If we could create entities of different classes dynamically,
+ // this would be much nicer. How to do this?
std::vector<std::pair<KnotHolderEntity*, const char*> >::iterator i;
for (i = kh_entity_vector.begin(); i != kh_entity_vector.end(); ++i) {
KnotHolderEntity *entity = i->first;
@@ -351,18 +506,49 @@ Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
}
}
-void
-Effect::addPointParamHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
- using namespace std;
+/**
+ * Return a vector of PathVectors which contain all helperpaths that should be drawn by the effect.
+ * This is the function called by external code like SPLPEItem.
+ */
+std::vector<Geom::PathVector>
+Effect::getHelperPaths(SPLPEItem *lpeitem)
+{
+ std::vector<Geom::PathVector> hp_vec;
+
+ if (!SP_IS_SHAPE(lpeitem)) {
+// g_print ("How to handle helperpaths for non-shapes?\n"); // non-shapes are for example SPGroups.
+ return hp_vec;
+ }
+
+ // TODO: we can probably optimize this by using a lot more references
+ // rather than copying PathVectors all over the place
+ if (show_orig_path) {
+ // add original path to helperpaths
+ SPCurve* curve = sp_shape_get_curve (SP_SHAPE(lpeitem));
+ hp_vec.push_back(curve->get_pathvector());
+ }
+
+ // add other helperpaths provided by the effect itself
+ addCanvasIndicators(lpeitem, hp_vec);
+
+ // add helperpaths provided by the effect's parameters
for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
- if ((*p)->paramType() == Inkscape::LivePathEffect::POINT_PARAM) {
- KnotHolderEntity *e = dynamic_cast<KnotHolderEntity *>(*p);
- e->create(desktop, item, knotholder);
- knotholder->add(e);
- }
+ (*p)->addCanvasIndicators(lpeitem, hp_vec);
}
+
+ return hp_vec;
}
+/**
+ * Add possible canvas indicators (i.e., helperpaths other than the original path) to \a hp_vec
+ * This function should be overwritten by derived effects if they want to provide their own helperpaths.
+ */
+void
+Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
+{
+}
+
+
/**
* This *creates* a new widget, management of deletion should be done by the caller
*/
std::vector<Parameter *>::iterator it = param_vector.begin();
while (it != param_vector.end()) {
- Parameter * param = *it;
- Gtk::Widget * widg = param->param_newWidget(tooltips);
- Glib::ustring * tip = param->param_getTooltip();
- if (widg) {
- vbox->pack_start(*widg, true, true, 2);
- if (tip != NULL) {
- tooltips->set_tip(*widg, *tip);
+ if ((*it)->widget_is_visible) {
+ Parameter * param = *it;
+ Gtk::Widget * widg = param->param_newWidget(tooltips);
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip != NULL) {
+ tooltips->set_tip(*widg, *tip);
+ }
}
}
void
Effect::resetDefaults(SPItem * /*item*/)
{
- // do nothing for simple effects
-}
-
-void
-Effect::setup_nodepath(Inkscape::NodePath::Path *np)
-{
- np->helperpath_rgba = 0xff0000ff;
- np->helperpath_width = 1.0;
+ std::vector<Inkscape::LivePathEffect::Parameter *>::iterator p;
+ for (p = param_vector.begin(); p != param_vector.end(); ++p) {
+ (*p)->param_set_default();
+ (*p)->write_to_SVG();
+ }
}
void
}
}
+bool
+Effect::providesKnotholder()
+{
+ // does the effect actively provide any knotholder entities of its own?
+ if (kh_entity_vector.size() > 0)
+ return true;
+
+ // otherwise: are there any parameters that have knotholderentities?
+ for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
+ if ((*p)->providesKnotHolderEntities()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
} /* namespace LivePathEffect */
} /* namespace Inkscape */