summaryrefslogtreecommitdiffstats
path: root/src/live_effects
diff options
context:
space:
mode:
authorJabier Arraiza Cenoz <jabier.arraiza@marker.es>2015-12-08 11:05:08 +0000
committerjabiertxof <jabier.arraiza@marker.es>2015-12-08 11:05:08 +0000
commitd79bd642b3214f1c44d7c1268e8adcb7bd55fffc (patch)
tree74a8529da5f23996ba4b358c045a7588f8c27f24 /src/live_effects
parentminor changes (diff)
parentmerge lp:~inkscape.dev/inkscape/lock_guides (diff)
downloadinkscape-d79bd642b3214f1c44d7c1268e8adcb7bd55fffc.tar.gz
inkscape-d79bd642b3214f1c44d7c1268e8adcb7bd55fffc.zip
update to trunk
(bzr r14272.1.11)
Diffstat (limited to 'src/live_effects')
-rw-r--r--src/live_effects/CMakeLists.txt2
-rw-r--r--src/live_effects/Makefile_insert2
-rw-r--r--src/live_effects/effect-enum.h1
-rw-r--r--src/live_effects/effect.cpp5
-rw-r--r--src/live_effects/lpe-bspline.cpp152
-rw-r--r--src/live_effects/lpe-bspline.h3
-rw-r--r--src/live_effects/lpe-fill-between-many.cpp2
-rw-r--r--src/live_effects/lpe-fillet-chamfer.cpp10
-rw-r--r--src/live_effects/lpe-interpolate_points.cpp5
-rw-r--r--src/live_effects/lpe-knot.cpp2
-rw-r--r--src/live_effects/lpe-powerstroke.cpp7
-rw-r--r--src/live_effects/lpe-roughen.cpp388
-rw-r--r--src/live_effects/lpe-roughen.h21
-rw-r--r--src/live_effects/lpe-simplify.cpp19
-rw-r--r--src/live_effects/lpe-taperstroke.cpp3
-rw-r--r--src/live_effects/lpe-transform_2pts.cpp462
-rw-r--r--src/live_effects/lpe-transform_2pts.h91
-rw-r--r--src/live_effects/parameter/bool.cpp32
-rw-r--r--src/live_effects/parameter/bool.h4
-rw-r--r--src/live_effects/parameter/filletchamferpointarray.cpp10
-rw-r--r--src/live_effects/parameter/originalpatharray.cpp12
-rw-r--r--src/live_effects/parameter/parameter.cpp44
-rw-r--r--src/live_effects/parameter/parameter.h6
-rw-r--r--src/live_effects/parameter/togglebutton.cpp4
-rw-r--r--src/live_effects/spiro-converters.cpp32
-rw-r--r--src/live_effects/spiro-converters.h24
-rw-r--r--src/live_effects/spiro.cpp15
27 files changed, 1090 insertions, 268 deletions
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index 8a097590a..9a2f06a76 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -6,6 +6,7 @@ set(live_effects_SRC
lpe-bounding-box.cpp
lpe-bspline.cpp
lpe-circle_3pts.cpp
+ lpe-transform_2pts.cpp
lpe-circle_with_radius.cpp
lpe-clone-original.cpp
lpe-constructgrid.cpp
@@ -83,6 +84,7 @@ set(live_effects_SRC
lpe-bounding-box.h
lpe-bspline.h
lpe-circle_3pts.h
+ lpe-transform_2pts.h
lpe-circle_with_radius.h
lpe-clone-original.h
lpe-constructgrid.h
diff --git a/src/live_effects/Makefile_insert b/src/live_effects/Makefile_insert
index c2c2ce93c..b5bee55c8 100644
--- a/src/live_effects/Makefile_insert
+++ b/src/live_effects/Makefile_insert
@@ -74,6 +74,8 @@ ink_common_sources += \
live_effects/lpe-mirror_symmetry.h \
live_effects/lpe-circle_3pts.cpp \
live_effects/lpe-circle_3pts.h \
+ live_effects/lpe-transform_2pts.cpp \
+ live_effects/lpe-transform_2pts.h \
live_effects/lpe-angle_bisector.cpp \
live_effects/lpe-angle_bisector.h \
live_effects/lpe-parallel.cpp \
diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h
index 383eec19e..eea26184c 100644
--- a/src/live_effects/effect-enum.h
+++ b/src/live_effects/effect-enum.h
@@ -38,6 +38,7 @@ enum EffectType {
TANGENT_TO_CURVE,
MIRROR_SYMMETRY,
CIRCLE_3PTS,
+ TRANSFORM_2PTS,
ANGLE_BISECTOR,
PARALLEL,
COPY_ROTATE,
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 332d81777..fe7bf3a97 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -57,6 +57,7 @@
#include "live_effects/lpe-sketch.h"
#include "live_effects/lpe-spiro.h"
#include "live_effects/lpe-tangent_to_curve.h"
+#include "live_effects/lpe-transform_2pts.h"
#include "live_effects/lpe-taperstroke.h"
#include "live_effects/lpe-test-doEffect-stack.h"
#include "live_effects/lpe-text_label.h"
@@ -150,6 +151,7 @@ const Util::EnumData<EffectType> LPETypeData[] = {
{PERSPECTIVE_ENVELOPE, N_("Perspective/Envelope"), "perspective-envelope"},
{FILLET_CHAMFER, N_("Fillet/Chamfer"), "fillet-chamfer"},
{INTERPOLATE_POINTS, N_("Interpolate points"), "interpolate_points"},
+ {TRANSFORM_2PTS, N_("Transform by 2 points"), "transform_2pts"},
};
const Util::EnumDataConverter<EffectType> LPETypeConverter(LPETypeData, sizeof(LPETypeData)/sizeof(*LPETypeData));
@@ -315,6 +317,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
case SHOW_HANDLES:
neweffect = static_cast<Effect*> ( new LPEShowHandles(lpeobj) );
break;
+ case TRANSFORM_2PTS:
+ neweffect = static_cast<Effect*> ( new LPETransform2Pts(lpeobj) );
+ break;
default:
g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
neweffect = NULL;
diff --git a/src/live_effects/lpe-bspline.cpp b/src/live_effects/lpe-bspline.cpp
index d2da33aa9..7e92e767d 100644
--- a/src/live_effects/lpe-bspline.cpp
+++ b/src/live_effects/lpe-bspline.cpp
@@ -10,16 +10,18 @@
#include "svg/svg.h"
#include "xml/repr.h"
#include "preferences.h"
+#include "document-undo.h"
+#include "verbs.h"
// TODO due to internal breakage in glibmm headers, this must be last:
#include <glibmm/i18n.h>
namespace Inkscape {
namespace LivePathEffect {
-const double HANDLE_CUBIC_GAP = 0.01;
+const double HANDLE_CUBIC_GAP = 0.001;
const double NO_POWER = 0.0;
-const double DEFAULT_START_POWER = 0.3334;
-const double DEFAULT_END_POWER = 0.6667;
+const double DEFAULT_START_POWER = 0.333334;
+const double DEFAULT_END_POWER = 0.666667;
Geom::PathVector hp;
void sp_bspline_drawHandle(Geom::Point p, double helper_size);
@@ -27,23 +29,27 @@ LPEBSpline::LPEBSpline(LivePathEffectObject *lpeobject)
: Effect(lpeobject),
steps(_("Steps with CTRL:"), _("Change number of steps with CTRL pressed"), "steps", &wr, this, 2),
helper_size(_("Helper size:"), _("Helper size"), "helper_size", &wr, this, 0),
- ignore_cusp(_("Ignore cusp nodes"), _("Change ignoring cusp nodes"), "ignore_cusp", &wr, this, true),
+ apply_no_weight(_("Apply changes if weight = 0%"), _("Apply changes if weight = 0%"), "apply_no_weight", &wr, this, true),
+ apply_with_weight(_("Apply changes if weight > 0%"), _("Apply changes if weight > 0%"), "apply_with_weight", &wr, this, true),
only_selected(_("Change only selected nodes"), _("Change only selected nodes"), "only_selected", &wr, this, false),
- weight(_("Change weight:"), _("Change weight of the effect"), "weight", &wr, this, DEFAULT_START_POWER)
+ weight(_("Change weight %:"), _("Change weight percent of the effect"), "weight", &wr, this, DEFAULT_START_POWER * 100)
{
registerParameter(&weight);
registerParameter(&steps);
registerParameter(&helper_size);
- registerParameter(&ignore_cusp);
+ registerParameter(&apply_no_weight);
+ registerParameter(&apply_with_weight);
registerParameter(&only_selected);
- weight.param_set_range(NO_POWER, 1);
+ weight.param_set_range(NO_POWER, 100.0);
weight.param_set_increments(0.1, 0.1);
weight.param_set_digits(4);
+ weight.param_overwrite_widget(true);
steps.param_set_range(1, 10);
steps.param_set_increments(1, 1);
steps.param_set_digits(0);
+ steps.param_overwrite_widget(true);
helper_size.param_set_range(0.0, 999.0);
helper_size.param_set_increments(1, 1);
@@ -111,15 +117,10 @@ Gtk::Widget *LPEBSpline::newWidget()
Gtk::HBox * hbox_weight_steps = dynamic_cast<Gtk::HBox *>(widg);
std::vector< Gtk::Widget* > childList = hbox_weight_steps->get_children();
Gtk::Entry* entry_widget = dynamic_cast<Gtk::Entry *>(childList[1]);
- entry_widget->set_width_chars(6);
+ entry_widget->set_width_chars(9);
}
}
- if (param->param_key == "only_selected") {
- Gtk::CheckButton *widg_registered =
- Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg));
- widg = dynamic_cast<Gtk::Widget *>(widg_registered);
- }
- if (param->param_key == "ignore_cusp") {
+ if (param->param_key == "only_selected" || param->param_key == "apply_no_weight" || param->param_key == "apply_with_weight") {
Gtk::CheckButton *widg_registered =
Gtk::manage(dynamic_cast<Gtk::CheckButton *>(widg));
widg = dynamic_cast<Gtk::Widget *>(widg_registered);
@@ -143,17 +144,20 @@ Gtk::Widget *LPEBSpline::newWidget()
void LPEBSpline::toDefaultWeight()
{
- changeWeight(DEFAULT_START_POWER);
+ changeWeight(DEFAULT_START_POWER * 100);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change to default weight"));
}
void LPEBSpline::toMakeCusp()
{
changeWeight(NO_POWER);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change to 0 weight"));
}
void LPEBSpline::toWeight()
{
changeWeight(weight);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
}
void LPEBSpline::changeWeight(double weight_ammount)
@@ -161,7 +165,7 @@ void LPEBSpline::changeWeight(double weight_ammount)
SPPath *path = dynamic_cast<SPPath *>(sp_lpe_item);
if(path) {
SPCurve *curve = path->get_curve_for_edit();
- doBSplineFromWidget(curve, weight_ammount);
+ doBSplineFromWidget(curve, weight_ammount/100.0);
gchar *str = sp_svg_write_path(curve->get_pathvector());
path->getRepr()->setAttribute("inkscape:original-d", str);
}
@@ -202,6 +206,18 @@ void sp_bspline_do_effect(SPCurve *curve, double helper_size)
Geom::D2<Geom::SBasis> sbasis_helper;
Geom::CubicBezier const *cubic = NULL;
curve_n->moveto(curve_it1->initialPoint());
+ 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();
+ }
+ }
while (curve_it1 != curve_endit) {
SPCurve *in = new SPCurve();
in->moveto(curve_it1->initialPoint());
@@ -357,6 +373,18 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weight_ammount)
Geom::D2<Geom::SBasis> sbasis_out;
Geom::CubicBezier const *cubic = NULL;
curve_n->moveto(curve_it1->initialPoint());
+ 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();
+ }
+ }
while (curve_it1 != curve_endit) {
SPCurve *in = new SPCurve();
in->moveto(curve_it1->initialPoint());
@@ -365,91 +393,61 @@ void LPEBSpline::doBSplineFromWidget(SPCurve *curve, double weight_ammount)
point_at0 = in->first_segment()->initialPoint();
point_at3 = in->first_segment()->finalPoint();
sbasis_in = in->first_segment()->toSBasis();
- if (!only_selected) {
- if (cubic) {
- if (!ignore_cusp || !Geom::are_near((*cubic)[1], point_at0)) {
+ if (cubic) {
+ if ((apply_no_weight && apply_with_weight) ||
+ (apply_no_weight && Geom::are_near((*cubic)[1], point_at0)) ||
+ (apply_with_weight && !Geom::are_near((*cubic)[1], point_at0)))
+ {
+ if (isNodePointSelected(point_at0) || !only_selected) {
point_at1 = sbasis_in.valueAt(weight_ammount);
if (weight_ammount != NO_POWER) {
point_at1 =
Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP);
}
} else {
- point_at1 = in->first_segment()->initialPoint();
- }
- if (!ignore_cusp || !Geom::are_near((*cubic)[2], point_at3)) {
- point_at2 = sbasis_in.valueAt(1 - weight_ammount);
- if (weight_ammount != NO_POWER) {
- point_at2 =
- Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP);
- }
- } else {
- point_at2 = in->first_segment()->finalPoint();
+ point_at1 = (*cubic)[1];
}
} else {
- if (!ignore_cusp && weight_ammount != NO_POWER) {
- point_at1 = sbasis_in.valueAt(weight_ammount);
- if (weight_ammount != NO_POWER) {
- point_at1 =
- Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP);
- }
+ point_at1 = (*cubic)[1];
+ }
+ if ((apply_no_weight && apply_with_weight) ||
+ (apply_no_weight && Geom::are_near((*cubic)[2], point_at3)) ||
+ (apply_with_weight && !Geom::are_near((*cubic)[2], point_at3)))
+ {
+ if (isNodePointSelected(point_at3) || !only_selected) {
point_at2 = sbasis_in.valueAt(1 - weight_ammount);
if (weight_ammount != NO_POWER) {
point_at2 =
Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP);
}
} else {
- point_at1 = in->first_segment()->initialPoint();
- point_at2 = in->first_segment()->finalPoint();
+ point_at2 = (*cubic)[2];
}
+ } else {
+ point_at2 = (*cubic)[2];
}
} else {
- if (cubic) {
- if (!ignore_cusp || !Geom::are_near((*cubic)[1], point_at0)) {
- if (isNodePointSelected(point_at0)) {
- point_at1 = sbasis_in.valueAt(weight_ammount);
- if (weight_ammount != NO_POWER) {
- point_at1 =
- Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP);
- }
- } else {
- point_at1 = (*cubic)[1];
- }
+ if ((apply_no_weight && apply_with_weight) ||
+ (apply_no_weight && weight_ammount == NO_POWER) ||
+ (apply_with_weight && weight_ammount != NO_POWER))
+ {
+ if (isNodePointSelected(point_at0) || !only_selected) {
+ point_at1 = sbasis_in.valueAt(weight_ammount);
+ point_at1 =
+ Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP);
} else {
point_at1 = in->first_segment()->initialPoint();
}
- if (!ignore_cusp || !Geom::are_near((*cubic)[2], point_at3)) {
- if (isNodePointSelected(point_at3)) {
- point_at2 = sbasis_in.valueAt(1 - weight_ammount);
- if (weight_ammount != NO_POWER) {
- point_at2 =
- Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP);
- }
- } else {
- point_at2 = (*cubic)[2];
- }
+ if (isNodePointSelected(point_at3) || !only_selected) {
+ point_at2 = sbasis_in.valueAt(1 - weight_ammount);
+ point_at2 =
+ Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP);
} else {
point_at2 = in->first_segment()->finalPoint();
}
} else {
- if (!ignore_cusp && weight_ammount != NO_POWER) {
- if (isNodePointSelected(point_at0)) {
- point_at1 = sbasis_in.valueAt(weight_ammount);
- point_at1 =
- Geom::Point(point_at1[X] + HANDLE_CUBIC_GAP, point_at1[Y] + HANDLE_CUBIC_GAP);
- } else {
- point_at1 = in->first_segment()->initialPoint();
- }
- if (isNodePointSelected(point_at3)) {
- point_at2 = sbasis_in.valueAt(weight_ammount);
- point_at2 =
- Geom::Point(point_at2[X] + HANDLE_CUBIC_GAP, point_at2[Y] + HANDLE_CUBIC_GAP);
- } else {
- point_at2 = in->first_segment()->finalPoint();
- }
- } else {
- point_at1 = in->first_segment()->initialPoint();
- point_at2 = in->first_segment()->finalPoint();
- }
+ point_at1 = in->first_segment()->initialPoint();
+ point_at2 = in->first_segment()->finalPoint();
}
}
in->reset();
diff --git a/src/live_effects/lpe-bspline.h b/src/live_effects/lpe-bspline.h
index 033e85cf0..7823f00f0 100644
--- a/src/live_effects/lpe-bspline.h
+++ b/src/live_effects/lpe-bspline.h
@@ -38,7 +38,8 @@ public:
private:
ScalarParam helper_size;
- BoolParam ignore_cusp;
+ BoolParam apply_no_weight;
+ BoolParam apply_with_weight;
BoolParam only_selected;
ScalarParam weight;
diff --git a/src/live_effects/lpe-fill-between-many.cpp b/src/live_effects/lpe-fill-between-many.cpp
index 3e0810cfc..574ec3580 100644
--- a/src/live_effects/lpe-fill-between-many.cpp
+++ b/src/live_effects/lpe-fill-between-many.cpp
@@ -37,7 +37,7 @@ void LPEFillBetweenMany::doEffect (SPCurve * curve)
{
Geom::PathVector res_pathv;
SPItem * firstObj = NULL;
- for (std::vector<PathAndDirection*>::iterator iter = linked_paths._vector.begin(); iter != linked_paths._vector.end(); iter++) {
+ 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;
diff --git a/src/live_effects/lpe-fillet-chamfer.cpp b/src/live_effects/lpe-fillet-chamfer.cpp
index a9a96864a..209805da3 100644
--- a/src/live_effects/lpe-fillet-chamfer.cpp
+++ b/src/live_effects/lpe-fillet-chamfer.cpp
@@ -76,12 +76,15 @@ LPEFilletChamfer::LPEFilletChamfer(LivePathEffectObject *lpeobject) :
radius.param_set_range(0., infinity());
radius.param_set_increments(1, 1);
radius.param_set_digits(4);
+ radius.param_overwrite_widget(true);
chamfer_steps.param_set_range(1, 999);
chamfer_steps.param_set_increments(1, 1);
chamfer_steps.param_set_digits(0);
+ chamfer_steps.param_overwrite_widget(true);
helper_size.param_set_range(0, infinity());
helper_size.param_set_increments(5, 5);
helper_size.param_set_digits(0);
+ helper_size.param_overwrite_widget(true);
fillet_chamfer_values.set_chamfer_steps(3);
}
@@ -226,18 +229,21 @@ void LPEFilletChamfer::updateFillet()
}
Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
doUpdateFillet(path_from_piecewise(pwd2, tolerance), power);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
}
void LPEFilletChamfer::fillet()
{
Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
doChangeType(path_from_piecewise(pwd2, tolerance), 1);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Convert to fillet"));
}
void LPEFilletChamfer::inverseFillet()
{
Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
doChangeType(path_from_piecewise(pwd2, tolerance), 2);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Convert to inverse fillet"));
}
void LPEFilletChamfer::chamferSubdivisions()
@@ -245,6 +251,7 @@ void LPEFilletChamfer::chamferSubdivisions()
fillet_chamfer_values.set_chamfer_steps(chamfer_steps);
Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
doChangeType(path_from_piecewise(pwd2, tolerance), chamfer_steps + 5000);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
}
void LPEFilletChamfer::chamfer()
@@ -252,6 +259,7 @@ void LPEFilletChamfer::chamfer()
fillet_chamfer_values.set_chamfer_steps(chamfer_steps);
Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
doChangeType(path_from_piecewise(pwd2, tolerance), chamfer_steps + 3000);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Convert to chamfer"));
}
void LPEFilletChamfer::inverseChamfer()
@@ -259,6 +267,7 @@ void LPEFilletChamfer::inverseChamfer()
fillet_chamfer_values.set_chamfer_steps(chamfer_steps);
Piecewise<D2<SBasis> > const &pwd2 = fillet_chamfer_values.get_pwd2();
doChangeType(path_from_piecewise(pwd2, tolerance), chamfer_steps + 4000);
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Convert to inverse fillet"));
}
void LPEFilletChamfer::refreshKnots()
@@ -270,6 +279,7 @@ void LPEFilletChamfer::refreshKnots()
tools_switch(desktop, TOOLS_SELECT);
tools_switch(desktop, TOOLS_NODES);
}
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Knots and helper paths refreshed"));
}
void LPEFilletChamfer::doUpdateFillet(Geom::PathVector const &original_pathv, double power)
diff --git a/src/live_effects/lpe-interpolate_points.cpp b/src/live_effects/lpe-interpolate_points.cpp
index 4ac139752..cf70832ee 100644
--- a/src/live_effects/lpe-interpolate_points.cpp
+++ b/src/live_effects/lpe-interpolate_points.cpp
@@ -52,8 +52,11 @@ Geom::PathVector
LPEInterpolatePoints::doEffect_path (Geom::PathVector const & path_in)
{
Geom::PathVector path_out;
-
+#if __cplusplus <= 199711L
std::auto_ptr<Geom::Interpolate::Interpolator> interpolator( Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())) );
+#else
+ std::unique_ptr<Geom::Interpolate::Interpolator> interpolator( Geom::Interpolate::Interpolator::create(static_cast<Geom::Interpolate::InterpolatorType>(interpolator_type.get_value())) );
+#endif
for(Geom::PathVector::const_iterator path_it = path_in.begin(); path_it != path_in.end(); ++path_it) {
if (path_it->empty())
diff --git a/src/live_effects/lpe-knot.cpp b/src/live_effects/lpe-knot.cpp
index c3000fe0d..bf84e645d 100644
--- a/src/live_effects/lpe-knot.cpp
+++ b/src/live_effects/lpe-knot.cpp
@@ -507,7 +507,7 @@ static void
collectPathsAndWidths (SPLPEItem const *lpeitem, Geom::PathVector &paths, std::vector<double> &stroke_widths){
if (SP_IS_GROUP(lpeitem)) {
std::vector<SPItem*> item_list = sp_item_group_item_list(SP_GROUP(lpeitem));
- for ( std::vector<SPItem*>::const_iterator iter = item_list.begin(); iter != item_list.end(); iter++) {
+ for ( std::vector<SPItem*>::const_iterator iter = item_list.begin(); iter != item_list.end(); ++iter) {
SPObject *subitem = *iter;
if (SP_IS_LPE_ITEM(subitem)) {
collectPathsAndWidths(SP_LPE_ITEM(subitem), paths, stroke_widths);
diff --git a/src/live_effects/lpe-powerstroke.cpp b/src/live_effects/lpe-powerstroke.cpp
index f90d67d4e..03102a84a 100644
--- a/src/live_effects/lpe-powerstroke.cpp
+++ b/src/live_effects/lpe-powerstroke.cpp
@@ -102,12 +102,15 @@ static Circle touching_circle( D2<SBasis> const &curve, double t, double tol=0.0
{
//Piecewise<SBasis> k = curvature(curve, tol);
D2<SBasis> dM=derivative(curve);
- if ( are_near(L2sq(dM(t)),0.) ) {
+ if ( are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) ) {
dM=derivative(dM);
}
- if ( are_near(L2sq(dM(t)),0.) ) { // try second time
+ if ( are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) ) { // try second time
dM=derivative(dM);
}
+ if ( are_near(L2sq(dM(t)),0.) && (dM[0].size() > 1) && (dM[1].size() > 1) ) { // admit defeat
+ return Geom::Circle(Geom::Point(0., 0.), 0.);
+ }
Piecewise<D2<SBasis> > unitv = unitVector(dM,tol);
Piecewise<SBasis> dMlength = dot(Piecewise<D2<SBasis> >(dM),unitv);
Piecewise<SBasis> k = cross(derivative(unitv),unitv);
diff --git a/src/live_effects/lpe-roughen.cpp b/src/live_effects/lpe-roughen.cpp
index 33ffd96d6..b4ee54f1a 100644
--- a/src/live_effects/lpe-roughen.cpp
+++ b/src/live_effects/lpe-roughen.cpp
@@ -18,7 +18,9 @@
#include "live_effects/lpe-roughen.h"
#include "display/curve.h"
#include "live_effects/parameter/parameter.h"
+#include <boost/functional/hash.hpp>
#include "helper/geom.h"
+#include "sp-item-group.h"
#include <glibmm/i18n.h>
#include <cmath>
@@ -32,13 +34,22 @@ static const Util::EnumData<DivisionMethod> DivisionMethodData[DM_END] = {
static const Util::EnumDataConverter<DivisionMethod>
DMConverter(DivisionMethodData, DM_END);
+static const Util::EnumData<HandlesMethod> HandlesMethodData[HM_END] = {
+ { HM_ALONG_NODES, N_("Along nodes"), "along" },
+ { HM_RAND, N_("Rand"), "rand" },
+ { HM_RETRACT, N_("Retract"), "retract" },
+ { HM_SMOOTH, N_("Smooth"), "smooth" }
+};
+static const Util::EnumDataConverter<HandlesMethod>
+HMConverter(HandlesMethodData, HM_END);
+
LPERoughen::LPERoughen(LivePathEffectObject *lpeobject)
: Effect(lpeobject),
// initialise your parameters here:
method(_("Method"), _("Division method"), "method", DMConverter, &wr,
this, DM_SEGMENTS),
max_segment_size(_("Max. segment size"), _("Max. segment size"),
- "max_segment_size", &wr, this, 10.),
+ "max_segment_size", &wr, this, 10),
segments(_("Number of segments"), _("Number of segments"), "segments",
&wr, this, 2),
displace_x(_("Max. displacement in X"), _("Max. displacement in X"),
@@ -47,10 +58,14 @@ LPERoughen::LPERoughen(LivePathEffectObject *lpeobject)
"displace_y", &wr, this, 10.),
global_randomize(_("Global randomize"), _("Global randomize"),
"global_randomize", &wr, this, 1.),
+ handles(_("Handles"), _("Handles options"), "handles", HMConverter, &wr,
+ this, HM_ALONG_NODES),
shift_nodes(_("Shift nodes"), _("Shift nodes"), "shift_nodes", &wr, this,
true),
- shift_node_handles(_("Shift node handles"), _("Shift node handles"),
- "shift_node_handles", &wr, this, true)
+ fixed_displacement(_("Fixed displacement"), _("Fixed displacement, 1/3 of segment length"),
+ "fixed_displacement", &wr, this, false),
+ spray_tool_friendly(_("Spray Tool friendly"), _("For use with spray tool in copy mode"),
+ "spray_tool_friendly", &wr, this, false)
{
registerParameter(&method);
registerParameter(&max_segment_size);
@@ -58,8 +73,10 @@ LPERoughen::LPERoughen(LivePathEffectObject *lpeobject)
registerParameter(&displace_x);
registerParameter(&displace_y);
registerParameter(&global_randomize);
+ registerParameter(&handles);
registerParameter(&shift_nodes);
- registerParameter(&shift_node_handles);
+ registerParameter(&fixed_displacement);
+ registerParameter(&spray_tool_friendly);
displace_x.param_set_range(0., Geom::infinity());
displace_y.param_set_range(0., Geom::infinity());
global_randomize.param_set_range(0., Geom::infinity());
@@ -69,12 +86,18 @@ LPERoughen::LPERoughen(LivePathEffectObject *lpeobject)
segments.param_set_range(1, Geom::infinity());
segments.param_set_increments(1, 1);
segments.param_set_digits(0);
+ seed = 0;
}
LPERoughen::~LPERoughen() {}
void LPERoughen::doBeforeEffect(SPLPEItem const *lpeitem)
{
+ if(spray_tool_friendly && seed == 0 && SP_OBJECT(lpeitem)->getId()){
+ std::string id_item(SP_OBJECT(lpeitem)->getId());
+ long seed = static_cast<long>(boost::hash_value(id_item));
+ global_randomize.param_set_value(global_randomize.get_value(), seed);
+ }
displace_x.resetRandomizer();
displace_y.resetRandomizer();
global_randomize.resetRandomizer();
@@ -122,6 +145,15 @@ Gtk::Widget *LPERoughen::newWidget()
vbox->pack_start(*Gtk::manage(new Gtk::HSeparator()),
Gtk::PACK_EXPAND_WIDGET);
}
+ if (param->param_key == "handles") {
+ Gtk::Label *options = Gtk::manage(new Gtk::Label(
+ Glib::ustring(_("<b>Options</b> Modify options to rough")),
+ Gtk::ALIGN_START));
+ options->set_use_markup(true);
+ vbox->pack_start(*options, 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);
@@ -133,7 +165,6 @@ Gtk::Widget *LPERoughen::newWidget()
}
}
}
-
++it;
}
return dynamic_cast<Gtk::Widget *>(vbox);
@@ -147,19 +178,25 @@ double LPERoughen::sign(double random_number)
return random_number;
}
-Geom::Point LPERoughen::randomize()
+Geom::Point LPERoughen::randomize(double max_lenght, bool is_node)
{
- double displace_x_parsed = displace_x * global_randomize;
- double displace_y_parsed = displace_y * global_randomize;
-
+ double factor = 1.0/3.0;
+ if(is_node){
+ factor = 1.0;
+ }
+ double displace_x_parsed = displace_x * global_randomize * factor;
+ double displace_y_parsed = displace_y * global_randomize * factor;
Geom::Point output = Geom::Point(sign(displace_x_parsed), sign(displace_y_parsed));
+ if( fixed_displacement ){
+ Geom::Ray ray(Geom::Point(0,0),output);
+ output = Geom::Point::polar(ray.angle(), max_lenght);
+ }
return output;
}
void LPERoughen::doEffect(SPCurve *curve)
{
- Geom::PathVector const original_pathv =
- pathv_to_linear_and_cubic_beziers(curve->get_pathvector());
+ 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) {
@@ -170,41 +207,30 @@ void LPERoughen::doEffect(SPCurve *curve)
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 prev(0, 0);
+ Geom::Point last_move(0, 0);
+ nCurve->moveto(curve_it1->initialPoint());
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();
- }
- }
- Geom::Point initialMove(0, 0);
- if (shift_nodes) {
- initialMove = randomize();
+ 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();
+ }
}
- Geom::Point initialPoint = curve_it1->initialPoint() + initialMove;
- nCurve->moveto(initialPoint);
- Geom::Point point0(0, 0);
- Geom::Point point1(0, 0);
- Geom::Point point2(0, 0);
- Geom::Point point3(0, 0);
- bool first = true;
while (curve_it1 != curve_endit) {
Geom::CubicBezier const *cubic = NULL;
- point0 = curve_it1->initialPoint();
- point1 = curve_it1->initialPoint();
- point2 = curve_it1->finalPoint();
- point3 = curve_it1->finalPoint();
cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
if (cubic) {
- point1 = (*cubic)[1];
- if (shift_nodes && first) {
- point1 = (*cubic)[1] + initialMove;
- }
- point2 = (*cubic)[2];
- nCurve->curveto(point1, point2, point3);
+ nCurve->curveto((*cubic)[1] + last_move, (*cubic)[2], curve_it1->finalPoint());
} else {
- nCurve->lineto(point3);
+ nCurve->lineto(curve_it1->finalPoint());
}
+ last_move = Geom::Point(0, 0);
double length = curve_it1->length(0.001);
std::size_t splits = 0;
if (method == DM_SEGMENTS) {
@@ -212,15 +238,21 @@ void LPERoughen::doEffect(SPCurve *curve)
} else {
splits = ceil(length / max_segment_size);
}
- for (unsigned int t = splits; t >= 1; t--) {
- if (t == 1 && splits != 1) {
+ Geom::Curve const * original = nCurve->last_segment()->duplicate() ;
+ for (unsigned int t = 1; t <= splits; t++) {
+ if(t == splits && splits != 1){
continue;
}
- const SPCurve *tmp;
+ SPCurve const * tmp;
if (splits == 1) {
- tmp = jitter(nCurve->last_segment());
+ tmp = jitter(nCurve->last_segment(), prev, last_move);
} else {
- tmp = addNodesAndJitter(nCurve->last_segment(), 1. / t);
+ bool last = false;
+ if(t == splits-1){
+ last = true;
+ }
+ double time = Geom::nearest_time(original->pointAt((1. / (double)splits) * t), *nCurve->last_segment());
+ tmp = addNodesAndJitter(nCurve->last_segment(), prev, last_move, time, last);
}
if (nCurve->get_segment_count() > 1) {
nCurve->backspace();
@@ -231,12 +263,44 @@ void LPERoughen::doEffect(SPCurve *curve)
delete tmp;
}
++curve_it1;
- if(curve_it2 != curve_endit) {
- ++curve_it2;
- }
- first = false;
+ ++curve_it2;
}
if (path_it->closed()) {
+ if(handles == HM_SMOOTH && curve_it1 == curve_endit){
+ SPCurve *out = new SPCurve();
+ nCurve = nCurve->create_reverse();
+ Geom::CubicBezier const *cubic_start = dynamic_cast<Geom::CubicBezier const *>(nCurve->first_segment());
+ Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(nCurve->last_segment());
+ Geom::Point oposite = nCurve->first_segment()->pointAt(1.0/3.0);
+ if(cubic_start){
+ Geom::Ray ray((*cubic_start)[1], (*cubic_start)[0]);
+ double dist = Geom::distance((*cubic_start)[1], (*cubic_start)[0]);
+ oposite = Geom::Point::polar(ray.angle(),dist) + (*cubic_start)[0];
+ }
+ if(cubic){
+ out->moveto((*cubic)[0]);
+ out->curveto((*cubic)[1], oposite, (*cubic)[3]);
+ } else {
+ out->moveto(nCurve->last_segment()->initialPoint());
+ out->curveto(nCurve->last_segment()->initialPoint(), oposite, nCurve->last_segment()->finalPoint());
+ }
+ nCurve->backspace();
+ nCurve->append_continuous(out, 0.001);
+ nCurve = nCurve->create_reverse();
+ }
+ if(handles == HM_ALONG_NODES && curve_it1 == curve_endit){
+ SPCurve *out = new SPCurve();
+ nCurve = nCurve->create_reverse();
+ Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(nCurve->last_segment());
+ if(cubic){
+ out->moveto((*cubic)[0]);
+ out->curveto((*cubic)[1], (*cubic)[2] - ((*cubic)[3] - nCurve->first_segment()->initialPoint()) , (*cubic)[3]);
+ nCurve->backspace();
+ nCurve->append_continuous(out, 0.001);
+ }
+ nCurve = nCurve->create_reverse();
+ }
+ nCurve->move_endpoints(nCurve->last_segment()->finalPoint(), nCurve->last_segment()->finalPoint());
nCurve->closepath_current();
}
curve->append(nCurve, false);
@@ -245,77 +309,211 @@ void LPERoughen::doEffect(SPCurve *curve)
}
}
-SPCurve *LPERoughen::addNodesAndJitter(const Geom::Curve *A, double t)
+SPCurve const * LPERoughen::addNodesAndJitter(Geom::Curve const * A, Geom::Point &prev, Geom::Point &last_move, double t, bool last)
{
SPCurve *out = new SPCurve();
Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*A);
- Geom::Point point1(0, 0);
- Geom::Point point2(0, 0);
- Geom::Point point3(0, 0);
+ double max_lenght = Geom::distance(A->initialPoint(),A->pointAt(t)) / 3.0;
+ Geom::Point point_a1(0, 0);
+ Geom::Point point_a2(0, 0);
+ Geom::Point point_a3(0, 0);
Geom::Point point_b1(0, 0);
Geom::Point point_b2(0, 0);
Geom::Point point_b3(0, 0);
if (shift_nodes) {
- point3 = randomize();
- point_b3 = randomize();
+ point_a3 = randomize(max_lenght, true);
+ if(last){
+ point_b3 = randomize(max_lenght, true);
+ }
}
- if (shift_node_handles) {
- point1 = randomize();
- point2 = randomize();
- point_b1 = randomize();
- point_b2 = randomize();
+ if (handles == HM_RAND || handles == HM_SMOOTH) {
+ point_a1 = randomize(max_lenght);
+ point_a2 = randomize(max_lenght);
+ point_b1 = randomize(max_lenght);
+ if(last){
+ point_b2 = randomize(max_lenght);
+ }
} else {
- point2 = point3;
- point_b1 = point3;
- point_b2 = point_b3;
+ point_a2 = point_a3;
+ point_b1 = point_a3;
+ if(last){
+ point_b2 = point_b3;
+ }
}
- if (cubic) {
- std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t);
- std::vector<Geom::Point> seg1 = div.first.controlPoints(),
- seg2 = div.second.controlPoints();
- out->moveto(seg1[0]);
- out->curveto(seg1[1] + point1, seg1[2] + point2, seg1[3] + point3);
- out->curveto(seg2[1] + point_b1, seg2[2], seg2[3]);
- } else if (shift_node_handles) {
- out->moveto(A->initialPoint());
- out->curveto(A->pointAt(t / 3) + point1, A->pointAt((t / 3) * 2) + point2,
- A->pointAt(t) + point3);
- out->curveto(A->pointAt(t + (t / 3)) + point_b1, A->pointAt(t + ((t / 3) * 2)),
- A->finalPoint());
- } else {
+ if(handles == HM_SMOOTH){
+ if(cubic) {
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t);
+ std::vector<Geom::Point> seg1 = div.first.controlPoints(),
+ seg2 = div.second.controlPoints();
+ Geom::Ray ray(seg1[3] + point_a3, seg2[1] + point_a3);
+ double lenght = max_lenght;
+ if(!fixed_displacement ){
+ lenght = Geom::distance(seg1[3] + point_a3, seg2[1] + point_a3);
+ }
+ point_b1 = seg1[3] + point_a3 + Geom::Point::polar(ray.angle() , lenght);
+ point_b2 = seg2[2];
+ point_b3 = seg2[3] + point_b3;
+ point_a3 = seg1[3] + point_a3;
+ ray.setPoints(prev,A->initialPoint());
+ point_a1 = A->initialPoint() + Geom::Point::polar(ray.angle(), max_lenght);
+ if(prev == Geom::Point(0,0)){
+ point_a1 = randomize(max_lenght);
+ }
+ if(last){
+ Geom::Path b2(point_b3);
+ b2.appendNew<Geom::LineSegment>(point_a3);
+ lenght = max_lenght;
+ ray.setPoints(point_b3, point_b2);
+ if(!fixed_displacement ){
+ lenght = Geom::distance(b2.pointAt(1.0/3.0), point_b3);
+ }
+ point_b2 = point_b3 + Geom::Point::polar(ray.angle() , lenght);
+ }
+ ray.setPoints(point_b1, point_a3);
+ point_a2 = point_a3 + Geom::Point::polar(ray.angle(), max_lenght);
+ if(last){
+ prev = point_b2;
+ } else {
+ prev = point_a2;
+ }
+ out->moveto(seg1[0]);
+ out->curveto(point_a1,point_a2,point_a3);
+ out->curveto(point_b1, point_b2, point_b3);
+ } else {
+ Geom::Ray ray(A->pointAt(t) + point_a3, A->pointAt(t + (t / 3)));
+ double lenght = max_lenght;
+ if(!fixed_displacement ){
+ lenght = Geom::distance(A->pointAt(t) + point_a3, A->pointAt(t + (t / 3)));
+ }
+ point_b1 = A->pointAt(t) + point_a3 + Geom::Point::polar(ray.angle() , lenght);
+ point_b2 = A->pointAt(t +((t / 3) * 2));
+ point_b3 = A->finalPoint() + point_b3;
+ point_a3 = A->pointAt(t) + point_a3;
+ ray.setPoints(prev,A->initialPoint());
+ point_a1 = A->initialPoint() + Geom::Point::polar(ray.angle(), max_lenght);
+ if(prev == Geom::Point(0,0)){
+ point_a1 = randomize(max_lenght);
+ }
+ if(last){
+ Geom::Path b2(point_b3);
+ b2.appendNew<Geom::LineSegment>(point_a3);
+ lenght = max_lenght;
+ ray.setPoints(point_b3, point_b2);
+ if(!fixed_displacement ){
+ lenght = Geom::distance(b2.pointAt(1.0/3.0), point_b3);
+ }
+ point_b2 = point_b3 + Geom::Point::polar(ray.angle() , lenght);
+ }
+ ray.setPoints(point_b1, point_a3);
+ point_a2 = point_a3 + Geom::Point::polar(ray.angle(), max_lenght);
+ if(last){
+ prev = point_b2;
+ } else {
+ prev = point_a2;
+ }
+ out->moveto(A->initialPoint());
+ out->curveto(point_a1,point_a2,point_a3);
+ out->curveto(point_b1, point_b2, point_b3);
+ }
+ } else if(handles == HM_RETRACT){
out->moveto(A->initialPoint());
- out->lineto(A->pointAt(t) + point3);
- out->lineto(A->finalPoint());
+ out->lineto(A->pointAt(t) + point_a3);
+ if(cubic && !last){
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t);
+ std::vector<Geom::Point> seg2 = div.second.controlPoints();
+ out->curveto(seg2[1], seg2[2], seg2[3]);
+ } else {
+ out->lineto(A->finalPoint() + point_b3);
+ }
+ } else if(handles == HM_ALONG_NODES){
+ if (cubic) {
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t);
+ std::vector<Geom::Point> seg1 = div.first.controlPoints(),
+ seg2 = div.second.controlPoints();
+ out->moveto(seg1[0]);
+ out->curveto(seg1[1] + last_move, seg1[2] + point_a3, seg1[3] + point_a3);
+ last_move = point_a3;
+ if(last){
+ last_move = point_b3;
+ }
+ out->curveto(seg2[1] + point_a3, seg2[2] + point_b3, seg2[3] + point_b3);
+ } else {
+ out->moveto(A->initialPoint());
+ out->lineto(A->pointAt(t) + point_a3);
+ out->lineto(A->finalPoint() + point_b3);
+ }
+ } else if(handles == HM_RAND) {
+ if (cubic) {
+ std::pair<Geom::CubicBezier, Geom::CubicBezier> div = cubic->subdivide(t);
+ std::vector<Geom::Point> seg1 = div.first.controlPoints(),
+ seg2 = div.second.controlPoints();
+ out->moveto(seg1[0]);
+ out->curveto(seg1[1] + point_a1, seg1[2] + point_a2 + point_a3, seg1[3] + point_a3);
+ out->curveto(seg2[1] + point_a3 + point_b1, seg2[2] + point_b2 + point_b3, seg2[3] + point_b3);
+ } else {
+ out->moveto(A->initialPoint());
+ out->lineto(A->pointAt(t) + point_a3);
+ out->lineto(A->finalPoint() + point_b3);
+ }
}
return out;
}
-SPCurve *LPERoughen::jitter(const Geom::Curve *A)
+SPCurve *LPERoughen::jitter(Geom::Curve const * A, Geom::Point &prev, Geom::Point &last_move)
{
SPCurve *out = new SPCurve();
Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*A);
- Geom::Point point1(0, 0);
- Geom::Point point2(0, 0);
- Geom::Point point3(0, 0);
+ double max_lenght = Geom::distance(A->initialPoint(),A->finalPoint()) / 3.0;
+ Geom::Point point_a1(0, 0);
+ Geom::Point point_a2(0, 0);
+ Geom::Point point_a3(0, 0);
if (shift_nodes) {
- point3 = randomize();
+ point_a3 = randomize(max_lenght, true);
}
- if (shift_node_handles) {
- point1 = randomize();
- point2 = randomize();
- } else {
- point2 = point3;
+ if (handles == HM_RAND || handles == HM_SMOOTH) {
+ point_a1 = randomize(max_lenght);
+ point_a2 = randomize(max_lenght);
}
- if (cubic) {
- out->moveto((*cubic)[0]);
- out->curveto((*cubic)[1] + point1, (*cubic)[2] + point2, (*cubic)[3] + point3);
- } else if (shift_node_handles) {
+ if(handles == HM_SMOOTH) {
+ if (cubic) {
+ Geom::Ray ray(prev,A->initialPoint());
+ point_a1 = Geom::Point::polar(ray.angle(), max_lenght);
+ if(prev == Geom::Point(0,0)){
+ point_a1 = A->pointAt(1.0/3.0) + randomize(max_lenght);
+ }
+ ray.setPoints((*cubic)[3] + point_a3, (*cubic)[2] + point_a3);
+ point_a2 = randomize(max_lenght, ray.angle());
+ prev = (*cubic)[2] + point_a2;
+ out->moveto((*cubic)[0]);
+ out->curveto((*cubic)[0] + point_a1, (*cubic)[2] + point_a2 + point_a3, (*cubic)[3] + point_a3);
+ } else {
+ Geom::Ray ray(prev,A->initialPoint());
+ point_a1 = Geom::Point::polar(ray.angle(), max_lenght);
+ if(prev==Geom::Point(0,0)){
+ point_a1 = A->pointAt(1.0/3.0) + randomize(max_lenght);
+ }
+ ray.setPoints(A->finalPoint() + point_a3, A->pointAt((1.0/3.0) * 2) + point_a3);
+ point_a2 = randomize(max_lenght, ray.angle());
+ prev = A->pointAt((1.0/3.0) * 2) + point_a2 + point_a3;
+ out->moveto(A->initialPoint());
+ out->curveto(A->initialPoint() + point_a1, A->pointAt((1.0/3.0) * 2) + point_a2 + point_a3, A->finalPoint() + point_a3);
+ }
+ } else if(handles == HM_RETRACT){
out->moveto(A->initialPoint());
- out->curveto(A->pointAt(0.3333) + point1, A->pointAt(0.6666) + point2,
- A->finalPoint() + point3);
- } else {
+ out->lineto(A->finalPoint() + point_a3);
+ } else if (handles == HM_ALONG_NODES) {
+ if(cubic){
+ out->moveto((*cubic)[0]);
+ out->curveto((*cubic)[1] + last_move, (*cubic)[2] + point_a3, (*cubic)[3] + point_a3);
+ last_move = point_a3;
+ } else {
+ out->moveto(A->initialPoint());
+ out->lineto(A->finalPoint() + point_a3);
+ }
+ } else if (handles == HM_RAND) {
out->moveto(A->initialPoint());
- out->lineto(A->finalPoint() + point3);
+ out->curveto(A->pointAt(0.3333) + point_a1, A->pointAt(0.6666) + point_a2 + point_a3,
+ A->finalPoint() + point_a3);
}
return out;
}
diff --git a/src/live_effects/lpe-roughen.h b/src/live_effects/lpe-roughen.h
index 2b285cd40..44a723c89 100644
--- a/src/live_effects/lpe-roughen.h
+++ b/src/live_effects/lpe-roughen.h
@@ -28,6 +28,15 @@ enum DivisionMethod {
DM_END
};
+enum HandlesMethod {
+ HM_ALONG_NODES,
+ HM_RAND,
+ HM_RETRACT,
+ HM_SMOOTH,
+ HM_END
+};
+
+
class LPERoughen : public Effect {
public:
@@ -36,10 +45,10 @@ public:
virtual void doEffect(SPCurve *curve);
virtual double sign(double randNumber);
- virtual Geom::Point randomize();
+ virtual Geom::Point randomize(double max_lenght, bool is_node = false);
virtual void doBeforeEffect(SPLPEItem const * lpeitem);
- virtual SPCurve *addNodesAndJitter(const Geom::Curve *A, double t);
- virtual SPCurve *jitter(const Geom::Curve *A);
+ virtual SPCurve const * addNodesAndJitter(Geom::Curve const * A, Geom::Point &prev, Geom::Point &last_move, double t, bool last);
+ virtual SPCurve *jitter(Geom::Curve const * A, Geom::Point &prev, Geom::Point &last_move);
virtual Geom::Point tPoint(Geom::Point A, Geom::Point B, double t = 0.5);
virtual Gtk::Widget *newWidget();
@@ -50,9 +59,11 @@ private:
RandomParam displace_x;
RandomParam displace_y;
RandomParam global_randomize;
+ EnumParam<HandlesMethod> handles;
BoolParam shift_nodes;
- BoolParam shift_node_handles;
-
+ BoolParam fixed_displacement;
+ BoolParam spray_tool_friendly;
+ long seed;
LPERoughen(const LPERoughen &);
LPERoughen &operator=(const LPERoughen &);
diff --git a/src/live_effects/lpe-simplify.cpp b/src/live_effects/lpe-simplify.cpp
index f6842a030..a919756df 100644
--- a/src/live_effects/lpe-simplify.cpp
+++ b/src/live_effects/lpe-simplify.cpp
@@ -28,8 +28,8 @@ namespace LivePathEffect {
LPESimplify::LPESimplify(LivePathEffectObject *lpeobject)
: Effect(lpeobject),
steps(_("Steps:"),_("Change number of simplify steps "), "steps", &wr, this,1),
- threshold(_("Roughly threshold:"), _("Roughly threshold:"), "threshold", &wr, this, 0.003),
- smooth_angles(_("Smooth angles:"), _("Max degree difference on handles to preform a smooth"), "smooth_angles", &wr, this, 20.),
+ threshold(_("Roughly threshold:"), _("Roughly threshold:"), "threshold", &wr, this, 0.002),
+ smooth_angles(_("Smooth angles:"), _("Max degree difference on handles to perform a smooth"), "smooth_angles", &wr, this, 0.),
helper_size(_("Helper size:"), _("Helper size"), "helper_size", &wr, this, 5),
simplify_individual_paths(_("Paths separately"), _("Simplifying paths (separately)"), "simplify_individual_paths", &wr, this, false,
"", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")),
@@ -51,7 +51,7 @@ LPESimplify::LPESimplify(LivePathEffectObject *lpeobject)
steps.param_set_increments(1, 1);
steps.param_set_digits(0);
- smooth_angles.param_set_range(0.0, 365.0);
+ smooth_angles.param_set_range(0.0, 360.0);
smooth_angles.param_set_increments(10, 10);
smooth_angles.param_set_digits(2);
@@ -138,6 +138,7 @@ LPESimplify::doEffect(SPCurve *curve)
if(simplify_individual_paths) {
size = Geom::L2(Geom::bounds_fast(original_pathv)->dimensions());
}
+ size /= sp_lpe_item->i2doc_affine().descrim();
for (int unsigned i = 0; i < steps; i++) {
if ( simplify_just_coalesce ) {
pathliv->Coalesce(threshold * size);
@@ -198,13 +199,15 @@ LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result)
Geom::Point point_at2 = curve_it1->finalPoint();
Geom::Point point_at3 = curve_it1->finalPoint();
Geom::Point point_at4 = curve_it1->finalPoint();
+
+ if(start == Geom::Point(0,0)) {
+ start = point_at1;
+ }
+
if (cubic) {
point_at1 = (*cubic)[1];
point_at2 = (*cubic)[2];
}
- if(start == Geom::Point(0,0)) {
- start = point_at1;
- }
if(path_it->closed() && curve_it2 == curve_endit) {
point_at4 = start;
@@ -219,11 +222,11 @@ LPESimplify::generateHelperPathAndSmooth(Geom::PathVector &result)
Geom::Ray ray2(point_at3, point_at4);
double angle1 = Geom::rad_to_deg(ray1.angle());
double angle2 = Geom::rad_to_deg(ray2.angle());
- if((smooth_angles >= angle2 - angle1) && !are_near(point_at4,point_at3) && !are_near(point_at2,point_at3)) {
+ if((smooth_angles >= std::abs(angle2 - angle1)) && !are_near(point_at4,point_at3) && !are_near(point_at2,point_at3)) {
double dist = Geom::distance(point_at2,point_at3);
Geom::Angle angleFixed = ray2.angle();
angleFixed -= Geom::Angle::from_degrees(180.0);
- point_at2 = Geom::Point::polar(angleFixed,dist) + point_at3;
+ point_at2 = Geom::Point::polar(angleFixed, dist) + point_at3;
}
nCurve->curveto(point_at1, point_at2, curve_it1->finalPoint());
cubic = dynamic_cast<Geom::CubicBezier const *>(nCurve->last_segment());
diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp
index ef616f802..f2ddd4929 100644
--- a/src/live_effects/lpe-taperstroke.cpp
+++ b/src/live_effects/lpe-taperstroke.cpp
@@ -408,9 +408,8 @@ Piecewise<D2<SBasis> > stretch_along(Piecewise<D2<SBasis> > pwd2_in, Geom::Path
n = force_continuity(remove_short_cuts(n,.1));
int nbCopies = 0;
- double scaling = 1;
+ double scaling = (uskeleton.domain().extent() - toffset)/pattBndsX->extent();
nbCopies = 1;
- scaling = (uskeleton.domain().extent() - toffset)/pattBndsX->extent();
double pattWidth = pattBndsX->extent() * scaling;
diff --git a/src/live_effects/lpe-transform_2pts.cpp b/src/live_effects/lpe-transform_2pts.cpp
new file mode 100644
index 000000000..f2b756567
--- /dev/null
+++ b/src/live_effects/lpe-transform_2pts.cpp
@@ -0,0 +1,462 @@
+/** \file
+ * LPE "Transform through 2 points" implementation
+ */
+
+/*
+ * Authors:
+ * Jabier Arraiza Cenoz<jabier.arraiza@marker.es>
+ *
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include <gtkmm.h>
+
+#include "live_effects/lpe-transform_2pts.h"
+#include "display/curve.h"
+#include <2geom/transforms.h>
+#include <2geom/pathvector.h>
+#include "sp-path.h"
+#include "ui/icon-names.h"
+#include "svg/svg.h"
+#include "verbs.h"
+// TODO due to internal breakage in glibmm headers, this must be last:
+#include <glibmm/i18n.h>
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+LPETransform2Pts::LPETransform2Pts(LivePathEffectObject *lpeobject) :
+ Effect(lpeobject),
+ elastic(_("Elastic"), _("Elastic transform mode"), "elastic", &wr, this, false,"", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")),
+ from_original_width(_("From original width"), _("From original width"), "from_original_width", &wr, this, false,"", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")),
+ lock_lenght(_("Lock length"), _("Lock length to current distance"), "lock_lenght", &wr, this, false,"", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")),
+ lock_angle(_("Lock angle"), _("Lock angle"), "lock_angle", &wr, this, false,"", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")),
+ flip_horizontal(_("Flip horizontal"), _("Flip horizontal"), "flip_horizontal", &wr, this, false,"", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")),
+ flip_vertical(_("Flip vertical"), _("Flip vertical"), "flip_vertical", &wr, this, false,"", INKSCAPE_ICON("on"), INKSCAPE_ICON("off")),
+ start(_("Start"), _("Start point"), "start", &wr, this, "Start point"),
+ end(_("End"), _("End point"), "end", &wr, this, "End point"),
+ strech(_("Stretch"), _("Stretch the result"), "strech", &wr, this, 1),
+ offset(_("Offset"), _("Offset from knots"), "offset", &wr, this, 0),
+ first_knot(_("First Knot"), _("First Knot"), "first_knot", &wr, this, 1),
+ last_knot(_("Last Knot"), _("Last Knot"), "last_knot", &wr, this, 1),
+ helper_size(_("Helper size:"), _("Rotation helper size"), "helper_size", &wr, this, 3),
+ from_original_width_toggler(false),
+ point_a(Geom::Point()),
+ point_b(Geom::Point()),
+ pathvector(),
+ append_path(false),
+ previous_angle(Geom::deg_to_rad(0)),
+ previous_start(Geom::Point()),
+ previous_lenght(-1)
+{
+
+ registerParameter(&first_knot);
+ registerParameter(&last_knot);
+ registerParameter(&helper_size);
+ registerParameter(&strech);
+ registerParameter(&offset);
+ registerParameter(&start);
+ registerParameter(&end);
+ registerParameter(&elastic);
+ registerParameter(&from_original_width);
+ registerParameter(&flip_vertical);
+ registerParameter(&flip_horizontal);
+ registerParameter(&lock_lenght);
+ registerParameter(&lock_angle);
+
+ first_knot.param_make_integer(true);
+ first_knot.param_overwrite_widget(true);
+ last_knot.param_make_integer(true);
+ last_knot.param_overwrite_widget(true);
+ helper_size.param_set_range(0, 999);
+ helper_size.param_set_increments(1, 1);
+ helper_size.param_set_digits(0);
+ offset.param_set_range(-999999.0, 999999.0);
+ offset.param_set_increments(1, 1);
+ offset.param_set_digits(2);
+ strech.param_set_range(0, 999.0);
+ strech.param_set_increments(0.01, 0.01);
+ strech.param_set_digits(4);
+}
+
+LPETransform2Pts::~LPETransform2Pts()
+{
+}
+
+void
+LPETransform2Pts::doOnApply(SPLPEItem const* lpeitem)
+{
+ using namespace Geom;
+ original_bbox(lpeitem);
+
+ point_a = Point(boundingbox_X.min(), boundingbox_Y.middle());
+ point_b = Point(boundingbox_X.max(), boundingbox_Y.middle());
+ SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem);
+ SPPath *sp_path = dynamic_cast<SPPath *>(splpeitem);
+ if (sp_path) {
+ pathvector = sp_path->get_original_curve()->get_pathvector();
+ }
+ if(!pathvector.empty()) {
+ point_a = pathvector.initialPoint();
+ point_b = pathvector.finalPoint();
+ if(are_near(point_a,point_b)) {
+ point_b = pathvector.back().finalCurve().initialPoint();
+ }
+ size_t nnodes = nodeCount(pathvector);
+ last_knot.param_set_value(nnodes);
+ }
+
+ previous_lenght = Geom::distance(point_a,point_b);
+ Geom::Ray transformed(point_a,point_b);
+ previous_angle = transformed.angle();
+ start.param_update_default(point_a);
+ start.param_set_default();
+ end.param_update_default(point_b);
+ end.param_set_default();
+}
+
+void
+LPETransform2Pts::doBeforeEffect (SPLPEItem const* lpeitem)
+{
+ using namespace Geom;
+ original_bbox(lpeitem);
+ point_a = Point(boundingbox_X.min(), boundingbox_Y.middle());
+ point_b = Point(boundingbox_X.max(), boundingbox_Y.middle());
+
+ SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem);
+ SPPath *sp_path = dynamic_cast<SPPath *>(splpeitem);
+ if (sp_path) {
+ pathvector = sp_path->get_original_curve()->get_pathvector();
+ }
+ if(from_original_width_toggler != from_original_width) {
+ from_original_width_toggler = from_original_width;
+ reset();
+ }
+ if(!pathvector.empty() && !from_original_width) {
+ append_path = false;
+ point_a = pointAtNodeIndex(pathvector,(size_t)first_knot-1);
+ point_b = pointAtNodeIndex(pathvector,(size_t)last_knot-1);
+ size_t nnodes = nodeCount(pathvector);
+ first_knot.param_set_range(1, last_knot-1);
+ last_knot.param_set_range(first_knot+1, nnodes);
+ from_original_width.param_setValue(false);
+ } else {
+ first_knot.param_set_value(1);
+ last_knot.param_set_value(2);
+ first_knot.param_set_range(1,1);
+ last_knot.param_set_range(2,2);
+ from_original_width.param_setValue(true);
+ append_path = false;
+ }
+ if(lock_lenght && !lock_angle && previous_lenght != -1) {
+ Geom::Ray transformed((Geom::Point)start,(Geom::Point)end);
+ if(previous_start == start || previous_angle == Geom::deg_to_rad(0)) {
+ previous_angle = transformed.angle();
+ }
+ } else if(lock_angle && !lock_lenght && previous_angle != Geom::deg_to_rad(0)) {
+ if(previous_start == start){
+ previous_lenght = Geom::distance((Geom::Point)start, (Geom::Point)end);
+ }
+ }
+ if(lock_lenght || lock_angle ) {
+ Geom::Point end_point = Geom::Point::polar(previous_angle, previous_lenght) + (Geom::Point)start;
+ end.param_setValue(end_point);
+ }
+ Geom::Ray transformed((Geom::Point)start,(Geom::Point)end);
+ previous_angle = transformed.angle();
+ previous_lenght = Geom::distance((Geom::Point)start, (Geom::Point)end);
+ previous_start = start;
+ splpeitem->apply_to_clippath(splpeitem);
+ splpeitem->apply_to_mask(splpeitem);
+}
+
+void
+LPETransform2Pts::updateIndex()
+{
+ SPLPEItem * splpeitem = const_cast<SPLPEItem *>(sp_lpe_item);
+ SPPath *sp_path = dynamic_cast<SPPath *>(splpeitem);
+ if (sp_path) {
+ pathvector = sp_path->get_original_curve()->get_pathvector();
+ }
+ if(pathvector.empty()) {
+ return;
+ }
+ if(!from_original_width) {
+ point_a = pointAtNodeIndex(pathvector,(size_t)first_knot-1);
+ point_b = pointAtNodeIndex(pathvector,(size_t)last_knot-1);
+ start.param_update_default(point_a);
+ start.param_set_default();
+ end.param_update_default(point_b);
+ end.param_set_default();
+ start.param_update_default(point_a);
+ end.param_update_default(point_b);
+ start.param_set_default();
+ end.param_set_default();
+ }
+ DocumentUndo::done(getSPDoc(), SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change index of knot"));
+}
+//todo migrate to PathVector class?
+size_t
+LPETransform2Pts::nodeCount(Geom::PathVector pathvector) const
+{
+ size_t n = 0;
+ for (Geom::PathVector::iterator it = pathvector.begin(); it != pathvector.end(); ++it) {
+ n += it->size_closed();
+ }
+ return n;
+}
+//todo migrate to PathVector class?
+Geom::Point
+LPETransform2Pts::pointAtNodeIndex(Geom::PathVector pathvector, size_t index) const
+{
+ size_t n = 0;
+ for (Geom::PathVector::iterator pv_it = pathvector.begin(); pv_it != pathvector.end(); ++pv_it) {
+ for (Geom::Path::iterator curve_it = pv_it->begin(); curve_it != pv_it->end_closed(); ++curve_it) {
+ if(index == n) {
+ return curve_it->initialPoint();
+ }
+ n++;
+ }
+ }
+ return Geom::Point();
+}
+//todo migrate to PathVector class? Not used
+Geom::Path
+LPETransform2Pts::pathAtNodeIndex(Geom::PathVector pathvector, size_t index) const
+{
+ size_t n = 0;
+ for (Geom::PathVector::iterator pv_it = pathvector.begin(); pv_it != pathvector.end(); ++pv_it) {
+ for (Geom::Path::iterator curve_it = pv_it->begin(); curve_it != pv_it->end_closed(); ++curve_it) {
+ if(index == n) {
+ return *pv_it;
+ }
+ n++;
+ }
+ }
+ return Geom::Path();
+}
+
+
+void
+LPETransform2Pts::reset()
+{
+ point_a = Geom::Point(boundingbox_X.min(), boundingbox_Y.middle());
+ point_b = Geom::Point(boundingbox_X.max(), boundingbox_Y.middle());
+ if(!pathvector.empty() && !from_original_width) {
+ size_t nnodes = nodeCount(pathvector);
+ first_knot.param_set_range(1, last_knot-1);
+ last_knot.param_set_range(first_knot+1, nnodes);
+ first_knot.param_set_value(1);
+ last_knot.param_set_value(nnodes);
+ point_a = pathvector.initialPoint();
+ point_b = pathvector.finalPoint();
+ } else {
+ first_knot.param_set_value(1);
+ last_knot.param_set_value(2);
+ }
+ Geom::Ray transformed(point_a, point_b);
+ previous_angle = transformed.angle();
+ previous_lenght = Geom::distance(point_a, point_b);
+ start.param_update_default(point_a);
+ end.param_update_default(point_b);
+ start.param_set_default();
+ end.param_set_default();
+}
+
+Gtk::Widget *LPETransform2Pts::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(6);
+
+ std::vector<Parameter *>::iterator it = param_vector.begin();
+ Gtk::HBox * button1 = Gtk::manage(new Gtk::HBox(true,0));
+ Gtk::HBox * button2 = Gtk::manage(new Gtk::HBox(true,0));
+ Gtk::HBox * button3 = Gtk::manage(new Gtk::HBox(true,0));
+ Gtk::HBox * button4 = Gtk::manage(new Gtk::HBox(true,0));
+ while (it != param_vector.end()) {
+ if ((*it)->widget_is_visible) {
+ Parameter *param = *it;
+ Gtk::Widget *widg = dynamic_cast<Gtk::Widget *>(param->param_newWidget());
+ Glib::ustring *tip = param->param_getTooltip();
+ if (param->param_key == "first_knot" || param->param_key == "last_knot") {
+ Inkscape::UI::Widget::Scalar *registered_widget = Gtk::manage(dynamic_cast<Inkscape::UI::Widget::Scalar *>(widg));
+ registered_widget->signal_value_changed().connect(sigc::mem_fun(*this, &LPETransform2Pts::updateIndex));
+ widg = registered_widget;
+ if (widg) {
+ Gtk::HBox *hbox_scalar = dynamic_cast<Gtk::HBox *>(widg);
+ std::vector<Gtk::Widget *> child_list = hbox_scalar->get_children();
+ Gtk::Entry *entry_widget = dynamic_cast<Gtk::Entry *>(child_list[1]);
+ entry_widget->set_width_chars(3);
+ vbox->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ } else if (param->param_key == "from_original_width" || param->param_key == "elastic") {
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ button1->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ } else if (param->param_key == "flip_horizontal" || param->param_key == "flip_vertical") {
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ button2->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ } else if (param->param_key == "lock_angle" || param->param_key == "lock_lenght") {
+ Glib::ustring * tip = param->param_getTooltip();
+ if (widg) {
+ button3->pack_start(*widg, true, true, 2);
+ if (tip) {
+ widg->set_tooltip_text(*tip);
+ } else {
+ widg->set_tooltip_text("");
+ widg->set_has_tooltip(false);
+ }
+ }
+ } else 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;
+ }
+ Gtk::Button *reset = Gtk::manage(new Gtk::Button(Glib::ustring(_("Reset"))));
+ reset->signal_clicked().connect(sigc::mem_fun(*this, &LPETransform2Pts::reset));
+ button4->pack_start(*reset, true, true, 2);
+ vbox->pack_start(*button1, true, true, 2);
+ vbox->pack_start(*button2, true, true, 2);
+ vbox->pack_start(*button3, true, true, 2);
+ vbox->pack_start(*button4, true, true, 2);
+ return dynamic_cast<Gtk::Widget *>(vbox);
+}
+
+Geom::Piecewise<Geom::D2<Geom::SBasis> >
+LPETransform2Pts::doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in)
+{
+ Geom::Piecewise<Geom::D2<Geom::SBasis> > output;
+ double sca = Geom::distance((Geom::Point)start,(Geom::Point)end)/Geom::distance(point_a,point_b);
+ Geom::Ray original(point_a,point_b);
+ Geom::Ray transformed((Geom::Point)start,(Geom::Point)end);
+ double rot = transformed.angle() - original.angle();
+ Geom::Path helper;
+ helper.start(point_a);
+ helper.appendNew<Geom::LineSegment>(point_b);
+ Geom::Affine m;
+ Geom::Angle original_angle = original.angle();
+ if(flip_horizontal && flip_vertical){
+ m *= Geom::Rotate(-original_angle);
+ m *= Geom::Scale(-1,-1);
+ m *= Geom::Rotate(original_angle);
+ } else if(flip_vertical){
+ m *= Geom::Rotate(-original_angle);
+ m *= Geom::Scale(1,-1);
+ m *= Geom::Rotate(original_angle);
+ } else if(flip_horizontal){
+ m *= Geom::Rotate(-original_angle);
+ m *= Geom::Scale(-1,1);
+ m *= Geom::Rotate(original_angle);
+ }
+ if(strech != 1){
+ m *= Geom::Rotate(-original_angle);
+ m *= Geom::Scale(1,strech);
+ m *= Geom::Rotate(original_angle);
+ }
+ if(elastic) {
+ m *= Geom::Rotate(-original_angle);
+ if(sca > 1){
+ m *= Geom::Scale(sca, 1.0);
+ } else {
+ m *= Geom::Scale(sca, 1.0-((1.0-sca)/2.0));
+ }
+ m *= Geom::Rotate(transformed.angle());
+ } else {
+ m *= Geom::Scale(sca);
+ m *= Geom::Rotate(rot);
+ }
+ helper *= m;
+ Geom::Point trans = (Geom::Point)start - helper.initialPoint();
+ if(flip_horizontal){
+ trans = (Geom::Point)end - helper.initialPoint();
+ }
+ if(offset != 0){
+ trans = Geom::Point::polar(transformed.angle() + Geom::deg_to_rad(-90),offset) + trans;
+ }
+ m *= Geom::Translate(trans);
+
+ output.concat(pwd2_in * m);
+
+ return output;
+}
+
+void
+LPETransform2Pts::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
+{
+ using namespace Geom;
+ hp_vec.clear();
+ Geom::Path hp;
+ hp.start((Geom::Point)start);
+ hp.appendNew<Geom::LineSegment>((Geom::Point)end);
+ Geom::PathVector pathv;
+ pathv.push_back(hp);
+ double r = helper_size*.1;
+ if(lock_lenght || lock_angle ) {
+ char const * svgd;
+ svgd = "M -5.39,8.78 -9.13,5.29 -10.38,10.28 Z M -7.22,7.07 -3.43,3.37 m -1.95,-12.16 -3.74,3.5 -1.26,-5 z m -1.83,1.71 3.78,3.7 M 5.24,8.78 8.98,5.29 10.24,10.28 Z M 7.07,7.07 3.29,3.37 M 5.24,-8.78 l 3.74,3.5 1.26,-5 z M 7.07,-7.07 3.29,-3.37";
+ PathVector pathv_move = sp_svg_read_pathv(svgd);
+ pathv_move *= Affine(r,0,0,r,0,0) * Translate(Geom::Point(start));
+ hp_vec.push_back(pathv_move);
+ }
+ if(!lock_angle && lock_lenght) {
+ char const * svgd;
+ svgd = "m 7.07,7.07 c -3.9,3.91 -10.24,3.91 -14.14,0 -3.91,-3.9 -3.91,-10.24 0,-14.14 3.9,-3.91 10.24,-3.91 14.14,0 l -2.83,-4.24 -0.7,2.12";
+ PathVector pathv_turn = sp_svg_read_pathv(svgd);
+ pathv_turn *= Geom::Rotate(previous_angle);
+ pathv_turn *= Affine(r,0,0,r,0,0) * Translate(Geom::Point(end));
+ hp_vec.push_back(pathv_turn);
+ }
+ hp_vec.push_back(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-transform_2pts.h b/src/live_effects/lpe-transform_2pts.h
new file mode 100644
index 000000000..c20d56206
--- /dev/null
+++ b/src/live_effects/lpe-transform_2pts.h
@@ -0,0 +1,91 @@
+#ifndef INKSCAPE_LPE_TRANSFORM_2PTS_H
+#define INKSCAPE_LPE_TRANSFORM_2PTS_H
+
+/** \file
+ * LPE "Transform through 2 points" implementation
+ */
+
+/*
+ * Authors:
+ *
+ *
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#include "live_effects/effect.h"
+#include "live_effects/lpegroupbbox.h"
+#include "live_effects/parameter/parameter.h"
+#include "live_effects/parameter/togglebutton.h"
+#include "live_effects/parameter/point.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+class LPETransform2Pts : public Effect, GroupBBoxEffect {
+public:
+ LPETransform2Pts(LivePathEffectObject *lpeobject);
+ virtual ~LPETransform2Pts();
+
+ virtual void doOnApply (SPLPEItem const* lpeitem);
+
+ virtual Geom::Piecewise<Geom::D2<Geom::SBasis> > doEffect_pwd2 (Geom::Piecewise<Geom::D2<Geom::SBasis> > const & pwd2_in);
+
+ virtual void doBeforeEffect (SPLPEItem const* lpeitem);
+
+ virtual Gtk::Widget *newWidget();
+
+ void updateIndex();
+
+ size_t nodeCount(Geom::PathVector pathvector) const;
+
+ Geom::Point pointAtNodeIndex(Geom::PathVector pathvector, size_t index) const;
+
+ Geom::Path pathAtNodeIndex(Geom::PathVector pathvector, size_t index) const;
+
+ void reset();
+
+protected:
+ virtual void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec);
+
+private:
+ ToggleButtonParam elastic;
+ ToggleButtonParam from_original_width;
+ ToggleButtonParam lock_lenght;
+ ToggleButtonParam lock_angle;
+ ToggleButtonParam flip_horizontal;
+ ToggleButtonParam flip_vertical;
+ PointParam start;
+ PointParam end;
+ ScalarParam strech;
+ ScalarParam offset;
+ ScalarParam first_knot;
+ ScalarParam last_knot;
+ ScalarParam helper_size;
+ bool from_original_width_toggler;
+ Geom::Point point_a;
+ Geom::Point point_b;
+ Geom::PathVector pathvector;
+ bool append_path;
+ Geom::Angle previous_angle;
+ Geom::Point previous_start;
+ double previous_lenght;
+ LPETransform2Pts(const LPETransform2Pts&);
+ LPETransform2Pts& operator=(const LPETransform2Pts&);
+};
+
+} //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/bool.cpp b/src/live_effects/parameter/bool.cpp
index c1e8f8a7b..9ecadbdeb 100644
--- a/src/live_effects/parameter/bool.cpp
+++ b/src/live_effects/parameter/bool.cpp
@@ -21,8 +21,8 @@ namespace LivePathEffect {
BoolParam::BoolParam( const Glib::ustring& label, const Glib::ustring& tip,
const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
- Effect* effect, bool default_value )
- : Parameter(label, tip, key, wr, effect), value(default_value), defvalue(default_value)
+ Effect* effect, bool default_value , bool no_widget)
+ : Parameter(label, tip, key, wr, effect), value(default_value), defvalue(default_value), hide_widget(no_widget)
{
}
@@ -53,20 +53,24 @@ BoolParam::param_getSVGValue() const
Gtk::Widget *
BoolParam::param_newWidget()
{
- Inkscape::UI::Widget::RegisteredCheckButton * checkwdg = Gtk::manage(
- new Inkscape::UI::Widget::RegisteredCheckButton( param_label,
- param_tooltip,
- param_key,
- *param_wr,
- false,
- param_effect->getRepr(),
- param_effect->getSPDoc()) );
+ if(!hide_widget){
+ Inkscape::UI::Widget::RegisteredCheckButton * checkwdg = Gtk::manage(
+ new Inkscape::UI::Widget::RegisteredCheckButton( param_label,
+ param_tooltip,
+ param_key,
+ *param_wr,
+ false,
+ param_effect->getRepr(),
+ param_effect->getSPDoc()) );
- checkwdg->setActive(value);
- checkwdg->setProgrammatically = false;
- checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change bool parameter"));
+ checkwdg->setActive(value);
+ checkwdg->setProgrammatically = false;
+ checkwdg->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change bool parameter"));
- return dynamic_cast<Gtk::Widget *> (checkwdg);
+ return dynamic_cast<Gtk::Widget *> (checkwdg);
+ } else {
+ return NULL;
+ }
}
void
diff --git a/src/live_effects/parameter/bool.h b/src/live_effects/parameter/bool.h
index b45eeb0b3..403dd0b87 100644
--- a/src/live_effects/parameter/bool.h
+++ b/src/live_effects/parameter/bool.h
@@ -25,7 +25,8 @@ public:
const Glib::ustring& key,
Inkscape::UI::Widget::Registry* wr,
Effect* effect,
- bool default_value = false);
+ bool default_value = false,
+ bool no_widget = false);
virtual ~BoolParam();
virtual Gtk::Widget * param_newWidget();
@@ -46,6 +47,7 @@ private:
bool value;
bool defvalue;
+ bool hide_widget;
};
diff --git a/src/live_effects/parameter/filletchamferpointarray.cpp b/src/live_effects/parameter/filletchamferpointarray.cpp
index b089213fd..399307502 100644
--- a/src/live_effects/parameter/filletchamferpointarray.cpp
+++ b/src/live_effects/parameter/filletchamferpointarray.cpp
@@ -159,9 +159,8 @@ void FilletChamferPointArrayParam::recalculate_controlpoints_for_new_pwd2(
//todo: if the path remove some nodes whith the result of a straight
//line but with handles, the node inserted into dont fire the knot
// because is not handle as cusp node by get_nodetype function
- bool this_is_line = true;
bool next_is_line = is_straight_curve(*curve_it1);
- this_is_line = is_straight_curve((*path_it)[counterCurves - 1]);
+ bool this_is_line = is_straight_curve((*path_it)[counterCurves - 1]);
nodetype = get_nodetype((*path_it)[counterCurves - 1], *curve_it1);
if (this_is_line || next_is_line) {
nodetype = NODE_CUSP;
@@ -307,9 +306,8 @@ void FilletChamferPointArrayParam::recalculate_knots(
nodetype = NODE_NONE;
}
} else {
- bool this_is_line = true;
bool next_is_line = is_straight_curve(*curve_it1);
- this_is_line = is_straight_curve((*path_it)[counterCurves - 1]);
+ bool this_is_line = is_straight_curve((*path_it)[counterCurves - 1]);
nodetype = get_nodetype((*path_it)[counterCurves - 1], *curve_it1);
if (this_is_line || next_is_line) {
nodetype = NODE_CUSP;
@@ -467,12 +465,12 @@ double FilletChamferPointArrayParam::len_to_rad(int index, double len)
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);
+ 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);
+ Geom::CubicBezier const *cubic2 = dynamic_cast<Geom::CubicBezier const *>(knotCurve2);
Ray ray2(B->initialPoint(), endArcPoint);
if (cubic2) {
ray2.setPoints(endArcPoint, (*cubic2)[1]);
diff --git a/src/live_effects/parameter/originalpatharray.cpp b/src/live_effects/parameter/originalpatharray.cpp
index 78e061e66..7e3a6f5fe 100644
--- a/src/live_effects/parameter/originalpatharray.cpp
+++ b/src/live_effects/parameter/originalpatharray.cpp
@@ -215,7 +215,7 @@ void OriginalPathArrayParam::on_up_button_click()
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++) {
+ 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]);
@@ -241,11 +241,11 @@ void OriginalPathArrayParam::on_down_button_click()
Gtk::TreeModel::Row row = *iter;
int i = 0;
- for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); i++, iter++) {
+ 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++;
+ ++niter;
i++;
}
_vector.insert(niter, row[_model->_colObject]);
@@ -295,7 +295,7 @@ OriginalPathArrayParam::on_link_button_click()
Inkscape::SVGOStringStream os;
bool foundOne = false;
- for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
+ for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); ++iter) {
if (foundOne) {
os << "|";
} else {
@@ -330,7 +330,7 @@ void OriginalPathArrayParam::unlink(PathAndDirection* to)
void OriginalPathArrayParam::remove_link(PathAndDirection* to)
{
unlink(to);
- for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
+ for (std::vector<PathAndDirection*>::iterator iter = _vector.begin(); iter != _vector.end(); ++iter) {
if (*iter == to) {
PathAndDirection *w = *iter;
_vector.erase(iter);
@@ -455,7 +455,7 @@ gchar * OriginalPathArrayParam::param_getSVGValue() const
{
Inkscape::SVGOStringStream os;
bool foundOne = false;
- for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); iter++) {
+ for (std::vector<PathAndDirection*>::const_iterator iter = _vector.begin(); iter != _vector.end(); ++iter) {
if (foundOne) {
os << "|";
} else {
diff --git a/src/live_effects/parameter/parameter.cpp b/src/live_effects/parameter/parameter.cpp
index 527bc06fe..d4e213948 100644
--- a/src/live_effects/parameter/parameter.cpp
+++ b/src/live_effects/parameter/parameter.cpp
@@ -54,7 +54,7 @@ void Parameter::write_to_SVG(void)
*/
ScalarParam::ScalarParam( const Glib::ustring& label, const Glib::ustring& tip,
const Glib::ustring& key, Inkscape::UI::Widget::Registry* wr,
- Effect* effect, gdouble default_value)
+ Effect* effect, gdouble default_value, bool no_widget)
: Parameter(label, tip, key, wr, effect),
value(default_value),
min(-SCALARPARAM_G_MAXDOUBLE),
@@ -64,7 +64,9 @@ ScalarParam::ScalarParam( const Glib::ustring& label, const Glib::ustring& tip,
digits(2),
inc_step(0.1),
inc_page(1),
- add_slider(false)
+ add_slider(false),
+ overwrite_widget(false),
+ hide_widget(no_widget)
{
}
@@ -143,24 +145,34 @@ ScalarParam::param_make_integer(bool yes)
inc_page = 10;
}
+void
+ScalarParam::param_overwrite_widget(bool overwrite_widget)
+{
+ this->overwrite_widget = overwrite_widget;
+}
+
Gtk::Widget *
ScalarParam::param_newWidget()
{
- Inkscape::UI::Widget::RegisteredScalar *rsu = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalar(
- param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc() ) );
-
- rsu->setValue(value);
- rsu->setDigits(digits);
- rsu->setIncrements(inc_step, inc_page);
- rsu->setRange(min, max);
- rsu->setProgrammatically = false;
- if (add_slider) {
- rsu->addSlider();
+ if(!hide_widget){
+ Inkscape::UI::Widget::RegisteredScalar *rsu = Gtk::manage( new Inkscape::UI::Widget::RegisteredScalar(
+ param_label, param_tooltip, param_key, *param_wr, param_effect->getRepr(), param_effect->getSPDoc() ) );
+
+ rsu->setValue(value);
+ rsu->setDigits(digits);
+ rsu->setIncrements(inc_step, inc_page);
+ rsu->setRange(min, max);
+ rsu->setProgrammatically = false;
+ if (add_slider) {
+ rsu->addSlider();
+ }
+ if(!overwrite_widget){
+ rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
+ }
+ return dynamic_cast<Gtk::Widget *> (rsu);
+ } else {
+ return NULL;
}
-
- rsu->set_undo_parameters(SP_VERB_DIALOG_LIVE_PATH_EFFECT, _("Change scalar parameter"));
-
- return dynamic_cast<Gtk::Widget *> (rsu);
}
void
diff --git a/src/live_effects/parameter/parameter.h b/src/live_effects/parameter/parameter.h
index cc2c4f3d6..0ef28650a 100644
--- a/src/live_effects/parameter/parameter.h
+++ b/src/live_effects/parameter/parameter.h
@@ -102,7 +102,8 @@ public:
const Glib::ustring& key,
Inkscape::UI::Widget::Registry* wr,
Effect* effect,
- gdouble default_value = 1.0);
+ gdouble default_value = 1.0,
+ bool no_widget = false);
virtual ~ScalarParam();
virtual bool param_readSVGValue(const gchar * strvalue);
@@ -117,6 +118,7 @@ public:
void addSlider(bool add_slider_widget) { add_slider = add_slider_widget; };
+ void param_overwrite_widget(bool overwrite_widget);
virtual Gtk::Widget * param_newWidget();
inline operator gdouble() const { return value; };
@@ -131,6 +133,8 @@ protected:
double inc_step;
double inc_page;
bool add_slider;
+ bool overwrite_widget;
+ bool hide_widget;
private:
ScalarParam(const ScalarParam&);
diff --git a/src/live_effects/parameter/togglebutton.cpp b/src/live_effects/parameter/togglebutton.cpp
index c5da8b858..47a8b5615 100644
--- a/src/live_effects/parameter/togglebutton.cpp
+++ b/src/live_effects/parameter/togglebutton.cpp
@@ -119,6 +119,10 @@ ToggleButtonParam::param_newWidget()
void
ToggleButtonParam::refresh_button()
{
+ if (!_toggled_connection.connected()) {
+ return;
+ }
+
if(!checkwdg){
return;
}
diff --git a/src/live_effects/spiro-converters.cpp b/src/live_effects/spiro-converters.cpp
index f116d5256..ee214704c 100644
--- a/src/live_effects/spiro-converters.cpp
+++ b/src/live_effects/spiro-converters.cpp
@@ -21,43 +21,49 @@
namespace Spiro {
void
-ConverterSPCurve::moveto(double x, double y, bool is_open)
+ConverterSPCurve::moveto(double x, double y)
{
if ( IS_FINITE(x) && IS_FINITE(y) ) {
_curve.moveto(x, y);
- if (!is_open) {
- _curve.closepath();
- }
} else {
SPIRO_G_MESSAGE("Spiro: moveto not finite");
}
}
void
-ConverterSPCurve::lineto(double x, double y)
+ConverterSPCurve::lineto(double x, double y, bool close_last)
{
if ( IS_FINITE(x) && IS_FINITE(y) ) {
_curve.lineto(x, y);
+ if (close_last) {
+ _curve.closepath();
+ }
} else {
SPIRO_G_MESSAGE("Spiro: lineto not finite");
}
}
void
-ConverterSPCurve::quadto(double xm, double ym, double x3, double y3)
+ConverterSPCurve::quadto(double xm, double ym, double x3, double y3, bool close_last)
{
if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
_curve.quadto(xm, ym, x3, y3);
+ if (close_last) {
+ _curve.closepath();
+ }
} else {
SPIRO_G_MESSAGE("Spiro: quadto not finite");
}
}
void
-ConverterSPCurve::curveto(double x1, double y1, double x2, double y2, double x3, double y3)
+ConverterSPCurve::curveto(double x1, double y1, double x2, double y2, double x3, double y3, bool close_last)
{
if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
_curve.curveto(x1, y1, x2, y2, x3, y3);
+ if (close_last) {
+ _curve.closepath();
+ }
} else {
SPIRO_G_MESSAGE("Spiro: curveto not finite");
}
@@ -71,41 +77,43 @@ ConverterPath::ConverterPath(Geom::Path &path)
}
void
-ConverterPath::moveto(double x, double y, bool is_open)
+ConverterPath::moveto(double x, double y)
{
if ( IS_FINITE(x) && IS_FINITE(y) ) {
_path.start(Geom::Point(x, y));
- _path.close(!is_open);
} else {
SPIRO_G_MESSAGE("spiro moveto not finite");
}
}
void
-ConverterPath::lineto(double x, double y)
+ConverterPath::lineto(double x, double y, bool close_last)
{
if ( IS_FINITE(x) && IS_FINITE(y) ) {
_path.appendNew<Geom::LineSegment>( Geom::Point(x, y) );
+ _path.close(close_last);
} else {
SPIRO_G_MESSAGE("spiro lineto not finite");
}
}
void
-ConverterPath::quadto(double xm, double ym, double x3, double y3)
+ConverterPath::quadto(double xm, double ym, double x3, double y3, bool close_last)
{
if ( IS_FINITE(xm) && IS_FINITE(ym) && IS_FINITE(x3) && IS_FINITE(y3) ) {
_path.appendNew<Geom::QuadraticBezier>(Geom::Point(xm, ym), Geom::Point(x3, y3));
+ _path.close(close_last);
} else {
SPIRO_G_MESSAGE("spiro quadto not finite");
}
}
void
-ConverterPath::curveto(double x1, double y1, double x2, double y2, double x3, double y3)
+ConverterPath::curveto(double x1, double y1, double x2, double y2, double x3, double y3, bool close_last)
{
if ( IS_FINITE(x1) && IS_FINITE(y1) && IS_FINITE(x2) && IS_FINITE(y2) ) {
_path.appendNew<Geom::CubicBezier>(Geom::Point(x1, y1), Geom::Point(x2, y2), Geom::Point(x3, y3));
+ _path.close(close_last);
} else {
SPIRO_G_MESSAGE("spiro curveto not finite");
}
diff --git a/src/live_effects/spiro-converters.h b/src/live_effects/spiro-converters.h
index 90855d2d6..6182a5dcd 100644
--- a/src/live_effects/spiro-converters.h
+++ b/src/live_effects/spiro-converters.h
@@ -11,10 +11,10 @@ public:
ConverterBase() {};
virtual ~ConverterBase() {};
- virtual void moveto(double x, double y, bool is_open) = 0;
- virtual void lineto(double x, double y) = 0;
- virtual void quadto(double x1, double y1, double x2, double y2) = 0;
- virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3) = 0;
+ virtual void moveto(double x, double y) = 0;
+ virtual void lineto(double x, double y, bool close_last) = 0;
+ virtual void quadto(double x1, double y1, double x2, double y2, bool close_last) = 0;
+ virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3, bool close_last) = 0;
};
@@ -27,10 +27,10 @@ public:
: _curve(curve)
{}
- virtual void moveto(double x, double y, bool is_open);
- virtual void lineto(double x, double y);
- virtual void quadto(double x1, double y1, double x2, double y2);
- virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3);
+ virtual void moveto(double x, double y);
+ virtual void lineto(double x, double y, bool close_last);
+ virtual void quadto(double x1, double y1, double x2, double y2, bool close_last);
+ virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3, bool close_last);
private:
SPCurve &_curve;
@@ -47,10 +47,10 @@ class ConverterPath : public ConverterBase {
public:
ConverterPath(Geom::Path &path);
- virtual void moveto(double x, double y, bool is_open);
- virtual void lineto(double x, double y);
- virtual void quadto(double x1, double y1, double x2, double y2);
- virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3);
+ virtual void moveto(double x, double y);
+ virtual void lineto(double x, double y, bool close_last);
+ virtual void quadto(double x1, double y1, double x2, double y2, bool close_last);
+ virtual void curveto(double x1, double y1, double x2, double y2, double x3, double y3, bool close_last);
private:
Geom::Path &_path;
diff --git a/src/live_effects/spiro.cpp b/src/live_effects/spiro.cpp
index 46e53a0da..0ac2815bf 100644
--- a/src/live_effects/spiro.cpp
+++ b/src/live_effects/spiro.cpp
@@ -847,13 +847,13 @@ solve_spiro(spiro_seg *s, const int nseg)
static void
spiro_seg_to_otherpath(const double ks[4],
double x0, double y0, double x1, double y1,
- ConverterBase &bc, int depth)
+ ConverterBase &bc, int depth, bool close_last)
{
double bend = fabs(ks[0]) + fabs(.5 * ks[1]) + fabs(.125 * ks[2]) +
fabs((1./48) * ks[3]);
if (!(bend > 1e-8)) {
- bc.lineto(x1, y1);
+ bc.lineto(x1, y1, close_last);
} else {
double seg_ch = hypot(x1 - x0, y1 - y0);
double seg_th = atan2(y1 - y0, x1 - x0);
@@ -876,7 +876,7 @@ spiro_seg_to_otherpath(const double ks[4],
vl = (scale * (1./3)) * sin(th_even - th_odd);
ur = (scale * (1./3)) * cos(th_even + th_odd);
vr = (scale * (1./3)) * sin(th_even + th_odd);
- bc.curveto(x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1);
+ bc.curveto(x0 + ul, y0 + vl, x1 - ur, y1 - vr, x1, y1, close_last);
} else {
/* subdivide */
double ksub[4];
@@ -895,11 +895,11 @@ spiro_seg_to_otherpath(const double ks[4],
integrate_spiro(ksub, xysub);
xmid = x0 + cth * xysub[0] - sth * xysub[1];
ymid = y0 + cth * xysub[1] + sth * xysub[0];
- spiro_seg_to_otherpath(ksub, x0, y0, xmid, ymid, bc, depth + 1);
+ spiro_seg_to_otherpath(ksub, x0, y0, xmid, ymid, bc, depth + 1, false);
ksub[0] += .25 * ks[1] + (1./384) * ks[3];
ksub[1] += .125 * ks[2];
ksub[2] += (1./16) * ks[3];
- spiro_seg_to_otherpath(ksub, xmid, ymid, x1, y1, bc, depth + 1);
+ spiro_seg_to_otherpath(ksub, xmid, ymid, x1, y1, bc, depth + 1, close_last);
}
}
}
@@ -933,9 +933,10 @@ spiro_to_otherpath(const spiro_seg *s, int n, ConverterBase &bc)
double y1 = s[i + 1].y;
if (i == 0) {
- bc.moveto(x0, y0, s[0].ty == '{');
+ bc.moveto(x0, y0);
}
- spiro_seg_to_otherpath(s[i].ks, x0, y0, x1, y1, bc, 0);
+ // on the last segment, set the 'close_last' flag if path is closed
+ spiro_seg_to_otherpath(s[i].ks, x0, y0, x1, y1, bc, 0, (nsegs == n) && (i == n - 1));
}
}