Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-perspective-envelope.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
5/*
6 * Authors:
7 * Jabiertxof Code migration from python extensions envelope and perspective
8 * Aaron Spike, aaron@ekips.org from envelope and perspective python code
9 * Dmitry Platonov, shadowjack@mail.ru, 2006 perspective approach & math
10 * Jose Hevia (freon) Transform algorithm from envelope
11 *
12 * Copyright (C) 2007-2014 Authors
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
18
19#include <glibmm/i18n.h>
20#include <gtkmm/box.h>
21#include <gtkmm/button.h>
22#include <gtkmm/label.h>
23#include <gtkmm/separator.h>
24#include <gtkmm/widget.h>
25#include <gsl/gsl_linalg.h>
26
27#include "display/curve.h"
28#include "helper/geom.h"
29#include "object/sp-lpe-item.h"
30#include "ui/pack.h"
31#include "ui/util.h"
32
33using namespace Geom;
34
36
41
43 {DEFORMATION_PERSPECTIVE , N_("Perspective"), "perspective"},
44 {DEFORMATION_ENVELOPE , N_("Envelope deformation"), "envelope_deformation"}
45};
46
48
50 Effect(lpeobject),
51 horizontal_mirror(_("Mirror movements in horizontal"), _("Mirror movements in horizontal"), "horizontal_mirror", &wr, this, false),
52 vertical_mirror(_("Mirror movements in vertical"), _("Mirror movements in vertical"), "vertical_mirror", &wr, this, false),
53 overflow_perspective(_("Overflow perspective"), _("Overflow perspective"), "overflow_perspective", &wr, this, false),
54 deform_type(_("Type"), _("Select the type of deformation"), "deform_type", DeformationTypeConverter, &wr, this, DEFORMATION_PERSPECTIVE),
55 up_left_point(_("Top Left"), _("Top Left - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "up_left_point", &wr, this),
56 up_right_point(_("Top Right"), _("Top Right - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "up_right_point", &wr, this),
57 down_left_point(_("Down Left"), _("Down Left - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "down_left_point", &wr, this),
58 down_right_point(_("Down Right"), _("Down Right - <b>Ctrl+Alt+Click</b>: reset, <b>Ctrl</b>: move along axes"), "down_right_point", &wr, this)
59{
60 // register all your parameters here, so Inkscape knows which parameters this effect has:
70}
71
73
83
84bool pointInTriangle(Geom::Point const &p, std::vector<Geom::Point> points)
85{
86 if (points.size() != 3) {
87 g_warning("Incorrect number of points in pointInTriangle\n");
88 return false;
89 }
90 Geom::Point p1 = points[0];
91 Geom::Point p2 = points[1];
92 Geom::Point p3 = points[2];
93 // http://totologic.blogspot.com.es/2014/01/accurate-point-in-triangle-test.html
94 using Geom::X;
95 using Geom::Y;
96 double denominator = (p1[X] * (p2[Y] - p3[Y]) + p1[Y] * (p3[X] - p2[X]) + p2[X] * p3[Y] - p2[Y] * p3[X]);
97 double t1 = (p[X] * (p3[Y] - p1[Y]) + p[Y] * (p1[X] - p3[X]) - p1[X] * p3[Y] + p1[Y] * p3[X]) / denominator;
98 double t2 = (p[X] * (p2[Y] - p1[Y]) + p[Y] * (p1[X] - p2[X]) - p1[X] * p2[Y] + p1[Y] * p2[X]) / -denominator;
99 double s = t1 + t2;
100
101 return 0 <= t1 && t1 <= 1 && 0 <= t2 && t2 <= 1 && s <= 1;
102}
103
105{
106 double projmatrix[3][3];
108 using Geom::X;
109 using Geom::Y;
110 std::vector<Geom::Point> source_handles(4);
111 source_handles[0] = Geom::Point(boundingbox_X.min(), boundingbox_Y.max());
112 source_handles[1] = Geom::Point(boundingbox_X.min(), boundingbox_Y.min());
113 source_handles[2] = Geom::Point(boundingbox_X.max(), boundingbox_Y.min());
114 source_handles[3] = Geom::Point(boundingbox_X.max(), boundingbox_Y.max());
115 double solmatrix[8][8] = {{0}};
116 double free_term[8] = {0};
117 double gslSolmatrix[64];
118 for(unsigned int i = 0; i < 4; ++i) {
119 solmatrix[i][0] = source_handles[i][X];
120 solmatrix[i][1] = source_handles[i][Y];
121 solmatrix[i][2] = 1;
122 solmatrix[i][6] = -handles[i][X] * source_handles[i][X];
123 solmatrix[i][7] = -handles[i][X] * source_handles[i][Y];
124 solmatrix[i+4][3] = source_handles[i][X];
125 solmatrix[i+4][4] = source_handles[i][Y];
126 solmatrix[i+4][5] = 1;
127 solmatrix[i+4][6] = -handles[i][Y] * source_handles[i][X];
128 solmatrix[i+4][7] = -handles[i][Y] * source_handles[i][Y];
129 free_term[i] = handles[i][X];
130 free_term[i+4] = handles[i][Y];
131 }
132 int h = 0;
133 for(auto & i : solmatrix) {
134 for(double j : i) {
135 gslSolmatrix[h] = j;
136 h++;
137 }
138 }
139 //this is get by this page:
140 //http://www.gnu.org/software/gsl/manual/html_node/Linear-Algebra-Examples.html#Linear-Algebra-Examples
141 gsl_matrix_view m = gsl_matrix_view_array (gslSolmatrix, 8, 8);
142 gsl_vector_view b = gsl_vector_view_array (free_term, 8);
143 gsl_vector *x = gsl_vector_alloc (8);
144 int s;
145 gsl_permutation * p = gsl_permutation_alloc (8);
146 gsl_linalg_LU_decomp (&m.matrix, p, &s);
147 gsl_linalg_LU_solve (&m.matrix, p, &b.vector, x);
148 h = 0;
149 for(auto & i : projmatrix) {
150 for(double & j : i) {
151 if(h==8) {
152 projmatrix[2][2] = 1.0;
153 continue;
154 }
155 j = gsl_vector_get(x, h);
156 h++;
157 }
158 }
159 gsl_permutation_free (p);
160 gsl_vector_free (x);
161 }
163 curve.clear();
164 Geom::CubicBezier const *cubic = nullptr;
165 Geom::Point point_at1(0, 0);
166 Geom::Point point_at2(0, 0);
167 Geom::Point point_at3(0, 0);
168 for (const auto & path_it : original_pathv) {
169 //Si está vacío...
170 if (path_it.empty())
171 continue;
172 //Itreadores
173 Geom::Path::const_iterator curve_it1 = path_it.begin();
174 Geom::Path::const_iterator curve_endit = path_it.end_default();
175
176 if (path_it.closed()) {
177 const Geom::Curve &closingline = path_it.back_closed();
178 if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
179 curve_endit = path_it.end_open();
180 }
181 }
182 auto nCurve = Geom::Path{
184 ? projectPoint(curve_it1->initialPoint(), projmatrix)
185 : projectPoint(curve_it1->initialPoint())
186 };
187 while (curve_it1 != curve_endit) {
188 cubic = dynamic_cast<Geom::CubicBezier const *>(&*curve_it1);
189 if (cubic) {
190 point_at1 = (*cubic)[1];
191 point_at2 = (*cubic)[2];
192 } else {
193 point_at1 = curve_it1->initialPoint();
194 point_at2 = curve_it1->finalPoint();
195 }
196 point_at3 = curve_it1->finalPoint();
198 point_at1 = projectPoint(point_at1, projmatrix);
199 point_at2 = projectPoint(point_at2, projmatrix);
200 point_at3 = projectPoint(point_at3, projmatrix);
201 } else {
202 point_at1 = projectPoint(point_at1);
203 point_at2 = projectPoint(point_at2);
204 point_at3 = projectPoint(point_at3);
205 }
206 if (cubic) {
207 nCurve.appendNew<Geom::CubicBezier>(point_at1, point_at2, point_at3);
208 } else {
209 nCurve.appendNew<Geom::LineSegment>(point_at3);
210 }
211 ++curve_it1;
212 }
213 //y cerramos la curva
214 if (path_it.closed()) {
215 move_endpoints(nCurve, point_at3, point_at3);
216 closepath_current(nCurve);
217 }
218 curve.push_back(std::move(nCurve));
219 }
220}
221
224{
225 double width = boundingbox_X.extent();
226 double height = boundingbox_Y.extent();
227 double delta_x = boundingbox_X.min() - p[X];
228 double delta_y = boundingbox_Y.max() - p[Y];
229 Geom::Coord x_ratio = (delta_x * -1) / width;
230 Geom::Coord y_ratio = delta_y / height;
231 Geom::Line horiz;
232 Geom::Line vert;
235
236 OptCrossing crossPoint = intersection(horiz,vert);
237 if(crossPoint) {
238 return horiz.pointAt(Geom::Coord(crossPoint->ta));
239 } else {
240 return p;
241 }
242}
243
246{
247 Geom::Coord x = p[0];
248 Geom::Coord y = p[1];
249 return Geom::Point(
250 Geom::Coord((x*m[0][0] + y*m[0][1] + m[0][2])/(x*m[2][0]+y*m[2][1]+m[2][2])),
251 Geom::Coord((x*m[1][0] + y*m[1][1] + m[1][2])/(x*m[2][0]+y*m[2][1]+m[2][2])));
252}
253
256{
257 Geom::Coord x = A[X] + (ratio * (B[X]-A[X]));
258 Geom::Coord y = A[Y]+ (ratio * (B[Y]-A[Y]));
259 return Point(x, y);
260}
261
262Gtk::Widget *
264{
265 // use manage here, because after deletion of Effect object, others might still be pointing to this widget.
266 auto const vbox = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::VERTICAL, 6);
267 vbox->set_margin(5);
268
269 auto const hbox_up_handles = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
270 auto const hbox_down_handles = Gtk::make_managed<Gtk::Box>(Gtk::Orientation::HORIZONTAL,0);
271
272 for (auto const param: param_vector) {
273 if (!param->widget_is_visible) continue;
274
275 auto const widg = param->param_newWidget();
276 if (!widg) continue;
277
278 if (param->param_key == "up_left_point" ||
279 param->param_key == "up_right_point" ||
280 param->param_key == "down_left_point" ||
281 param->param_key == "down_right_point")
282 {
283 auto &point_hbox = dynamic_cast<Gtk::Box &>(*widg);
284 auto const child_list = UI::get_children(point_hbox);
285 auto &point_hboxHBox = dynamic_cast<Gtk::Box &>(*child_list.at(0));
286 auto const child_list2 = UI::get_children(point_hboxHBox);
287 point_hboxHBox.remove(*child_list2.at(0));
288
289 if (param->param_key == "up_left_point") {
290 auto const handles = Gtk::make_managed<Gtk::Label>(Glib::ustring(_("Handles:")),Gtk::Align::START);
291 UI::pack_start(*vbox, *handles, false, false, 2);
292 UI::pack_start(*hbox_up_handles, *widg, true, true, 2);
293 UI::pack_start(*hbox_up_handles, *Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::VERTICAL),
295 } else if (param->param_key == "up_right_point") {
296 UI::pack_start(*hbox_up_handles, *widg, true, true, 2);
297 } else if (param->param_key == "down_left_point") {
298 UI::pack_start(*hbox_down_handles, *widg, true, true, 2);
299 UI::pack_start(*hbox_down_handles, *Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::VERTICAL),
301 } else {
302 UI::pack_start(*hbox_down_handles, *widg, true, true, 2);
303 }
304 }
305 else
306 {
307 UI::pack_start(*vbox, *widg, true, true, 2);
308 }
309
310 if (auto const tip = param->param_getTooltip()) {
311 widg->set_tooltip_markup(*tip);
312 } else {
313 widg->set_tooltip_text({});
314 widg->set_has_tooltip(false);
315 }
316 }
317
318 UI::pack_start(*vbox, *hbox_up_handles,true, true, 2);
319 UI::pack_start(*vbox, *Gtk::make_managed<Gtk::Separator>(Gtk::Orientation::HORIZONTAL), UI::PackOptions::expand_widget);
320 UI::pack_start(*vbox, *hbox_down_handles, true, true, 2);
321 auto const reset_button = Gtk::make_managed<Gtk::Button>(_("_Clear"), true);
322 reset_button->set_image_from_icon_name("edit-clear");
323 reset_button->signal_clicked().connect(sigc::mem_fun (*this,&LPEPerspectiveEnvelope::resetGrid));
324 reset_button->set_size_request(140,30);
325 reset_button->set_halign(Gtk::Align::START);
326 UI::pack_start(*vbox, *reset_button, false, false, 2);
327 return vbox;
328}
329
330void
332{
333 Geom::Point A = param_one;
334 Geom::Point B = param_two;
335 double Y = (A[Geom::Y] + B[Geom::Y])/2;
336 A[Geom::Y] = Y;
337 B[Geom::Y] = Y;
338 Geom::Point nearest = vert.pointAt(vert.nearestTime(A));
339 double distance_one = Geom::distance(A,nearest);
340 double distance_two = Geom::distance(B,nearest);
341 double distance_middle = (distance_one + distance_two)/2;
342 if(A[Geom::X] > B[Geom::X]) {
343 distance_middle *= -1;
344 }
345 A[Geom::X] = nearest[Geom::X] - distance_middle;
346 B[Geom::X] = nearest[Geom::X] + distance_middle;
347 param_one.param_setValue(A);
348 param_two.param_setValue(B);
349}
350
351void
353{
354 Geom::Point A = param_one;
355 Geom::Point B = param_two;
356 double X = (A[Geom::X] + B[Geom::X])/2;
357 A[Geom::X] = X;
358 B[Geom::X] = X;
359 Geom::Point nearest = horiz.pointAt(horiz.nearestTime(A));
360 double distance_one = Geom::distance(A,nearest);
361 double distance_two = Geom::distance(B,nearest);
362 double distance_middle = (distance_one + distance_two)/2;
363 if(A[Geom::Y] > B[Geom::Y]) {
364 distance_middle *= -1;
365 }
366 A[Geom::Y] = nearest[Geom::Y] - distance_middle;
367 B[Geom::Y] = nearest[Geom::Y] + distance_middle;
368 param_one.param_setValue(A);
369 param_two.param_setValue(B);
370}
371
372void
374{
375 original_bbox(lpeitem, false, true);
378 {
379 g_warning("Couldn`t apply perspective/envelope to a element with geometric width or height equal 0 we add a temporary bounding box to allow handle");
382 }
385 }
386 }
389 if(vertical_mirror) {
392 }
396 }
397 setDefaults();
400 g_warning(
401 "Perspective/Envelope LPE::doBeforeEffect - lpeobj with invalid parameter, the same value in 4 handles!");
402 resetGrid();
403 return;
404 }
406 if (!overflow_perspective && handles.size() == 4) {
407 bool move0 = false;
408 if (handles[0] != down_left_point) {
409 move0 = true;
410 }
411 bool move1 = false;
412 if (handles[1] != up_left_point) {
413 move1 = true;
414 }
415 bool move2 = false;
416 if (handles[2] != up_right_point) {
417 move2 = true;
418 }
419 bool move3 = false;
420 if (handles[3] != down_right_point) {
421 move3 = true;
422 }
423 handles.resize(4);
428 Geom::Line line_a(handles[3], handles[1]);
429 Geom::Line line_b(handles[1], handles[2]);
430 Geom::Line line_c(handles[2], handles[3]);
431 int position_a = Geom::sgn(Geom::cross(handles[3] - handles[1], handles[0] - handles[1]));
432 int position_b = Geom::sgn(Geom::cross(handles[1] - handles[2], handles[0] - handles[2]));
433 int position_c = Geom::sgn(Geom::cross(handles[2] - handles[3], handles[0] - handles[3]));
434 if (position_a != 1 && move0) {
435 Geom::Point point_a = line_a.pointAt(line_a.nearestTime(handles[0]));
436 down_left_point.param_setValue(point_a, true);
437 }
438 if (position_b == 1 && move0) {
439 Geom::Point point_b = line_b.pointAt(line_b.nearestTime(handles[0]));
440 down_left_point.param_setValue(point_b, true);
441 }
442 if (position_c == 1 && move0) {
443 Geom::Point point_c = line_c.pointAt(line_c.nearestTime(handles[0]));
444 down_left_point.param_setValue(point_c, true);
445 }
446 line_a.setPoints(handles[0], handles[2]);
447 line_b.setPoints(handles[2], handles[3]);
448 line_c.setPoints(handles[3], handles[0]);
449 position_a = Geom::sgn(Geom::cross(handles[0] - handles[2], handles[1] - handles[2]));
450 position_b = Geom::sgn(Geom::cross(handles[2] - handles[3], handles[1] - handles[3]));
451 position_c = Geom::sgn(Geom::cross(handles[3] - handles[0], handles[1] - handles[0]));
452 if (position_a != 1 && move1) {
453 Geom::Point point_a = line_a.pointAt(line_a.nearestTime(handles[1]));
454 up_left_point.param_setValue(point_a, true);
455 }
456 if (position_b == 1 && move1) {
457 Geom::Point point_b = line_b.pointAt(line_b.nearestTime(handles[1]));
458 up_left_point.param_setValue(point_b, true);
459 }
460 if (position_c == 1 && move1) {
461 Geom::Point point_c = line_c.pointAt(line_c.nearestTime(handles[1]));
462 up_left_point.param_setValue(point_c, true);
463 }
464 line_a.setPoints(handles[1], handles[3]);
465 line_b.setPoints(handles[3], handles[0]);
466 line_c.setPoints(handles[0], handles[1]);
467 position_a = Geom::sgn(Geom::cross(handles[1] - handles[3], handles[2] - handles[3]));
468 position_b = Geom::sgn(Geom::cross(handles[3] - handles[0], handles[2] - handles[0]));
469 position_c = Geom::sgn(Geom::cross(handles[0] - handles[1], handles[2] - handles[1]));
470 if (position_a != 1 && move2) {
471 Geom::Point point_a = line_a.pointAt(line_a.nearestTime(handles[2]));
472 up_right_point.param_setValue(point_a, true);
473 }
474 if (position_b == 1 && move2) {
475 Geom::Point point_b = line_b.pointAt(line_b.nearestTime(handles[2]));
476 up_right_point.param_setValue(point_b, true);
477 }
478 if (position_c == 1 && move2) {
479 Geom::Point point_c = line_c.pointAt(line_c.nearestTime(handles[2]));
480 up_right_point.param_setValue(point_c, true);
481 }
482 line_a.setPoints(handles[2], handles[0]);
483 line_b.setPoints(handles[0], handles[1]);
484 line_c.setPoints(handles[1], handles[2]);
485 position_a = Geom::sgn(Geom::cross(handles[2] - handles[0], handles[3] - handles[0]));
486 position_b = Geom::sgn(Geom::cross(handles[0] - handles[1], handles[3] - handles[1]));
487 position_c = Geom::sgn(Geom::cross(handles[1] - handles[2], handles[3] - handles[2]));
488 if (position_a != 1 && move3) {
489 Geom::Point point_a = line_a.pointAt(line_a.nearestTime(handles[3]));
490 down_right_point.param_setValue(point_a, true);
491 }
492 if (position_b == 1 && move3) {
493 Geom::Point point_b = line_b.pointAt(line_b.nearestTime(handles[3]));
494 down_right_point.param_setValue(point_b, true);
495 }
496 if (position_c == 1 && move3) {
497 Geom::Point point_c = line_c.pointAt(line_c.nearestTime(handles[3]));
498 down_right_point.param_setValue(point_c, true);
499 }
500 } else {
501 handles.resize(4);
506 }
507 }
508}
509
510void
533
534void
542
543void
545{
547 original_bbox(cast<SPLPEItem>(item), false, true);
548 setDefaults();
549 resetGrid();
550}
551
552void
553LPEPerspectiveEnvelope::addCanvasIndicators(SPLPEItem const */*lpeitem*/, std::vector<Geom::PathVector> &hp_vec)
554{
555 hp_vec.clear();
556
563 hp_vec.emplace_back(std::move(c));
564}
565
566} // namespace Inkscape::LivePathEffect
567
568/*
569 Local Variables:
570 mode:c++
571 c-file-style:"stroustrup"
572 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
573 indent-tabs-mode:nil
574 fill-column:99
575 End:
576*/
577// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
3x3 matrix representing an affine transformation.
Definition affine.h:70
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.
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
Infinite line on a plane.
Definition line.h:53
void setPoints(Point const &a, Point const &b)
Set a line based on two points it should pass through.
Definition line.h:168
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
Sequence of subpaths.
Definition pathvector.h:122
Sequence of contiguous curves, aka spline.
Definition path.h:353
void start(Point const &p)
Definition path.cpp:426
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 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...
void resetDefaults(SPItem const *item) override
Sets all parameters to their default values and writes them to SVG.
virtual Geom::Point pointAtRatio(Geom::Coord ratio, Geom::Point A, Geom::Point B)
Gtk::Widget * newWidget() override
This creates a managed widget.
void doBeforeEffect(SPLPEItem const *lpeitem) override
Is performed each time before the effect is updated.
virtual void vertical(PointParam &paramA, PointParam &paramB, Geom::Line vert)
void transform_multiply(Geom::Affine const &postmul, bool set) override
Overridden function to apply transforms for example to powerstroke, jointtype or tapperstroke.
virtual void horizontal(PointParam &paramA, PointParam &paramB, Geom::Line horiz)
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
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:42
Base class for visual SVG elements.
Definition sp-item.h:109
bool pathEffectsEnabled() const
bool optimizeTransforms()
returns false when LPE write unoptimiced
double c[8][4]
void move_endpoints(Geom::PathVector &pathv, Geom::Point const &new_p0, Geom::Point const &new_p1)
Sets start of first path to new_p0, and end of first path to new_p1.
Definition curve.cpp:188
void closepath_current(Geom::Path &path)
Close path by setting the end point to the start point instead of adding a new lineto.
Definition curve.cpp:37
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Definition geom.cpp:586
Specific geometry functions for Inkscape, not provided my lib2geom.
SPItem * item
LPE <perspective-envelope> implementation , see lpe-perspective-envelope.cpp.
Various utility functions.
Definition affine.h:22
int sgn(const T &x)
Sign function - indicates the sign of a numeric type.
Definition math-utils.h:51
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
std::optional< Crossing > OptCrossing
Definition crossing.h:64
OptCrossing intersection(Ray const &r1, Line const &l2)
Definition line.h:545
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Live Path Effects code.
static const Util::EnumDataConverter< unsigned > DeformationTypeConverter(DeformationTypeData, sizeof(DeformationTypeData)/sizeof(*DeformationTypeData))
bool pointInTriangle(Geom::Point const &p, std::vector< Geom::Point > points)
static const Util::EnumData< unsigned > DeformationTypeData[]
std::vector< Gtk::Widget * > get_children(Gtk::Widget &widget)
Get a vector of the widgetʼs children, from get_first_child() through each get_next_sibling().
Definition util.cpp:141
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.
Base class for live path effect items.
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:27
Definition curve.h:24
double height
double width