Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-offset.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * Maximilian Albert
8 * Jabiertxo Arraiza
9 *
10 * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
11 * Copyright (C) Maximilian Albert 2008 <maximilian.albert@gmail.com>
12 * Copyright (C) Jabierto Arraiza 2015 <jabier.arraiza@marker.es>
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17#include "lpe-offset.h"
18
19#include "display/curve.h"
21#include "helper/geom.h"
24#include "object/sp-lpe-item.h"
25#include "object/sp-shape.h"
27#include "ui/knot/knot-holder.h"
28#include "util/units.h"
29
30// TODO due to internal breakage in glibmm headers, this must be last:
31#include <glibmm/i18n.h>
32
33namespace Inkscape {
34namespace LivePathEffect {
35
36namespace OfS {
37 class KnotHolderEntityOffsetPoint : public LPEKnotHolderEntity {
38 public:
39 KnotHolderEntityOffsetPoint(LPEOffset * effect) : LPEKnotHolderEntity(effect) {}
40 ~KnotHolderEntityOffsetPoint() override
41 {
42 LPEOffset *lpe = dynamic_cast<LPEOffset *>(_effect);
43 if (lpe) {
44 lpe->_knotholder = nullptr;
45 }
46 }
47 void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
48 void knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state) override;
49 Geom::Point knot_get() const override;
50 private:
51 };
52} // OfS
53
54
56 // clang-format off
57 {JOIN_BEVEL, N_("Beveled"), "bevel"},
58 {JOIN_ROUND, N_("Rounded"), "round"},
59 {JOIN_MITER, N_("Miter"), "miter"},
60 {JOIN_MITER_CLIP, N_("Miter Clip"), "miter-clip"},
61 {JOIN_EXTRAPOLATE, N_("Extrapolated arc"), "extrp_arc"},
62 {JOIN_EXTRAPOLATE1, N_("Extrapolated arc Alt1"), "extrp_arc1"},
63 {JOIN_EXTRAPOLATE2, N_("Extrapolated arc Alt2"), "extrp_arc2"},
64 {JOIN_EXTRAPOLATE3, N_("Extrapolated arc Alt3"), "extrp_arc3"},
65 // clang-format on
66};
67
69
70
72 Effect(lpeobject),
73 unit(_("Unit"), _("Unit of measurement"), "unit", &wr, this, "mm"),
74 offset(_("Offset:"), _("Offset"), "offset", &wr, this, 0.0),
75 linejoin_type(_("Join:"), _("Determines the shape of the path's corners"), "linejoin_type", JoinTypeConverter, &wr, this, JOIN_MITER),
76 miter_limit(_("Miter limit:"), _("Maximum length of the miter join (in units of stroke width)"), "miter_limit", &wr, this, 4.0),
77 attempt_force_join(_("Force miter"), _("Overrides the miter limit and forces a join."), "attempt_force_join", &wr, this, false),
78 update_on_knot_move(_("Live update"), _("Update while moving handle"), "update_on_knot_move", &wr, this, true)
79{
80 show_orig_path = true;
90 _knotholder = nullptr;
94 liveknot = false;
96}
97
99 modified_connection.disconnect();
100 if (_knotholder) {
102 _knotholder = nullptr;
103 }
104};
105
106bool LPEOffset::doOnOpen(SPLPEItem const *lpeitem)
107{
108 if (!is_load || is_applied) {
109 return false;
110 }
111 Glib::ustring version = lpeversion.param_getSVGValue();
112 if (version < "1.3") {
113 lpeversion.param_setValue("1.3", true);
114 }
115 return false;
116}
117
118void
120{
121 lpeversion.param_setValue("1.3", true);
122}
123
124Glib::ustring
126 SPCSSAttr *css;
127 css = sp_repr_css_attr (obj->getRepr() , "style");
128 Glib::ustring val = sp_repr_css_property (css, "fill-rule", "");
130 return val;
131}
132
133void
135{
136 // we check for style changes and apply LPE to get appropiate fill rule on change
137 if (flags & SP_OBJECT_STYLE_MODIFIED_FLAG && obj) {
138 // Get the used fillrule
139 Glib::ustring fr = sp_get_fill_rule(obj);
140 FillRule fillrule_chan = fill_nonZero;
141 if (fr == "evenodd") {
142 fillrule_chan = fill_oddEven;
143 }
144 if (fillrule != fillrule_chan && sp_lpe_item) {
146 }
147 }
148}
149
151{
153 std::optional< Geom::PathVectorTime > pathvectortime = pathv.nearestTime(point);
154 if (pathvectortime) {
155 Geom::PathTime pathtime = pathvectortime->asPathTime();
156 res = pathv[(*pathvectortime).path_index].pointAt(pathtime.curve_index + pathtime.t);
157 }
158 return res;
159}
160
161void LPEOffset::transform_multiply(Geom::Affine const &postmul, bool /*set*/)
162{
163 refresh_widgets = true;
164 if (!postmul.isTranslation()) {
166 offset.param_transform_multiply(postmul * current_affine.inverse(), true);
167 }
168 offset_pt *= postmul;
169}
170
172{
174 Geom::OptRect bbox = pathv.boundsFast();
175 if (bbox) {
176 origin = Geom::Point((*bbox).midpoint()[Geom::X], (*bbox).top());
178 }
179 return origin;
180}
181
182double
184{
185 double ret_offset = 0;
187 std::optional< Geom::PathVectorTime > pathvectortime = mix_pathv_all.nearestTime(offset_pt);
188 if (pathvectortime) {
189 Geom::PathTime pathtime = pathvectortime->asPathTime();
190 Geom::Path npath = mix_pathv_all[(*pathvectortime).path_index];
191 res = npath.pointAt(pathtime.curve_index + pathtime.t);
192 if (npath.closed()) {
193 int winding_value = mix_pathv_all.winding(offset_pt);
194 bool inset = false;
195 if (winding_value % 2 != 0) {
196 inset = true;
197 }
198
199 ret_offset = Geom::distance(offset_pt, res);
200 if (inset) {
201 ret_offset *= -1;
202 }
203 } else {
204 ret_offset = Geom::distance(offset_pt, res);
205 if (!sign) {
206 ret_offset *= -1;
207 }
208 }
209 }
210 return Inkscape::Util::Quantity::convert(ret_offset, "px", unit.get_abbreviation()) * this->scale;
211}
212
213void
214LPEOffset::addCanvasIndicators(SPLPEItem const *lpeitem, std::vector<Geom::PathVector> &hp_vec)
215{
216 hp_vec.push_back(helper_path);
217}
218
219void
221{
222 auto obj = sp_lpe_item;
223 if (is_load && obj) {
224 modified_connection = obj->connectModified(sigc::mem_fun(*this, &LPEOffset::modified));
225 }
226 original_bbox(lpeitem);
227 auto group = cast<SPGroup>(sp_lpe_item);
228 if (group) {
230 }
231 this->scale = lpeitem->i2doc_affine().descrim();
232 if (!is_load && prev_unit != unit.get_abbreviation()) {
233 offset.param_set_undo(false);
235 } else {
237 }
239}
240
242{
244 if (_knotholder && !_knotholder->entity.empty()) {
245 _knotholder->entity.front()->knot_get();
246 }
247 }
248 if (is_load) {
250 }
251 if (_knotholder && !_knotholder->entity.empty() && sp_lpe_item && !liveknot) {
253 // we don do this on groups, editing is joining ito so no need to update knot
254 auto shape = cast<SPShape>(sp_lpe_item);
255 if (shape) {
256 out = cast<SPShape>(sp_lpe_item)->curve()->get_pathvector();
258 _knotholder->entity.front()->knot_get();
259 }
260 }
261}
262
265{
267 SPDocument *document = getSPDoc();
268 if (!item || !document) {
269 return path_in;
270 }
271 if (Geom::are_near(offset,0.0)) {
272 // this is to keep reference to multiple pathvectors like in a group. Used by knot position in LPE Offset
273 mix_pathv_all.insert(mix_pathv_all.end(), path_in.begin(), path_in.end());
275 offset_pt = get_default_point(path_in);
276 if (_knotholder && !_knotholder->entity.empty()) {
277 _knotholder->entity.front()->knot_get();
278 }
279 }
280 // allow offset 0 to get flattened version
281 }
282 // Get the used fillrule
283 Glib::ustring fr = sp_get_fill_rule(item);
285 if (fr == "evenodd") {
287 }
288 // ouline operations faster on live editing knot, on release it, get updated to -1
289 // todo study remove/change value. Use text paths to test if is needed
290 double tolerance = -1;
291 if (liveknot) {
292 tolerance = 3;
293 }
294 // Get the doc units offset
295 double to_offset = Inkscape::Util::Quantity::convert(offset, unit.get_abbreviation(), "px") / this->scale;
296 Geom::PathVector inputpv = path_in;
297 Geom::PathVector prev_mix_pathv_all;
298 Geom::PathVector prev_helper_path;
299 bool is_group = is<SPGroup>(sp_lpe_item);
300 // Pathvector used outside this function to calculate the offset
301 if (!is_group) {
304 }
306 double miterlimit = attempt_force_join ? std::numeric_limits<double>::max() : miter_limit;
307 Geom::Point point = offset_pt;
308 if (is_group) {
310 }
311 Geom::PathVector out = do_offset(inputpv, to_offset, tolerance, miterlimit, fillrule, join, point, helper_path, mix_pathv_all);
312 // we are fastering outline on expand
313 if (!prev_mix_pathv_all.empty()) {
314 mix_pathv_all = prev_mix_pathv_all;
315 helper_path = prev_helper_path;
316 }
317 return out;
318}
319
321{
322 _knotholder = knotholder;
323 KnotHolderEntity * knot_entity = new OfS::KnotHolderEntityOffsetPoint(this);
324 knot_entity->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE,
325 "LPEOffset", _("Offset point"));
326 knot_entity->knot->updateCtrl();
328 _knotholder->add(knot_entity);
329}
330
331namespace OfS {
332
333void KnotHolderEntityOffsetPoint::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state)
334{
335 using namespace Geom;
336 LPEOffset* lpe = dynamic_cast<LPEOffset *>(_effect);
337 lpe->offset_pt = snap_knot_position(p, state);
338 double offset = lpe->sp_get_offset();
339 if (lpe->update_on_knot_move) {
340 lpe->liveknot = true;
342 sp_lpe_item_update_patheffect (cast<SPLPEItem>(item), false, false);
343 } else {
344 lpe->liveknot = false;
345 }
346}
347
348void KnotHolderEntityOffsetPoint::knot_ungrabbed(Geom::Point const &p, Geom::Point const &origin, guint state)
349{
350 LPEOffset *lpe = dynamic_cast<LPEOffset *>(_effect);
351 lpe->liveknot = false;
352 using namespace Geom;
353 double offset = lpe->sp_get_offset();
354 lpe->offset.param_set_value(offset);
355 lpe->makeUndoDone(_("Move handle"));
356}
357
358Geom::Point KnotHolderEntityOffsetPoint::knot_get() const
359{
360 LPEOffset *lpe = dynamic_cast<LPEOffset *>(_effect);
361 if (!lpe) {
362 return Geom::Point();
363 }
364 if (!lpe->update_on_knot_move) {
365 return lpe->offset_pt;
366 }
367 if (lpe->offset_pt == Geom::Point(Geom::infinity(), Geom::infinity())) {
368 lpe->offset_pt = lpe->get_default_point(lpe->pathvector_after_effect);
369 }
370
371 return lpe->offset_pt;
372}
373
374} // namespace OfS
375} //namespace LivePathEffect
376} /* namespace Inkscape */
377
378/*
379 Local Variables:
380 mode:c++
381 c-file-style:"stroustrup"
382 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
383 indent-tabs-mode:nil
384 fill-column:99
385 End:
386*/
387// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
FillRule
Definition LivarotDefs.h:67
@ fill_oddEven
Definition LivarotDefs.h:68
@ fill_nonZero
Definition LivarotDefs.h:69
Point origin
Definition aa.cpp:227
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
bool isTranslation(Coord eps=EPSILON) const
Check whether this matrix represents a pure translation.
Definition affine.cpp:123
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Sequence of subpaths.
Definition pathvector.h:122
int winding(Point const &p) const
Determine the winding number at the specified point.
Point pointAt(Coord t) const
OptRect boundsFast() const
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
iterator insert(iterator pos, Path const &p)
Definition pathvector.h:179
std::optional< PathVectorTime > nearestTime(Point const &p, Coord *dist=NULL) const
iterator begin()
Definition pathvector.h:151
iterator end()
Definition pathvector.h:152
Sequence of contiguous curves, aka spline.
Definition path.h:353
bool closed() const
Check whether the path is closed.
Definition path.h:503
Point pointAt(Coord t) const
Get the point at the specified time value.
Definition path.cpp:449
Two-dimensional point that doubles as a vector.
Definition point.h:66
void registerParameter(Parameter *param)
Definition effect.cpp:1710
void original_bbox(SPLPEItem const *lpeitem, bool absolute=false, bool clip_mask=false, Geom::Affine base_transform=Geom::identity())
void param_setValue(Glib::ustring newvalue, bool write=false)
Definition hidden.cpp:71
Glib::ustring param_getSVGValue() const override
Definition hidden.cpp:53
void addCanvasIndicators(SPLPEItem const *lpeitem, std::vector< Geom::PathVector > &hp_vec) override
Add possible canvas indicators (i.e., helperpaths other than the original path) to hp_vec This functi...
void modified(SPObject *, guint flags)
friend class OfS::KnotHolderEntityOffsetPoint
Definition lpe-offset.h:53
LPEOffset(LivePathEffectObject *lpeobject)
void transform_multiply(Geom::Affine const &postmul, bool set) override
Overridden function to apply transforms for example to powerstroke, jointtype or tapperstroke.
Geom::Point get_default_point(Geom::PathVector pathv)
void doOnApply(SPLPEItem const *lpeitem) override
Is performed a single time when the effect is freshly applied to a path.
void doAfterEffect(SPLPEItem const *, SPCurve *curve) override
Is performed at the end of the LPE only one time per "lpeitem" in paths/shapes is called in middle of...
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override
bool doOnOpen(SPLPEItem const *lpeitem) override
Is performed on load document or revert If the item is fixed legacy return true.
EnumParam< unsigned > linejoin_type
Definition lpe-offset.h:58
void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override
void param_set_digits(unsigned digits)
void param_transform_multiply(Geom::Affine const &postmul, bool set) override
void param_set_increments(double step, double page)
const gchar * get_abbreviation() const
Definition unit.cpp:77
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:42
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
Definition units.cpp:525
KnotHolderEntity definition.
void create(SPDesktop *desktop, SPItem *item, KnotHolder *parent, Inkscape::CanvasItemCtrlType type=Inkscape::CANVAS_ITEM_CTRL_TYPE_DEFAULT, Glib::ustring const &name="unknown", char const *tip="", uint32_t color=0xffffff00)
std::list< KnotHolderEntity * > entity
Definition knot-holder.h:85
void add(KnotHolderEntity *e)
Inkscape::LivePathEffect::Effect * _effect
Wrapper around a Geom::PathVector object.
Definition curve.h:26
Typed SVG document implementation.
Definition document.h:103
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1816
void updateCtrl()
Update knot's control state.
Definition knot.cpp:389
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
sigc::connection connectModified(sigc::slot< void(SPObject *, unsigned int)> slot)
Connects to the modification notification signal.
Definition sp-object.h:705
std::shared_ptr< Css const > css
constexpr Coord infinity()
Get a value representing infinity.
Definition coord.h:88
@ X
Definition coord.h:48
Specific geometry functions for Inkscape, not provided my lib2geom.
SPItem * item
LPE <offset> implementation, see lpe-offset.cpp.
double offset
Various utility functions.
Definition affine.h:22
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Glib::ustring sp_get_fill_rule(SPObject *obj)
static const Util::EnumDataConverter< unsigned > JoinTypeConverter(JoinTypeData, sizeof(JoinTypeData)/sizeof(*JoinTypeData))
static const Util::EnumData< unsigned > JoinTypeData[]
Geom::Point get_nearest_point(Geom::PathVector pathv, Geom::Point point)
Helper class to stream background task notifications as a series of messages.
static Glib::ustring join(std::vector< Glib::ustring > const &accels, char const separator)
Geom::PathVector do_offset(Geom::PathVector const &path_in, double to_offset, double tolerance, double miter_limit, FillRule fillrule, Inkscape::LineJoinType join, Geom::Point point, Geom::PathVector &helper_path, Geom::PathVector &mix_pathv_all)
Create a user spected offset from a pathvector.
@ CANVAS_ITEM_CTRL_TYPE_LPE
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
Definition repr-css.cpp:88
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
Geom::Affine sp_item_transform_repr(SPItem *item)
Find out the inverse of previous transform of an item (from its repr)
Definition sp-item.cpp:1500
void sp_lpe_item_update_patheffect(SPLPEItem *lpeitem, bool wholetree, bool write, bool with_satellites)
Calls any registered handlers for the update_patheffect action.
Base class for live path effect items.
Generalized time value in the path.
Definition path.h:139
size_type curve_index
Index of the curve in the path.
Definition path.h:143
Coord t
Time value in the curve.
Definition path.h:142
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:27
Definition curve.h:24