summaryrefslogtreecommitdiffstats
path: root/src/live_effects
diff options
context:
space:
mode:
authorLiam P. White <inkscapebrony@gmail.com>2014-09-27 14:17:45 +0000
committerLiam P. White <inkscapebrony@gmail.com>2014-09-27 14:17:45 +0000
commitfa9bd6393f316dab9303569b28f6b5d179fedd61 (patch)
tree33df00632f850a6f117978c36145feeac05f1a4c /src/live_effects
parentUpdate to experimental r13527 (diff)
downloadinkscape-fa9bd6393f316dab9303569b28f6b5d179fedd61.tar.gz
inkscape-fa9bd6393f316dab9303569b28f6b5d179fedd61.zip
Update to experimental r13565
(bzr r13341.5.16)
Diffstat (limited to 'src/live_effects')
-rw-r--r--src/live_effects/CMakeLists.txt13
-rw-r--r--src/live_effects/Makefile_insert16
-rw-r--r--src/live_effects/effect-enum.h7
-rw-r--r--src/live_effects/effect.cpp77
-rw-r--r--src/live_effects/effect.h11
-rw-r--r--src/live_effects/lpe-attach-path.cpp198
-rw-r--r--src/live_effects/lpe-attach-path.h52
-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-bspline.cpp394
-rw-r--r--src/live_effects/lpe-copy_rotate.cpp1
-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.cpp78
-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-fillet-chamfer.cpp163
-rw-r--r--src/live_effects/lpe-fillet-chamfer.h10
-rw-r--r--src/live_effects/lpe-interpolate_points.cpp2
-rw-r--r--src/live_effects/lpe-jointype.cpp190
-rw-r--r--src/live_effects/lpe-jointype.h57
-rw-r--r--src/live_effects/lpe-knot.cpp5
-rw-r--r--src/live_effects/lpe-knot.h3
-rw-r--r--src/live_effects/lpe-powerstroke-interpolators.h40
-rw-r--r--src/live_effects/lpe-powerstroke.cpp100
-rw-r--r--src/live_effects/lpe-powerstroke.h2
-rw-r--r--src/live_effects/lpe-roughen.cpp595
-rw-r--r--src/live_effects/lpe-roughen.h64
-rw-r--r--src/live_effects/lpe-tangent_to_curve.cpp19
-rw-r--r--src/live_effects/lpe-tangent_to_curve.h1
-rw-r--r--src/live_effects/lpe-taperstroke.cpp632
-rw-r--r--src/live_effects/lpe-taperstroke.h72
-rw-r--r--src/live_effects/lpeobject-reference.h6
-rw-r--r--src/live_effects/lpeobject.h4
-rw-r--r--src/live_effects/parameter/Makefile_insert4
-rw-r--r--src/live_effects/parameter/filletchamferpointarray.cpp263
-rw-r--r--src/live_effects/parameter/filletchamferpointarray.h8
-rw-r--r--src/live_effects/parameter/originalpatharray.cpp487
-rw-r--r--src/live_effects/parameter/originalpatharray.h122
-rw-r--r--src/live_effects/parameter/powerstrokepointarray.cpp43
-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.h82
-rw-r--r--src/live_effects/pathoutlineprovider.cpp803
-rw-r--r--src/live_effects/pathoutlineprovider.h55
46 files changed, 4643 insertions, 799 deletions
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index d126aca9f..30c2b2f41 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,9 +13,12 @@ set(live_effects_SRC
lpe-copy_rotate.cpp
lpe-curvestitch.cpp
lpe-dynastroke.cpp
+ lpe-ellipse-5pts.cpp
lpe-envelope.cpp
lpe-envelope-perspective.cpp
lpe-extrude.cpp
+ lpe-fill-between-many.cpp
+ lpe-fill-between-strokes.cpp
lpe-fillet-chamfer.cpp
lpe-gears.cpp
lpe-interpolate.cpp
@@ -55,11 +60,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/togglebutton.cpp
parameter/unit.cpp
parameter/vector.cpp
@@ -70,8 +77,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
@@ -79,8 +88,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-fillet-chamfer.h
lpe-gears.h
lpe-interpolate.h
@@ -125,6 +137,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 1b8f587e1..f18dcdef0 100644
--- a/src/live_effects/Makefile_insert
+++ b/src/live_effects/Makefile_insert
@@ -97,5 +97,21 @@ ink_common_sources += \
live_effects/lpe-path_length.h \
live_effects/lpe-line_segment.cpp \
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-strokes.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/pathoutlineprovider.cpp \
+ live_effects/pathoutlineprovider.h \
+ live_effects/lpe-jointype.cpp \
+ live_effects/lpe-jointype.h \
+ live_effects/lpe-taperstroke.cpp \
+ live_effects/lpe-taperstroke.h \
live_effects/lpe-envelope-perspective.cpp \
live_effects/lpe-envelope-perspective.h
diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h
index 30dbf4092..c53f1a5b9 100644
--- a/src/live_effects/effect-enum.h
+++ b/src/live_effects/effect-enum.h
@@ -56,6 +56,13 @@ enum EffectType {
EXTRUDE,
POWERSTROKE,
CLONE_ORIGINAL,
+ ATTACH_PATH,
+ FILL_BETWEEN_STROKES,
+ FILL_BETWEEN_MANY,
+ ELLIPSE_5PTS,
+ BOUNDING_BOX,
+ JOIN_TYPE,
+ TAPER_STROKE,
ENVELOPE_PERSPECTIVE,
FILLET_CHAMFER,
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 b012e3ab1..87562ddc4 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -5,6 +5,8 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+//#define LPE_ENABLE_TEST_EFFECTS //uncomment for toy effects
+
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
@@ -19,7 +21,6 @@
#include "live_effects/lpe-rough-hatches.h"
#include "live_effects/lpe-dynastroke.h"
#include "live_effects/lpe-test-doEffect-stack.h"
-#include "live_effects/lpe-bspline.h"
#include "live_effects/lpe-gears.h"
#include "live_effects/lpe-curvestitch.h"
#include "live_effects/lpe-circle_with_radius.h"
@@ -51,6 +52,14 @@
#include "live_effects/lpe-extrude.h"
#include "live_effects/lpe-powerstroke.h"
#include "live_effects/lpe-clone-original.h"
+#include "live_effects/lpe-bspline.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 "live_effects/lpe-taperstroke.h"
#include "live_effects/lpe-envelope-perspective.h"
#include "live_effects/lpe-fillet-chamfer.h"
@@ -132,10 +141,18 @@ const Util::EnumData<EffectType> LPETypeData[] = {
{SHOW_HANDLES, N_("Show handles"), "show_handles"},
{ROUGHEN, N_("Roughen"), "roughen"},
{BSPLINE, N_("BSpline"), "bspline"},
- {SIMPLIFY, N_("Simplify"), "simplify"},
- {LATTICE2, N_("Lattice Deformation 2"), "lattice2"},
- // TRANSLATORS: "Envelope Perspective" should be equivalent to "perspective transformation"
- {ENVELOPE_PERSPECTIVE, N_("Envelope Perspective"), "envelope-perspective"},
+ {JOIN_TYPE, N_("Join type"), "join_type"},
+ {TAPER_STROKE, N_("Taper stroke"), "taper_stroke"},
+/* 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"},
+/* 0.91 */
+ {SIMPLIFY, N_("Simplify"), "simplify"},
+ {LATTICE2, N_("Lattice Deformation 2"), "lattice2"},
+ {ENVELOPE_PERSPECTIVE, N_("Envelope-Perspective"), "envelope-perspective"},
{FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"},
{INTERPOLATE_POINTS, N_("Interpolate points"), "interpolate_points"},
};
@@ -267,6 +284,27 @@ 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;
+ case TAPER_STROKE:
+ neweffect = static_cast<Effect*> ( new LPETaperStroke(lpeobj) );
+ break;
case SIMPLIFY:
neweffect = static_cast<Effect*> ( new LPESimplify(lpeobj) );
break;
@@ -286,7 +324,7 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
neweffect = static_cast<Effect*> ( new LPEShowHandles(lpeobj) );
break;
default:
- g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
+ g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
neweffect = NULL;
break;
}
@@ -369,6 +407,33 @@ Effect::doBeforeEffect (SPLPEItem const*/*lpeitem*/)
//Do nothing for simple effects
}
+void Effect::doAfterEffect (SPLPEItem const* /*lpeitem*/)
+{
+}
+
+void Effect::doOnRemove (SPLPEItem const* /*lpeitem*/)
+{
+}
+
+//secret impl methods (shhhh!)
+void Effect::doOnApply_impl(SPLPEItem const* lpeitem)
+{
+ sp_lpe_item = const_cast<SPLPEItem *>(lpeitem);
+ /*sp_curve = SP_SHAPE(sp_lpe_item)->getCurve();
+ pathvector_before_effect = sp_curve->get_pathvector();*/
+ doOnApply(lpeitem);
+}
+
+void Effect::doBeforeEffect_impl(SPLPEItem const* lpeitem)
+{
+ sp_lpe_item = const_cast<SPLPEItem *>(lpeitem);
+ //printf("(SPLPEITEM*) %p\n", sp_lpe_item);
+ sp_curve = SP_SHAPE(sp_lpe_item)->getCurve();
+ pathvector_before_effect = sp_curve->get_pathvector();
+
+ doBeforeEffect(lpeitem);
+}
+
/**
* Effects can have a parameter path set before they are applied by accepting a nonzero number of
* mouse clicks. This method activates the pen context, which waits for the specified number of
diff --git a/src/live_effects/effect.h b/src/live_effects/effect.h
index 940770616..a486e8491 100644
--- a/src/live_effects/effect.h
+++ b/src/live_effects/effect.h
@@ -53,8 +53,16 @@ public:
EffectType effectType() const;
+ //basically, to get this method called before the derived classes, a bit
+ //of indirection is needed. We first call these methods, then the below.
+ void doOnApply_impl(SPLPEItem const* lpeitem);
+ void doBeforeEffect_impl(SPLPEItem const* lpeitem);
+
virtual void doOnApply (SPLPEItem const* lpeitem);
virtual void doBeforeEffect (SPLPEItem const* lpeitem);
+
+ virtual void doAfterEffect (SPLPEItem const* lpeitem);
+ virtual void doOnRemove (SPLPEItem const* lpeitem);
void writeParamsToSVG();
@@ -147,6 +155,9 @@ protected:
// instead of normally 'splitting' the path into continuous pwd2 paths and calling doEffect_pwd2 for each.
bool concatenate_before_pwd2;
+ SPLPEItem * sp_lpe_item; // these get stored in doBeforeEffect_impl, and derived classes may do as they please with them.
+ SPCurve * sp_curve;
+ std::vector<Geom::Path> pathvector_before_effect;
private:
bool provides_own_flash_paths; // if true, the standard flash path is suppressed
diff --git a/src/live_effects/lpe-attach-path.cpp b/src/live_effects/lpe-attach-path.cpp
new file mode 100644
index 000000000..768c66ee2
--- /dev/null
+++ b/src/live_effects/lpe-attach-path.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * 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(&start_path);
+ registerParameter(&start_path_position);
+ registerParameter(&start_path_curve_start);
+ registerParameter(&start_path_curve_end);
+
+ registerParameter(&end_path);
+ registerParameter(&end_path_position);
+ registerParameter(&end_path_curve_start);
+ registerParameter(&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::doEffect (SPCurve * curve)
+{
+ std::vector<Geom::Path> this_pathv = curve->get_pathvector();
+ if (sp_lpe_item && !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(sp_lpe_item);
+
+ 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(sp_lpe_item);
+
+ 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..1d6c590d1
--- /dev/null
+++ b/src/live_effects/lpe-attach-path.h
@@ -0,0 +1,52 @@
+#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 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;
+};
+
+}; //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-bspline.cpp b/src/live_effects/lpe-bspline.cpp
index 3a6dca795..d45afb0d9 100644
--- a/src/live_effects/lpe-bspline.cpp
+++ b/src/live_effects/lpe-bspline.cpp
@@ -52,19 +52,24 @@ using Inkscape::DocumentUndo;
namespace Inkscape {
namespace LivePathEffect {
+const double handleCubicGap = 0.01;
+const double noPower = 0.0;
+const double defaultStartPower = 0.3334;
+const double defaultEndPower = 0.6667;
+
LPEBSpline::LPEBSpline(LivePathEffectObject *lpeobject) :
Effect(lpeobject),
steps(_("Steps whith CTRL:"), _("Change number of steps whith CTRL pressed"), "steps", &wr, this, 2),
ignoreCusp(_("Ignore cusp nodes"), _("Change ignoring cusp nodes"), "ignoreCusp", &wr, this, true),
onlySelected(_("Change only selected nodes"), _("Change only selected nodes"), "onlySelected", &wr, this, false),
- weight(_("Change weight:"), _("Change weight of the effect"), "weight", &wr, this, 0.3334)
+ weight(_("Change weight:"), _("Change weight of the effect"), "weight", &wr, this, defaultStartPower)
{
registerParameter(&weight);
registerParameter(&steps);
registerParameter(&ignoreCusp);
registerParameter(&onlySelected);
- weight.param_set_range(0.0000, 1);
+ weight.param_set_range(noPower, 1);
weight.param_set_increments(0.1, 0.1);
weight.param_set_digits(4);
@@ -97,192 +102,136 @@ void LPEBSpline::createAndApply(const char *name, SPDocument *doc, SPItem *item)
void LPEBSpline::doEffect(SPCurve *curve)
{
- if (curve->get_segment_count() < 1)
+ if (curve->get_segment_count() < 1){
return;
- // Make copy of old path as it is changed during processing
+ }
+
Geom::PathVector const original_pathv = curve->get_pathvector();
curve->reset();
- //Recorremos todos los paths a los que queremos aplicar el efecto, hasta el
- //penúltimo
for (Geom::PathVector::const_iterator path_it = original_pathv.begin();
path_it != original_pathv.end(); ++path_it) {
- //Si está vacío...
if (path_it->empty())
continue;
- //Itreadores
- Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
- Geom::Path::const_iterator curve_it2 =
- ++(path_it->begin()); // outgoing curve
- Geom::Path::const_iterator curve_endit =
- path_it->end_default(); // this determines when the loop has to stop
- //Creamos las lineas rectas que unen todos los puntos del trazado y donde se
- //calcularán
- //los puntos clave para los manejadores.
- //Esto hace que la curva BSpline no pierda su condición aunque se trasladen
- //dichos manejadores
+ Geom::Path::const_iterator curve_it1 = path_it->begin();
+ Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
+ Geom::Path::const_iterator curve_endit = path_it->end_default();
SPCurve *nCurve = new SPCurve();
Geom::Point previousNode(0, 0);
Geom::Point node(0, 0);
Geom::Point pointAt1(0, 0);
Geom::Point pointAt2(0, 0);
Geom::Point nextPointAt1(0, 0);
- Geom::Point nextPointAt2(0, 0);
- Geom::Point nextPointAt3(0, 0);
Geom::D2<Geom::SBasis> SBasisIn;
Geom::D2<Geom::SBasis> SBasisOut;
Geom::D2<Geom::SBasis> SBasisHelper;
Geom::CubicBezier const *cubic = NULL;
if (path_it->closed()) {
- // if the path is closed, maybe we have to stop a bit earlier because the
- // closing line segment has zerolength.
- const Geom::Curve &closingline =
- path_it->back_closed(); // the closing line segment is always of type
- // Geom::LineSegment.
+ const Geom::Curve &closingline = path_it->back_closed();
if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- // closingline.isDegenerate() did not work, because it only checks for
- // *exact* zero length, which goes wrong for relative coordinates and
- // rounding errors...
- // the closing line segment has zero-length. So stop before that one!
curve_endit = path_it->end_open();
}
}
- //Si la curva está cerrada calculamos el punto donde
- //deveria estar el nodo BSpline de cierre/inicio de la curva
- //en posible caso de que se cierre con una linea recta creando un nodo
- //BSPline
nCurve->moveto(curve_it1->initialPoint());
- //Recorremos todos los segmentos menos el último
- while (curve_it2 != curve_endit) {
- //previousPointAt3 = pointAt3;
- //Calculamos los puntos que dividirían en tres segmentos iguales el path
- //recto de entrada y de salida
+ while (curve_it1 != curve_endit) {
SPCurve *in = new SPCurve();
in->moveto(curve_it1->initialPoint());
in->lineto(curve_it1->finalPoint());
cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
if (cubic) {
SBasisIn = in->first_segment()->toSBasis();
- pointAt1 = SBasisIn.valueAt(
- Geom::nearest_point((*cubic)[1], *in->first_segment()));
- pointAt2 = SBasisIn.valueAt(
- Geom::nearest_point((*cubic)[2], *in->first_segment()));
+ if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) {
+ pointAt1 = SBasisIn.valueAt(defaultStartPower);
+ } else {
+ pointAt1 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[1], *in->first_segment()));
+ }
+ if(are_near((*cubic)[2],(*cubic)[3]) && !are_near((*cubic)[1],(*cubic)[0])) {
+ pointAt2 = SBasisIn.valueAt(defaultEndPower);
+ } else {
+ pointAt2 = SBasisIn.valueAt(Geom::nearest_point((*cubic)[2], *in->first_segment()));
+ }
} else {
pointAt1 = in->first_segment()->initialPoint();
pointAt2 = in->first_segment()->finalPoint();
}
in->reset();
delete in;
- //Y hacemos lo propio con el path de salida
- //nextPointAt0 = curveOut.valueAt(0);
- SPCurve *out = new SPCurve();
- out->moveto(curve_it2->initialPoint());
- out->lineto(curve_it2->finalPoint());
- cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it2);
- if (cubic) {
- SBasisOut = out->first_segment()->toSBasis();
- nextPointAt1 = SBasisOut.valueAt(
- Geom::nearest_point((*cubic)[1], *out->first_segment()));
- nextPointAt2 = SBasisOut.valueAt(
- Geom::nearest_point((*cubic)[2], *out->first_segment()));
- ;
- nextPointAt3 = out->first_segment()->finalPoint();
+ if ( curve_it2 != curve_endit ) {
+ SPCurve *out = new SPCurve();
+ out->moveto(curve_it2->initialPoint());
+ out->lineto(curve_it2->finalPoint());
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it2);
+ if (cubic) {
+ SBasisOut = out->first_segment()->toSBasis();
+ if(are_near((*cubic)[1],(*cubic)[0]) && !are_near((*cubic)[2],(*cubic)[3])) {
+ nextPointAt1 = SBasisIn.valueAt(defaultStartPower);
+ } else {
+ nextPointAt1 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[1], *out->first_segment()));
+ }
+ } else {
+ nextPointAt1 = out->first_segment()->initialPoint();
+ }
+ out->reset();
+ delete out;
+ }
+ Geom::Point startNode = path_it->begin()->initialPoint();
+ if (path_it->closed() && curve_it2 == curve_endit) {
+ SPCurve *start = new SPCurve();
+ start->moveto(path_it->begin()->initialPoint());
+ start->lineto(path_it->begin()->finalPoint());
+ Geom::D2<Geom::SBasis> SBasisStart = start->first_segment()->toSBasis();
+ SPCurve *lineHelper = new SPCurve();
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*path_it->begin());
+ if (cubic) {
+ lineHelper->moveto(SBasisStart.valueAt(
+ Geom::nearest_point((*cubic)[1], *start->first_segment())));
+ } else {
+ lineHelper->moveto(start->first_segment()->initialPoint());
+ }
+ start->reset();
+ delete start;
+
+ SPCurve *end = new SPCurve();
+ end->moveto(curve_it1->initialPoint());
+ end->lineto(curve_it1->finalPoint());
+ Geom::D2<Geom::SBasis> SBasisEnd = end->first_segment()->toSBasis();
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ if (cubic) {
+ lineHelper->lineto(SBasisEnd.valueAt(
+ Geom::nearest_point((*cubic)[2], *end->first_segment())));
+ } else {
+ lineHelper->lineto(end->first_segment()->finalPoint());
+ }
+ end->reset();
+ delete end;
+ SBasisHelper = lineHelper->first_segment()->toSBasis();
+ lineHelper->reset();
+ delete lineHelper;
+ startNode = SBasisHelper.valueAt(0.5);
+ nCurve->curveto(pointAt1, pointAt2, startNode);
+ nCurve->move_endpoints(startNode, startNode);
+ } else if ( curve_it2 == curve_endit) {
+ nCurve->curveto(pointAt1, pointAt2, curve_it1->finalPoint());
+ nCurve->move_endpoints(path_it->begin()->initialPoint(), curve_it1->finalPoint());
} else {
- nextPointAt1 = out->first_segment()->initialPoint();
- nextPointAt2 = out->first_segment()->finalPoint();
- nextPointAt3 = out->first_segment()->finalPoint();
+ SPCurve *lineHelper = new SPCurve();
+ lineHelper->moveto(pointAt2);
+ lineHelper->lineto(nextPointAt1);
+ SBasisHelper = lineHelper->first_segment()->toSBasis();
+ lineHelper->reset();
+ delete lineHelper;
+ previousNode = node;
+ node = SBasisHelper.valueAt(0.5);
+ Geom::CubicBezier const *cubic2 = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ if((cubic && are_near((*cubic)[0],(*cubic)[1])) || (cubic2 && are_near((*cubic2)[2],(*cubic2)[3]))) {
+ node = curve_it1->finalPoint();
+ }
+ nCurve->curveto(pointAt1, pointAt2, node);
}
- out->reset();
- delete out;
- //La curva BSpline se forma calculando el centro del segmanto de unión
- //de el punto situado en las 2/3 partes de el segmento de entrada
- //con el punto situado en la posición 1/3 del segmento de salida
- //Estos dos puntos ademas estan posicionados en el lugas correspondiente
- //de
- //los manejadores de la curva
- SPCurve *lineHelper = new SPCurve();
- lineHelper->moveto(pointAt2);
- lineHelper->lineto(nextPointAt1);
- SBasisHelper = lineHelper->first_segment()->toSBasis();
- lineHelper->reset();
- delete lineHelper;
- //almacenamos el punto del anterior bucle -o el de cierre- que nos hara de
- //principio de curva
- previousNode = node;
- //Y este hará de final de curva
- node = SBasisHelper.valueAt(0.5);
- nCurve->curveto(pointAt1, pointAt2, node);
- //aumentamos los valores para el siguiente paso en el bucle
++curve_it1;
++curve_it2;
}
- SPCurve *out = new SPCurve();
- out->moveto(curve_it1->initialPoint());
- out->lineto(curve_it1->finalPoint());
- cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
- if (cubic) {
- SBasisOut = out->first_segment()->toSBasis();
- nextPointAt1 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[1], *out->first_segment()));
- nextPointAt2 = SBasisOut.valueAt(Geom::nearest_point((*cubic)[2], *out->first_segment()));
- nextPointAt3 = out->first_segment()->finalPoint();
- } else {
- nextPointAt1 = out->first_segment()->initialPoint();
- nextPointAt2 = out->first_segment()->finalPoint();
- nextPointAt3 = out->first_segment()->finalPoint();
- }
- out->reset();
- delete out;
- //Si está cerrada la curva, la cerramos sobre el valor guardado
- //previamente
- //Si no finalizamos en el punto final
- Geom::Point startNode = path_it->begin()->initialPoint();
- if (path_it->closed()) {
- SPCurve *start = new SPCurve();
- start->moveto(path_it->begin()->initialPoint());
- start->lineto(path_it->begin()->finalPoint());
- Geom::D2<Geom::SBasis> SBasisStart = start->first_segment()->toSBasis();
- SPCurve *lineHelper = new SPCurve();
- cubic = dynamic_cast<Geom::CubicBezier const *>(&*path_it->begin());
- if (cubic) {
- lineHelper->moveto(SBasisStart.valueAt(
- Geom::nearest_point((*cubic)[1], *start->first_segment())));
- } else {
- lineHelper->moveto(start->first_segment()->initialPoint());
- }
- start->reset();
- delete start;
-
- SPCurve *end = new SPCurve();
- end->moveto(curve_it1->initialPoint());
- end->lineto(curve_it1->finalPoint());
- Geom::D2<Geom::SBasis> SBasisEnd = end->first_segment()->toSBasis();
-
- cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
- if (cubic) {
- lineHelper->lineto(SBasisEnd.valueAt(
- Geom::nearest_point((*cubic)[2], *end->first_segment())));
- } else {
- lineHelper->lineto(end->first_segment()->finalPoint());
- }
- end->reset();
- delete end;
- SBasisHelper = lineHelper->first_segment()->toSBasis();
- lineHelper->reset();
- delete lineHelper;
- startNode = SBasisHelper.valueAt(0.5);
- nCurve->curveto(nextPointAt1, nextPointAt2, startNode);
- nCurve->move_endpoints(startNode, startNode);
- } else {
- SPCurve *start = new SPCurve();
- start->moveto(path_it->begin()->initialPoint());
- start->lineto(path_it->begin()->finalPoint());
- startNode = start->first_segment()->initialPoint();
- start->reset();
- delete start;
- nCurve->curveto(nextPointAt1, nextPointAt2, nextPointAt3);
- nCurve->move_endpoints(startNode, nextPointAt3);
- }
//y cerramos la curva
if (path_it->closed()) {
nCurve->closepath_current();
@@ -349,22 +298,22 @@ Gtk::Widget *LPEBSpline::newWidget()
void LPEBSpline::toDefaultWeight(Gtk::Widget *widgWeight)
{
- weight.param_set_value(0.3334);
- changeWeight(0.3334);
+ weight.param_set_value(defaultStartPower);
+ changeWeight(defaultStartPower);
Gtk::HBox * scalarParameter = dynamic_cast<Gtk::HBox *>(widgWeight);
std::vector< Gtk::Widget* > childList = scalarParameter->get_children();
Gtk::Entry* entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]);
- entryWidg->set_text("0.3334");
+ entryWidg->set_text("defaultStartPower");
}
void LPEBSpline::toMakeCusp(Gtk::Widget *widgWeight)
{
- weight.param_set_value(0.0000);
- changeWeight(0.0000);
+ weight.param_set_value(noPower);
+ changeWeight(noPower);
Gtk::HBox * scalarParameter = dynamic_cast<Gtk::HBox *>(widgWeight);
std::vector< Gtk::Widget* > childList = scalarParameter->get_children();
Gtk::Entry* entryWidg = dynamic_cast<Gtk::Entry *>(childList[1]);
- entryWidg->set_text("0.0000");
+ entryWidg->set_text("noPower");
}
void LPEBSpline::toWeight()
@@ -402,7 +351,7 @@ bool LPEBSpline::nodeIsSelected(Geom::Point nodePoint)
for (std::vector<Geom::Point>::iterator i = points.begin();
i != points.end(); ++i) {
Geom::Point p = *i;
- if (Geom::are_near(p, nodePoint, 0.0001)) {
+ if (Geom::are_near(p, nodePoint, handleCubicGap)) {
return true;
} else {
}
@@ -463,10 +412,6 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weightValue)
Geom::Point pointAt1(0, 0);
Geom::Point pointAt2(0, 0);
Geom::Point pointAt3(0, 0);
- Geom::Point nextPointAt0(0, 0);
- Geom::Point nextPointAt1(0, 0);
- Geom::Point nextPointAt2(0, 0);
- Geom::Point nextPointAt3(0, 0);
Geom::D2<Geom::SBasis> SBasisIn;
Geom::D2<Geom::SBasis> SBasisOut;
Geom::CubicBezier const *cubic = NULL;
@@ -490,7 +435,7 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weightValue)
//BSPline
nCurve->moveto(curve_it1->initialPoint());
//Recorremos todos los segmentos menos el último
- while (curve_it2 != curve_endit) {
+ while (curve_it1 != curve_endit) {
//previousPointAt3 = pointAt3;
//Calculamos los puntos que dividirían en tres segmentos iguales el path
//recto de entrada y de salida
@@ -505,33 +450,33 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weightValue)
if (cubic) {
if (!ignoreCusp || !Geom::are_near((*cubic)[1], pointAt0)) {
pointAt1 = SBasisIn.valueAt(weightValue);
- if (weightValue != 0.0000) {
+ if (weightValue != noPower) {
pointAt1 =
- Geom::Point(pointAt1[X] + 0.0001, pointAt1[Y] + 0.0001);
+ Geom::Point(pointAt1[X] + handleCubicGap, pointAt1[Y] + handleCubicGap);
}
} else {
pointAt1 = in->first_segment()->initialPoint();
}
if (!ignoreCusp || !Geom::are_near((*cubic)[2], pointAt3)) {
pointAt2 = SBasisIn.valueAt(1 - weightValue);
- if (weightValue != 0.0000) {
+ if (weightValue != noPower) {
pointAt2 =
- Geom::Point(pointAt2[X] + 0.0001, pointAt2[Y] + 0.0001);
+ Geom::Point(pointAt2[X] + handleCubicGap, pointAt2[Y] + handleCubicGap);
}
} else {
pointAt2 = in->first_segment()->finalPoint();
}
} else {
- if (!ignoreCusp && weightValue != 0.0000) {
+ if (!ignoreCusp && weightValue != noPower) {
pointAt1 = SBasisIn.valueAt(weightValue);
- if (weightValue != 0.0000) {
+ if (weightValue != noPower) {
pointAt1 =
- Geom::Point(pointAt1[X] + 0.0001, pointAt1[Y] + 0.0001);
+ Geom::Point(pointAt1[X] + handleCubicGap, pointAt1[Y] + handleCubicGap);
}
pointAt2 = SBasisIn.valueAt(1 - weightValue);
- if (weightValue != 0.0000) {
+ if (weightValue != noPower) {
pointAt2 =
- Geom::Point(pointAt2[X] + 0.0001, pointAt2[Y] + 0.0001);
+ Geom::Point(pointAt2[X] + handleCubicGap, pointAt2[Y] + handleCubicGap);
}
} else {
pointAt1 = in->first_segment()->initialPoint();
@@ -543,9 +488,9 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weightValue)
if (!ignoreCusp || !Geom::are_near((*cubic)[1], pointAt0)) {
if (nodeIsSelected(pointAt0)) {
pointAt1 = SBasisIn.valueAt(weightValue);
- if (weightValue != 0.0000) {
+ if (weightValue != noPower) {
pointAt1 =
- Geom::Point(pointAt1[X] + 0.0001, pointAt1[Y] + 0.0001);
+ Geom::Point(pointAt1[X] + handleCubicGap, pointAt1[Y] + handleCubicGap);
}
} else {
pointAt1 = (*cubic)[1];
@@ -556,9 +501,9 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weightValue)
if (!ignoreCusp || !Geom::are_near((*cubic)[2], pointAt3)) {
if (nodeIsSelected(pointAt3)) {
pointAt2 = SBasisIn.valueAt(1 - weightValue);
- if (weightValue != 0.0000) {
+ if (weightValue != noPower) {
pointAt2 =
- Geom::Point(pointAt2[X] + 0.0001, pointAt2[Y] + 0.0001);
+ Geom::Point(pointAt2[X] + handleCubicGap, pointAt2[Y] + handleCubicGap);
}
} else {
pointAt2 = (*cubic)[2];
@@ -567,18 +512,18 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weightValue)
pointAt2 = in->first_segment()->finalPoint();
}
} else {
- if (!ignoreCusp && weightValue != 0.000) {
+ if (!ignoreCusp && weightValue != noPower) {
if (nodeIsSelected(pointAt0)) {
pointAt1 = SBasisIn.valueAt(weightValue);
pointAt1 =
- Geom::Point(pointAt1[X] + 0.0001, pointAt1[Y] + 0.0001);
+ Geom::Point(pointAt1[X] + handleCubicGap, pointAt1[Y] + handleCubicGap);
} else {
pointAt1 = in->first_segment()->initialPoint();
}
if (nodeIsSelected(pointAt3)) {
pointAt2 = SBasisIn.valueAt(weightValue);
pointAt2 =
- Geom::Point(pointAt2[X] + 0.0001, pointAt2[Y] + 0.0001);
+ Geom::Point(pointAt2[X] + handleCubicGap, pointAt2[Y] + handleCubicGap);
} else {
pointAt2 = in->first_segment()->finalPoint();
}
@@ -601,110 +546,11 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weightValue)
++curve_it1;
++curve_it2;
}
- SPCurve *out = new SPCurve();
- out->moveto(curve_it1->initialPoint());
- out->lineto(curve_it1->finalPoint());
- SBasisOut = out->first_segment()->toSBasis();
- nextPointAt0 = out->first_segment()->initialPoint();
- nextPointAt3 = out->first_segment()->finalPoint();
- cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
- if (!onlySelected) {
- if (cubic) {
- if (!ignoreCusp || !Geom::are_near((*cubic)[1], nextPointAt0)) {
- nextPointAt1 = SBasisOut.valueAt(weightValue);
- if (weightValue != 0.0000) {
- nextPointAt1 =
- Geom::Point(nextPointAt1[X] + 0.0001, nextPointAt1[Y] + 0.0001);
- }
- } else {
- nextPointAt1 = out->first_segment()->initialPoint();
- }
- if (!ignoreCusp || !Geom::are_near((*cubic)[2], nextPointAt3)) {
- nextPointAt2 = SBasisOut.valueAt(1 - weightValue);
- if (weightValue != 0.0000) {
- nextPointAt2 =
- Geom::Point(nextPointAt2[X] + 0.0001, nextPointAt2[Y] + 0.0001);
- }
- } else {
- nextPointAt2 = out->first_segment()->finalPoint();
- }
- } else {
- if (!ignoreCusp && weightValue != 0.0000) {
- nextPointAt1 = SBasisOut.valueAt(weightValue);
- nextPointAt1 =
- Geom::Point(nextPointAt1[X] + 0.0001, nextPointAt1[Y] + 0.0001);
- nextPointAt2 = SBasisOut.valueAt(1 - weightValue);
- nextPointAt2 =
- Geom::Point(nextPointAt2[X] + 0.0001, nextPointAt2[Y] + 0.0001);
- } else {
- nextPointAt1 = out->first_segment()->initialPoint();
- nextPointAt2 = out->first_segment()->finalPoint();
- }
- }
- } else {
- if (cubic) {
- if (!ignoreCusp || !Geom::are_near((*cubic)[1], nextPointAt0)) {
- if (nodeIsSelected(nextPointAt0)) {
- nextPointAt1 = SBasisOut.valueAt(weightValue);
- if (weightValue != 0.0000) {
- nextPointAt1 = Geom::Point(nextPointAt1[X] + 0.0001,
- nextPointAt1[Y] + 0.0001);
- }
- } else {
- nextPointAt1 = (*cubic)[1];
- }
- } else {
- nextPointAt1 = out->first_segment()->initialPoint();
- }
- if (!ignoreCusp || !Geom::are_near((*cubic)[2], nextPointAt3)) {
- if (nodeIsSelected(nextPointAt3)) {
- nextPointAt2 = SBasisOut.valueAt(1 - weightValue);
- if (weightValue != 0.0000) {
- nextPointAt2 = Geom::Point(nextPointAt2[X] + 0.0001,
- nextPointAt2[Y] + 0.0001);
- }
- } else {
- nextPointAt2 = (*cubic)[2];
- }
- } else {
- nextPointAt2 = out->first_segment()->finalPoint();
- }
- } else {
- if (!ignoreCusp && weightValue != 0.0000) {
- if (nodeIsSelected(nextPointAt0)) {
- nextPointAt1 = SBasisOut.valueAt(weightValue);
- nextPointAt1 =
- Geom::Point(nextPointAt1[X] + 0.0001, nextPointAt1[Y] + 0.0001);
- } else {
- nextPointAt1 = out->first_segment()->initialPoint();
- }
- if (nodeIsSelected(nextPointAt3)) {
- nextPointAt2 = SBasisOut.valueAt(weightValue);
- nextPointAt2 =
- Geom::Point(nextPointAt2[X] + 0.0001, nextPointAt2[Y] + 0.0001);
- } else {
- nextPointAt2 = out->first_segment()->finalPoint();
- }
- } else {
- nextPointAt1 = out->first_segment()->initialPoint();
- nextPointAt2 = out->first_segment()->finalPoint();
- }
- }
- }
- out->reset();
- delete out;
- //Aberiguamos la ultima parte de la curva correspondiente al último
- //segmento
- //Y hacemos lo propio con el path de salida
- //nextPointAt0 = curveOut.valueAt(0);
if (path_it->closed()) {
- nCurve->curveto(nextPointAt1, nextPointAt2,
- path_it->begin()->initialPoint());
nCurve->move_endpoints(path_it->begin()->initialPoint(),
path_it->begin()->initialPoint());
} else {
- nCurve->curveto(nextPointAt1, nextPointAt2, nextPointAt3);
- nCurve->move_endpoints(path_it->begin()->initialPoint(), nextPointAt3);
+ nCurve->move_endpoints(path_it->begin()->initialPoint(), pointAt3);
}
//y cerramos la curva
if (path_it->closed()) {
diff --git a/src/live_effects/lpe-copy_rotate.cpp b/src/live_effects/lpe-copy_rotate.cpp
index 65bbcdad1..e466093d3 100644
--- a/src/live_effects/lpe-copy_rotate.cpp
+++ b/src/live_effects/lpe-copy_rotate.cpp
@@ -12,6 +12,7 @@
*/
#include <glibmm/i18n.h>
+#include <gdk/gdk.h>
#include "live_effects/lpe-copy_rotate.h"
#include "sp-shape.h"
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..7cf354044
--- /dev/null
+++ b/src/live_effects/lpe-fill-between-many.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#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"
+
+#include <glibmm/i18n.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-fillet-chamfer.cpp b/src/live_effects/lpe-fillet-chamfer.cpp
index bfb2c3ba4..857e9d9cc 100644
--- a/src/live_effects/lpe-fillet-chamfer.cpp
+++ b/src/live_effects/lpe-fillet-chamfer.cpp
@@ -22,6 +22,9 @@
#include "desktop.h"
#include "display/curve.h"
#include "helper/geom-nodetype.h"
+#include "helper/geom-curves.h"
+#include "helper/geom.h"
+
#include "live_effects/parameter/filletchamferpointarray.h"
// for programmatically updating knots
@@ -49,6 +52,8 @@ LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject) :
ignore_radius_0(_("Ignore 0 radius knots"), _("Ignore 0 radius knots"), "ignore_radius_0", &wr, this, false),
only_selected(_("Change only selected nodes"), _("Change only selected nodes"), "only_selected", &wr, this, false),
flexible(_("Flexible radius size (%)"), _("Flexible radius size (%)"), "flexible", &wr, this, false),
+ force_arcs(_("Use arcs in cubic curves"), _("Use arcs in cubic curves"), "force_arcs", &wr, this, false),
+ use_knot_distance(_("Use knots distance instead radius"), _("Use knots distance instead radius"), "use_knot_distance", &wr, this, false),
unit(_("Unit"), _("Unit"), "unit", &wr, this),
radius(_("Radius (unit or %)"), _("Radius, in unit or %"), "radius", &wr, this, 0.),
helper_size(_("Helper size with direction"), _("Helper size with direction"), "helper_size", &wr, this, 0)
@@ -58,9 +63,11 @@ LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject) :
registerParameter(&radius);
registerParameter(&helper_size);
registerParameter(&flexible);
+ registerParameter(&use_knot_distance);
registerParameter(&ignore_radius_0);
registerParameter(&only_selected);
registerParameter(&hide_knots);
+ registerParameter(&force_arcs);
radius.param_set_range(0., infinity());
radius.param_set_increments(1, 1);
@@ -270,10 +277,10 @@ void LPEFilletChamfer::doUpdateFillet(std::vector<Geom::Path> const& original_pa
}
std::vector<Point> filletChamferData = fillet_chamfer_values.data();
std::vector<Geom::Point> result;
+ std::vector<Geom::Path> original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv);
int counter = 0;
- for (PathVector::const_iterator path_it = original_pathv.begin();
- path_it != original_pathv.end(); ++path_it) {
- int pathCounter = 0;
+ for (PathVector::const_iterator path_it = original_pathv_processed.begin();
+ path_it != original_pathv_processed.end(); ++path_it) {
if (path_it->empty())
continue;
@@ -289,6 +296,9 @@ void LPEFilletChamfer::doUpdateFillet(std::vector<Geom::Path> const& original_pa
double powerend = 0;
while (curve_it1 != curve_endit) {
powerend = power;
+ if (power < 0 && !use_knot_distance) {
+ powerend = fillet_chamfer_values.rad_to_len(counter,powerend);
+ }
if (power > 0) {
powerend = counter + (power / 100);
}
@@ -306,7 +316,6 @@ void LPEFilletChamfer::doUpdateFillet(std::vector<Geom::Path> const& original_pa
++curve_it1;
++curve_it2;
counter++;
- pathCounter++;
}
}
fillet_chamfer_values.param_set_and_write_new_value(result);
@@ -334,8 +343,9 @@ void LPEFilletChamfer::doChangeType(std::vector<Geom::Path> const& original_path
}
std::vector<Point> filletChamferData = fillet_chamfer_values.data();
std::vector<Geom::Point> result;
+ std::vector<Geom::Path> original_pathv_processed = pathv_to_linear_and_cubic_beziers(original_pathv);
int counter = 0;
- for (PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
+ for (PathVector::const_iterator path_it = original_pathv_processed.begin(); path_it != original_pathv_processed.end(); ++path_it) {
int pathCounter = 0;
if (path_it->empty())
continue;
@@ -378,7 +388,7 @@ void LPEFilletChamfer::doOnApply(SPLPEItem const *lpeItem)
{
if (SP_IS_SHAPE(lpeItem)) {
std::vector<Point> point;
- PathVector const &original_pathv = SP_SHAPE(lpeItem)->_curve->get_pathvector();
+ PathVector const &original_pathv = pathv_to_linear_and_cubic_beziers(SP_SHAPE(lpeItem)->_curve->get_pathvector());
Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(original_pathv);
for (PathVector::const_iterator path_it = original_pathv.begin(); path_it != original_pathv.end(); ++path_it) {
if (path_it->empty())
@@ -401,10 +411,15 @@ void LPEFilletChamfer::doOnApply(SPLPEItem const *lpeItem)
}
int counter = 0;
while (curve_it1 != curve_endit) {
+ std::pair<std::size_t, std::size_t> positions = fillet_chamfer_values.get_positions(counter, original_pathv);
Geom::NodeType nodetype;
- if (counter == 0) {
+ if (positions.second == 0) {
if (path_it->closed()) {
- nodetype = get_nodetype(path_it->back_default(), *curve_it1);
+ Piecewise<D2<SBasis> > u;
+ u.push_cut(0);
+ u.push(pwd2_in[fillet_chamfer_values.last_index(counter, original_pathv)], 1);
+ Geom::Curve const * A = path_from_piecewise(u, 0.1)[0][0].duplicate();
+ nodetype = get_nodetype(*A, *curve_it1);
} else {
nodetype = NODE_NONE;
}
@@ -433,12 +448,15 @@ void LPEFilletChamfer::doBeforeEffect(SPLPEItem const *lpeItem)
{
if (SP_IS_SHAPE(lpeItem)) {
fillet_chamfer_values.set_helper_size(helper_size);
+ fillet_chamfer_values.set_use_distance(use_knot_distance);
fillet_chamfer_values.set_unit(unit.get_abbreviation());
- SPCurve *c = SP_IS_PATH(lpeItem) ? static_cast<SPPath const *>(lpeItem)->get_original_curve() : SP_SHAPE(lpeItem)->getCurve();
+ SPCurve *c = SP_IS_PATH(lpeItem) ? static_cast<SPPath const *>(lpeItem)
+ ->get_original_curve()
+ : SP_SHAPE(lpeItem)->getCurve();
std::vector<Point> filletChamferData = fillet_chamfer_values.data();
if (!filletChamferData.empty() && getKnotsNumber(c) != (int)
filletChamferData.size()) {
- PathVector const original_pathv = c->get_pathvector();
+ PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(original_pathv);
fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(pwd2_in);
}
@@ -450,7 +468,7 @@ void LPEFilletChamfer::doBeforeEffect(SPLPEItem const *lpeItem)
int LPEFilletChamfer::getKnotsNumber(SPCurve const *c)
{
int nKnots = c->nodes_in_path();
- PathVector const pv = c->get_pathvector();
+ PathVector const pv = pathv_to_linear_and_cubic_beziers(c->get_pathvector());
for (std::vector<Geom::Path>::const_iterator path_it = pv.begin();
path_it != pv.end(); ++path_it) {
if (!(*path_it).closed()) {
@@ -464,8 +482,7 @@ void
LPEFilletChamfer::adjustForNewPath(std::vector<Geom::Path> const &path_in)
{
if (!path_in.empty()) {
- fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(path_in[0]
- .toPwSb());
+ fillet_chamfer_values.recalculate_controlpoints_for_new_pwd2(pathv_to_linear_and_cubic_beziers(path_in)[0].toPwSb());
}
}
@@ -473,17 +490,17 @@ std::vector<Geom::Path>
LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
{
std::vector<Geom::Path> pathvector_out;
- Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(path_in);
+ Piecewise<D2<SBasis> > pwd2_in = paths_to_pw(pathv_to_linear_and_cubic_beziers(path_in));
pwd2_in = remove_short_cuts(pwd2_in, .01);
Piecewise<D2<SBasis> > der = derivative(pwd2_in);
Piecewise<D2<SBasis> > n = rot90(unitVector(der));
fillet_chamfer_values.set_pwd2(pwd2_in, n);
std::vector<Point> filletChamferData = fillet_chamfer_values.data();
unsigned int counter = 0;
- //from http://launchpadlibrarian.net/12692602/rcp.svg
const double K = (4.0 / 3.0) * (sqrt(2.0) - 1.0);
- for (PathVector::const_iterator path_it = path_in.begin();
- path_it != path_in.end(); ++path_it) {
+ std::vector<Geom::Path> path_in_processed = pathv_to_linear_and_cubic_beziers(path_in);
+ for (PathVector::const_iterator path_it = path_in_processed.begin();
+ path_it != path_in_processed.end(); ++path_it) {
if (path_it->empty())
continue;
Geom::Path path_out;
@@ -504,81 +521,24 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
}
unsigned int counterCurves = 0;
while (curve_it1 != curve_endit) {
- Coord it1_length = (*curve_it1).length(tolerance);
- double time_it1, time_it2, time_it1_B, intpart;
- time_it1 = modf(
- fillet_chamfer_values.to_time(counter, filletChamferData[counter][X]),
- &intpart);
- if (filletChamferData[counter][Y] == 0) {
- time_it1 = 0;
- }
- if (path_it->closed() && curve_it2 == curve_endit) {
- time_it2 = modf(fillet_chamfer_values.to_time(
- counter - counterCurves,
- filletChamferData[counter - counterCurves][X]),
- &intpart);
- } else if (!path_it->closed() && curve_it2 == curve_endit){
- time_it2 = 0;
- } else {
- time_it2 = modf(fillet_chamfer_values.to_time(
- counter + 1, filletChamferData[counter + 1][X]),
- &intpart);
- }
- double resultLenght = 0;
- time_it1_B = 1;
- if (path_it->closed() && curve_it2 == curve_endit) {
- resultLenght =
- it1_length + fillet_chamfer_values.to_len(
- counter - counterCurves,
- filletChamferData[counter - counterCurves][X]);
- } else if (!path_it->closed() && curve_it2 == curve_endit){
- resultLenght = 0;
- } else {
- resultLenght =
- it1_length + fillet_chamfer_values.to_len(
- counter + 1, filletChamferData[counter + 1][X]);
- }
- if (resultLenght > 0 && time_it2 != 0) {
- time_it1_B = modf(fillet_chamfer_values.to_time(counter, -resultLenght),
- &intpart);
- } else {
- if (time_it2 == 0) {
- time_it1_B = 1;
- } else {
- time_it1_B = gapHelper;
- }
- }
- if (path_it->closed() && curve_it2 == curve_endit &&
- filletChamferData[counter - counterCurves][Y] == 0) {
- time_it1_B = 1;
- time_it2 = 0;
- } else if (path_it->size() > counterCurves + 1 &&
- filletChamferData[counter + 1][Y] == 0) {
- time_it1_B = 1;
- time_it2 = 0;
+ Curve *curve_it2Fixed = (*path_it->begin()).duplicate();
+ if(!path_it->closed() || curve_it2 != curve_endit){
+ curve_it2Fixed = (*curve_it2).duplicate();
}
- if (time_it1_B < time_it1) {
- time_it1_B = time_it1 + gapHelper;
- }
- Curve *knotCurve1 = curve_it1->portion(time_it1, time_it1_B);
+ bool last = curve_it2 == curve_endit;
+ std::vector<double> times = fillet_chamfer_values.get_times(counter, path_in, last);
+ Curve *knotCurve1 = curve_it1->portion(times[0], times[1]);
if (counterCurves > 0) {
knotCurve1->setInitial(path_out.finalPoint());
} else {
- path_out.start((*curve_it1).pointAt(time_it1));
- }
- Curve *knotCurve2 = (*path_it).front().portion(time_it2, 1);
- if (curve_it2 != curve_endit) {
- knotCurve2 = (*curve_it2).portion(time_it2, 1);
+ path_out.start((*curve_it1).pointAt(times[0]));
}
+ Curve *knotCurve2 = curve_it2Fixed->portion(times[2], 1);
Point startArcPoint = knotCurve1->finalPoint();
- Point endArcPoint = (*path_it).front().pointAt(time_it2);
- if (curve_it2 != curve_endit) {
- endArcPoint = (*curve_it2).pointAt(time_it2);
- }
+ Point endArcPoint = curve_it2Fixed->pointAt(times[2]);
double k1 = distance(startArcPoint, curve_it1->finalPoint()) * K;
double k2 = distance(endArcPoint, curve_it1->finalPoint()) * K;
- Geom::CubicBezier const *cubic1 =
- dynamic_cast<Geom::CubicBezier const *>(&*knotCurve1);
+ Geom::CubicBezier const *cubic1 = dynamic_cast<Geom::CubicBezier const *>(&*knotCurve1);
Ray ray1(startArcPoint, curve_it1->finalPoint());
if (cubic1) {
ray1.setPoints((*cubic1)[2], startArcPoint);
@@ -591,8 +551,7 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
ray2.setPoints(endArcPoint, (*cubic2)[1]);
}
Point handle2 = endArcPoint - Point::polar(ray2.angle(),k2);
- bool ccwToggle = cross(curve_it1->finalPoint() - startArcPoint,
- endArcPoint - startArcPoint) < 0;
+ bool ccwToggle = cross(curve_it1->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0;
double angle = angle_between(ray1, ray2, ccwToggle);
double handleAngle = ray1.angle() - angle;
if (ccwToggle) {
@@ -604,14 +563,22 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
handleAngle = ray2.angle() - angle;
}
Point inverseHandle2 = endArcPoint - Point::polar(handleAngle,k2);
- if (time_it1_B != 1) {
- if (time_it1_B != gapHelper && time_it1_B != time_it1 + gapHelper) {
+ //straigth lines arc based
+ Line const x_line(Geom::Point(0,0),Geom::Point(1,0));
+ Line const angled_line(startArcPoint,endArcPoint);
+ double angleArc = Geom::angle_between( x_line,angled_line);
+ double radius = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint))/sin(angle/2.0);
+ Coord rx = radius;
+ Coord ry = rx;
+
+ if (times[1] != 1) {
+ if (times[1] != gapHelper && times[1] != times[0] + gapHelper) {
path_out.append(*knotCurve1);
}
int type = 0;
- if(path_it->closed() && curve_it2 == curve_endit){
+ if(path_it->closed() && last){
type = abs(filletChamferData[counter - counterCurves][Y]);
- } else if (!path_it->closed() && curve_it2 == curve_endit){
+ } else if (!path_it->closed() && last){
//0
} else {
type = abs(filletChamferData[counter + 1][Y]);
@@ -624,15 +591,23 @@ LPEFilletChamfer::doEffect_path(std::vector<Geom::Path> const &path_in)
}
path_out.appendNew<Geom::LineSegment>(endArcPoint);
} else if (type == 2) {
- path_out.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2,
- endArcPoint);
+ if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed)) || force_arcs){
+ ccwToggle = ccwToggle?0:1;
+ path_out.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
+ }else{
+ path_out.appendNew<Geom::CubicBezier>(inverseHandle1, inverseHandle2, endArcPoint);
+ }
} else {
- path_out.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint);
+ if((is_straight_curve(*curve_it1) && is_straight_curve(*curve_it2Fixed)) || force_arcs){
+ path_out.appendNew<SVGEllipticalArc>(rx, ry, angleArc, 0, ccwToggle, endArcPoint);
+ } else {
+ path_out.appendNew<Geom::CubicBezier>(handle1, handle2, endArcPoint);
+ }
}
} else {
path_out.append(*knotCurve1);
}
- if (path_it->closed() && curve_it2 == curve_endit) {
+ if (path_it->closed() && last) {
path_out.close();
}
++curve_it1;
diff --git a/src/live_effects/lpe-fillet-chamfer.h b/src/live_effects/lpe-fillet-chamfer.h
index 420425650..49b407b71 100644
--- a/src/live_effects/lpe-fillet-chamfer.h
+++ b/src/live_effects/lpe-fillet-chamfer.h
@@ -15,7 +15,13 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include <glibmm/threads.h>
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if defined(GLIBMM_DISABLE_DEPRECATED) && defined(HAVE_GLIBMM_THREADS_H)
+# include <glibmm/threads.h>
+#endif
#include "live_effects/parameter/enum.h"
#include "live_effects/parameter/bool.h"
@@ -61,6 +67,8 @@ private:
BoolParam ignore_radius_0;
BoolParam only_selected;
BoolParam flexible;
+ BoolParam force_arcs;
+ BoolParam use_knot_distance;
UnitParam unit;
ScalarParam radius;
ScalarParam helper_size;
diff --git a/src/live_effects/lpe-interpolate_points.cpp b/src/live_effects/lpe-interpolate_points.cpp
index 865b46ca7..4ac139752 100644
--- a/src/live_effects/lpe-interpolate_points.cpp
+++ b/src/live_effects/lpe-interpolate_points.cpp
@@ -40,7 +40,7 @@ LPEInterpolatePoints::LPEInterpolatePoints(LivePathEffectObject *lpeobject)
{
show_orig_path = false;
- registerParameter( dynamic_cast<Parameter *>(&interpolator_type) );
+ registerParameter( &interpolator_type );
}
LPEInterpolatePoints::~LPEInterpolatePoints()
diff --git a/src/live_effects/lpe-jointype.cpp b/src/live_effects/lpe-jointype.cpp
new file mode 100644
index 000000000..bf2526986
--- /dev/null
+++ b/src/live_effects/lpe-jointype.cpp
@@ -0,0 +1,190 @@
+/* Authors:
+*
+* Liam P White
+*
+* Copyright (C) 2014 Authors
+*
+* Released under GNU GPL v2, read the file 'COPYING' for more information
+*/
+
+#include "live_effects/parameter/enum.h"
+#include "live_effects/pathoutlineprovider.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 <2geom/path.h>
+#include <2geom/svg-elliptical-arc.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"},
+ {BUTT_LEANED, N_("Leaned"), "leaned"}
+};
+
+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, 1.),
+ 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, LINEJOIN_EXTRAPOLATED),
+ start_lean(_("Start path lean"), _("Start path lean"), "start_lean", &wr, this, 0.),
+ end_lean(_("End path lean"), _("End path lean"), "end_lean", &wr, this, 0.),
+ 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(&linecap_type);
+ registerParameter(&line_width);
+ registerParameter(&linejoin_type);
+ registerParameter(&start_lean);
+ registerParameter(&end_lean);
+ registerParameter(&miter_limit);
+ registerParameter(&attempt_force_join);
+ was_initialized = false;
+ start_lean.param_set_range(-1,1);
+ start_lean.param_set_increments(0.1, 0.1);
+ start_lean.param_set_digits(4);
+ end_lean.param_set_range(-1,1);
+ end_lean.param_set_increments(0.1, 0.1);
+ end_lean.param_set_digits(4);
+}
+
+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 (true) {
+ 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 (true) {
+ 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();
+ }
+}
+
+// NOTE: I originally had all the outliner functions defined in here, but they were actually useful
+// enough for other LPEs so I moved them all into pathoutlineprovider.cpp. The code here is just a
+// wrapper around it.
+std::vector<Geom::Path> LPEJoinType::doEffect_path(std::vector<Geom::Path> const & path_in)
+{
+ return Outline::PathVectorOutline(path_in, line_width, static_cast<ButtTypeMod>(linecap_type.get_value()),
+ static_cast<LineJoinType>(linejoin_type.get_value()),
+ (attempt_force_join ? std::numeric_limits<double>::max() : miter_limit),
+ start_lean/2 ,end_lean/2);
+}
+
+} // 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:fileencoding=utf-8 :
diff --git a/src/live_effects/lpe-jointype.h b/src/live_effects/lpe-jointype.h
new file mode 100644
index 000000000..73705666d
--- /dev/null
+++ b/src/live_effects/lpe-jointype.h
@@ -0,0 +1,57 @@
+/* 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 start_lean;
+ ScalarParam end_lean;
+ ScalarParam miter_limit;
+ BoolParam attempt_force_join;
+ bool was_initialized;
+};
+
+} //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:fileencoding=utf-8 :
diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp
index cac3a9347..3876aa24b 100644
--- a/src/live_effects/lpe-knot.cpp
+++ b/src/live_effects/lpe-knot.cpp
@@ -22,6 +22,7 @@
#include "knotholder.h"
#include <glibmm/i18n.h>
+#include <gdk/gdk.h>
#include <2geom/sbasis-to-bezier.h>
#include <2geom/sbasis.h>
@@ -536,6 +537,10 @@ LPEKnot::doBeforeEffect (SPLPEItem const* lpeitem)
{
using namespace Geom;
original_bbox(lpeitem);
+
+ if (SP_IS_PATH(lpeitem)) {
+ supplied_path = SP_PATH(lpeitem)->getCurve()->get_pathvector();
+ }
gpaths.clear();
gstroke_widths.clear();
diff --git a/src/live_effects/lpe-knot.h b/src/live_effects/lpe-knot.h
index f926bf085..080f32de2 100644
--- a/src/live_effects/lpe-knot.h
+++ b/src/live_effects/lpe-knot.h
@@ -64,7 +64,8 @@ public:
void addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item);
protected:
- virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
+ virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
+ std::vector<Geom::Path> supplied_path; //for knotholder business
private:
void updateSwitcher();
diff --git a/src/live_effects/lpe-powerstroke-interpolators.h b/src/live_effects/lpe-powerstroke-interpolators.h
index 986bd3544..3cde0b4b3 100644
--- a/src/live_effects/lpe-powerstroke-interpolators.h
+++ b/src/live_effects/lpe-powerstroke-interpolators.h
@@ -28,6 +28,7 @@ enum InterpolatorType {
INTERP_CUBICBEZIER,
INTERP_CUBICBEZIER_JOHAN,
INTERP_SPIRO,
+ INTERP_CUBICBEZIER_SMOOTH,
INTERP_CENTRIPETAL_CATMULLROM
};
@@ -134,6 +135,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:
@@ -261,6 +299,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();
case INTERP_CENTRIPETAL_CATMULLROM:
return new Geom::Interpolate::CentripetalCatmullRomInterpolator();
default:
diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp
index 5bfe88ed1..b1951d978 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"},
@@ -223,9 +229,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));
@@ -233,12 +237,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;
@@ -267,20 +271,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 (true) {
+ 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);
@@ -289,6 +325,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 (true) {
+ 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)
{
@@ -588,6 +663,9 @@ LPEPowerStroke::doEffect_path (std::vector<Geom::Path> const & path_in)
if (Geom::Interpolate::CubicBezierJohan *johan = dynamic_cast<Geom::Interpolate::CubicBezierJohan*>(interpolator)) {
johan->setBeta(interpolator_beta);
}
+ if (Geom::Interpolate::CubicBezierSmooth *smooth = dynamic_cast<Geom::Interpolate::CubicBezierSmooth*>(interpolator)) {
+ smooth->setBeta(interpolator_beta);
+ }
Geom::Path strokepath = interpolator->interpolateToPath(ts);
delete interpolator;
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/lpe-roughen.cpp b/src/live_effects/lpe-roughen.cpp
index 5f6acd6f4..b9d575e06 100644
--- a/src/live_effects/lpe-roughen.cpp
+++ b/src/live_effects/lpe-roughen.cpp
@@ -1,10 +1,21 @@
-/*
+/**
+ * @file
+ * Roughen LPE implementation. Creates roughen paths.
+ */
+/* Authors:
+ * Jabier Arraiza Cenoz <jabier.arraiza@marker.es>
+ *
+ * Thanks to all people involved specialy to Josh Andler for the idea and to the
+ * original extensions authors.
+ *
+ * Copyright (C) 2014 Authors
+ *
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include "live_effects/lpe-roughen.h"
-#include <gtkmm/separator.h>
+#include <gtkmm.h>
+#include "live_effects/lpe-roughen.h"
#include "display/curve.h"
#include "live_effects/parameter/parameter.h"
#include "helper/geom.h"
@@ -15,375 +26,299 @@ namespace Inkscape {
namespace LivePathEffect {
static const Util::EnumData<DivisionMethod> DivisionMethodData[DM_END] = {
- { DM_SEGMENTS, N_("By number of segments"), "segments" },
- { DM_SIZE, N_("By max. segment size"), "size" }
+ { DM_SEGMENTS, N_("By number of segments"), "segments" },
+ { DM_SIZE, N_("By max. segment size"), "size" }
};
static const Util::EnumDataConverter<DivisionMethod>
- DMConverter(DivisionMethodData, DM_END);
+DMConverter(DivisionMethodData, DM_END);
LPERoughen::LPERoughen(LivePathEffectObject *lpeobject)
: Effect(lpeobject),
// initialise your parameters here:
- unit(_("Unit:"), _("Unit"), "unit", &wr, this),
- method(_("Method:"), _("Division method"), "method", DMConverter, &wr,
+ unit(_("Unit"), _("Unit"), "unit", &wr, this),
+ method(_("Method"), _("Division method"), "method", DMConverter, &wr,
this, DM_SEGMENTS),
- maxSegmentSize(_("Max. segment size:"), _("Max. segment size"),
+ maxSegmentSize(_("Max. segment size"), _("Max. segment size"),
"maxSegmentSize", &wr, this, 10.),
- segments(_("Number of segments:"), _("Number of segments"), "segments",
+ segments(_("Number of segments"), _("Number of segments"), "segments",
&wr, this, 2),
- displaceX(_("Max. displacement in X:"), _("Max. displacement in X"),
+ displaceX(_("Max. displacement in X"), _("Max. displacement in X"),
"displaceX", &wr, this, 10.),
- displaceY(_("Max. displacement in Y:"), _("Max. displacement in Y"),
+ displaceY(_("Max. displacement in Y"), _("Max. displacement in Y"),
"displaceY", &wr, this, 10.),
shiftNodes(_("Shift nodes"), _("Shift nodes"), "shiftNodes", &wr, this,
true),
shiftNodeHandles(_("Shift node handles"), _("Shift node handles"),
- "shiftNodeHandles", &wr, this, true) {
- registerParameter(dynamic_cast<Parameter *>(&unit));
- registerParameter(dynamic_cast<Parameter *>(&method));
- registerParameter(dynamic_cast<Parameter *>(&maxSegmentSize));
- registerParameter(dynamic_cast<Parameter *>(&segments));
- registerParameter(dynamic_cast<Parameter *>(&displaceX));
- registerParameter(dynamic_cast<Parameter *>(&displaceY));
- registerParameter(dynamic_cast<Parameter *>(&shiftNodes));
- registerParameter(dynamic_cast<Parameter *>(&shiftNodeHandles));
- displaceX.param_set_range(0., Geom::infinity());
- displaceY.param_set_range(0., Geom::infinity());
- maxSegmentSize.param_set_range(0., Geom::infinity());
- maxSegmentSize.param_set_increments(1, 1);
- maxSegmentSize.param_set_digits(1);
- segments.param_set_range(1, Geom::infinity());
- segments.param_set_increments(1, 1);
- segments.param_set_digits(0);
+ "shiftNodeHandles", &wr, this, true)
+{
+ registerParameter(&unit);
+ registerParameter(&method);
+ registerParameter(&maxSegmentSize);
+ registerParameter(&segments);
+ registerParameter(&displaceX);
+ registerParameter(&displaceY);
+ registerParameter(&shiftNodes);
+ registerParameter(&shiftNodeHandles);
+ displaceX.param_set_range(0., Geom::infinity());
+ displaceY.param_set_range(0., Geom::infinity());
+ maxSegmentSize.param_set_range(0., Geom::infinity());
+ maxSegmentSize.param_set_increments(1, 1);
+ maxSegmentSize.param_set_digits(1);
+ segments.param_set_range(1, Geom::infinity());
+ segments.param_set_increments(1, 1);
+ segments.param_set_digits(0);
}
LPERoughen::~LPERoughen() {}
-void LPERoughen::doBeforeEffect(SPLPEItem const */*lpeitem*/) {
- displaceX.resetRandomizer();
- displaceY.resetRandomizer();
- srand(1);
+void LPERoughen::doBeforeEffect(SPLPEItem const */*lpeitem*/)
+{
+ displaceX.resetRandomizer();
+ displaceY.resetRandomizer();
+ srand(1);
}
-Gtk::Widget *LPERoughen::newWidget() {
- // use manage here, because after deletion of Effect object, others might
- // still be pointing to this widget.
- Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
- vbox->set_border_width(5);
- vbox->set_homogeneous(false);
- vbox->set_spacing(2);
- std::vector<Parameter *>::iterator it = param_vector.begin();
- while (it != param_vector.end()) {
- if ((*it)->widget_is_visible) {
- Parameter *param = *it;
- Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
- if (param->param_key == "unit") {
- Gtk::Label *unitLabel = Gtk::manage(new Gtk::Label(
- Glib::ustring(_("<b>Roughen unit</b>")), Gtk::ALIGN_START));
- unitLabel->set_use_markup(true);
- vbox->pack_start(*unitLabel, false, false, 2);
-#if WITH_GTKMM_3_0
- vbox->pack_start(*Gtk::manage(new Gtk::Separator()),
-#else
- vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()),
-#endif
- Gtk::PACK_EXPAND_WIDGET);
- }
- if (param->param_key == "method") {
- Gtk::Label *methodLabel = Gtk::manage(new Gtk::Label(
- Glib::ustring(_("<b>Add nodes</b> Subdivide each segment")),
- Gtk::ALIGN_START));
- methodLabel->set_use_markup(true);
- vbox->pack_start(*methodLabel, false, false, 2);
-#if WITH_GTKMM_3_0
- vbox->pack_start(*Gtk::manage(new Gtk::Separator()),
-#else
- vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()),
-#endif
- Gtk::PACK_EXPAND_WIDGET);
- }
- if (param->param_key == "displaceX") {
- Gtk::Label *displaceXLabel = Gtk::manage(new Gtk::Label(
- Glib::ustring(_("<b>Jitter nodes</b> Move nodes/handles")),
- Gtk::ALIGN_START));
- displaceXLabel->set_use_markup(true);
- vbox->pack_start(*displaceXLabel, false, false, 2);
-#if WITH_GTKMM_3_0
- vbox->pack_start(*Gtk::manage(new Gtk::Separator()),
-#else
- vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()),
-#endif
- Gtk::PACK_EXPAND_WIDGET);
- }
- Glib::ustring *tip = param->param_getTooltip();
- if (widg) {
- vbox->pack_start(*widg, true, true, 2);
- if (tip) {
- widg->set_tooltip_text(*tip);
- } else {
- widg->set_tooltip_text("");
- widg->set_has_tooltip(false);
+Gtk::Widget *LPERoughen::newWidget()
+{
+ Gtk::VBox *vbox = Gtk::manage(new Gtk::VBox(Effect::newWidget()));
+ vbox->set_border_width(5);
+ vbox->set_homogeneous(false);
+ vbox->set_spacing(2);
+ std::vector<Parameter *>::iterator it = param_vector.begin();
+ while (it != param_vector.end()) {
+ if ((*it)->widget_is_visible) {
+ Parameter *param = *it;
+ Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
+ if (param->param_key == "unit") {
+ Gtk::Label *unitLabel = Gtk::manage(new Gtk::Label(
+ Glib::ustring(_("<b>Roughen unit</b>")), Gtk::ALIGN_START));
+ unitLabel->set_use_markup(true);
+ vbox->pack_start(*unitLabel, false, false, 2);
+ vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()),
+ Gtk::PACK_EXPAND_WIDGET);
+ }
+ if (param->param_key == "method") {
+ Gtk::Label *methodLabel = Gtk::manage(new Gtk::Label(
+ Glib::ustring(_("<b>Add nodes</b> Subdivide each segment")),
+ Gtk::ALIGN_START));
+ methodLabel->set_use_markup(true);
+ vbox->pack_start(*methodLabel, false, false, 2);
+ vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()),
+ Gtk::PACK_EXPAND_WIDGET);
+ }
+ if (param->param_key == "displaceX") {
+ Gtk::Label *displaceXLabel = Gtk::manage(new Gtk::Label(
+ Glib::ustring(_("<b>Jitter nodes</b> Move nodes/handles")),
+ Gtk::ALIGN_START));
+ displaceXLabel->set_use_markup(true);
+ vbox->pack_start(*displaceXLabel, false, false, 2);
+ vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()),
+ Gtk::PACK_EXPAND_WIDGET);
+ }
+ Glib::ustring *tip = param->param_getTooltip();
+ if (widg) {
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
}
- }
- }
- ++it;
- }
- return dynamic_cast<Gtk::Widget *>(vbox);
+ ++it;
+ }
+ return dynamic_cast<Gtk::Widget *>(vbox);
}
-double LPERoughen::sign(double randNumber) {
- if (rand() % 100 < 49) {
- randNumber *= -1.;
- }
- return randNumber;
+double LPERoughen::sign(double randNumber)
+{
+ if (rand() % 100 < 49) {
+ randNumber *= -1.;
+ }
+ return randNumber;
}
-Geom::Point LPERoughen::randomize() {
- double displaceXParsed = Inkscape::Util::Quantity::convert(
- displaceX, unit.get_abbreviation(), "px");
- double displaceYParsed = Inkscape::Util::Quantity::convert(
- displaceY, unit.get_abbreviation(), "px");
- //maybe is better divide this point by 2...
- Geom::Point output =
- Geom::Point(sign(displaceXParsed), sign(displaceYParsed));
- return output;
-}
+Geom::Point LPERoughen::randomize()
+{
+ double displaceXParsed = Inkscape::Util::Quantity::convert(
+ displaceX, unit.get_abbreviation(), "px");
+ double displaceYParsed = Inkscape::Util::Quantity::convert(
+ displaceY, unit.get_abbreviation(), "px");
-void LPERoughen::doEffect(SPCurve *curve) {
- Geom::PathVector const original_pathv =
- pathv_to_linear_and_cubic_beziers(curve->get_pathvector());
- curve->reset();
+ Geom::Point output = Geom::Point(sign(displaceXParsed), sign(displaceYParsed));
+ return output;
+}
- //Recorremos todos los paths a los que queremos aplicar el efecto, hasta el
- //penúltimo
- for (Geom::PathVector::const_iterator path_it = original_pathv.begin();
- path_it != original_pathv.end(); ++path_it) {
- //Si está vacío...
- if (path_it->empty())
- continue;
- //Itreadores
+void LPERoughen::doEffect(SPCurve *curve)
+{
+ Geom::PathVector const original_pathv =
+ pathv_to_linear_and_cubic_beziers(curve->get_pathvector());
+ curve->reset();
+ for (Geom::PathVector::const_iterator path_it = original_pathv.begin();
+ path_it != original_pathv.end(); ++path_it) {
+ if (path_it->empty())
+ continue;
- Geom::Path::const_iterator curve_it1 = path_it->begin(); // incoming curve
- Geom::Path::const_iterator curve_it2 =
- ++(path_it->begin()); // outgoing curve
- Geom::Path::const_iterator curve_endit =
- path_it->end_default(); // this determines when the loop has to stop
- //Creamos las lineas rectas que unen todos los puntos del trazado y donde se
- //calcularán
- //los puntos clave para los manejadores.
- //Esto hace que la curva BSpline no pierda su condición aunque se trasladen
- //dichos manejadores
- SPCurve *nCurve = new SPCurve();
- if (path_it->closed()) {
- // if the path is closed, maybe we have to stop a bit earlier because the
- // closing line segment has zerolength.
- const Geom::Curve &closingline =
- path_it->back_closed(); // the closing line segment is always of type
- // Geom::LineSegment.
- if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
- // closingline.isDegenerate() did not work, because it only checks for
- // *exact* zero length, which goes wrong for relative coordinates and
- // rounding errors...
- // the closing line segment has zero-length. So stop before that one!
- curve_endit = path_it->end_open();
- }
- }
- //Si la curva está cerrada calculamos el punto donde
- //deveria estar el nodo BSpline de cierre/inicio de la curva
- //en posible caso de que se cierre con una linea recta creando un nodo
- //BSPline
- Geom::Point initialMove(0, 0);
- if (shiftNodes) {
- initialMove = randomize();
- }
- Geom::Point initialPoint = curve_it1->initialPoint() + initialMove;
- nCurve->moveto(initialPoint);
- Geom::Point A0(0, 0);
- Geom::Point A1(0, 0);
- Geom::Point A2(0, 0);
- Geom::Point A3(0, 0);
- while (curve_it2 != curve_endit) {
- //aumentamos los valores para el siguiente paso en el bucle
- //Recorremos todos los segmentos menos el último
- Geom::CubicBezier const *cubic = NULL;
- A0 = curve_it1->initialPoint();
- A1 = curve_it1->initialPoint();
- A2 = curve_it1->finalPoint();
- A3 = curve_it1->finalPoint();
- cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
- if (cubic) {
- A1 = (*cubic)[1];
- if (shiftNodes) {
- A1 = (*cubic)[1] + initialMove;
+ Geom::Path::const_iterator curve_it1 = path_it->begin();
+ Geom::Path::const_iterator curve_it2 = ++(path_it->begin());
+ Geom::Path::const_iterator curve_endit = path_it->end_default();
+ SPCurve *nCurve = new SPCurve();
+ if (path_it->closed()) {
+ const Geom::Curve &closingline =
+ path_it->back_closed();
+ if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ curve_endit = path_it->end_open();
+ }
}
- A2 = (*cubic)[2];
- nCurve->curveto(A1, A2, A3);
- } else {
- nCurve->lineto(A3);
- }
- double length = Inkscape::Util::Quantity::convert(
- curve_it1->length(0.001), "px", unit.get_abbreviation());
- unsigned int splits = 0;
- if (method == DM_SEGMENTS) {
- splits = segments;
- } else {
- splits = ceil(length / maxSegmentSize);
- }
- for (unsigned int t = splits; t >= 1; t--) {
- if (t == 1 && splits != 1) {
- continue;
+ Geom::Point initialMove(0, 0);
+ if (shiftNodes) {
+ initialMove = randomize();
}
- const SPCurve *tmp;
- if (splits == 1) {
- tmp = jitter(nCurve->last_segment());
- } else {
- tmp = addNodesAndJitter(nCurve->last_segment(), 1. / t);
+ Geom::Point initialPoint = curve_it1->initialPoint() + initialMove;
+ nCurve->moveto(initialPoint);
+ Geom::Point A0(0, 0);
+ Geom::Point A1(0, 0);
+ Geom::Point A2(0, 0);
+ Geom::Point A3(0, 0);
+ while (curve_it1 != curve_endit) {
+ Geom::CubicBezier const *cubic = NULL;
+ A0 = curve_it1->initialPoint();
+ A1 = curve_it1->initialPoint();
+ A2 = curve_it1->finalPoint();
+ A3 = curve_it1->finalPoint();
+ cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
+ if (cubic) {
+ A1 = (*cubic)[1];
+ if (shiftNodes) {
+ A1 = (*cubic)[1] + initialMove;
+ }
+ A2 = (*cubic)[2];
+ nCurve->curveto(A1, A2, A3);
+ } else {
+ nCurve->lineto(A3);
+ }
+ double length = Inkscape::Util::Quantity::convert(
+ curve_it1->length(0.001), "px", unit.get_abbreviation());
+ std::size_t splits = 0;
+ if (method == DM_SEGMENTS) {
+ splits = segments;
+ } else {
+ splits = ceil(length / maxSegmentSize);
+ }
+ for (unsigned int t = splits; t >= 1; t--) {
+ if (t == 1 && splits != 1) {
+ continue;
+ }
+ const SPCurve *tmp;
+ if (splits == 1) {
+ tmp = jitter(nCurve->last_segment());
+ } else {
+ tmp = addNodesAndJitter(nCurve->last_segment(), 1. / t);
+ }
+ if (nCurve->get_segment_count() > 1) {
+ nCurve->backspace();
+ nCurve->append_continuous(tmp, 0.001);
+ } else {
+ nCurve = tmp->copy();
+ }
+ delete tmp;
+ }
+ ++curve_it1;
+ if(curve_it2 != curve_endit) {
+ ++curve_it2;
+ }
}
- if (nCurve->get_segment_count() > 1) {
- nCurve->backspace();
- nCurve->append_continuous(tmp, 0.001);
- } else {
- nCurve = tmp->copy();
+ if (path_it->closed()) {
+ nCurve->closepath_current();
}
- delete tmp;
- }
- ++curve_it1;
- ++curve_it2;
+ curve->append(nCurve, false);
+ nCurve->reset();
+ delete nCurve;
}
- Geom::CubicBezier const *cubic = NULL;
- A0 = curve_it1->initialPoint();
- A1 = curve_it1->initialPoint();
- A2 = curve_it1->finalPoint();
- A3 = curve_it1->finalPoint();
- cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
- if (cubic) {
- A1 = (*cubic)[1];
- A2 = (*cubic)[2];
- if (path_it->closed()) {
- A2 = A2 + initialMove;
- A3 = initialPoint;
- }
- nCurve->curveto(A1, A2, A3);
- } else {
- if (path_it->closed()) {
- A3 = initialPoint;
- }
- nCurve->lineto(A3);
+}
+
+SPCurve *LPERoughen::addNodesAndJitter(const Geom::Curve *A, double t)
+{
+ SPCurve *out = new SPCurve();
+ Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*A);
+ Geom::Point A1(0, 0);
+ Geom::Point A2(0, 0);
+ Geom::Point A3(0, 0);
+ Geom::Point B1(0, 0);
+ Geom::Point B2(0, 0);
+ Geom::Point B3(0, 0);
+ if (shiftNodes) {
+ A3 = randomize();
+ B3 = randomize();
}
- double length = Inkscape::Util::Quantity::convert(
- curve_it1->length(0.001), "px", unit.get_abbreviation());
- unsigned int splits = 0;
- if (method == DM_SEGMENTS) {
- splits = segments;
+ if (shiftNodeHandles) {
+ A1 = randomize();
+ A2 = randomize();
+ B1 = randomize();
+ B2 = randomize();
} else {
- splits = ceil(length / maxSegmentSize);
+ A2 = A3;
+ B1 = A3;
+ B2 = B3;
}
- for (unsigned int t = splits; t >= 1; t--) {
- if (t == 1 && splits != 1) {
- continue;
- }
- const SPCurve *tmp;
- if (splits == 1) {
- tmp = jitter(nCurve->last_segment());
- } else {
- tmp = addNodesAndJitter(nCurve->last_segment(), 1. / t);
- }
- if (nCurve->get_segment_count() > 1) {
- nCurve->backspace();
- nCurve->append_continuous(tmp, 0.001);
- } else {
- nCurve = tmp->copy();
- }
- delete tmp;
- }
- //y cerramos la curva
- if (path_it->closed()) {
- nCurve->closepath_current();
+ if (cubic) {
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t);
+ std::vector<Geom::Point> seg1 = div.first.points(),
+ seg2 = div.second.points();
+ out->moveto(seg1[0]);
+ out->curveto(seg1[1] + A1, seg1[2] + A2, seg1[3] + A3);
+ out->curveto(seg2[1] + B1, seg2[2], seg2[3]);
+ } else if (shiftNodeHandles) {
+ out->moveto(A->initialPoint());
+ out->curveto(A->pointAt(t / 3) + A1, A->pointAt((t / 3) * 2) + A2,
+ A->pointAt(t) + A3);
+ out->curveto(A->pointAt(t + (t / 3)) + B1, A->pointAt(t + ((t / 3) * 2)),
+ A->finalPoint());
+ } else {
+ out->moveto(A->initialPoint());
+ out->lineto(A->pointAt(t) + A3);
+ out->lineto(A->finalPoint());
}
- curve->append(nCurve, false);
- nCurve->reset();
- delete nCurve;
- }
-}
-
-SPCurve *LPERoughen::addNodesAndJitter(const Geom::Curve *A, double t) {
- SPCurve *out = new SPCurve();
- Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*A);
- Geom::Point A1(0, 0);
- Geom::Point A2(0, 0);
- Geom::Point A3(0, 0);
- Geom::Point B1(0, 0);
- Geom::Point B2(0, 0);
- Geom::Point B3(0, 0);
- if (shiftNodes) {
- A3 = randomize();
- B3 = randomize();
- }
- if (shiftNodeHandles) {
- A1 = randomize();
- A2 = randomize();
- B1 = randomize();
- B2 = randomize();
- } else {
- A2 = A3;
- B1 = A3;
- B2 = B3;
- }
- if (cubic) {
- std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t);
- std::vector<Geom::Point> seg1 = div.first.points(),
- seg2 = div.second.points();
- out->moveto(seg1[0]);
- out->curveto(seg1[1] + A1, seg1[2] + A2, seg1[3] + A3);
- out->curveto(seg2[1] + B1, seg2[2], seg2[3]);
- } else if (shiftNodeHandles) {
- out->moveto(A->initialPoint());
- out->curveto(A->pointAt(t / 3) + A1, A->pointAt((t / 3) * 2) + A2,
- A->pointAt(t) + A3);
- out->curveto(A->pointAt(t + (t / 3)) + B1, A->pointAt(t + ((t / 3) * 2)),
- A->finalPoint());
- } else {
- out->moveto(A->initialPoint());
- out->lineto(A->pointAt(t) + A3);
- out->lineto(A->finalPoint());
- }
- return out;
+ return out;
}
-SPCurve *LPERoughen::jitter(const Geom::Curve *A) {
- SPCurve *out = new SPCurve();
- Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*A);
- Geom::Point A1(0, 0);
- Geom::Point A2(0, 0);
- Geom::Point A3(0, 0);
- if (shiftNodes) {
- A3 = randomize();
- }
- if (shiftNodeHandles) {
- A1 = randomize();
- A2 = randomize();
- } else {
- A2 = A3;
- }
- if (cubic) {
- out->moveto((*cubic)[0]);
- out->curveto((*cubic)[1] + A1, (*cubic)[2] + A2, (*cubic)[3] + A3);
- } else if (shiftNodeHandles) {
- out->moveto(A->initialPoint());
- out->curveto(A->pointAt(0.3333) + A1, A->pointAt(0.6666) + A2,
- A->finalPoint() + A3);
- } else {
- out->moveto(A->initialPoint());
- out->lineto(A->finalPoint() + A3);
- }
- return out;
+SPCurve *LPERoughen::jitter(const Geom::Curve *A)
+{
+ SPCurve *out = new SPCurve();
+ Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*A);
+ Geom::Point A1(0, 0);
+ Geom::Point A2(0, 0);
+ Geom::Point A3(0, 0);
+ if (shiftNodes) {
+ A3 = randomize();
+ }
+ if (shiftNodeHandles) {
+ A1 = randomize();
+ A2 = randomize();
+ } else {
+ A2 = A3;
+ }
+ if (cubic) {
+ out->moveto((*cubic)[0]);
+ out->curveto((*cubic)[1] + A1, (*cubic)[2] + A2, (*cubic)[3] + A3);
+ } else if (shiftNodeHandles) {
+ out->moveto(A->initialPoint());
+ out->curveto(A->pointAt(0.3333) + A1, A->pointAt(0.6666) + A2,
+ A->finalPoint() + A3);
+ } else {
+ out->moveto(A->initialPoint());
+ out->lineto(A->finalPoint() + A3);
+ }
+ return out;
}
-Geom::Point LPERoughen::tpoint(Geom::Point A, Geom::Point B, double t) {
- using Geom::X;
- using Geom::Y;
- return Geom::Point(A[X] + t * (B[X] - A[X]), A[Y] + t * (B[Y] - A[Y]));
+Geom::Point LPERoughen::tpoint(Geom::Point A, Geom::Point B, double t)
+{
+ using Geom::X;
+ using Geom::Y;
+ return Geom::Point(A[X] + t * (B[X] - A[X]), A[Y] + t * (B[Y] - A[Y]));
}
}; //namespace LivePathEffect
diff --git a/src/live_effects/lpe-roughen.h b/src/live_effects/lpe-roughen.h
index cc7784dc0..7e99b47bf 100644
--- a/src/live_effects/lpe-roughen.h
+++ b/src/live_effects/lpe-roughen.h
@@ -1,11 +1,17 @@
-#ifndef INKSCAPE_LPE_ROUGHEN_H
-#define INKSCAPE_LPE_ROUGHEN_H
-
-/*
- * Inkscape::LPERoughen
+/** @file
+ * @brief Roughen LPE effect, see lpe-roughen.cpp.
+ */
+/* Authors:
+ * Jabier Arraiza Cenoz <jabier.arraiza@marker.es>
+ *
+ * Copyright (C) 2014 Authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+
+#ifndef INKSCAPE_LPE_ROUGHEN_H
+#define INKSCAPE_LPE_ROUGHEN_H
+
#include "live_effects/parameter/enum.h"
#include "live_effects/effect.h"
#include "live_effects/parameter/parameter.h"
@@ -18,37 +24,37 @@ namespace Inkscape {
namespace LivePathEffect {
enum DivisionMethod {
- DM_SEGMENTS,
- DM_SIZE,
- DM_END
+ DM_SEGMENTS,
+ DM_SIZE,
+ DM_END
};
class LPERoughen : public Effect {
public:
- LPERoughen(LivePathEffectObject *lpeobject);
- virtual ~LPERoughen();
-
- virtual void doEffect(SPCurve *curve);
- virtual double sign(double randNumber);
- virtual Geom::Point randomize();
- virtual void doBeforeEffect(SPLPEItem const */*lpeitem*/);
- virtual SPCurve *addNodesAndJitter(const Geom::Curve *A, double t);
- virtual SPCurve *jitter(const Geom::Curve *A);
- virtual Geom::Point tpoint(Geom::Point A, Geom::Point B, double t = 0.5);
- virtual Gtk::Widget *newWidget();
+ LPERoughen(LivePathEffectObject *lpeobject);
+ virtual ~LPERoughen();
+
+ virtual void doEffect(SPCurve *curve);
+ virtual double sign(double randNumber);
+ virtual Geom::Point randomize();
+ virtual void doBeforeEffect(SPLPEItem const */*lpeitem*/);
+ virtual SPCurve *addNodesAndJitter(const Geom::Curve *A, double t);
+ virtual SPCurve *jitter(const Geom::Curve *A);
+ virtual Geom::Point tpoint(Geom::Point A, Geom::Point B, double t = 0.5);
+ virtual Gtk::Widget *newWidget();
private:
- UnitParam unit;
- EnumParam<DivisionMethod> method;
- ScalarParam maxSegmentSize;
- ScalarParam segments;
- RandomParam displaceX;
- RandomParam displaceY;
- BoolParam shiftNodes;
- BoolParam shiftNodeHandles;
- LPERoughen(const LPERoughen &);
- LPERoughen &operator=(const LPERoughen &);
+ UnitParam unit;
+ EnumParam<DivisionMethod> method;
+ ScalarParam maxSegmentSize;
+ ScalarParam segments;
+ RandomParam displaceX;
+ RandomParam displaceY;
+ BoolParam shiftNodes;
+ BoolParam shiftNodeHandles;
+ LPERoughen(const LPERoughen &);
+ LPERoughen &operator=(const LPERoughen &);
};
diff --git a/src/live_effects/lpe-tangent_to_curve.cpp b/src/live_effects/lpe-tangent_to_curve.cpp
index dbebdf7fb..bce4876af 100644
--- a/src/live_effects/lpe-tangent_to_curve.cpp
+++ b/src/live_effects/lpe-tangent_to_curve.cpp
@@ -16,8 +16,6 @@
#include <glibmm/i18n.h>
#include "live_effects/lpe-tangent_to_curve.h"
-// FIXME: The following are only needed to convert the path's SPCurve* to pwd2.
-// There must be a more convenient way to achieve this.
#include "sp-path.h"
#include "display/curve.h"
@@ -108,13 +106,13 @@ LPETangentToCurve::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desk
{
KnotHolderEntity *e = new TtC::KnotHolderEntityLeftEnd(this);
e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,
- _("Adjust the \"left\" end of the tangent") );
+ _("Adjust the <b>left</b> end of the tangent") );
knotholder->add(e);
}
{
KnotHolderEntity *e = new TtC::KnotHolderEntityRightEnd(this);
e->create( desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN,
- _("Adjust the \"right\" end of the tangent") );
+ _("Adjust the <b>right</b> end of the tangent") );
knotholder->add(e);
}
};
@@ -130,14 +128,13 @@ KnotHolderEntityAttachPt::knot_set(Geom::Point const &p, Geom::Point const &/*or
Geom::Point const s = snap_knot_position(p, state);
- // FIXME: There must be a better way of converting the path's SPCurve* to pwd2.
- SPCurve *curve = SP_PATH(item)->get_curve_for_edit();
- Geom::PathVector pathv = curve->get_pathvector();
- Piecewise<D2<SBasis> > pwd2;
- for (unsigned int i=0; i < pathv.size(); i++) {
- pwd2.concat(pathv[i].toPwSb());
+ if ( !SP_IS_SHAPE(lpe->sp_lpe_item) ) {
+ //lpe->t_attach.param_set_value(0);
+ g_warning("LPEItem is not a path! %s:%d\n", __FILE__, __LINE__);
+ return;
}
-
+ Piecewise<D2<SBasis> > pwd2 = paths_to_pw( lpe->pathvector_before_effect );
+
double t0 = nearest_point(s, pwd2);
lpe->t_attach.param_set_value(t0);
diff --git a/src/live_effects/lpe-tangent_to_curve.h b/src/live_effects/lpe-tangent_to_curve.h
index 309afc14b..8e44c01d1 100644
--- a/src/live_effects/lpe-tangent_to_curve.h
+++ b/src/live_effects/lpe-tangent_to_curve.h
@@ -34,7 +34,6 @@ class LPETangentToCurve : public Effect {
public:
LPETangentToCurve(LivePathEffectObject *lpeobject);
virtual ~LPETangentToCurve();
-
virtual Geom::Piecewise<Geom::D2<Geom::SBasis> >
doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp
new file mode 100644
index 000000000..b595b8d55
--- /dev/null
+++ b/src/live_effects/lpe-taperstroke.cpp
@@ -0,0 +1,632 @@
+/**
+ * @file
+ * Taper Stroke path effect, provided as an alternative to Power Strokes
+ * for otherwise constant-width paths.
+ *
+ * Authors:
+ * Liam P White <inkscapebrony@gmail.com>
+ *
+ * Copyright (C) 2014 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/lpe-taperstroke.h"
+
+#include <2geom/path.h>
+#include <2geom/shape.h>
+#include <2geom/path.h>
+#include <2geom/circle.h>
+#include <2geom/sbasis-to-bezier.h>
+
+#include "pathoutlineprovider.h"
+#include "display/curve.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 "svg/svg.h"
+
+#include "knot-holder-entity.h"
+#include "knotholder.h"
+
+#include <glibmm/i18n.h>
+
+template<typename T>
+inline bool withinRange(T value, T low, T high) {
+ return (value > low && value < high);
+}
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+namespace TpS {
+ class KnotHolderEntityAttachBegin : public LPEKnotHolderEntity {
+ public:
+ KnotHolderEntityAttachBegin(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {}
+ virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
+ virtual Geom::Point knot_get() const;
+ };
+
+ class KnotHolderEntityAttachEnd : public LPEKnotHolderEntity {
+ public:
+ KnotHolderEntityAttachEnd(LPETaperStroke * effect) : LPEKnotHolderEntity(effect) {}
+ virtual void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state);
+ virtual Geom::Point knot_get() const;
+ };
+} // TpS
+
+static const Util::EnumData<unsigned> JoinType[] = {
+ {LINEJOIN_STRAIGHT, N_("Beveled"), "bevel"},
+ {LINEJOIN_ROUND, N_("Rounded"), "round"},
+ {LINEJOIN_REFLECTED, N_("Reflected"), "reflected"},
+ {LINEJOIN_POINTY, N_("Miter"), "miter"},
+ {LINEJOIN_EXTRAPOLATED, N_("Extrapolated"), "extrapolated"}
+};
+
+static const Util::EnumDataConverter<unsigned> JoinTypeConverter(JoinType, sizeof (JoinType)/sizeof(*JoinType));
+
+LPETaperStroke::LPETaperStroke(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ line_width(_("Stroke width"), _("The (non-tapered) width of the path"), "stroke_width", &wr, this, 1.),
+ attach_start(_("Start offset"), _("Taper distance from path start"), "attach_start", &wr, this, 0.2),
+ attach_end(_("End offset"), _("The ending position of the taper"), "end_offset", &wr, this, 0.2),
+ smoothing(_("Taper smoothing"), _("Amount of smoothing to apply to the tapers"), "smoothing", &wr, this, 0.5),
+ join_type(_("Join type"), _("Join type for non-smooth nodes"), "jointype", JoinTypeConverter, &wr, this, LINEJOIN_EXTRAPOLATED),
+ miter_limit(_("Miter limit"), _("Limit for miter joins"), "miter_limit", &wr, this, 100.)
+{
+ show_orig_path = true;
+ _provides_knotholder_entities = true;
+
+ attach_start.param_set_digits(3);
+ attach_end.param_set_digits(3);
+
+ registerParameter(&line_width);
+ registerParameter(&attach_start);
+ registerParameter(&attach_end);
+ registerParameter(&smoothing);
+ registerParameter(&join_type);
+ registerParameter(&miter_limit);
+}
+
+// from LPEPowerStroke -- sets fill if stroke color because we will
+// be converting to a fill to make the new join.
+
+void LPETaperStroke::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 (true) {
+ 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);
+
+ line_width.param_set_value(width);
+ } else {
+ printf("WARNING: It only makes sense to apply Join Type to paths (not groups).\n");
+ }
+}
+
+// from LPEPowerStroke -- sets stroke color from existing fill color
+
+void LPETaperStroke::doOnRemove(SPLPEItem const* lpeitem)
+{
+ if (SP_IS_SHAPE(lpeitem)) {
+ SPLPEItem *item = const_cast<SPLPEItem*>(lpeitem);
+
+ SPCSSAttr *css = sp_repr_css_attr_new ();
+ if (true) {
+ 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);
+ }
+}
+
+using Geom::Piecewise;
+using Geom::D2;
+using Geom::SBasis;
+// leave Geom::Path
+
+Geom::Path return_at_first_cusp(Geom::Path const & path_in, double /*smooth_tolerance*/ = 0.05) {
+ return Geom::split_at_cusps(path_in)[0];
+}
+
+Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path pattern, double width);
+
+// references to pointers
+void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second);
+
+// actual effect
+
+Geom::PathVector LPETaperStroke::doEffect_path(Geom::PathVector const& path_in)
+{
+ Geom::Path first_cusp = return_at_first_cusp(path_in[0]);
+ Geom::Path last_cusp = return_at_first_cusp(path_in[0].reverse());
+
+ bool zeroStart = false; // [distance from start taper knot -> start of path] == 0
+ bool zeroEnd = false; // [distance from end taper knot -> end of path] == 0
+ bool metInMiddle = false; // knots are touching
+
+ // there is a pretty good chance that people will try to drag the knots
+ // on top of each other, so block it
+
+ unsigned size = path_in[0].size();
+ if (size == first_cusp.size()) {
+ // check to see if the knots were dragged over each other
+ // if so, reset the end offset, but still allow the start offset.
+ if ( attach_start >= (size - attach_end) ) {
+ attach_end.param_set_value( size - attach_start );
+ metInMiddle = true;
+ }
+ }
+
+ if (attach_start == size - attach_end) {
+ metInMiddle = true;
+ }
+ if (attach_end == size - attach_start) {
+ metInMiddle = true;
+ }
+
+ // don't let it be integer (TODO this is stupid!)
+ {
+ if (double(unsigned(attach_start)) == attach_start) {
+ attach_start.param_set_value(attach_start - 0.00001);
+ }
+ if (double(unsigned(attach_end)) == attach_end) {
+ attach_end.param_set_value(attach_end - 0.00001);
+ }
+ }
+
+ unsigned allowed_start = first_cusp.size();
+ unsigned allowed_end = last_cusp.size();
+
+ // don't let the knots be farther than they are allowed to be
+ {
+ if ((unsigned)attach_start >= allowed_start) {
+ attach_start.param_set_value((double)allowed_start - 0.00001);
+ }
+ if ((unsigned)attach_end >= allowed_end) {
+ attach_end.param_set_value((double)allowed_end - 0.00001);
+ }
+ }
+
+ // don't let it be zero (this is stupid too!)
+ if (attach_start < 0.0000001 || withinRange(double(attach_start), 0.00000001, 0.000001)) {
+ attach_start.param_set_value( 0.0000001 );
+ zeroStart = true;
+ }
+ if (attach_end < 0.0000001 || withinRange(double(attach_end), 0.00000001, 0.000001)) {
+ attach_end.param_set_value( 0.0000001 );
+ zeroEnd = true;
+ }
+
+ // Path::operator () means get point at time t
+ start_attach_point = first_cusp(attach_start);
+ end_attach_point = last_cusp(attach_end);
+ Geom::PathVector pathv_out;
+
+ // the following function just splits it up into three pieces.
+ pathv_out = doEffect_simplePath(path_in);
+
+ // now for the actual tapering. the stretch_along method (stolen from PaP) is used to accomplish this
+
+ Geom::PathVector real_pathv;
+ Geom::Path real_path;
+ Geom::PathVector pat_vec;
+ Piecewise<D2<SBasis> > pwd2;
+ Geom::Path throwaway_path;
+
+ if (!zeroStart) {
+ // Construct the pattern
+ std::stringstream pat_str;
+ pat_str.imbue(std::locale::classic());
+ pat_str << "M 1,0 C " << 1 - (double)smoothing << ",0 0,0.5 0,0.5 0,0.5 " << 1 - (double)smoothing << ",1 1,1";
+
+ pat_vec = sp_svg_read_pathv(pat_str.str().c_str());
+ pwd2.concat(stretch_along(pathv_out[0].toPwSb(), pat_vec[0], -fabs(line_width)));
+ throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0];
+
+ real_path.append(throwaway_path);
+ }
+
+ // if this condition happens to evaluate false, i.e. there was no space for a path to be drawn, it is simply skipped.
+ // although this seems obvious, it can probably lead to bugs.
+ if (!metInMiddle) {
+ // append the outside outline of the path (goes with the direction of the path)
+ throwaway_path = Outline::PathOutsideOutline(pathv_out[1], -fabs(line_width), static_cast<LineJoinType>(join_type.get_value()), miter_limit);
+ if (!zeroStart && real_path.size() >= 1 && throwaway_path.size() >= 1) {
+ if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint())) {
+ real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
+ } else {
+ real_path.setFinal(throwaway_path.initialPoint());
+ }
+ }
+ real_path.append(throwaway_path);
+ }
+
+ if (!zeroEnd) {
+ // append the ending taper
+ std::stringstream pat_str_1;
+ pat_str_1.imbue(std::locale::classic());
+ pat_str_1 << "M 0,1 C " << (double)smoothing << ",1 1,0.5 1,0.5 1,0.5 " << double(smoothing) << ",0 0,0";
+ pat_vec = sp_svg_read_pathv(pat_str_1.str().c_str());
+
+ pwd2 = Piecewise<D2<SBasis> >();
+ pwd2.concat(stretch_along(pathv_out[2].toPwSb(), pat_vec[0], -fabs(line_width)));
+
+ throwaway_path = Geom::path_from_piecewise(pwd2, LPE_CONVERSION_TOLERANCE)[0];
+ if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) {
+ real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
+ } else {
+ real_path.setFinal(throwaway_path.initialPoint());
+ }
+ real_path.append(throwaway_path);
+ }
+
+ if (!metInMiddle) {
+ // append the inside outline of the path (against direction)
+ throwaway_path = Outline::PathOutsideOutline(pathv_out[1].reverse(), -fabs(line_width), static_cast<LineJoinType>(join_type.get_value()), miter_limit);
+
+ if (!Geom::are_near(real_path.finalPoint(), throwaway_path.initialPoint()) && real_path.size() >= 1) {
+ real_path.appendNew<Geom::LineSegment>(throwaway_path.initialPoint());
+ } else {
+ real_path.setFinal(throwaway_path.initialPoint());
+ }
+ real_path.append(throwaway_path);
+ }
+
+ if (!Geom::are_near(real_path.finalPoint(), real_path.initialPoint())) {
+ real_path.appendNew<Geom::LineSegment>(real_path.initialPoint());
+ } else {
+ real_path.setFinal(real_path.initialPoint());
+ }
+ real_path.close();
+
+ real_pathv.push_back(real_path);
+
+ return real_pathv;
+}
+
+/**
+ * @return Always returns a PathVector with three elements.
+ *
+ * The positions of the effect knots are accessed to determine
+ * where exactly the input path should be split.
+ */
+Geom::PathVector LPETaperStroke::doEffect_simplePath(Geom::PathVector const & path_in)
+{
+ size_t size = path_in[0].size();
+
+ unsigned loc = (unsigned)attach_start;
+ Geom::Curve * curve_start = path_in[0] [loc].duplicate();
+
+ std::vector<Geom::Path> pathv_out;
+ Geom::Path path_out = Geom::Path();
+
+ Geom::Path trimmed_start = Geom::Path();
+ Geom::Path trimmed_end = Geom::Path();
+
+ for (size_t i = 0; i < loc; ++i) {
+ trimmed_start.append(path_in[0] [i]);
+ }
+
+ Geom::Curve * temp;
+ subdivideCurve(curve_start, attach_start - loc, temp, curve_start);
+ trimmed_start.append(*temp);
+ if (temp) delete temp; temp = 0;
+
+ // special case: path is one segment long
+ // special case: what if the two knots occupy the same segment?
+ if ((size == 1) || ( size - unsigned(attach_end) - 1 == loc )) {
+
+ // If you look into it, I don't actually think there is a working way to do this
+ // with only point math. So we use nearest_point instead.
+ Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_start);
+
+ // it is just a dumb segment
+ // we have to do some shifting here because the value changed when we reduced the length
+ // of the previous segment.
+
+ subdivideCurve(curve_start, t, curve_start, temp);
+ trimmed_end.append(*temp);
+ if (temp) delete temp; temp = 0;
+
+ for (size_t j = (size - attach_end) + 1; j < size; ++j) {
+ trimmed_end.append(path_in[0] [j]);
+ }
+
+ path_out.append(*curve_start);
+ pathv_out.push_back(trimmed_start);
+ pathv_out.push_back(path_out);
+ pathv_out.push_back(trimmed_end);
+ return pathv_out;
+ }
+
+ pathv_out.push_back(trimmed_start);
+
+ // append almost all of the rest of the path, ignore the curves that the knot is past (we'll get to it in a minute)
+ path_out.append(*curve_start);
+
+ for (size_t k = loc + 1; k < (size - unsigned(attach_end)) - 1; ++k) {
+ path_out.append(path_in[0] [k]);
+ }
+
+ // deal with the last segment in a very similar fashion to the first
+ loc = size - attach_end;
+
+ Geom::Curve * curve_end = path_in[0] [loc].duplicate();
+
+ Geom::Coord t = Geom::nearest_point(end_attach_point, *curve_end);
+
+ subdivideCurve(curve_end, t, curve_end, temp);
+ trimmed_end.append(*temp);
+ if (temp) delete temp; temp = 0;
+
+ for (size_t j = (size - attach_end) + 1; j < size; ++j) {
+ trimmed_end.append(path_in[0] [j]);
+ }
+
+ path_out.append(*curve_end);
+ pathv_out.push_back(path_out);
+
+ pathv_out.push_back(trimmed_end);
+
+ if (curve_end) delete curve_end;
+ if (curve_start) delete curve_start;
+ return pathv_out;
+}
+
+
+/**
+ * Most of the below function is verbatim from Pattern Along Path. However, it needed a little
+ * tweaking to get it to work right in this case. Also, large portions of the effect have been
+ * stripped out as I deemed them unnecessary for the relative simplicity of this effect.
+ */
+Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path pattern, double prop_scale)
+{
+ using namespace Geom;
+
+ // Don't allow empty path parameter:
+ if ( pattern.empty() ) {
+ return pwd2_in;
+ }
+
+ /* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */
+ Piecewise<D2<SBasis> > output;
+ std::vector<Piecewise<D2<SBasis> > > pre_output;
+
+ D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(pattern.toPwSb());
+ Piecewise<SBasis> x0 = Piecewise<SBasis>(patternd2[0]);
+ Piecewise<SBasis> y0 = Piecewise<SBasis>(patternd2[1]);
+ OptInterval pattBndsX = bounds_exact(x0);
+ OptInterval pattBndsY = bounds_exact(y0);
+ if (pattBndsX && pattBndsY) {
+ x0 -= pattBndsX->min();
+ y0 -= pattBndsY->middle();
+
+ double xspace = 0;
+ double noffset = 0;
+ double toffset = 0;
+ // Prevent more than 90% overlap...
+ if (xspace < -pattBndsX->extent()*.9) {
+ xspace = -pattBndsX->extent()*.9;
+ }
+
+ y0+=noffset;
+
+ std::vector<Piecewise<D2<SBasis> > > paths_in;
+ paths_in = split_at_discontinuities(pwd2_in);
+
+ for (unsigned idx = 0; idx < paths_in.size(); idx++) {
+ Piecewise<D2<SBasis> > path_i = paths_in[idx];
+ Piecewise<SBasis> x = x0;
+ Piecewise<SBasis> y = y0;
+ Piecewise<D2<SBasis> > uskeleton = arc_length_parametrization(path_i,2,.1);
+ uskeleton = remove_short_cuts(uskeleton,.01);
+ Piecewise<D2<SBasis> > n = rot90(derivative(uskeleton));
+ n = force_continuity(remove_short_cuts(n,.1));
+
+ int nbCopies = 0;
+ double scaling = 1;
+ nbCopies = 1;
+ scaling = (uskeleton.domain().extent() - toffset)/pattBndsX->extent();
+
+ double pattWidth = pattBndsX->extent() * scaling;
+
+ if (scaling != 1.0) {
+ x*=scaling;
+ }
+ if ( false ) {
+ y*=(scaling*prop_scale);
+ } else {
+ if (prop_scale != 1.0) y *= prop_scale;
+ }
+ x += toffset;
+
+ double offs = 0;
+ for (int i=0; i<nbCopies; i++) {
+ if (false) {
+ Piecewise<D2<SBasis> > output_piece = compose(uskeleton,x+offs)+y*compose(n,x+offs);
+ std::vector<Piecewise<D2<SBasis> > > splited_output_piece = split_at_discontinuities(output_piece);
+ pre_output.insert(pre_output.end(), splited_output_piece.begin(), splited_output_piece.end() );
+ } else {
+ output.concat(compose(uskeleton,x+offs)+y*compose(n,x+offs));
+ }
+ offs+=pattWidth;
+ }
+ }
+ return output;
+ } else {
+ return pwd2_in;
+ }
+}
+
+void subdivideCurve(Geom::Curve * curve_in, Geom::Coord t, Geom::Curve *& val_first, Geom::Curve *& val_second)
+{
+ if (Geom::LineSegment* linear = dynamic_cast<Geom::LineSegment*>(curve_in)) {
+ // special case for line segments
+ std::pair<Geom::LineSegment, Geom::LineSegment> seg_pair = linear->subdivide(t);
+ val_first = seg_pair.first.duplicate();
+ val_second = seg_pair.second.duplicate();
+ } else {
+ // all other cases:
+ Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(curve_in->toSBasis());
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> cubic_pair = cubic.subdivide(t);
+ val_first = cubic_pair.first.duplicate();
+ val_second = cubic_pair.second.duplicate();
+ }
+}
+
+
+void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPDesktop *desktop, SPItem *item)
+{
+ KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this);
+ e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("Start point of the taper"), SP_KNOT_SHAPE_CIRCLE);
+ knotholder->add(e);
+
+ KnotHolderEntity *f = new TpS::KnotHolderEntityAttachEnd(this);
+ f->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _("End point of the taper"), SP_KNOT_SHAPE_CIRCLE);
+ knotholder->add(f);
+}
+
+namespace TpS {
+
+void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const&/*origin*/, guint state)
+{
+ using namespace Geom;
+
+ LPETaperStroke* lpe = dynamic_cast<LPETaperStroke *>(_effect);
+
+ Geom::Point const s = snap_knot_position(p, state);
+
+ if (!SP_IS_SHAPE(lpe->sp_lpe_item)) {
+ printf("WARNING: LPEItem is not a path!\n");
+ return;
+ }
+
+ SPCurve* curve;
+ if (!(curve = SP_SHAPE(lpe->sp_lpe_item)->getCurve())) {
+ // oops
+ return;
+ }
+ // in case you are wondering, the above are simply sanity checks. we never want to actually
+ // use that object.
+
+ Geom::PathVector pathv = lpe->pathvector_before_effect;
+
+ Piecewise<D2<SBasis> > pwd2;
+ Geom::Path p_in = return_at_first_cusp(pathv[0]);
+ pwd2.concat(p_in.toPwSb());
+
+ double t0 = nearest_point(s, pwd2);
+ lpe->attach_start.param_set_value(t0);
+
+ // FIXME: this should not directly ask for updating the item. It should write to SVG, which triggers updating.
+ sp_lpe_item_update_patheffect(SP_LPE_ITEM(item), false, true);
+}
+void KnotHolderEntityAttachEnd::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state)
+{
+ using namespace Geom;
+
+ LPETaperStroke* lpe = dynamic_cast<LPETaperStroke *>(_effect);
+
+ Geom::Point const s = snap_knot_position(p, state);
+
+ if (!SP_IS_SHAPE(lpe->sp_lpe_item) ) {
+ printf("WARNING: LPEItem is not a path!\n");
+ return;
+ }
+
+ SPCurve* curve;
+ if ( !(curve = SP_SHAPE(lpe->sp_lpe_item)->getCurve()) ) {
+ // oops
+ return;
+ }
+ Geom::PathVector pathv = lpe->pathvector_before_effect;
+ Geom::Path p_in = return_at_first_cusp(pathv[0].reverse());
+ Piecewise<D2<SBasis> > pwd2 = p_in.toPwSb();
+
+ double t0 = nearest_point(s, pwd2);
+ lpe->attach_end.param_set_value(t0);
+
+ sp_lpe_item_update_patheffect (SP_LPE_ITEM(item), false, true);
+}
+
+Geom::Point KnotHolderEntityAttachBegin::knot_get() const
+{
+ LPETaperStroke const * lpe = dynamic_cast<LPETaperStroke const*> (_effect);
+ return lpe->start_attach_point;
+}
+
+Geom::Point KnotHolderEntityAttachEnd::knot_get() const
+{
+ LPETaperStroke const * lpe = dynamic_cast<LPETaperStroke const*> (_effect);
+ return lpe->end_attach_point;
+}
+
+} // namespace TpS
+} // 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:fileencoding=utf-8 :
diff --git a/src/live_effects/lpe-taperstroke.h b/src/live_effects/lpe-taperstroke.h
new file mode 100644
index 000000000..88604486e
--- /dev/null
+++ b/src/live_effects/lpe-taperstroke.h
@@ -0,0 +1,72 @@
+/** @file
+ * @brief Taper Stroke path effect (meant as a replacement for using Power Strokes for tapering)
+ */
+/* Authors:
+ * Liam P White <inkscapebrony@gmail.com>
+ * Copyright (C) 2014 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef INKSCAPE_LPE_TAPERSTROKE_H
+#define INKSCAPE_LPE_TAPERSTROKE_H
+
+#include "live_effects/parameter/enum.h"
+#include "live_effects/effect.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/vector.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+namespace TpS {
+// we need a separate namespace to avoid clashes with other LPEs
+class KnotHolderEntityAttachBegin;
+class KnotHolderEntityAttachEnd;
+}
+
+class LPETaperStroke : public Effect {
+public:
+ LPETaperStroke(LivePathEffectObject *lpeobject);
+ virtual ~LPETaperStroke() {}
+
+ virtual void doOnApply(SPLPEItem const* lpeitem);
+ virtual void doOnRemove(SPLPEItem const* lpeitem);
+
+ virtual Geom::PathVector doEffect_path (Geom::PathVector const& path_in);
+ Geom::PathVector doEffect_simplePath(Geom::PathVector const& path_in);
+
+ virtual void addKnotHolderEntities(KnotHolder * knotholder, SPDesktop * desktop, SPItem * item);
+
+ friend class TpS::KnotHolderEntityAttachBegin;
+ friend class TpS::KnotHolderEntityAttachEnd;
+private:
+ ScalarParam line_width;
+ ScalarParam attach_start;
+ ScalarParam attach_end;
+ ScalarParam smoothing;
+ EnumParam<unsigned> join_type;
+ ScalarParam miter_limit;
+
+ Geom::Point start_attach_point;
+ Geom::Point end_attach_point;
+
+ LPETaperStroke(const LPETaperStroke&);
+ LPETaperStroke& operator=(const LPETaperStroke&);
+};
+
+} //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:fileencoding=utf-8 :
diff --git a/src/live_effects/lpeobject-reference.h b/src/live_effects/lpeobject-reference.h
index b1ba1ee4e..374e715ec 100644
--- a/src/live_effects/lpeobject-reference.h
+++ b/src/live_effects/lpeobject-reference.h
@@ -9,10 +9,10 @@
* Released under GNU GPL, read the file 'COPYING' for more information.
*/
-#include <uri-references.h>
-#include <stddef.h>
#include <sigc++/sigc++.h>
+#include "uri-references.h"
+
namespace Inkscape {
namespace XML {
class Node;
@@ -33,7 +33,7 @@ public:
SPObject *owner;
// concerning the LPEObject that is refered to:
- gchar *lpeobject_href;
+ char *lpeobject_href;
Inkscape::XML::Node *lpeobject_repr;
LivePathEffectObject *lpeobject;
diff --git a/src/live_effects/lpeobject.h b/src/live_effects/lpeobject.h
index 9700024fe..2e62707e3 100644
--- a/src/live_effects/lpeobject.h
+++ b/src/live_effects/lpeobject.h
@@ -47,9 +47,9 @@ protected:
virtual void build(SPDocument* doc, Inkscape::XML::Node* repr);
virtual void release();
- virtual void set(unsigned int key, const gchar* value);
+ virtual void set(unsigned int key, char const* value);
- virtual Inkscape::XML::Node* write(Inkscape::XML::Document* doc, Inkscape::XML::Node* repr, guint flags);
+ virtual Inkscape::XML::Node* write(Inkscape::XML::Document* doc, Inkscape::XML::Node* repr, unsigned int flags);
};
#endif
diff --git a/src/live_effects/parameter/Makefile_insert b/src/live_effects/parameter/Makefile_insert
index 1137ef34b..f990f41c7 100644
--- a/src/live_effects/parameter/Makefile_insert
+++ b/src/live_effects/parameter/Makefile_insert
@@ -20,12 +20,16 @@ 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/filletchamferpointarray.cpp \
live_effects/parameter/filletchamferpointarray.h \
live_effects/parameter/text.cpp \
live_effects/parameter/text.h \
+ live_effects/parameter/transformedpoint.cpp \
+ live_effects/parameter/transformedpoint.h \
live_effects/parameter/togglebutton.cpp \
live_effects/parameter/togglebutton.h \
live_effects/parameter/unit.cpp \
diff --git a/src/live_effects/parameter/filletchamferpointarray.cpp b/src/live_effects/parameter/filletchamferpointarray.cpp
index a89a6279b..bb00ef045 100644
--- a/src/live_effects/parameter/filletchamferpointarray.cpp
+++ b/src/live_effects/parameter/filletchamferpointarray.cpp
@@ -8,12 +8,20 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
+#include <glibmm.h>
+
#include "ui/dialog/lpe-fillet-chamfer-properties.h"
#include "live_effects/parameter/filletchamferpointarray.h"
+
#include <2geom/piecewise.h>
#include <2geom/sbasis-to-bezier.h>
#include <2geom/sbasis-geometric.h>
+#include <2geom/svg-elliptical-arc.h>
+#include <2geom/line.h>
+#include <2geom/path-intersection.h>
+#include "ui/dialog/lpe-fillet-chamfer-properties.h"
+#include "live_effects/parameter/filletchamferpointarray.h"
#include "live_effects/effect.h"
#include "svg/svg.h"
#include "svg/stringstream.h"
@@ -32,6 +40,7 @@
// this has to be included last.
#include <glibmm/i18n.h>
+
using namespace Geom;
namespace Inkscape {
@@ -165,11 +174,12 @@ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2(
}
}
if (last_pathv.size() > pathv.size() ||
- (last_pathv[counterPaths].size() > counter - offset &&
+ (last_pathv.size() > counterPaths &&
+ last_pathv[counterPaths].size() > counter - offset &&
!are_near(curve_it1->initialPoint(),
last_pathv[counterPaths][counter - offset].initialPoint(),
0.1))) {
- if (last_pathv.size() > counterPaths && curve_it2 == curve_endit) {
+ if ( curve_it2 == curve_endit) {
if (last_pathv[counterPaths].size() < pathv[counterPaths].size()) {
offset = abs(last_pathv[counterPaths].size() -
pathv[counterPaths].size());
@@ -332,10 +342,10 @@ void FilletChamferPointArrayParam::recalculate_knots(
result.push_back(Point(xPos, 0));
}
++curve_it1;
+ counter++;
if (curve_it2 != curve_endit) {
++curve_it2;
}
- counter++;
counterCurves++;
}
}
@@ -359,6 +369,11 @@ void FilletChamferPointArrayParam::set_helper_size(int hs)
helper_size = hs;
}
+void FilletChamferPointArrayParam::set_use_distance(bool use_knot_distance )
+{
+ use_distance = use_knot_distance;
+}
+
void FilletChamferPointArrayParam::set_unit(const gchar *abbr)
{
unit = abbr;
@@ -398,6 +413,205 @@ void FilletChamferPointArrayParam::addCanvasIndicators(
hp_vec.push_back(hp);
}
+double FilletChamferPointArrayParam::rad_to_len(int index, double rad)
+{
+ double len = 0;
+ std::vector<Geom::Path> subpaths = path_from_piecewise(last_pwd2, 0.1);
+ std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths);
+ D2<SBasis> A = last_pwd2[last_index(index, subpaths)];
+ if(positions.second != 0){
+ A = last_pwd2[index-1];
+ }else{
+ if(!subpaths[positions.first].closed()){
+ return len;
+ }
+ }
+ D2<SBasis> B = last_pwd2[index];
+ Piecewise<D2<SBasis> > offset_curve0 = Piecewise<D2<SBasis> >(A)+rot90(unitVector(derivative(A)))*(rad);
+ Piecewise<D2<SBasis> > offset_curve1 = Piecewise<D2<SBasis> >(B)+rot90(unitVector(derivative(B)))*(rad);
+ Geom::Path p0 = path_from_piecewise(offset_curve0, 0.1)[0];
+ Geom::Path p1 = path_from_piecewise(offset_curve1, 0.1)[0];
+ Geom::Crossings cs = Geom::crossings(p0, p1);
+ if(cs.size() > 0){
+ Point cp =p0(cs[0].ta);
+ double p0pt = nearest_point(cp, B);
+ len = time_to_len(index,p0pt);
+ } else {
+ if(rad < 0){
+ len = rad_to_len(index, rad * -1);
+ }
+ }
+ return len;
+}
+
+double FilletChamferPointArrayParam::len_to_rad(int index, double len)
+{
+ double rad = 0;
+ double tmp_len = _vector[index][X];
+ _vector[index] = Geom::Point(len,_vector[index][Y]);
+ std::vector<Geom::Path> subpaths = path_from_piecewise(last_pwd2, 0.1);
+ std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths);
+ Piecewise<D2<SBasis> > u;
+ u.push_cut(0);
+ u.push(last_pwd2[last_index(index, subpaths)], 1);
+ Geom::Curve * A = path_from_piecewise(u, 0.1)[0][0].duplicate();
+ Geom::Curve * B = subpaths[positions.first][positions.second].duplicate();
+ std::vector<double> times;
+ if(positions.second != 0){
+ A = subpaths[positions.first][positions.second-1].duplicate();
+ times = get_times(index-1, subpaths, false);
+ }else{
+ if(!subpaths[positions.first].closed()){
+ return rad;
+ }
+ times = get_times(last_index(index, subpaths), subpaths, true);
+ }
+ _vector[index] = Geom::Point(tmp_len,_vector[index][Y]);
+ Geom::Point startArcPoint = A->toSBasis().valueAt(times[1]);
+ Geom::Point endArcPoint = B->toSBasis().valueAt(times[2]);
+ Curve *knotCurve1 = A->portion(times[0], times[1]);
+ Curve *knotCurve2 = B->portion(times[2], 1);
+ Geom::CubicBezier const *cubic1 = dynamic_cast<Geom::CubicBezier const *>(&*knotCurve1);
+ Ray ray1(startArcPoint, A->finalPoint());
+ if (cubic1) {
+ ray1.setPoints((*cubic1)[2], startArcPoint);
+ }
+ Geom::CubicBezier const *cubic2 = dynamic_cast<Geom::CubicBezier const *>(&*knotCurve2);
+ Ray ray2(B->initialPoint(), endArcPoint);
+ if (cubic2) {
+ ray2.setPoints(endArcPoint, (*cubic2)[1]);
+ }
+ bool ccwToggle = cross(A->finalPoint() - startArcPoint, endArcPoint - startArcPoint) < 0;
+ double distanceArc = Geom::distance(startArcPoint,middle_point(startArcPoint,endArcPoint));
+ double angleBetween = angle_between(ray1, ray2, ccwToggle);
+ rad = distanceArc/sin(angleBetween/2.0);
+ return rad * -1;
+}
+
+std::vector<double> FilletChamferPointArrayParam::get_times(int index, std::vector<Geom::Path> subpaths, bool last)
+{
+ const double tolerance = 0.001;
+ const double gapHelper = 0.00001;
+ std::pair<std::size_t, std::size_t> positions = get_positions(index, subpaths);
+ Curve *curve_it1;
+ curve_it1 = subpaths[positions.first][positions.second].duplicate();
+ Coord it1_length = (*curve_it1).length(tolerance);
+ double time_it1, time_it2, time_it1_B, intpart;
+ time_it1 = modf(to_time(index, _vector[index][X]), &intpart);
+ if (_vector[index][Y] == 0) {
+ time_it1 = 0;
+ }
+ double resultLenght = 0;
+ time_it1_B = 1;
+ if (subpaths[positions.first].closed() && last) {
+ time_it2 = modf(to_time(index - positions.second , _vector[index - positions.second ][X]), &intpart);
+ resultLenght = it1_length + to_len(index - positions.second, _vector[index - positions.second ][X]);
+ } else if (!subpaths[positions.first].closed() && last){
+ time_it2 = 0;
+ resultLenght = 0;
+ } else {
+ time_it2 = modf(to_time(index + 1, _vector[index + 1][X]), &intpart);
+ resultLenght = it1_length + to_len( index + 1, _vector[index + 1][X]);
+ }
+ if (resultLenght > 0 && time_it2 != 0) {
+ time_it1_B = modf(to_time(index, -resultLenght), &intpart);
+ } else {
+ if (time_it2 == 0) {
+ time_it1_B = 1;
+ } else {
+ time_it1_B = gapHelper;
+ }
+ }
+
+ if ((subpaths[positions.first].closed() && last && _vector[index - positions.second][Y] == 0) || (subpaths[positions.first].size() > positions.second + 1 && _vector[index + 1][Y] == 0)) {
+ time_it1_B = 1;
+ time_it2 = 0;
+ }
+ if (time_it1_B < time_it1) {
+ time_it1_B = time_it1 + gapHelper;
+ }
+ std::vector<double> out;
+ out.push_back(time_it1);
+ out.push_back(time_it1_B);
+ out.push_back(time_it2);
+ return out;
+}
+
+std::pair<std::size_t, std::size_t> FilletChamferPointArrayParam::get_positions(int index, std::vector<Geom::Path> subpaths)
+{
+ int counter = -1;
+ std::size_t first = 0;
+ std::size_t second = 0;
+ for (PathVector::const_iterator path_it = subpaths.begin(); path_it != subpaths.end(); ++path_it) {
+ if (path_it->empty())
+ continue;
+ Geom::Path::const_iterator curve_it1 = path_it->begin();
+ Geom::Path::const_iterator curve_endit = path_it->end_default();
+ if (path_it->closed()) {
+ const Geom::Curve &closingline = path_it->back_closed();
+ // the closing line segment is always of type
+ // Geom::LineSegment.
+ if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ // closingline.isDegenerate() did not work, because it only checks for
+ // *exact* zero length, which goes wrong for relative coordinates and
+ // rounding errors...
+ // the closing line segment has zero-length. So stop before that one!
+ curve_endit = path_it->end_open();
+ }
+ }
+ first++;
+ second = 0;
+ while (curve_it1 != curve_endit) {
+ counter++;
+ second++;
+ if(counter == index){
+ break;
+ }
+ ++curve_it1;
+ }
+ if(counter == index){
+ break;
+ }
+ }
+ first--;
+ second--;
+ std::pair<std::size_t, std::size_t> out(first, second);
+ return out;
+}
+
+int FilletChamferPointArrayParam::last_index(int index, std::vector<Geom::Path> subpaths)
+{
+ int counter = -1;
+ bool inSubpath = false;
+ for (PathVector::const_iterator path_it = subpaths.begin(); path_it != subpaths.end(); ++path_it) {
+ if (path_it->empty())
+ continue;
+ Geom::Path::const_iterator curve_it1 = path_it->begin();
+ Geom::Path::const_iterator curve_endit = path_it->end_default();
+ if (path_it->closed()) {
+ const Geom::Curve &closingline = path_it->back_closed();
+ if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
+ curve_endit = path_it->end_open();
+ }
+ }
+ while (curve_it1 != curve_endit) {
+ counter++;
+ if(counter == index){
+ inSubpath = true;
+ }
+ ++curve_it1;
+ }
+ if(inSubpath){
+ break;
+ }
+ }
+ if(!inSubpath){
+ counter = -1;
+ }
+ return counter;
+}
+
+
double FilletChamferPointArrayParam::len_to_time(int index, double len)
{
double t = 0;
@@ -405,7 +619,7 @@ double FilletChamferPointArrayParam::len_to_time(int index, double len)
if (len != 0) {
if (last_pwd2[index][0].degreesOfFreedom() != 2) {
Piecewise<D2<SBasis> > u;
- u.push_cut(0);
+ u.push_cut(0);
u.push(last_pwd2[index], 1);
std::vector<double> t_roots = roots(arcLengthSb(u) - std::abs(len));
if (t_roots.size() > 0) {
@@ -586,34 +800,34 @@ void FilletChamferPointArrayParamKnotHolderEntity::knot_click(guint state)
this->knot->tip = g_strdup(tip);
this->knot->show();
}
- } 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());
+ } else if (state & GDK_SHIFT_MASK) {
+ double xModified = _pparam->_vector.at(_index).x();
+ if(xModified < 0 && !_pparam->use_distance){
+ xModified = _pparam->len_to_rad(_index, _pparam->_vector.at(_index).x());
+ }
+ std::vector<Geom::Path> subpaths = path_from_piecewise(_pparam->last_pwd2, 0.1);
+ std::pair<std::size_t, std::size_t> positions = _pparam->get_positions(_index, subpaths);
+ D2<SBasis> A = _pparam->last_pwd2[_pparam->last_index(_index, subpaths)];
+ if(positions.second != 0){
+ A = _pparam->last_pwd2[_index-1];
+ }
+ D2<SBasis> B = _pparam->last_pwd2[_index];
+ bool aprox = (A[0].degreesOfFreedom() != 2 || B[0].degreesOfFreedom() != 2) && !_pparam->use_distance?true:false;
+ Geom::Point offset = Geom::Point(xModified, _pparam->_vector.at(_index).y());
Inkscape::UI::Dialogs::FilletChamferPropertiesDialog::showDialog(
- this->desktop, offset, this, _pparam->unit);
+ this->desktop, offset, this, _pparam->unit, _pparam->use_distance, aprox);
}
}
-void
-FilletChamferPointArrayParamKnotHolderEntity::knot_doubleclicked(guint state)
-{
- //todo: fill the double click dialog whith this parameters in the added file
- //src/ui/dialog/lpe-fillet-chamfer-properties.cpp(.h)
- //My idea for when have enought time is:
- //label whith radius in percent
- //label whith radius in size -maybe handle units-
- //entry whith actual radius -in flexible % mode or fixed -?px-
- //2 radio options to switch entry from fixed or flexible, also update the
- //entry
- //Checkbox or two radios to swith fillet or chamfer
-
-}
-
void FilletChamferPointArrayParamKnotHolderEntity::knot_set_offset(
Geom::Point offset)
{
- _pparam->_vector.at(_index) = Geom::Point(offset.x(), offset.y());
+ double xModified = offset.x();
+ if(xModified < 0 && !_pparam->use_distance){
+ xModified = _pparam->rad_to_len(_index, offset.x());
+ }
+ _pparam->_vector.at(_index) = Geom::Point(xModified, offset.y());
this->parent_holder->knot_ungrabbed_handler(this->knot, 0);
}
@@ -644,7 +858,6 @@ void FilletChamferPointArrayParam::addKnotHolderEntities(KnotHolder *knotholder,
"<b>Shift+Click</b> open dialog, "
"<b>Ctrl+Alt+Click</b> reset");
}
-
FilletChamferPointArrayParamKnotHolderEntity *e =
new FilletChamferPointArrayParamKnotHolderEntity(this, i);
e->create(desktop, item, knotholder, Inkscape::CTRL_TYPE_UNKNOWN, _(tip),
diff --git a/src/live_effects/parameter/filletchamferpointarray.h b/src/live_effects/parameter/filletchamferpointarray.h
index 93d6e9f4c..a1fa698ae 100644
--- a/src/live_effects/parameter/filletchamferpointarray.h
+++ b/src/live_effects/parameter/filletchamferpointarray.h
@@ -43,9 +43,15 @@ public:
guint32 color);
virtual double to_time(int index, double A);
virtual double to_len(int index, double A);
+ virtual double rad_to_len(int index, double rad);
+ virtual double len_to_rad(int index, double len);
virtual double len_to_time(int index, double len);
virtual double time_to_len(int index, double time);
+ virtual std::pair<std::size_t, std::size_t> get_positions(int index, std::vector<Geom::Path> subpaths);
+ virtual int last_index(int index, std::vector<Geom::Path> subpaths);
+ std::vector<double> get_times(int index, std::vector<Geom::Path> subpaths, bool last);
virtual void set_helper_size(int hs);
+ virtual void set_use_distance(bool use_knot_distance);
virtual void set_unit(const gchar *abbr);
virtual void addCanvasIndicators(SPLPEItem const *lpeitem,
std::vector<Geom::PathVector> &hp_vec);
@@ -79,6 +85,7 @@ private:
SPKnotModeType knot_mode;
guint32 knot_color;
int helper_size;
+ bool use_distance;
const gchar *unit;
Geom::PathVector hp;
@@ -96,7 +103,6 @@ public:
guint state);
virtual Geom::Point knot_get() const;
virtual void knot_click(guint state);
- virtual void knot_doubleclicked(guint state);
virtual void knot_set_offset(Geom::Point offset);
/*Checks whether the index falls within the size of the parameter's vector*/
diff --git a/src/live_effects/parameter/originalpatharray.cpp b/src/live_effects/parameter/originalpatharray.cpp
new file mode 100644
index 000000000..7706dbdf8
--- /dev/null
+++ b/src/live_effects/parameter/originalpatharray.cpp
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#if GLIBMM_DISABLE_DEPRECATED && HAVE_GLIBMM_THREADS_H
+#include <glibmm/threads.h>
+#endif
+
+#include "live_effects/parameter/originalpatharray.h"
+
+#include <gtkmm/widget.h>
+#include <gtkmm/icontheme.h>
+#include <gtkmm/imagemenuitem.h>
+#include <gtkmm/separatormenuitem.h>
+#include <gtkmm/scrolledwindow.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 <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);
+
+ //quick little hack -- newer versions of gtk gave the item zero space allotment
+ _scroller.set_size_request(-1, 120);
+
+ _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::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));
+}
+
+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..6c792613f
--- /dev/null
+++ b/src/live_effects/parameter/originalpatharray.h
@@ -0,0 +1,122 @@
+#ifndef INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H
+#define INKSCAPE_LIVEPATHEFFECT_PARAMETER_ORIGINALPATHARRAY_H
+
+/*
+ * Inkscape::LivePathEffectParameters
+ *
+ * Copyright (C) Theodore Janeczko 2012 <flutterguy317@gmail.com>
+ *
+ * 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;
+ 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 *, SPItem *, PathAndDirection*) {}
+ 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 647986da6..427be8065 100644
--- a/src/live_effects/parameter/powerstrokepointarray.cpp
+++ b/src/live_effects/parameter/powerstrokepointarray.cpp
@@ -4,8 +4,7 @@
* Released under GNU GPL, read the file 'COPYING' for more information
*/
-#include <glibmm/i18n.h>
-
+#include "ui/dialog/lpe-powerstroke-properties.h"
#include "live_effects/parameter/powerstrokepointarray.h"
#include "live_effects/effect.h"
@@ -21,6 +20,8 @@
#include "desktop.h"
#include "live_effects/lpeobject.h"
+#include <glibmm/i18n.h>
+
namespace Inkscape {
namespace LivePathEffect {
@@ -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),
@@ -184,6 +202,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, 0);
+}
+
void
PowerStrokePointArrayParamKnotHolderEntity::knot_click(guint state)
{
@@ -226,10 +250,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);
}
}
@@ -238,7 +267,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..0d03432c3
--- /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 "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"
+
+#include <glibmm/i18n.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..c96bedb53
--- /dev/null
+++ b/src/live_effects/parameter/transformedpoint.h
@@ -0,0 +1,82 @@
+#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_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;
+
+ friend class TransformedPointParamKnotHolderEntity_Vector;
+};
+
+
+} //namespace LivePathEffect
+
+} //namespace Inkscape
+
+#endif
diff --git a/src/live_effects/pathoutlineprovider.cpp b/src/live_effects/pathoutlineprovider.cpp
new file mode 100644
index 000000000..21a0fb809
--- /dev/null
+++ b/src/live_effects/pathoutlineprovider.cpp
@@ -0,0 +1,803 @@
+/* Author:
+ * Liam P. White <inkscapebrony@gmail.com>
+ *
+ * Copyright (C) 2014 Author
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <2geom/angle.h>
+#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 <cstdio>
+
+#include "pathoutlineprovider.h"
+#include "livarot/path-description.h"
+#include "helper/geom-nodetype.h"
+#include "svg/svg.h"
+
+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));
+}
+
+std::vector<Geom::Path> split_at_cusps(const Geom::Path& in)
+{
+ PathVector out = PathVector();
+ Path temp = Path();
+
+ for (unsigned i = 0; i < in.size(); i++) {
+ temp.append(in[i]);
+ if ( get_nodetype(in[i], in[i + 1]) != Geom::NODE_SMOOTH ) {
+ out.push_back(temp);
+ temp = Path();
+ }
+ }
+ if (temp.size() > 0) {
+ out.push_back(temp);
+ }
+ return out;
+}
+
+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 Geom
+
+namespace Outline {
+
+typedef Geom::D2<Geom::SBasis> D2SB;
+typedef Geom::Piecewise<D2SB> PWD2;
+
+// UTILITY
+
+unsigned bezierOrder (const Geom::Curve* curve_in)
+{
+ using namespace Geom;
+ if ( const BezierCurve* bz = dynamic_cast<const BezierCurve*>(curve_in) ) {
+ return bz->order();
+ }
+ return 0;
+}
+
+/**
+ * @return true if the angle formed by the curves and their handles is greater than 180 degrees clockwise, otherwise false.
+ */
+bool outside_angle (const Geom::Curve& cbc1, const Geom::Curve& cbc2)
+{
+ Geom::Point start_point;
+ Geom::Point cross_point = cbc1.finalPoint();
+ Geom::Point end_point;
+
+ if (cross_point != cbc2.initialPoint()) {
+ printf("WARNING: Non-contiguous path in Outline::outside_angle()");
+ return false;
+ }
+
+ Geom::CubicBezier cubicBezier = Geom::sbasis_to_cubicbezier(cbc1.toSBasis());
+ start_point = cubicBezier [2];
+
+ /*
+ * Because the node editor does not yet support true quadratics, paths are converted to
+ * cubic beziers in the node tool with degenerate handles on one side.
+ */
+
+ if (are_near(start_point, cross_point, 0.0000001)) {
+ start_point = cubicBezier [1];
+ }
+ cubicBezier = Geom::sbasis_to_cubicbezier(cbc2.toSBasis());
+ end_point = cubicBezier [1];
+ if (are_near(end_point, cross_point, 0.0000001)) {
+ end_point = cubicBezier [2];
+ }
+
+ // got our three points, now let's see what their clockwise angle is
+
+ // Definition of a Graham scan
+
+ /********************************************************************
+ # Three points are a counter-clockwise turn if ccw > 0, clockwise if
+ # ccw < 0, and collinear if ccw = 0 because ccw is a determinant that
+ # gives the signed area of the triangle formed by p1, p2 and p3.
+ function ccw(p1, p2, p3):
+ return (p2.x - p1.x)*(p3.y - p1.y) - (p2.y - p1.y)*(p3.x - p1.x)
+ *********************************************************************/
+
+ double ccw = ( (cross_point.x() - start_point.x()) * (end_point.y() - start_point.y()) ) -
+ ( (cross_point.y() - start_point.y()) * (end_point.x() - start_point.x()) );
+ return ccw > 0;
+}
+
+// LINE JOINS
+
+typedef Geom::BezierCurveN<1u> BezierLine;
+
+/**
+ * Removes the crossings on an interior join.
+ * @param path_builder Contains the incoming segment; result is appended to this
+ * @param outgoing The outgoing segment
+ */
+void joinInside(Geom::Path& path_builder, Geom::Curve const& outgoing)
+{
+ Geom::Curve const& incoming = path_builder.back();
+
+ // Using Geom::crossings to find intersections between two curves
+ Geom::Crossings cross = Geom::crossings(incoming, outgoing);
+ if (!cross.empty()) {
+ // Crossings found, create the join
+ Geom::CubicBezier cubic = Geom::sbasis_to_cubicbezier(incoming.toSBasis());
+ cubic = cubic.subdivide(cross[0].ta).first;
+ // erase the last segment, as we're going to overwrite it now
+ path_builder.erase_last();
+ path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS);
+
+ cubic = Geom::sbasis_to_cubicbezier(outgoing.toSBasis());
+ cubic = cubic.subdivide(cross[0].tb).second;
+ path_builder.append(cubic, Geom::Path::STITCH_DISCONTINUOUS);
+ } else {
+ // No crossings occurred, or Geom::crossings() failed; default to bevel
+ if (Geom::are_near(incoming.finalPoint(), outgoing.initialPoint())) {
+ path_builder.appendNew<BezierLine>(outgoing.initialPoint());
+ } else {
+ path_builder.setFinal(outgoing.initialPoint());
+ }
+ }
+}
+
+/**
+ * Try to create a miter join. Falls back to bevel if no miter can be created.
+ * @param path_builder Path to append curves to; back() is the incoming curve
+ * @param outgoing Outgoing curve.
+ * @param miter_limit When mitering, don't exceed this length
+ * @param line_width The thickness of the line.
+ */
+void miter_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width)
+{
+ using namespace Geom;
+ Curve const& incoming = path_builder.back();
+ Point tang1 = unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.);
+ Point tang2 = unitTangentAt(outgoing.toSBasis(), 0);
+
+ boost::optional <Point> p = intersection_point (incoming.finalPoint(), tang1, outgoing.initialPoint(), tang2);
+ if (p) {
+ // check size of miter
+ Point point_on_path = incoming.finalPoint() - rot90(tang1) * line_width;
+ Coord len = distance(*p, point_on_path);
+ if (len <= miter_limit) {
+ // miter OK
+ path_builder.appendNew<BezierLine>(*p);
+ }
+ }
+ path_builder.appendNew<BezierLine>(outgoing.initialPoint());
+}
+
+/**
+ * Smoothly extrapolate curves along a circular route. Falls back to miter if necessary.
+ * @param path_builder Path to append curves to; back() is the incoming curve
+ * @param outgoing Outgoing curve.
+ * @param miter_limit When mitering, don't exceed this length
+ * @param line_width The thickness of the line. Used for miter fallback.
+ */
+void extrapolate_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width)
+{
+ Geom::Curve const& incoming = path_builder.back();
+ Geom::Point endPt = outgoing.initialPoint();
+
+ // The method used when extrapolating curves fails to work when either side of the join to be extrapolated
+ // is a line segment. When this situation is encountered, fall back to a regular miter join.
+ bool lineProblem = (dynamic_cast<const BezierLine *>(&incoming)) || (dynamic_cast<const BezierLine *>(&outgoing));
+ if (lineProblem == false) {
+ // Geom::Point tang1 = Geom::unitTangentAt(Geom::reverse(incoming.toSBasis()), 0.);
+ Geom::Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0);
+
+ Geom::Circle circle1 = Geom::touching_circle(Geom::reverse(incoming.toSBasis()), 0.);
+ Geom::Circle circle2 = Geom::touching_circle(outgoing.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(incoming.finalPoint(), 0.5*(incoming.finalPoint()+sol), sol, true);
+ Geom::EllipticalArc *arc1 = circle2.arc(sol, 0.5*(sol+endPt), endPt, true);
+ try {
+ if (arc0) {
+ path_builder.append (arc0->toSBasis());
+ delete arc0;
+ arc0 = NULL;
+ } else {
+ throw std::exception();
+ }
+
+ if (arc1) {
+ path_builder.append (arc1->toSBasis());
+ delete arc1;
+ arc1 = NULL;
+ } else {
+ throw std::exception();
+ }
+
+ } catch (std::exception const & ex) {
+ printf("WARNING: Error extrapolating line join: %s\n", ex.what());
+ path_builder.appendNew<Geom::LineSegment>(endPt);
+ }
+ } else {
+ // 1 or no solutions found, default to miter
+ miter_curves(path_builder, outgoing, miter_limit, line_width);
+ }
+ } else {
+ // Line segments exist
+ miter_curves(path_builder, outgoing, miter_limit, line_width);
+ }
+}
+
+/**
+ * Extrapolate curves by reflecting them along the line that would be given by beveling the join.
+ * @param path_builder Path to append curves to; back() is the incoming curve
+ * @param outgoing Outgoing curve.
+ * @param miter_limit When mitering, don't exceed this length
+ * @param line_width The thickness of the line. Used for miter fallback.
+ */
+void reflect_curves(Geom::Path& path_builder, Geom::Curve const& outgoing, double miter_limit, double line_width)
+{
+ using namespace Geom;
+ Curve const& incoming = path_builder.back();
+ // On the outside, we'll take the incoming curve, the outgoing curve, and
+ // reflect them over the line formed by taking the unit tangent vector at times
+ // 0 and 1, respectively, rotated by 90 degrees.
+ Crossings cross;
+
+ // reflect curves along the line that would be given by beveling the join
+ Point tang1 = unitTangentAt(reverse(incoming.toSBasis()), 0.);
+ D2SB newcurve1 = incoming.toSBasis() * reflection(-rot90(tang1), incoming.finalPoint());
+ CubicBezier bzr1 = sbasis_to_cubicbezier(reverse(newcurve1));
+
+ Point tang2 = Geom::unitTangentAt(outgoing.toSBasis(), 0.);
+ D2SB newcurve2 = outgoing.toSBasis() * reflection(-rot90(tang2), outgoing.initialPoint());
+ CubicBezier bzr2 = sbasis_to_cubicbezier(reverse(newcurve2));
+
+ cross = crossings(bzr1, bzr2);
+ if (cross.empty()) {
+ // paths don't cross, fall back to miter
+ miter_curves(path_builder, outgoing, miter_limit, line_width);
+ } else {
+ // reflected join
+ std::pair<CubicBezier, CubicBezier> sub1 = bzr1.subdivide(cross[0].ta);
+ std::pair<CubicBezier, CubicBezier> sub2 = bzr2.subdivide(cross[0].tb);
+
+ // TODO it seems as if a bug in 2geom sometimes doesn't catch the first
+ // crossing of paths, but the second instead; but only sometimes.
+ path_builder.appendNew <CubicBezier> (sub1.first[1], sub1.first[2], sub2.second[0]);
+ path_builder.appendNew <CubicBezier> (sub2.second[1], sub2.second[2], outgoing.initialPoint());
+ }
+}
+
+// Ideal function pointer we want to pass
+typedef void JoinFunc(Geom::Path& /*path_builder*/, Geom::Curve const& /*outgoing*/, double /*miter_limit*/, double /*line_width*/);
+
+/**
+ * Helper function for repeated logic in outlineHalf.
+ */
+static void outlineHelper(Geom::Path& path_builder, Geom::PathVector* path_vec, bool outside, double width, double miter, JoinFunc func)
+{
+ Geom::Curve * cbc2 = path_vec->front()[0].duplicate();
+
+ if (outside) {
+ func(path_builder, *cbc2, miter, width);
+ } else {
+ joinInside(path_builder, *cbc2);
+ }
+
+ // store it
+ Geom::Path temp_path = path_vec->front();
+ if (!outside) {
+ // erase the first segment since the inside join code already appended it
+ temp_path.erase(temp_path.begin());
+ }
+
+ if (temp_path.initialPoint() != path_builder.finalPoint()) {
+ temp_path.setInitial(path_builder.finalPoint());
+ }
+
+ path_builder.append(temp_path);
+
+ delete cbc2;
+}
+
+/**
+ * Offsets exactly one half of a bezier spline (path).
+ * @param path_in The input path to use. (To create the other side use path_in.reverse() )
+ * @param line_width the line width to use (usually you want to divide this by 2)
+ * @param miter_limit the miter parameter
+ * @param func Join function to apply at each join.
+ */
+
+Geom::Path outlineHalf(const Geom::Path& path_in, double line_width, double miter_limit, JoinFunc func)
+{
+ // NOTE: it is important to notice the distinction between a Geom::Path and a livarot ::Path here!
+ // if you do not see "Geom::" there is a different function set!
+
+ Geom::PathVector pv = split_at_cusps(path_in);
+
+ ::Path to_outline;
+ ::Path outlined_result;
+
+ Geom::Path path_builder = Geom::Path(); // the path to store the result in
+ Geom::PathVector* path_vec; // needed because livarot returns a pointer (TODO make this not a pointer)
+
+ // Do two curves at a time for efficiency, since the join function needs to know the outgoing curve as well
+ const size_t k = pv.size();
+ for (size_t u = 0; u < k; u += 2) {
+ to_outline = Path();
+ outlined_result = Path();
+
+ to_outline.LoadPath(pv[u], Geom::identity(), false, false);
+ to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
+ // now a curve has been outside outlined and loaded into outlined_result
+
+ // get the Geom::Path
+ path_vec = outlined_result.MakePathVector();
+
+ // on the first run through, there is no join
+ if (u == 0) {
+ path_builder.start(path_vec->front().initialPoint());
+ path_builder.append(path_vec->front());
+ } else {
+ outlineHelper(path_builder, path_vec, outside_angle(pv[u-1][pv[u-1].size()-1], pv[u][0]), line_width, miter_limit, func);
+ }
+
+ // outline the next segment, but don't store it yet
+ if (path_vec)
+ delete path_vec;
+ path_vec = NULL;
+
+ // odd number of paths
+ if (u < k - 1) {
+ outlined_result = Path();
+ to_outline = Path();
+
+ to_outline.LoadPath(pv[u+1], Geom::Affine(), false, false);
+ to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
+
+ path_vec = outlined_result.MakePathVector();
+ outlineHelper(path_builder, path_vec, outside_angle(pv[u][pv[u].size()-1], pv[u+1][0]), line_width, miter_limit, func);
+
+ if (path_vec)
+ delete path_vec;
+ path_vec = NULL;
+ }
+ }
+
+ if (path_in.closed()) {
+ Geom::Curve * cbc1;
+ Geom::Curve * cbc2;
+
+ if ( path_in[path_in.size()].isDegenerate() ) {
+ // handle case for last segment curved
+ outlined_result = Path();
+ to_outline = Path();
+
+ Geom::Path oneCurve; oneCurve.append(path_in[0]);
+
+ to_outline.LoadPath(oneCurve, Geom::Affine(), false, false);
+ to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
+
+ path_vec = outlined_result.MakePathVector();
+
+ cbc1 = path_builder[path_builder.size() - 1].duplicate();
+ cbc2 = path_vec->front()[0].duplicate();
+
+ delete path_vec;
+ } else {
+ // handle case for last segment straight
+ // since the path doesn't actually give us access to it, we'll do it ourselves
+ outlined_result = Path();
+ to_outline = Path();
+
+ Geom::Path oneCurve; oneCurve.append(Geom::LineSegment(path_in.finalPoint(), path_in.initialPoint()));
+
+ to_outline.LoadPath(oneCurve, Geom::Affine(), false, false);
+ to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
+
+ path_vec = outlined_result.MakePathVector();
+
+ cbc1 = path_builder[path_builder.size() - 1].duplicate();
+ cbc2 = (*path_vec)[0] [0].duplicate();
+
+ outlineHelper(path_builder, path_vec, outside_angle(path_in[path_in.size()-1], oneCurve[0]), line_width, miter_limit, func);
+
+ delete cbc1;
+ cbc1 = cbc2->duplicate();
+ delete path_vec;
+
+ oneCurve = Geom::Path(); oneCurve.append(path_in[0]);
+
+ to_outline.LoadPath(oneCurve, Geom::Affine(), false, false);
+ to_outline.OutsideOutline(&outlined_result, line_width / 2, join_straight, butt_straight, 10);
+
+ path_vec = outlined_result.MakePathVector();
+ delete cbc2; cbc2 = (*path_vec)[0] [0].duplicate();
+ delete path_vec;
+ }
+
+ Geom::Path temporary;
+ temporary.append(*cbc1);
+
+ Geom::Curve const & prev_curve = path_in[path_in.size()].isDegenerate() ? path_in[path_in.size() - 1] : path_in[path_in.size()];
+ Geom::Path isStraight;
+ isStraight.append(prev_curve);
+ isStraight.append(path_in[0]);
+ // does closing path require a join?
+ if (Geom::split_at_cusps(isStraight).size() > 1) {
+ bool outside = outside_angle(prev_curve, path_in[0]);
+ if (outside) {
+ func(temporary, *cbc2, miter_limit, line_width);
+ } else {
+ joinInside(temporary, *cbc2);
+ path_builder.erase(path_builder.begin());
+ }
+
+ // extract the appended curves
+ path_builder.erase_last();
+ if (Geom::are_near(path_builder.finalPoint(), temporary.initialPoint())) {
+ path_builder.setFinal(temporary.initialPoint());
+ } else {
+ path_builder.appendNew<BezierLine>(temporary.initialPoint());
+ }
+ path_builder.append(temporary);
+ } else {
+ // closing path does not require a join
+ path_builder.setFinal(path_builder.initialPoint());
+ }
+ path_builder.close();
+
+ if (cbc1) delete cbc1;
+ if (cbc2) delete cbc2;
+ }
+
+ return path_builder;
+}
+
+Geom::PathVector outlinePath(const Geom::PathVector& path_in, double line_width, LineJoinType join, ButtTypeMod butt, double miter_lim, bool extrapolate, double start_lean, double end_lean)
+{
+ Geom::PathVector path_out;
+
+ unsigned pv_size = path_in.size();
+ for (unsigned i = 0; i < pv_size; i++) {
+
+ if (path_in[i].size() > 1) {
+ Geom::Path with_direction;
+ Geom::Path against_direction;
+
+ with_direction = Outline::outlineHalf(path_in[i], -line_width, miter_lim, extrapolate ? extrapolate_curves : reflect_curves);
+ against_direction = Outline::outlineHalf(path_in[i].reverse(), -line_width, miter_lim, extrapolate ? extrapolate_curves : reflect_curves);
+
+ Geom::PathBuilder pb;
+
+ pb.moveTo(with_direction.initialPoint());
+ pb.append(with_direction);
+
+ //add in our line caps
+ if (!path_in[i].closed()) {
+ switch (butt) {
+ case BUTT_STRAIGHT:
+ pb.lineTo(against_direction.initialPoint());
+ break;
+ case BUTT_ROUND:
+ pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, against_direction.initialPoint() );
+ break;
+ case BUTT_POINTY: {
+ Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.);
+ double radius = 0.5 * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint());
+ Geom::Point midpoint = 0.5 * (with_direction.finalPoint() + against_direction.initialPoint()) + radius*end_deriv;
+ pb.lineTo(midpoint);
+ pb.lineTo(against_direction.initialPoint());
+ break;
+ }
+ case BUTT_SQUARE: {
+ Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.);
+ double radius = 0.5 * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint());
+ pb.lineTo(with_direction.finalPoint() + radius*end_deriv);
+ pb.lineTo(against_direction.initialPoint() + radius*end_deriv);
+ pb.lineTo(against_direction.initialPoint());
+ break;
+ }
+ case BUTT_LEANED: {
+ Geom::Point end_deriv = -Geom::unitTangentAt(Geom::reverse(path_in[i].back().toSBasis()), 0.);
+ double maxRadius = (end_lean+0.5) * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint());
+ double minRadius = ((end_lean*-1)+0.5) * Geom::distance(with_direction.finalPoint(), against_direction.initialPoint());
+ pb.lineTo(with_direction.finalPoint() + maxRadius*end_deriv);
+ pb.lineTo(against_direction.initialPoint() + minRadius*end_deriv);
+ pb.lineTo(against_direction.initialPoint());
+ break;
+ }
+ }
+ } else {
+ pb.moveTo(against_direction.initialPoint());
+ }
+
+ pb.append(against_direction);
+
+ //cap (if necessary)
+ if (!path_in[i].closed()) {
+ switch (butt) {
+ case BUTT_STRAIGHT:
+ pb.lineTo(with_direction.initialPoint());
+ break;
+ case BUTT_ROUND:
+ pb.arcTo((-line_width) / 2, (-line_width) / 2, 0., true, true, with_direction.initialPoint() );
+ break;
+ case BUTT_POINTY: {
+ Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.);
+ double radius = 0.5 * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint());
+ Geom::Point midpoint = 0.5 * (against_direction.finalPoint() + with_direction.initialPoint()) + radius*end_deriv;
+ pb.lineTo(midpoint);
+ pb.lineTo(with_direction.initialPoint());
+ break;
+ }
+ case BUTT_SQUARE: {
+ Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.);
+ double radius = 0.5 * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint());
+ pb.lineTo(against_direction.finalPoint() + radius*end_deriv);
+ pb.lineTo(with_direction.initialPoint() + radius*end_deriv);
+ pb.lineTo(with_direction.initialPoint());
+ break;
+ }
+ case BUTT_LEANED: {
+ Geom::Point end_deriv = -Geom::unitTangentAt(path_in[i].front().toSBasis(), 0.);
+ double maxRadius = (start_lean+0.5) * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint());
+ double minRadius = ((start_lean*-1)+0.5) * Geom::distance(against_direction.finalPoint(), with_direction.initialPoint());
+ pb.lineTo(against_direction.finalPoint() + minRadius*end_deriv);
+ pb.lineTo(with_direction.initialPoint() + maxRadius*end_deriv);
+ pb.lineTo(with_direction.initialPoint());
+ break;
+ }
+ }
+ }
+ pb.flush();
+ path_out.push_back(pb.peek()[0]);
+ if (path_in[i].closed()) {
+ path_out.push_back(pb.peek()[1]);
+ }
+ } else {
+ Path p = Path();
+ Path outlinepath = Path();
+ ButtType original_butt;
+ switch (butt) {
+ case BUTT_STRAIGHT:
+ original_butt = butt_straight;
+ break;
+ case BUTT_ROUND:
+ original_butt = butt_round;
+ break;
+ case butt_pointy: {
+ original_butt = butt_pointy;
+ break;
+ }
+ case BUTT_SQUARE: {
+ original_butt = butt_square;
+ break;
+ }
+ case BUTT_LEANED: {
+ original_butt = butt_straight;
+ break;
+ }
+ }
+ p.LoadPath(path_in[i], Geom::Affine(), false, false);
+ p.Outline(&outlinepath, line_width / 2, static_cast<join_typ>(join), original_butt, miter_lim);
+ Geom::PathVector *pv_p = outlinepath.MakePathVector();
+ //somewhat hack-ish
+ path_out.push_back( (*pv_p)[0].reverse() );
+ if (pv_p) delete pv_p;
+ }
+ }
+ return path_out;
+}
+
+#define miter_lim fabs(line_width * miter_limit)
+
+Geom::PathVector PathVectorOutline(Geom::PathVector const & path_in, double line_width, ButtTypeMod linecap_type, LineJoinType linejoin_type, double miter_limit, double start_lean, double end_lean)
+{
+ 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));
+ }
+
+ // magic!
+ ButtType original_butt;
+ switch (linecap_type) {
+ case BUTT_STRAIGHT:
+ original_butt = butt_straight;
+ break;
+ case BUTT_ROUND:
+ original_butt = butt_round;
+ break;
+ case butt_pointy: {
+ original_butt = butt_pointy;
+ break;
+ }
+ case BUTT_SQUARE: {
+ original_butt = butt_square;
+ break;
+ }
+ case BUTT_LEANED: {
+ original_butt = butt_straight;
+ break;
+ }
+ }
+ if (linejoin_type <= LINEJOIN_POINTY) {
+ p.Outline(&outlinepath, line_width / 2, static_cast<join_typ>(linejoin_type),
+ original_butt, miter_lim);
+ // fix memory leak
+ std::vector<Geom::Path> *pv_p = outlinepath.MakePathVector();
+ path_out = *pv_p;
+ delete pv_p;
+
+ } else if (linejoin_type == LINEJOIN_REFLECTED) {
+ // reflected arc join
+ path_out = outlinePath(path_in, line_width, static_cast<LineJoinType>(linejoin_type),
+ linecap_type , miter_lim, false, start_lean, end_lean);
+
+ } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) {
+ // extrapolated arc join
+ path_out = outlinePath(path_in, line_width, LINEJOIN_STRAIGHT, linecap_type, miter_lim, true, start_lean, end_lean);
+ }
+
+ return path_out;
+}
+
+Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit)
+{
+
+ Geom::Path path_out;
+
+ if (linejoin_type <= LINEJOIN_POINTY || path_in.size() <= 1) {
+
+ Geom::PathVector * pathvec;
+
+ Path path_tangent = Path();
+ Path path_outline = Path();
+ path_outline.LoadPath(path_in, Geom::Affine(), false, false);
+ path_outline.OutsideOutline(&path_tangent, line_width / 2, static_cast<join_typ>(linejoin_type), butt_straight, miter_lim);
+
+ pathvec = path_tangent.MakePathVector();
+ path_out = pathvec->front();
+ delete pathvec;
+ return path_out;
+ } else if (linejoin_type == LINEJOIN_REFLECTED) {
+ path_out = outlineHalf(path_in, line_width, miter_lim, reflect_curves);
+ return path_out;
+ } else if (linejoin_type == LINEJOIN_EXTRAPOLATED) {
+ path_out = outlineHalf(path_in, line_width, miter_lim, extrapolate_curves);
+ return path_out;
+ }
+ 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 :
diff --git a/src/live_effects/pathoutlineprovider.h b/src/live_effects/pathoutlineprovider.h
new file mode 100644
index 000000000..c17584be2
--- /dev/null
+++ b/src/live_effects/pathoutlineprovider.h
@@ -0,0 +1,55 @@
+#ifndef SEEN_PATH_OUTLINE_H
+#define SEEN_PATH_OUTLINE_H
+
+/* Author:
+ * Liam P. White <inkscapebrony@gmail.com>
+ *
+ * Copyright (C) 2014 Author
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <livarot/Path.h>
+#include <livarot/LivarotDefs.h>
+
+enum LineJoinType {
+ LINEJOIN_STRAIGHT,
+ LINEJOIN_ROUND,
+ LINEJOIN_POINTY,
+ LINEJOIN_REFLECTED,
+ LINEJOIN_EXTRAPOLATED
+};
+enum ButtTypeMod {
+ BUTT_STRAIGHT,
+ BUTT_ROUND,
+ BUTT_SQUARE,
+ BUTT_POINTY,
+ BUTT_LEANED
+};
+
+namespace Geom
+{
+ Geom::CubicBezier sbasis_to_cubicbezier(Geom::D2<Geom::SBasis> const & sbasis_in);
+ std::vector<Geom::Path> split_at_cusps(const Geom::Path& in);
+}
+
+namespace Outline
+{
+ unsigned bezierOrder (const Geom::Curve* curve_in);
+ std::vector<Geom::Path> PathVectorOutline(std::vector<Geom::Path> const & path_in, double line_width, ButtTypeMod linecap_type,
+ LineJoinType linejoin_type, double miter_limit, double start_lean = 0, double end_lean = 0);
+ Geom::Path PathOutsideOutline(Geom::Path const & path_in, double line_width, LineJoinType linejoin_type, double miter_limit);
+}
+
+#endif // SEEN_PATH_OUTLINE_H
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8 :