summaryrefslogtreecommitdiffstats
path: root/src/live_effects
diff options
context:
space:
mode:
authorLiam P. White <inkscapebronyat-signgmaildotcom>2014-03-02 15:29:06 +0000
committerLiam P. White <inkscapebronyat-signgmaildotcom>2014-03-02 15:29:06 +0000
commit1d854ff519b9c0867bfa4ecaf2f6329ca256f06a (patch)
tree437352a17301fbdd1787213b7dec3788b6e8eefa /src/live_effects
parentrename variable names to prevent hiding of names in outer scopes (diff)
downloadinkscape-1d854ff519b9c0867bfa4ecaf2f6329ca256f06a.tar.gz
inkscape-1d854ff519b9c0867bfa4ecaf2f6329ca256f06a.zip
Experimental merge of Ponyscape features into trunk (will not compile)
(bzr r13090.1.1)
Diffstat (limited to 'src/live_effects')
-rw-r--r--src/live_effects/CMakeLists.txt13
-rw-r--r--src/live_effects/Makefile_insert14
-rw-r--r--src/live_effects/effect-enum.h6
-rw-r--r--src/live_effects/effect.cpp39
-rw-r--r--src/live_effects/lpe-attach-path.cpp203
-rw-r--r--src/live_effects/lpe-attach-path.h54
-rw-r--r--src/live_effects/lpe-bounding-box.cpp67
-rw-r--r--src/live_effects/lpe-bounding-box.h37
-rw-r--r--src/live_effects/lpe-ellipse_5pts.cpp214
-rw-r--r--src/live_effects/lpe-ellipse_5pts.h50
-rw-r--r--src/live_effects/lpe-fill-between-many.cpp77
-rw-r--r--src/live_effects/lpe-fill-between-many.h36
-rw-r--r--src/live_effects/lpe-fill-between-strokes.cpp116
-rw-r--r--src/live_effects/lpe-fill-between-strokes.h38
-rw-r--r--src/live_effects/lpe-jointype.cpp203
-rwxr-xr-xsrc/live_effects/lpe-jointype.h43
-rw-r--r--src/live_effects/lpe-powerstroke-interpolators.h42
-rw-r--r--src/live_effects/lpe-powerstroke.cpp97
-rw-r--r--src/live_effects/lpe-powerstroke.h2
-rw-r--r--src/live_effects/parameter/Makefile_insert4
-rw-r--r--src/live_effects/parameter/originalpatharray.cpp486
-rw-r--r--src/live_effects/parameter/originalpatharray.h123
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.cpp39
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.h23
-rw-r--r--src/live_effects/parameter/transformedpoint.cpp182
-rw-r--r--src/live_effects/parameter/transformedpoint.h87
-rwxr-xr-xsrc/live_effects/pathoutlineprovider.h766
27 files changed, 3037 insertions, 24 deletions
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index a5f50a69d..8ad1ccf9b 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -2,8 +2,10 @@
set(live_effects_SRC
effect.cpp
lpe-angle_bisector.cpp
+ lpe-attach-path.cpp
lpe-bendpath.cpp
lpe-boolops.cpp
+ lpe-bounding-box.cpp
lpe-circle_3pts.cpp
lpe-circle_with_radius.cpp
lpe-clone-original.cpp
@@ -11,8 +13,11 @@ set(live_effects_SRC
lpe-copy_rotate.cpp
lpe-curvestitch.cpp
lpe-dynastroke.cpp
+ lpe-ellipse-5pts.cpp
lpe-envelope.cpp
lpe-extrude.cpp
+ lpe-fill-between-many.cpp
+ lpe-fill-between-strokes.cpp
lpe-gears.cpp
lpe-interpolate.cpp
lpe-knot.cpp
@@ -47,11 +52,13 @@ set(live_effects_SRC
parameter/parameter.cpp
parameter/path.cpp
parameter/originalpath.cpp
+ parameter/originalpatharray.cpp
parameter/path-reference.cpp
parameter/point.cpp
parameter/powerstrokepointarray.cpp
parameter/random.cpp
parameter/text.cpp
+ paramter/transformedpoint.cpp
parameter/unit.cpp
parameter/vector.cpp
@@ -61,8 +68,10 @@ set(live_effects_SRC
effect-enum.h
effect.h
lpe-angle_bisector.h
+ lpe-attach-path.h
lpe-bendpath.h
lpe-boolops.h
+ lpe-bounding-box.h
lpe-circle_3pts.h
lpe-circle_with_radius.h
lpe-clone-original.h
@@ -70,8 +79,11 @@ set(live_effects_SRC
lpe-copy_rotate.h
lpe-curvestitch.h
lpe-dynastroke.h
+ lpe-ellipse-5pts.h
lpe-envelope.h
lpe-extrude.h
+ lpe-fill-between-many.h
+ lpe-fill-between-strokes.h
lpe-gears.h
lpe-interpolate.h
lpe-knot.h
@@ -109,6 +121,7 @@ set(live_effects_SRC
parameter/path-reference.h
parameter/path.h
parameter/originalpath.h
+ parameter/originalpatharray.h
parameter/point.h
parameter/powerstrokepointarray.h
parameter/random.h
diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert
index 9c3c171f2..76d7b418f 100644
--- a/src/live_effects/Makefile_insert
+++ b/src/live_effects/Makefile_insert
@@ -82,4 +82,16 @@ ink_common_sources += \
live_effects/lpe-path_length.cpp \
live_effects/lpe-path_length.h \
live_effects/lpe-line_segment.cpp \
- live_effects/lpe-line_segment.h
+ live_effects/lpe-line_segment.h \
+ live_effects/lpe-bounding-box.cpp \
+ live_effects/lpe-bounding-box.h \
+ live_effects/lpe-attach-path.cpp \
+ live_effects/lpe-attach-path.h \
+ live_effects/lpe-fill-between-strokes.cpp \
+ live_effects/lpe-fill-between-stroke.h \
+ live_effects/lpe-fill-between-many.cpp \
+ live_effects/lpe-fill-between-many.h \
+ live_effects/lpe-ellipse_5pts.cpp \
+ live_effects/lpe-ellipse_5pts.h \
+ live_effects/lpe-jointype.cpp \
+ live_effects/lpe-jointype.h
diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h
index 43af33b53..46bc56a81 100644
--- a/src/live_effects/effect-enum.h
+++ b/src/live_effects/effect-enum.h
@@ -50,6 +50,12 @@ enum EffectType {
EXTRUDE,
POWERSTROKE,
CLONE_ORIGINAL,
+ ATTACH_PATH,
+ FILL_BETWEEN_STROKES,
+ FILL_BETWEEN_MANY,
+ ELLIPSE_5PTS,
+ BOUNDING_BOX,
+ JOIN_TYPE,
INVALID_LPE // This must be last (I made it such that it is not needed anymore I think..., Don't trust on it being last. - johan)
};
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 12990ee24..17b229352 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -5,7 +5,7 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-//#define LPE_ENABLE_TEST_EFFECTS
+#define LPE_ENABLE_TEST_EFFECTS
#ifdef HAVE_CONFIG_H
# include "config.h"
@@ -47,6 +47,12 @@
#include "live_effects/lpe-extrude.h"
#include "live_effects/lpe-powerstroke.h"
#include "live_effects/lpe-clone-original.h"
+#include "live_effects/lpe-attach-path.h"
+#include "live_effects/lpe-fill-between-strokes.h"
+#include "live_effects/lpe-fill-between-many.h"
+#include "live_effects/lpe-ellipse_5pts.h"
+#include "live_effects/lpe-bounding-box.h"
+#include "live_effects/lpe-jointype.h"
#include "xml/node-event-vector.h"
#include "sp-object.h"
@@ -103,9 +109,10 @@ const Util::EnumData<EffectType> LPETypeData[] = {
{RECURSIVE_SKELETON, N_("Recursive skeleton"), "recursive_skeleton"},
{TANGENT_TO_CURVE, N_("Tangent to curve"), "tangent_to_curve"},
{TEXT_LABEL, N_("Text label"), "text_label"},
+ {JOIN_TYPE, N_("Join type"), "join_type"},
#endif
/* 0.46 */
- {BEND_PATH, N_("Bend"), "bend_path"},
+ {BEND_PATH, N_("Bend"), "bend_path"},
{GEARS, N_("Gears"), "gears"},
{PATTERN_ALONG_PATH, N_("Pattern Along Path"), "skeletal"}, // for historic reasons, this effect is called skeletal(strokes) in Inkscape:SVG
{CURVE_STITCH, N_("Stitch Sub-Paths"), "curvestitching"},
@@ -120,8 +127,14 @@ const Util::EnumData<EffectType> LPETypeData[] = {
{SKETCH, N_("Sketch"), "sketch"},
{RULER, N_("Ruler"), "ruler"},
/* 0.49 */
- {POWERSTROKE, N_("Power stroke"), "powerstroke"},
- {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"},
+ {POWERSTROKE, N_("Power stroke"), "powerstroke"},
+ {CLONE_ORIGINAL, N_("Clone original path"), "clone_original"},
+/* Ponyscape */
+ {ATTACH_PATH, N_("Attach path"), "attach_path"},
+ {FILL_BETWEEN_STROKES, N_("Fill between strokes"), "fill_between_strokes"},
+ {FILL_BETWEEN_MANY, N_("Fill between many"), "fill_between_many"},
+ {ELLIPSE_5PTS, N_("Ellipse by 5 points"), "ellipse_5pts"},
+ {BOUNDING_BOX, N_("Bounding Box"), "bounding_box"},
};
const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
@@ -245,6 +258,24 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
case CLONE_ORIGINAL:
neweffect = static_cast<Effect*> ( new LPECloneOriginal(lpeobj) );
break;
+ case ATTACH_PATH:
+ neweffect = static_cast<Effect*> ( new LPEAttachPath(lpeobj) );
+ break;
+ case FILL_BETWEEN_STROKES:
+ neweffect = static_cast<Effect*> ( new LPEFillBetweenStrokes(lpeobj) );
+ break;
+ case FILL_BETWEEN_MANY:
+ neweffect = static_cast<Effect*> ( new LPEFillBetweenMany(lpeobj) );
+ break;
+ case ELLIPSE_5PTS:
+ neweffect = static_cast<Effect*> ( new LPEEllipse5Pts(lpeobj) );
+ break;
+ case BOUNDING_BOX:
+ neweffect = static_cast<Effect*> ( new LPEBoundingBox(lpeobj) );
+ break;
+ case JOIN_TYPE:
+ neweffect = static_cast<Effect*> ( new LPEJoinType(lpeobj) );
+ break;
default:
g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
neweffect = NULL;
diff --git a/src/live_effects/lpe-attach-path.cpp b/src/live_effects/lpe-attach-path.cpp
new file mode 100644
index 000000000..b3d5ed9b7
--- /dev/null
+++ b/src/live_effects/lpe-attach-path.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) Johan Engelen 2012 <j.b.c.engelen@alumnus.utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glibmm/i18n.h>
+#include <math.h>
+
+#include "live_effects/lpe-attach-path.h"
+
+#include "display/curve.h"
+#include "sp-item.h"
+#include "2geom/path.h"
+#include "sp-shape.h"
+#include "sp-text.h"
+#include "2geom/bezier-curve.h"
+#include "2geom/path-sink.h"
+#include "parameter/parameter.h"
+#include "live_effects/parameter/point.h"
+#include "parameter/originalpath.h"
+#include "2geom/affine.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPEAttachPath::LPEAttachPath(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ start_path(_("Start path:"), _("Path to attach to the start of this path"), "startpath", &wr, this),
+ start_path_position(_("Start path position:"), _("Position to attach path start to"), "startposition", &wr, this, 0.0),
+ start_path_curve_start(_("Start path curve start:"), _("Starting curve"), "startcurvestart", &wr, this, Geom::Point(20,0)/*, true*/),
+ start_path_curve_end(_("Start path curve end:"), _("Ending curve"), "startcurveend", &wr, this, Geom::Point(20,0)/*, true*/),
+ end_path(_("End path:"), _("Path to attach to the end of this path"), "endpath", &wr, this),
+ end_path_position(_("End path position:"), _("Position to attach path end to"), "endposition", &wr, this, 0.0),
+ end_path_curve_start(_("End path curve start:"), _("Starting curve"), "endcurvestart", &wr, this, Geom::Point(20,0)/*, true*/),
+ end_path_curve_end(_("End path curve end:"), _("Ending curve"), "endcurveend", &wr, this, Geom::Point(20,0)/*, true*/)
+{
+ registerParameter( dynamic_cast<Parameter *>(&start_path) );
+ registerParameter( dynamic_cast<Parameter *>(&start_path_position) );
+ registerParameter( dynamic_cast<Parameter *>(&start_path_curve_start) );
+ registerParameter( dynamic_cast<Parameter *>(&start_path_curve_end) );
+
+ registerParameter( dynamic_cast<Parameter *>(&end_path) );
+ registerParameter( dynamic_cast<Parameter *>(&end_path_position) );
+ registerParameter( dynamic_cast<Parameter *>(&end_path_curve_start) );
+ registerParameter( dynamic_cast<Parameter *>(&end_path_curve_end) );
+
+ //perceived_path = true;
+ show_orig_path = true;
+ curve_start_previous_origin = start_path_curve_end.getOrigin();
+ curve_end_previous_origin = end_path_curve_end.getOrigin();
+}
+
+LPEAttachPath::~LPEAttachPath()
+{
+
+}
+
+void LPEAttachPath::resetDefaults(SPItem const * item)
+{
+ curve_start_previous_origin = start_path_curve_end.getOrigin();
+ curve_end_previous_origin = end_path_curve_end.getOrigin();
+}
+
+void LPEAttachPath::doBeforeEffect(const SPLPEItem *lpeitem)
+{
+ lpe_effect = lpeitem;
+}
+
+void LPEAttachPath::doEffect (SPCurve * curve)
+{
+ std::vector<Geom::Path> this_pathv = curve->get_pathvector();
+ if (lpe_effect && !this_pathv.empty()) {
+ Geom::Path p = Geom::Path(this_pathv.front().initialPoint());
+
+ bool set_start_end = start_path_curve_end.getOrigin() != curve_start_previous_origin;
+ bool set_end_end = end_path_curve_end.getOrigin() != curve_end_previous_origin;
+
+ if (start_path.linksToPath()) {
+
+ std::vector<Geom::Path> linked_pathv = start_path.get_pathvector();
+ Geom::Affine linkedtransform = start_path.getObject()->getRelativeTransform(lpe_effect);
+
+ if ( !linked_pathv.empty() )
+ {
+ Geom::Path transformedpath = linked_pathv.front() * linkedtransform;
+ start_path_curve_start.setOrigin(this_pathv.front().initialPoint());
+
+ std::vector<Geom::Point> derivs = this_pathv.front().front().pointAndDerivatives(0, 3);
+
+ for (unsigned deriv_n = 1; deriv_n < derivs.size(); deriv_n++) {
+ Geom::Coord length = derivs[deriv_n].length();
+ if ( ! Geom::are_near(length, 0) ) {
+ if (set_start_end) {
+ start_path_position.param_set_value(transformedpath.nearestPoint(start_path_curve_end.getOrigin()));
+ }
+
+ if (start_path_position > transformedpath.size()) {
+ start_path_position.param_set_value(transformedpath.size());
+ } else if (start_path_position < 0) {
+ start_path_position.param_set_value(0);
+ }
+ const Geom::Curve *c = start_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)start_path_position);
+
+ std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(start_path_position >= transformedpath.size() ? 1 : (start_path_position - (int)start_path_position), 3);
+ for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) {
+ Geom::Coord length_2 = derivs[deriv_n_2].length();
+ if ( ! Geom::are_near(length_2, 0) ) {
+ start_path_curve_end.setOrigin(derivs_2[0]);
+ curve_start_previous_origin = start_path_curve_end.getOrigin();
+
+ double startangle = atan2(start_path_curve_start.getVector().y(), start_path_curve_start.getVector().x());
+ double endangle = atan2(start_path_curve_end.getVector().y(), start_path_curve_end.getVector().x());
+ double startderiv = atan2(derivs[deriv_n].y(), derivs[deriv_n].x());
+ double endderiv = atan2(derivs_2[deriv_n_2].y(), derivs_2[deriv_n_2].x());
+ Geom::Point pt1 = Geom::Point(start_path_curve_start.getVector().length() * cos(startangle + startderiv), start_path_curve_start.getVector().length() * sin(startangle + startderiv));
+ Geom::Point pt2 = Geom::Point(start_path_curve_end.getVector().length() * cos(endangle + endderiv), start_path_curve_end.getVector().length() * sin(endangle + endderiv));
+ p = Geom::Path(derivs_2[0]);
+ p.appendNew<Geom::CubicBezier>(-pt2 + derivs_2[0], -pt1 + this_pathv.front().initialPoint(), this_pathv.front().initialPoint());
+ break;
+
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ p.append(this_pathv.front());
+
+ if (end_path.linksToPath()) {
+
+ std::vector<Geom::Path> linked_pathv = end_path.get_pathvector();
+ Geom::Affine linkedtransform = end_path.getObject()->getRelativeTransform(lpe_effect);
+
+ if ( !linked_pathv.empty() )
+ {
+ Geom::Path transformedpath = linked_pathv.front() * linkedtransform;
+ Geom::Curve * last_seg_reverse = this_pathv.front().back().reverse();
+
+ end_path_curve_start.setOrigin(last_seg_reverse->initialPoint());
+
+ std::vector<Geom::Point> derivs = last_seg_reverse->pointAndDerivatives(0, 3);
+ for (unsigned deriv_n = 1; deriv_n < derivs.size(); deriv_n++) {
+ Geom::Coord length = derivs[deriv_n].length();
+ if ( ! Geom::are_near(length, 0) ) {
+ if (set_end_end) {
+ end_path_position.param_set_value(transformedpath.nearestPoint(end_path_curve_end.getOrigin()));
+ }
+
+ if (end_path_position > transformedpath.size()) {
+ end_path_position.param_set_value(transformedpath.size());
+ } else if (end_path_position < 0) {
+ end_path_position.param_set_value(0);
+ }
+ const Geom::Curve *c = end_path_position >= transformedpath.size() ? &transformedpath.back() : &transformedpath.at_index((int)end_path_position);
+
+ std::vector<Geom::Point> derivs_2 = c->pointAndDerivatives(end_path_position >= transformedpath.size() ? 1 : (end_path_position - (int)end_path_position), 3);
+ for (unsigned deriv_n_2 = 1; deriv_n_2 < derivs_2.size(); deriv_n_2++) {
+ Geom::Coord length_2 = derivs[deriv_n_2].length();
+ if ( ! Geom::are_near(length_2, 0) ) {
+
+ end_path_curve_end.setOrigin(derivs_2[0]);
+ curve_end_previous_origin = end_path_curve_end.getOrigin();
+
+ double startangle = atan2(end_path_curve_start.getVector().y(), end_path_curve_start.getVector().x());
+ double endangle = atan2(end_path_curve_end.getVector().y(), end_path_curve_end.getVector().x());
+ double startderiv = atan2(derivs[deriv_n].y(), derivs[deriv_n].x());
+ double endderiv = atan2(derivs_2[deriv_n_2].y(), derivs_2[deriv_n_2].x());
+ Geom::Point pt1 = Geom::Point(end_path_curve_start.getVector().length() * cos(startangle + startderiv), end_path_curve_start.getVector().length() * sin(startangle + startderiv));
+ Geom::Point pt2 = Geom::Point(end_path_curve_end.getVector().length() * cos(endangle + endderiv), end_path_curve_end.getVector().length() * sin(endangle + endderiv));
+ p.appendNew<Geom::CubicBezier>(-pt1 + this_pathv.front().finalPoint(), -pt2 + derivs_2[0], derivs_2[0]);
+
+ break;
+
+ }
+ }
+ break;
+ }
+ }
+ delete last_seg_reverse;
+ }
+ }
+ Geom::PathVector outvector;
+ outvector.push_back(p);
+ curve->set_pathvector(outvector);
+ }
+}
+
+} // 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-attach-path.h b/src/live_effects/lpe-attach-path.h
new file mode 100644
index 000000000..3dda189d6
--- /dev/null
+++ b/src/live_effects/lpe-attach-path.h
@@ -0,0 +1,54 @@
+#ifndef INKSCAPE_LPE_ATTACH_PATH_H
+#define INKSCAPE_LPE_ATTACH_PATH_H
+
+/*
+ * Inkscape::LPEAttachPath
+ *
+ * Copyright (C) Ted Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * 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 "live_effects/parameter/originalpath.h"
+#include "live_effects/parameter/vector.h"
+#include "live_effects/parameter/bool.h"
+#include "live_effects/parameter/transformedpoint.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEAttachPath : public Effect {
+public:
+ LPEAttachPath(LivePathEffectObject *lpeobject);
+ virtual ~LPEAttachPath();
+
+ virtual void doBeforeEffect(const SPLPEItem *lpeitem);
+ virtual void doEffect (SPCurve * curve);
+ virtual void resetDefaults(SPItem const * item);
+
+private:
+ LPEAttachPath(const LPEAttachPath&);
+ LPEAttachPath& operator=(const LPEAttachPath&);
+
+ Geom::Point curve_start_previous_origin;
+ Geom::Point curve_end_previous_origin;
+
+ OriginalPathParam start_path;
+ ScalarParam start_path_position;
+ TransformedPointParam start_path_curve_start;
+ VectorParam start_path_curve_end;
+
+ OriginalPathParam end_path;
+ ScalarParam end_path_position;
+ TransformedPointParam end_path_curve_start;
+ VectorParam end_path_curve_end;
+ const SPLPEItem * lpe_effect;
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-bounding-box.cpp b/src/live_effects/lpe-bounding-box.cpp
new file mode 100644
index 000000000..bafd5e70e
--- /dev/null
+++ b/src/live_effects/lpe-bounding-box.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glibmm/i18n.h>
+
+#include "live_effects/lpe-bounding-box.h"
+
+#include "display/curve.h"
+#include "sp-item.h"
+#include "2geom/path.h"
+#include "sp-shape.h"
+#include "sp-text.h"
+#include "2geom/bezier-curve.h"
+#include "lpe-bounding-box.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPEBoundingBox::LPEBoundingBox(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this),
+ visual_bounds(_("Visual Bounds"), _("Uses the visual bounding box"), "visualbounds", &wr, this)
+{
+ registerParameter( dynamic_cast<Parameter *>(&linked_path) );
+ registerParameter( dynamic_cast<Parameter *>(&visual_bounds) );
+ //perceived_path = true;
+}
+
+LPEBoundingBox::~LPEBoundingBox()
+{
+
+}
+
+void LPEBoundingBox::doEffect (SPCurve * curve)
+{
+ if (curve) {
+ if ( linked_path.linksToPath() && linked_path.getObject() ) {
+ SPItem * item = linked_path.getObject();
+ Geom::OptRect bbox = visual_bounds.get_value() ? item->visualBounds() : item->geometricBounds();
+ Geom::Path p(Geom::Point(bbox->left(), bbox->top()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->top()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->right(), bbox->bottom()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->bottom()));
+ p.appendNew<Geom::LineSegment>(Geom::Point(bbox->left(), bbox->top()));
+ std::vector<Geom::Path> out;
+ out.push_back(p);
+ curve->set_pathvector(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-bounding-box.h b/src/live_effects/lpe-bounding-box.h
new file mode 100644
index 000000000..d028a20ac
--- /dev/null
+++ b/src/live_effects/lpe-bounding-box.h
@@ -0,0 +1,37 @@
+#ifndef INKSCAPE_LPE_BOUNDING_BOX_H
+#define INKSCAPE_LPE_BOUNDING_BOX_H
+
+/*
+ * Inkscape::LPEFillBetweenStrokes
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/originalpath.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEBoundingBox : public Effect {
+public:
+ LPEBoundingBox(LivePathEffectObject *lpeobject);
+ virtual ~LPEBoundingBox();
+
+ virtual void doEffect (SPCurve * curve);
+
+private:
+ OriginalPathParam linked_path;
+ BoolParam visual_bounds;
+
+private:
+ LPEBoundingBox(const LPEBoundingBox&);
+ LPEBoundingBox& operator=(const LPEBoundingBox&);
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-ellipse_5pts.cpp b/src/live_effects/lpe-ellipse_5pts.cpp
new file mode 100644
index 000000000..b0a5919fe
--- /dev/null
+++ b/src/live_effects/lpe-ellipse_5pts.cpp
@@ -0,0 +1,214 @@
+/** \file
+ * LPE "Ellipse through 5 points" implementation
+ */
+
+/*
+ * Authors:
+ * Theodore Janeczko
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-ellipse_5pts.h"
+
+// You might need to include other 2geom files. You can add them here:
+#include <glibmm/i18n.h>
+#include <2geom/path.h>
+#include <2geom/circle.h>
+#include <2geom/ellipse.h>
+#include <2geom/path-sink.h>
+#include "inkscape.h"
+#include "desktop.h"
+#include "message-stack.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPEEllipse5Pts::LPEEllipse5Pts(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject)
+{
+ //perceived_path = true;
+}
+
+LPEEllipse5Pts::~LPEEllipse5Pts()
+{
+}
+
+static double _det3(double (*mat)[3])
+{
+ for (int i = 0; i < 2; i++)
+ {
+ for (int j = i + 1; j < 3; j++)
+ {
+ for (int k = i + 1; k < 3; k++)
+ {
+ mat[j][k] = (mat[j][k] * mat[i][i] - mat[j][i] * mat[i][k]);
+ if (i) mat[j][k] /= mat[i-1][i-1];
+ }
+ }
+ }
+ return mat[2][2];
+}
+static double _det5(double (*mat)[5])
+{
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = i + 1; j < 5; j++)
+ {
+ for (int k = i + 1; k < 5; k++)
+ {
+ mat[j][k] = (mat[j][k] * mat[i][i] - mat[j][i] * mat[i][k]);
+ if (i) mat[j][k] /= mat[i-1][i-1];
+ }
+ }
+ }
+ return mat[4][4];
+}
+
+std::vector<Geom::Path>
+LPEEllipse5Pts::doEffect_path (std::vector<Geom::Path> const & path_in)
+{
+ std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
+
+ if (path_in[0].size() < 4) {
+
+ SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Five points required for constructing an ellipse"));
+ return path_in;
+ }
+ // we assume that the path has >= 3 nodes
+ Geom::Point A = path_in[0].initialPoint();
+ Geom::Point B = path_in[0].pointAt(1);
+ Geom::Point C = path_in[0].pointAt(2);
+ Geom::Point D = path_in[0].pointAt(3);
+ Geom::Point E = path_in[0].pointAt(4);
+
+ using namespace Geom;
+
+ double rowmajor_matrix[5][6] =
+ {
+ {A.x()*A.x(), A.x()*A.y(), A.y()*A.y(), A.x(), A.y(), 1},
+ {B.x()*B.x(), B.x()*B.y(), B.y()*B.y(), B.x(), B.y(), 1},
+ {C.x()*C.x(), C.x()*C.y(), C.y()*C.y(), C.x(), C.y(), 1},
+ {D.x()*D.x(), D.x()*D.y(), D.y()*D.y(), D.x(), D.y(), 1},
+ {E.x()*E.x(), E.x()*E.y(), E.y()*E.y(), E.x(), E.y(), 1}
+ };
+
+ double mat_a[5][5] =
+ {
+ {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
+ {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
+ {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
+ {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
+ {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
+ };
+ double mat_b[5][5] =
+ {
+ {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
+ {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
+ {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
+ {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
+ {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
+ };
+ double mat_c[5][5] =
+ {
+ {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
+ {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
+ {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
+ {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
+ {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
+ };
+ double mat_d[5][5] =
+ {
+ {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
+ {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
+ {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
+ {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]},
+ {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
+ };
+ double mat_e[5][5] =
+ {
+ {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
+ {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
+ {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
+ {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
+ {rowmajor_matrix[0][5], rowmajor_matrix[1][5], rowmajor_matrix[2][5], rowmajor_matrix[3][5], rowmajor_matrix[4][5]}
+ };
+ double mat_f[5][5] =
+ {
+ {rowmajor_matrix[0][0], rowmajor_matrix[1][0], rowmajor_matrix[2][0], rowmajor_matrix[3][0], rowmajor_matrix[4][0]},
+ {rowmajor_matrix[0][1], rowmajor_matrix[1][1], rowmajor_matrix[2][1], rowmajor_matrix[3][1], rowmajor_matrix[4][1]},
+ {rowmajor_matrix[0][2], rowmajor_matrix[1][2], rowmajor_matrix[2][2], rowmajor_matrix[3][2], rowmajor_matrix[4][2]},
+ {rowmajor_matrix[0][3], rowmajor_matrix[1][3], rowmajor_matrix[2][3], rowmajor_matrix[3][3], rowmajor_matrix[4][3]},
+ {rowmajor_matrix[0][4], rowmajor_matrix[1][4], rowmajor_matrix[2][4], rowmajor_matrix[3][4], rowmajor_matrix[4][4]}
+ };
+
+ double a1 = _det5(mat_a);
+ double b1 = -_det5(mat_b);
+ double c1 = _det5(mat_c);
+ double d1 = -_det5(mat_d);
+ double e1 = _det5(mat_e);
+ double f1 = -_det5(mat_f);
+
+ double mat_check[][3] =
+ {
+ {a1, b1/2, d1/2},
+ {b1/2, c1, e1/2},
+ {d1/2, e1/2, f1}
+ };
+
+ if (_det3(mat_check) == 0 || a1*c1 - b1*b1/4 <= 0) {
+ SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No ellipse found for specified points"));
+ return path_in;
+ }
+
+ Geom::Ellipse el(a1, b1, c1, d1, e1, f1);
+
+ double s, e;
+ double x0, y0, x1, y1, x2, y2, x3, y3;
+ double len;
+
+ // figure out if we have a slice, guarding against rounding errors
+
+ Path p(Geom::Point(cos(0), sin(0)));
+
+ double end = 2 * M_PI;
+ for (s = 0; s < end; s += M_PI_2) {
+ e = s + M_PI_2;
+ if (e > end)
+ e = end;
+ len = 4*tan((e - s)/4)/3;
+ x0 = cos(s);
+ y0 = sin(s);
+ x1 = x0 + len * cos(s + M_PI_2);
+ y1 = y0 + len * sin(s + M_PI_2);
+ x3 = cos(e);
+ y3 = sin(e);
+ x2 = x3 + len * cos(e - M_PI_2);
+ y2 = y3 + len * sin(e - M_PI_2);
+ p.appendNew<Geom::CubicBezier>(Geom::Point(x1,y1), Geom::Point(x2,y2), Geom::Point(x3,y3));
+ }
+
+ Geom::Affine aff = Geom::Scale(el.ray(Geom::X), el.ray(Geom::Y)) * Geom::Rotate(el.rot_angle()) * Geom::Translate(el.center());
+
+ path_out.push_back(p * aff);
+
+ 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-ellipse_5pts.h b/src/live_effects/lpe-ellipse_5pts.h
new file mode 100644
index 000000000..d3b1fccfa
--- /dev/null
+++ b/src/live_effects/lpe-ellipse_5pts.h
@@ -0,0 +1,50 @@
+#ifndef INKSCAPE_LPE_ELLIPSE_5PTS_H
+#define INKSCAPE_LPE_ELLIPSE_5PTS_H
+
+/** \file
+ * LPE "Ellipse through 5 points" implementation
+ */
+
+/*
+ * Authors:
+ * Theodore Janeczko
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * 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"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEEllipse5Pts : public Effect {
+public:
+ LPEEllipse5Pts(LivePathEffectObject *lpeobject);
+ virtual ~LPEEllipse5Pts();
+
+ virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+
+private:
+ LPEEllipse5Pts(const LPEEllipse5Pts&);
+ LPEEllipse5Pts& operator=(const LPEEllipse5Pts&);
+};
+
+} //namespace LivePathEffect
+} //namespace Inkscape
+
+#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 :
diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp
new file mode 100644
index 000000000..00cc1fed5
--- /dev/null
+++ b/src/live_effects/lpe-fill-between-many.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glibmm/i18n.h>
+#include <gtkmm/box.h>
+
+#include "live_effects/lpe-fill-between-many.h"
+
+#include "display/curve.h"
+#include "sp-item.h"
+#include "2geom/path.h"
+#include "sp-shape.h"
+#include "sp-text.h"
+#include "2geom/bezier-curve.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPEFillBetweenMany::LPEFillBetweenMany(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ linked_paths(_("Linked path:"), _("Paths from which to take the original path data"), "linkedpaths", &wr, this)
+{
+ registerParameter( dynamic_cast<Parameter *>(&linked_paths) );
+ //perceived_path = true;
+}
+
+LPEFillBetweenMany::~LPEFillBetweenMany()
+{
+
+}
+
+void LPEFillBetweenMany::doEffect (SPCurve * curve)
+{
+ std::vector<Geom::Path> res_pathv;
+ SPItem * firstObj = NULL;
+ for (std::vector<PathAndDirection*>::iterator iter = linked_paths._vector.begin(); iter != linked_paths._vector.end(); iter++) {
+ SPObject *obj;
+ if ((*iter)->ref.isAttached() && (obj = (*iter)->ref.getObject()) && SP_IS_ITEM(obj) && !(*iter)->_pathvector.empty()) {
+ Geom::Path linked_path;
+ if ((*iter)->reversed) {
+ linked_path = (*iter)->_pathvector.front().reverse();
+ } else {
+ linked_path = (*iter)->_pathvector.front();
+ }
+
+ if (!res_pathv.empty()) {
+ linked_path = linked_path * SP_ITEM(obj)->getRelativeTransform(firstObj);
+ res_pathv.front().appendNew<Geom::LineSegment>(linked_path.initialPoint());
+ res_pathv.front().append(linked_path);
+ } else {
+ firstObj = SP_ITEM(obj);
+ res_pathv.push_back(linked_path);
+ }
+ }
+ }
+ if (!res_pathv.empty()) {
+ res_pathv.front().close();
+ }
+ curve->set_pathvector(res_pathv);
+}
+
+} // 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-fill-between-many.h b/src/live_effects/lpe-fill-between-many.h
new file mode 100644
index 000000000..99ee8b15f
--- /dev/null
+++ b/src/live_effects/lpe-fill-between-many.h
@@ -0,0 +1,36 @@
+#ifndef INKSCAPE_LPE_FILL_BETWEEN_MANY_H
+#define INKSCAPE_LPE_FILL_BETWEEN_MANY_H
+
+/*
+ * Inkscape::LPEFillBetweenStrokes
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/originalpatharray.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEFillBetweenMany : public Effect {
+public:
+ LPEFillBetweenMany(LivePathEffectObject *lpeobject);
+ virtual ~LPEFillBetweenMany();
+
+ virtual void doEffect (SPCurve * curve);
+
+private:
+ OriginalPathArrayParam linked_paths;
+
+private:
+ LPEFillBetweenMany(const LPEFillBetweenMany&);
+ LPEFillBetweenMany& operator=(const LPEFillBetweenMany&);
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-fill-between-strokes.cpp b/src/live_effects/lpe-fill-between-strokes.cpp
new file mode 100644
index 000000000..e72979ed0
--- /dev/null
+++ b/src/live_effects/lpe-fill-between-strokes.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glibmm/i18n.h>
+
+#include "live_effects/lpe-fill-between-strokes.h"
+
+#include "display/curve.h"
+#include "sp-item.h"
+#include "2geom/path.h"
+#include "sp-shape.h"
+#include "sp-text.h"
+#include "2geom/bezier-curve.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPEFillBetweenStrokes::LPEFillBetweenStrokes(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ linked_path(_("Linked path:"), _("Path from which to take the original path data"), "linkedpath", &wr, this),
+ second_path(_("Second path:"), _("Second path from which to take the original path data"), "secondpath", &wr, this),
+ reverse_second(_("Reverse Second"), _("Reverses the second path order"), "reversesecond", &wr, this)
+{
+ registerParameter( dynamic_cast<Parameter *>(&linked_path) );
+ registerParameter( dynamic_cast<Parameter *>(&second_path) );
+ registerParameter( dynamic_cast<Parameter *>(&reverse_second) );
+ //perceived_path = true;
+}
+
+LPEFillBetweenStrokes::~LPEFillBetweenStrokes()
+{
+
+}
+
+void LPEFillBetweenStrokes::doEffect (SPCurve * curve)
+{
+ if (curve) {
+ if ( linked_path.linksToPath() && second_path.linksToPath() && linked_path.getObject() && second_path.getObject() ) {
+ std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector();
+ std::vector<Geom::Path> second_pathv = second_path.get_pathvector();
+ std::vector<Geom::Path> result_linked_pathv;
+ std::vector<Geom::Path> result_second_pathv;
+ Geom::Affine second_transform = second_path.getObject()->getRelativeTransform(linked_path.getObject());
+
+ for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter)
+ {
+ result_linked_pathv.push_back((*iter));
+ }
+ for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter)
+ {
+ result_second_pathv.push_back((*iter) * second_transform);
+ }
+
+ if ( !result_linked_pathv.empty() && !result_second_pathv.empty() && !result_linked_pathv.front().closed() ) {
+ if (reverse_second.get_value())
+ {
+ result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().finalPoint());
+ result_linked_pathv.front().append(result_second_pathv.front().reverse());
+ }
+ else
+ {
+ result_linked_pathv.front().appendNew<Geom::LineSegment>(result_second_pathv.front().initialPoint());
+ result_linked_pathv.front().append(result_second_pathv.front());
+ }
+ curve->set_pathvector(result_linked_pathv);
+ }
+ else if ( !result_linked_pathv.empty() ) {
+ curve->set_pathvector(result_linked_pathv);
+ }
+ else if ( !result_second_pathv.empty() ) {
+ curve->set_pathvector(result_second_pathv);
+ }
+ }
+ else if ( linked_path.linksToPath() && linked_path.getObject() ) {
+ std::vector<Geom::Path> linked_pathv = linked_path.get_pathvector();
+ std::vector<Geom::Path> result_pathv;
+
+ for (std::vector<Geom::Path>::iterator iter = linked_pathv.begin(); iter != linked_pathv.end(); ++iter)
+ {
+ result_pathv.push_back((*iter));
+ }
+ if ( !result_pathv.empty() ) {
+ curve->set_pathvector(result_pathv);
+ }
+ }
+ else if ( second_path.linksToPath() && second_path.getObject() ) {
+ std::vector<Geom::Path> second_pathv = second_path.get_pathvector();
+ std::vector<Geom::Path> result_pathv;
+
+ for (std::vector<Geom::Path>::iterator iter = second_pathv.begin(); iter != second_pathv.end(); ++iter)
+ {
+ result_pathv.push_back((*iter));
+ }
+ if ( !result_pathv.empty() ) {
+ curve->set_pathvector(result_pathv);
+ }
+ }
+ }
+}
+
+} // 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-fill-between-strokes.h b/src/live_effects/lpe-fill-between-strokes.h
new file mode 100644
index 000000000..ec57b1852
--- /dev/null
+++ b/src/live_effects/lpe-fill-between-strokes.h
@@ -0,0 +1,38 @@
+#ifndef INKSCAPE_LPE_FILL_BETWEEN_STROKES_H
+#define INKSCAPE_LPE_FILL_BETWEEN_STROKES_H
+
+/*
+ * Inkscape::LPEFillBetweenStrokes
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/originalpath.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEFillBetweenStrokes : public Effect {
+public:
+ LPEFillBetweenStrokes(LivePathEffectObject *lpeobject);
+ virtual ~LPEFillBetweenStrokes();
+
+ virtual void doEffect (SPCurve * curve);
+
+private:
+ OriginalPathParam linked_path;
+ OriginalPathParam second_path;
+ BoolParam reverse_second;
+
+private:
+ LPEFillBetweenStrokes(const LPEFillBetweenStrokes&);
+ LPEFillBetweenStrokes& operator=(const LPEFillBetweenStrokes&);
+};
+
+}; //namespace LivePathEffect
+}; //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp
new file mode 100644
index 000000000..97c0a1b8a
--- /dev/null
+++ b/src/live_effects/lpe-jointype.cpp
@@ -0,0 +1,203 @@
+/** \file
+ * LPE "Join Type" implementation
+ */
+ /* Authors:
+ *
+ * Liam P White
+ *
+ * Copyright (C) 2014 Authors
+ *
+ * Released under GNU GPL v2, read the file 'COPYING' for more information
+ */
+
+#include <math.h>
+
+#include "sp-shape.h"
+#include "style.h"
+#include "xml/repr.h"
+#include "sp-paint-server.h"
+#include "svg/svg-color.h"
+#include "desktop-style.h"
+#include "svg/css-ostringstream.h"
+#include "display/curve.h"
+#include "live_effects/parameter/enum.h"
+
+#include <2geom/path.h>
+#include <2geom/svg-elliptical-arc.h>
+#include "live_effects/pathoutlineprovider.h"
+
+#include "lpe-jointype.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+static const Util::EnumData<unsigned> JoinTypeData[] = {
+ {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"},
+ {LINEJOIN_ROUND, N_("Rounded"), "round"},
+ {LINEJOIN_POINTY, N_("Miter"), "miter"},
+ {LINEJOIN_REFLECTED, N_("Reflected"), "extrapolated"},
+ {LINEJOIN_EXTRAPOLATED, N_("Extrapolated arc"), "extrp_arc"}
+};
+
+static const Util::EnumData<unsigned> CapTypeData[] = {
+ {butt_straight, N_("Butt"), "butt"},
+ {butt_round, N_("Rounded"), "round"},
+ {butt_square, N_("Square"), "square"},
+ {butt_pointy, N_("Peak"), "peak"}
+};
+
+static const Util::EnumDataConverter<unsigned> CapTypeConverter(CapTypeData, sizeof(CapTypeData)/sizeof(*CapTypeData));
+static const Util::EnumDataConverter<unsigned> JoinTypeConverter(JoinTypeData, sizeof(JoinTypeData)/sizeof(*JoinTypeData));
+
+LPEJoinType::LPEJoinType(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ line_width(_("Line width"), _("Thickness of the stroke"), "line_width", &wr, this, 10.),
+ linecap_type(_("Line cap"), _("The end shape of the stroke"), "linecap_type", CapTypeConverter, &wr, this, butt_straight),
+ linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", JoinTypeConverter, &wr, this, join_pointy),
+ miter_limit(_("Miter limit:"), _("Maximum length of the miter join (in units of stroke width)"), "miter_limit", &wr, this, 100.),
+ attempt_force_join(_("Force miter"), _("Overrides the miter limit and forces a join."), "attempt_force_join", &wr, this, true)
+{
+ show_orig_path = true;
+ registerParameter( dynamic_cast<Parameter *>(&linecap_type) );
+ registerParameter( dynamic_cast<Parameter *>(&line_width) );
+ registerParameter( dynamic_cast<Parameter *>(&linejoin_type) );
+ registerParameter( dynamic_cast<Parameter *>(&miter_limit) );
+ registerParameter( dynamic_cast<Parameter *>(&attempt_force_join) );
+ was_initialized = false;
+}
+
+LPEJoinType::~LPEJoinType()
+{
+}
+
+//from LPEPowerStroke -- sets fill if stroke color because we will
+//be converting to a fill to make the new join.
+
+void LPEJoinType::doOnApply(SPLPEItem const* lpeitem)
+{
+ if (SP_IS_SHAPE(lpeitem)) {
+ SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem);
+ double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.;
+
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ if (lpeitem->style->stroke.isSet()) {
+ if (lpeitem->style->stroke.isPaintserver()) {
+ SPPaintServer * server = lpeitem->style->getStrokePaintServer();
+ if (server) {
+ Glib::ustring str;
+ str += "url(#";
+ str += server->getId();
+ str += ")";
+ sp_repr_css_set_property (css, "fill", str.c_str());
+ }
+ } else if (lpeitem->style->stroke.isColor()) {
+ gchar c[64];
+ sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
+ sp_repr_css_set_property (css, "fill", c);
+ } else {
+ sp_repr_css_set_property (css, "fill", "none");
+ }
+ } else {
+ sp_repr_css_unset_property (css, "fill");
+ }
+
+ sp_repr_css_set_property(css, "stroke", "none");
+
+ sp_desktop_apply_css_recursive(item, css, true);
+ sp_repr_css_attr_unref (css);
+ if (!was_initialized)
+ {
+ was_initialized = true;
+ line_width.param_set_value(width);
+ }
+ } else {
+ g_warning("LPE Join Type can only be applied to paths (not groups).");
+ }
+}
+
+//from LPEPowerStroke -- sets stroke color from existing fill color
+
+void LPEJoinType::doOnRemove(SPLPEItem const* lpeitem)
+{
+
+ if (SP_IS_SHAPE(lpeitem)) {
+ SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem);
+
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ if (lpeitem->style->fill.isSet()) {
+ if (lpeitem->style->fill.isPaintserver()) {
+ SPPaintServer * server = lpeitem->style->getFillPaintServer();
+ if (server) {
+ Glib::ustring str;
+ str += "url(#";
+ str += server->getId();
+ str += ")";
+ sp_repr_css_set_property (css, "stroke", str.c_str());
+ }
+ } else if (lpeitem->style->fill.isColor()) {
+ gchar c[64];
+ sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
+ sp_repr_css_set_property (css, "stroke", c);
+ } else {
+ sp_repr_css_set_property (css, "stroke", "none");
+ }
+ } else {
+ sp_repr_css_unset_property (css, "stroke");
+ }
+
+ Inkscape::CSSOStringStream os;
+ os << fabs(line_width);
+ sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
+
+ sp_repr_css_set_property(css, "fill", "none");
+
+ sp_desktop_apply_css_recursive(item, css, true);
+ sp_repr_css_attr_unref (css);
+ item->updateRepr();
+ }
+}
+
+
+std::vector<Geom::Path> LPEJoinType::doEffect_path(std::vector<Geom::Path> const & path_in)
+{
+ std::vector<Geom::Path> path_out = std::vector<Geom::Path>();
+ if (path_in.empty())
+ {
+ return path_out;
+ }
+ Path p = Path();
+ Path outlinepath = Path();
+ for (unsigned i = 0; i < path_in.size(); i++)
+ {
+ p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
+ }
+
+ #define miter_lim ( (attempt_force_join) ? std::numeric_limits<double>::max() : fabs(line_width * miter_limit))
+
+ //magic!
+ if (linejoin_type.get_value() <= 2)
+ {
+ p.Outline(&outlinepath, line_width / 2, static_cast<JoinType>( linejoin_type.get_value() ),
+ static_cast<ButtType>( linecap_type.get_value() ), miter_lim);
+ //fix memory leak
+ std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
+ path_out = *pv_p;
+ delete pv_p;
+
+ } else if (linejoin_type.get_value() == 3) {
+ //reflected arc join
+ path_out = Outline::outlinePath(path_in, line_width, static_cast<JoinType>( linejoin_type.get_value() ),
+ static_cast<ButtType>( linecap_type.get_value() ), miter_lim);
+
+ } else if (linejoin_type.get_value() == 4) {
+ //extrapolated arc join
+ path_out = Outline::outlinePath_extr(path_in, line_width, LINEJOIN_STRAIGHT, static_cast<ButtType>(linecap_type.get_value()), miter_lim);
+
+ }
+
+ #undef miter_lim
+ return path_out;
+}
+
+} //namespace LivePathEffect
+} //namespace Inkscape
diff --git a/src/live_effects/lpe-jointype.h b/src/live_effects/lpe-jointype.h
new file mode 100755
index 000000000..db113c66a
--- /dev/null
+++ b/src/live_effects/lpe-jointype.h
@@ -0,0 +1,43 @@
+/* Authors:
+ * Liam P White
+ *
+ * Copyright (C) 2014 Authors
+ *
+ * Released under GNU GPL v2, read the file COPYING for more information
+ */
+#ifndef INKSCAPE_LPE_JOINTYPE_H
+#define INKSCAPE_LPE_JOINTYPE_H
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/point.h"
+#include "live_effects/parameter/enum.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPEJoinType : public Effect {
+public:
+ LPEJoinType(LivePathEffectObject *lpeobject);
+ virtual ~LPEJoinType();
+
+ virtual void doOnApply(SPLPEItem const* lpeitem);
+ virtual void doOnRemove(SPLPEItem const* lpeitem);
+ virtual std::vector <Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
+
+private:
+ LPEJoinType(const LPEJoinType&);
+ LPEJoinType& operator=(const LPEJoinType&);
+
+ ScalarParam line_width;
+ EnumParam<unsigned> linecap_type;
+ EnumParam<unsigned> linejoin_type;
+ ScalarParam miter_limit;
+ BoolParam attempt_force_join;
+ bool was_initialized;
+};
+
+} //namespace LivePathEffect
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h
index 6f5b75af8..f080b06e6 100644
--- a/src/live_effects/lpe-powerstroke-interpolators.h
+++ b/src/live_effects/lpe-powerstroke-interpolators.h
@@ -27,7 +27,8 @@ enum InterpolatorType {
INTERP_LINEAR,
INTERP_CUBICBEZIER,
INTERP_CUBICBEZIER_JOHAN,
- INTERP_SPIRO
+ INTERP_SPIRO,
+ INTERP_CUBICBEZIER_SMOOTH
};
class Interpolator {
@@ -133,6 +134,43 @@ private:
CubicBezierJohan& operator=(const CubicBezierJohan&);
};
+/// @todo invent name for this class
+class CubicBezierSmooth : public Interpolator {
+public:
+ CubicBezierSmooth(double beta = 0.2) {
+ _beta = beta;
+ };
+ virtual ~CubicBezierSmooth() {};
+
+ virtual Path interpolateToPath(std::vector<Point> const &points) const {
+ Path fit;
+ fit.start(points.at(0));
+ unsigned int num_points = points.size();
+ for (unsigned int i = 1; i < num_points; ++i) {
+ Point p0 = points.at(i-1);
+ Point p1 = points.at(i);
+ Point dx = Point(p1[X] - p0[X], 0);
+ if (i == 1) {
+ fit.appendNew<CubicBezier>(p0, p1-0.75*dx, p1);
+ } else if (i == points.size() - 1) {
+ fit.appendNew<CubicBezier>(p0+0.75*dx, p1, p1);
+ } else {
+ fit.appendNew<CubicBezier>(p0+_beta*dx, p1-_beta*dx, p1);
+ }
+ }
+ return fit;
+ };
+
+ void setBeta(double beta) {
+ _beta = beta;
+ }
+
+ double _beta;
+
+private:
+ CubicBezierSmooth(const CubicBezierSmooth&);
+ CubicBezierSmooth& operator=(const CubicBezierSmooth&);
+};
class SpiroInterpolator : public Interpolator {
public:
@@ -179,6 +217,8 @@ Interpolator::create(InterpolatorType type) {
return new Geom::Interpolate::CubicBezierJohan();
case INTERP_SPIRO:
return new Geom::Interpolate::SpiroInterpolator();
+ case INTERP_CUBICBEZIER_SMOOTH:
+ return new Geom::Interpolate::CubicBezierSmooth();
default:
return new Geom::Interpolate::Linear();
}
diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp
index acf8ab6a5..f6bc1de65 100644
--- a/src/live_effects/lpe-powerstroke.cpp
+++ b/src/live_effects/lpe-powerstroke.cpp
@@ -15,6 +15,11 @@
#include "sp-shape.h"
#include "style.h"
+#include "xml/repr.h"
+#include "sp-paint-server.h"
+#include "svg/svg-color.h"
+#include "desktop-style.h"
+#include "svg/css-ostringstream.h"
#include "display/curve.h"
#include <2geom/path.h>
@@ -185,6 +190,7 @@ namespace Inkscape {
namespace LivePathEffect {
static const Util::EnumData<unsigned> InterpolatorTypeData[] = {
+ {Geom::Interpolate::INTERP_CUBICBEZIER_SMOOTH, N_("CubicBezierSmooth"), "CubicBezierSmooth"},
{Geom::Interpolate::INTERP_LINEAR , N_("Linear"), "Linear"},
{Geom::Interpolate::INTERP_CUBICBEZIER , N_("CubicBezierFit"), "CubicBezierFit"},
{Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN , N_("CubicBezierJohan"), "CubicBezierJohan"},
@@ -222,9 +228,7 @@ static const Util::EnumData<unsigned> LineJoinTypeData[] = {
{LINEJOIN_EXTRP_MITER, N_("Extrapolated"), "extrapolated"},
{LINEJOIN_MITER, N_("Miter"), "miter"},
{LINEJOIN_SPIRO, N_("Spiro"), "spiro"},
-#ifdef LPE_ENABLE_TEST_EFFECTS
{LINEJOIN_EXTRP_MITER_ARC, N_("Extrapolated arc"), "extrp_arc"},
-#endif
};
static const Util::EnumDataConverter<unsigned> LineJoinTypeConverter(LineJoinTypeData, sizeof(LineJoinTypeData)/sizeof(*LineJoinTypeData));
@@ -232,12 +236,12 @@ LPEPowerStroke::LPEPowerStroke(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
offset_points(_("Offset points"), _("Offset points"), "offset_points", &wr, this),
sort_points(_("Sort points"), _("Sort offset points according to their time value along the curve"), "sort_points", &wr, this, true),
- interpolator_type(_("Interpolator type:"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path"), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER_JOHAN),
+ interpolator_type(_("Interpolator type:"), _("Determines which kind of interpolator will be used to interpolate between stroke width along the path"), "interpolator_type", InterpolatorTypeConverter, &wr, this, Geom::Interpolate::INTERP_CUBICBEZIER),
interpolator_beta(_("Smoothness:"), _("Sets the smoothness for the CubicBezierJohan interpolator; 0 = linear interpolation, 1 = smooth"), "interpolator_beta", &wr, this, 0.2),
- start_linecap_type(_("Start cap:"), _("Determines the shape of the path's start"), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND),
- linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_ROUND),
+ start_linecap_type(_("Start cap:"), _("Determines the shape of the path's start"), "start_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_BUTT),
+ linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", LineJoinTypeConverter, &wr, this, LINEJOIN_EXTRP_MITER_ARC),
miter_limit(_("Miter limit:"), _("Maximum length of the miter (in units of stroke width)"), "miter_limit", &wr, this, 4.),
- end_linecap_type(_("End cap:"), _("Determines the shape of the path's end"), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_ROUND)
+ end_linecap_type(_("End cap:"), _("Determines the shape of the path's end"), "end_linecap_type", LineCapTypeConverter, &wr, this, LINECAP_BUTT)
{
show_orig_path = true;
@@ -266,20 +270,52 @@ void
LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem)
{
if (SP_IS_SHAPE(lpeitem)) {
+ SPLPEItem* item = const_cast<SPLPEItem*>(lpeitem);
std::vector<Geom::Point> points;
Geom::PathVector const &pathv = SP_SHAPE(lpeitem)->_curve->get_pathvector();
- double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed : 1.;
+ double width = (lpeitem && lpeitem->style) ? lpeitem->style->stroke_width.computed / 2 : 1.;
+
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ if (lpeitem->style->stroke.isSet()) {
+ if (lpeitem->style->stroke.isPaintserver()) {
+ SPPaintServer * server = lpeitem->style->getStrokePaintServer();
+ if (server) {
+ Glib::ustring str;
+ str += "url(#";
+ str += server->getId();
+ str += ")";
+ sp_repr_css_set_property (css, "fill", str.c_str());
+ }
+ } else if (lpeitem->style->stroke.isColor()) {
+ gchar c[64];
+ sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
+ sp_repr_css_set_property (css, "fill", c);
+ } else {
+ sp_repr_css_set_property (css, "fill", "none");
+ }
+ } else {
+ sp_repr_css_unset_property (css, "fill");
+ }
+
+ sp_repr_css_set_property(css, "stroke", "none");
+
+ sp_desktop_apply_css_recursive(item, css, true);
+ sp_repr_css_attr_unref (css);
+
+ item->updateRepr();
if (pathv.empty()) {
- points.push_back( Geom::Point(0.,width) );
+ points.push_back( Geom::Point(0.2,width) );
points.push_back( Geom::Point(0.5,width) );
- points.push_back( Geom::Point(1.,width) );
+ points.push_back( Geom::Point(0.8,width) );
} else {
Geom::Path const &path = pathv.front();
Geom::Path::size_type const size = path.size_default();
- points.push_back( Geom::Point(0.,width) );
+ if (!path.closed()) {
+ points.push_back( Geom::Point(0.2,width) );
+ }
points.push_back( Geom::Point(0.5*size,width) );
if (!path.closed()) {
- points.push_back( Geom::Point(size,width) );
+ points.push_back( Geom::Point(size - 0.2,width) );
}
}
offset_points.param_set_and_write_new_value(points);
@@ -288,6 +324,45 @@ LPEPowerStroke::doOnApply(SPLPEItem const* lpeitem)
}
}
+void LPEPowerStroke::doOnRemove(SPLPEItem const* lpeitem)
+{
+ if (SP_IS_SHAPE(lpeitem)) {
+ SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem);
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ if (lpeitem->style->fill.isSet()) {
+ if (lpeitem->style->fill.isPaintserver()) {
+ SPPaintServer * server = lpeitem->style->getFillPaintServer();
+ if (server) {
+ Glib::ustring str;
+ str += "url(#";
+ str += server->getId();
+ str += ")";
+ sp_repr_css_set_property (css, "stroke", str.c_str());
+ }
+ } else if (lpeitem->style->fill.isColor()) {
+ gchar c[64];
+ sp_svg_write_color (c, sizeof(c), lpeitem->style->stroke.value.color.toRGBA32(SP_SCALE24_TO_FLOAT(lpeitem->style->stroke_opacity.value)));
+ sp_repr_css_set_property (css, "stroke", c);
+ } else {
+ sp_repr_css_set_property (css, "stroke", "none");
+ }
+ } else {
+ sp_repr_css_unset_property (css, "stroke");
+ }
+
+ Inkscape::CSSOStringStream os;
+ os << offset_points.median_width() * 2;
+ sp_repr_css_set_property (css, "stroke-width", os.str().c_str());
+
+ sp_repr_css_set_property(css, "fill", "none");
+
+ sp_desktop_apply_css_recursive(item, css, true);
+ sp_repr_css_attr_unref (css);
+
+ item->updateRepr();
+ }
+}
+
void
LPEPowerStroke::adjustForNewPath(std::vector<Geom::Path> const & path_in)
{
diff --git a/src/live_effects/lpe-powerstroke.h b/src/live_effects/lpe-powerstroke.h
index 7bc736820..a773434aa 100644
--- a/src/live_effects/lpe-powerstroke.h
+++ b/src/live_effects/lpe-powerstroke.h
@@ -25,9 +25,11 @@ public:
LPEPowerStroke(LivePathEffectObject *lpeobject);
virtual ~LPEPowerStroke();
+
virtual std::vector<Geom::Path> doEffect_path (std::vector<Geom::Path> const & path_in);
virtual void doOnApply(SPLPEItem const* lpeitem);
+ virtual void doOnRemove(SPLPEItem const* lpeitem);
// methods called by path-manipulator upon edits
void adjustForNewPath(std::vector<Geom::Path> const & path_in);
diff --git a/src/live_effects/parameter/Makefile_insert b/src/live_effects/parameter/Makefile_insert
index efdda686a..37cc3dc62 100644
--- a/src/live_effects/parameter/Makefile_insert
+++ b/src/live_effects/parameter/Makefile_insert
@@ -18,10 +18,14 @@ ink_common_sources += \
live_effects/parameter/path.h \
live_effects/parameter/originalpath.cpp \
live_effects/parameter/originalpath.h \
+ live_effects/parameter/originalpatharray.cpp \
+ live_effects/parameter/originalpatharray.h \
live_effects/parameter/powerstrokepointarray.cpp \
live_effects/parameter/powerstrokepointarray.h \
live_effects/parameter/text.cpp \
live_effects/parameter/text.h \
+ live_effects/parameter/transformedpoint.cpp \
+ live_effects/parameter/transformedpoint.h \
live_effects/parameter/unit.cpp \
live_effects/parameter/unit.h \
live_effects/parameter/vector.cpp \
diff --git a/src/live_effects/parameter/originalpatharray.cpp b/src/live_effects/parameter/originalpatharray.cpp
new file mode 100644
index 000000000..29e4c409c
--- /dev/null
+++ b/src/live_effects/parameter/originalpatharray.cpp
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/parameter/originalpatharray.h"
+
+#include <gtkmm/widget.h>
+#include <gtkmm/icontheme.h>
+#include <gtkmm/imagemenuitem.h>
+#include <gtkmm/separatormenuitem.h>
+
+#include <glibmm/i18n.h>
+
+#include "inkscape.h"
+#include "icon-size.h"
+#include "widgets/icon.h"
+#include "ui/clipboard.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "originalpath.h"
+#include "uri.h"
+#include "display/curve.h"
+
+#include <glibmm/i18n.h>
+#include <2geom/coord.h>
+#include <2geom/point.h>
+#include "sp-shape.h"
+#include "sp-text.h"
+#include "live_effects/effect.h"
+
+#include "verbs.h"
+#include "document-undo.h"
+#include "document.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class OriginalPathArrayParam::ModelColumns : public Gtk::TreeModel::ColumnRecord
+{
+public:
+
+ ModelColumns()
+ {
+ add(_colObject);
+ add(_colLabel);
+ add(_colReverse);
+ }
+ virtual ~ModelColumns() {}
+
+ Gtk::TreeModelColumn<PathAndDirection*> _colObject;
+ Gtk::TreeModelColumn<Glib::ustring> _colLabel;
+ Gtk::TreeModelColumn<bool> _colReverse;
+};
+
+OriginalPathArrayParam::OriginalPathArrayParam( 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),
+ _vector(),
+ _tree(),
+ _text_renderer(),
+ _toggle_renderer(),
+ _scroller()
+{
+ _model = new ModelColumns();
+ _store = Gtk::TreeStore::create(*_model);
+ _tree.set_model(_store);
+
+ _tree.set_reorderable(true);
+ _tree.enable_model_drag_dest (Gdk::ACTION_MOVE);
+
+ _text_renderer = manage(new Gtk::CellRendererText());
+ int nameColNum = _tree.append_column(_("Name"), *_text_renderer) - 1;
+ _name_column = _tree.get_column(nameColNum);
+ _name_column->add_attribute(_text_renderer->property_text(), _model->_colLabel);
+
+ _tree.set_expander_column( *_tree.get_column(nameColNum) );
+ _tree.set_search_column(_model->_colLabel);
+
+ Gtk::CellRendererToggle * _toggle_renderer = manage(new Gtk::CellRendererToggle());
+ int toggleColNum = _tree.append_column(_("Reverse"), *_toggle_renderer) - 1;
+ Gtk::TreeViewColumn* col = _tree.get_column(toggleColNum);
+ _toggle_renderer->set_activatable(true);
+ _toggle_renderer->signal_toggled().connect(sigc::mem_fun(*this, &OriginalPathArrayParam::on_reverse_toggled));
+ col->add_attribute(_toggle_renderer->property_active(), _model->_colReverse);
+
+ _scroller.add(_tree);
+ _scroller.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC );
+ _scroller.set_shadow_type(Gtk::SHADOW_IN);
+
+ oncanvas_editable = true;
+
+}
+
+OriginalPathArrayParam::~OriginalPathArrayParam()
+{
+ while (!_vector.empty()) {
+ PathAndDirection *w = _vector.back();
+ _vector.pop_back();
+ unlink(w);
+ delete w;
+ }
+ delete _model;
+}
+
+void OriginalPathArrayParam::on_reverse_toggled(const Glib::ustring& path)
+{
+ Gtk::TreeModel::iterator iter = _store->get_iter(path);
+ Gtk::TreeModel::Row row = *iter;
+ PathAndDirection *w = row[_model->_colObject];
+ row[_model->_colReverse] = !row[_model->_colReverse];
+ w->reversed = row[_model->_colReverse];
+
+ gchar * full = param_getSVGValue();
+ param_write_to_repr(full);
+ g_free(full);
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link path parameter to path"));
+}
+
+void OriginalPathArrayParam::param_set_default()
+{
+
+}
+
+Gtk::Widget* OriginalPathArrayParam::param_newWidget()
+{
+ Gtk::VBox* vbox = Gtk::manage(new Gtk::VBox());
+ Gtk::HBox* hbox = Gtk::manage(new Gtk::HBox());
+
+ vbox->pack_start(_scroller, Gtk::PACK_EXPAND_WIDGET);
+
+
+ { // Paste path to link button
+ Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_PASTE, 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, &OriginalPathArrayParam::on_link_button_click));
+ hbox->pack_start(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Link to path"));
+ }
+
+ { // Remove linked path
+ Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_REMOVE, 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, &OriginalPathArrayParam::on_remove_button_click));
+ hbox->pack_start(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Remove Path"));
+ }
+
+ { // Move Down
+ Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_GO_DOWN, 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, &OriginalPathArrayParam::on_down_button_click));
+ hbox->pack_end(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Move Down"));
+ }
+
+ { // Move Down
+ Gtk::Widget *pIcon = Gtk::manage( sp_icon_get_icon( GTK_STOCK_GO_UP, 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, &OriginalPathArrayParam::on_up_button_click));
+ hbox->pack_end(*pButton, Gtk::PACK_SHRINK);
+ pButton->set_tooltip_text(_("Move Up"));
+ }
+
+ vbox->pack_end(*hbox, Gtk::PACK_SHRINK);
+
+ vbox->show_all_children(true);
+
+ return vbox;
+}
+
+bool OriginalPathArrayParam::_selectIndex(const Gtk::TreeIter& iter, int* i)
+{
+ if ((*i)-- <= 0) {
+ _tree.get_selection()->select(iter);
+ return true;
+ }
+ return false;
+}
+
+void OriginalPathArrayParam::on_up_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+
+ int i = -1;
+ std::vector<PathAndDirection*>::iterator piter = _vector.begin();
+ for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); piter = iter, i++, iter++) {
+ if (*iter == row[_model->_colObject]) {
+ _vector.erase(iter);
+ _vector.insert(piter, row[_model->_colObject]);
+ break;
+ }
+ }
+
+ gchar * full = param_getSVGValue();
+ param_write_to_repr(full);
+ g_free(full);
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Move path up"));
+
+ _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_selectIndex), &i));
+ }
+}
+
+void OriginalPathArrayParam::on_down_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+
+ int i = 0;
+ for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); i++, iter++) {
+ if (*iter == row[_model->_colObject]) {
+ std::vector<PathAndDirection*>::iterator niter = _vector.erase(iter);
+ if (niter != _vector.end()) {
+ niter++;
+ i++;
+ }
+ _vector.insert(niter, row[_model->_colObject]);
+ break;
+ }
+ }
+
+ gchar * full = param_getSVGValue();
+ param_write_to_repr(full);
+ g_free(full);
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Move path down"));
+
+ _store->foreach_iter(sigc::bind<int*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_selectIndex), &i));
+ }
+}
+
+void OriginalPathArrayParam::on_remove_button_click()
+{
+ Gtk::TreeModel::iterator iter = _tree.get_selection()->get_selected();
+ if (iter) {
+ Gtk::TreeModel::Row row = *iter;
+ remove_link(row[_model->_colObject]);
+
+ gchar * full = param_getSVGValue();
+ param_write_to_repr(full);
+ g_free(full);
+
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Remove path"));
+ }
+
+}
+
+void
+OriginalPathArrayParam::on_link_button_click()
+{
+ Inkscape::UI::ClipboardManager *cm = Inkscape::UI::ClipboardManager::get();
+ Glib::ustring pathid = cm->getShapeOrTextObjectId(SP_ACTIVE_DESKTOP);
+
+ if (pathid == "") {
+ return;
+ }
+ // add '#' at start to make it an uri.
+ pathid.insert(pathid.begin(), '#');
+
+ Inkscape::SVGOStringStream os;
+ bool foundOne = false;
+ for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
+ if (foundOne) {
+ os << "|";
+ } else {
+ foundOne = true;
+ }
+ os << (*iter)->href << "," << ((*iter)->reversed ? "1" : "0");
+ }
+
+ if (foundOne) {
+ os << "|";
+ }
+
+ os << pathid.c_str() << ",0";
+
+ param_write_to_repr(os.str().c_str());
+ DocumentUndo::done(param_effect->getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT,
+ _("Link path parameter to path"));
+}
+
+void OriginalPathArrayParam::unlink(PathAndDirection* to)
+{
+ to->linked_modified_connection.disconnect();
+ to->linked_delete_connection.disconnect();
+ to->ref.detach();
+ to->_pathvector = Geom::PathVector();
+ if (to->href) {
+ g_free(to->href);
+ to->href = NULL;
+ }
+}
+
+void OriginalPathArrayParam::remove_link(PathAndDirection* to)
+{
+ unlink(to);
+ for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
+ if (*iter == to) {
+ PathAndDirection *w = *iter;
+ _vector.erase(iter);
+ delete w;
+ return;
+ }
+ }
+}
+
+void OriginalPathArrayParam::linked_delete(SPObject */*deleted*/, PathAndDirection* to)
+{
+ //remove_link(to);
+
+ gchar * full = param_getSVGValue();
+ param_write_to_repr(full);
+ g_free(full);
+}
+
+bool OriginalPathArrayParam::_updateLink(const Gtk::TreeIter& iter, PathAndDirection* pd)
+{
+ Gtk::TreeModel::Row row = *iter;
+ if (row[_model->_colObject] == pd) {
+ SPObject *obj = pd->ref.getObject();
+ row[_model->_colLabel] = obj && obj->getId() ? ( obj->label() ? obj->label() : obj->getId() ) : pd->href;
+ return true;
+ }
+ return false;
+}
+
+void OriginalPathArrayParam::linked_changed(SPObject */*old_obj*/, SPObject *new_obj, PathAndDirection* to)
+{
+ to->linked_delete_connection.disconnect();
+ to->linked_modified_connection.disconnect();
+ to->linked_transformed_connection.disconnect();
+
+ if (new_obj && SP_IS_ITEM(new_obj)) {
+ to->linked_delete_connection = new_obj->connectDelete(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_delete), to));
+ to->linked_modified_connection = new_obj->connectModified(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_modified), to));
+ to->linked_transformed_connection = SP_ITEM(new_obj)->connectTransformed(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_transformed), to));
+
+ linked_modified(new_obj, SP_OBJECT_MODIFIED_FLAG, to);
+ } else {
+ to->_pathvector = Geom::PathVector();
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ _store->foreach_iter(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_updateLink), to));
+ }
+}
+
+void OriginalPathArrayParam::linked_transformed(Geom::Affine const *mp, SPItem* original, PathAndDirection* to)
+{
+
+}
+
+void OriginalPathArrayParam::setPathVector(SPObject *linked_obj, guint flags, PathAndDirection* to)
+{
+ if (!to) {
+ return;
+ }
+ SPCurve *curve = NULL;
+ if (SP_IS_SHAPE(linked_obj)) {
+ curve = SP_SHAPE(linked_obj)->getCurveBeforeLPE();
+ }
+ if (SP_IS_TEXT(linked_obj)) {
+ curve = SP_TEXT(linked_obj)->getNormalizedBpath();
+ }
+
+ if (curve == NULL) {
+ // curve invalid, set empty pathvector
+ to->_pathvector = Geom::PathVector();
+ } else {
+ to->_pathvector = curve->get_pathvector();
+ curve->unref();
+ }
+}
+
+void OriginalPathArrayParam::linked_modified(SPObject *linked_obj, guint flags, PathAndDirection* to)
+{
+ if (!to) {
+ return;
+ }
+ setPathVector(linked_obj, flags, to);
+ SP_OBJECT(param_effect->getLPEObj())->requestModified(SP_OBJECT_MODIFIED_FLAG);
+ _store->foreach_iter(sigc::bind<PathAndDirection*>(sigc::mem_fun(*this, &OriginalPathArrayParam::_updateLink), to));
+}
+
+//void PathParam::linked_transformed(Geom::Affine const *rel_transf, SPItem *moved_item)
+//{
+// linked_transformed_callback(rel_transf, moved_item);
+//}
+
+bool OriginalPathArrayParam::param_readSVGValue(const gchar* strvalue)
+{
+ if (strvalue) {
+ while (!_vector.empty()) {
+ PathAndDirection *w = _vector.back();
+ unlink(w);
+ _vector.pop_back();
+ delete w;
+ }
+ _store->clear();
+
+ gchar ** strarray = g_strsplit(strvalue, "|", 0);
+ for (gchar ** iter = strarray; *iter != NULL; iter++) {
+ if ((*iter)[0] == '#') {
+ gchar ** substrarray = g_strsplit(*iter, ",", 0);
+ PathAndDirection* w = new PathAndDirection((SPObject *)param_effect->getLPEObj());
+ w->href = g_strdup(*substrarray);
+ w->reversed = *(substrarray+1) != NULL && (*(substrarray+1))[0] == '1';
+
+ w->linked_changed_connection = w->ref.changedSignal().connect(sigc::bind<PathAndDirection *>(sigc::mem_fun(*this, &OriginalPathArrayParam::linked_changed), w));
+ w->ref.attach(URI(w->href));
+
+ _vector.push_back(w);
+
+ Gtk::TreeModel::iterator iter = _store->append();
+ Gtk::TreeModel::Row row = *iter;
+ SPObject *obj = w->ref.getObject();
+
+ row[_model->_colObject] = w;
+ row[_model->_colLabel] = obj ? ( obj->label() ? obj->label() : obj->getId() ) : w->href;
+ row[_model->_colReverse] = w->reversed;
+ g_strfreev (substrarray);
+ }
+ }
+ g_strfreev (strarray);
+ return true;
+ }
+ return false;
+}
+
+gchar * OriginalPathArrayParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ bool foundOne = false;
+ for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
+ if (foundOne) {
+ os << "|";
+ } else {
+ foundOne = true;
+ }
+ os << (*iter)->href << "," << ((*iter)->reversed ? "1" : "0");
+ }
+ gchar * str = g_strdup(os.str().c_str());
+ return str;
+}
+
+} /* 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/originalpatharray.h b/src/live_effects/parameter/originalpatharray.h
new file mode 100644
index 000000000..865a3f8e5
--- /dev/null
+++ b/src/live_effects/parameter/originalpatharray.h
@@ -0,0 +1,123 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+* Copyright (C) Johan Engelen 2008 <j.b.c.engelen@utwente.nl>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <vector>
+
+#include <gtkmm/box.h>
+#include <gtkmm/treeview.h>
+#include <gtkmm/treestore.h>
+#include <gtkmm/scrolledwindow.h>
+
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/path-reference.h"
+
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+#include "path-reference.h"
+#include "sp-object.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+class PathAndDirection {
+public:
+ PathAndDirection(SPObject *owner)
+ : href(NULL),
+ ref(owner),
+ _pathvector(Geom::PathVector()),
+ reversed(false)
+ {
+
+ }
+ gchar *href;
+ URIReference ref;
+ //SPItem *obj;
+ std::vector<Geom::Path> _pathvector;
+ bool reversed;
+
+ sigc::connection linked_changed_connection;
+ sigc::connection linked_delete_connection;
+ sigc::connection linked_modified_connection;
+ sigc::connection linked_transformed_connection;
+};
+
+class OriginalPathArrayParam : public Parameter {
+public:
+ class ModelColumns;
+
+ OriginalPathArrayParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect);
+
+ virtual ~OriginalPathArrayParam();
+
+ virtual Gtk::Widget * param_newWidget();
+ virtual bool param_readSVGValue(const gchar * strvalue);
+ virtual gchar * param_getSVGValue() const;
+ virtual void param_set_default();
+
+ /** Disable the canvas indicators of parent class by overriding this method */
+ virtual void param_editOncanvas(SPItem * /*item*/, SPDesktop * /*dt*/) {};
+ /** Disable the canvas indicators of parent class by overriding this method */
+ virtual void addCanvasIndicators(SPLPEItem const* /*lpeitem*/, std::vector<Geom::PathVector> & /*hp_vec*/) {};
+
+ std::vector<PathAndDirection*> _vector;
+
+protected:
+ bool _updateLink(const Gtk::TreeIter& iter, PathAndDirection* pd);
+ bool _selectIndex(const Gtk::TreeIter& iter, int* i);
+ void unlink(PathAndDirection* to);
+ void remove_link(PathAndDirection* to);
+ void setPathVector(SPObject *linked_obj, guint flags, PathAndDirection* to);
+
+ void linked_changed(SPObject *old_obj, SPObject *new_obj, PathAndDirection* to);
+ void linked_modified(SPObject *linked_obj, guint flags, PathAndDirection* to);
+ void linked_transformed(Geom::Affine const *mp, SPItem *original, PathAndDirection* to);
+ void linked_delete(SPObject *deleted, PathAndDirection* to);
+
+ ModelColumns *_model;
+ Glib::RefPtr<Gtk::TreeStore> _store;
+ Gtk::TreeView _tree;
+ Gtk::CellRendererText *_text_renderer;
+ Gtk::CellRendererToggle *_toggle_renderer;
+ Gtk::TreeView::Column *_name_column;
+ Gtk::ScrolledWindow _scroller;
+
+ void on_link_button_click();
+ void on_remove_button_click();
+ void on_up_button_click();
+ void on_down_button_click();
+ void on_reverse_toggled(const Glib::ustring& path);
+
+private:
+ OriginalPathArrayParam(const OriginalPathArrayParam&);
+ OriginalPathArrayParam& operator=(const OriginalPathArrayParam&);
+};
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#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 :
diff --git a/src/live_effects/parameter/powerstrokepointarray.cpp b/src/live_effects/parameter/powerstrokepointarray.cpp
index fecdfeda8..ac0000b1a 100644
--- a/src/live_effects/parameter/powerstrokepointarray.cpp
+++ b/src/live_effects/parameter/powerstrokepointarray.cpp
@@ -6,6 +6,7 @@
#include <glibmm/i18n.h>
+#include "ui/dialog/lpe-powerstroke-properties.h"
#include "live_effects/parameter/powerstrokepointarray.h"
#include "live_effects/effect.h"
@@ -102,6 +103,23 @@ PowerStrokePointArrayParam::recalculate_controlpoints_for_new_pwd2(Geom::Piecewi
}
}
+float PowerStrokePointArrayParam::median_width()
+{
+ size_t size = _vector.size();
+ if (size > 0)
+ {
+ if (size % 2 == 0)
+ {
+ return (_vector[size / 2 - 1].y() + _vector[size / 2].y()) / 2;
+ }
+ else
+ {
+ return _vector[size / 2].y();
+ }
+ }
+ return 1;
+}
+
void
PowerStrokePointArrayParam::set_pwd2(Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in, Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_normal_in)
{
@@ -117,7 +135,7 @@ PowerStrokePointArrayParam::set_oncanvas_looks(SPKnotShapeType shape, SPKnotMode
knot_mode = mode;
knot_color = color;
}
-
+/*
class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity {
public:
PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index);
@@ -127,7 +145,7 @@ public:
virtual Geom::Point knot_get() const;
virtual void knot_click(guint state);
- /** Checks whether the index falls within the size of the parameter's vector */
+ /** Checks whether the index falls within the size of the parameter's vector /
bool valid_index(unsigned int index) const {
return (_pparam->_vector.size() > index);
};
@@ -135,7 +153,7 @@ public:
private:
PowerStrokePointArrayParam *_pparam;
unsigned int _index;
-};
+};*/
PowerStrokePointArrayParamKnotHolderEntity::PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index)
: _pparam(p),
@@ -181,6 +199,12 @@ PowerStrokePointArrayParamKnotHolderEntity::knot_get() const
return canvas_point;
}
+void PowerStrokePointArrayParamKnotHolderEntity::knot_set_offset(Geom::Point offset)
+{
+ _pparam->_vector.at(_index) = Geom::Point(offset.x(), offset.y() / 2);
+ this->parent_holder->knot_ungrabbed_handler(this->knot);
+}
+
void
PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state)
{
@@ -223,10 +247,15 @@ PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state)
// add knot to knotholder
PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(_pparam, _index+1);
e->create( this->desktop, this->item, parent_holder, Inkscape::CTRL_TYPE_UNKNOWN,
- _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it."),
+ _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it, <b>Shift+click</b> launches width dialog."),
_pparam->knot_shape, _pparam->knot_mode, _pparam->knot_color);
parent_holder->add(e);
}
+ }
+ else if ((state & GDK_MOD1_MASK) || (state & GDK_SHIFT_MASK))
+ {
+ Geom::Point offset = Geom::Point(_pparam->_vector.at(_index).x(), _pparam->_vector.at(_index).y() * 2);
+ Inkscape::UI::Dialogs::PowerstrokePropertiesDialog::showDialog(this->desktop, offset, this);
}
}
@@ -235,7 +264,7 @@ void PowerStrokePointArrayParam::addKnotHolderEntities(KnotHolder *knotholder, S
for (unsigned int i = 0; i < _vector.size(); ++i) {
PowerStrokePointArrayParamKnotHolderEntity *e = new PowerStrokePointArrayParamKnotHolderEntity(this, i);
e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,
- _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it."),
+ _("<b>Stroke width control point</b>: drag to alter the stroke width. <b>Ctrl+click</b> adds a control point, <b>Ctrl+Alt+click</b> deletes it, <b>Shift+click</b> launches width dialog."),
knot_shape, knot_mode, knot_color);
knotholder->add(e);
}
diff --git a/src/live_effects/parameter/powerstrokepointarray.h b/src/live_effects/parameter/powerstrokepointarray.h
index e1fa440f2..911bbc82d 100644
--- a/src/live_effects/parameter/powerstrokepointarray.h
+++ b/src/live_effects/parameter/powerstrokepointarray.h
@@ -20,8 +20,6 @@ namespace Inkscape {
namespace LivePathEffect {
-class PowerStrokePointArrayParamKnotHolderEntity;
-
class PowerStrokePointArrayParam : public ArrayParam<Geom::Point> {
public:
PowerStrokePointArrayParam( const Glib::ustring& label,
@@ -37,6 +35,8 @@ public:
void set_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+ float median_width();
+
virtual bool providesKnotHolderEntities() const { return true; }
virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);
@@ -60,6 +60,25 @@ private:
Geom::Piecewise<Geom::D2<Geom::SBasis> > last_pwd2_normal;
};
+class PowerStrokePointArrayParamKnotHolderEntity : public KnotHolderEntity {
+public:
+ PowerStrokePointArrayParamKnotHolderEntity(PowerStrokePointArrayParam *p, unsigned int index);
+ virtual ~PowerStrokePointArrayParamKnotHolderEntity() {}
+
+ virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
+ virtual Geom::Point knot_get() const;
+ virtual void knot_set_offset(Geom::Point offset);
+ virtual void knot_click(guint state);
+
+ /** Checks whether the index falls within the size of the parameter's vector */
+ bool valid_index(unsigned int index) const {
+ return (_pparam->_vector.size() > index);
+ };
+
+private:
+ PowerStrokePointArrayParam *_pparam;
+ unsigned int _index;
+};
} //namespace LivePathEffect
diff --git a/src/live_effects/parameter/transformedpoint.cpp b/src/live_effects/parameter/transformedpoint.cpp
new file mode 100644
index 000000000..f5b01e267
--- /dev/null
+++ b/src/live_effects/parameter/transformedpoint.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glibmm/i18n.h>
+
+#include "ui/widget/registered-widget.h"
+#include "live_effects/parameter/transformedpoint.h"
+#include "sp-lpe-item.h"
+#include "knotholder.h"
+#include "svg/svg.h"
+#include "svg/stringstream.h"
+
+#include "live_effects/effect.h"
+#include "desktop.h"
+#include "verbs.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+TransformedPointParam::TransformedPointParam( const Glib::ustring& label, const Glib::ustring& tip,
+ const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
+ Effect* effect, Geom::Point default_vector,
+ bool dontTransform)
+ : Parameter(label, tip, key, wr, effect),
+ defvalue(default_vector),
+ origin(0.,0.),
+ vector(default_vector),
+ noTransform(dontTransform)
+{
+ vec_knot_shape = SP_KNOT_SHAPE_DIAMOND;
+ vec_knot_mode = SP_KNOT_MODE_XOR;
+ vec_knot_color = 0xffffb500;
+}
+
+TransformedPointParam::~TransformedPointParam()
+{
+
+}
+
+void
+TransformedPointParam::param_set_default()
+{
+ setOrigin(Geom::Point(0.,0.));
+ setVector(defvalue);
+}
+
+bool
+TransformedPointParam::param_readSVGValue(const gchar * strvalue)
+{
+ gchar ** strarray = g_strsplit(strvalue, ",", 4);
+ if (!strarray) {
+ return false;
+ }
+ double val[4];
+ unsigned int i = 0;
+ while (i < 4 && strarray[i]) {
+ if (sp_svg_number_read_d(strarray[i], &val[i]) != 0) {
+ i++;
+ } else {
+ break;
+ }
+ }
+ g_strfreev (strarray);
+ if (i == 4) {
+ setOrigin( Geom::Point(val[0], val[1]) );
+ setVector( Geom::Point(val[2], val[3]) );
+ return true;
+ }
+ return false;
+}
+
+gchar *
+TransformedPointParam::param_getSVGValue() const
+{
+ Inkscape::SVGOStringStream os;
+ os << origin << " , " << vector;
+ gchar * str = g_strdup(os.str().c_str());
+ return str;
+}
+
+Gtk::Widget *
+TransformedPointParam::param_newWidget()
+{
+ Inkscape::UI::Widget::RegisteredVector * pointwdg = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredVector( param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ param_effect->getRepr(),
+ param_effect->getSPDoc() ) );
+ pointwdg->setPolarCoords();
+ pointwdg->setValue( vector, origin );
+ pointwdg->clearProgrammatically();
+ pointwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change vector parameter"));
+
+ Gtk::HBox * hbox = Gtk::manage( new Gtk::HBox() );
+ static_cast<Gtk::HBox*>(hbox)->pack_start(*pointwdg, true, true);
+ static_cast<Gtk::HBox*>(hbox)->show_all_children();
+
+ return dynamic_cast<Gtk::Widget *> (hbox);
+}
+
+void
+TransformedPointParam::set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector)
+{
+ setValues(new_origin, new_vector);
+ gchar * str = param_getSVGValue();
+ param_write_to_repr(str);
+ g_free(str);
+}
+
+void
+TransformedPointParam::param_transform_multiply(Geom::Affine const& postmul, bool /*set*/)
+{
+ if (!noTransform) {
+ set_and_write_new_values( origin * postmul, vector * postmul.withoutTranslation() );
+ }
+}
+
+
+void
+TransformedPointParam::set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color)
+{
+ vec_knot_shape = shape;
+ vec_knot_mode = mode;
+ vec_knot_color = color;
+}
+
+void
+TransformedPointParam::set_oncanvas_color(guint32 color)
+{
+ vec_knot_color = color;
+}
+
+class TransformedPointParamKnotHolderEntity_Vector : public KnotHolderEntity {
+public:
+ TransformedPointParamKnotHolderEntity_Vector(TransformedPointParam *p) : param(p) { }
+ virtual ~TransformedPointParamKnotHolderEntity_Vector() {}
+
+ virtual void knot_set(Geom::Point const &p, Geom::Point const &/*origin*/, guint /*state*/) {
+ Geom::Point const s = p - param->origin;
+ /// @todo implement angle snapping when holding CTRL
+ param->setVector(s);
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, false);
+ };
+ virtual Geom::Point knot_get() const{
+ return param->origin + param->vector;
+ };
+ virtual void knot_click(guint /*state*/){
+ g_print ("This is the vector handle associated to parameter '%s'\n", param->param_key.c_str());
+ };
+
+private:
+ TransformedPointParam *param;
+};
+
+void
+TransformedPointParam::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item)
+{
+ TransformedPointParamKnotHolderEntity_Vector *vector_e = new TransformedPointParamKnotHolderEntity_Vector(this);
+ vector_e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, handleTip(), vec_knot_shape, vec_knot_mode, vec_knot_color);
+ knotholder->add(vector_e);
+}
+
+} /* 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/transformedpoint.h b/src/live_effects/parameter/transformedpoint.h
new file mode 100644
index 000000000..37af8b98f
--- /dev/null
+++ b/src/live_effects/parameter/transformedpoint.h
@@ -0,0 +1,87 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_TRANSFORMED_POINT_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_TRANSFORMED_POINT_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <glib.h>
+#include <2geom/point.h>
+
+#include "live_effects/parameter/parameter.h"
+
+#include "knot-holder-entity.h"
+
+namespace Inkscape {
+
+namespace LivePathEffect {
+
+
+class TransformedPointParam : public Parameter {
+public:
+ TransformedPointParam( const Glib::ustring& label,
+ const Glib::ustring& tip,
+ const Glib::ustring& key,
+ Inkscape::UI::Widget::Registry* wr,
+ Effect* effect,
+ Geom::Point default_vector = Geom::Point(1,0),
+ bool dontTransform = false);
+ virtual ~TransformedPointParam();
+
+ virtual Gtk::Widget * param_newWidget();
+ inline const gchar *handleTip() const { return param_tooltip.c_str(); }
+
+ virtual bool param_readSVGValue(const gchar * strvalue);
+ virtual gchar * param_getSVGValue() const;
+
+ Geom::Point getVector() const { return vector; };
+ Geom::Point getOrigin() const { return origin; };
+ void setValues(Geom::Point const &new_origin, Geom::Point const &new_vector) { setVector(new_vector); setOrigin(new_origin); };
+ void setVector(Geom::Point const &new_vector) { vector = new_vector; };
+ void setOrigin(Geom::Point const &new_origin) { origin = new_origin; };
+ virtual void param_set_default();
+
+ void set_and_write_new_values(Geom::Point const &new_origin, Geom::Point const &new_vector);
+
+ virtual void param_transform_multiply(Geom::Affine const &postmul, bool set);
+
+ void set_vector_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+ //void set_origin_oncanvas_looks(SPKnotShapeType shape, SPKnotModeType mode, guint32 color);
+ void set_oncanvas_color(guint32 color);
+
+ virtual bool providesKnotHolderEntities() const { return true; }
+ virtual void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);
+
+private:
+ TransformedPointParam(const TransformedPointParam&);
+ TransformedPointParam& operator=(const TransformedPointParam&);
+
+ Geom::Point defvalue;
+
+ Geom::Point origin;
+ Geom::Point vector;
+
+ bool noTransform;
+
+ /// The looks of the vector and origin knots oncanvas
+ SPKnotShapeType vec_knot_shape;
+ SPKnotModeType vec_knot_mode;
+ guint32 vec_knot_color;
+// SPKnotShapeType ori_knot_shape;
+// SPKnotModeType ori_knot_mode;
+// guint32 ori_knot_color;
+
+// friend class VectorParamKnotHolderEntity_Origin;
+ friend class TransformedPointParamKnotHolderEntity_Vector;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/pathoutlineprovider.h b/src/live_effects/pathoutlineprovider.h
new file mode 100755
index 000000000..8aa2e38ad
--- /dev/null
+++ b/src/live_effects/pathoutlineprovider.h
@@ -0,0 +1,766 @@
+#pragma once
+
+#include <2geom/path.h>
+#include <2geom/circle.h>
+#include <2geom/sbasis-to-bezier.h>
+#include <2geom/shape.h>
+#include <2geom/transforms.h>
+#include <2geom/path-sink.h>
+
+#include <livarot/Path.h>
+#include <livarot/LivarotDefs.h>
+
+enum LineJoinType {
+ LINEJOIN_STRAIGHT,
+ LINEJOIN_ROUND,
+ LINEJOIN_POINTY,
+ LINEJOIN_REFLECTED,
+ LINEJOIN_EXTRAPOLATED
+};
+
+namespace Geom
+{
+ /**
+ * Refer to: Weisstein, Eric W. "Circle-Circle Intersection."
+ From MathWorld--A Wolfram Web Resource.
+ http://mathworld.wolfram.com/Circle-CircleIntersection.html
+ *
+ * @return 0 if no intersection
+ * @return 1 if one circle is contained in the other
+ * @return 2 if intersections are found (they are written to p0 and p1)
+ */
+ static int circle_circle_intersection(Circle const &circle0, Circle const &circle1,
+ Point & p0, Point & p1)
+ {
+ Point X0 = circle0.center();
+ double r0 = circle0.ray();
+ Point X1 = circle1.center();
+ double r1 = circle1.ray();
+
+ /* dx and dy are the vertical and horizontal distances between
+ * the circle centers.
+ */
+ Point D = X1 - X0;
+
+ /* Determine the straight-line distance between the centers. */
+ double d = L2(D);
+
+ /* Check for solvability. */
+ if (d > (r0 + r1))
+ {
+ /* no solution. circles do not intersect. */
+ return 0;
+ }
+ if (d <= fabs(r0 - r1))
+ {
+ /* no solution. one circle is contained in the other */
+ return 1;
+ }
+
+ /* 'point 2' is the point where the line through the circle
+ * intersection points crosses the line between the circle
+ * centers.
+ */
+
+ /* Determine the distance from point 0 to point 2. */
+ double a = ((r0*r0) - (r1*r1) + (d*d)) / (2.0 * d) ;
+
+ /* Determine the coordinates of point 2. */
+ Point p2 = X0 + D * (a/d);
+
+ /* Determine the distance from point 2 to either of the
+ * intersection points.
+ */
+ double h = std::sqrt((r0*r0) - (a*a));
+
+ /* Now determine the offsets of the intersection points from
+ * point 2.
+ */
+ Point r = (h/d)*rot90(D);
+
+ /* Determine the absolute intersection points. */
+ p0 = p2 + r;
+ p1 = p2 - r;
+
+ return 2;
+ }
+ /**
+ * Find circle that touches inside of the curve, with radius matching the curvature, at time value \c t.
+ * Because this method internally uses unitTangentAt, t should be smaller than 1.0 (see unitTangentAt).
+ */
+ static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.01 )
+ {
+ D2<SBasis> dM=derivative(curve);
+ if ( are_near(L2sq(dM(t)),0.) ) {
+ dM=derivative(dM);
+ }
+ if ( are_near(L2sq(dM(t)),0.) ) { // try second time
+ dM=derivative(dM);
+ }
+ Piecewise<D2<SBasis> > unitv = unitVector(dM,tol);
+ Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv);
+ Piecewise<SBasis> k = cross(derivative(unitv),unitv);
+ k = divide(k,dMlength,tol,3);
+ double curv = k(t); // note that this value is signed
+
+ Geom::Point normal = unitTangentAt(curve, t).cw();
+ double radius = 1/curv;
+ Geom::Point center = curve(t) + radius*normal;
+ return Geom::Circle(center, fabs(radius));
+ }
+
+ static std::vector<Geom::Path> split_at_cusps(const Geom::Path& in)
+ {
+ Geom::PathVector out = Geom::PathVector();
+ Geom::Path temp = Geom::Path();
+
+ for (unsigned path_descr = 0; path_descr < in.size(); path_descr++)
+ {
+ temp = Geom::Path();
+ temp.append(in[path_descr]);
+ out.push_back(temp);
+ }
+
+ return out;
+ }
+
+ static Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in)
+ {
+ std::vector<Geom::Point> temp;
+ sbasis_to_bezier(temp, sbasis_in, 4);
+ return Geom::CubicBezier( temp );
+ }
+
+ static boost::optional<Geom::Point> intersection_point( Geom::Point const & origin_a, Geom::Point const & vector_a,
+ Geom::Point const & origin_b, Geom::Point const & vector_b)
+ {
+ Geom::Coord denom = cross(vector_b, vector_a);
+ if (!Geom::are_near(denom,0.)){
+ Geom::Coord t = (cross(origin_a,vector_b) + cross(vector_b,origin_b)) / denom;
+ return origin_a + t * vector_a;
+ }
+ return boost::none;
+ }
+}
+
+namespace Outline
+{
+
+ typedef Geom::D2<Geom::SBasis> D2SB;
+ typedef Geom::Piecewise<D2SB> PWD2;
+
+ static void extrapolate_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve*cbc2, Geom::Point endPt, double miter_limit)
+{
+ Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
+ if (cross.empty())
+ {
+ Geom::Path pth;
+ pth.append(*cbc1);
+
+ Geom::Point tang1 = Geom::unitTangentAt(pth.toPwSb()[0], 1);
+
+ pth = Geom::Path();
+ pth.append( *cbc2 );
+ Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0);
+
+
+ Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(cbc1->toSBasis()), 0.);
+ Geom::Circle circle2 = Geom::touching_circle(cbc2->toSBasis(), 0);
+
+ Geom::Point points[2];
+ int solutions = Geom::circle_circle_intersection(circle1, circle2, points[0], points[1]);
+ if (solutions == 2)
+ {
+ Geom::Point sol(0,0);
+ if ( dot(tang2,points[0]-endPt) > 0 )
+ {
+ // points[0] is bad, choose points[1]
+ sol = points[1];
+ }
+ else if ( dot(tang2,points[1]-endPt) > 0 ) { // points[0] could be good, now check points[1]
+ // points[1] is bad, choose points[0]
+ sol = points[0];
+ }
+ else
+ {
+ // both points are good, choose nearest
+ sol = ( distanceSq(endPt, points[0]) < distanceSq(endPt, points[1]) ) ?
+ points[0] : points[1];
+ }
+ Geom::EllipticalArc *arc0 = circle1.arc(cbc1->finalPoint(), 0.5*(cbc1->finalPoint()+sol), sol, true);
+ Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
+
+ if (arc0)
+ {
+ path_builder.append (arc0->toSBasis());
+ delete arc0;
+ arc0 = NULL;
+ }
+
+ if (arc1)
+ {
+ path_builder.append (arc1->toSBasis());
+ delete arc1;
+ arc1 = NULL;
+ }
+ }
+ else
+ {
+ path_builder.appendNew<Geom::LineSegment> (endPt);
+ }
+ }
+ else
+ {
+ path_builder.appendNew<Geom::LineSegment> (endPt);
+ }
+}
+ static Geom::Path half_outline_extrp(const Geom::Path& path_in, double line_width, ButtType linecap_type, double miter_limit)
+ {
+ Geom::PathVector pv = split_at_cusps(path_in);
+ unsigned m;
+ Path path_outline = Path();
+ Path path_tangent = Path();
+
+ Geom::Point initialPoint;
+ Geom::Point endPoint;
+
+ Geom::Path path_builder = Geom::Path();
+ Geom::PathVector * pathvec;
+
+ //load the first portion in before the loop starts
+ {
+ path_outline = Path();
+ path_outline.LoadPath(pv[0], Geom::Affine(), false, false);
+ path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
+ //now half of first cusp has been loaded
+
+ pathvec = path_tangent.MakePathVector();
+ path_tangent = Path();
+
+ //instead of array accessing twice, dereferencing used for clarity
+ initialPoint = (*pathvec)[0].initialPoint();
+
+ path_builder.start(initialPoint);
+ path_builder.append( (*pathvec)[0] );
+
+ path_outline = Path();
+ path_outline.LoadPath(pv[1], Geom::Affine(), false, false);
+ path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
+
+ delete pathvec; pathvec = NULL;
+ pathvec = path_tangent.MakePathVector();
+ path_tangent = Path();
+
+ Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate();
+ Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate();
+
+ extrapolate_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit );
+
+ path_builder.append( (*pathvec)[0] );
+
+ //always set pointers null after deleting
+ delete pathvec; pathvec = NULL;
+ delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
+ }
+
+ for (m = 2; m < pv.size(); m++)
+ {
+ path_outline = Path();
+ path_outline.LoadPath(pv[m], Geom::Affine(), false, false);
+ path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
+
+ delete pathvec; pathvec = NULL;
+ pathvec = path_tangent.MakePathVector();
+
+ Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate();
+ Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate();
+
+ extrapolate_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit );
+ path_builder.append( (*pathvec)[0] );
+
+ delete pathvec; pathvec = NULL;
+ delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
+ }
+
+ return path_builder;
+ }
+
+ //Create a reflected outline join.
+ //Note: it is generally recommended to let half_outline do this for you!
+ //path_builder: the path to append the curves to
+ //cbc1: the curve before the join
+ //cbc2: the curve after the join
+ //endPt: the point to end at
+ //miter_limit: the miter parameter
+ static void reflect_curves(Geom::Path& path_builder, Geom::Curve* cbc1, Geom::Curve* cbc2, Geom::Point endPt, double miter_limit)
+ {
+ //the most important work for the reflected join is done here
+
+ //determine where we are in the path. If we're on the inside, ignore
+ //and just lineTo. On the outside, we'll do a little reflection magic :)
+ Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
+ if (cross.empty())
+ {
+ //probably on the outside of the corner
+ Geom::Path pth;
+ pth.append(*cbc1);
+
+ Geom::Point tang1 = Geom::unitTangentAt(pth.toPwSb()[0], 1);
+
+ //reflect curves along the bevel
+ D2SB newcurve1 = pth.toPwSb()[0] *
+ Geom::reflection ( -Geom::rot90(tang1) ,
+ cbc1->finalPoint() );
+
+ Geom::CubicBezier bzr1 = sbasis_to_cubicbezier(Geom::reverse(newcurve1));
+
+ pth = Geom::Path();
+ pth.append( *cbc2 );
+ Geom::Point tang2 = Geom::unitTangentAt(pth.toPwSb()[0], 0);
+
+ D2SB newcurve2 = pth.toPwSb()[0] *
+ Geom::reflection ( -Geom::rot90(tang2) ,
+ cbc2->initialPoint() );
+ Geom::CubicBezier bzr2 = sbasis_to_cubicbezier(Geom::reverse(newcurve2));
+
+ cross = Geom::crossings(bzr1, bzr2);
+ if ( cross.empty() )
+ {
+ //std::cout << "Oops, no crossings!" << std::endl;
+ //curves didn't cross; default to miter
+ /*boost::optional <Geom::Point> p = intersection_point (cbc1->finalPoint(), tang1,
+ cbc2->initialPoint(), tang2);
+ if (p)
+ {
+ path_builder.appendNew<Geom::LineSegment> (*p);
+ }*/
+ //bevel
+ path_builder.appendNew<Geom::LineSegment>( endPt );
+ }
+ else
+ {
+ //join
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> sub1 = bzr1.subdivide(cross[0].ta);
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> sub2 = bzr2.subdivide(cross[0].tb);
+
+ //@TODO joins have a strange tendency to cross themselves twice. Check this.
+
+ //sections commented out are for general stability
+ path_builder.appendNew <Geom::CubicBezier> (sub1.first[1], sub1.first[2], /*sub1.first[3]*/ sub2.second[0] );
+ path_builder.appendNew <Geom::CubicBezier> (sub2.second[1], sub2.second[2], /*sub2.second[3]*/ endPt );
+ }
+ }
+ else // cross.empty()
+ {
+ //probably on the inside of the corner
+ path_builder.appendNew<Geom::LineSegment> ( endPt );
+ }
+ }
+
+ /** @brief Converts a path to one half of an outline.
+ * path_in: The input path to use. (To create the other side use path_in.reverse() )
+ * line_width: the line width to use (usually you want to divide this by 2)
+ * linecap_type: (not used here) the cap to apply. Passed to libvarot.
+ * miter_limit: the miter parameter
+ */
+ static Geom::Path half_outline(const Geom::Path& path_in, double line_width, ButtType linecap_type, double miter_limit)
+ {
+ Geom::PathVector pv = split_at_cusps(path_in);
+ unsigned m;
+ Path path_outline = Path();
+ Path path_tangent = Path();
+ //needed for closing the path
+ Geom::Point initialPoint;
+ Geom::Point endPoint;
+
+ //some issues prevented me from using a PathBuilder here
+ //it seems like PathBuilder::peek() gave me a null reference exception
+ //and I was unable to get a stack trace on Windows, so had to switch to Linux
+ //to see what the hell was wrong. :(
+ //I wasted five hours opening it in IDAPro, VS2012, and GDB Windows
+
+ /*Program received signal SIGSEGV, Segmentation fault.
+ 0x00000000006539ac in get_curves (this=0x0)
+ at /usr/include/c++/4.6/bits/locale_facets.h:1077
+ 1077 { return __c; }
+ */
+
+ Geom::Path path_builder = Geom::Path();
+ Geom::PathVector * pathvec;
+
+ //load the first portion in before the loop starts
+ {
+ path_outline = Path();
+ path_outline.LoadPath(pv[0], Geom::Affine(), false, false);
+ path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
+ //now half of first cusp has been loaded
+
+ pathvec = path_tangent.MakePathVector();
+ path_tangent = Path();
+
+ //instead of array accessing twice, dereferencing used for clarity
+ initialPoint = (*pathvec)[0].initialPoint();
+
+ path_builder.start(initialPoint);
+ path_builder.append( (*pathvec)[0] );
+
+ path_outline = Path();
+ path_outline.LoadPath(pv[1], Geom::Affine(), false, false);
+ path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
+
+ delete pathvec; pathvec = NULL;
+ pathvec = path_tangent.MakePathVector();
+ path_tangent = Path();
+
+ Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate();
+ Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate();
+
+ reflect_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit );
+
+ path_builder.append( (*pathvec)[0] );
+
+ //always set pointers null after deleting
+ delete pathvec; pathvec = NULL;
+ delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
+ }
+
+ for (m = 2; m < pv.size(); m++)
+ {
+ path_outline = Path();
+ path_outline.LoadPath(pv[m], Geom::Affine(), false, false);
+ path_outline.OutsideOutline(&path_tangent, line_width / 2, join_straight, linecap_type, 10);
+
+ delete pathvec; pathvec = NULL;
+ pathvec = path_tangent.MakePathVector();
+
+ Geom::Curve *cbc1 = path_builder[path_builder.size() - 1].duplicate();
+ Geom::Curve *cbc2 = (*pathvec)[0][0].duplicate();
+
+ reflect_curves(path_builder, cbc1, cbc2, (*pathvec)[0].initialPoint(), miter_limit );
+ path_builder.append( (*pathvec)[0] );
+
+ delete pathvec; pathvec = NULL;
+ delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
+ }
+
+ return path_builder;
+ }
+
+ static Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, JoinType join, ButtType butt, double miter_lim)
+ {
+ Path p = Path();
+ Path outlinepath = Path();
+ for (unsigned i = 0; i < path_in.size(); i++)
+ {
+ p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
+ }
+
+ Geom::PathVector path_out;
+ for (unsigned lmnop = 0; lmnop < path_in.size(); lmnop++)
+ {
+ if (path_in[lmnop].size() > 1)
+ {
+ Geom::Path p_init;
+ Geom::Path p_rev;
+ Geom::PathBuilder pb = Geom::PathBuilder();
+
+ if ( !path_in[lmnop].closed() )
+ {
+ p_init = Outline::half_outline( path_in[lmnop], -line_width, butt,
+ miter_lim );
+ p_rev = Outline::half_outline( path_in[lmnop].reverse(), -line_width, butt,
+ miter_lim );
+
+ pb.moveTo(p_init.initialPoint() );
+ pb.append(p_init);
+
+ //cap
+ if (butt == butt_straight) {
+ pb.lineTo(p_rev.initialPoint() );
+ } else if (butt == butt_round) {
+ pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_rev.initialPoint() );
+ } else if (butt == butt_square) {
+ //don't know what to do
+ pb.lineTo(p_rev.initialPoint() );
+ } else if (butt == butt_pointy) {
+ //don't know what to do
+ pb.lineTo(p_rev.initialPoint() );
+ }
+
+ pb.append(p_rev);
+
+ if (butt == butt_straight) {
+ pb.lineTo(p_init.initialPoint() );
+ } else if (butt == butt_round) {
+ pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_init.initialPoint() );
+ } else if (butt == butt_square) {
+ //don't know what to do
+ pb.lineTo(p_init.initialPoint() );
+ } else if (butt == butt_pointy) {
+ //don't know what to do
+ //Geom::Point end_deriv = Geom::unitTangentAt( Geom::reverse(path_in[lmnop].toPwSb()[path_in[lmnop].size()]), 0);
+ //double radius = 0.5 * Geom::distance(path_in[lmnop].finalPoint(), p_rev.initialPoint());
+
+ pb.lineTo(p_init.initialPoint() );
+ }
+ }
+ else
+ {
+ //final join
+ //refer to half_outline for documentation
+ Geom::Path p_almost = path_in[lmnop];
+ p_almost.appendNew<Geom::LineSegment> ( path_in[lmnop].initialPoint() );
+ p_init = Outline::half_outline( p_almost, -line_width, butt,
+ miter_lim );
+ p_rev = Outline::half_outline( p_almost.reverse(), -line_width, butt,
+ miter_lim );
+ p.LoadPath(path_in[lmnop], Geom::Affine(), false, false);
+
+ //this is a kludge, because I can't find how to make this work properly
+ bool lastIsLinear = ( (Geom::distance(path_in[lmnop].finalPoint(),
+ path_in[lmnop] [path_in[lmnop].size() - 1].finalPoint())) ==
+ (path_in[lmnop] [path_in[lmnop].size()].length()));
+
+ p_almost = p_init;
+ if (lastIsLinear)
+ {
+ p_almost.erase_last(); p_almost.erase_last();
+ }
+
+ //outside test
+ Geom::Curve* cbc1 = p_almost[p_almost.size() - 1].duplicate();
+ Geom::Curve* cbc2 = p_almost[0].duplicate();
+
+ Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
+
+ if (cross.empty())
+ {
+ //this is the outside path
+
+ //reuse the old one
+ p_init = p_almost;
+ Outline::reflect_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim );
+ pb.moveTo(p_init.initialPoint()); pb.append(p_init);
+ }
+ else
+ {
+ //inside, carry on :-)
+ pb.moveTo(p_almost.initialPoint()); pb.append(p_almost);
+ }
+
+ p_almost = p_rev;
+ if (lastIsLinear)
+ {
+ p_almost.erase(p_almost.begin() );
+ p_almost.erase(p_almost.begin() );
+ }
+
+ delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
+
+ cbc1 = p_almost[p_almost.size() - 1].duplicate();
+ cbc2 = p_almost[0].duplicate();
+
+ cross = Geom::crossings(*cbc1, *cbc2);
+
+ if (cross.empty())
+ {
+ //outside path
+
+ p_init = p_almost;
+ reflect_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim );
+ pb.moveTo(p_init.initialPoint()); pb.append(p_init);
+ }
+ else
+ {
+ //inside
+ pb.moveTo(p_almost.initialPoint()); pb.append(p_almost);
+ }
+ delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
+ }
+ //pb.closePath();
+ pb.flush();
+ Geom::PathVector pv_np = pb.peek();
+ //hack
+ for (unsigned abcd = 0; abcd < pv_np.size(); abcd++)
+ {
+ path_out.push_back( pv_np[abcd] );
+ }
+ }
+ else
+ {
+ p.LoadPath(path_in[lmnop], Geom::Affine(), false, false);
+ p.Outline(&outlinepath, line_width / 2, join, butt, miter_lim);
+ std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
+ //hack
+ path_out.push_back( (*pv_p)[0].reverse() );
+ delete pv_p;
+ }
+ }
+ return path_out;
+ }
+ static Geom::PathVector outlinePath_extr(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtType butt, double miter_lim)
+ {
+ Path p = Path();
+ Path outlinepath = Path();
+ for (unsigned i = 0; i < path_in.size(); i++)
+ {
+ p.LoadPath(path_in[i], Geom::Affine(), false, ( (i==0) ? false : true));
+ }
+
+ Geom::PathVector path_out;
+ for (unsigned lmnop = 0; lmnop < path_in.size(); lmnop++)
+ {
+ if (path_in[lmnop].size() > 1)
+ {
+ Geom::Path p_init;
+ Geom::Path p_rev;
+ Geom::PathBuilder pb = Geom::PathBuilder();
+
+ if ( !path_in[lmnop].closed() )
+ {
+ p_init = Outline::half_outline_extrp( path_in[lmnop], -line_width, butt,
+ miter_lim );
+ p_rev = Outline::half_outline_extrp( path_in[lmnop].reverse(), -line_width, butt,
+ miter_lim );
+
+ pb.moveTo(p_init.initialPoint() );
+ pb.append(p_init);
+
+ //cap
+ if (butt == butt_straight) {
+ pb.lineTo(p_rev.initialPoint() );
+ } else if (butt == butt_round) {
+ pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_rev.initialPoint() );
+ } else if (butt == butt_square) {
+ //don't know what to do
+ pb.lineTo(p_rev.initialPoint() );
+ } else if (butt == butt_pointy) {
+ //don't know what to do
+ pb.lineTo(p_rev.initialPoint() );
+ }
+
+ pb.append(p_rev);
+
+ if (butt == butt_straight) {
+ pb.lineTo(p_init.initialPoint() );
+ } else if (butt == butt_round) {
+ pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, p_init.initialPoint() );
+ } else if (butt == butt_square) {
+ //don't know what to do
+ pb.lineTo(p_init.initialPoint() );
+ } else if (butt == butt_pointy) {
+ //don't know what to do
+ //Geom::Point end_deriv = Geom::unitTangentAt( Geom::reverse(path_in[lmnop].toPwSb()[path_in[lmnop].size()]), 0);
+ //double radius = 0.5 * Geom::distance(path_in[lmnop].finalPoint(), p_rev.initialPoint());
+
+ pb.lineTo(p_init.initialPoint() );
+ }
+ }
+ else
+ {
+ //final join
+ //refer to half_outline for documentation
+ Geom::Path p_almost = path_in[lmnop];
+ p_almost.appendNew<Geom::LineSegment> ( path_in[lmnop].initialPoint() );
+ p_init = Outline::half_outline_extrp( p_almost, -line_width, butt,
+ miter_lim );
+ p_rev = Outline::half_outline_extrp( p_almost.reverse(), -line_width, butt,
+ miter_lim );
+ p.LoadPath(path_in[lmnop], Geom::Affine(), false, false);
+
+ //this is a kludge, because I can't find how to make this work properly
+ bool lastIsLinear = ( (Geom::distance(path_in[lmnop].finalPoint(),
+ path_in[lmnop] [path_in[lmnop].size() - 1].finalPoint())) ==
+ (path_in[lmnop] [path_in[lmnop].size()].length()));
+
+ p_almost = p_init;
+ if (lastIsLinear)
+ {
+ p_almost.erase_last(); p_almost.erase_last();
+ }
+
+ //outside test
+ Geom::Curve* cbc1 = p_almost[p_almost.size() - 1].duplicate();
+ Geom::Curve* cbc2 = p_almost[0].duplicate();
+
+ Geom::Crossings cross = Geom::crossings(*cbc1, *cbc2);
+
+ if (cross.empty())
+ {
+ //this is the outside path
+
+ //reuse the old one
+ p_init = p_almost;
+ Outline::extrapolate_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim );
+ pb.moveTo(p_init.initialPoint()); pb.append(p_init);
+ }
+ else
+ {
+ //inside, carry on :-)
+ pb.moveTo(p_almost.initialPoint()); pb.append(p_almost);
+ }
+
+ p_almost = p_rev;
+ if (lastIsLinear)
+ {
+ p_almost.erase(p_almost.begin() );
+ p_almost.erase(p_almost.begin() );
+ }
+
+ delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
+
+ cbc1 = p_almost[p_almost.size() - 1].duplicate();
+ cbc2 = p_almost[0].duplicate();
+
+ cross = Geom::crossings(*cbc1, *cbc2);
+
+ if (cross.empty())
+ {
+ //outside path
+
+ p_init = p_almost;
+ extrapolate_curves(p_init, cbc1, cbc2, p_almost.initialPoint(), miter_lim );
+ pb.moveTo(p_init.initialPoint()); pb.append(p_init);
+ }
+ else
+ {
+ //inside
+ pb.moveTo(p_almost.initialPoint()); pb.append(p_almost);
+ }
+ delete cbc1; delete cbc2; cbc1 = cbc2 = NULL;
+ }
+ //pb.closePath();
+ pb.flush();
+ Geom::PathVector pv_np = pb.peek();
+ //hack
+ for (unsigned abcd = 0; abcd < pv_np.size(); abcd++)
+ {
+ path_out.push_back( pv_np[abcd] );
+ }
+ }
+ else
+ {
+ p.LoadPath(path_in[lmnop], Geom::Affine(), false, false);
+ p.Outline(&outlinepath, line_width / 2, join_pointy, butt, miter_lim);
+ std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
+ //hack
+ path_out.push_back( (*pv_p)[0].reverse() );
+ delete pv_p;
+ }
+ }
+ return path_out;
+ }
+
+
+} // namespace Outline
+
+/*
+ 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 :