Code

Split SPCanvasItem and SPCanvasGroup to individual .h files. Removed forward header.
[inkscape.git] / src / live_effects / effect.cpp
index d5ac6dc7485812be8a98564405b48385a204bdcd..3ea57de23f3f3110134db2e8654e8dd82d19c7ca 100644 (file)
@@ -1,14 +1,18 @@
-#define INKSCAPE_LIVEPATHEFFECT_CPP
-
 /*
  * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *   Abhishek Sharma
  *
  * 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 "tools-switch.h"
 #include "message-stack.h"
 #include "desktop.h"
-#include "nodepath.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>
 
@@ -36,7 +39,7 @@
 
 #include <2geom/sbasis-to-bezier.h>
 #include <2geom/matrix.h>
-
+#include <2geom/pathvector.h>
 
 // include effects:
 #include "live_effects/lpe-patternalongpath.h"
@@ -44,6 +47,8 @@
 #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-copy_rotate.h"
 #include "live_effects/lpe-offset.h"
 #include "live_effects/lpe-ruler.h"
-// end of includes
+#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"
+
 
 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"},
-    {MIRROR_SYMMETRY, N_("Mirror symmetry"), "mirror_symmetry"},
-    {CIRCLE_3PTS, N_("Circle through 3 points"), "circle_3pts"},
-    {ANGLE_BISECTOR, N_("Angle bisector"), "angle_bisector"},
-    {PARALLEL, N_("Parallel"), "parallel"},
-    {COPY_ROTATE, N_("Rotate copies"), "copy_rotate"},
-    {OFFSET, N_("Offset"), "offset"},
-    {RULER, N_("Ruler"), "ruler"},
+/* 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)
@@ -112,17 +152,15 @@ 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;
@@ -174,6 +212,36 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
         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;
@@ -187,11 +255,10 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
     return neweffect;
 }
 
-void
-Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
+void Effect::createAndApply(const char* name, SPDocument *doc, SPItem *item)
 {
     // Path effect definition
-    Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
+    Inkscape::XML::Document *xml_doc = doc->getReprDoc();
     Inkscape::XML::Node *repr = xml_doc->createElement("inkscape:path-effect");
     repr->setAttribute("effect", name);
 
@@ -213,13 +280,14 @@ Effect::createAndApply(EffectType type, SPDocument *doc, SPItem *item)
 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),
-      done_pathparam_set(false),
       show_orig_path(false),
       lpeobj(lpeobject),
       concatenate_before_pwd2(false),
-      provides_own_flash_paths(true) // is automatically set to false if providesOwnFlashPaths() is not overridden
+      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()
@@ -229,7 +297,7 @@ 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") );
@@ -260,7 +328,7 @@ Effect::doBeforeEffect (SPLPEItem */*lpeitem*/)
 /**
  * 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::acceptsNumParams() to return the number of expected mouse clicks.
+ * clicks. Override Effect::acceptsNumClicks() to return the number of expected mouse clicks.
  */
 void
 Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
@@ -273,14 +341,14 @@ Effect::doAcceptPathPreparations(SPLPEItem *lpeitem)
 
     SPEventContext *ec = desktop->event_context;
     SPPenContext *pc = SP_PEN_CONTEXT(ec);
-    pc->expecting_clicks_for_LPE = this->acceptsNumParams();
+    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(), acceptsNumParams()));
+                        getName().c_str(), acceptsNumClicks()));
 }
 
 void
@@ -294,11 +362,11 @@ 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 done_pathparam_set is set to true!
+ * 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*/) {
-    done_pathparam_set = true;
+    setReady();
 }
 
 /*
@@ -408,6 +476,24 @@ Effect::registerKnotHolderHandle(KnotHolderEntity* entity, const char* descr)
  */
 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;
@@ -418,56 +504,49 @@ Effect::addHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
     }
 }
 
-void
-Effect::addPointParamHandles(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item) {
-    using namespace Inkscape::LivePathEffect;
-    for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
-        if ((*p)->paramType() == POINT_PARAM) {
-            PointParam *pparam = static_cast<PointParam *>(*p);
-            KnotHolderEntity *e = dynamic_cast<KnotHolderEntity *>(*p);
-            e->create(desktop, item, knotholder, pparam->handleTip(),
-                      pparam->knotShape(), pparam->knotMode(), pparam->knotColor());
-            knotholder->add(e);
-        }
+/**
+ * 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;
     }
-}
 
-void
-Effect::addHelperPaths(SPLPEItem *lpeitem, SPDesktop *desktop)
-{
-    g_return_if_fail(desktop);
-    g_return_if_fail(SP_IS_PATH(lpeitem));
-
-    if (providesKnotholder() && showOrigPath()) {
-        // TODO: we assume that if the LPE provides its own knotholder, there is no nodepath so we
-        // must create the helper curve for the original path manually; once we allow nodepaths and
-        // knotholders alongside each other, this needs to be rethought!
-        SPCanvasItem *canvasitem = sp_nodepath_generate_helperpath(desktop, SP_PATH(lpeitem));
-        Inkscape::Display::TemporaryItem* tmpitem = desktop->add_temporary_canvasitem (canvasitem, 0);
-        lpeitem->lpe_helperpaths.push_back(tmpitem);
+    // 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(lpeitem)->getCurve ();
+        hp_vec.push_back(curve->get_pathvector());
     }
 
-    for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
-        if ((*p)->paramType() == Inkscape::LivePathEffect::PATH_PARAM) {
-            SPCurve *c = new SPCurve(static_cast<Inkscape::LivePathEffect::PathParam*>(*p)->get_pathvector());
+    // add other helperpaths provided by the effect itself
+    addCanvasIndicators(lpeitem, hp_vec);
 
-            // TODO: factor this out (also the copied code above); see also lpe-lattice.cpp
-            SPCanvasItem *canvasitem = sp_nodepath_generate_helperpath(desktop, c, SP_ITEM(lpeitem), 0x009000ff);
-            Inkscape::Display::TemporaryItem* tmpitem = desktop->add_temporary_canvasitem (canvasitem, 0);
-            lpeitem->lpe_helperpaths.push_back(tmpitem);
-        }
+    // add helperpaths provided by the effect's parameters
+    for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
+        (*p)->addCanvasIndicators(lpeitem, hp_vec);
     }
 
-    addHelperPathsImpl(lpeitem, desktop);
+    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::addHelperPathsImpl(SPLPEItem */*lpeitem*/, SPDesktop */*desktop*/)
+Effect::addCanvasIndicators(SPLPEItem */*lpeitem*/, std::vector<Geom::PathVector> &/*hp_vec*/)
 {
-    // if this method is overloaded in derived classes, provides_own_flash_paths will be true
-    provides_own_flash_paths = false;
 }
 
+
 /**
  * This *creates* a new widget, management of deletion should be done by the caller
  */
@@ -481,13 +560,15 @@ Effect::newWidget(Gtk::Tooltips * tooltips)
 
     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);
+                }
             }
         }
 
@@ -579,14 +660,11 @@ Effect::editNextParamOncanvas(SPItem * item, SPDesktop * desktop)
 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
@@ -606,9 +684,9 @@ Effect::providesKnotholder()
     if (kh_entity_vector.size() > 0)
         return true;
 
-    // otherwise: are there any PointParams?
+    // otherwise: are there any parameters that have knotholderentities?
     for (std::vector<Parameter *>::iterator p = param_vector.begin(); p != param_vector.end(); ++p) {
-        if ((*p)->paramType() == Inkscape::LivePathEffect::POINT_PARAM) {
+        if ((*p)->providesKnotHolderEntities()) {
             return true;
         }
     }