From: johanengelen Date: Fri, 30 May 2008 20:36:13 +0000 (+0000) Subject: LPE STACKING! X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=0563fd55cbad59e8a878e6d4cbbdd8e47f74488d;p=inkscape.git LPE STACKING! (many thanks to the french students who made this.) --- diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt index 28d54d87c..7d3221357 100644 --- a/src/live_effects/CMakeLists.txt +++ b/src/live_effects/CMakeLists.txt @@ -13,13 +13,13 @@ lpe-constructgrid.h lpe-curvestitch.cpp lpe-curvestitch.h lpe-envelope.cpp -lpe-envelope.h lpe-gears.cpp lpe-gears.h lpegroupbbox.cpp lpegroupbbox.h lpe-knot.cpp lpe-knot.h +lpe-lattice.cpp lpeobject.cpp lpeobject.h lpeobject-reference.cpp @@ -46,3 +46,4 @@ spiro.cpp spiro.h ) + diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert index 4629a4d02..ea49aa269 100644 --- a/src/live_effects/Makefile_insert +++ b/src/live_effects/Makefile_insert @@ -32,8 +32,16 @@ live_effects_liblive_effects_a_SOURCES = \ live_effects/lpe-gears.h \ live_effects/lpe-test-doEffect-stack.cpp \ live_effects/lpe-test-doEffect-stack.h \ + live_effects/lpe-lattice.cpp \ + live_effects/lpe-lattice.h \ + live_effects/lpe-envelope.cpp \ + live_effects/lpe-envelope.h \ live_effects/lpe-spiro.cpp \ live_effects/lpe-spiro.h \ + live_effects/lpe-tangent_to_curve.cpp \ + live_effects/lpe-tangent_to_curve.h \ + live_effects/lpe-perp_bisector.cpp \ + live_effects/lpe-perp_bisector.h \ live_effects/spiro.h \ live_effects/spiro.cpp \ live_effects/bezctx.h \ @@ -42,11 +50,6 @@ live_effects_liblive_effects_a_SOURCES = \ live_effects/lpe-circle_with_radius.cpp \ live_effects/lpe-circle_with_radius.h \ live_effects/lpe-perspective_path.cpp \ - live_effects/lpe-perspective_path.h \ - live_effects/lpe-envelope.cpp \ - live_effects/lpe-envelope.h \ - live_effects/lpe-perp_bisector.cpp \ - live_effects/lpe-perp_bisector.h \ - live_effects/lpe-tangent_to_curve.cpp \ - live_effects/lpe-tangent_to_curve.h + live_effects/lpe-perspective_path.h + diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp index 906955575..f968ec5c0 100644 --- a/src/live_effects/effect.cpp +++ b/src/live_effects/effect.cpp @@ -43,8 +43,9 @@ #include "live_effects/lpe-circle_with_radius.h" #include "live_effects/lpe-perspective_path.h" #include "live_effects/lpe-spiro.h" -#include "live_effects/lpe-constructgrid.h" +#include "live_effects/lpe-lattice.h" #include "live_effects/lpe-envelope.h" +#include "live_effects/lpe-constructgrid.h" #include "live_effects/lpe-perp_bisector.h" #include "live_effects/lpe-tangent_to_curve.h" // end of includes @@ -66,14 +67,15 @@ const Util::EnumData LPETypeData[INVALID_LPE] = { {DOEFFECTSTACK_TEST, N_("doEffect stack test"), "doeffectstacktest"}, #endif {GEARS, N_("Gears"), "gears"}, - {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"}, + {CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"}, {CIRCLE_WITH_RADIUS, N_("Circle (center+radius)"), "circle_with_radius"}, {PERSPECTIVE_PATH, N_("Perspective path"), "perspective_path"}, {SPIRO, N_("Spiro spline"), "spiro"}, - {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"}, + {LATTICE, N_("Lattice Deformation"), "lattice"}, {ENVELOPE, N_("Envelope Deformation"), "envelope"}, + {CONSTRUCT_GRID, N_("Construct grid"), "construct_grid"}, {PERP_BISECTOR, N_("Perpendicular bisector"), "perp_bisector"}, - {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"}, + {TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"} }; const Util::EnumDataConverter LPETypeConverter(LPETypeData, INVALID_LPE); @@ -108,6 +110,12 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case CURVE_STITCH: neweffect = static_cast ( new LPECurveStitch(lpeobj) ); break; + case LATTICE: + neweffect = static_cast ( new LPELattice(lpeobj) ); + break; + case ENVELOPE: + neweffect = static_cast ( new LPEEnvelope(lpeobj) ); + break; case CIRCLE_WITH_RADIUS: neweffect = static_cast ( new LPECircleWithRadius(lpeobj) ); break; @@ -120,9 +128,6 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj) case CONSTRUCT_GRID: neweffect = static_cast ( new LPEConstructGrid(lpeobj) ); break; - case ENVELOPE: - neweffect = static_cast ( new LPEEnvelope(lpeobj) ); - break; case PERP_BISECTOR: neweffect = static_cast ( new LPEPerpBisector(lpeobj) ); break; diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h index 1255595d0..d5a49d71e 100644 --- a/src/live_effects/effect.h +++ b/src/live_effects/effect.h @@ -66,8 +66,9 @@ enum EffectType { CIRCLE_WITH_RADIUS, PERSPECTIVE_PATH, SPIRO, - CONSTRUCT_GRID, + LATTICE, ENVELOPE, + CONSTRUCT_GRID, PERP_BISECTOR, TANGENT_TO_CURVE, INVALID_LPE // This must be last diff --git a/src/live_effects/lpe-bendpath.cpp b/src/live_effects/lpe-bendpath.cpp index 6b9fbbb97..282ea8270 100644 --- a/src/live_effects/lpe-bendpath.cpp +++ b/src/live_effects/lpe-bendpath.cpp @@ -68,8 +68,6 @@ LPEBendPath::LPEBendPath(LivePathEffectObject *lpeobject) : prop_scale.param_set_increments(0.01, 0.10); concatenate_before_pwd2 = true; - - groupSpecialBehavior = false; } LPEBendPath::~LPEBendPath() @@ -80,12 +78,8 @@ LPEBendPath::~LPEBendPath() void LPEBendPath::doBeforeEffect (SPLPEItem *lpeitem) { - if(SP_IS_GROUP(lpeitem)) - { - groupSpecialBehavior = true; - - original_bbox(lpeitem); - } + // get the item bounding box + original_bbox(lpeitem); } Geom::Piecewise > @@ -105,11 +99,6 @@ LPEBendPath::doEffect_pwd2 (Geom::Piecewise > const & pwd Piecewise y = vertical_pattern.get_value() ? Piecewise(patternd2[0]) : Piecewise(patternd2[1]); //We use the group bounding box size or the path bbox size to translate well x and y - if(groupSpecialBehavior == false) - { - boundingbox_X = bounds_exact(x); - boundingbox_Y = bounds_exact(y); - } x-= vertical_pattern.get_value() ? boundingbox_Y.min() : boundingbox_X.min(); y-= vertical_pattern.get_value() ? boundingbox_X.middle() : boundingbox_Y.middle(); diff --git a/src/live_effects/lpe-bendpath.h b/src/live_effects/lpe-bendpath.h index e05112de9..1d3a9861e 100644 --- a/src/live_effects/lpe-bendpath.h +++ b/src/live_effects/lpe-bendpath.h @@ -47,8 +47,6 @@ private: BoolParam scale_y_rel; BoolParam vertical_pattern; - bool groupSpecialBehavior; - void on_pattern_pasted(); LPEBendPath(const LPEBendPath&); diff --git a/src/live_effects/lpe-perp_bisector.cpp b/src/live_effects/lpe-perp_bisector.cpp index b31ad6696..52a21fbaf 100644 --- a/src/live_effects/lpe-perp_bisector.cpp +++ b/src/live_effects/lpe-perp_bisector.cpp @@ -1,187 +1,204 @@ -#define INKSCAPE_LPE_PERP_BISECTOR_CPP -/** \file - * LPE implementation. - */ -/* - * Authors: - * Maximilian Albert - * Johan Engelen - * - * Copyright (C) Johan Engelen 2007 - * Copyright (C) Maximilin Albert 2008 - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "live_effects/lpe-perp_bisector.h" -#include "display/curve.h" -#include -#include "sp-path.h" -#include "line-geometry.h" - -#include <2geom/path.h> - -namespace Inkscape { -namespace LivePathEffect { - -/* FIXME: We should arguably make these member functions of LPEPerpBisector. - Is there an easy way to register member functions with knotholder? - */ -NR::Point bisector_left_end_get(SPItem *item) { - Inkscape::LivePathEffect::LPEPerpBisector *lpe = - (Inkscape::LivePathEffect::LPEPerpBisector *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - return NR::Point(lpe->C); -} - -NR::Point bisector_right_end_get(SPItem *item) { - Inkscape::LivePathEffect::LPEPerpBisector *lpe = - (Inkscape::LivePathEffect::LPEPerpBisector *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - return NR::Point(lpe->D); -} - -void -bisector_end_set(SPItem *item, NR::Point const &p, bool left) { - Inkscape::LivePathEffect::LPEPerpBisector *lpe = - (Inkscape::LivePathEffect::LPEPerpBisector *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - double lambda = Geom::nearest_point(p.to_2geom(), lpe->M, lpe->perp_dir); - if (left) { - lpe->C = lpe->M + lpe->perp_dir * lambda; - lpe->length_left.param_set_value(lambda); - } else { - lpe->D = lpe->M + lpe->perp_dir * lambda; - lpe->length_right.param_set_value(-lambda); - } - - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true); -} - -void -bisector_left_end_set(SPItem *item, NR::Point const &p, NR::Point const &/*origin*/, guint /*state*/) { - bisector_end_set(item, p); -} - -void -bisector_right_end_set(SPItem *item, NR::Point const &p, NR::Point const &/*origin*/, guint /*state*/) { - bisector_end_set(item, p, false); -} - -NR::Point path_start_get(SPItem *item) { - Inkscape::LivePathEffect::LPEPerpBisector *lpe = - (Inkscape::LivePathEffect::LPEPerpBisector *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - return NR::Point(lpe->A); -} - -NR::Point path_end_get(SPItem *item) { - Inkscape::LivePathEffect::LPEPerpBisector *lpe = - (Inkscape::LivePathEffect::LPEPerpBisector *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - return NR::Point(lpe->B); -} - -void -path_set_start_end(SPItem *item, NR::Point const &p, bool start) { - SPCurve* curve = sp_path_get_curve_for_edit (SP_PATH(item)); // TODO: Should we use sp_shape_get_curve()? - NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM(item))); - - Geom::Point A, B; - if (start) { - A = p.to_2geom(); - B = (curve->last_point()).to_2geom(); - } else { - A = (curve->first_point()).to_2geom(); - B = (p.to_2geom()); - } - - SPCurve *c = new SPCurve(); - c->moveto(A); - c->lineto(B); - sp_path_set_original_curve(SP_PATH(item), c, TRUE, true); - c->unref(); -} - -void path_start_set(SPItem *item, NR::Point const &p, NR::Point const &/*origin*/, guint /*state*/) { - path_set_start_end(item, p); -} - -void path_end_set(SPItem *item, NR::Point const &p, NR::Point const &/*origin*/, guint /*state*/) { - path_set_start_end(item, p, false); -} - -LPEPerpBisector::LPEPerpBisector(LivePathEffectObject *lpeobject) : - Effect(lpeobject), - length_left(_("Length left"), _("Specifies the left end of the bisector"), "length-left", &wr, this, 200), - length_right(_("Length right"), _("Specifies the right end of the bisector"), "length-right", &wr, this, 200), - A(0,0), B(0,0), M(0,0), C(0,0), D(0,0), perp_dir(0,0) -{ - // register all your parameters here, so Inkscape knows which parameters this effect has: - registerParameter( dynamic_cast(&length_left) ); - registerParameter( dynamic_cast(&length_right) ); - - registerKnotHolderHandle(path_start_set, path_start_get); - registerKnotHolderHandle(path_end_set, path_end_get); - registerKnotHolderHandle(bisector_left_end_set, bisector_left_end_get); - registerKnotHolderHandle(bisector_right_end_set, bisector_right_end_get); -} - -LPEPerpBisector::~LPEPerpBisector() -{ -} - -void -LPEPerpBisector::doOnApply (SPLPEItem *lpeitem) -{ - /* make the path a straight line */ - SPCurve* curve = sp_path_get_curve_for_edit (SP_PATH(lpeitem)); // TODO: Should we use sp_shape_get_curve()? - - Geom::Point A((curve->first_point()).to_2geom()); - Geom::Point B((curve->last_point()).to_2geom()); - - SPCurve *c = new SPCurve(); - c->moveto(A); - c->lineto(B); - // TODO: Why doesn't sp_path_set_original_curve(SP_PATH(lpeitem), c, TRUE, true) work? - SP_PATH(lpeitem)->original_curve = c->ref(); - c->unref(); -} - - -Geom::Piecewise > -LPEPerpBisector::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) -{ - using namespace Geom; - - Piecewise > output; - - A = pwd2_in.firstValue(); - B = pwd2_in.lastValue(); - M = (A + B)/2; - - perp_dir = unit_vector((B - A).ccw()); - - C = M + perp_dir * length_left; - D = M - perp_dir * length_right; - - output = Piecewise >(D2(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); - - 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 : +#define INKSCAPE_LPE_PERP_BISECTOR_CPP +/** \file + * LPE implementation. + */ +/* + * Authors: + * Maximilian Albert + * Johan Engelen + * + * Copyright (C) Johan Engelen 2007 + * Copyright (C) Maximilin Albert 2008 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-perp_bisector.h" +#include "display/curve.h" +#include +#include "sp-path.h" +#include "line-geometry.h" +#include "sp-lpe-item.h" +#include <2geom/path.h> + +namespace Inkscape { +namespace LivePathEffect { + +/* FIXME: We should make these member functions of LPEPerpBisector. + Is there an easy way to register member functions with knotholder? + KNOWN BUG: Because of the above, this effect does not work well when in an LPE stack + */ +NR::Point bisector_left_end_get(SPItem *item) { + Inkscape::LivePathEffect::LPEPerpBisector *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (lpe) + return NR::Point(lpe->C); + else + return NR::Point(0,0); +} + +NR::Point bisector_right_end_get(SPItem *item) { + Inkscape::LivePathEffect::LPEPerpBisector *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (lpe) + return NR::Point(lpe->D); + else + return NR::Point(0,0); +} + +void +bisector_end_set(SPItem *item, NR::Point const &p, bool left) { + Inkscape::LivePathEffect::LPEPerpBisector *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (!lpe) + return; + + double lambda = Geom::nearest_point(p.to_2geom(), lpe->M, lpe->perp_dir); + if (left) { + lpe->C = lpe->M + lpe->perp_dir * lambda; + lpe->length_left.param_set_value(lambda); + } else { + lpe->D = lpe->M + lpe->perp_dir * lambda; + lpe->length_right.param_set_value(-lambda); + } + + // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true, true); +} + +void +bisector_left_end_set(SPItem *item, NR::Point const &p, NR::Point const &/*origin*/, guint /*state*/) { + bisector_end_set(item, p); +} + +void +bisector_right_end_set(SPItem *item, NR::Point const &p, NR::Point const &/*origin*/, guint /*state*/) { + bisector_end_set(item, p, false); +} + +NR::Point path_start_get(SPItem *item) { + Inkscape::LivePathEffect::LPEPerpBisector *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (lpe) + return NR::Point(lpe->A); + else + return NR::Point(0,0); +} + +NR::Point path_end_get(SPItem *item) { + Inkscape::LivePathEffect::LPEPerpBisector *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (lpe) + return NR::Point(lpe->B); + else + return NR::Point(0,0); +} + +void +path_set_start_end(SPItem *item, NR::Point const &p, bool start) { + SPCurve* curve = sp_path_get_curve_for_edit (SP_PATH(item)); // TODO: Should we use sp_shape_get_curve()? + NR::Matrix const i2d (sp_item_i2d_affine (SP_ITEM(item))); + + Geom::Point A, B; + if (start) { + A = p.to_2geom(); + B = (curve->last_point()).to_2geom(); + } else { + A = (curve->first_point()).to_2geom(); + B = (p.to_2geom()); + } + + SPCurve *c = new SPCurve(); + c->moveto(A); + c->lineto(B); + sp_path_set_original_curve(SP_PATH(item), c, TRUE, true); + c->unref(); +} + +void path_start_set(SPItem *item, NR::Point const &p, NR::Point const &/*origin*/, guint /*state*/) { + path_set_start_end(item, p); +} + +void path_end_set(SPItem *item, NR::Point const &p, NR::Point const &/*origin*/, guint /*state*/) { + path_set_start_end(item, p, false); +} + +LPEPerpBisector::LPEPerpBisector(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + length_left(_("Length left"), _("Specifies the left end of the bisector"), "length-left", &wr, this, 200), + length_right(_("Length right"), _("Specifies the right end of the bisector"), "length-right", &wr, this, 200), + A(0,0), B(0,0), M(0,0), C(0,0), D(0,0), perp_dir(0,0) +{ + // register all your parameters here, so Inkscape knows which parameters this effect has: + registerParameter( dynamic_cast(&length_left) ); + registerParameter( dynamic_cast(&length_right) ); + + registerKnotHolderHandle(path_start_set, path_start_get); + registerKnotHolderHandle(path_end_set, path_end_get); + registerKnotHolderHandle(bisector_left_end_set, bisector_left_end_get); + registerKnotHolderHandle(bisector_right_end_set, bisector_right_end_get); +} + +LPEPerpBisector::~LPEPerpBisector() +{ +} + +void +LPEPerpBisector::doOnApply (SPLPEItem *lpeitem) +{ + /* make the path a straight line */ + SPCurve* curve = sp_path_get_curve_for_edit (SP_PATH(lpeitem)); // TODO: Should we use sp_shape_get_curve()? + + Geom::Point A((curve->first_point()).to_2geom()); + Geom::Point B((curve->last_point()).to_2geom()); + + SPCurve *c = new SPCurve(); + c->moveto(A); + c->lineto(B); + // TODO: Why doesn't sp_path_set_original_curve(SP_PATH(lpeitem), c, TRUE, true) work? + SP_PATH(lpeitem)->original_curve = c->ref(); + c->unref(); +} + + +Geom::Piecewise > +LPEPerpBisector::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) +{ + using namespace Geom; + + Piecewise > output; + + A = pwd2_in.firstValue(); + B = pwd2_in.lastValue(); + M = (A + B)/2; + + perp_dir = unit_vector((B - A).ccw()); + + C = M + perp_dir * length_left; + D = M - perp_dir * length_right; + + output = Piecewise >(D2(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); + + 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-perspective_path.cpp b/src/live_effects/lpe-perspective_path.cpp index 57bc458a9..cf6d1cecd 100644 --- a/src/live_effects/lpe-perspective_path.cpp +++ b/src/live_effects/lpe-perspective_path.cpp @@ -59,8 +59,6 @@ LPEPerspectivePath::LPEPerspectivePath(LivePathEffectObject *lpeobject) : Proj::TransfMat3x4 pmat = persp->tmat; pmat.copy_tmat(tmat); - - groupSpecialBehavior = false; } LPEPerspectivePath::~LPEPerspectivePath() @@ -71,14 +69,9 @@ LPEPerspectivePath::~LPEPerspectivePath() void LPEPerspectivePath::doBeforeEffect (SPLPEItem *lpeitem) { - if(SP_IS_GROUP(lpeitem)) - { - groupSpecialBehavior = true; - original_bbox(lpeitem); - } + original_bbox(lpeitem, true); } - Geom::Piecewise > LPEPerspectivePath::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) { @@ -93,14 +86,9 @@ LPEPerspectivePath::doEffect_pwd2 (Geom::Piecewise > cons D2 > B = make_cuts_independant(path_a_pw); Piecewise preimage[4]; - if(!groupSpecialBehavior) - { - boundingbox_X = bounds_fast(pwd2_in)[0]; - boundingbox_Y = bounds_fast(pwd2_in)[1]; - } - Geom::Point orig = Geom::Point(uses_plane_xy ? boundingbox_X.max() : boundingbox_X.min(), boundingbox_Y.middle()); + //Geom::Point orig = Geom::Point(bounds_X.min(), bounds_Y.middle()); //orig = Geom::Point(orig[X], sp_document_height(inkscape_active_document()) - orig[Y]); diff --git a/src/live_effects/lpe-perspective_path.h b/src/live_effects/lpe-perspective_path.h index a2f451e65..02dcf2325 100644 --- a/src/live_effects/lpe-perspective_path.h +++ b/src/live_effects/lpe-perspective_path.h @@ -48,8 +48,6 @@ private: std::vector handles; double tmat[3][4]; - - bool groupSpecialBehavior; }; } //namespace LivePathEffect diff --git a/src/live_effects/lpe-tangent_to_curve.cpp b/src/live_effects/lpe-tangent_to_curve.cpp index 65948837f..6293ba233 100644 --- a/src/live_effects/lpe-tangent_to_curve.cpp +++ b/src/live_effects/lpe-tangent_to_curve.cpp @@ -1,148 +1,170 @@ -#define INKSCAPE_LPE_TANGENT_TO_CURVE_CPP -/** \file - * Implementation of tangent-to-curve LPE. - */ - -/* - * Authors: - * Johan Engelen - * Maximilian Albert - * - * Copyright (C) Johan Engelen 2007 - * Copyright (C) Maximilian Albert 2008 - * - * Released under GNU GPL, read the file 'COPYING' for more information - */ - -#include "live_effects/lpe-tangent_to_curve.h" -// FIXME: The following are only needed to convert the path's SPCurve* to pwd2. -// There must be a more convenient way to achieve this. -#include "sp-path.h" -#include "display/curve.h" -#include "libnr/n-art-bpath-2geom.h" - -#include <2geom/path.h> -#include <2geom/transforms.h> - -namespace Inkscape { -namespace LivePathEffect { - -/* FIXME: We should arguably make these member functions of LPETangentToCurve. - Is there an easy way to register member functions with knotholder? - */ -NR::Point attach_pt_get(SPItem *item) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - (Inkscape::LivePathEffect::LPETangentToCurve *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - return lpe->ptA; -} - -void attach_pt_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - (Inkscape::LivePathEffect::LPETangentToCurve *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - using namespace Geom; - - // FIXME: There must be a better way of converting the path's SPCurve* to pwd2. - SPCurve *curve = sp_path_get_curve_for_edit (SP_PATH(item)); - const NArtBpath *bpath = curve->get_bpath(); - Piecewise > pwd2; - std::vector pathv = BPath_to_2GeomPath(bpath); - for (unsigned int i=0; i < pathv.size(); i++) { - pwd2.concat(pathv[i].toPwSb()); - } - - double t0 = nearest_point(p.to_2geom(), pwd2); - lpe->t_attach.param_set_value(t0); - - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true); -} - -NR::Point left_end_get(SPItem *item) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - (Inkscape::LivePathEffect::LPETangentToCurve *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - return lpe->C; -} - -NR::Point right_end_get(SPItem *item) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - (Inkscape::LivePathEffect::LPETangentToCurve *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - return lpe->D; -} - -void left_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - (Inkscape::LivePathEffect::LPETangentToCurve *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - double lambda = Geom::nearest_point(p.to_2geom(), lpe->ptA, lpe->derivA); - lpe->length_left.param_set_value(-lambda); - - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true); -} - -void right_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { - Inkscape::LivePathEffect::LPETangentToCurve *lpe = - (Inkscape::LivePathEffect::LPETangentToCurve *) sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - - double lambda = Geom::nearest_point(p.to_2geom(), lpe->ptA, lpe->derivA); - lpe->length_right.param_set_value(lambda); - - sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true); -} - -LPETangentToCurve::LPETangentToCurve(LivePathEffectObject *lpeobject) : - Effect(lpeobject), - t_attach(_("Location along curve"), _("Location of the point of attachment along the curve (between 0.0 and number-of-segments)"), "t_attach", &wr, this, 0.5), - length_left(_("Length left"), _("Specifies the left end of the tangent"), "length-left", &wr, this, 150), - length_right(_("Length right"), _("Specifies the right end of the tangent"), "length-right", &wr, this, 150), - angle(_("Angle"), _("Additional angle between tangent and curve"), "angle", &wr, this, 0.0) -{ - registerParameter( dynamic_cast(&t_attach) ); - registerParameter( dynamic_cast(&length_left) ); - registerParameter( dynamic_cast(&length_right) ); - registerParameter( dynamic_cast(&angle) ); - registerKnotHolderHandle(attach_pt_set, attach_pt_get); - registerKnotHolderHandle(left_end_set, left_end_get); - registerKnotHolderHandle(right_end_set, right_end_get); -} - -LPETangentToCurve::~LPETangentToCurve() -{ -} - -Geom::Piecewise > -LPETangentToCurve::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) -{ - using namespace Geom; - Piecewise > output; - - ptA = pwd2_in.valueAt(t_attach); - derivA = unit_vector(derivative(pwd2_in).valueAt(t_attach)); - - // TODO: Why are positive angles measured clockwise, not counterclockwise? - Geom::Rotate rot(Geom::Rotate::from_degrees(-angle)); - derivA = derivA * rot; - - C = ptA - derivA * length_left; - D = ptA + derivA * length_right; - - output = Piecewise >(D2(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); - - 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 : +#define INKSCAPE_LPE_TANGENT_TO_CURVE_CPP +/** \file + * Implementation of tangent-to-curve LPE. + */ + +/* + * Authors: + * Johan Engelen + * Maximilian Albert + * + * Copyright (C) Johan Engelen 2007 + * Copyright (C) Maximilian Albert 2008 + * + * Released under GNU GPL, read the file 'COPYING' for more information + */ + +#include "live_effects/lpe-tangent_to_curve.h" +// FIXME: The following are only needed to convert the path's SPCurve* to pwd2. +// There must be a more convenient way to achieve this. +#include "sp-path.h" +#include "display/curve.h" +#include "libnr/n-art-bpath-2geom.h" + +#include <2geom/path.h> +#include <2geom/transforms.h> + +namespace Inkscape { +namespace LivePathEffect { + +/* FIXME: We should make these member functions of LPETangentToCurve. + Is there an easy way to register member functions with knotholder? + KNOWN BUG: Because of the above, this effect does not work well when in an LPE stack +*/ +NR::Point attach_pt_get(SPItem *item) { + Inkscape::LivePathEffect::LPETangentToCurve *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (lpe) + return lpe->ptA; + else + return NR::Point(0,0); +} + +void attach_pt_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { + Inkscape::LivePathEffect::LPETangentToCurve *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (!lpe) + return; + + using namespace Geom; + + // FIXME: There must be a better way of converting the path's SPCurve* to pwd2. + SPCurve *curve = sp_path_get_curve_for_edit (SP_PATH(item)); + const NArtBpath *bpath = curve->get_bpath(); + Piecewise > pwd2; + std::vector pathv = BPath_to_2GeomPath(bpath); + for (unsigned int i=0; i < pathv.size(); i++) { + pwd2.concat(pathv[i].toPwSb()); + } + + double t0 = nearest_point(p.to_2geom(), pwd2); + lpe->t_attach.param_set_value(t0); + + // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true, true); +} + +NR::Point left_end_get(SPItem *item) { + Inkscape::LivePathEffect::LPETangentToCurve *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (lpe) + return lpe->C; + else + return NR::Point(0,0); +} + +NR::Point right_end_get(SPItem *item) { + Inkscape::LivePathEffect::LPETangentToCurve *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (lpe) + return lpe->D; + else + return NR::Point(0,0); +} + +void left_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { + Inkscape::LivePathEffect::LPETangentToCurve *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (!lpe) + return; + + double lambda = Geom::nearest_point(p.to_2geom(), lpe->ptA, lpe->derivA); + lpe->length_left.param_set_value(-lambda); + + // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true, true); +} + +void right_end_set(SPItem *item, NR::Point const &p, NR::Point const &origin, guint state) { + Inkscape::LivePathEffect::LPETangentToCurve *lpe = + dynamic_cast (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))); + + if (!lpe) + return; + + double lambda = Geom::nearest_point(p.to_2geom(), lpe->ptA, lpe->derivA); + lpe->length_right.param_set_value(lambda); + + // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating. + sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), true, true); +} + +LPETangentToCurve::LPETangentToCurve(LivePathEffectObject *lpeobject) : + Effect(lpeobject), + t_attach(_("Location along curve"), _("Location of the point of attachment along the curve (between 0.0 and number-of-segments)"), "t_attach", &wr, this, 0.5), + length_left(_("Length left"), _("Specifies the left end of the tangent"), "length-left", &wr, this, 150), + length_right(_("Length right"), _("Specifies the right end of the tangent"), "length-right", &wr, this, 150), + angle(_("Angle"), _("Additional angle between tangent and curve"), "angle", &wr, this, 0.0) +{ + registerParameter( dynamic_cast(&t_attach) ); + registerParameter( dynamic_cast(&length_left) ); + registerParameter( dynamic_cast(&length_right) ); + registerParameter( dynamic_cast(&angle) ); + registerKnotHolderHandle(attach_pt_set, attach_pt_get); + registerKnotHolderHandle(left_end_set, left_end_get); + registerKnotHolderHandle(right_end_set, right_end_get); +} + +LPETangentToCurve::~LPETangentToCurve() +{ +} + +Geom::Piecewise > +LPETangentToCurve::doEffect_pwd2 (Geom::Piecewise > const & pwd2_in) +{ + using namespace Geom; + Piecewise > output; + + ptA = pwd2_in.valueAt(t_attach); + derivA = unit_vector(derivative(pwd2_in).valueAt(t_attach)); + + // TODO: Why are positive angles measured clockwise, not counterclockwise? + Geom::Rotate rot(Geom::Rotate::from_degrees(-angle)); + derivA = derivA * rot; + + C = ptA - derivA * length_left; + D = ptA + derivA * length_right; + + output = Piecewise >(D2(Linear(C[X], D[X]), Linear(C[Y], D[Y]))); + + 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/lpeobject-reference.cpp b/src/live_effects/lpeobject-reference.cpp index 34005e791..8a8dd94da 100644 --- a/src/live_effects/lpeobject-reference.cpp +++ b/src/live_effects/lpeobject-reference.cpp @@ -59,7 +59,7 @@ bool LPEObjectReference::_acceptObject(SPObject * const obj) const } void -LPEObjectReference::link(char *to) +LPEObjectReference::link(const char *to) { if ( to == NULL ) { quit_listening(); diff --git a/src/live_effects/lpeobject-reference.h b/src/live_effects/lpeobject-reference.h index 6a2f75327..e5f7556f1 100644 --- a/src/live_effects/lpeobject-reference.h +++ b/src/live_effects/lpeobject-reference.h @@ -41,7 +41,7 @@ public: sigc::connection _delete_connection; sigc::connection _changed_connection; - void link(char* to); + void link(const char* to); void unlink(void); void start_listening(LivePathEffectObject* to); void quit_listening(void); diff --git a/src/nodepath.cpp b/src/nodepath.cpp index ff442fd70..e827f9fb0 100644 --- a/src/nodepath.cpp +++ b/src/nodepath.cpp @@ -51,6 +51,7 @@ #include #include #include "live_effects/lpeobject.h" +#include "live_effects/effect.h" #include "live_effects/parameter/parameter.h" #include "util/mathfns.h" #include "display/snap-indicator.h" @@ -236,9 +237,9 @@ Inkscape::NodePath::Path *sp_nodepath_new(SPDesktop *desktop, SPObject *object, if ( sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(np->object)) ) { np->repr_key = g_strdup("inkscape:original-d"); - LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(SP_LPE_ITEM(np->object)); - if (lpeobj && lpeobj->lpe) { - lpeobj->lpe->setup_nodepath(np); + Inkscape::LivePathEffect::Effect* lpe = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(np->object)); + if (lpe) { + lpe->setup_nodepath(np); } } else { np->repr_key = g_strdup("d"); diff --git a/src/object-edit.cpp b/src/object-edit.cpp index 091c99734..ecd6227f4 100644 --- a/src/object-edit.cpp +++ b/src/object-edit.cpp @@ -63,8 +63,12 @@ static SPKnotHolder *sp_lpe_knot_holder(SPItem *item, SPDesktop *desktop) { SPKnotHolder *knot_holder = sp_knot_holder_new(desktop, item, NULL); - Inkscape::LivePathEffect::Effect *effect = sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item)); - effect->addHandles(knot_holder); + Inkscape::LivePathEffect::Effect *effect = sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item)); + if (!effect) { + g_error("sp_lpe_knot_holder: logical error, this method cannot be called with item having an LPE"); + } else { + effect->addHandles(knot_holder); + } return knot_holder; } @@ -72,12 +76,11 @@ static SPKnotHolder *sp_lpe_knot_holder(SPItem *item, SPDesktop *desktop) SPKnotHolder * sp_item_knot_holder(SPItem *item, SPDesktop *desktop) { - if (sp_lpe_item_has_path_effect(SP_LPE_ITEM(item)) && - sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item))->isVisible() && - sp_lpe_item_get_livepatheffect(SP_LPE_ITEM(item))->providesKnotholder()) { + if (sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item)) && + sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))->isVisible() && + sp_lpe_item_get_current_lpe(SP_LPE_ITEM(item))->providesKnotholder()) { return sp_lpe_knot_holder(item, desktop); - } else - if (SP_IS_RECT(item)) { + } else if (SP_IS_RECT(item)) { return sp_rect_knot_holder(item, desktop); } else if (SP_IS_BOX3D(item)) { return box3d_knot_holder(item, desktop); diff --git a/src/path-chemistry.cpp b/src/path-chemistry.cpp index 5b57e326e..996ae3fd5 100644 --- a/src/path-chemistry.cpp +++ b/src/path-chemistry.cpp @@ -368,7 +368,7 @@ sp_item_list_to_curves(const GSList *items, GSList **selected, GSList **to_selec } if (SP_IS_GROUP(item)) { - sp_lpe_item_remove_path_effect(SP_LPE_ITEM(item), true); + sp_lpe_item_remove_all_path_effects(SP_LPE_ITEM(item), true); GSList *item_list = sp_item_group_item_list(SP_GROUP(item)); GSList *item_to_select = NULL; diff --git a/src/selection-chemistry.cpp b/src/selection-chemistry.cpp index 0f8946441..9c826c949 100644 --- a/src/selection-chemistry.cpp +++ b/src/selection-chemistry.cpp @@ -893,7 +893,7 @@ void sp_selection_paste_livepatheffect() void sp_selection_remove_livepatheffect_impl(SPItem *item) { if ( item && SP_IS_LPE_ITEM(item) ) { - sp_lpe_item_remove_path_effect(SP_LPE_ITEM(item), false); + sp_lpe_item_remove_all_path_effects(SP_LPE_ITEM(item), false); } } diff --git a/src/shape-editor.cpp b/src/shape-editor.cpp index 40e92e0dd..1d60671aa 100644 --- a/src/shape-editor.cpp +++ b/src/shape-editor.cpp @@ -191,9 +191,9 @@ void ShapeEditor::set_item(SPItem *item) { if (item) { SPLPEItem *lpeitem = SP_LPE_ITEM(item); - if (!sp_lpe_item_has_path_effect(lpeitem) || - !sp_lpe_item_get_livepatheffect(lpeitem)->isVisible() || - !sp_lpe_item_get_livepatheffect(lpeitem)->providesKnotholder()) { + if (!sp_lpe_item_get_current_lpe(lpeitem) || // if returns NULL, the whole expression evaluates to true and C++ will not call the otherwise crashing 2 functions below + !sp_lpe_item_get_current_lpe(lpeitem)->isVisible() || + !sp_lpe_item_get_current_lpe(lpeitem)->providesKnotholder()) { // only create nodepath if the item either doesn't have an LPE // or the LPE is invisible or it doesn't provide a knotholder itself this->nodepath = diff --git a/src/sp-item-group.cpp b/src/sp-item-group.cpp index acdf8aa36..20efd8fe7 100644 --- a/src/sp-item-group.cpp +++ b/src/sp-item-group.cpp @@ -23,6 +23,7 @@ #include #include "display/nr-arena-group.h" +#include "display/curve.h" #include "libnr/nr-matrix-ops.h" #include "libnr/nr-matrix-fns.h" #include "xml/repr.h" @@ -42,6 +43,8 @@ #include "inkscape.h" #include "desktop-handles.h" #include "selection.h" +#include "live_effects/lpeobject.h" +#include "live_effects/lpeobject-reference.h" static void sp_group_class_init (SPGroupClass *klass); static void sp_group_init (SPGroup *group); @@ -66,6 +69,7 @@ static void sp_group_hide (SPItem * item, unsigned int key); static void sp_group_snappoints (SPItem const *item, SnapPointsIter p); static void sp_group_update_patheffect(SPLPEItem *lpeitem, bool write); +static void sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup); static SPLPEItemClass * parent_class; @@ -207,6 +211,9 @@ sp_group_update (SPObject *object, SPCtx *ctx, unsigned int flags) static void sp_group_modified (SPObject *object, guint flags) { + if (((SPObjectClass *) (parent_class))->modified) + ((SPObjectClass *) (parent_class))->modified (object, flags); + SP_GROUP(object)->group->onModified(flags); } @@ -364,7 +371,7 @@ sp_item_group_ungroup (SPGroup *group, GSList **children, bool do_done) gitem = SP_ITEM(group); } - sp_lpe_item_remove_path_effect(SP_LPE_ITEM(group), false); + sp_lpe_item_remove_all_path_effects(SP_LPE_ITEM(group), false); /* Step 1 - generate lists of children objects */ GSList *items = NULL; @@ -802,7 +809,7 @@ void CGroup::onOrderChanged (Inkscape::XML::Node *child, Inkscape::XML::Node *, } static void -sp_group_update_patheffect (SPLPEItem *lpeitem, bool /*write*/) +sp_group_update_patheffect (SPLPEItem *lpeitem, bool write) { #ifdef GROUP_VERBOSE g_message("sp_group_update_patheffect: %p\n", lpeitem); @@ -814,11 +821,51 @@ sp_group_update_patheffect (SPLPEItem *lpeitem, bool /*write*/) for ( GSList const *iter = item_list; iter; iter = iter->next ) { SPObject *subitem = static_cast(iter->data); if (SP_IS_LPE_ITEM(subitem)) { - sp_lpe_item_update_patheffect(SP_LPE_ITEM(subitem), true); + if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (subitem))->update_patheffect) { + SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (subitem))->update_patheffect (SP_LPE_ITEM(subitem), write); + } + } + } + + if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) { + for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) + { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + lpeobj->lpe->doBeforeEffect(lpeitem); } + + sp_group_perform_patheffect(SP_GROUP(lpeitem), SP_GROUP(lpeitem)); } } +static void +sp_group_perform_patheffect(SPGroup *group, SPGroup *topgroup) +{ + GSList const *item_list = sp_item_group_item_list(SP_GROUP(group)); + for ( GSList const *iter = item_list; iter; iter = iter->next ) { + SPObject *subitem = static_cast(iter->data); + if (SP_IS_GROUP(subitem)) { + sp_group_perform_patheffect(SP_GROUP(subitem), topgroup); + } else if (SP_IS_SHAPE(subitem)) { + SPCurve * c = sp_shape_get_curve(SP_SHAPE(subitem)); + sp_lpe_item_perform_path_effect(SP_LPE_ITEM(topgroup), c); + sp_shape_set_curve(SP_SHAPE(subitem), c, TRUE); + + Inkscape::XML::Node *repr = SP_OBJECT_REPR(subitem); + + NArtBpath *abp = c->first_bpath(); + if (abp) { + gchar *str = sp_svg_write_path(abp); + repr->setAttribute("d", str); + g_free(str); + } else { + repr->setAttribute("d", ""); + } + + c->unref(); + } + } +} /* Local Variables: diff --git a/src/sp-item.cpp b/src/sp-item.cpp index 0ab8566eb..be6be4f63 100644 --- a/src/sp-item.cpp +++ b/src/sp-item.cpp @@ -71,6 +71,7 @@ #include "live_effects/lpeobject.h" #include "live_effects/effect.h" +#include "live_effects/lpeobject-reference.h" #define noSP_ITEM_DEBUG_IDLE @@ -1296,15 +1297,21 @@ sp_item_adjust_livepatheffect (SPItem *item, NR::Matrix const &postmul, bool set SPLPEItem *lpeitem = SP_LPE_ITEM (item); if ( sp_lpe_item_has_path_effect(lpeitem) ) { - LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem); - LivePathEffectObject *new_lpeobj = lpeobj->fork_private_if_necessary(); - if (new_lpeobj != lpeobj) { - sp_lpe_item_set_path_effect(lpeitem, new_lpeobj); - } - - Inkscape::LivePathEffect::Effect * effect = sp_lpe_item_get_livepatheffect(lpeitem); - if (effect) { - effect->transform_multiply (to_2geom(postmul), set); + PathEffectList effect_list = sp_lpe_item_get_effect_list(lpeitem); + for (PathEffectList::iterator it = effect_list.begin(); it != effect_list.end(); it++) + { + // If the path effect is used by 2 or more items, fork it + // so that each object has its own independent copy of the effect + LivePathEffectObject *lpeobj = (*it)->lpeobject; + LivePathEffectObject *new_lpeobj = lpeobj->fork_private_if_necessary(); + if (new_lpeobj != lpeobj) { + sp_lpe_item_replace_path_effect(lpeitem, lpeobj, new_lpeobj); + } + + if (lpeobj->lpe) { + Inkscape::LivePathEffect::Effect * effect = lpeobj->lpe; + effect->transform_multiply(to_2geom(postmul), set); + } } } } diff --git a/src/sp-lpe-item.cpp b/src/sp-lpe-item.cpp index f16b455dd..12473f770 100644 --- a/src/sp-lpe-item.cpp +++ b/src/sp-lpe-item.cpp @@ -30,6 +30,8 @@ #include "xml/repr.h" #include "uri.h" +#include + /* LPEItem base class */ static void sp_lpe_item_class_init(SPLPEItemClass *klass); @@ -46,11 +48,16 @@ static Inkscape::XML::Node *sp_lpe_item_write(SPObject *object, Inkscape::XML::N static void sp_lpe_item_child_added (SPObject * object, Inkscape::XML::Node * child, Inkscape::XML::Node * ref); static void sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child); +static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable); + static void lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem); static void lpeobject_ref_modified(SPObject *href, guint flags, SPLPEItem *lpeitem); static void sp_lpe_item_create_original_path_recursive(SPLPEItem *lpeitem); static void sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem); +typedef std::list HRefList; +static std::string patheffectlist_write_svg(PathEffectList const & list); +static std::string hreflist_write_svg(HRefList const & list); static SPItemClass *parent_class; @@ -77,7 +84,7 @@ sp_lpe_item_get_type() static void sp_lpe_item_class_init(SPLPEItemClass *klass) -{ +{ GObjectClass *gobject_class; SPObjectClass *sp_object_class; @@ -95,14 +102,18 @@ sp_lpe_item_class_init(SPLPEItemClass *klass) sp_object_class->write = sp_lpe_item_write; sp_object_class->child_added = sp_lpe_item_child_added; sp_object_class->remove_child = sp_lpe_item_remove_child; - + klass->update_patheffect = NULL; } static void sp_lpe_item_init(SPLPEItem *lpeitem) { - lpeitem->path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem)); + lpeitem->path_effects_enabled = 1; + + lpeitem->path_effect_list = new PathEffectList(); + lpeitem->current_path_effect = NULL; + new (&lpeitem->lpe_modified_connection) sigc::connection(); } @@ -112,6 +123,8 @@ sp_lpe_item_finalize(GObject *object) if (((GObjectClass *) (parent_class))->finalize) { (* ((GObjectClass *) (parent_class))->finalize)(object); } + + delete SP_LPE_ITEM(object)->path_effect_list; } /** @@ -122,10 +135,8 @@ sp_lpe_item_finalize(GObject *object) static void sp_lpe_item_build(SPObject *object, SPDocument *document, Inkscape::XML::Node *repr) { - SP_LPE_ITEM(object)->path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object))); - sp_object_read_attr(object, "inkscape:path-effect"); - + if (((SPObjectClass *) parent_class)->build) { ((SPObjectClass *) parent_class)->build(object, document, repr); } @@ -140,8 +151,6 @@ sp_lpe_item_release(SPObject *object) SPLPEItem *lpeitem; lpeitem = (SPLPEItem *) object; - lpeitem->path_effect_ref->detach(); - lpeitem->lpe_modified_connection.disconnect(); lpeitem->lpe_modified_connection.~connection(); @@ -159,22 +168,48 @@ sp_lpe_item_set(SPObject *object, unsigned int key, gchar const *value) switch (key) { case SP_ATTR_INKSCAPE_PATH_EFFECT: - if ( value && lpeitem->path_effect_ref->lpeobject_href - && streq(value, lpeitem->path_effect_ref->lpeobject_href) ) { - /* No change, do nothing. */ - } else { - if (value) { - // Now do the attaching, which emits the changed signal. - try { - lpeitem->path_effect_ref->link((gchar*)value); - } catch (Inkscape::BadURIException &e) { - g_warning("%s", e.what()); - lpeitem->path_effect_ref->detach(); + { + lpeitem->current_path_effect = NULL; + + // Disable the path effects while populating the LPE list + sp_lpe_item_enable_path_effects(lpeitem, false); + + // Clear the path effect list + PathEffectList::iterator it = lpeitem->path_effect_list->begin(); + while ( it != lpeitem->path_effect_list->end() ) + { + (*it)->unlink(); + delete *it; + it = lpeitem->path_effect_list->erase(it); + } + + // Parse the contents of "value" to rebuild the path effect reference list + if ( value ) { + std::istringstream iss(value); + std::string href; + while (std::getline(iss, href, ';')) + { + Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref; + path_effect_ref = new Inkscape::LivePathEffect::LPEObjectReference(SP_OBJECT(lpeitem)); + path_effect_ref->changedSignal().connect(sigc::bind(sigc::ptr_fun(lpeobject_ref_changed), SP_LPE_ITEM(object))); + // Now do the attaching, which emits the changed signal. + // Fixme, it should not do this changed signal and updating before all effects are added to the path_effect_list + try { + path_effect_ref->link(href.c_str()); + } catch (Inkscape::BadURIException &e) { + g_warning("BadURIException: %s", e.what()); + path_effect_ref->unlink(); + delete path_effect_ref; + path_effect_ref = NULL; + } + + if (path_effect_ref) { + lpeitem->path_effect_list->push_back(path_effect_ref); + } } - } else { - // Detach, which emits the changed signal. - lpeitem->path_effect_ref->detach(); } + + sp_lpe_item_enable_path_effects(lpeitem, true); } break; default: @@ -202,9 +237,14 @@ sp_lpe_item_update(SPObject *object, SPCtx *ctx, guint flags) static void sp_lpe_item_modified (SPObject *object, unsigned int flags) { - if (((SPObjectClass *) (parent_class))->modified) { - (* ((SPObjectClass *) (parent_class))->modified) (object, flags); - } + + if (SP_IS_GROUP(object) && (flags & SP_OBJECT_MODIFIED_FLAG) && (flags & SP_OBJECT_USER_MODIFIED_FLAG_B)) { + sp_lpe_item_update_patheffect(SP_LPE_ITEM(object), true, true); + } + + if (((SPObjectClass *) (parent_class))->modified) { + (* ((SPObjectClass *) (parent_class))->modified) (object, flags); + } } /** @@ -216,7 +256,8 @@ sp_lpe_item_write(SPObject *object, Inkscape::XML::Node *repr, guint flags) SPLPEItem *lpeitem = (SPLPEItem *) object; if ( sp_lpe_item_has_path_effect(lpeitem) ) { - repr->setAttribute("inkscape:path-effect", lpeitem->path_effect_ref->lpeobject_href); + std::string href = patheffectlist_write_svg(*lpeitem->path_effect_list); + repr->setAttribute("inkscape:path-effect", href.c_str()); } else { repr->setAttribute("inkscape:path-effect", NULL); } @@ -228,51 +269,32 @@ sp_lpe_item_write(SPObject *object, Inkscape::XML::Node *repr, guint flags) return repr; } - -LivePathEffectObject * -sp_lpe_item_get_livepatheffectobject(SPLPEItem *lpeitem) { - if (!lpeitem) return NULL; - - if (sp_lpe_item_has_path_effect(lpeitem)) { - return lpeitem->path_effect_ref->lpeobject; - } else { - return NULL; - } -} - -Inkscape::LivePathEffect::Effect * -sp_lpe_item_get_livepatheffect(SPLPEItem *lpeitem) { - if (!lpeitem) return NULL; - - LivePathEffectObject * lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem); - if (lpeobj) - return lpeobj->lpe; - else - return NULL; -} - void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve) { if (!lpeitem) return; if (!curve) return; - if (sp_lpe_item_has_path_effect(lpeitem)) { - LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem); - if (lpeobj->lpe->isVisible()) { - lpeobj->lpe->doBeforeEffect(lpeitem); - lpeobj->lpe->doEffect(curve); + if (sp_lpe_item_has_path_effect(lpeitem) && sp_lpe_item_path_effects_enabled(lpeitem)) { + for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it) + { + LivePathEffectObject *lpeobj = (*it)->lpeobject; + + if (lpeobj->lpe->isVisible()) { + // Groups have their doBeforeEffect called elsewhere + if (!SP_IS_GROUP(lpeitem)) { + lpeobj->lpe->doBeforeEffect(lpeitem); + } + + lpeobj->lpe->doEffect(curve); + } } } - - SPObject *parent = lpeitem->parent; - if (parent && SP_IS_LPE_ITEM(parent)) - sp_lpe_item_perform_path_effect(SP_LPE_ITEM(parent), curve); } /** * Calls any registered handlers for the update_patheffect action */ void -sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool write) +sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write) { #ifdef SHAPE_VERBOSE g_message("sp_lpe_item_update_patheffect: %p\n", lpeitem); @@ -280,8 +302,23 @@ sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool write) g_return_if_fail (lpeitem != NULL); g_return_if_fail (SP_IS_LPE_ITEM (lpeitem)); - if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (lpeitem))->update_patheffect) { - SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (lpeitem))->update_patheffect (lpeitem, write); + SPLPEItem *top; + + if (wholetree) { + SPObject *prev_parent = lpeitem; + SPObject *parent = prev_parent->parent; + while (parent && SP_IS_LPE_ITEM(parent) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(parent))) { + prev_parent = parent; + parent = prev_parent->parent; + } + top = SP_LPE_ITEM(prev_parent); + } + else { + top = lpeitem; + } + + if (SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect) { + SP_LPE_ITEM_CLASS (G_OBJECT_GET_CLASS (top))->update_patheffect (top, write); } } @@ -308,7 +345,7 @@ lpeobject_ref_changed(SPObject *old_ref, SPObject *ref, SPLPEItem *lpeitem) static void lpeobject_ref_modified(SPObject */*href*/, guint /*flags*/, SPLPEItem *lpeitem) { - sp_lpe_item_update_patheffect (lpeitem, true); + sp_lpe_item_update_patheffect (lpeitem, true, true); } static void @@ -345,63 +382,137 @@ sp_lpe_item_cleanup_original_path_recursive(SPLPEItem *lpeitem) } else if (SP_IS_PATH(lpeitem)) { Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem); - if (!sp_lpe_item_has_path_effect_recursive(lpeitem) + if (!sp_lpe_item_has_path_effect_recursive(lpeitem) && repr->attribute("inkscape:original-d")) { repr->setAttribute("d", repr->attribute("inkscape:original-d")); repr->setAttribute("inkscape:original-d", NULL); } else { - sp_lpe_item_update_patheffect(lpeitem, true); + sp_lpe_item_update_patheffect(lpeitem, true, true); } } } -void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset) +void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset) { - if (!value) { - sp_lpe_item_remove_path_effect(lpeitem, false); - } else { - SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", value); - - // Ask the path effect to reset itself if it doesn't have parameters yet - if (lpeitem->path_effect_ref) { - LivePathEffectObject *lpeobj = lpeitem->path_effect_ref->lpeobject; - if (lpeobj && lpeobj->lpe) { - if(reset) { - // has to be called when all the subitems have their lpes applied - lpeobj->lpe->resetDefaults(lpeitem); - } - // perform this once when the effect is applied - lpeobj->lpe->doOnApply(SP_LPE_ITEM(lpeitem)); - } + if (value) { + // Apply the path effects here because in the casse of a group, lpe->resetDefaults + // needs that all the subitems have their effects applied + sp_lpe_item_update_patheffect(lpeitem, false, true); + + // Disable the path effects while preparing the new lpe + sp_lpe_item_enable_path_effects(lpeitem, false); + + // Add the new reference to the list of LPE references + HRefList hreflist; + for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it) + { + hreflist.push_back( std::string((*it)->lpeobject_href) ); } - + hreflist.push_back( std::string(value) ); + std::string hrefs = hreflist_write_svg(hreflist); + + SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", hrefs.c_str()); + // make sure there is an original-d for paths!!! sp_lpe_item_create_original_path_recursive(lpeitem); + + LivePathEffectObject *lpeobj = lpeitem->path_effect_list->back()->lpeobject; + if (lpeobj && lpeobj->lpe) { + // Ask the path effect to reset itself if it doesn't have parameters yet + if (reset) { + // has to be called when all the subitems have their lpes applied + lpeobj->lpe->resetDefaults(lpeitem); + } + } + + //Enable the path effects now that everything is ready to apply the new path effect + sp_lpe_item_enable_path_effects(lpeitem, true); + + // Apply the path effect + sp_lpe_item_update_patheffect(lpeitem, true, true); } } -void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj) +void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj) { const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id"); gchar *hrefstr = g_strdup_printf("#%s", repr_id); - sp_lpe_item_set_path_effect(lpeitem, hrefstr, false); + sp_lpe_item_add_path_effect(lpeitem, hrefstr, false); g_free(hrefstr); } -void sp_lpe_item_remove_path_effect(SPLPEItem *lpeitem, bool keep_paths) +void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths) { - Inkscape::XML::Node *repr = SP_OBJECT_REPR(lpeitem); - repr->setAttribute("inkscape:path-effect", NULL); - + Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem); + if (!lperef) + return; + + PathEffectList new_list = *lpeitem->path_effect_list; + new_list.remove(lperef); //current lpe ref is always our 'own' pointer from the path_effect_list + std::string r = patheffectlist_write_svg(new_list); + + SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str()); + if (!keep_paths) { sp_lpe_item_cleanup_original_path_recursive(lpeitem); } } +void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths) +{ + SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", NULL); + + if (!keep_paths) { + sp_lpe_item_cleanup_original_path_recursive(lpeitem); + } +} + +void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem) +{ + Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem); + if (!lperef) + return; + + PathEffectList new_list = *lpeitem->path_effect_list; + PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef ); + if (cur_it != new_list.end()) { + PathEffectList::iterator down_it = cur_it; + down_it++; + if (down_it != new_list.end()) { // perhaps current effect is already last effect + std::iter_swap(cur_it, down_it); + } + } + std::string r = patheffectlist_write_svg(new_list); + SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str()); + + sp_lpe_item_cleanup_original_path_recursive(lpeitem); +} + +void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem) +{ + Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem); + if (!lperef) + return; + + PathEffectList new_list = *lpeitem->path_effect_list; + PathEffectList::iterator cur_it = find( new_list.begin(), new_list.end(), lperef ); + if (cur_it != new_list.end() && cur_it != new_list.begin()) { + PathEffectList::iterator up_it = cur_it; + up_it--; + std::iter_swap(cur_it, up_it); + } + std::string r = patheffectlist_write_svg(new_list); + + SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str()); + + sp_lpe_item_cleanup_original_path_recursive(lpeitem); +} + + bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem) { - return lpeitem->path_effect_ref && lpeitem->path_effect_ref->lpeobject; + return !lpeitem->path_effect_list->empty(); } bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem) @@ -417,9 +528,9 @@ bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem) void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt) { - LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem); - if (lpeobj && lpeobj->lpe) { - lpeobj->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt); + Inkscape::LivePathEffect::LPEObjectReference *lperef = sp_lpe_item_get_current_lpereference(lpeitem); + if (lperef && lperef->lpeobject && lperef->lpeobject->lpe) { + lperef->lpeobject->lpe->editNextParamOncanvas(SP_ITEM(lpeitem), dt); } } @@ -428,7 +539,7 @@ sp_lpe_item_child_added (SPObject *object, Inkscape::XML::Node *child, Inkscape: { if (((SPObjectClass *) (parent_class))->child_added) (* ((SPObjectClass *) (parent_class))->child_added) (object, child, ref); - + if (SP_IS_LPE_ITEM(object) && sp_lpe_item_has_path_effect_recursive(SP_LPE_ITEM(object))) { SPObject *ochild = sp_object_get_child_by_repr(object, child); if ( ochild && SP_IS_LPE_ITEM(ochild) ) { @@ -451,6 +562,114 @@ sp_lpe_item_remove_child (SPObject * object, Inkscape::XML::Node * child) (* ((SPObjectClass *) (parent_class))->remove_child) (object, child); } +static std::string patheffectlist_write_svg(PathEffectList const & list) +{ + HRefList hreflist; + for (PathEffectList::const_iterator it = list.begin(); it != list.end(); ++it) + { + hreflist.push_back( std::string((*it)->lpeobject_href) ); + } + return hreflist_write_svg(hreflist); +} + +/** + * THE function that should be used to generate any patheffectlist string. + * one of the methods to change the effect list: + * - create temporary href list + * - populate the templist with the effects from the old list that you want to have and their order + * - call this function with temp list as param + */ +static std::string hreflist_write_svg(HRefList const & list) +{ + std::string r; + bool semicolon_first = false; + for (HRefList::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (semicolon_first) { + r += ';'; + } + semicolon_first = true; + + r += (*it); + } + return r; +} + +// Return a copy of the effect list +PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem) +{ + return *lpeitem->path_effect_list; +} + +Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem) +{ + if (!lpeitem->current_path_effect && !lpeitem->path_effect_list->empty()) + sp_lpe_item_set_current_path_effect(lpeitem, lpeitem->path_effect_list->back()); + + return lpeitem->current_path_effect; +} + +Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem) +{ + Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem); + + if (lperef && lperef->lpeobject) + return lperef->lpeobject->lpe; + else + return NULL; +} + +bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef) +{ + for (PathEffectList::iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); it++) { + if ((*it)->lpeobject_repr == lperef->lpeobject_repr) { + lpeobject_ref_changed(NULL, (*it)->lpeobject, SP_LPE_ITEM(lpeitem)); // FIXME: explain why this is here? + lpeitem->current_path_effect = (*it); // current_path_effect should always be a pointer from the path_effect_list ! + return true; + } + } + + return false; +} + +void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj, + LivePathEffectObject * new_lpeobj) +{ + HRefList hreflist; + for (PathEffectList::const_iterator it = lpeitem->path_effect_list->begin(); it != lpeitem->path_effect_list->end(); ++it) + { + if ((*it)->lpeobject == old_lpeobj) { + const gchar * repr_id = SP_OBJECT_REPR(new_lpeobj)->attribute("id"); + gchar *hrefstr = g_strdup_printf("#%s", repr_id); + hreflist.push_back( std::string(hrefstr) ); + g_free(hrefstr); + } + else { + hreflist.push_back( std::string((*it)->lpeobject_href) ); + } + } + std::string r = hreflist_write_svg(hreflist); + SP_OBJECT_REPR(lpeitem)->setAttribute("inkscape:path-effect", r.c_str()); +} + +// Enable or disable the path effects of the item. +// The counter allows nested calls +static void sp_lpe_item_enable_path_effects(SPLPEItem *lpeitem, bool enable) +{ + if (enable) { + lpeitem->path_effects_enabled++; + } + else { + lpeitem->path_effects_enabled--; + } +} + +// Are the path effects enabled on this item ? +bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem) +{ + return lpeitem->path_effects_enabled > 0; +} + /* Local Variables: mode:c++ diff --git a/src/sp-lpe-item.h b/src/sp-lpe-item.h index 7d85bb2be..3aba7b8a7 100644 --- a/src/sp-lpe-item.h +++ b/src/sp-lpe-item.h @@ -17,6 +17,8 @@ #include "sp-item.h" #include "display/display-forward.h" +#include + #define SP_TYPE_LPE_ITEM (sp_lpe_item_get_type()) #define SP_LPE_ITEM(o) (G_TYPE_CHECK_INSTANCE_CAST((o), SP_TYPE_LPE_ITEM, SPLPEItem)) #define SP_LPE_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SP_TYPE_LPE_ITEM, SPLPEItemClass)) @@ -31,8 +33,14 @@ namespace LivePathEffect{ }; }; +typedef std::list PathEffectList; + struct SPLPEItem : public SPItem { - Inkscape::LivePathEffect::LPEObjectReference *path_effect_ref; + int path_effects_enabled; + + PathEffectList* path_effect_list; + Inkscape::LivePathEffect::LPEObjectReference* current_path_effect; + sigc::connection lpe_modified_connection; }; @@ -44,16 +52,24 @@ struct SPLPEItemClass { GType sp_lpe_item_get_type(); -LivePathEffectObject * sp_lpe_item_get_livepatheffectobject(SPLPEItem *lpeitem); -Inkscape::LivePathEffect::Effect * sp_lpe_item_get_livepatheffect(SPLPEItem *lpeitem); -void sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool write); +void sp_lpe_item_update_patheffect (SPLPEItem *lpeitem, bool wholetree, bool write); void sp_lpe_item_perform_path_effect(SPLPEItem *lpeitem, SPCurve *curve); -void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset); -void sp_lpe_item_set_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj); -void sp_lpe_item_remove_path_effect(SPLPEItem *lpeitem, bool keep_paths); +void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, gchar *value, bool reset); +void sp_lpe_item_add_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * new_lpeobj); +void sp_lpe_item_replace_path_effect(SPLPEItem *lpeitem, LivePathEffectObject * old_lpeobj, + LivePathEffectObject * new_lpeobj); +void sp_lpe_item_remove_all_path_effects(SPLPEItem *lpeitem, bool keep_paths); +void sp_lpe_item_remove_current_path_effect(SPLPEItem *lpeitem, bool keep_paths); +void sp_lpe_item_down_current_path_effect(SPLPEItem *lpeitem); +void sp_lpe_item_up_current_path_effect(SPLPEItem *lpeitem); bool sp_lpe_item_has_path_effect(SPLPEItem *lpeitem); bool sp_lpe_item_has_path_effect_recursive(SPLPEItem *lpeitem); void sp_lpe_item_edit_next_param_oncanvas(SPLPEItem *lpeitem, SPDesktop *dt); +PathEffectList sp_lpe_item_get_effect_list(SPLPEItem *lpeitem); +Inkscape::LivePathEffect::LPEObjectReference* sp_lpe_item_get_current_lpereference(SPLPEItem *lpeitem); +Inkscape::LivePathEffect::Effect* sp_lpe_item_get_current_lpe(SPLPEItem *lpeitem); +bool sp_lpe_item_set_current_path_effect(SPLPEItem *lpeitem, Inkscape::LivePathEffect::LPEObjectReference* lperef); +bool sp_lpe_item_path_effects_enabled(SPLPEItem *lpeitem); #endif /* !SP_LPE_ITEM_H_SEEN */ diff --git a/src/sp-path.cpp b/src/sp-path.cpp index 4654b1b13..b2e26efa0 100644 --- a/src/sp-path.cpp +++ b/src/sp-path.cpp @@ -464,7 +464,7 @@ sp_path_set_original_curve (SPPath *path, SPCurve *curve, unsigned int owner, bo path->original_curve = curve->copy(); } } - sp_path_update_patheffect(path, write); + sp_lpe_item_update_patheffect(path, true, write); SP_OBJECT(path)->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG); } diff --git a/src/ui/clipboard.cpp b/src/ui/clipboard.cpp index fff6e3b09..2d38f5ccd 100644 --- a/src/ui/clipboard.cpp +++ b/src/ui/clipboard.cpp @@ -61,6 +61,7 @@ #include "sp-textpath.h" #include "sp-rect.h" #include "live_effects/lpeobject.h" +#include "live_effects/lpeobject-reference.h" #include "live_effects/parameter/path.h" #include "svg/svg.h" // for sp_svg_transform_write, used in _copySelection #include "svg/css-ostringstream.h" // used in _parseColor @@ -560,10 +561,14 @@ void ClipboardManagerImpl::_copyUsedDefs(SPItem *item) } } // For lpe items, copy liveeffect if applicable + // TODO: copy the whole effect stack. now it only copies current selected effect if (SP_IS_LPE_ITEM(item)) { SPLPEItem *lpeitem = SP_LPE_ITEM (item); if (sp_lpe_item_has_path_effect(lpeitem)) { - _copyNode(SP_OBJECT_REPR(SP_OBJECT(sp_lpe_item_get_livepatheffectobject(lpeitem))), _doc, _defs); + Inkscape::LivePathEffect::LPEObjectReference* lperef = sp_lpe_item_get_current_lpereference(lpeitem); + if (lperef && lperef->lpeobject) { + _copyNode(SP_OBJECT_REPR(SP_OBJECT(lperef->lpeobject)), _doc, _defs); + } } } // For 3D boxes, copy perspectives @@ -900,7 +905,7 @@ void ClipboardManagerImpl::_applyPathEffect(SPItem *item, gchar const *effect) if (!obj) return; // if the effect is not used by anyone, we might as well take it LivePathEffectObject *lpeobj = LIVEPATHEFFECT(obj)->fork_private_if_necessary(1); - sp_lpe_item_set_path_effect(lpeitem, lpeobj); + sp_lpe_item_add_path_effect(lpeitem, lpeobj); } } diff --git a/src/ui/dialog/livepatheffect-editor.cpp b/src/ui/dialog/livepatheffect-editor.cpp index 43cdb726c..c92d015f5 100644 --- a/src/ui/dialog/livepatheffect-editor.cpp +++ b/src/ui/dialog/livepatheffect-editor.cpp @@ -23,6 +23,7 @@ #include "sp-item-group.h" #include "sp-path.h" #include "sp-rect.h" +#include "sp-lpe-item.h" #include "path-chemistry.h" #include "live_effects/effect.h" #include "live_effects/lpeobject.h" @@ -34,6 +35,10 @@ #include "document-private.h" #include "xml/node.h" #include "xml/document.h" +#include +#include + +#include "live_effects/lpeobject-reference.h" namespace Inkscape { class Application; @@ -59,38 +64,88 @@ static void lpeeditor_selection_changed (Inkscape::Selection * selection, gpoint LivePathEffectEditor::LivePathEffectEditor() : UI::Widget::Panel("", "dialogs.livepatheffect", SP_VERB_DIALOG_LIVE_PATH_EFFECT), combo_effecttype(Inkscape::LivePathEffect::LPETypeConverter), - button_apply(_("_Apply"), _("Apply chosen effect to selection")), - button_remove(_("_Remove"), _("Remove effect from selection")), effectwidget(NULL), explain_label("", Gtk::ALIGN_CENTER), effectapplication_frame(_("Apply new effect")), effectcontrol_frame(_("Current effect")), - current_desktop(NULL) + effectlist_frame(_("Effect list")), + button_up(Gtk::Stock::GO_UP), + button_down(Gtk::Stock::GO_DOWN), + button_apply(Gtk::Stock::ADD), + button_remove(Gtk::Stock::REMOVE), + current_desktop(NULL), + current_lpeitem(NULL) { Gtk::Box *contents = _getContents(); contents->set_spacing(4); + //Add the TreeView, inside a ScrolledWindow, with the button underneath: + scrolled_window.add(effectlist_view); + //Only show the scrollbars when they are necessary: + scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); + effectapplication_hbox.set_spacing(4); effectcontrol_vbox.set_spacing(4); + effectlist_vbox.set_spacing(4); effectapplication_hbox.pack_start(combo_effecttype, true, true); effectapplication_hbox.pack_start(button_apply, true, true); effectapplication_frame.add(effectapplication_hbox); + effectlist_vbox.pack_start(scrolled_window, Gtk::PACK_EXPAND_WIDGET); + effectlist_vbox.pack_end(toolbar, Gtk::PACK_SHRINK); + // effectlist_vbox.pack_end(button_hbox, Gtk::PACK_SHRINK); + effectlist_frame.add(effectlist_vbox); + effectcontrol_vbox.pack_start(explain_label, true, true); - effectcontrol_vbox.pack_end(button_remove, true, true); effectcontrol_frame.add(effectcontrol_vbox); + // button_hbox.pack_start(button_up, true, true); + // button_hbox.pack_start(button_down, true, true); + // button_hbox.pack_end(button_remove, true, true); + toolbar.set_toolbar_style(Gtk::TOOLBAR_ICONS); + // Add toolbar items to toolbar + toolbar.append(button_up); + toolbar.append(button_down); + toolbar.append(button_remove); + + + // Add toolbar + //add_toolbar(toolbar); + toolbar.show_all(); //Show the toolbar and all its child widgets. + + + //Create the Tree model: + effectlist_store = Gtk::ListStore::create(columns); + effectlist_view.set_model(effectlist_store); + + effectlist_view.set_rules_hint(); + effectlist_view.set_headers_clickable(true); + effectlist_view.set_headers_visible(true); + + // Handle tree selections + effectlist_selection = effectlist_view.get_selection(); + effectlist_selection->signal_changed().connect( sigc::mem_fun(*this, &LivePathEffectEditor::on_effect_selection_changed) ); + + effectlist_view.set_headers_visible(false); + //Add the TreeView's view columns: + effectlist_view.append_column("Effect", columns.col_name); + + contents->pack_start(effectapplication_frame, false, false); + contents->pack_start(effectlist_frame, true, true); contents->pack_start(effectcontrol_frame, false, false); // connect callback functions to buttons button_apply.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onApply)); button_remove.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onRemove)); + button_up.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onUp)); + button_down.signal_clicked().connect(sigc::mem_fun(*this, &LivePathEffectEditor::onDown)); + show_all_children(); - button_remove.hide(); + //button_remove.hide(); } LivePathEffectEditor::~LivePathEffectEditor() @@ -136,7 +191,7 @@ LivePathEffectEditor::showText(Glib::ustring const &str) } explain_label.set_label(str); - button_remove.hide(); + //button_remove.hide(); // fixme: do resizing of dialog ? } @@ -147,21 +202,33 @@ LivePathEffectEditor::set_sensitize_all(bool sensitive) combo_effecttype.set_sensitive(sensitive); button_apply.set_sensitive(sensitive); button_remove.set_sensitive(sensitive); + effectlist_view.set_sensitive(sensitive); + button_up.set_sensitive(sensitive); + button_down.set_sensitive(sensitive); } + void LivePathEffectEditor::onSelectionChanged(Inkscape::Selection *sel) { + effectlist_store->clear(); + current_lpeitem = NULL; + if ( sel && !sel->isEmpty() ) { SPItem *item = sel->singleItem(); if ( item ) { if ( SP_IS_LPE_ITEM(item) ) { SPLPEItem *lpeitem = SP_LPE_ITEM(item); - LivePathEffectObject *lpeobj = sp_lpe_item_get_livepatheffectobject(lpeitem); + + effect_list_update(lpeitem); + + current_lpeitem = lpeitem; + set_sensitize_all(true); - if (lpeobj) { - if (lpeobj->lpe) { - showParams(lpeobj->lpe); + if ( sp_lpe_item_has_path_effect(lpeitem) ) { + Inkscape::LivePathEffect::Effect *lpe = sp_lpe_item_get_current_lpe(lpeitem); + if (lpe) { + showParams(lpe); } else { showText(_("Unknown effect is applied")); } @@ -185,6 +252,22 @@ LivePathEffectEditor::onSelectionChanged(Inkscape::Selection *sel) } } +void +LivePathEffectEditor::effect_list_update(SPLPEItem *lpeitem) +{ + effectlist_store->clear(); + + PathEffectList effectlist = sp_lpe_item_get_effect_list(lpeitem); + PathEffectList::iterator it; + for( it = effectlist.begin() ; it!=effectlist.end(); it++ ) + { + Gtk::TreeModel::Row row = *(effectlist_store->append()); + row[columns.col_name] = (*it)->lpeobject->lpe->getName(); + row[columns.lperef] = *it; + } +} + + void LivePathEffectEditor::setDesktop(SPDesktop *desktop) { @@ -247,7 +330,7 @@ LivePathEffectEditor::onApply() Inkscape::GC::release(repr); gchar *href = g_strdup_printf("#%s", repr_id); - sp_lpe_item_set_path_effect(SP_LPE_ITEM(item), href, true); + sp_lpe_item_add_path_effect(SP_LPE_ITEM(item), href, true); g_free(href); sp_document_done(doc, SP_VERB_DIALOG_LIVE_PATH_EFFECT, @@ -265,15 +348,67 @@ LivePathEffectEditor::onRemove() if ( sel && !sel->isEmpty() ) { SPItem *item = sel->singleItem(); if ( item && SP_IS_LPE_ITEM(item) ) { - sp_lpe_item_remove_path_effect(SP_LPE_ITEM(item), false); - showText(_("No effect applied")); - button_remove.set_sensitive(false); + sp_lpe_item_remove_current_path_effect(SP_LPE_ITEM(item), false); + + sp_document_done ( sp_desktop_document (current_desktop), SP_VERB_DIALOG_LIVE_PATH_EFFECT, + _("Remove path effect") ); + + effect_list_update(SP_LPE_ITEM(item)); + } + } +} + +void LivePathEffectEditor::onUp() +{ + Inkscape::Selection *sel = _getSelection(); + if ( sel && !sel->isEmpty() ) { + SPItem *item = sel->singleItem(); + if ( item && SP_IS_LPE_ITEM(item) ) { + + sp_lpe_item_up_current_path_effect(SP_LPE_ITEM(item)); + + sp_document_done ( sp_desktop_document (current_desktop), SP_VERB_DIALOG_LIVE_PATH_EFFECT, + _("Remove path effect") ); + + effect_list_update(SP_LPE_ITEM(item)); + } + } + +} + +void LivePathEffectEditor::onDown() +{ + Inkscape::Selection *sel = _getSelection(); + if ( sel && !sel->isEmpty() ) { + SPItem *item = sel->singleItem(); + if ( item && SP_IS_LPE_ITEM(item) ) { + + sp_lpe_item_down_current_path_effect(SP_LPE_ITEM(item)); + sp_document_done ( sp_desktop_document (current_desktop), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Remove path effect") ); + + effect_list_update(SP_LPE_ITEM(item)); } } } +void LivePathEffectEditor::on_effect_selection_changed() +{ + Glib::RefPtr sel = effectlist_view.get_selection(); + if (sel->count_selected_rows () == 0) + return; + + Gtk::TreeModel::iterator it = sel->get_selected(); + LivePathEffect::LPEObjectReference * lperef = (*it)[columns.lperef]; + + if (lperef && current_lpeitem) { + sp_lpe_item_set_current_path_effect(current_lpeitem, lperef); + showParams(lperef->lpeobject->lpe); + } +} + + } // namespace Dialog } // namespace UI } // namespace Inkscape diff --git a/src/ui/dialog/livepatheffect-editor.h b/src/ui/dialog/livepatheffect-editor.h index dc96fc313..e2c7da322 100644 --- a/src/ui/dialog/livepatheffect-editor.h +++ b/src/ui/dialog/livepatheffect-editor.h @@ -21,6 +21,13 @@ #include #include "ui/widget/combo-enums.h" #include "live_effects/effect.h" +#include "live_effects/lpeobject-reference.h" +#include +#include +#include +#include +#include + class SPDesktop; @@ -37,6 +44,7 @@ public: static LivePathEffectEditor &getInstance() { return *new LivePathEffectEditor(); } void onSelectionChanged(Inkscape::Selection *sel); + virtual void on_effect_selection_changed(); void setDesktop(SPDesktop *desktop); private: @@ -47,22 +55,60 @@ private: void showParams(LivePathEffect::Effect* effect); void showText(Glib::ustring const &str); + // void add_entry(const char* name ); + void effect_list_update(SPLPEItem *lpeitem); + // callback methods for buttons on grids page. void onApply(); void onRemove(); + void onUp(); + void onDown(); + + class ModelColumns : public Gtk::TreeModel::ColumnRecord + { + public: + ModelColumns() + { + add(col_name); + add(lperef); + } + + Gtk::TreeModelColumn col_name; + Gtk::TreeModelColumn lperef; + }; + Inkscape::UI::Widget::ComboBoxEnum combo_effecttype; - Inkscape::UI::Widget::Button button_apply; - Inkscape::UI::Widget::Button button_remove; + Gtk::Widget * effectwidget; Gtk::Label explain_label; Gtk::Frame effectapplication_frame; Gtk::Frame effectcontrol_frame; + Gtk::Frame effectlist_frame; Gtk::HBox effectapplication_hbox; Gtk::VBox effectcontrol_vbox; + Gtk::VBox effectlist_vbox; Gtk::Tooltips tooltips; + ModelColumns columns; + Gtk::ScrolledWindow scrolled_window; + Gtk::TreeView effectlist_view; + Glib::RefPtr effectlist_store; + Glib::RefPtr effectlist_selection; + + Gtk::Toolbar toolbar; + Gtk::ToolButton button_up; + Gtk::ToolButton button_down; + Gtk::Button button_apply; + Gtk::ToolButton button_remove; + /*Gtk::HButtonBox button_hbox; + Gtk::Button button_up; + Gtk::Button button_down; + Gtk::Button button_apply; + Gtk::Button button_remove;*/ SPDesktop * current_desktop; + + SPLPEItem * current_lpeitem; LivePathEffectEditor(LivePathEffectEditor const &d); LivePathEffectEditor& operator=(LivePathEffectEditor const &d);