Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-lattice2.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * Johan Engelen <j.b.c.engelen@utwente.nl>
8 * Steren Giannini
9 * No� Falzon
10 * Victor Navez
11 * ~suv
12 * Jabiertxo Arraiza
13 *
14 * Copyright (C) 2007-2008 Authors
15 *
16 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
17 */
18
19#include "lpe-lattice2.h"
20
21#include <glibmm/i18n.h>
22#include <gtkmm/box.h>
23#include <gtkmm/button.h>
24#include <gtkmm/expander.h>
25#include <gtkmm/widget.h>
26
27#include <2geom/sbasis-2d.h>
29
30#include "helper/geom.h"
31#include "object/sp-lpe-item.h"
32#include "ui/pack.h"
33
34using namespace Geom;
35
36namespace Inkscape {
37namespace LivePathEffect {
38
40 Effect(lpeobject),
41 horizontal_mirror(_("Mirror movements in horizontal"), _("Mirror movements in horizontal"), "horizontal_mirror", &wr, this, false),
42 vertical_mirror(_("Mirror movements in vertical"), _("Mirror movements in vertical"), "vertical_mirror", &wr, this, false),
43 perimetral(_("Use only perimeter"), _("Use only perimeter"), "perimetral", &wr, this, false),
44 live_update(_("Update while moving knots (maybe slow)"), _("Update while moving knots (maybe slow)"), "live_update", &wr, this, true),
45 grid_point_0(_("Control 0:"), _("Control 0 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint0", &wr, this),
46 grid_point_1(_("Control 1:"), _("Control 1 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint1", &wr, this),
47 grid_point_2(_("Control 2:"), _("Control 2 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint2", &wr, this),
48 grid_point_3(_("Control 3:"), _("Control 3 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint3", &wr, this),
49 grid_point_4(_("Control 4:"), _("Control 4 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint4", &wr, this),
50 grid_point_5(_("Control 5:"), _("Control 5 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint5", &wr, this),
51 grid_point_6(_("Control 6:"), _("Control 6 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint6", &wr, this),
52 grid_point_7(_("Control 7:"), _("Control 7 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint7", &wr, this),
53 grid_point_8x9(_("Control 8x9:"), _("Control 8x9 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint8x9", &wr, this),
54 grid_point_10x11(_("Control 10x11:"), _("Control 10x11 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint10x11", &wr, this),
55 grid_point_12(_("Control 12:"), _("Control 12 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint12", &wr, this),
56 grid_point_13(_("Control 13:"), _("Control 13 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint13", &wr, this),
57 grid_point_14(_("Control 14:"), _("Control 14 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint14", &wr, this),
58 grid_point_15(_("Control 15:"), _("Control 15 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint15", &wr, this),
59 grid_point_16(_("Control 16:"), _("Control 16 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint16", &wr, this),
60 grid_point_17(_("Control 17:"), _("Control 17 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint17", &wr, this),
61 grid_point_18(_("Control 18:"), _("Control 18 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint18", &wr, this),
62 grid_point_19(_("Control 19:"), _("Control 19 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint19", &wr, this),
63 grid_point_20x21(_("Control 20x21:"), _("Control 20x21 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint20x21", &wr, this),
64 grid_point_22x23(_("Control 22x23:"), _("Control 22x23 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint22x23", &wr, this),
65 grid_point_24x26(_("Control 24x26:"), _("Control 24x26 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint24x26", &wr, this),
66 grid_point_25x27(_("Control 25x27:"), _("Control 25x27 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint25x27", &wr, this),
67 grid_point_28x30(_("Control 28x30:"), _("Control 28x30 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint28x30", &wr, this),
68 grid_point_29x31(_("Control 29x31:"), _("Control 29x31 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint29x31", &wr, this),
69 grid_point_32x33x34x35(_("Control 32x33x34x35:"), _("Control 32x33x34x35 - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "gridpoint32x33x34x35", &wr, this),
70 expanded(false)
71{
72 // register all your parameters here, so Inkscape knows which parameters this effect has:
103}
104
105LPELattice2::~LPELattice2() = default;
106
109{
110 PathVector pathv = path_from_piecewise(pwd2_in,0.001);
111 //this is because strange problems with sb2 and LineSegment
112 PathVector cubic = pathv_to_cubicbezier(pathv, true);
113 if (cubic.empty()) {
114 return pwd2_in;
115 }
116 Geom::Piecewise<Geom::D2<Geom::SBasis> > const &pwd2_in_linear_and_cubic = paths_to_pw(cubic);
117 D2<SBasis2d> sb2;
118
119 //Initialisation of the sb2
120 for(unsigned dim = 0; dim < 2; dim++) {
121 sb2[dim].us = 3;
122 sb2[dim].vs = 3;
123 const int depth = sb2[dim].us*sb2[dim].vs;
124 sb2[dim].resize(depth, Linear2d(0));
125 }
126
127 //Grouping the point params in a convenient vector
128
129 std::vector<Geom::Point > handles(36);
130
131 handles[0] = grid_point_0;
132 handles[1] = grid_point_1;
133 handles[2] = grid_point_2;
134 handles[3] = grid_point_3;
135 handles[4] = grid_point_4;
136 handles[5] = grid_point_5;
137 handles[6] = grid_point_6;
138 handles[7] = grid_point_7;
139 handles[8] = grid_point_8x9;
140 handles[9] = grid_point_8x9;
141 handles[10] = grid_point_10x11;
142 handles[11] = grid_point_10x11;
143 handles[12] = grid_point_12;
144 handles[13] = grid_point_13;
145 handles[14] = grid_point_14;
146 handles[15] = grid_point_15;
147 handles[16] = grid_point_16;
148 handles[17] = grid_point_17;
149 handles[18] = grid_point_18;
150 handles[19] = grid_point_19;
151 handles[20] = grid_point_20x21;
152 handles[21] = grid_point_20x21;
153 handles[22] = grid_point_22x23;
154 handles[23] = grid_point_22x23;
155 handles[24] = grid_point_24x26;
156 handles[25] = grid_point_25x27;
157 handles[26] = grid_point_24x26;
158 handles[27] = grid_point_25x27;
159 handles[28] = grid_point_28x30;
160 handles[29] = grid_point_29x31;
161 handles[30] = grid_point_28x30;
162 handles[31] = grid_point_29x31;
163 handles[32] = grid_point_32x33x34x35;
164 handles[33] = grid_point_32x33x34x35;
165 handles[34] = grid_point_32x33x34x35;
166 handles[35] = grid_point_32x33x34x35;
167
169
170 double width = boundingbox_X.extent();
171 double height = boundingbox_Y.extent();
172
173 //numbering is based on 4 rectangles.16
174 for(unsigned dim = 0; dim < 2; dim++) {
175 Geom::Point dir(0,0);
176 dir[dim] = 1;
177 for(unsigned vi = 0; vi < sb2[dim].vs; vi++) {
178 for(unsigned ui = 0; ui < sb2[dim].us; ui++) {
179 for(unsigned iv = 0; iv < 2; iv++) {
180 for(unsigned iu = 0; iu < 2; iu++) {
181 unsigned corner = iu + 2*iv;
182 unsigned i = ui + vi*sb2[dim].us;
183
184 //This is the offset from the Upperleft point
185 Geom::Point base( (ui + iu*(4-2*ui))*width/4.,
186 (vi + iv*(4-2*vi))*height/4.);
187
188 //Special action for corners
189 if(vi == 0 && ui == 0) {
190 base = Geom::Point(0,0);
191 }
192
193 // i = Upperleft corner of the considerated rectangle
194 // corner = actual corner of the rectangle
195 // origin = Upperleft point
196 double dl = dot((handles[corner+4*i] - (base + origin)), dir)/dot(dir,dir);
197 sb2[dim][i][corner] = dl/( dim ? height : width )*pow(4.0,ui+vi);
198 }
199 }
200 }
201 }
202 }
203
204 Piecewise<D2<SBasis> > output;
205 output.push_cut(0.);
206 for(unsigned i = 0; i < pwd2_in_linear_and_cubic.size(); i++) {
207 D2<SBasis> B = pwd2_in_linear_and_cubic[i];
208 B[Geom::X] -= origin[Geom::X];
209 B[Geom::X]*= 1/width;
210 B[Geom::Y] -= origin[Geom::Y];
211 B[Geom::Y]*= 1/height;
212 //Here comes the magic
213 D2<SBasis> tB = compose_each(sb2,B);
214 tB[Geom::X] = tB[Geom::X] * width + origin[Geom::X];
215 tB[Geom::Y] = tB[Geom::Y] * height + origin[Geom::Y];
216
217 output.push(tB,i+1);
218 }
219 return output;
220}
221
222
223Gtk::Widget *
225{
226 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
227 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
228 vbox->set_margin(5);
229
230 auto const hbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
231
232 auto const vbox_expander = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL);
233 vbox_expander->set_spacing(2);
234
235 auto const reset_button = Gtk::make_managed<Gtk::Button>(Glib::ustring(_("Reset grid")));
236 reset_button->signal_clicked().connect(sigc::mem_fun (*this,&LPELattice2::resetGrid));
237 reset_button->set_size_request(140,30);
238
239 UI::pack_start(*vbox, *hbox, true ,true, 2);
240 UI::pack_start(*hbox, *reset_button, false, false, 2);
241
242 std::vector<Parameter *>::iterator it = param_vector.begin();
243 while (it != param_vector.end()) {
244 if ((*it)->widget_is_visible) {
245 Parameter * param = *it;
246 auto widg = param->param_newWidget();
247 if(param->param_key == "grid") {
248 widg = nullptr;
249 }
250 if (widg) {
251 if (param->param_key == "horizontal_mirror" ||
252 param->param_key == "vertical_mirror" ||
253 param->param_key == "live_update" ||
254 param->param_key == "perimetral")
255 {
256 UI::pack_start(*vbox, *widg, true, true, 2);
257 } else {
258 UI::pack_start(*vbox_expander, *widg, true, true, 2);
259 }
260
261 if (auto const tip = param->param_getTooltip()) {
262 widg->set_tooltip_markup(*tip);
263 } else {
264 widg->set_tooltip_text("");
265 widg->set_has_tooltip(false);
266 }
267 }
268 }
269
270 ++it;
271 }
272
273 expander = Gtk::make_managed<Gtk::Expander>(Glib::ustring(_("Show Points")));
274 expander->set_child(*vbox_expander);
275 expander->set_expanded(expanded);
276 UI::pack_start(*vbox, *expander, true, true, 2);
277 expander->property_expanded().signal_changed().connect(sigc::mem_fun(*this, &LPELattice2::onExpanderChanged) );
278 return vbox;
279}
280
281void
283{
284 expanded = expander->get_expanded();
285 if(expanded) {
286 expander->set_label (Glib::ustring(_("Hide Points")));
287 } else {
288 expander->set_label (Glib::ustring(_("Show Points")));
289 }
290}
291void
293{
294 Geom::Point A = param_one;
295 Geom::Point B = param_two;
296 double Y = (A[Geom::Y] + B[Geom::Y])/2;
297 A[Geom::Y] = Y;
298 B[Geom::Y] = Y;
299 Geom::Point nearest = vert.pointAt(vert.nearestTime(A));
300 double distance_one = Geom::distance(A,nearest);
301 double distance_two = Geom::distance(B,nearest);
302 double distance_middle = (distance_one + distance_two)/2;
303 if(A[Geom::X] > B[Geom::X]) {
304 distance_middle *= -1;
305 }
306 A[Geom::X] = nearest[Geom::X] - distance_middle;
307 B[Geom::X] = nearest[Geom::X] + distance_middle;
308 param_one.param_setValue(A, live_update);
309 param_two.param_setValue(B, live_update);
310}
311
312void
314{
315 Geom::Point A = param_one;
316 Geom::Point B = param_two;
317 double X = (A[Geom::X] + B[Geom::X])/2;
318 A[Geom::X] = X;
319 B[Geom::X] = X;
320 Geom::Point nearest = horiz.pointAt(horiz.nearestTime(A));
321 double distance_one = Geom::distance(A,nearest);
322 double distance_two = Geom::distance(B,nearest);
323 double distance_middle = (distance_one + distance_two)/2;
324 if(A[Geom::Y] > B[Geom::Y]) {
325 distance_middle *= -1;
326 }
327 A[Geom::Y] = nearest[Geom::Y] - distance_middle;
328 B[Geom::Y] = nearest[Geom::Y] + distance_middle;
329 param_one.param_setValue(A, live_update);
330 param_two.param_setValue(B, live_update);
331}
332
333
334void
336{
337 original_bbox(lpeitem, false, true);
338 setDefaults();
339 if (is_applied) {
340 resetGrid();
341 }
344 if(vertical_mirror) {
355 }
367 }
368 if (perimetral) {
387 } else {
397 }
398}
399
400void
402{
405
408
411
414
417
420
423
426
429
432
435
438
441
444
447
450
453
456
459
462
465
468
471
474
477
528}
529
530void
559
560void
562{
564 original_bbox(cast<SPLPEItem>(item), false, true);
565 setDefaults();
566 resetGrid();
567}
568
569void
571{
572 using Geom::X;
573 using Geom::Y;
574 if(move) c.moveTo(a);
575 Geom::Point cubic1 = a + (1./3)* (b - a);
576 Geom::Point cubic2 = b + (1./3)* (a - b);
577 if (horizontal) {
578 c.curveTo(Geom::Point(cubic1[X],a[Y]),Geom::Point(cubic2[X],b[Y]),b);
579 } else {
580 c.curveTo(Geom::Point(a[X],cubic1[Y]),Geom::Point(b[X],cubic2[Y]),b);
581 }
582}
583
584void LPELattice2::addCanvasIndicators(SPLPEItem const *, std::vector<Geom::PathVector> &hp_vec)
585{
586 hp_vec.clear();
587
589 if (perimetral) {
594
599
604
609
610 } else {
615
620
625
630
635
640
645
650
655
660 }
661 c.flush();
662 hp_vec.push_back(c.peek());
663}
664
665} //namespace LivePathEffect
666} /* namespace Inkscape */
667
668/*
669 Local Variables:
670 mode:c++
671 c-file-style:"stroustrup"
672 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
673 indent-tabs-mode:nil
674 fill-column:99
675 End:
676*/
677// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Point origin
Definition aa.cpp:227
Conversion between Bezier control points and SBasis curves.
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
constexpr C extent() const
constexpr C min() const
constexpr C max() const
Infinite line on a plane.
Definition line.h:53
Coord nearestTime(Point const &p) const
Find a point on the line closest to the query point.
Definition line.h:252
Point pointAt(Coord t) const
Definition line.h:231
Store paths to a PathVector.
Definition path-sink.h:226
Sequence of subpaths.
Definition pathvector.h:122
bool empty() const
Check whether the vector contains any paths.
Definition pathvector.h:145
Function defined as discrete pieces.
Definition piecewise.h:71
unsigned size() const
Definition piecewise.h:131
void push(const T &s, double to)
Convenience/implementation hiding function to add segment/cut pairs.
Definition piecewise.h:141
void push_cut(double c)
Definition piecewise.h:152
Two-dimensional point that doubles as a vector.
Definition point.h:66
std::vector< Parameter * > param_vector
Definition effect.h:178
void registerParameter(Parameter *param)
Definition effect.cpp:1704
virtual void resetDefaults(SPItem const *item)
Sets all parameters to their default values and writes them to SVG.
Definition effect.cpp:2008
void original_bbox(SPLPEItem const *lpeitem, bool absolute=false, bool clip_mask=false, Geom::Affine base_transform=Geom::identity())
void horizontal(PointParam &paramA, PointParam &paramB, Geom::Line horiz)
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...
Gtk::Widget * newWidget() override
This creates a managed widget.
void calculateCurve(Geom::Point a, Geom::Point b, Geom::PathBuilder &c, bool horizontal, bool move)
void resetDefaults(SPItem const *item) override
Sets all parameters to their default values and writes them to SVG.
Geom::Piecewise< Geom::D2< Geom::SBasis > > doEffect_pwd2(Geom::Piecewise< Geom::D2< Geom::SBasis > > const &pwd2_in) override
void vertical(PointParam &paramA, PointParam &paramB, Geom::Line vert)
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
LPELattice2(LivePathEffectObject *lpeobject)
Glib::ustring const * param_getTooltip() const
Definition parameter.h:81
virtual Gtk::Widget * param_newWidget()=0
void param_update_default(Geom::Point default_point)
Definition point.cpp:64
Geom::Point param_get_default() const
Definition point.cpp:59
void param_setValue(Geom::Point newpoint, bool write=false)
Definition point.cpp:100
void param_set_liveupdate(bool live_update)
Definition point.cpp:53
Base class for visual SVG elements.
Definition sp-item.h:109
double c[8][4]
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
T pow(T const &t, int n)
Integer exponentiation for transforms.
Definition transforms.h:98
Geom::PathVector pathv_to_cubicbezier(Geom::PathVector const &pathv, bool nolines)
Definition geom.cpp:690
Specific geometry functions for Inkscape, not provided my lib2geom.
SPItem * item
LPE <lattice2> implementation, see lpe-lattice2.cpp.
Various utility functions.
Definition affine.h:22
Piecewise< D2< SBasis > > paths_to_pw(PathVector const &paths)
Definition path.cpp:1123
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
PathVector path_from_piecewise(Piecewise< D2< SBasis > > const &B, double tol, bool only_cubicbeziers=false)
Make a path from a d2 sbasis.
D2< T > compose_each(D2< T > const &a, D2< T > const &b)
Definition d2.h:414
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
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
Helper class to stream background task notifications as a series of messages.
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
Obsolete 2D SBasis function class.
Base class for live path effect items.
double height
double width