Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-simplify.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
4 */
5
6#include "lpe-simplify.h"
7
9#include <glibmm/i18n.h>
10#include <gtkmm/box.h>
11#include <gtkmm/entry.h>
12
13#include "display/curve.h"
14#include "helper/geom.h"
15#include "object/sp-lpe-item.h"
16#include "path/path-util.h"
17#include "svg/svg.h"
18#include "ui/icon-names.h"
19#include "ui/pack.h"
20#include "ui/tools/node-tool.h"
21#include "ui/util.h"
23
25
27 : Effect(lpeobject)
28 , steps(_("Repeat"), _("Change number of repeats of simplifying operation. Useful for complex paths that need to be significantly simplified. "), "steps", &wr, this, 1)
29 , threshold(_("Complexity"), _("Drag slider to set the amount of simplification"), "threshold", &wr, this, 5)
30 , smooth_angles(_("Smoothness"), _("Max degree difference on handles to perform smoothing"), "smooth_angles",
31 &wr, this, 360.)
32 , helper_size(_("Handle size"), _("Size of the handles in the effect visualization (not editable)"), "helper_size", &wr, this, 10)
33 , simplify_individual_paths(_("Simplify paths separately"), _("Simplify each path individually. This maintains detail in complex shapes."), "simplify_individual_paths",
34 &wr, this, true)
35 , simplify_just_coalesce(_("Just coalesce"), _("Simplify just coalesce"), "simplify_just_coalesce", &wr, this,
36 false, "", INKSCAPE_ICON("on-outline"), INKSCAPE_ICON("off-outline"))
37{
44
45 threshold.addSlider(true);
47 steps.addSlider(true);
51
55
63}
64
66
67void
69{
70 if(!hp.empty()) {
71 hp.clear();
72 }
73 bbox = lpeitem->visualBounds();
75}
76
77void
97
98void
100{
101 lpeversion.param_setValue("1.3", true);
103}
104
105Gtk::Widget *
107{
108 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
109 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 2);
110 vbox->set_margin(5);
111
112 auto const buttons = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
113
114 for (auto const param: param_vector) {
115 if (!param->widget_is_visible) continue;
116
117 auto const widg = param->param_newWidget();
118 if (!widg) continue;
119
120 if (param->param_key == "simplify_just_coalesce") {
121 continue;
122 }
123
124 if (param->param_key == "simplify_individual_paths") {
125 UI::pack_start(*buttons, *widg, true, true, 2);
126 } else {
127 auto &scalar = dynamic_cast<UI::Widget::Scalar &>(*widg);
128 scalar.getSpinButton().set_width_chars(8);
129 UI::pack_start(*vbox, *widg, true, true, 2);
130 }
131
132 if (auto const tip = param->param_getTooltip()) {
133 widg->set_tooltip_markup(*tip);
134 } else {
135 widg->set_tooltip_text({});
136 widg->set_has_tooltip(false);
137 }
138 }
139
140 buttons->set_halign(Gtk::Align::START);
141 UI::pack_start(*vbox, *buttons, true, true, 2);
142 return vbox;
143}
144
145void
147{
148 Geom::PathVector const original_pathv = pathv_to_linear_and_cubic_beziers(curve->get_pathvector());
149 auto pathliv = Path_for_pathvector(original_pathv);
150
151 double size = Geom::L2(bbox->dimensions());
153 size = Geom::L2(Geom::bounds_fast(original_pathv)->dimensions());
154 }
156
157 auto const &version = lpeversion.param_getSVGValue();
158 int const factor = version < "1.3" ? 1 : 10000;
159
160 for (auto i = 0u; i < steps; ++i) {
162 pathliv->Coalesce((threshold / factor) * size);
163 } else {
164 pathliv->ConvertEvenLines((threshold / factor) * size);
165 pathliv->Simplify((threshold / factor) * size);
166 }
167 }
168
169 auto result = pathliv->MakePathVector();
171 curve->set_pathvector(result);
173}
174
175void
177{
178 if(steps < 1) {
179 return;
180 }
181 Geom::PathVector tmp_path;
182 Geom::CubicBezier const *cubic = nullptr;
183 for (auto & path_it : result) {
184 if (path_it.empty()) {
185 continue;
186 }
187
188 Geom::Path::iterator curve_it1 = path_it.begin(); // incoming curve
189 Geom::Path::iterator curve_it2 = ++(path_it.begin());// outgoing curve
190 Geom::Path::iterator curve_endit = path_it.end_default(); // this determines when the loop has to stop
191 SPCurve *nCurve = new SPCurve();
192 if (path_it.closed()) {
193 // if the path is closed, maybe we have to stop a bit earlier because the
194 // closing line segment has zerolength.
195 const Geom::Curve &closingline =
196 path_it.back_closed(); // the closing line segment is always of type
197 // Geom::LineSegment.
198 if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
199 // closingline.isDegenerate() did not work, because it only checks for
200 // *exact* zero length, which goes wrong for relative coordinates and
201 // rounding errors...
202 // the closing line segment has zero-length. So stop before that one!
203 curve_endit = path_it.end_open();
204 }
205 }
206 if(helper_size > 0) {
207 drawNode(curve_it1->initialPoint());
208 }
209 nCurve->moveto(curve_it1->initialPoint());
211 while (curve_it1 != curve_endit) {
212 cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
213 Geom::Point point_at1 = curve_it1->initialPoint();
214 Geom::Point point_at2 = curve_it1->finalPoint();
215 Geom::Point point_at3 = curve_it1->finalPoint();
216 Geom::Point point_at4 = curve_it1->finalPoint();
217
218 if(start == Geom::Point(0,0)) {
219 start = point_at1;
220 }
221
222 if (cubic) {
223 point_at1 = (*cubic)[1];
224 point_at2 = (*cubic)[2];
225 }
226
227 if(path_it.closed() && curve_it2 == curve_endit) {
228 point_at4 = start;
229 }
230 if(curve_it2 != curve_endit) {
231 cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it2);
232 if (cubic) {
233 point_at4 = (*cubic)[1];
234 }
235 }
236 Geom::Ray ray1(point_at2, point_at3);
237 Geom::Ray ray2(point_at3, point_at4);
238 double angle1 = Geom::deg_from_rad(ray1.angle());
239 double angle2 = Geom::deg_from_rad(ray2.angle());
240 if((smooth_angles >= std::abs(angle2 - angle1)) && !are_near(point_at4,point_at3) && !are_near(point_at2,point_at3)) {
241 double dist = Geom::distance(point_at2,point_at3);
242 Geom::Angle angleFixed = ray2.angle();
243 angleFixed -= Geom::Angle::from_degrees(180.0);
244 point_at2 = Geom::Point::polar(angleFixed, dist) + point_at3;
245 }
246 nCurve->curveto(point_at1, point_at2, curve_it1->finalPoint());
247 cubic = dynamic_cast<Geom::CubicBezier const *>(nCurve->last_segment());
248 if (cubic) {
249 point_at1 = (*cubic)[1];
250 point_at2 = (*cubic)[2];
251 if(helper_size > 0) {
252 if(!are_near((*cubic)[0],(*cubic)[1])) {
253 drawHandle((*cubic)[1]);
254 drawHandleLine((*cubic)[0],(*cubic)[1]);
255 }
256 if(!are_near((*cubic)[3],(*cubic)[2])) {
257 drawHandle((*cubic)[2]);
258 drawHandleLine((*cubic)[3],(*cubic)[2]);
259 }
260 }
261 }
262 if(helper_size > 0) {
263 drawNode(curve_it1->finalPoint());
264 }
265 ++curve_it1;
266 ++curve_it2;
267 }
268 if (path_it.closed()) {
269 nCurve->closepath_current();
270 }
271 tmp_path.push_back(nCurve->get_pathvector()[0]);
272 nCurve->reset();
273 delete nCurve;
274 }
275 result = tmp_path;
276}
277
278void
280{
281 double r = radius_helper_nodes;
282 char const * svgd;
283 svgd = "M 0.55,0.5 A 0.05,0.05 0 0 1 0.5,0.55 0.05,0.05 0 0 1 0.45,0.5 0.05,0.05 0 0 1 0.5,0.45 0.05,0.05 0 0 1 0.55,0.5 Z M 0,0 1,0 1,1 0,1 Z";
285 pathv *= Geom::Scale(r) * Geom::Translate(p - Geom::Point(0.5*r,0.5*r));
286 hp.push_back(pathv[0]);
287 hp.push_back(pathv[1]);
288}
289
290void
292{
293 double r = radius_helper_nodes;
294 char const * svgd;
295 svgd = "M 0.7,0.35 A 0.35,0.35 0 0 1 0.35,0.7 0.35,0.35 0 0 1 0,0.35 0.35,0.35 0 0 1 0.35,0 0.35,0.35 0 0 1 0.7,0.35 Z";
297 pathv *= Geom::Scale(r) * Geom::Translate(p - Geom::Point(0.35*r,0.35*r));
298 hp.push_back(pathv[0]);
299}
300
301void
303{
304 Geom::Path path;
305 path.start( p );
306 double diameter = radius_helper_nodes;
307 if(helper_size > 0 && Geom::distance(p,p2) > (diameter * 0.35)) {
308 Geom::Ray ray2(p, p2);
309 p2 = p2 - Geom::Point::polar(ray2.angle(),(diameter * 0.35));
310 }
311 path.appendNew<Geom::LineSegment>( p2 );
312 hp.push_back(path);
313}
314
315void
316LPESimplify::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
317{
318 hp_vec.push_back(hp);
319}
320
321} // namespace Inkscape::LivePathEffect
322
323/*
324 Local Variables:
325 mode:c++
326 c-file-style:"stroustrup"
327 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
328 indent-tabs-mode:nil
329 fill-column:99
330 End:
331*/
332// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Wrapper for angular values.
Definition angle.h:73
static Angle from_degrees(Coord d)
Create an angle from its measure in degrees.
Definition angle.h:136
Bezier curve with compile-time specified order.
Abstract continuous curve on a plane defined on [0,1].
Definition curve.h:78
virtual Point initialPoint() const =0
Retrieve the start of the curve.
virtual Point finalPoint() const =0
Retrieve the end of the curve.
Sequence of subpaths.
Definition pathvector.h:122
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
void clear()
Remove all paths from the vector.
Definition pathvector.h:195
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
Sequence of contiguous curves, aka spline.
Definition path.h:353
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
void start(Point const &p)
Definition path.cpp:426
Two-dimensional point that doubles as a vector.
Definition point.h:66
Straight ray from a specific point to infinity.
Definition ray.h:53
Coord angle() const
Definition ray.h:73
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
std::vector< Parameter * > param_vector
Definition effect.h:179
void registerParameter(Parameter *param)
Definition effect.cpp:1710
void update_helperpath()
Call to a method on nodetool to update the helper path from the effect.
Definition effect.cpp:1776
void param_setValue(Glib::ustring newvalue, bool write=false)
Definition hidden.cpp:71
Glib::ustring param_getSVGValue() const override
Definition hidden.cpp:53
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
void doOnApply(SPLPEItem const *lpeitem) override
Is performed a single time when the effect is freshly applied to a path.
Gtk::Widget * newWidget() override
This creates a managed widget.
void addCanvasIndicators(SPLPEItem const *, std::vector< Geom::PathVector > &hp_vec) override
Add possible canvas indicators (i.e., helperpaths other than the original path) to hp_vec This functi...
virtual void drawHandle(Geom::Point p)
void doEffect(SPCurve *curve) override
virtual void drawHandleLine(Geom::Point p, Geom::Point p2)
LPESimplify(LivePathEffectObject *lpeobject)
virtual void drawNode(Geom::Point p)
virtual void generateHelperPathAndSmooth(Geom::PathVector &result)
void param_set_digits(unsigned digits)
void param_set_range(double min, double max)
void param_set_increments(double step, double page)
void addSlider(bool add_slider_widget)
Definition parameter.h:140
A labelled text box, with spin buttons and optional icon, for entering arbitrary number values.
Definition scalar.h:34
SpinButton const & getSpinButton() const
Definition scalar.cpp:178
Wrapper around a Geom::PathVector object.
Definition curve.h:26
Geom::Curve const * last_segment() const
Return last pathsegment (possibly the closing path segment) of the last path in PathVector or NULL.
Definition curve.cpp:289
void moveto(Geom::Point const &p)
Perform a moveto to a point, thus starting a new subpath.
Definition curve.cpp:138
void reset()
Set curve to empty curve.
Definition curve.cpp:118
Geom::PathVector const & get_pathvector() const
Definition curve.cpp:52
void closepath_current()
Like SPCurve::closepath() but sets the end point of the last subpath to the subpath start point inste...
Definition curve.cpp:224
void curveto(Geom::Point const &p0, Geom::Point const &p1, Geom::Point const &p2)
Adds a bezier segment to the current subpath.
Definition curve.cpp:190
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1823
Geom::OptRect visualBounds(Geom::Affine const &transform=Geom::identity(), bool wfilter=true, bool wclip=true, bool wmask=true) const
Get item's visual bounding box in this item's coordinate system.
Definition sp-item.cpp:932
Css & result
constexpr Coord infinity()
Get a value representing infinity.
Definition coord.h:88
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Definition geom.cpp:588
Specific geometry functions for Inkscape, not provided my lib2geom.
Macro for icon names used in Inkscape.
Geom::Point start
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
OptInterval bounds_fast(Bezier const &b)
Definition bezier.cpp:305
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Live Path Effects code.
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
New node tool with support for multiple path editing.
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
std::unique_ptr< Path > Path_for_pathvector(Geom::PathVector const &pathv)
Creates a Livarot Path object from the Geom::PathVector.
Definition path-util.cpp:25
Path utilities.
Base class for live path effect items.
Definition curve.h:24
parse SVG path specifications
Geom::PathVector sp_svg_read_pathv(char const *str)
Definition svg-path.cpp:37
Geom::IntPoint dimensions(const Cairo::RefPtr< Cairo::ImageSurface > &surface)
Definition util.cpp:367