From 55d43e4e27e0ba58a47fad70957dfa989aa173ad Mon Sep 17 00:00:00 2001 From: "Johan B. C. Engelen" Date: Tue, 14 Aug 2007 20:54:48 +0000 Subject: Commit LivePathEffect branch to trunk! (disabled extension/internal/bitmap/*.* in build.xml to fix compilation) (bzr r3472) --- src/live_effects/Makefile_insert | 27 ++ src/live_effects/effect.cpp | 232 +++++++++++++++ src/live_effects/effect.h | 108 +++++++ src/live_effects/lpe-gears.cpp | 266 +++++++++++++++++ src/live_effects/lpe-gears.h | 38 +++ src/live_effects/lpe-skeletalstrokes.cpp | 161 +++++++++++ src/live_effects/lpe-skeletalstrokes.h | 49 ++++ src/live_effects/lpe-slant.cpp | 62 ++++ src/live_effects/lpe-slant.h | 40 +++ src/live_effects/lpe-test-doEffect-stack.cpp | 96 +++++++ src/live_effects/lpe-test-doEffect-stack.h | 42 +++ src/live_effects/lpeobject-reference.cpp | 161 +++++++++++ src/live_effects/lpeobject-reference.h | 71 +++++ src/live_effects/lpeobject.cpp | 260 +++++++++++++++++ src/live_effects/lpeobject.h | 52 ++++ src/live_effects/makefile.in | 17 ++ src/live_effects/n-art-bpath-2geom.cpp | 416 +++++++++++++++++++++++++++ src/live_effects/n-art-bpath-2geom.h | 33 +++ src/live_effects/parameter/Makefile_insert | 18 ++ src/live_effects/parameter/enum.h | 87 ++++++ src/live_effects/parameter/makefile.in | 17 ++ src/live_effects/parameter/parameter.cpp | 105 +++++++ src/live_effects/parameter/parameter.h | 84 ++++++ src/live_effects/parameter/path.cpp | 166 +++++++++++ src/live_effects/parameter/path.h | 56 ++++ src/live_effects/parameter/point.cpp | 166 +++++++++++ src/live_effects/parameter/point.h | 57 ++++ src/live_effects/todo.txt | 19 ++ 28 files changed, 2906 insertions(+) create mode 100644 src/live_effects/Makefile_insert create mode 100644 src/live_effects/effect.cpp create mode 100644 src/live_effects/effect.h create mode 100644 src/live_effects/lpe-gears.cpp create mode 100644 src/live_effects/lpe-gears.h create mode 100644 src/live_effects/lpe-skeletalstrokes.cpp create mode 100644 src/live_effects/lpe-skeletalstrokes.h create mode 100644 src/live_effects/lpe-slant.cpp create mode 100644 src/live_effects/lpe-slant.h create mode 100644 src/live_effects/lpe-test-doEffect-stack.cpp create mode 100644 src/live_effects/lpe-test-doEffect-stack.h create mode 100644 src/live_effects/lpeobject-reference.cpp create mode 100644 src/live_effects/lpeobject-reference.h create mode 100644 src/live_effects/lpeobject.cpp create mode 100644 src/live_effects/lpeobject.h create mode 100644 src/live_effects/makefile.in create mode 100644 src/live_effects/n-art-bpath-2geom.cpp create mode 100644 src/live_effects/n-art-bpath-2geom.h create mode 100644 src/live_effects/parameter/Makefile_insert create mode 100644 src/live_effects/parameter/enum.h create mode 100644 src/live_effects/parameter/makefile.in create mode 100644 src/live_effects/parameter/parameter.cpp create mode 100644 src/live_effects/parameter/parameter.h create mode 100644 src/live_effects/parameter/path.cpp create mode 100644 src/live_effects/parameter/path.h create mode 100644 src/live_effects/parameter/point.cpp create mode 100644 src/live_effects/parameter/point.h create mode 100644 src/live_effects/todo.txt (limited to 'src/live_effects') 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 + * + * 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 + +#include "live_effects/effect.h" +#include "live_effects/lpeobject.h" +#include "live_effects/parameter/parameter.h" +#include +#include "live_effects/n-art-bpath-2geom.h" +#include "display/curve.h" +#include <2geom/sbasis-to-bezier.h> +#include + +// 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 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 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 orig_pathv = BPath_to_2GeomPath(path_in); + + std::vector result_pathv = doEffect(orig_pathv); + + NArtBpath *new_bpath = BPath_from_2GeomPath(result_pathv); + + return new_bpath; +} + +std::vector +Effect::doEffect (std::vector & path_in) +{ + Geom::Piecewise > pwd2_in; + // FIXME: find standard function to convert std::vector ==> Piecewise< D2 > + for (unsigned int i=0; i < path_in.size(); i++) { + pwd2_in.concat( path_in[i].toPwSb() ); + } + + Geom::Piecewise > pwd2_out = doEffect(pwd2_in); + + std::vector path_out = Geom::path_from_piecewise( pwd2_out, LPE_CONVERSION_TOLERANCE); + + return path_out; +} + +Geom::Piecewise > +Effect::doEffect (Geom::Piecewise > & 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(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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include "display/display-forward.h" +#include +#include +#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 LPETypeData[ENDTYPE_LPE]; +extern const Util::EnumDataConverter 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 ) + virtual NArtBpath * + doEffect (NArtBpath * path_in); + virtual std::vector + doEffect (std::vector & path_in); + virtual Geom::Piecewise > + doEffect (Geom::Piecewise > & pwd2_in); + + void registerParameter(Parameter * param); + + typedef std::map 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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-gears.h" + +#include +#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 _involute(double start, double stop) { + D2 B; + D2 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 _arc(double start, double stop, double R) { + D2 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 &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 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 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 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(leading_end); + } + + D2 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(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(&teeth) ); + registerParameter( dynamic_cast(&phi) ); +} + +LPEGears::~LPEGears() +{ + +} + +std::vector +LPEGears::doEffect (std::vector & path_in) +{ + std::vector 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 + * + * 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 doEffect (std::vector & 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 + * + * 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 +#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 +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 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 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(&origin) ); + registerParameter( dynamic_cast(&pattern) ); + registerParameter( dynamic_cast(©type) ); + + pattern.signal_path_pasted.connect(sigc::mem_fun(*this, &LPESkeletalStrokes::on_pattern_pasted)); +} + +LPESkeletalStrokes::~LPESkeletalStrokes() +{ + +} + + +Geom::Piecewise > +LPESkeletalStrokes::doEffect (Geom::Piecewise > & 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 * data = copytype.get_selected_data(); + if (!data) + return pwd2_in; + SkelCopyType type = data->id; + + Piecewise > uskeleton = arc_length_parametrization(Piecewise >(pwd2_in),2,.1); + uskeleton = remove_short_cuts(uskeleton,.01); + Piecewise > n = rot90(derivative(uskeleton)); + n = force_continuity(remove_short_cuts(n,.1)); + + D2 > patternd2 = make_cuts_independant(pattern); + Piecewise x=Piecewise(patternd2[0]-origin[0]); + Piecewise y=Piecewise(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 >output; + for (int i=0; i + * + * 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 > doEffect (Geom::Piecewise > & pwd2_in); + +private: + PathParam pattern; + PointParam origin; + EnumParam 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 + * + * 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 +#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(&factor) ); + registerParameter( dynamic_cast(¢er) ); +} + +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 + * + * 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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-test-doEffect-stack.h" + +#include <2geom/piecewise.h> +#include +#include + +namespace Inkscape { +namespace LivePathEffect { + + +LPEdoEffectStackTest::LPEdoEffectStackTest(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + step(_("Stack step"), _(""), "step", &wr, this) +{ + registerParameter( dynamic_cast(&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 +LPEdoEffectStackTest::doEffect (std::vector & path_in) +{ + if (step >= 3) { + return Effect::doEffect(path_in); + } else { + // return here + std::vector path_out = path_in; + return path_out; + } +} + +Geom::Piecewise > +LPEdoEffectStackTest::doEffect (Geom::Piecewise > & pwd2_in) +{ + Geom::Piecewise > 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 + * + * 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 doEffect (std::vector & path_in); + Geom::Piecewise > doEffect (Geom::Piecewise > & 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 +#include +#include + +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 + * + * 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 + +#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 + * + * 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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + + +#include "live_effects/n-art-bpath-2geom.h" +#include "svg/svg.h" +#include +#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 +#include +#include +#include + +static void curve_to_svgd(std::ostream & f, Geom::Curve const* c) { + if(Geom::LineSegment const *line_segment = dynamic_cast(c)) { + f << boost::format("L %g,%g ") % (*line_segment)[1][0] % (*line_segment)[1][1]; + } + else if(Geom::QuadraticBezier const *quadratic_bezier = dynamic_cast(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(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(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 const &p) { + std::vector::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(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(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(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 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 const &peek() const { return _pathset; } + +private: + std::vector _pathset; + Path *_current_path; + Point _current_point; + Point _initial_point; + double _continuity_tollerance; +}; + +static +std::vector +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 +SVGD_to_2GeomPath (char const *svgd) +{ + std::vector 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 +BPath_to_2GeomPath(NArtBpath const * bpath) +{ + std::vector 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 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 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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include <2geom/path.h> +#include + +std::vector SVGD_to_2GeomPath (char const *svgd); +std::vector BPath_to_2GeomPath (NArtBpath const *bpath); +char * SVGD_from_2GeomPath(std::vector const & path); +NArtBpath * BPath_from_2GeomPath (std::vector 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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include + +#include "ui/widget/registry.h" +#include "ui/widget/registered-enums.h" +#include + +#include "live_effects/parameter/parameter.h" +#include "verbs.h" + +namespace Inkscape { + +namespace LivePathEffect { + +template class EnumParam : public Parameter { +public: + EnumParam( const Glib::ustring& label, + const Glib::ustring& tip, + const Glib::ustring& key, + const Util::EnumDataConverter& 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(); + 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 (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* 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 * regenum; + + const Util::EnumDataConverter * 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 + * + * 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 +#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 (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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#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 ¶m_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 + * + * 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 +#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 > newpath; + std::vector temppath = SVGD_to_2GeomPath(strvalue); + for (unsigned int i=0; i < temppath.size(); i++) { + newpath.concat( temppath[i].toPwSb() ); + } + *( dynamic_cast > *> (this) ) = newpath; + return true; + } + + return false; +} + +gchar * +PathParam::param_writeSVGValue() const +{ + const std::vector temppath = + Geom::path_from_piecewise(* dynamic_cast > *> (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(_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(_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(_widget)->pack_start(*pButton, true, true); + _tooltips->set_tip(*pButton, _("Paste path")); + + static_cast(_widget)->show_all_children(); + + } + return dynamic_cast (_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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include <2geom/path.h> + +#include "ui/widget/registry.h" +#include + +#include "live_effects/parameter/parameter.h" + +#include + +namespace Inkscape { + +namespace LivePathEffect { + +class PathParam : public Geom::Piecewise >, 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 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 + * + * 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 +#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( 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(_widget)->pack_start(*pButton, true, true); + static_cast(_widget)->pack_start(*(pointwdg->getPoint()), true, true); + static_cast(_widget)->show_all_children(); + + _tooltips = new Gtk::Tooltips(); + _tooltips->set_tip(*pButton, _("Edit on-canvas")); + } + return dynamic_cast (_widget); +} + +void +PointParam::param_setValue(Geom::Point newpoint) +{ + *dynamic_cast( 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 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include +#include <2geom/point.h> + +#include "ui/widget/registry.h" +#include "ui/widget/registered-widget.h" +#include + +#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 -- cgit v1.2.3