Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-transform_2pts.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * Jabier Arraiza Cenoz<jabier.arraiza@marker.es>
8 *
9 *
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include "lpe-transform_2pts.h"
14
15#include <gtkmm/box.h>
16#include <gtkmm/entry.h>
17#include <gtkmm/grid.h>
18
19#include "display/curve.h"
20#include "helper/geom.h"
21#include "object/sp-path.h"
22#include "svg/svg.h"
23#include "ui/icon-names.h"
24#include "ui/pack.h"
25#include "ui/util.h"
27
28// TODO due to internal breakage in glibmm headers, this must be last:
29#include <glibmm/i18n.h>
30
32
34 Effect(lpeobject),
35 elastic(_("Elastic"), _("Elastic transform mode"), "elastic", &wr, this, false),
36 from_original_width(_("From original width"), _("From original width"), "from_original_width", &wr, this, false),
37 lock_length(_("Lock length"), _("Lock length to current distance"), "lock_length", &wr, this, false),
38 lock_angle(_("Lock angle"), _("Lock angle"), "lock_angle", &wr, this, false),
39 flip_horizontal(_("Flip horizontal"), _("Flip horizontal"), "flip_horizontal", &wr, this, false),
40 flip_vertical(_("Flip vertical"), _("Flip vertical"), "flip_vertical", &wr, this, false),
41 start(_("Start"), _("Start point"), "start", &wr, this, "Start point"),
42 end(_("End"), _("End point"), "end", &wr, this, "End point"),
43 stretch(_("Stretch"), _("Stretch the result"), "stretch", &wr, this, 1),
44 offset(_("Offset"), _("Offset from knots"), "offset", &wr, this, 0),
45 first_knot(_("First Knot"), _("First Knot"), "first_knot", &wr, this, 1),
46 last_knot(_("Last Knot"), _("Last Knot"), "last_knot", &wr, this, 1),
47 helper_size(_("Helper size:"), _("Rotation helper size"), "helper_size", &wr, this, 3),
48 from_original_width_toggler(false),
49 point_a(Geom::Point()),
50 point_b(Geom::Point()),
51 pathvector(),
52 append_path(false),
53 previous_angle(Geom::rad_from_deg(0)),
54 previous_start(Geom::Point()),
55 previous_length(-1)
56{
70
76 offset.param_set_range(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max());
79 stretch.param_set_range(0, 999.0);
80 stretch.param_set_increments(0.01, 0.01);
83}
84
86
87void
89{
90 using namespace Geom;
91 original_bbox(lpeitem, false, true);
94 SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem);
95 auto sp_path = cast<SPPath>(splpeitem);
96 if (sp_path) {
97 pathvector = sp_path->curveForEdit()->get_pathvector();
98 }
99 if(!pathvector.empty()) {
104 }
105 size_t nnodes = nodeCount(pathvector);
106 // this is need to fix undo on apply
111 }
112
114 Geom::Ray transformed(point_a,point_b);
115 previous_angle = transformed.angle();
120}
121
122void LPETransform2Pts::transform_multiply(Geom::Affine const &postmul, bool /*set*/)
123{
125 start.param_transform_multiply(postmul, false);
126 end.param_transform_multiply(postmul, false);
127 }
128}
129
130void
132{
133 using namespace Geom;
134 original_bbox(lpeitem, false, true);
137
138 SPLPEItem * splpeitem = const_cast<SPLPEItem *>(lpeitem);
139 auto sp_path = cast<SPPath>(splpeitem);
140 if (sp_path) {
141 pathvector = sp_path->curveForEdit()->get_pathvector();
142 }
145 reset();
146 }
148 append_path = false;
151 size_t nnodes = nodeCount(pathvector);
156 }
157 } else {
158 if (first_knot != 1){
160 }
161 if (last_knot != 2){
163 }
166 append_path = false;
169 }
170 }
171 if(lock_length && !lock_angle && previous_length != -1) {
173 if(previous_start == start || previous_angle == Geom::rad_from_deg(0)) {
174 previous_angle = transformed.angle();
175 }
176 } else if(lock_angle && !lock_length && previous_angle != Geom::rad_from_deg(0)) {
177 if(previous_start == start){
179 }
180 }
181 if(lock_length || lock_angle ) {
182 Geom::Point end_point = Geom::Point::polar(previous_angle, previous_length) + (Geom::Point)start;
183 end.param_setValue(end_point);
184 }
186 previous_angle = transformed.angle();
189}
190
191void
193{
194 SPLPEItem * splpeitem = const_cast<SPLPEItem *>(sp_lpe_item);
195 auto sp_path = cast<SPPath>(splpeitem);
196 if (sp_path) {
197 pathvector = sp_path->curveForEdit()->get_pathvector();
198 }
199 if(pathvector.empty()) {
200 return;
201 }
213 // seems silly but fix undo resetting value on it
216 }
217 refresh_widgets = true;
218}
219//todo migrate to PathVector class?
220size_t
222{
223 size_t n = 0;
224 for (auto & it : pathvector) {
225 n += count_path_nodes(it);
226 }
227 return n;
228}
229//todo migrate to PathVector class?
232{
233 size_t n = 0;
234 for (auto & pv_it : pathvector) {
235 for (Geom::Path::iterator curve_it = pv_it.begin(); curve_it != pv_it.end_closed(); ++curve_it) {
236 if(index == n) {
237 return curve_it->initialPoint();
238 }
239 n++;
240 }
241 }
242 return Geom::Point();
243}
244//todo migrate to PathVector class? Not used
247{
248 size_t n = 0;
249 for (auto & pv_it : pathvector) {
250 for (Geom::Path::iterator curve_it = pv_it.begin(); curve_it != pv_it.end_closed(); ++curve_it) {
251 if(index == n) {
252 return pv_it;
253 }
254 n++;
255 }
256 }
257 return Geom::Path();
258}
259
260void
288
290{
291 // use manage here, because after deletion of Effect object, others might
292 // still be pointing to this widget.
293 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
294 vbox->set_margin(5);
295
296 auto const grid = Gtk::make_managed<Gtk::Grid>();
297 grid->set_column_spacing(50);
298 grid->set_row_spacing(6);
299
300 std::map<std::string, std::pair<int, int>> widget_positions = {
301 {"elastic", {0, 0}}, {"from_original_width", {1, 0}},
302 {"flip_vertical", {0, 1}}, {"flip_horizontal", {1, 1}},
303 {"lock_length", {0, 2}}, {"lock_angle", {1, 2}}
304 };
305
306 for (auto const param: param_vector) {
307 if (!param->widget_is_visible) continue;
308
309 auto const widg = param->param_newWidget();
310 if (!widg) continue;
311
312 auto parent = vbox;
313
314 if (param->param_key == "first_knot" || param->param_key == "last_knot") {
315 auto &scalar = dynamic_cast<UI::Widget::Scalar &>(*widg);
316 Gtk::manage(&scalar);
317 scalar.signal_value_changed().connect(sigc::mem_fun(*this, &LPETransform2Pts::updateIndex));
318 scalar.getSpinButton().set_width_chars(3);
319 } else if (widget_positions.find(param->param_key) != widget_positions.end()) {
320 auto [col, row] = widget_positions[param->param_key];
321 grid->attach(*widg, col, row, 1, 1);
322 parent = nullptr; // To avoid adding it to vbox later
323 }
324
325 // Add to vbox only if it's not in the grid
326 if(parent) {
327 UI::pack_start(*vbox, *widg, true, true, 2);
328 }
329
330 if (auto const tip = param->param_getTooltip()) {
331 widg->set_tooltip_markup(*tip);
332 } else {
333 widg->set_tooltip_text({});
334 widg->set_has_tooltip(false);
335 }
336 }
337
338 auto const reset = Gtk::make_managed<Gtk::Button>(Glib::ustring(_("Reset")));
339 reset->signal_clicked().connect(sigc::mem_fun(*this, &LPETransform2Pts::reset));
340 UI::pack_start(*vbox, *grid, true, true, 2); // Add grid to vbox
341 UI::pack_start(*vbox, *reset, true, true, 2); // Add reset button to vbox
342
343 return vbox;
344}
345
348{
353 double rot = transformed.angle() - original.angle();
354 Geom::Path helper;
355 helper.start(point_a);
357 Geom::Affine m;
358 Geom::Angle original_angle = original.angle();
360 m *= Geom::Rotate(-original_angle);
361 m *= Geom::Scale(-1,-1);
362 m *= Geom::Rotate(original_angle);
363 } else if(flip_vertical){
364 m *= Geom::Rotate(-original_angle);
365 m *= Geom::Scale(1,-1);
366 m *= Geom::Rotate(original_angle);
367 } else if(flip_horizontal){
368 m *= Geom::Rotate(-original_angle);
369 m *= Geom::Scale(-1,1);
370 m *= Geom::Rotate(original_angle);
371 }
372 if(stretch != 1){
373 m *= Geom::Rotate(-original_angle);
374 m *= Geom::Scale(1,stretch);
375 m *= Geom::Rotate(original_angle);
376 }
377 if(elastic) {
378 m *= Geom::Rotate(-original_angle);
379 if(sca > 1){
380 m *= Geom::Scale(sca, 1.0);
381 } else {
382 m *= Geom::Scale(sca, 1.0-((1.0-sca)/2.0));
383 }
384 m *= Geom::Rotate(transformed.angle());
385 } else {
386 m *= Geom::Scale(sca);
387 m *= Geom::Rotate(rot);
388 }
389 helper *= m;
390 Geom::Point trans = (Geom::Point)start - helper.initialPoint();
391 if(flip_horizontal){
392 trans = (Geom::Point)end - helper.initialPoint();
393 }
394 if(offset != 0){
395 trans = Geom::Point::polar(transformed.angle() + Geom::rad_from_deg(-90),offset) + trans;
396 }
397 m *= Geom::Translate(trans);
398
399 output.concat(pwd2_in * m);
400
401 return output;
402}
403
404void
405LPETransform2Pts::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
406{
407 using namespace Geom;
408 hp_vec.clear();
409 Geom::Path hp;
412 Geom::PathVector pathv;
413 pathv.push_back(hp);
414 double r = helper_size*.1;
415 if(lock_length || lock_angle ) {
416 char const * svgd;
417 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";
418 PathVector pathv_move = sp_svg_read_pathv(svgd);
419 pathv_move *= Affine(r,0,0,r,0,0) * Translate(Geom::Point(start));
420 hp_vec.push_back(pathv_move);
421 }
422 if(!lock_angle && lock_length) {
423 char const * svgd;
424 svgd = "M 0,9.94 C -2.56,9.91 -5.17,8.98 -7.07,7.07 c -3.91,-3.9 -3.91,-10.24 0,-14.14 1.97,-1.97 4.51,-3.02 7.07,-3.04 2.56,0.02 5.1,1.07 7.07,3.04 3.91,3.9 3.91,10.24 0,14.14 C 5.17,8.98 2.56,9.91 0,9.94 Z";
425 PathVector pathv_turn = sp_svg_read_pathv(svgd);
426 pathv_turn *= Geom::Rotate(previous_angle);
427 pathv_turn *= Affine(r,0,0,r,0,0) * Translate(Geom::Point(end));
428 hp_vec.push_back(pathv_turn);
429 }
430 hp_vec.push_back(pathv);
431}
432
433} // namespace Inkscape::LivePathEffect
434
435/*
436 Local Variables:
437 mode:c++
438 c-file-style:"stroustrup"
439 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
440 indent-tabs-mode:nil
441 fill-column:99
442 End:
443*/
444// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
3x3 matrix representing an affine transformation.
Definition affine.h:70
Wrapper for angular values.
Definition angle.h:73
virtual Point initialPoint() const =0
Retrieve the start of the curve.
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
constexpr C min() const
constexpr C max() const
constexpr C middle() const
Sequence of subpaths.
Definition pathvector.h:122
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
Point finalPoint() const
Get the last point in the last path of the vector.
Definition pathvector.h:222
Point initialPoint() const
Get the first point in the first path of the vector.
Definition pathvector.h:217
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
Sequence of contiguous curves, aka spline.
Definition path.h:353
Curve const & finalCurve() const
Definition path.h:462
Point initialPoint() const
Get the first point in the path.
Definition path.h:705
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
void concat(const Piecewise< T > &other)
Definition piecewise.h:235
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
Rotation around the origin.
Definition transforms.h:187
Scaling from the origin.
Definition transforms.h:150
Translation by a vector.
Definition transforms.h:115
void param_setValue(bool newvalue)
Definition bool.cpp:89
std::vector< Parameter * > param_vector
Definition effect.h:179
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())
size_t nodeCount(Geom::PathVector pathvector) const
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
Geom::Point pointAtNodeIndex(Geom::PathVector pathvector, size_t index) const
Gtk::Widget * newWidget() override
This creates a managed widget.
void doOnApply(SPLPEItem const *lpeitem) override
Is performed a single time when the effect is freshly applied to a path.
Geom::Piecewise< Geom::D2< Geom::SBasis > > doEffect_pwd2(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwd2_in) override
LPETransform2Pts(LivePathEffectObject *lpeobject)
Geom::Path pathAtNodeIndex(Geom::PathVector pathvector, size_t index) const
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 transform_multiply(Geom::Affine const &postmul, bool set) override
Overridden function to apply transforms for example to powerstroke, jointtype or tapperstroke.
void param_update_default(Geom::Point default_point)
Definition point.cpp:64
void param_setValue(Geom::Point newpoint, bool write=false)
Definition point.cpp:100
void param_transform_multiply(Geom::Affine const &, bool set) final
Definition point.cpp:145
void param_set_digits(unsigned digits)
void param_set_range(double min, double max)
void param_set_increments(double step, double page)
A labelled text box, with spin buttons and optional icon, for entering arbitrary number values.
Definition scalar.h:34
bool pathEffectsEnabled() const
bool optimizeTransforms()
returns false when LPE write unoptimiced
static char const *const parent
Definition dir-util.cpp:70
size_t count_path_nodes(Geom::Path const &path)
Definition geom.cpp:796
Specific geometry functions for Inkscape, not provided my lib2geom.
Macro for icon names used in Inkscape.
std::string original
LPE "Transform through 2 points" implementation.
double offset
Geom::Point start
Geom::Point end
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)
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
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Geom::PathVector sp_svg_read_pathv(char const *str)
Definition svg-path.cpp:37
int index