diff --git a/share/icons/Dash/scalable/actions/trim.svg b/share/icons/Dash/scalable/actions/trim.svg
new file mode 100644
index 0000000000000000000000000000000000000000..85065dba6307b223d50d3d74156e0970968e81e3
--- /dev/null
+++ b/share/icons/Dash/scalable/actions/trim.svg
@@ -0,0 +1,8 @@
+
diff --git a/share/icons/Dash/symbolic/actions/trim-symbolic.svg b/share/icons/Dash/symbolic/actions/trim-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..85065dba6307b223d50d3d74156e0970968e81e3
--- /dev/null
+++ b/share/icons/Dash/symbolic/actions/trim-symbolic.svg
@@ -0,0 +1,8 @@
+
diff --git a/share/icons/hicolor/scalable/actions/trim.svg b/share/icons/hicolor/scalable/actions/trim.svg
new file mode 100644
index 0000000000000000000000000000000000000000..85065dba6307b223d50d3d74156e0970968e81e3
--- /dev/null
+++ b/share/icons/hicolor/scalable/actions/trim.svg
@@ -0,0 +1,8 @@
+
diff --git a/share/icons/hicolor/symbolic/actions/trim-symbolic.svg b/share/icons/hicolor/symbolic/actions/trim-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..85065dba6307b223d50d3d74156e0970968e81e3
--- /dev/null
+++ b/share/icons/hicolor/symbolic/actions/trim-symbolic.svg
@@ -0,0 +1,8 @@
+
diff --git a/share/icons/multicolor/symbolic/actions/trim-symbolic.svg b/share/icons/multicolor/symbolic/actions/trim-symbolic.svg
new file mode 100644
index 0000000000000000000000000000000000000000..85065dba6307b223d50d3d74156e0970968e81e3
--- /dev/null
+++ b/share/icons/multicolor/symbolic/actions/trim-symbolic.svg
@@ -0,0 +1,8 @@
+
diff --git a/src/live_effects/CMakeLists.txt b/src/live_effects/CMakeLists.txt
index 89703479cc443022f0bbbdf437f1fb8eb5e0d4f5..04780b5ba20e54233d335be0ad8d3e2ac0838dcb 100644
--- a/src/live_effects/CMakeLists.txt
+++ b/src/live_effects/CMakeLists.txt
@@ -60,6 +60,7 @@ set(live_effects_SRC
lpe-text_label.cpp
lpe-tiling.cpp
lpe-transform_2pts.cpp
+ lpe-trim-shape.cpp
lpegroupbbox.cpp
lpeobject-reference.cpp
lpe-vonkoch.cpp
@@ -158,6 +159,7 @@ set(live_effects_SRC
lpe-text_label.h
lpe-tiling.h
lpe-transform_2pts.h
+ lpe-trim-shape.h
lpe-vonkoch.h
lpegroupbbox.h
lpeobject-reference.h
diff --git a/src/live_effects/effect-enum.h b/src/live_effects/effect-enum.h
index 0ae38eb4142168b7b26b59952f130734d3f96e39..0aaf3b85bde9da04da766ffe3399d5e911f5563d 100644
--- a/src/live_effects/effect-enum.h
+++ b/src/live_effects/effect-enum.h
@@ -59,6 +59,7 @@ enum EffectType {
BOOL_OP,
SLICE,
TILING,
+ TRIM_SHAPE,
// PUT NEW LPE BEFORE EXPERIMENTAL IN THE SAME ORDER AS IN effect.cpp
// Visible Experimental LPE's
ANGLE_BISECTOR,
diff --git a/src/live_effects/effect.cpp b/src/live_effects/effect.cpp
index 7685b05918ff0b906516bf8bbddac16a357e15fb..f41d9187cfe7eba4aa2ea996367df2d545170f3b 100644
--- a/src/live_effects/effect.cpp
+++ b/src/live_effects/effect.cpp
@@ -77,6 +77,7 @@
#include "live_effects/lpe-test-doEffect-stack.h"
#include "live_effects/lpe-text_label.h"
#include "live_effects/lpe-tiling.h"
+#include "live_effects/lpe-trim-shape.h"
#include "live_effects/lpe-transform_2pts.h"
#include "live_effects/lpe-vonkoch.h"
#include "message-stack.h"
@@ -691,6 +692,21 @@ const EnumEffectData LPETypeData[] = {
false ,//on_text
false ,//experimental
},
+ /* 1.5 */
+ {
+ TRIM_SHAPE,
+ NC_("path effect", "Trim shapes") ,//label
+ "trim_shape" ,//key
+ "trim" ,//icon
+ N_("Trim shape") ,//description
+ LPECategory::EditTools ,//category
+ true ,//on_path
+ true ,//on_shape
+ false ,//on_group
+ false ,//on_image
+ false ,//on_text
+ false ,//experimental
+ },
// VISIBLE experimental LPEs
{
ANGLE_BISECTOR,
@@ -1113,6 +1129,9 @@ Effect::New(EffectType lpenr, LivePathEffectObject *lpeobj)
case TILING:
neweffect = static_cast ( new LPETiling(lpeobj) );
break;
+ case TRIM_SHAPE:
+ neweffect = static_cast ( new LPETrimShape(lpeobj) );
+ break;
default:
g_warning("LivePathEffect::Effect::New called with invalid patheffect type (%d)", lpenr);
neweffect = nullptr;
diff --git a/src/live_effects/lpe-taperstroke.cpp b/src/live_effects/lpe-taperstroke.cpp
index 25b19194fea1df071638da8f42970a5d716b55df..6baa215a33f4b6a253deb474e1bee64fd05e35cb 100644
--- a/src/live_effects/lpe-taperstroke.cpp
+++ b/src/live_effects/lpe-taperstroke.cpp
@@ -38,7 +38,7 @@ inline bool withinRange(T value, T low, T high) {
namespace Inkscape {
namespace LivePathEffect {
-namespace TpS {
+namespace TapperStrokeNS {
class KnotHolderEntityAttachBegin : public LPEKnotHolderEntity {
public:
KnotHolderEntityAttachBegin(LPETaperStroke * effect, size_t index)
@@ -51,7 +51,7 @@ namespace TpS {
bool valid_index(unsigned int index) const {
return (_effect->attach_start._vector.size() > index);
};
- private:
+ protected:
size_t _index;
LPETaperStroke * _effect;
};
@@ -66,13 +66,13 @@ namespace TpS {
void knot_click(guint state) override;
Geom::Point knot_get() const override;
bool valid_index(unsigned int index) const {
- return (_effect->attach_end._vector.size() > index);
+ return (_effect->attach_start._vector.size() > index);
};
- private:
+ protected:
size_t _index;
LPETaperStroke * _effect;
};
-} // TpS
+} // TapperStrokeNS
static const Util::EnumData JoinType[] = {
// clang-format off
@@ -611,19 +611,19 @@ LPETaperStroke::doBeforeEffect (SPLPEItem const* lpeitem)
void LPETaperStroke::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item)
{
for (size_t i = 0 ; i < attach_start._vector.size(); i++) {
- KnotHolderEntity *e = new TpS::KnotHolderEntityAttachBegin(this, i);
+ KnotHolderEntity *e = new TapperStrokeNS::KnotHolderEntityAttachBegin(this, i);
e->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:TaperStrokeBegin",
_("Start point of the taper: drag to alter the taper, Shift+click changes the taper direction"));
knotholder->add(e);
- KnotHolderEntity *f = new TpS::KnotHolderEntityAttachEnd(this, i);
+ KnotHolderEntity *f = new TapperStrokeNS::KnotHolderEntityAttachEnd(this, i);
f->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:TaperStrokeEnd",
_("End point of the taper: drag to alter the taper, Shift+click changes the taper direction"));
knotholder->add(f);
}
}
-namespace TpS {
+namespace TapperStrokeNS {
void KnotHolderEntityAttachBegin::knot_set(Geom::Point const &p, Geom::Point const&/*origin*/, guint state)
{
@@ -737,7 +737,7 @@ Geom::Point KnotHolderEntityAttachEnd::knot_get() const
return Geom::Point();
}
-} // namespace TpS
+} // namespace TapperStrokeNS
} // namespace LivePathEffect
} // namespace Inkscape
diff --git a/src/live_effects/lpe-taperstroke.h b/src/live_effects/lpe-taperstroke.h
index 42b8ca84774e4a8a3b50a2a7e36c4c7f91af4b40..a4e2cf03da0d206e32d37b8ba0514966b46c149d 100644
--- a/src/live_effects/lpe-taperstroke.h
+++ b/src/live_effects/lpe-taperstroke.h
@@ -21,7 +21,7 @@
namespace Inkscape {
namespace LivePathEffect {
-namespace TpS {
+namespace TapperStrokeNS {
// we need a separate namespace to avoid clashes with other LPEs
class KnotHolderEntityAttachBegin;
class KnotHolderEntityAttachEnd;
@@ -41,8 +41,8 @@ public:
void addKnotHolderEntities(KnotHolder * knotholder, SPItem * item) override;
protected:
- friend class TpS::KnotHolderEntityAttachBegin;
- friend class TpS::KnotHolderEntityAttachEnd;
+ friend class TapperStrokeNS::KnotHolderEntityAttachBegin;
+ friend class TapperStrokeNS::KnotHolderEntityAttachEnd;
ScalarArrayParam attach_start;
ScalarArrayParam attach_end;
ScalarArrayParam start_smoothing;
diff --git a/src/live_effects/lpe-trim-shape.cpp b/src/live_effects/lpe-trim-shape.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0120f811d698168d9eaed1469a704a25ff699f69
--- /dev/null
+++ b/src/live_effects/lpe-trim-shape.cpp
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author(s):
+ * Jabiertxo Arraiza Cenoz
+ *
+ * Copyright (C) 2014 Author(s)
+ *
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#include "lpe-trim-shape.h"
+#include
+#include "helper/geom.h"
+#include "helper/geom-nodetype.h"
+#include "object/sp-shape.h"
+#include "object/sp-lpe-item.h"
+#include "svg/svg.h"
+#include "ui/knot/knot-holder.h"
+#include "ui/knot/knot-holder-entity.h"
+
+
+template
+inline bool withinRange(T value, T low, T high) {
+ return (value > low && value < high);
+}
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+namespace TrimShapeNS {
+ class KnotHolderEntityAttach : public LPEKnotHolderEntity {
+ public:
+ KnotHolderEntityAttach(LPETrimShape * effect, size_t index, bool begin)
+ : LPEKnotHolderEntity(effect)
+ , _effect(effect)
+ , _index(index)
+ , _begin(begin) {};
+ void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
+ void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, unsigned state) override;
+ Geom::Point knot_get() const override;
+ bool valid_index(unsigned int index) const {
+ return (_effect->attach_start._vector.size() > index);
+ };
+ protected:
+ size_t _index;
+ bool _begin;
+ LPETrimShape * _effect;
+ };
+} // TrimShapeNS
+
+LPETrimShape::LPETrimShape(LivePathEffectObject *lpeobject)
+ : Effect(lpeobject),
+ subpath(_("Select subpath"), _("Select the subpath you want to modify"), "subpath", &wr, this, 1.),
+ attach_start(_("Start offset"), _("Trim distance from path start"), "attach_start", &wr, this, 10),
+ attach_end(_("End offset"), _("The ending position of the trim"), "end_offset", &wr, this, 10),
+ flexible(_("Flexible position"), _("Flexible or absolute document units"), "flexible", &wr, this, true),
+ linkall(_("Link all subpaths"), _("Link all subpaths"), "linkall", &wr, this, false)
+{
+ show_orig_path = true;
+ _provides_knotholder_entities = true;
+ attach_start.param_set_digits(2);
+ attach_start.param_set_increments(1, 1);
+ attach_end.param_set_digits(2);
+ attach_end.param_set_increments(1, 1);
+ subpath.param_set_range(1, 1);
+ subpath.param_set_increments(1, 1);
+ subpath.param_set_digits(0);
+ registerParameter(&subpath);
+ registerParameter(&attach_start);
+ registerParameter(&attach_end);
+ registerParameter(&flexible);
+ registerParameter(&linkall);
+}
+
+LPETrimShape::~LPETrimShape() = default;
+
+void LPETrimShape::doOnApply(SPLPEItem const *lpeItem)
+{
+ SPLPEItem *splpeitem = const_cast(lpeItem);
+ auto shape = cast(splpeitem);
+ if (!shape) {
+ g_warning("LPE Slice Nodes can only be applied to shapes (not groups).");
+ SPLPEItem *item = const_cast(lpeItem);
+ item->removeCurrentPathEffect(false);
+ }
+}
+
+// breaks time value into integral and fractional part
+// must be better add PathVectorTimeAt and PathTimeAt to 2Geom adding this dupled function
+Geom::PathTime LPETrimShape::_factorTime(Geom::Path const path, Geom::Coord t) const
+{
+ Geom::Path::size_type sz = path.size_default();
+ if (t < 0 || t > sz) {
+ g_warning("parameter t out of bounds");
+ t = sz;
+ }
+
+ Geom::PathTime ret;
+ Geom::Coord k;
+ ret.t = modf(t, &k);
+ ret.curve_index = k;
+ if (ret.curve_index == sz) {
+ --ret.curve_index;
+ ret.t = 1;
+ }
+ return ret;
+}
+
+/**
+ * @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::Path LPETrimShape::doEffect_simplePath(Geom::Path const & path, double start, double end)
+{
+ bool cross_start = false;
+ if (path.size() - start < end) { // this allow coninuing on closed paths like circles
+ cross_start = path.closed();
+ }
+ if (path.size() - start == 0 && end == 0) { // this hide the path if start and end collapse at 0
+ return Geom::Path();
+ }
+ return path.portion(_factorTime(path, start), _factorTime(path, path.size() - end), cross_start);
+}
+
+///Calculate the time in curve with a length
+//TODO: find a better place to it dupled code better in 2GEOM
+double LPETrimShape::timeAtLength(double const A, Geom::Piecewise> pwd2)
+{
+ if ( A == 0 || pwd2.size() == 0) {
+ return 0;
+ }
+
+ double t = pwd2.size();
+ std::vector t_roots = roots(Geom::arcLengthSb(pwd2) - A);
+ if (!t_roots.empty()) {
+ t = t_roots[0];
+ }
+ return t;
+}
+
+void LPETrimShape::doBeforeEffect(SPLPEItem const *lpeItem)
+{
+ using namespace Geom;
+ // this allow multi stack LPE
+ auto first = dynamic_cast(lpeItem->getFirstPathEffectOfType(TRIM_SHAPE));
+ _pathvector_before_effect = pathvector_before_effect;
+ if (this != first) {
+ _pathvector_before_effect = first->_pathvector_before_effect;
+ }
+ // define ranges based on flexible value
+ if (prevflex != flexible) {
+ if (flexible) {
+ attach_start.param_set_range(0, 100);
+ attach_end.param_set_range(0, 100);
+ } else {
+ attach_start.param_set_range(0, std::numeric_limits::max());
+ attach_end.param_set_range(0, std::numeric_limits::max());
+ }
+ }
+ prevflex = flexible;
+ Geom::PathVector pathv = pathv_to_linear_and_cubic_beziers(_pathvector_before_effect);
+ size_t sicepv = pathv.size();
+ bool write = false;
+ // if number of subpaths change
+ if (previous_size != sicepv) {
+ subpath.param_set_range(1, sicepv);
+ // move to first one
+ subpath.param_readSVGValue("1");
+ if (!is_load) {
+ attach_start._vector.clear();
+ attach_end._vector.clear();
+ }
+ previous_size = sicepv;
+ linkall.param_widget_is_enabled(sicepv > 1);
+ refresh_widgets = true;
+ }
+
+ // if no begin data
+ if (!attach_start._vector.size()) {
+ // add 0 to all paths
+ for (auto path : _pathvector_before_effect) {
+ attach_start._vector.push_back(0);
+ attach_end._vector.push_back(0);
+ }
+ attach_start.param_set_default();
+ attach_end.param_set_default();
+ write = true;
+ }
+ // if active subpath change
+ if (prev_subpath != subpath) {
+ attach_start.param_setActive(subpath - 1);
+ attach_end.param_setActive(subpath - 1);
+ prev_subpath = subpath;
+ refresh_widgets = true;
+ write = true;
+ }
+ std::vector attach_startv;
+ Geom::Path::size_type index = 0;
+ // for each data in begin
+ for (auto & doub : attach_start.data()) {
+ if (linkall) {
+ // we need to sinc, add all the same to all
+ attach_startv.push_back(attach_start._vector[subpath - 1]);
+ } else {
+ // if active subpath change and path is not current set value to negative to further ignore
+ if (prev_subpath != subpath && index != (int)subpath) {
+ attach_startv.push_back(-1);
+ } else {
+ attach_startv.push_back(doub);
+ }
+ }
+ index += 1;
+ }
+ std::vector attach_endv;
+ index = 0;
+ for (auto & doub : attach_end.data()) {
+ if (linkall) {
+ attach_endv.push_back(attach_end._vector[subpath - 1]);
+ } else {
+ // if active subpath change and path is not current set value to negative to further ignore
+ if (prev_subpath != subpath && index != (int)subpath) {
+ attach_endv.push_back(-1);
+ } else {
+ attach_endv.push_back(doub);
+ }
+ }
+ index += 1;
+ }
+ // if write or start or end are moved
+ if (write || (linkall && (
+ prev_attach_start != attach_start._vector[subpath - 1] ||
+ prev_attach_end != attach_end._vector[subpath - 1]
+ )))
+ {
+ attach_start.param_set_and_write_new_value(attach_startv);
+ attach_end.param_set_and_write_new_value(attach_endv);
+ }
+ prev_attach_start = attach_start._vector[subpath - 1];
+ prev_attach_end = attach_end._vector[subpath - 1];
+ pathv_out.clear();
+ if (_pathvector_before_effect.empty()) {
+ return;
+ }
+
+ index = 0;
+ // clear points
+ start_attach_point.clear();
+ end_attach_point.clear();
+ for (auto path : pathv) {
+ Geom::Path first_cusp = path;
+ Geom::Path last_cusp = path.reversed();
+ Geom::Coord path_length = path.length();
+ Geom::Piecewise< Geom::D2< Geom::SBasis > > first_cusp_pwd2 = first_cusp.toPwSb();
+ Geom::Piecewise< Geom::D2< Geom::SBasis > > last_cusp_pwd2 = last_cusp.toPwSb();
+
+ // there is a pretty good chance that people will try to drag the knots
+ // on top of each other, so block it
+
+ if (flexible) {
+ if (double(unsigned(attach_startv[index])) > 100.0) {
+ attach_startv[index] = 100.0;
+ }
+ if (double(unsigned(attach_endv[index])) > 100.0) {
+ attach_endv[index] = 100.0;
+ }
+ unsigned allowed_start = 100.0;
+ unsigned allowed_end = 100.0;
+
+ // don't let the knots be farther than they are allowed to be
+ {
+ if ((unsigned)attach_startv[index] >= allowed_start) {
+ attach_startv[index] = ((double)allowed_start);
+ }
+ if ((unsigned)attach_endv[index] >= allowed_end) {
+ attach_endv[index] = ((double)allowed_end);
+ }
+ }
+ }
+
+
+ Geom::Path path_tmp;
+ // Calculate length
+ double start_path_length;
+ double end_path_length;
+ if (flexible) {
+ start_path_length = (attach_startv[index] * path_length) / 100.0;
+ end_path_length = (attach_endv[index] * path_length) / 100.0;
+ } else {
+ start_path_length = std::min(path_length,attach_startv[index]);
+ end_path_length = std::min(path_length,attach_endv[index]);
+ }
+ // get positions
+ Geom::Coord new_pos_start = timeAtLength(start_path_length,first_cusp_pwd2);
+ Geom::Coord new_pos_end = timeAtLength(end_path_length,last_cusp_pwd2);
+ start_attach_point.push_back(first_cusp(new_pos_start));
+ end_attach_point.push_back(last_cusp(new_pos_end));
+ // do portioned
+ path_tmp = doEffect_simplePath(path, new_pos_start, new_pos_end);
+ pathv_out.push_back(path_tmp);
+ index++;
+ }
+ if (this != first) { // if LPE is not the first one add it to allow staking
+ for (auto path : pathvector_before_effect) {
+ pathv_out.push_back(path);
+ }
+ }
+ attach_startv.clear();
+ attach_endv.clear();
+}
+
+Geom::PathVector
+LPETrimShape::doEffect_path(Geom::PathVector const &path_in)
+{
+ return pathv_out;
+}
+
+void LPETrimShape::addKnotHolderEntities(KnotHolder *knotholder, SPItem *item)
+{
+ for (size_t i = 0 ; i < attach_start._vector.size(); i++) {
+ KnotHolderEntity *e = new TrimShapeNS::KnotHolderEntityAttach(this, i, true);
+ e->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:TrimShapeBegin",
+ _("Start point of the trim: drag to alter the trim"));
+ knotholder->add(e);
+
+ KnotHolderEntity *f = new TrimShapeNS::KnotHolderEntityAttach(this, i, false);
+ f->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:TrimShapeEnd",
+ _("End point of the trim: drag to alter the trim"));
+ knotholder->add(f);
+ }
+}
+
+namespace TrimShapeNS {
+
+void KnotHolderEntityAttach::knot_set(Geom::Point const &p, Geom::Point const&/*origin*/, guint state)
+{
+ using namespace Geom;
+ // check is valid index
+ if (_begin && (!valid_index(_index) || _effect->start_attach_point.size() <= _index)) {
+ return;
+ }
+
+ if (!_begin && (!valid_index(_index) || _effect->end_attach_point.size() <= _index)) {
+ return;
+ }
+
+ Geom::Point const s = snap_knot_position(p, state);
+
+ if (!is(_effect->sp_lpe_item)) {
+ g_warning("LPEItem is not a path!");
+ return;
+ }
+
+ if (!cast_unsafe(_effect->sp_lpe_item)->curve()) {
+ // oops
+ return;
+ }
+ // in case you are wondering, the above are simply sanity checks. we never want to actually
+ // use that object.
+
+ Geom::PathVector pathv = _effect->_pathvector_before_effect;
+ Piecewise > pwd2;
+ Geom::Path p_in;
+ if (_begin) {
+ p_in = pathv[_index];
+ } else {
+ p_in = pathv[_index].reversed();
+ }
+ // calculate lenght
+ Geom::Coord path_length = p_in.length();
+ pwd2.concat(p_in.toPwSb());
+ double t0 = nearest_time(s, pwd2);
+ // if the position is not very near to start or end
+ if (!Geom::are_near(0,t0,0.01)) {
+ // get the removed portion to measure
+ Geom::Path p_in_port = p_in.portion(0,t0);
+ Geom::Coord length = p_in_port.length();
+ if (_effect->flexible) {
+ t0 = (length * 100.0) / path_length;
+ } else {
+ t0 = length;
+ }
+ } else {
+ t0 = 0;
+ }
+ // if link all we use current subpath to
+ size_t pos = _index;
+ if (_effect->linkall) {
+ pos = _effect->subpath - 1;
+ }
+ if (_begin) {
+ _effect->attach_start._vector[pos] = t0;
+ _effect->attach_start.write_to_SVG();
+ } else {
+ _effect->attach_end._vector[pos] = t0;
+ _effect->attach_end.write_to_SVG();
+ }
+}
+
+void KnotHolderEntityAttach::knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, unsigned state)
+{
+ LPETrimShape *lpe = dynamic_cast(_effect);
+ lpe->makeUndoDone(_("Move handle"));
+ sp_lpe_item_update_patheffect(cast(item), false, false);
+}
+
+Geom::Point KnotHolderEntityAttach::knot_get() const
+{
+ if (!valid_index(_index)) {
+ return Geom::Point();
+ }
+ if (_begin && _effect && _effect->start_attach_point.size() > _index) {
+ return _effect->start_attach_point[_index];
+ }
+ if (!_begin && _effect && _effect->end_attach_point.size() > _index) {
+ return _effect->end_attach_point[_index];
+ }
+ return Geom::Point();
+}
+
+} // namespace TrimShapeNS
+} // namespace LivePathEffect
+} // namespace Inkscape
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offset:((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-trim-shape.h b/src/live_effects/lpe-trim-shape.h
new file mode 100644
index 0000000000000000000000000000000000000000..13d5228851a68842a9409269de607994a95c0f04
--- /dev/null
+++ b/src/live_effects/lpe-trim-shape.h
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author(s):
+ * Jabiertxo Arraiza Cenoz
+ *
+ * Copyright (C) 2014 Author(s)
+ *
+ * Jabiertxof:Thanks to all people help me
+ *
+ * Released under GNU GPL v2+, read the file 'COPYING' for more information.
+ */
+
+#ifndef INKSCAPE_LPE_TRIM_SHAPE_H
+#define INKSCAPE_LPE_TRIM_SHAPE_H
+
+#include "live_effects/effect.h"
+#include "live_effects/parameter/enum.h"
+#include "live_effects/parameter/enumarray.h"
+#include "live_effects/parameter/scalararray.h"
+#include "live_effects/parameter/parameter.h"
+
+namespace Inkscape {
+namespace LivePathEffect {
+
+namespace TrimShapeNS {
+// we need a separate namespace to avoid clashes with other LPEs
+// we use TrimShapeNS to follow similar usage in tapper stroke
+class KnotHolderEntityAttach;
+}
+
+class LPETrimShape : public Effect {
+public:
+ LPETrimShape(LivePathEffectObject *lpeobject);
+ ~LPETrimShape() override;
+
+ void doOnApply(SPLPEItem const* lpeitem) override;
+ void doBeforeEffect (SPLPEItem const* lpeitem) override;
+ Geom::PathVector doEffect_path (Geom::PathVector const& path_in) override;
+ Geom::Path doEffect_simplePath(Geom::Path const& path, double start, double end);
+
+ void addKnotHolderEntities(KnotHolder * knotholder, SPItem * item) override;
+protected:
+ friend class TrimShapeNS::KnotHolderEntityAttach;
+ ScalarArrayParam attach_start;
+ ScalarArrayParam attach_end;
+private:
+ ScalarParam subpath;
+ BoolParam linkall;
+ BoolParam flexible;
+ size_t previous_size = 0;
+ bool prevflex = false;
+ double prev_attach_start = -1;
+ double prev_attach_end = -1;
+ std::vector start_attach_point;
+ std::vector end_attach_point;
+ Geom::PathTime _factorTime(Geom::Path const path,Geom::Coord t) const;
+ double timeAtLength(double const A, Geom::Piecewise> pwd2);
+ size_t prev_subpath = Glib::ustring::npos;
+ Geom::PathVector _pathvector_before_effect;
+ Geom::PathVector pathv_out;
+ LPETrimShape(const LPETrimShape&) = delete;
+ LPETrimShape& operator=(const LPETrimShape&) = delete;
+};
+
+} //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/parameter/array.h b/src/live_effects/parameter/array.h
index dc262fedbede2b7529814068206d76a5e1dc99b1..59d2cfaa3c272c29ce6803ea086654593b7cc2be 100644
--- a/src/live_effects/parameter/array.h
+++ b/src/live_effects/parameter/array.h
@@ -23,11 +23,16 @@
namespace Inkscape::LivePathEffect {
-namespace TpS {
+namespace TapperStrokeNS {
// we need a separate namespace to avoid clashes with other LPEs
class KnotHolderEntityAttachBegin;
class KnotHolderEntityAttachEnd;
-} // namespace TpS
+} // namespace TapperStrokeNS
+
+namespace TrimShapeNS {
+// we need a separate namespace to avoid clashes with other LPEs
+class KnotHolderEntityAttach;
+} // namespace TrimShapeNS
template
class ArrayParam : public Parameter {
@@ -99,8 +104,9 @@ public:
bool valid_index(int index) const { return _vector.size() > index; }
protected:
- friend class TpS::KnotHolderEntityAttachBegin;
- friend class TpS::KnotHolderEntityAttachEnd;
+ friend class TapperStrokeNS::KnotHolderEntityAttachBegin;
+ friend class TapperStrokeNS::KnotHolderEntityAttachEnd;
+ friend class TrimShapeNS::KnotHolderEntityAttach;
std::vector _vector;
std::size_t _default_size;
diff --git a/src/live_effects/parameter/enumarray.h b/src/live_effects/parameter/enumarray.h
index 7a1069bb1201c52efd4bd4e2da7113f56ea740c3..84795a46534e318d1be5bbc6297ba5bbaabe61f4 100644
--- a/src/live_effects/parameter/enumarray.h
+++ b/src/live_effects/parameter/enumarray.h
@@ -94,6 +94,7 @@ public:
protected:
friend class LPETaperStroke;
+ friend class LPETrimShape;
private:
size_t _active_index = 0;
diff --git a/src/live_effects/parameter/scalararray.h b/src/live_effects/parameter/scalararray.h
index 33a3265600a90633e6e3d8e956950dd2e6530483..413aef001319c18bb4b4641fbd0e95d840154038 100644
--- a/src/live_effects/parameter/scalararray.h
+++ b/src/live_effects/parameter/scalararray.h
@@ -58,6 +58,7 @@ public:
private:
friend class LPETaperStroke;
+ friend class LPETrimShape;
size_t _active_index = 0;
double min;