summaryrefslogtreecommitdiffstats
path: root/src/live_effects
diff options
context:
space:
mode:
authorJohan B. C. Engelen <jbc.engelen@swissonline.ch>2007-08-14 20:54:48 +0000
committerjohanengelen <johanengelen@users.sourceforge.net>2007-08-14 20:54:48 +0000
commit55d43e4e27e0ba58a47fad70957dfa989aa173ad (patch)
tree2ccfbac1c50023d08ae32975c876fa2478c1ad2a /src/live_effects
parentFix for bug #1752113; added set_preview_widget_active(false) to FileSaveDialo... (diff)
downloadinkscape-55d43e4e27e0ba58a47fad70957dfa989aa173ad.tar.gz
inkscape-55d43e4e27e0ba58a47fad70957dfa989aa173ad.zip
Commit LivePathEffect branch to trunk!
(disabled extension/internal/bitmap/*.* in build.xml to fix compilation) (bzr r3472)
Diffstat (limited to 'src/live_effects')
-rw-r--r--src/live_effects/Makefile_insert27
-rw-r--r--src/live_effects/effect.cpp232
-rw-r--r--src/live_effects/effect.h108
-rw-r--r--src/live_effects/lpe-gears.cpp266
-rw-r--r--src/live_effects/lpe-gears.h38
-rw-r--r--src/live_effects/lpe-skeletalstrokes.cpp161
-rw-r--r--src/live_effects/lpe-skeletalstrokes.h49
-rw-r--r--src/live_effects/lpe-slant.cpp62
-rw-r--r--src/live_effects/lpe-slant.h40
-rw-r--r--src/live_effects/lpe-test-doEffect-stack.cpp96
-rw-r--r--src/live_effects/lpe-test-doEffect-stack.h42
-rw-r--r--src/live_effects/lpeobject-reference.cpp161
-rw-r--r--src/live_effects/lpeobject-reference.h71
-rw-r--r--src/live_effects/lpeobject.cpp260
-rw-r--r--src/live_effects/lpeobject.h52
-rw-r--r--src/live_effects/makefile.in17
-rw-r--r--src/live_effects/n-art-bpath-2geom.cpp416
-rw-r--r--src/live_effects/n-art-bpath-2geom.h33
-rw-r--r--src/live_effects/parameter/Makefile_insert18
-rw-r--r--src/live_effects/parameter/enum.h87
-rw-r--r--src/live_effects/parameter/makefile.in17
-rw-r--r--src/live_effects/parameter/parameter.cpp105
-rw-r--r--src/live_effects/parameter/parameter.h84
-rw-r--r--src/live_effects/parameter/path.cpp166
-rw-r--r--src/live_effects/parameter/path.h56
-rw-r--r--src/live_effects/parameter/point.cpp166
-rw-r--r--src/live_effects/parameter/point.h57
-rw-r--r--src/live_effects/todo.txt19
28 files changed, 2906 insertions, 0 deletions
diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert
new file mode 100644
index 000000000..f285416e2
--- /dev/null
+++ b/src/live_effects/Makefile_insert
@@ -0,0 +1,27 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+live_effects/all: live_effects/liblive_effects.a live_effects/parameter/all
+
+live_effects/clean:
+ rm -f live_effects/liblive_effects.a $(live_effects_liblive_effects_a_OBJECTS)
+
+live_effects_liblive_effects_a_SOURCES = \
+ live_effects/effect.cpp \
+ live_effects/effect.h \
+ live_effects/lpeobject.cpp \
+ live_effects/lpeobject.h \
+ live_effects/lpeobject-reference.cpp \
+ live_effects/lpeobject-reference.h \
+ live_effects/n-art-bpath-2geom.cpp \
+ live_effects/n-art-bpath-2geom.h \
+ live_effects/lpe-skeletalstrokes.cpp \
+ live_effects/lpe-skeletalstrokes.h \
+ live_effects/lpe-gears.cpp \
+ live_effects/lpe-gears.h \
+ live_effects/lpe-test-doEffect-stack.cpp \
+ live_effects/lpe-test-doEffect-stack.h \
+ live_effects/lpe-slant.cpp \
+ live_effects/lpe-slant.h
+
+
+
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
new file mode 100644
index 000000000..a0a8f3db9
--- /dev/null
+++ b/src/live_effects/effect.cpp
@@ -0,0 +1,232 @@
+#define INKSCAPE_LIVEPATHEFFECT_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "display/display-forward.h"
+#include "xml/node-event-vector.h"
+#include "sp-object.h"
+#include "attributes.h"
+
+#include "desktop.h"
+
+#include "document.h"
+#include <glibmm/i18n.h>
+
+#include "live_effects/effect.h"
+#include "live_effects/lpeobject.h"
+#include "live_effects/parameter/parameter.h"
+#include <glibmm/ustring.h>
+#include "live_effects/n-art-bpath-2geom.h"
+#include "display/curve.h"
+#include <2geom/sbasis-to-bezier.h>
+#include <gtkmm.h>
+
+// include effects:
+#include "live_effects/lpe-skeletalstrokes.h"
+#include "live_effects/lpe-slant.h"
+#include "live_effects/lpe-test-doEffect-stack.h"
+#include "live_effects/lpe-gears.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+const Util::EnumData<EffectType> LPETypeData[ENDTYPE_LPE] = {
+ {INVALID_LPE, _("Invalid effect"), "invalid"},
+ {SKELETAL_STROKES, _("Skeletal Strokes"), "skeletal"},
+ {SLANT, _("Slant"), "slant"},
+ {DOEFFECTSTACK_TEST, _("doEffect stack test"), "doeffectstacktest"},
+ {GEARS, _("Gears"), "gears"}
+};
+const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, ENDTYPE_LPE);
+
+Effect*
+Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
+{
+ switch (lpenr) {
+ case INVALID_LPE:
+ g_warning("LivePathEffect::Effect::New called with invalid patheffect type");
+ return NULL;
+ case SKELETAL_STROKES:
+ return (Effect*) new LPESkeletalStrokes(lpeobj);
+ case SLANT:
+ return (Effect*) new LPESlant(lpeobj);
+ case DOEFFECTSTACK_TEST:
+ return (Effect*) new LPEdoEffectStackTest(lpeobj);
+ case GEARS:
+ return (Effect*) new LPEGears(lpeobj);
+ case ENDTYPE_LPE:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+Effect::Effect(LivePathEffectObject *lpeobject)
+{
+ vbox = NULL;
+ tooltips = NULL;
+ lpeobj = lpeobject;
+}
+
+Effect::~Effect()
+{
+ if (tooltips) {
+ delete tooltips;
+ }
+}
+
+Glib::ustring
+Effect::getName()
+{
+ return Glib::ustring( LPETypeConverter.get_label(lpeobj->effecttype) );
+}
+
+/*
+ * Here be the doEffect function chain:
+ */
+void
+Effect::doEffect (SPCurve * curve)
+{
+ NArtBpath *new_bpath = doEffect(SP_CURVE_BPATH(curve));
+
+ if (new_bpath) { // FIXME, add function to SPCurve to change bpath? or a copy function?
+ if (curve->_bpath) {
+ g_free(curve->_bpath); //delete old bpath
+ }
+ curve->_bpath = new_bpath;
+ }
+}
+
+NArtBpath *
+Effect::doEffect (NArtBpath * path_in)
+{
+ std::vector<Geom::Path> orig_pathv = BPath_to_2GeomPath(path_in);
+
+ std::vector<Geom::Path> result_pathv = doEffect(orig_pathv);
+
+ NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv);
+
+ return new_bpath;
+}
+
+std::vector<Geom::Path>
+Effect::doEffect (std::vector<Geom::Path> & path_in)
+{
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_in;
+ // FIXME: find standard function to convert std::vector<Geom::Path> ==> Piecewise< D2<SBasis> >
+ for (unsigned int i=0; i < path_in.size(); i++) {
+ pwd2_in.concat( path_in[i].toPwSb() );
+ }
+
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > pwd2_out = doEffect(pwd2_in);
+
+ std::vector<Geom::Path> path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE);
+
+ return path_out;
+}
+
+Geom::Piecewise<Geom::D2<Geom::SBasis> >
+Effect::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
+{
+ g_warning("Effect has no doEffect implementation");
+ return pwd2_in;
+}
+
+void
+Effect::readallParameters(Inkscape::XML::Node * repr)
+{
+ param_map_type::iterator it = param_map.begin();
+ while (it != param_map.end()) {
+ const gchar * key = (*it).first.c_str();
+ const gchar * value = repr->attribute(key);
+ if(value) {
+ setParameter(repr, key, NULL, value);
+ }
+ it++;
+ }
+}
+
+void
+Effect::setParameter(Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value)
+{
+ Glib::ustring stringkey(key);
+
+ param_map_type::iterator it = param_map.find(stringkey);
+ if (it != param_map.end()) {
+ bool accepted = it->second->param_readSVGValue(new_value);
+ /* think: can this backfire and create infinite loop when started with unacceptable old_value?
+ if (!accepted) { // change was not accepted, so change it back.
+ repr->setAttribute(key, old_value);
+ } */
+ }
+}
+
+void
+Effect::registerParameter(Parameter * param)
+{
+ param_map[param->param_key] = param; // inserts or updates
+}
+
+Gtk::Widget *
+Effect::getWidget()
+{
+ if (!vbox) {
+ vbox = Gtk::manage( new Gtk::VBox() ); // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
+ //if (!tooltips)
+ tooltips = new Gtk::Tooltips();
+
+ vbox->set_border_width(5);
+
+ param_map_type::iterator it = param_map.begin();
+ while (it != param_map.end()) {
+ Parameter * param = it->second;
+ Gtk::Widget * widg = param->param_getWidget();
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip != NULL) {
+ tooltips->set_tip(*widg, *tip);
+ }
+ }
+
+ it++;
+ }
+ }
+
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+
+Inkscape::XML::Node *
+Effect::getRepr()
+{
+ return SP_OBJECT_REPR(lpeobj);
+}
+
+SPDocument *
+Effect::getSPDoc()
+{
+ if (SP_OBJECT_DOCUMENT(lpeobj) == NULL) g_message("oh crap");
+ return SP_OBJECT_DOCUMENT(lpeobj);
+}
+
+
+}; /* namespace LivePathEffect */
+
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h
new file mode 100644
index 000000000..0ebd5d5a5
--- /dev/null
+++ b/src/live_effects/effect.h
@@ -0,0 +1,108 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_H
+#define INKSCAPE_LIVEPATHEFFECT_H
+
+/*
+ * Inkscape::LivePathEffect
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+
+#include "display/display-forward.h"
+#include <map>
+#include <glibmm/ustring.h>
+#include <2geom/path.h>
+#include "ui/widget/registry.h"
+#include "util/enums.h"
+
+#define LPE_CONVERSION_TOLERANCE 0.01 // FIXME: find good solution for this.
+
+struct SPShape;
+struct SPDocument;
+class NArtBpath;
+struct LivePathEffectObject;
+
+namespace Gtk {
+ class Widget;
+ class VBox;
+ class Tooltips;
+}
+
+namespace Inkscape {
+
+namespace XML {
+ class Node;
+};
+
+namespace LivePathEffect {
+
+enum EffectType {
+ INVALID_LPE = 0,
+ SKELETAL_STROKES,
+ SLANT,
+ DOEFFECTSTACK_TEST,
+ GEARS,
+ ENDTYPE_LPE // This must be last
+};
+
+extern const Util::EnumData<EffectType> LPETypeData[ENDTYPE_LPE];
+extern const Util::EnumDataConverter<EffectType> LPETypeConverter;
+
+class Parameter;
+
+class Effect {
+public:
+ virtual ~Effect();
+
+ Glib::ustring getName();
+
+ virtual void doEffect (SPCurve * curve);
+
+ static Effect* New(EffectType lpenr, LivePathEffectObject *lpeobj);
+
+ virtual Gtk::Widget * getWidget();
+
+ Inkscape::XML::Node * getRepr();
+ SPDocument * getSPDoc();
+
+ void readallParameters(Inkscape::XML::Node * repr);
+ void setParameter(Inkscape::XML::Node * repr, const gchar * key, const gchar * old_value, const gchar * new_value);
+
+protected:
+ Effect(LivePathEffectObject *lpeobject);
+
+ // provide a set of doEffect functions so the developer has a choice
+ // of what kind of input/output parameters he desires.
+ // the order in which they appear is the order in which they are
+ // called by this base class. (i.e. doEffect(SPCurve * curve) defaults to calling
+ // doEffect(std::vector<Geom::Path> )
+ virtual NArtBpath *
+ doEffect (NArtBpath * path_in);
+ virtual std::vector<Geom::Path>
+ doEffect (std::vector<Geom::Path> & path_in);
+ virtual Geom::Piecewise<Geom::D2<Geom::SBasis> >
+ doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in);
+
+ void registerParameter(Parameter * param);
+
+ typedef std::map<Glib::ustring, Parameter *> param_map_type;
+ param_map_type param_map;
+
+ Inkscape::UI::Widget::Registry wr;
+ Gtk::VBox * vbox;
+ Gtk::Tooltips * tooltips;
+
+ LivePathEffectObject *lpeobj;
+
+private:
+ Effect(const Effect&);
+ Effect& operator=(const Effect&);
+};
+
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-gears.cpp b/src/live_effects/lpe-gears.cpp
new file mode 100644
index 000000000..8da819ef6
--- /dev/null
+++ b/src/live_effects/lpe-gears.cpp
@@ -0,0 +1,266 @@
+#define INKSCAPE_LPE_DOEFFECT_STACK_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-gears.h"
+
+#include <vector>
+#include <2geom/d2.h>
+#include <2geom/sbasis.h>
+#include <2geom/bezier-to-sbasis.h>
+#include <2geom/path.h>
+
+using std::vector;
+using namespace Geom;
+
+class Gear {
+public:
+ // pitch circles touch on two properly meshed gears
+ // all measurements are taken from the pitch circle
+ double pitch_diameter() {return (_number_of_teeth * _module) / M_PI;}
+ double pitch_radius() {return pitch_diameter() / 2.0;}
+ void pitch_radius(double R) {_module = (2 * M_PI * R) / _number_of_teeth;}
+
+ // base circle serves as the basis for the involute toothe profile
+ double base_diameter() {return pitch_diameter() * cos(_pressure_angle);}
+ double base_radius() {return base_diameter() / 2.0;}
+
+ // diametrical pitch
+ double diametrical_pitch() {return _number_of_teeth / pitch_diameter();}
+
+ // height of the tooth above the pitch circle
+ double addendum() {return 1.0 / diametrical_pitch();}
+ // depth of the tooth below the pitch circle
+ double dedendum() {return addendum() + _clearance;}
+
+ // root circle specifies the bottom of the fillet between teeth
+ double root_radius() {return pitch_radius() - dedendum();}
+ double root_diameter() {return root_radius() * 2.0;}
+
+ // outer circle is the outside diameter of the gear
+ double outer_radius() {return pitch_radius() + addendum();}
+ double outer_diameter() {return outer_radius() * 2.0;}
+
+ // angle covered by the tooth on the pitch circle
+ double tooth_thickness_angle() {return M_PI / _number_of_teeth;}
+
+ Geom::Point centre() {return _centre;}
+ void centre(Geom::Point c) {_centre = c;}
+
+ double angle() {return _angle;}
+ void angle(double a) {_angle = a;}
+
+ int number_of_teeth() {return _number_of_teeth;}
+
+ Geom::Path path();
+ Gear spawn(Geom::Point p);
+
+ Gear(int n, double m, double phi) {
+ _number_of_teeth = n;
+ _module = m;
+ _pressure_angle = phi;
+ _clearance = 0.0;
+ _angle = 0.0;
+ _centre = Geom::Point(0.0,0.0);
+ }
+private:
+ int _number_of_teeth;
+ double _pressure_angle;
+ double _module;
+ double _clearance;
+ double _angle;
+ Geom::Point _centre;
+ D2<SBasis> _involute(double start, double stop) {
+ D2<SBasis> B;
+ D2<SBasis> I;
+ Linear bo = Linear(start,stop);
+
+ B[0] = cos(bo,2);
+ B[1] = sin(bo,2);
+
+ I = B - Linear(0,1) * derivative(B);
+ I = I*base_radius() + _centre;
+ return I;
+ }
+ D2<SBasis> _arc(double start, double stop, double R) {
+ D2<SBasis> B;
+ Linear bo = Linear(start,stop);
+
+ B[0] = cos(bo,2);
+ B[1] = sin(bo,2);
+
+ B = B*R + _centre;
+ return B;
+ }
+ // angle of the base circle used to create the involute to a certain radius
+ double involute_swath_angle(double R) {
+ if (R <= base_radius()) return 0.0;
+ return sqrt(R*R - base_radius()*base_radius())/base_radius();
+ }
+
+ // angle of the base circle between the origin of the involute and the intersection on another radius
+ double involute_intersect_angle(double R) {
+ if (R <= base_radius()) return 0.0;
+ return (sqrt(R*R - base_radius()*base_radius())/base_radius()) - acos(base_radius()/R);
+ }
+};
+
+void makeContinuous(D2<SBasis> &a, Point const b) {
+ for(unsigned d=0;d<2;d++)
+ a[d][0][0] = b[d];
+}
+
+Geom::Path Gear::path() {
+ Geom::Path pb;
+
+ // angle covered by a full tooth and fillet
+ double tooth_rotation = 2.0 * tooth_thickness_angle();
+ // angle covered by an involute
+ double involute_advance = involute_intersect_angle(outer_radius()) - involute_intersect_angle(root_radius());
+ // angle covered by the tooth tip
+ double tip_advance = tooth_thickness_angle() - (2 * (involute_intersect_angle(outer_radius()) - involute_intersect_angle(pitch_radius())));
+ // angle covered by the toothe root
+ double root_advance = (tooth_rotation - tip_advance) - (2.0 * involute_advance);
+ // begin drawing the involute at t if the root circle is larger than the base circle
+ double involute_t = involute_swath_angle(root_radius())/involute_swath_angle(outer_radius());
+
+ //rewind angle to start drawing from the leading edge of the tooth
+ double first_tooth_angle = _angle - ((0.5 * tip_advance) + involute_advance);
+
+ Geom::Point prev;
+ for (int i=0; i < _number_of_teeth; i++)
+ {
+ double cursor = first_tooth_angle + (i * tooth_rotation);
+
+ D2<SBasis> leading_I = compose(_involute(cursor, cursor + involute_swath_angle(outer_radius())), Linear(involute_t,1));
+ if(i != 0) makeContinuous(leading_I, prev);
+ pb.append(SBasisCurve(leading_I));
+ cursor += involute_advance;
+ prev = leading_I.at1();
+
+ D2<SBasis> tip = _arc(cursor, cursor+tip_advance, outer_radius());
+ makeContinuous(tip, prev);
+ pb.append(SBasisCurve(tip));
+ cursor += tip_advance;
+ prev = tip.at1();
+
+ cursor += involute_advance;
+ D2<SBasis> trailing_I = compose(_involute(cursor, cursor - involute_swath_angle(outer_radius())), Linear(1,involute_t));
+ makeContinuous(trailing_I, prev);
+ pb.append(SBasisCurve(trailing_I));
+ prev = trailing_I.at1();
+
+ if (base_radius() > root_radius()) {
+ Geom::Point leading_start = trailing_I.at1();
+ Geom::Point leading_end = (root_radius() * unit_vector(leading_start - _centre)) + _centre;
+ prev = leading_end;
+ pb.appendNew<LineSegment>(leading_end);
+ }
+
+ D2<SBasis> root = _arc(cursor, cursor+root_advance, root_radius());
+ makeContinuous(root, prev);
+ pb.append(SBasisCurve(root));
+ cursor += root_advance;
+ prev = root.at1();
+
+ if (base_radius() > root_radius()) {
+ Geom::Point trailing_start = root.at1();
+ Geom::Point trailing_end = (base_radius() * unit_vector(trailing_start - _centre)) + _centre;
+ pb.appendNew<LineSegment>(trailing_end);
+ prev = trailing_end;
+ }
+ }
+
+ return pb;
+}
+
+Gear Gear::spawn(Geom::Point p) {
+ double radius = Geom::distance(this->centre(), p) - this->pitch_radius();
+ int N = (int) floor( (radius / this->pitch_radius()) * this->number_of_teeth() );
+
+ Gear gear(N, _module, _pressure_angle);
+ gear.centre(p);
+
+ double a = atan2(p - this->centre());
+ double new_angle = 0.0;
+ if (gear.number_of_teeth() % 2 == 0)
+ new_angle -= gear.tooth_thickness_angle();
+ new_angle -= (_angle) * (pitch_radius() / gear.pitch_radius());
+ new_angle += (a) * (pitch_radius() / gear.pitch_radius());
+ gear.angle(new_angle + a);
+ return gear;
+}
+
+
+
+// #################################################################
+
+
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+
+LPEGears::LPEGears(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ teeth(_("Teeth"), _("The number of teeth"), "teeth", &wr, this, 10),
+ phi(_("Phi"), _("???"), "phi", &wr, this, 5)
+{
+ registerParameter( dynamic_cast<Parameter *>(&teeth) );
+ registerParameter( dynamic_cast<Parameter *>(&phi) );
+}
+
+LPEGears::~LPEGears()
+{
+
+}
+
+std::vector<Geom::Path>
+LPEGears::doEffect (std::vector<Geom::Path> & path_in)
+{
+ std::vector<Geom::Path> path_out;
+ Geom::Path gearpath = path_in[0];
+
+ Geom::Path::iterator it(gearpath.begin());
+ if ( it == gearpath.end() ) return path_out;
+
+ Gear * gear = new Gear(teeth, 200.0, phi * M_PI / 180);
+ Geom::Point gear_centre = (*it).finalPoint();
+ gear->centre(gear_centre);
+ gear->angle(atan2((*it).initialPoint() - gear_centre));
+
+ it++; if ( it == gearpath.end() ) return path_out;
+ gear->pitch_radius(Geom::distance(gear_centre, (*it).finalPoint()));
+
+ path_out.push_back( gear->path());
+
+ for (it++ ; it != gearpath.end() ; it++) {
+ // iterate through Geom::Curve in path_in
+ Gear* gearnew = new Gear(gear->spawn( (*it).finalPoint() ));
+ path_out.push_back( gearnew->path() );
+ delete gear;
+ gear = gearnew;
+ }
+ delete gear;
+
+ return path_out;
+}
+
+
+}; // namespace LivePathEffect
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-gears.h b/src/live_effects/lpe-gears.h
new file mode 100644
index 000000000..81a4ef058
--- /dev/null
+++ b/src/live_effects/lpe-gears.h
@@ -0,0 +1,38 @@
+#ifndef INKSCAPE_LPE_GEARS_H
+#define INKSCAPE_LPE_GEARS_H
+
+/*
+ * Inkscape::LPEGears
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+*
+*
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEGears : public Effect {
+public:
+ LPEGears(LivePathEffectObject *lpeobject);
+ ~LPEGears();
+
+ std::vector<Geom::Path> doEffect (std::vector<Geom::Path> & path_in);
+
+private:
+ RealParam teeth;
+ RealParam phi;
+
+ LPEGears(const LPEGears&);
+ LPEGears& operator=(const LPEGears&);
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-skeletalstrokes.cpp b/src/live_effects/lpe-skeletalstrokes.cpp
new file mode 100644
index 000000000..47c73a56b
--- /dev/null
+++ b/src/live_effects/lpe-skeletalstrokes.cpp
@@ -0,0 +1,161 @@
+#define INKSCAPE_LPE_SKELETAL_STROKES_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-skeletalstrokes.h"
+#include "sp-shape.h"
+#include "display/curve.h"
+#include <libnr/n-art-bpath.h>
+#include "live_effects/n-art-bpath-2geom.h"
+#include "svg/svg.h"
+
+#include <2geom/sbasis.h>
+#include <2geom/sbasis-geometric.h>
+#include <2geom/bezier-to-sbasis.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/d2.h>
+#include <2geom/piecewise.h>
+
+#include <algorithm>
+using std::vector;
+
+
+/* Theory in e-mail from J.F. Barraud
+Let B be the skeleton path, and P the pattern (the path to be deformed).
+
+P is a map t --> P(t) = ( x(t), y(t) ).
+B is a map t --> B(t) = ( a(t), b(t) ).
+
+The first step is to re-parametrize B by its arc length: this is the parametrization in which a point p on B is located by its distance s from start. One obtains a new map s --> U(s) = (a'(s),b'(s)), that still describes the same path B, but where the distance along B from start to
+U(s) is s itself.
+
+We also need a unit normal to the path. This can be obtained by computing a unit tangent vector, and rotate it by 90°. Call this normal vector N(s).
+
+The basic deformation associated to B is then given by:
+
+ (x,y) --> U(x)+y*N(x)
+
+(i.e. we go for distance x along the path, and then for distance y along the normal)
+
+Of course this formula needs some minor adaptations (as is it depends on the absolute position of P for instance, so a little translation is needed
+first) but I think we can first forget about them.
+*/
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+static const Util::EnumData<SkelCopyType> SkelCopyTypeData[SSCT_END] = {
+ {SSCT_SINGLE, N_("Single"), "single"},
+ {SSCT_SINGLE_STRETCHED, N_("Single, stretched"), "single_stretched"},
+ {SSCT_REPEATED, N_("Repeated"), "repeated"},
+ {SSCT_REPEATED_STRETCHED, N_("Repeated, stretched"), "repeated_stretched"}
+};
+static const Util::EnumDataConverter<SkelCopyType> SkelCopyTypeConverter(SkelCopyTypeData, SSCT_END);
+
+LPESkeletalStrokes::LPESkeletalStrokes(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ pattern(_("Pattern"), _("Pattern to put along path"), "pattern", &wr, this),
+ origin(_("Origin"), _("Origin of ?"), "origin", &wr, this),
+ copytype(_("Copytype"), _("tooltip"), "copytype", SkelCopyTypeConverter, &wr, this)
+{
+ registerParameter( dynamic_cast<Parameter *>(&origin) );
+ registerParameter( dynamic_cast<Parameter *>(&pattern) );
+ registerParameter( dynamic_cast<Parameter *>(&copytype) );
+
+ pattern.signal_path_pasted.connect(sigc::mem_fun(*this, &LPESkeletalStrokes::on_pattern_pasted));
+}
+
+LPESkeletalStrokes::~LPESkeletalStrokes()
+{
+
+}
+
+
+Geom::Piecewise<Geom::D2<Geom::SBasis> >
+LPESkeletalStrokes::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
+{
+ using namespace Geom;
+
+/* LOTS OF CODE COPIED FROM 2geom/src/toys/path-along-path.cpp
+ * All credits should go to jfb and mgsloan of lib2geom development! */
+
+ const Util::EnumData<SkelCopyType> * data = copytype.get_selected_data();
+ if (!data)
+ return pwd2_in;
+ SkelCopyType type = data->id;
+
+ Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(Piecewise<D2<SBasis> >(pwd2_in),2,.1);
+ uskeleton = remove_short_cuts(uskeleton,.01);
+ Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton));
+ n = force_continuity(remove_short_cuts(n,.1));
+
+ D2<Piecewise<SBasis> > patternd2 = make_cuts_independant(pattern);
+ Piecewise<SBasis> x=Piecewise<SBasis>(patternd2[0]-origin[0]);
+ Piecewise<SBasis> y=Piecewise<SBasis>(patternd2[1]-origin[1]);
+ Interval pattBnds = bounds_exact(x);
+
+ int nbCopies = int(uskeleton.cuts.back()/pattBnds.extent());
+ double scaling = 1;
+
+ switch(type) {
+ case SSCT_REPEATED:
+ break;
+
+ case SSCT_SINGLE:
+ nbCopies = (nbCopies > 0) ? 1 : 0;
+ break;
+
+ case SSCT_SINGLE_STRETCHED:
+ nbCopies = 1;
+ scaling = uskeleton.cuts.back()/pattBnds.extent();
+ break;
+
+ case SSCT_REPEATED_STRETCHED:
+ scaling = uskeleton.cuts.back()/(((double)nbCopies)*pattBnds.extent());
+ break;
+
+ default:
+ return pwd2_in;
+ };
+
+ double pattWidth = pattBnds.extent() * scaling;
+
+ x-=pattBnds.min();
+ if (scaling != 1)
+ x*=scaling;
+
+ double offs = 0;
+ Piecewise<D2<SBasis> >output;
+ for (int i=0; i<nbCopies; i++){
+ output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs));
+ offs+=pattWidth;
+ }
+ return output;
+}
+
+void
+LPESkeletalStrokes::on_pattern_pasted()
+{
+ // a new pattern was pasted through the dialog. overwrite the origin thingie to the first point of the path
+ origin.param_setValue(pattern.valueAt(0));
+}
+
+
+
+}; // namespace LivePathEffect
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-skeletalstrokes.h b/src/live_effects/lpe-skeletalstrokes.h
new file mode 100644
index 000000000..159a82f57
--- /dev/null
+++ b/src/live_effects/lpe-skeletalstrokes.h
@@ -0,0 +1,49 @@
+#ifndef INKSCAPE_LPE_SKELETAL_STROKES_H
+#define INKSCAPE_LPE_SKELETAL_STROKES_H
+
+/*
+ * Inkscape::LPESkeletalStrokes
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/path.h"
+#include "live_effects/parameter/point.h"
+#include "live_effects/parameter/enum.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+enum SkelCopyType {
+ SSCT_SINGLE = 0,
+ SSCT_SINGLE_STRETCHED,
+ SSCT_REPEATED,
+ SSCT_REPEATED_STRETCHED,
+ SSCT_END // This must be last
+};
+
+class LPESkeletalStrokes : public Effect {
+public:
+ LPESkeletalStrokes(LivePathEffectObject *lpeobject);
+ ~LPESkeletalStrokes();
+
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in);
+
+private:
+ PathParam pattern;
+ PointParam origin;
+ EnumParam<SkelCopyType> copytype;
+
+ void on_pattern_pasted();
+
+ LPESkeletalStrokes(const LPESkeletalStrokes&);
+ LPESkeletalStrokes& operator=(const LPESkeletalStrokes&);
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-slant.cpp b/src/live_effects/lpe-slant.cpp
new file mode 100644
index 000000000..93107f81a
--- /dev/null
+++ b/src/live_effects/lpe-slant.cpp
@@ -0,0 +1,62 @@
+#define INKSCAPE_LPE_SLANT_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-slant.h"
+#include "sp-shape.h"
+#include "display/curve.h"
+#include <libnr/n-art-bpath.h>
+#include "live_effects/n-art-bpath-2geom.h"
+
+#include <2geom/path.h>
+#include <2geom/transforms.h>
+
+#include "svg/svg.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPESlant::LPESlant(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ factor(_("Slant factor"), _("y = y + x*(slant factor)"), "factor", &wr, this),
+ center(_("Center"), _("The x-coord of this point is around which the slant will happen"), "center", &wr, this)
+{
+ registerParameter( dynamic_cast<Parameter *>(&factor) );
+ registerParameter( dynamic_cast<Parameter *>(&center) );
+}
+
+LPESlant::~LPESlant()
+{
+}
+
+void
+LPESlant::doEffect(SPCurve * curve)
+{
+ NArtBpath *bpath = curve->_bpath;
+ int i = 0;
+ while(bpath[i].code != NR_END) {
+ bpath[i].y1 += (bpath[i].x1-center[Geom::X]) * factor;
+ bpath[i].y2 += (bpath[i].x2-center[Geom::X]) * factor;
+ bpath[i].y3 += (bpath[i].x3-center[Geom::X]) * factor;
+ i++;
+ }
+
+}
+
+}; //namespace LivePathEffect
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-slant.h b/src/live_effects/lpe-slant.h
new file mode 100644
index 000000000..95d4420fa
--- /dev/null
+++ b/src/live_effects/lpe-slant.h
@@ -0,0 +1,40 @@
+#ifndef INKSCAPE_LPE_SLANT_H
+#define INKSCAPE_LPE_SLANT_H
+
+/*
+ * Inkscape::LPESlant
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/point.h"
+#include "ui/widget/registered-widget.h"
+
+
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPESlant : public Effect {
+public:
+ LPESlant(LivePathEffectObject *lpeobject);
+ ~LPESlant();
+
+ void doEffect(SPCurve * curve);
+
+private:
+ RealParam factor;
+ PointParam center;
+
+ LPESlant(const LPESlant&);
+ LPESlant& operator=(const LPESlant&);
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-test-doEffect-stack.cpp b/src/live_effects/lpe-test-doEffect-stack.cpp
new file mode 100644
index 000000000..2e3d12725
--- /dev/null
+++ b/src/live_effects/lpe-test-doEffect-stack.cpp
@@ -0,0 +1,96 @@
+#define INKSCAPE_LPE_DOEFFECT_STACK_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-test-doEffect-stack.h"
+
+#include <2geom/piecewise.h>
+#include <vector>
+#include <libnr/n-art-bpath.h>
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+
+LPEdoEffectStackTest::LPEdoEffectStackTest(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ step(_("Stack step"), _(""), "step", &wr, this)
+{
+ registerParameter( dynamic_cast<Parameter *>(&step) );
+}
+
+LPEdoEffectStackTest::~LPEdoEffectStackTest()
+{
+
+}
+
+void
+LPEdoEffectStackTest::doEffect (SPCurve * curve)
+{
+ if (step >= 1) {
+ Effect::doEffect(curve);
+ } else {
+ // return here
+ return;
+ }
+}
+
+NArtBpath *
+LPEdoEffectStackTest::doEffect (NArtBpath * path_in)
+{
+ if (step >= 2) {
+ return Effect::doEffect(path_in);
+ } else {
+ // return here
+ NArtBpath *path_out;
+
+ unsigned ret = 0;
+ while ( path_in[ret].code != NR_END ) {
+ ++ret;
+ }
+ unsigned len = ++ret;
+
+ path_out = g_new(NArtBpath, len);
+ memcpy(path_out, path_in, len * sizeof(NArtBpath));
+ return path_out;
+ }
+}
+
+std::vector<Geom::Path>
+LPEdoEffectStackTest::doEffect (std::vector<Geom::Path> & path_in)
+{
+ if (step >= 3) {
+ return Effect::doEffect(path_in);
+ } else {
+ // return here
+ std::vector<Geom::Path> path_out = path_in;
+ return path_out;
+ }
+}
+
+Geom::Piecewise<Geom::D2<Geom::SBasis> >
+LPEdoEffectStackTest::doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in)
+{
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > output = pwd2_in;
+
+ return output;
+}
+
+
+}; // namespace LivePathEffect
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpe-test-doEffect-stack.h b/src/live_effects/lpe-test-doEffect-stack.h
new file mode 100644
index 000000000..57748f503
--- /dev/null
+++ b/src/live_effects/lpe-test-doEffect-stack.h
@@ -0,0 +1,42 @@
+#ifndef INKSCAPE_LPE_DOEFFECT_STACK_H
+#define INKSCAPE_LPE_DOEFFECT_STACK_H
+
+/*
+ * Inkscape::LPEdoEffectStackTest
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+*
+* This effect is to test whether running up and down the doEffect stack does not change the original-d too much.
+* i.e. for this effect, the output should match more or less exactly with the input.
+*
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEdoEffectStackTest : public Effect {
+public:
+ LPEdoEffectStackTest(LivePathEffectObject *lpeobject);
+ ~LPEdoEffectStackTest();
+
+ void doEffect (SPCurve * curve);
+ NArtBpath * doEffect (NArtBpath * path_in);
+ std::vector<Geom::Path> doEffect (std::vector<Geom::Path> & path_in);
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect (Geom::Piecewise<Geom::D2<Geom::SBasis> > & pwd2_in);
+
+private:
+ RealParam step;
+
+ LPEdoEffectStackTest(const LPEdoEffectStackTest&);
+ LPEdoEffectStackTest& operator=(const LPEdoEffectStackTest&);
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpeobject-reference.cpp b/src/live_effects/lpeobject-reference.cpp
new file mode 100644
index 000000000..a5392ef90
--- /dev/null
+++ b/src/live_effects/lpeobject-reference.cpp
@@ -0,0 +1,161 @@
+/*
+ * The reference corresponding to the inkscape:live-effect attribute
+ *
+ * Copyright (C) 2007 Johan Engelen
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include "enums.h"
+#include "live_effects/lpeobject-reference.h"
+#include "live_effects/lpeobject.h"
+
+#include "prefs-utils.h"
+#include "uri.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+static void lpeobjectreference_href_changed(SPObject *old_ref, SPObject *ref, LPEObjectReference *lpeobjref);
+static void lpeobjectreference_delete_self(SPObject *deleted, LPEObjectReference *lpeobjref);
+static void lpeobjectreference_source_modified(SPObject *iSource, guint flags, LPEObjectReference *lpeobjref);
+
+LPEObjectReference::LPEObjectReference(SPObject* i_owner) : URIReference(i_owner)
+{
+ owner=i_owner;
+ lpeobject_href = NULL;
+ lpeobject_repr = NULL;
+ lpeobject = NULL;
+ _changed_connection = changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobjectreference_href_changed), this)); // listening to myself, this should be virtual instead
+
+ user_unlink = NULL;
+}
+
+LPEObjectReference::~LPEObjectReference(void)
+{
+ _changed_connection.disconnect(); // to do before unlinking
+
+ quit_listening();
+ unlink();
+}
+
+bool LPEObjectReference::_acceptObject(SPObject * const obj) const
+{
+ if (IS_LIVEPATHEFFECT(obj)) {
+ SPObject * const owner = getOwner();
+ /* Refuse references to us or to an ancestor. */
+ for ( SPObject *iter = owner ; iter ; iter = SP_OBJECT_PARENT(iter) ) {
+ if ( iter == obj ) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void
+LPEObjectReference::link(char *to)
+{
+ if ( to == NULL ) {
+ quit_listening();
+ unlink();
+ } else {
+ if ( !lpeobject_href || ( strcmp(to, lpeobject_href) != 0 ) ) {
+ g_free(lpeobject_href);
+ lpeobject_href = g_strdup(to);
+ try {
+ attach(Inkscape::URI(to));
+ } catch (Inkscape::BadURIException &e) {
+ /* TODO: Proper error handling as per
+ * http://www.w3.org/TR/SVG11/implnote.html#ErrorProcessing.
+ */
+ g_warning("%s", e.what());
+ detach();
+ }
+ }
+ }
+}
+
+void
+LPEObjectReference::unlink(void)
+{
+ g_free(lpeobject_href);
+ lpeobject_href = NULL;
+ detach();
+}
+
+void
+LPEObjectReference::start_listening(LivePathEffectObject* to)
+{
+ if ( to == NULL ) {
+ return;
+ }
+ lpeobject = to;
+ lpeobject_repr = SP_OBJECT_REPR(to);
+ _delete_connection = to->connectDelete(sigc::bind(sigc::ptr_fun(&lpeobjectreference_delete_self), this));
+ _modified_connection = to->connectModified(sigc::bind<2>(sigc::ptr_fun(&lpeobjectreference_source_modified), this));
+}
+
+void
+LPEObjectReference::quit_listening(void)
+{
+ if ( lpeobject == NULL ) {
+ return;
+ }
+ _modified_connection.disconnect();
+ _delete_connection.disconnect();
+ lpeobject_repr = NULL;
+ lpeobject = NULL;
+}
+
+static void
+lpeobjectreference_href_changed(SPObject */*old_ref*/, SPObject */*ref*/, LPEObjectReference *lpeobjref)
+{
+ lpeobjref->quit_listening();
+ LivePathEffectObject *refobj = LIVEPATHEFFECT( lpeobjref->getObject() );
+ if ( refobj ) {
+ lpeobjref->start_listening(refobj);
+ }
+
+ lpeobjref->owner->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+static void
+lpeobjectreference_delete_self(SPObject */*deleted*/, LPEObjectReference *lpeobjref)
+{
+ guint const mode = prefs_get_int_attribute("options.cloneorphans", "value", SP_CLONE_ORPHANS_UNLINK);
+
+ if (mode == SP_CLONE_ORPHANS_UNLINK) {
+ // leave it be. just forget about the source
+ lpeobjref->quit_listening();
+ lpeobjref->unlink();
+ if (lpeobjref->user_unlink)
+ lpeobjref->user_unlink(lpeobjref->owner);
+ } else if (mode == SP_CLONE_ORPHANS_DELETE) {
+ lpeobjref->owner->deleteObject();
+ }
+}
+
+static void
+lpeobjectreference_source_modified(SPObject *iSource, guint flags, LPEObjectReference *lpeobjref)
+{
+ lpeobjref->owner->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
+}
+
+}; //namespace LivePathEffect
+
+}; // namespace inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpeobject-reference.h b/src/live_effects/lpeobject-reference.h
new file mode 100644
index 000000000..7aaa5f691
--- /dev/null
+++ b/src/live_effects/lpeobject-reference.h
@@ -0,0 +1,71 @@
+#ifndef SEEN_LPEOBJECT_REFERENCE_H
+#define SEEN_LPEOBJECT_REFERENCE_H
+
+/*
+ * The reference corresponding to the inkscape:live-effect attribute
+ *
+ * Copyright (C) 2007 Johan Engelen
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information.
+ */
+
+#include <forward.h>
+#include <uri-references.h>
+#include <sigc++/sigc++.h>
+
+namespace Inkscape {
+namespace XML {
+struct Node;
+}
+}
+
+struct LivePathEffectObject;
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class LPEObjectReference : public Inkscape::URIReference {
+public:
+ LPEObjectReference(SPObject *owner);
+ ~LPEObjectReference();
+
+ SPObject *owner;
+
+ // concerning the LPEObject that is refered to:
+ gchar *lpeobject_href;
+ Inkscape::XML::Node *lpeobject_repr;
+ LivePathEffectObject *lpeobject;
+
+ sigc::connection _modified_connection;
+ sigc::connection _delete_connection;
+ sigc::connection _changed_connection;
+
+ void link(char* to);
+ void unlink(void);
+ void start_listening(LivePathEffectObject* to);
+ void quit_listening(void);
+
+ void (*user_unlink) (SPObject *user);
+
+protected:
+ bool _acceptObject(SPObject * const obj) const;
+
+};
+
+}; //namespace LivePathEffect
+
+}; // namespace inkscape
+
+#endif /* !SEEN_LPEOBJECT_REFERENCE_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpeobject.cpp b/src/live_effects/lpeobject.cpp
new file mode 100644
index 000000000..0ce2ce283
--- /dev/null
+++ b/src/live_effects/lpeobject.cpp
@@ -0,0 +1,260 @@
+#define INKSCAPE_LIVEPATHEFFECT_OBJECT_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "xml/repr.h"
+#include "xml/node-event-vector.h"
+#include "sp-object.h"
+#include "attributes.h"
+
+#include "document.h"
+#include <glibmm/i18n.h>
+
+#include "live_effects/lpeobject.h"
+#include "live_effects/effect.h"
+
+//#define LIVEPATHEFFECT_VERBOSE
+
+static void livepatheffect_class_init(LivePathEffectObjectClass *klass);
+static void livepatheffect_init(LivePathEffectObject *stop);
+
+static void livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr);
+static void livepatheffect_release(SPObject *object);
+
+static void livepatheffect_set(SPObject *object, unsigned key, gchar const *value);
+static Inkscape::XML::Node *livepatheffect_write(SPObject *object, Inkscape::XML::Node *repr, guint flags);
+
+static void livepatheffect_on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data);
+
+static SPObjectClass *livepatheffect_parent_class;
+/**
+ * Registers the LivePathEffect class with Gdk and returns its type number.
+ */
+GType
+livepatheffect_get_type ()
+{
+ static GType livepatheffect_type = 0;
+
+ if (!livepatheffect_type) {
+ GTypeInfo livepatheffect_info = {
+ sizeof (LivePathEffectObjectClass),
+ NULL, NULL,
+ (GClassInitFunc) livepatheffect_class_init,
+ NULL, NULL,
+ sizeof (LivePathEffectObject),
+ 16,
+ (GInstanceInitFunc) livepatheffect_init,
+ NULL,
+ };
+ livepatheffect_type = g_type_register_static (SP_TYPE_OBJECT, "LivePathEffectObject", &livepatheffect_info, (GTypeFlags)0);
+ }
+ return livepatheffect_type;
+}
+
+static Inkscape::XML::NodeEventVector const livepatheffect_repr_events = {
+ NULL, /* child_added */
+ NULL, /* child_removed */
+ livepatheffect_on_repr_attr_changed,
+ NULL, /* content_changed */
+ NULL /* order_changed */
+};
+
+
+/**
+ * Callback to initialize livepatheffect vtable.
+ */
+static void
+livepatheffect_class_init(LivePathEffectObjectClass *klass)
+{
+ SPObjectClass *sp_object_class = (SPObjectClass *) klass;
+
+ livepatheffect_parent_class = (SPObjectClass *) g_type_class_ref(SP_TYPE_OBJECT);
+
+ sp_object_class->build = livepatheffect_build;
+ sp_object_class->release = livepatheffect_release;
+
+ sp_object_class->set = livepatheffect_set;
+ sp_object_class->write = livepatheffect_write;
+}
+
+/**
+ * Callback to initialize livepatheffect object.
+ */
+static void
+livepatheffect_init(LivePathEffectObject *lpeobj)
+{
+#ifdef LIVEPATHEFFECT_VERBOSE
+ g_message("Init livepatheffectobject");
+#endif
+ lpeobj->effecttype = Inkscape::LivePathEffect::INVALID_LPE;
+ lpeobj->lpe = NULL;
+
+ lpeobj->effecttype_set = false;
+}
+
+/**
+ * Virtual build: set livepatheffect attributes from its associated XML node.
+ */
+static void
+livepatheffect_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr)
+{
+#ifdef LIVEPATHEFFECT_VERBOSE
+ g_message("Build livepatheffect");
+#endif
+
+ if (((SPObjectClass *) livepatheffect_parent_class)->build)
+ (* ((SPObjectClass *) livepatheffect_parent_class)->build)(object, document, repr);
+
+ sp_object_read_attr(object, "effect");
+
+ if (repr) {
+ repr->addListener (&livepatheffect_repr_events, object);
+ }
+
+ /* Register ourselves */
+// sp_document_add_resource(document, "path-effect", object);
+}
+
+/**
+ * Virtual release of livepatheffect members before destruction.
+ */
+static void
+livepatheffect_release(SPObject *object)
+{
+ LivePathEffectObject *lpeobj = LIVEPATHEFFECT(object);
+
+#ifdef LIVEPATHEFFECT_VERBOSE
+ g_print("Releasing livepatheffect");
+#endif
+
+/*
+ if (SP_OBJECT_DOCUMENT(object)) {
+ // Unregister ourselves
+ sp_document_remove_resource(SP_OBJECT_DOCUMENT(object), "livepatheffect", SP_OBJECT(object));
+ }
+
+ if (gradient->ref) {
+ gradient->modified_connection.disconnect();
+ gradient->ref->detach();
+ delete gradient->ref;
+ gradient->ref = NULL;
+ }
+
+ gradient->modified_connection.~connection();
+
+*/
+
+ if (lpeobj->lpe) {
+ delete lpeobj->lpe;
+ lpeobj->lpe = NULL;
+ }
+ lpeobj->effecttype = Inkscape::LivePathEffect::INVALID_LPE;
+
+ if (((SPObjectClass *) livepatheffect_parent_class)->release)
+ ((SPObjectClass *) livepatheffect_parent_class)->release(object);
+}
+
+/**
+ * Virtual set: set attribute to value.
+ */
+static void
+livepatheffect_set(SPObject *object, unsigned key, gchar const *value)
+{
+ LivePathEffectObject *lpeobj = LIVEPATHEFFECT(object);
+#ifdef LIVEPATHEFFECT_VERBOSE
+ g_print("Set livepatheffect");
+#endif
+ switch (key) {
+ case SP_PROP_PATH_EFFECT:
+ if (lpeobj->lpe) {
+ delete lpeobj->lpe;
+ lpeobj->lpe = NULL;
+ }
+
+ if (value) {
+ lpeobj->effecttype = Inkscape::LivePathEffect::LPETypeConverter.get_id_from_key(value);
+ if (lpeobj->effecttype != Inkscape::LivePathEffect::INVALID_LPE) {
+ lpeobj->lpe = Inkscape::LivePathEffect::Effect::New(lpeobj->effecttype, lpeobj);
+ lpeobj->lpe->readallParameters(SP_OBJECT_REPR(object));
+ }
+ lpeobj->effecttype_set = true;
+ } else {
+ lpeobj->effecttype = Inkscape::LivePathEffect::INVALID_LPE;
+ lpeobj->effecttype_set = false;
+ }
+ object->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ break;
+ }
+
+ if (((SPObjectClass *) livepatheffect_parent_class)->set) {
+ ((SPObjectClass *) livepatheffect_parent_class)->set(object, key, value);
+ }
+}
+
+/**
+ * Virtual write: write object attributes to repr.
+ */
+static Inkscape::XML::Node *
+livepatheffect_write(SPObject *object, Inkscape::XML::Node *repr, guint flags)
+{
+#ifdef LIVEPATHEFFECT_VERBOSE
+ g_print("Write livepatheffect");
+#endif
+
+ LivePathEffectObject *lpeobj = LIVEPATHEFFECT(object);
+
+ if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
+ Inkscape::XML::Document *xml_doc = sp_document_repr_doc(SP_OBJECT_DOCUMENT(object));
+ repr = xml_doc->createElement("inkscape:path-effect");
+ }
+
+ if ((flags & SP_OBJECT_WRITE_ALL) || lpeobj->effecttype_set)
+ repr->setAttribute("effect", Inkscape::LivePathEffect::LPETypeConverter.get_key(lpeobj->effecttype).c_str());
+
+// lpeobj->lpe->write(repr); something like this.
+
+ if (((SPObjectClass *) livepatheffect_parent_class)->write)
+ (* ((SPObjectClass *) livepatheffect_parent_class)->write)(object, repr, flags);
+
+ return repr;
+}
+
+static void
+livepatheffect_on_repr_attr_changed ( Inkscape::XML::Node * repr,
+ const gchar *key,
+ const gchar *oldval,
+ const gchar *newval,
+ bool is_interactive,
+ void * data )
+{
+#ifdef LIVEPATHEFFECT_VERBOSE
+ g_print("livepatheffect_on_repr_attr_changed");
+#endif
+
+ if (!data)
+ return;
+
+ LivePathEffectObject *lpeobj = (LivePathEffectObject*) data;
+ if (!lpeobj->lpe)
+ return;
+
+ lpeobj->lpe->setParameter(repr, key, oldval, newval);
+
+ lpeobj->requestModified(SP_OBJECT_MODIFIED_FLAG);
+}
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/lpeobject.h b/src/live_effects/lpeobject.h
new file mode 100644
index 000000000..fff43cdcf
--- /dev/null
+++ b/src/live_effects/lpeobject.h
@@ -0,0 +1,52 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_OBJECT_H
+#define INKSCAPE_LIVEPATHEFFECT_OBJECT_H
+
+/*
+ * Inkscape::LivePathEffect
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "sp-object.h"
+#include "effect.h"
+
+#define TYPE_LIVEPATHEFFECT (livepatheffect_get_type())
+#define LIVEPATHEFFECT(o) (G_TYPE_CHECK_INSTANCE_CAST((o), TYPE_LIVEPATHEFFECT, LivePathEffectObject))
+#define IS_LIVEPATHEFFECT(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), TYPE_LIVEPATHEFFECT))
+
+/*
+namespace Inkscape {
+namespace LivePathEffect {
+ class Effect;
+};
+};
+*/
+
+struct LivePathEffectObject : public SPObject {
+ Inkscape::LivePathEffect::EffectType effecttype; // fixme: i think this is not needed
+ Inkscape::LivePathEffect::Effect *lpe;
+
+ bool effecttype_set;
+};
+
+/// The LivePathEffect vtable.
+struct LivePathEffectObjectClass {
+ SPObjectClass parent_class;
+};
+
+GType livepatheffect_get_type();
+
+#endif
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/live_effects/makefile.in b/src/live_effects/makefile.in
new file mode 100644
index 000000000..360773401
--- /dev/null
+++ b/src/live_effects/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+ cd .. && $(MAKE) live_effects/all
+
+clean %.a %.o:
+ cd .. && $(MAKE) live_effects/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/live_effects/n-art-bpath-2geom.cpp b/src/live_effects/n-art-bpath-2geom.cpp
new file mode 100644
index 000000000..5f4e4c7b9
--- /dev/null
+++ b/src/live_effects/n-art-bpath-2geom.cpp
@@ -0,0 +1,416 @@
+#define SEEN_LIBNR_N_ART_BPATH_2GEOM_CPP
+
+/** \file
+ * Contains functions to convert from NArtBpath to 2geom's Path
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+
+#include "live_effects/n-art-bpath-2geom.h"
+#include "svg/svg.h"
+#include <glib.h>
+#include <2geom/path.h>
+#include <2geom/svg-path.h>
+#include <2geom/svg-path-parser.h>
+#include <2geom/sbasis-to-bezier.h>
+
+#define LPE_USE_2GEOM_CONVERSION
+
+//##########################################################
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <boost/format.hpp>
+
+static void curve_to_svgd(std::ostream & f, Geom::Curve const* c) {
+ if(Geom::LineSegment const *line_segment = dynamic_cast<Geom::LineSegment const *>(c)) {
+ f << boost::format("L %g,%g ") % (*line_segment)[1][0] % (*line_segment)[1][1];
+ }
+ else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast<Geom::QuadraticBezier const *>(c)) {
+ f << boost::format("Q %g,%g %g,%g ") % (*quadratic_bezier)[1][0] % (*quadratic_bezier)[1][0]
+ % (*quadratic_bezier)[2][0] % (*quadratic_bezier)[2][1];
+ }
+ else if(Geom::CubicBezier const *cubic_bezier = dynamic_cast<Geom::CubicBezier const *>(c)) {
+ f << boost::format("C %g,%g %g,%g %g,%g ")
+ % (*cubic_bezier)[1][0] % (*cubic_bezier)[1][1]
+ % (*cubic_bezier)[2][0] % (*cubic_bezier)[2][1]
+ % (*cubic_bezier)[3][0] % (*cubic_bezier)[3][1];
+ }
+// else if(Geom::SVGEllipticalArc const *svg_elliptical_arc = dynamic_cast<Geom::SVGEllipticalArc *>(c)) {
+// //get at the innards and spit them out as svgd
+// }
+ else {
+ //this case handles sbasis as well as all other curve types
+ Geom::Path sbasis_path;
+ path_from_sbasis(sbasis_path, c->sbasis(), 0.1);
+
+ //recurse to convert the new path resulting from the sbasis to svgd
+ for(Geom::Path::iterator iter = sbasis_path.begin(); iter != sbasis_path.end(); ++iter) {
+ curve_to_svgd(f, &(*iter));
+ }
+ }
+}
+
+static void write_svgd(std::ostream & f, Geom::Path const &p) {
+ if(f == NULL) {
+ f << "ERRRRRRORRRRR";
+ return;
+ }
+
+ f << boost::format("M %g,%g ") % p.initialPoint()[0] % p.initialPoint()[1];
+
+ for(Geom::Path::const_iterator iter(p.begin()), end(p.end()); iter != end; ++iter) {
+ curve_to_svgd(f, &(*iter));
+ }
+ if(p.closed())
+ f << "Z ";
+}
+
+static void write_svgd(std::ostream & f, std::vector<Geom::Path> const &p) {
+ std::vector<Geom::Path>::const_iterator it(p.begin());
+ for(; it != p.end(); it++) {
+ write_svgd(f, *it);
+ }
+}
+
+//##########################################################
+#ifndef LPE_USE_2GEOM_CONVERSION
+
+static
+Geom::Point point(double *nums, int ix) {
+ return Geom::Point(nums[ix], nums[ix + 1]);
+}
+
+using namespace Geom;
+
+class OldPathBuilder {
+public:
+ OldPathBuilder(double const &c = Geom_EPSILON) : _current_path(NULL) {
+ _continuity_tollerance = c;
+ }
+
+ void startPathRel(Point const &p0) { startPath(p0 + _current_point); }
+ void startPath(Point const &p0) {
+ _pathset.push_back(Geom::Path());
+ _current_path = &_pathset.back();
+ _initial_point = _current_point = p0;
+ }
+
+ void pushLineRel(Point const &p0) { pushLine(p0 + _current_point); }
+ void pushLine(Point const &p1) {
+ if (!_current_path) startPath(_current_point);
+ _current_path->appendNew<LineSegment>(p1);
+ _current_point = p1;
+ }
+
+ void pushLineRel(Point const &p0, Point const &p1) { pushLine(p0 + _current_point, p1 + _current_point); }
+ void pushLine(Point const &p0, Point const &p1) {
+ if(p0 != _current_point) startPath(p0);
+ pushLine(p1);
+ }
+
+ void pushHorizontalRel(Coord y) { pushHorizontal(y + _current_point[1]); }
+ void pushHorizontal(Coord y) {
+ if (!_current_path) startPath(_current_point);
+ pushLine(Point(_current_point[0], y));
+ }
+
+ void pushVerticalRel(Coord x) { pushVertical(x + _current_point[0]); }
+ void pushVertical(Coord x) {
+ if (!_current_path) startPath(_current_point);
+ pushLine(Point(x, _current_point[1]));
+ }
+
+ void pushQuadraticRel(Point const &p1, Point const &p2) { pushQuadratic(p1 + _current_point, p2 + _current_point); }
+ void pushQuadratic(Point const &p1, Point const &p2) {
+ if (!_current_path) startPath(_current_point);
+ _current_path->appendNew<QuadraticBezier>(p1, p2);
+ _current_point = p2;
+ }
+
+ void pushQuadraticRel(Point const &p0, Point const &p1, Point const &p2) {
+ pushQuadratic(p0 + _current_point, p1 + _current_point, p2 + _current_point);
+ }
+ void pushQuadratic(Point const &p0, Point const &p1, Point const &p2) {
+ if(p0 != _current_point) startPath(p0);
+ pushQuadratic(p1, p2);
+ }
+
+ void pushCubicRel(Point const &p1, Point const &p2, Point const &p3) {
+ pushCubic(p1 + _current_point, p2 + _current_point, p3 + _current_point);
+ }
+ void pushCubic(Point const &p1, Point const &p2, Point const &p3) {
+ if (!_current_path) startPath(_current_point);
+ _current_path->appendNew<CubicBezier>(p1, p2, p3);
+ _current_point = p3;
+ }
+
+ void pushCubicRel(Point const &p0, Point const &p1, Point const &p2, Point const &p3) {
+ pushCubic(p0 + _current_point, p1 + _current_point, p2 + _current_point, p3 + _current_point);
+ }
+ void pushCubic(Point const &p0, Point const &p1, Point const &p2, Point const &p3) {
+ if(p0 != _current_point) startPath(p0);
+ pushCubic(p1, p2, p3);
+ }
+/*
+ void pushEllipseRel(Point const &radii, double rotation, bool large, bool sweep, Point const &end) {
+ pushEllipse(radii, rotation, large, sweep, end + _current_point);
+ }
+ void pushEllipse(Point const &radii, double rotation, bool large, bool sweep, Point const &end) {
+ if (!_current_path) startPath(_current_point);
+ _current_path->append(SVGEllipticalArc(_current_point, radii[0], radii[1], rotation, large, sweep, end));
+ _current_point = end;
+ }
+
+ void pushEllipseRel(Point const &initial, Point const &radii, double rotation, bool large, bool sweep, Point const &end) {
+ pushEllipse(initial + _current_point, radii, rotation, large, sweep, end + _current_point);
+ }
+ void pushEllipse(Point const &initial, Point const &radii, double rotation, bool large, bool sweep, Point const &end) {
+ if(initial != _current_point) startPath(initial);
+ pushEllipse(radii, rotation, large, sweep, end);
+ }*/
+
+ void pushSBasis(SBasisCurve &sb) {
+ pushSBasis(sb.sbasis());
+ }
+ void pushSBasis(D2<SBasis> sb) {
+ Point initial = Point(sb[X][0][0], sb[Y][0][0]);
+ if (!_current_path) startPath(_current_point);
+ if (distance(initial, _current_point) > _continuity_tollerance) {
+ startPath(initial);
+ } else if (_current_point != initial) {
+ /* in this case there are three possible options
+ 1. connect the points with tiny line segments
+ this may well translate into bug reports from
+ users claiming "duplicate or extraneous nodes"
+ 2. fudge the initial point of the multidimsb
+ we've chosen to do this here but question the
+ numerical stability of this decision
+ 3. translate the whole sbasis so that initial is coincident
+ with _current_point. this could very well lead
+ to an accumulation of error for paths that expect
+ to meet in the end.
+ perhaps someday an option could be made to allow
+ the user to choose between these alternatives
+ if the need arises
+ */
+ sb[X][0][0] = _current_point[X];
+ sb[Y][0][0] = _current_point[Y];
+ }
+ _current_path->append(sb);
+ }
+
+ void closePath() {
+ if (_current_path) {
+ _current_path->close(true);
+ _current_path = NULL;
+ }
+ _current_point = _initial_point = Point();
+ }
+
+ std::vector<Path> const &peek() const { return _pathset; }
+
+private:
+ std::vector<Path> _pathset;
+ Path *_current_path;
+ Point _current_point;
+ Point _initial_point;
+ double _continuity_tollerance;
+};
+
+static
+std::vector<Geom::Path>
+read_svgd(std::istringstream & s) {
+ assert(s);
+
+ OldPathBuilder builder;
+
+ char mode = 0;
+
+ double nums[7];
+ int cur = 0;
+ while(!s.eof()) {
+ char ch;
+ s >> ch;
+ if((ch >= 'A' and ch <= 'Z') or (ch >= 'a' and ch <= 'z')) {
+ mode = ch;
+ cur = 0;
+ } else if (ch == ' ' or ch == '\t' or ch == '\n' or ch == '\r' or ch == ',')
+ continue;
+ else if ((ch >= '0' and ch <= '9') or ch == '-' or ch == '.' or ch == '+') {
+ s.unget();
+ //TODO: use something else, perhaps. Unless the svg path number spec matches scan.
+ s >> nums[cur];
+ cur++;
+ }
+
+ switch(mode) {
+ //FIXME: "If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit lineto commands."
+ case 'm':
+ if(cur >= 2) {
+ builder.startPathRel(point(nums, 0));
+ cur = 0;
+ }
+ break;
+ case 'M':
+ if(cur >= 2) {
+ builder.startPath(point(nums, 0));
+ cur = 0;
+ }
+ break;
+ case 'l':
+ if(cur >= 2) {
+ builder.pushLineRel(point(nums, 0));
+ cur = 0;
+ }
+ break;
+ case 'L':
+ if(cur >= 2) {
+ builder.pushLine(point(nums, 0));
+ cur = 0;
+ }
+ break;
+ case 'h':
+ if(cur >= 1) {
+ builder.pushHorizontalRel(nums[0]);
+ cur = 0;
+ }
+ break;
+ case 'H':
+ if(cur >= 1) {
+ builder.pushHorizontal(nums[0]);
+ cur = 0;
+ }
+ break;
+ case 'v':
+ if(cur >= 1) {
+ builder.pushVerticalRel(nums[0]);
+ cur = 0;
+ }
+ break;
+ case 'V':
+ if(cur >= 1) {
+ builder.pushVertical(nums[0]);
+ cur = 0;
+ }
+ break;
+ case 'c':
+ if(cur >= 6) {
+ builder.pushCubicRel(point(nums, 0), point(nums, 2), point(nums, 4));
+ cur = 0;
+ }
+ break;
+ case 'C':
+ if(cur >= 6) {
+ builder.pushCubic(point(nums, 0), point(nums, 2), point(nums, 4));
+ cur = 0;
+ }
+ break;
+ case 'q':
+ if(cur >= 4) {
+ builder.pushQuadraticRel(point(nums, 0), point(nums, 2));
+ cur = 0;
+ }
+ break;
+ case 'Q':
+ if(cur >= 4) {
+ builder.pushQuadratic(point(nums, 0), point(nums, 2));
+ cur = 0;
+ }
+ break;
+ case 'a':
+ if(cur >= 7) {
+ //builder.pushEllipseRel(point(nums, 0), nums[2], nums[3] > 0, nums[4] > 0, point(nums, 5));
+ cur = 0;
+ }
+ break;
+ case 'A':
+ if(cur >= 7) {
+ //builder.pushEllipse(point(nums, 0), nums[2], nums[3] > 0, nums[4] > 0, point(nums, 5));
+ cur = 0;
+ }
+ break;
+ case 'z':
+ case 'Z':
+ builder.closePath();
+ break;
+ }
+ }
+ return builder.peek();
+}
+
+
+#endif
+//##########################################################
+
+std::vector<Geom::Path>
+SVGD_to_2GeomPath (char const *svgd)
+{
+ std::vector<Geom::Path> pathv;
+#ifdef LPE_USE_2GEOM_CONVERSION
+ try {
+ pathv = Geom::parse_svg_path(svgd);
+ }
+ catch (std::runtime_error e) {
+ g_warning("SVGPathParseError: %s", e.what());
+ }
+#else
+ std::istringstream ss;
+ std::string svgd_string = svgd;
+ ss.str(svgd_string);
+ pathv = read_svgd(ss);
+#endif
+ return pathv;
+}
+
+
+std::vector<Geom::Path>
+BPath_to_2GeomPath(NArtBpath const * bpath)
+{
+ std::vector<Geom::Path> pathv;
+ char *svgpath = sp_svg_write_path(bpath);
+ if (!svgpath) {
+ g_warning("BPath_to_2GeomPath - empty path returned");
+ return pathv;
+ }
+ pathv = SVGD_to_2GeomPath(svgpath);
+ g_free(svgpath);
+ return pathv;
+}
+
+char *
+SVGD_from_2GeomPath(std::vector<Geom::Path> const & path)
+{
+ std::ostringstream ss;
+ write_svgd(ss, path);
+ ss.flush();
+ std::string str = ss.str();
+ char * svgd = g_strdup(str.c_str());
+ return svgd;
+}
+
+NArtBpath *
+BPath_from_2GeomPath(std::vector<Geom::Path> const & path)
+{
+ char * svgd = SVGD_from_2GeomPath(path);
+ NArtBpath *bpath = sp_svg_read_path(svgd);
+ g_free(svgd);
+ return bpath;
+}
+
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/n-art-bpath-2geom.h b/src/live_effects/n-art-bpath-2geom.h
new file mode 100644
index 000000000..52b4cb9ee
--- /dev/null
+++ b/src/live_effects/n-art-bpath-2geom.h
@@ -0,0 +1,33 @@
+#ifndef SEEN_LIBNR_N_ART_BPATH_2GEOM_H
+#define SEEN_LIBNR_N_ART_BPATH_2GEOM_H
+
+/** \file
+ * Contains functions to convert from NArtBpath to 2geom's Path
+ *
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <vector>
+#include <2geom/path.h>
+#include <libnr/n-art-bpath.h>
+
+std::vector<Geom::Path> SVGD_to_2GeomPath (char const *svgd);
+std::vector<Geom::Path> BPath_to_2GeomPath (NArtBpath const *bpath);
+char * SVGD_from_2GeomPath(std::vector<Geom::Path> const & path);
+NArtBpath * BPath_from_2GeomPath (std::vector<Geom::Path> const & path);
+
+
+#endif /* !SEEN_LIBNR_N_ART_BPATH_2GEOM_H */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/Makefile_insert b/src/live_effects/parameter/Makefile_insert
new file mode 100644
index 000000000..a737da1f0
--- /dev/null
+++ b/src/live_effects/parameter/Makefile_insert
@@ -0,0 +1,18 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+live_effects/parameter/all: live_effects/parameter/liblpeparam.a
+
+live_effects/parameter/clean:
+ rm -f live_effects/parameter/liblpeparam.a $(live_effects_parameter_liblpeparam_a_OBJECTS)
+
+live_effects_parameter_liblpeparam_a_SOURCES = \
+ live_effects/parameter/parameter.cpp \
+ live_effects/parameter/parameter.h \
+ live_effects/parameter/point.cpp \
+ live_effects/parameter/point.h \
+ live_effects/parameter/enum.h \
+ live_effects/parameter/path.cpp \
+ live_effects/parameter/path.h
+
+
+
diff --git a/src/live_effects/parameter/enum.h b/src/live_effects/parameter/enum.h
new file mode 100644
index 000000000..d70360a24
--- /dev/null
+++ b/src/live_effects/parameter/enum.h
@@ -0,0 +1,87 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ENUM_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ENUM_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib/gtypes.h>
+
+#include "ui/widget/registry.h"
+#include "ui/widget/registered-enums.h"
+#include <gtkmm/tooltips.h>
+
+#include "live_effects/parameter/parameter.h"
+#include "verbs.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+template<typename E> class EnumParam : public Parameter {
+public:
+ EnumParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ const Util::EnumDataConverter<E>& c,
+ Inkscape::UI::Widget::Registry* wr, Effect* effect)
+ : Parameter(label, tip, key, wr, effect)
+ {
+ regenum = NULL;
+ enumdataconv = &c;
+ };
+ ~EnumParam() {
+ if (regenum)
+ delete regenum;
+ };
+
+ Gtk::Widget * param_getWidget() {
+ if (!regenum) {
+ regenum = new Inkscape::UI::Widget::RegisteredEnum<E>();
+ regenum->init(param_label, param_tooltip, param_key, *enumdataconv, *param_wr, param_effect->getRepr(), param_effect->getSPDoc());
+ regenum->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change enum parameter"));
+ }
+ return dynamic_cast<Gtk::Widget *> (regenum->labelled);
+ };
+
+ bool param_readSVGValue(const gchar * strvalue) {
+ if (regenum)
+ regenum->combobox()->set_active_by_key(Glib::ustring(strvalue));
+ return true;
+ };
+ gchar * param_writeSVGValue() const {
+ if (regenum) {
+ gchar * str = g_strdup(regenum->combobox()->get_active_data()->key.c_str());
+ return str;
+ } else {
+ return NULL;
+ }
+ };
+
+ const Util::EnumData<E>* get_selected_data() {
+ if (regenum) {
+ return regenum->combobox()->get_active_data();
+ } else {
+ return NULL;
+ }
+ };
+
+private:
+ EnumParam(const EnumParam&);
+ EnumParam& operator=(const EnumParam&);
+
+ UI::Widget::RegisteredEnum<E> * regenum;
+
+ const Util::EnumDataConverter<E> * enumdataconv;
+};
+
+
+}; //namespace LivePathEffect
+
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/makefile.in b/src/live_effects/parameter/makefile.in
new file mode 100644
index 000000000..2a6294c4d
--- /dev/null
+++ b/src/live_effects/parameter/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+ cd ../.. && $(MAKE) live_effects/parameter/all
+
+clean %.a %.o:
+ cd ../.. && $(MAKE) live_effects/parameter/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/live_effects/parameter/parameter.cpp b/src/live_effects/parameter/parameter.cpp
new file mode 100644
index 000000000..6806a1d49
--- /dev/null
+++ b/src/live_effects/parameter/parameter.cpp
@@ -0,0 +1,105 @@
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+
+#include <gtkmm.h>
+#include "ui/widget/scalar.h"
+
+#include "svg/stringstream.h"
+
+#include "verbs.h"
+
+#define noLPEREALPARAM_DEBUG
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+Parameter::Parameter( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect )
+{
+ param_label = label;
+ param_tooltip = tip;
+ param_key = key;
+ param_wr = wr;
+ param_effect = effect;
+}
+
+
+
+/*###########################################
+ * REAL PARAM
+ */
+RealParam::RealParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, gdouble initial_value)
+ : Parameter(label, tip, key, wr, effect)
+{
+ value = initial_value;
+ rsu = NULL;
+}
+
+RealParam::~RealParam()
+{
+ if (rsu)
+ delete rsu;
+}
+
+bool
+RealParam::param_readSVGValue(const gchar * strvalue)
+{
+ double newval;
+ unsigned int success = sp_svg_number_read_d(strvalue, &newval);
+ if (success == 1) {
+ value = newval;
+ return true;
+ }
+ return false;
+}
+
+gchar *
+RealParam::param_writeSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << rsu->getS()->getValue();
+ gchar * str = g_strdup(os.str().c_str());
+ return str;
+}
+
+Gtk::Widget *
+RealParam::param_getWidget()
+{
+ if (!rsu) {
+ rsu = new Inkscape::UI::Widget::RegisteredScalar();
+ rsu->init(param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc());
+ rsu->setValue(value);
+ rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
+ }
+ return dynamic_cast<Gtk::Widget *> (rsu->getS());
+}
+
+
+}; /* namespace LivePathEffect */
+
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/parameter.h b/src/live_effects/parameter/parameter.h
new file mode 100644
index 000000000..327d3d153
--- /dev/null
+++ b/src/live_effects/parameter/parameter.h
@@ -0,0 +1,84 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glibmm/ustring.h>
+#include <2geom/point.h>
+#include <2geom/path.h>
+
+#include "ui/widget/registry.h"
+#include "ui/widget/registered-widget.h"
+
+namespace Gtk {
+ class Widget;
+}
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class Effect;
+
+class Parameter {
+public:
+ Parameter(const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, Effect* effect);
+ virtual ~Parameter() {};
+
+ virtual bool param_readSVGValue(const gchar * strvalue) = 0; // returns true if new value is valid / accepted.
+ virtual gchar * param_writeSVGValue() const = 0;
+
+ // This returns pointer to the parameter's widget to be put in the live-effects dialog. Must also create the
+ // necessary widget if it does not exist yet.
+ virtual Gtk::Widget * param_getWidget() = 0;
+ virtual Glib::ustring * param_getTooltip() { return &param_tooltip; };
+
+ Glib::ustring param_key;
+ Inkscape::UI::Widget::Registry * param_wr;
+ Glib::ustring param_label;
+
+protected:
+ Glib::ustring param_tooltip;
+
+ Effect* param_effect;
+
+private:
+ Parameter(const Parameter&);
+ Parameter& operator=(const Parameter&);
+};
+
+
+class RealParam : public Parameter {
+public:
+ RealParam( const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr, Effect* effect, gdouble initial_value = 1.0);
+ ~RealParam();
+
+ bool param_readSVGValue(const gchar * strvalue);
+ gchar * param_writeSVGValue() const;
+
+ Gtk::Widget * param_getWidget();
+
+ inline operator gdouble()
+ { return value; };
+
+private:
+ RealParam(const RealParam&);
+ RealParam& operator=(const RealParam&);
+
+ gdouble value;
+ Inkscape::UI::Widget::RegisteredScalar * rsu;
+};
+
+
+}; //namespace LivePathEffect
+
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/path.cpp b/src/live_effects/parameter/path.cpp
new file mode 100644
index 000000000..90974f686
--- /dev/null
+++ b/src/live_effects/parameter/path.cpp
@@ -0,0 +1,166 @@
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_PATH_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/parameter/path.h"
+#include "live_effects/effect.h"
+#include "live_effects/n-art-bpath-2geom.h"
+#include "svg/svg.h"
+#include <2geom/svg-path-parser.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/d2.h>
+
+#include "ui/widget/point.h"
+#include "widgets/icon.h"
+#include <gtk/gtkstock.h>
+#include "selection-chemistry.h"
+
+#include "desktop.h"
+#include "inkscape.h"
+#include "message-stack.h"
+#include "verbs.h"
+#include "document.h"
+
+#define noLPEPATHPARAM_DEBUG
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+PathParam::PathParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect )
+ : Parameter(label, tip, key, wr, effect)
+{
+ _widget = NULL;
+ _tooltips = NULL;
+}
+
+PathParam::~PathParam()
+{
+ if (_tooltips)
+ delete _tooltips;
+ // _widget is managed by GTK so do not delete!
+}
+
+bool
+PathParam::param_readSVGValue(const gchar * strvalue)
+{
+ if (strvalue) {
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > newpath;
+ std::vector<Geom::Path> temppath = SVGD_to_2GeomPath(strvalue);
+ for (unsigned int i=0; i < temppath.size(); i++) {
+ newpath.concat( temppath[i].toPwSb() );
+ }
+ *( dynamic_cast<Geom::Piecewise<Geom::D2<Geom::SBasis> > *> (this) ) = newpath;
+ return true;
+ }
+
+ return false;
+}
+
+gchar *
+PathParam::param_writeSVGValue() const
+{
+ const std::vector<Geom::Path> temppath =
+ Geom::path_from_piecewise(* dynamic_cast<const Geom::Piecewise<Geom::D2<Geom::SBasis> > *> (this), LPE_CONVERSION_TOLERANCE);
+ gchar * svgd = SVGD_from_2GeomPath( temppath );
+ return svgd;
+}
+
+Gtk::Widget *
+PathParam::param_getWidget()
+{
+ if (!_widget) {
+ _widget = Gtk::manage(new Gtk::HBox());
+ _tooltips = new Gtk::Tooltips();
+
+ Gtk::Label* pLabel = Gtk::manage(new Gtk::Label(param_label));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pLabel, true, true);
+ _tooltips->set_tip(*pLabel, param_tooltip);
+
+ Gtk::Widget* pIcon = Gtk::manage( sp_icon_get_icon( "draw_node", Inkscape::ICON_SIZE_BUTTON) );
+ Gtk::Button * pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_edit_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ _tooltips->set_tip(*pButton, _("Edit on-canvas"));
+#ifndef LPEPATHPARAM_DEBUG
+ pButton->set_sensitive(false);
+#endif
+
+ pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_PASTE, Inkscape::ICON_SIZE_BUTTON) );
+ pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &PathParam::on_paste_button_click));
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ _tooltips->set_tip(*pButton, _("Paste path"));
+
+ static_cast<Gtk::HBox*>(_widget)->show_all_children();
+
+ }
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void
+PathParam::on_edit_button_click()
+{
+ g_message("give this path to edit on canvas!");
+}
+
+void
+PathParam::on_paste_button_click()
+{
+ // check if something is in the clipboard
+ GSList * clipboard = sp_selection_get_clipboard();
+ if (clipboard == NULL || clipboard->data == NULL) {
+ SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Nothing on the clipboard."));
+ return;
+ }
+
+ Inkscape::XML::Node *repr = (Inkscape::XML::Node *) clipboard->data;
+ if (!strcmp (repr->name(), "svg:path")) {
+ const char * svgd = repr->attribute("d");
+ if (svgd) {
+ param_write_to_repr(svgd);
+ signal_path_pasted.emit();
+ sp_document_done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Paste path parameter"));
+ }
+ } else {
+ SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Clipboard does not contain a path."));
+ }
+
+}
+
+void
+PathParam::param_write_to_repr(const char * svgd)
+{
+ param_effect->getRepr()->setAttribute(param_key.c_str(), svgd);
+}
+
+
+}; /* namespace LivePathEffect */
+
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/path.h b/src/live_effects/parameter/path.h
new file mode 100644
index 000000000..39ea9e2d8
--- /dev/null
+++ b/src/live_effects/parameter/path.h
@@ -0,0 +1,56 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_PATH_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_PATH_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib/gtypes.h>
+#include <2geom/path.h>
+
+#include "ui/widget/registry.h"
+#include <gtkmm/tooltips.h>
+
+#include "live_effects/parameter/parameter.h"
+
+#include <sigc++/sigc++.h>
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class PathParam : public Geom::Piecewise<Geom::D2<Geom::SBasis> >, public Parameter {
+public:
+ PathParam(const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, Effect* effect);;
+ ~PathParam();
+
+ Gtk::Widget * param_getWidget();
+
+ bool param_readSVGValue(const gchar * strvalue);
+ gchar * param_writeSVGValue() const;
+
+ sigc::signal <void> signal_path_pasted;
+
+private:
+ PathParam(const PathParam&);
+ PathParam& operator=(const PathParam&);
+
+ Gtk::Widget * _widget;
+ Gtk::Tooltips * _tooltips;
+
+ void param_write_to_repr(const char * svgd);
+
+ void on_edit_button_click();
+ void on_paste_button_click();
+};
+
+
+}; //namespace LivePathEffect
+
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/parameter/point.cpp b/src/live_effects/parameter/point.cpp
new file mode 100644
index 000000000..8079f54eb
--- /dev/null
+++ b/src/live_effects/parameter/point.cpp
@@ -0,0 +1,166 @@
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_POINT_CPP
+
+/*
+ * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/parameter/point.h"
+#include "live_effects/effect.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include <gtkmm.h>
+#include "ui/widget/point.h"
+#include "widgets/icon.h"
+
+#include "knot.h"
+#include "inkscape.h"
+#include "verbs.h"
+
+#define noLPEPOINTPARAM_DEBUG
+
+#define PRM_KNOT_COLOR_NORMAL 0xffffff00
+#define PRM_KNOT_COLOR_SELECTED 0x0000ff00
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+PointParam::PointParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect )
+ : Geom::Point(0,0), Parameter(label, tip, key, wr, effect)
+{
+ _widget = NULL;
+ pointwdg = NULL;
+ knot = NULL;
+ _tooltips = NULL;
+}
+
+PointParam::~PointParam()
+{
+ if (pointwdg)
+ delete pointwdg;
+ if (_tooltips)
+ delete _tooltips;
+
+ if (knot)
+ g_object_unref (G_OBJECT (knot));
+}
+
+bool
+PointParam::param_readSVGValue(const gchar * strvalue)
+{
+ gchar ** strarray = g_strsplit(strvalue, ",", 2);
+ double newx, newy;
+ unsigned int success = sp_svg_number_read_d(strarray[0], &newx);
+ success += sp_svg_number_read_d(strarray[1], &newy);
+ g_strfreev (strarray);
+ if (success == 2) {
+ *dynamic_cast<Geom::Point *>( this ) = Geom::Point(newx, newy);
+ return true;
+ }
+ return false;
+}
+
+gchar *
+PointParam::param_writeSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << pointwdg->getPoint()->getXValue() << "," << pointwdg->getPoint()->getYValue();
+ gchar * str = g_strdup(os.str().c_str());
+ return str;
+}
+
+Gtk::Widget *
+PointParam::param_getWidget()
+{
+ if (!_widget) {
+ pointwdg = new Inkscape::UI::Widget::RegisteredPoint();
+ pointwdg->init(param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc());
+ pointwdg->setValue(0.1, 0.2);
+ pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change point parameter"));
+
+ Gtk::Widget* pIcon = Gtk::manage( sp_icon_get_icon( "draw_node", Inkscape::ICON_SIZE_BUTTON) );
+ Gtk::Button * pButton = Gtk::manage(new Gtk::Button());
+ pButton->set_relief(Gtk::RELIEF_NONE);
+ pIcon->show();
+ pButton->add(*pIcon);
+ pButton->show();
+ pButton->signal_clicked().connect(sigc::mem_fun(*this, &PointParam::on_button_click));
+#ifndef LPEPOINTPARAM_DEBUG
+ pButton->set_sensitive(false);
+#endif
+
+ _widget = Gtk::manage( new Gtk::HBox() );
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*pButton, true, true);
+ static_cast<Gtk::HBox*>(_widget)->pack_start(*(pointwdg->getPoint()), true, true);
+ static_cast<Gtk::HBox*>(_widget)->show_all_children();
+
+ _tooltips = new Gtk::Tooltips();
+ _tooltips->set_tip(*pButton, _("Edit on-canvas"));
+ }
+ return dynamic_cast<Gtk::Widget *> (_widget);
+}
+
+void
+PointParam::param_setValue(Geom::Point newpoint)
+{
+ *dynamic_cast<Geom::Point *>( this ) = newpoint;
+ pointwdg->setValue(newpoint[0], newpoint[1]);
+}
+
+
+// CALLBACKS:
+
+void
+PointParam::on_button_click()
+{
+ g_message("add knot to canvas on correct location :S");
+
+ if (!knot) {
+ // create the knot
+ knot = sp_knot_new (SP_ACTIVE_DESKTOP, NULL);
+ knot->setMode(SP_KNOT_MODE_XOR);
+ knot->setFill(PRM_KNOT_COLOR_NORMAL, PRM_KNOT_COLOR_NORMAL, PRM_KNOT_COLOR_NORMAL);
+ knot->setStroke(0x000000ff, 0x000000ff, 0x000000ff);
+ sp_knot_update_ctrl(knot);
+
+ // move knot to the given point
+ sp_knot_set_position (knot, &NR::Point((*this)[0], (*this)[1]), SP_KNOT_STATE_NORMAL);
+ sp_knot_show (knot);
+/*
+ // connect knot's signals
+ if ( (draggable) // it can be NULL if a node in unsnapped (eg. focus point unsnapped from center)
+ // luckily, midstops never snap to other nodes so are never unsnapped...
+ && ( (draggable->point_type == POINT_LG_MID)
+ || (draggable->point_type == POINT_RG_MID1)
+ || (draggable->point_type == POINT_RG_MID2) ) )
+ {
+ this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_midpoint_handler), this);
+ } else {
+ this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_handler), this);
+ }
+ g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (gr_knot_clicked_handler), this);
+ g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (gr_knot_doubleclicked_handler), this);
+ g_signal_connect (G_OBJECT (this->knot), "grabbed", G_CALLBACK (gr_knot_grabbed_handler), this);
+ g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (gr_knot_ungrabbed_handler), this);
+*/
+ }
+}
+
+}; /* namespace LivePathEffect */
+
+}; /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/live_effects/parameter/point.h b/src/live_effects/parameter/point.h
new file mode 100644
index 000000000..1240ea3d1
--- /dev/null
+++ b/src/live_effects/parameter/point.h
@@ -0,0 +1,57 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_POINT_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_POINT_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+* Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib/gtypes.h>
+#include <2geom/point.h>
+
+#include "ui/widget/registry.h"
+#include "ui/widget/registered-widget.h"
+#include <gtkmm/tooltips.h>
+
+#include "live_effects/parameter/parameter.h"
+
+struct SPKnot;
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+class PointParam : public Geom::Point, public Parameter {
+public:
+ PointParam(const Glib::ustring& label, const Glib::ustring& tip, const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr, Effect* effect);;
+ ~PointParam();
+
+ Gtk::Widget * param_getWidget();
+
+ bool param_readSVGValue(const gchar * strvalue);
+ gchar * param_writeSVGValue() const;
+
+ void param_setValue(Geom::Point newpoint);
+
+private:
+ PointParam(const PointParam&);
+ PointParam& operator=(const PointParam&);
+
+ Gtk::Widget * _widget;
+ Gtk::Tooltips * _tooltips;
+ Inkscape::UI::Widget::RegisteredPoint * pointwdg;
+ void on_button_click();
+
+ SPKnot *knot;
+};
+
+
+}; //namespace LivePathEffect
+
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/todo.txt b/src/live_effects/todo.txt
new file mode 100644
index 000000000..87bafe4a1
--- /dev/null
+++ b/src/live_effects/todo.txt
@@ -0,0 +1,19 @@
+reminder list
+
+
+cleanup nodepath code that draws helper path
+
+implement effect application to shapes: sp_shape_apply_path_effect
+ (done: star, ellipse, spiral)
+ARCS !!! see sp_arc_set_elliptical_path_attribute(SPArc *arc, Inkscape::XML::Node *repr)
+
+make sp_nodepath_is_over_stroke perhaps
+
+Parameters:
+- make robust (?)
+- add range checking etc
+- add more types! (straightlinepath, enum, bool)
+- -->> add write to svg functionality (for lpeobject->write)
+
+
+find dir "fixme" and fix'em! \ No newline at end of file