Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-bendpath.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (C) Johan Engelen 2007 <j.b.c.engelen@utwente.nl>
4 * Copyright (C) Steren Giannini 2008 <steren.giannini@gmail.com>
5 *
6 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
7 */
8
10
11#include <vector>
12
13#include "inkscape.h"
14#include "preferences.h"
15#include "selection.h"
16#include "display/curve.h"
17#include "object/sp-lpe-item.h"
18#include "ui/knot/knot-holder.h"
20
21// TODO due to internal breakage in glibmm headers, this must be last:
22#include <glibmm/i18n.h>
23
24/* Theory in e-mail from J.F. Barraud
25Let B be the skeleton path, and P the pattern (the path to be deformed).
26
27P is a map t --> P(t) = ( x(t), y(t) ).
28B is a map t --> B(t) = ( a(t), b(t) ).
29
30The first step is to re-parametrize B by its arc length: this is the parametrization in which a point p on B is located by its distance s from start. One obtains a new map s --> U(s) = (a'(s),b'(s)), that still describes the same path B, but where the distance along B from start to
31U(s) is s itself.
32
33We also need a unit normal to the path. This can be obtained by computing a unit tangent vector, and rotate it by 90�. Call this normal vector N(s).
34
35The basic deformation associated to B is then given by:
36
37 (x,y) --> U(x)+y*N(x)
38
39(i.e. we go for distance x along the path, and then for distance y along the normal)
40
41Of course this formula needs some minor adaptations (as is it depends on the absolute position of P for instance, so a little translation is needed
42first) but I think we can first forget about them.
43*/
44
45namespace Inkscape {
46namespace LivePathEffect {
47
48namespace BeP {
49class KnotHolderEntityWidthBendPath : public LPEKnotHolderEntity {
50 public:
51 KnotHolderEntityWidthBendPath(LPEBendPath * effect) : LPEKnotHolderEntity(effect) {}
52 ~KnotHolderEntityWidthBendPath() override
53 {
54 if (auto const lpe = dynamic_cast<LPEBendPath *> (_effect)) {
55 lpe->_knotholder = nullptr;
56 }
57 }
58 void knot_set(Geom::Point const &p, Geom::Point const &origin, guint state) override;
59 Geom::Point knot_get() const override;
60 };
61} // BeP
62
64 Effect(lpeobject),
65 bend_path(_("Bend path:"), _("Path along which to bend the original path"), "bendpath", &wr, this, "M0,0 L1,0"),
66 original_height(0.0),
67 prop_scale(_("_Width:"), _("Width of the path"), "prop_scale", &wr, this, 1.0),
68 scale_y_rel(_("W_idth in units of length"), _("Scale the width of the path in units of its length"), "scale_y_rel", &wr, this, false),
69 vertical_pattern(_("_Original path is vertical"), _("Rotates the original 90 degrees, before bending it along the bend path"), "vertical", &wr, this, false),
70 hide_knot(_("Hide width knot"), _("Hide width knot"),"hide_knot", &wr, this, false)
71{
77
80
81 _knotholder = nullptr;
85}
86
88{
89 if (_knotholder) {
91 _knotholder = nullptr;
92 }
93}
94
95
96bool
98{
99 if (!is_load || is_applied) {
100 return false;
101 }
103 return false;
104}
105
106
107void
109{
110 // get the item bounding box
111 original_bbox(lpeitem, false, true);
113 if (is_load) {
115 }
116 if (_knotholder && !_knotholder->entity.empty()) {
117 if (hide_knot) {
119 _knotholder->entity.front()->knot->hide();
120 } else {
121 _knotholder->entity.front()->knot->show();
122 }
124 }
125}
126
127void LPEBendPath::transform_multiply(Geom::Affine const &postmul, bool /*set*/)
128{
129 Inkscape::Selection * selection = nullptr;
130 SPItem *linked = nullptr;
131 if (SP_ACTIVE_DESKTOP) {
132 selection = SP_ACTIVE_DESKTOP->getSelection();
133 linked = bend_path.getItem();
134 }
135 if (linked) {
136 linked->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
137 return;
138 }
140 bend_path.param_transform_multiply(postmul, false);
141 } else if( //this allow transform user spected way when lpeitem and pathparamenter are both selected
142 sp_lpe_item &&
144 linked &&
145 (selection->includes(linked)))
146 {
147 Geom::Affine transformlpeitem = sp_item_transform_repr(sp_lpe_item).inverse() * postmul;
148 sp_lpe_item->transform *= transformlpeitem.inverse();
149 sp_lpe_item->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
150 }
151}
152
155{
156 using namespace Geom;
157
158 /* Much credit should go to jfb and mgsloan of lib2geom development for the code below! */
160
161 if (bend_path.changed) {
163 uskeleton = remove_short_cuts(uskeleton,.01);
165 n = force_continuity(remove_short_cuts(n,.01));
166
167 bend_path.changed = false;
168 }
169
170 if (uskeleton.empty()) {
171 return pwd2_in;
172 }
173
174 D2<Piecewise<SBasis> > patternd2 = make_cuts_independent(pwd2_in);
177
180
181 //+0.1 in x fix bug #1658855
182 //We use the group bounding box size or the path bbox size to translate well x and y
183 x-= bboxHorizontal.min() + 0.1;
184 y-= bboxVertical.middle();
185
186 double scaling = uskeleton.cuts.back()/bboxHorizontal.extent();
187
188 if (scaling != 1.0) {
189 x*=scaling;
190 }
191
192 if ( scale_y_rel.get_value() ) {
193 y*=(scaling*prop_scale);
194 } else {
195 if (prop_scale != 1.0) y *= prop_scale;
196 }
197
198 Piecewise<D2<SBasis> > output = compose(uskeleton,x) + y*compose(n,x);
199 return output;
200}
201
202void
204{
206 original_bbox(cast<SPLPEItem>(item), false, true);
207
210
211 if ( Geom::are_near(start,end) ) {
212 end += Geom::Point(1.,0.);
213 }
214
215 Geom::Path path;
216 path.start( start );
218 bend_path.set_new_value( path.toPwSb(), true );
219}
220
221void
222LPEBendPath::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
223{
224 hp_vec.push_back(helper_path);
225}
226
227void
229{
230 _knotholder = knotholder;
232 knot_entity->create(nullptr, item, knotholder, Inkscape::CANVAS_ITEM_CTRL_TYPE_LPE, "LPE:WidthBend",
233 _("Change the width"));
234 _knotholder->add(knot_entity);
235 if (hide_knot) {
236 knot_entity->knot->hide();
237 knot_entity->update_knot();
238 }
239}
240
241namespace BeP {
242
243void
244KnotHolderEntityWidthBendPath::knot_set(Geom::Point const &p, Geom::Point const& /*origin*/, guint state)
245{
246 LPEBendPath *lpe = dynamic_cast<LPEBendPath *> (_effect);
247 if (!lpe)
248 return;
249
250 Geom::Point const s = snap_knot_position(p, state);
252 Geom::Point ptA = path_in.pointAt(Geom::PathTime(0, 0.0));
253 Geom::Point B = path_in.pointAt(Geom::PathTime(1, 0.0));
254 Geom::Curve const *first_curve = &path_in.curveAt(Geom::PathTime(0, 0.0));
255 Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*first_curve);
256 Geom::Ray ray(ptA, B);
257 if (cubic) {
258 ray.setPoints(ptA, (*cubic)[1]);
259 }
260 ray.setAngle(ray.angle() + Geom::rad_from_deg(90));
261 Geom::Point knot_pos = this->knot->pos * item->i2dt_affine().inverse();
262 Geom::Coord nearest_to_ray = ray.nearestTime(knot_pos);
263 if(nearest_to_ray == 0){
264 lpe->prop_scale.param_set_value(-Geom::distance(s , ptA)/(lpe->original_height/2.0));
265 } else {
267 }
268 if (!lpe->original_height) {
270 }
272 prefs->setDouble("/live_effects/bend_path/width", lpe->prop_scale);
273
274 sp_lpe_item_update_patheffect (cast<SPLPEItem>(item), false, true);
275}
276
278KnotHolderEntityWidthBendPath::knot_get() const
279{
280 LPEBendPath *lpe = dynamic_cast<LPEBendPath *> (_effect);
281 if (!lpe)
282 return Geom::Point(0, 0);
283
284 Geom::Path path_in = lpe->bend_path.get_pathvector().pathAt(Geom::PathVectorTime(0, 0, 0.0));
285 Geom::Point ptA = path_in.pointAt(Geom::PathTime(0, 0.0));
286 Geom::Point B = path_in.pointAt(Geom::PathTime(1, 0.0));
287 Geom::Curve const *first_curve = &path_in.curveAt(Geom::PathTime(0, 0.0));
288 Geom::CubicBezier const *cubic = dynamic_cast<Geom::CubicBezier const *>(&*first_curve);
289 Geom::Ray ray(ptA, B);
290 if (cubic) {
291 ray.setPoints(ptA, (*cubic)[1]);
292 }
293 ray.setAngle(ray.angle() + Geom::rad_from_deg(90));
294 Geom::Point result_point = Geom::Point::polar(ray.angle(), (lpe->original_height/2.0) * lpe->prop_scale) + ptA;
295 lpe->helper_path.clear();
296 if (!lpe->hide_knot) {
297 Geom::Path hp(result_point);
298 hp.appendNew<Geom::LineSegment>(ptA);
299 lpe->helper_path.push_back(hp);
300 hp.clear();
301 }
302 return result_point;
303}
304} // namespace BeP
305} // namespace LivePathEffect
306} /* namespace Inkscape */
307
308/*
309 Local Variables:
310 mode:c++
311 c-file-style:"stroustrup"
312 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
313 indent-tabs-mode:nil
314 fill-column:99
315 End:
316*/
317// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Point origin
Definition aa.cpp:227
3x3 matrix representing an affine transformation.
Definition affine.h:70
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Abstract continuous curve on a plane defined on [0,1].
Definition curve.h:78
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
constexpr C extent() const
constexpr C min() const
constexpr C max() const
constexpr C middle() const
Range of real numbers that is never empty.
Definition interval.h:59
void clear()
Remove all paths from the vector.
Definition pathvector.h:195
Path & pathAt(Coord t, Coord *rest=NULL)
Sequence of contiguous curves, aka spline.
Definition path.h:353
Piecewise< D2< SBasis > > toPwSb() const
Definition path.cpp:388
Point pointAt(Coord t) const
Get the point at the specified time value.
Definition path.cpp:449
Curve const & curveAt(Coord t, Coord *rest=NULL) const
Get the curve at the specified time value.
Definition path.cpp:440
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
void start(Point const &p)
Definition path.cpp:426
Function defined as discrete pieces.
Definition piecewise.h:71
bool empty() const
Definition piecewise.h:132
std::vector< double > cuts
Definition piecewise.h:75
Two-dimensional point that doubles as a vector.
Definition point.h:66
static Point polar(Coord angle, Coord radius)
Construct a point from its polar coordinates.
Definition point.h:88
Straight ray from a specific point to infinity.
Definition ray.h:53
void registerParameter(Parameter *param)
Definition effect.cpp:1710
virtual void resetDefaults(SPItem const *item)
Sets all parameters to their default values and writes them to SVG.
Definition effect.cpp:2014
void original_bbox(SPLPEItem const *lpeitem, bool absolute=false, bool clip_mask=false, Geom::Affine base_transform=Geom::identity())
friend class BeP::KnotHolderEntityWidthBendPath
void resetDefaults(SPItem const *item) override
Sets all parameters to their default values and writes them to SVG.
LPEBendPath(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::Piecewise< Geom::D2< Geom::SBasis > > uskeleton
Geom::Piecewise< Geom::D2< Geom::SBasis > > n
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
bool doOnOpen(SPLPEItem const *lpeitem) override
Is performed on load document or revert If the item is fixed legacy return true.
Geom::Piecewise< Geom::D2< Geom::SBasis > > doEffect_pwd2(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwd2_in) override
void addKnotHolderEntities(KnotHolder *knotholder, SPItem *item) override
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...
Geom::PathVector const & get_pathvector() const
Definition path.cpp:99
Geom::Piecewise< Geom::D2< Geom::SBasis > > const & get_pwd2()
Definition path.cpp:105
void set_new_value(Geom::PathVector const &newpath, bool write_to_svg)
Definition path.cpp:370
void param_transform_multiply(Geom::Affine const &postmul, bool set) override
Definition path.cpp:323
Geom::Affine get_relative_affine()
Definition path.cpp:86
void param_set_digits(unsigned digits)
void param_set_increments(double step, double page)
Preference storage class.
Definition preferences.h:66
static Preferences * get()
Access the singleton Preferences object.
void setDouble(Glib::ustring const &pref_path, double value)
Set a floating point value.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
bool includes(XML::Node *repr, bool anyAncestor=false)
Returns true if the given item is selected.
Definition selection.h:140
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)
virtual void update_knot()
void update_knots()
std::list< KnotHolderEntity * > entity
Definition knot-holder.h:85
void add(KnotHolderEntity *e)
Inkscape::LivePathEffect::Effect * _effect
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1828
Geom::Affine transform
Definition sp-item.h:138
void hide()
Hide knot on its canvas.
Definition knot.cpp:327
bool pathEffectsEnabled() const
bool optimizeTransforms()
returns false when LPE write unoptimiced
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
SPItem * item
Geom::Point start
Geom::Point end
Various utility functions.
Definition affine.h:22
D2< Piecewise< SBasis > > make_cuts_independent(Piecewise< D2< SBasis > > const &a)
Definition d2-sbasis.cpp:75
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
D2< T > compose(D2< T > const &a, T const &b)
Definition d2.h:405
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
Piecewise< D2< SBasis > > arc_length_parametrization(D2< SBasis > const &M, unsigned order=3, double tol=.01)
Piecewise< D2< SBasis > > force_continuity(Piecewise< D2< SBasis > > const &f, double tol=0, bool closed=false)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
Helper class to stream background task notifications as a series of messages.
@ CANVAS_ITEM_CTRL_TYPE_LPE
Singleton class to access the preferences file in a convenient way.
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:1507
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
Generalized time value in the path vector.
Definition pathvector.h:58