Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
lpe-pts2ellipse.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
6/*
7 * Authors:
8 * Markus Schwienbacher
9 *
10 * Copyright (C) Markus Schwienbacher 2013 <mschwienbacher@gmail.com>
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include "lpe-pts2ellipse.h"
16
17
19#include <object/sp-item.h>
20#include <object/sp-path.h>
21#include <object/sp-shape.h>
22#include <svg/svg.h>
23
24#include <2geom/circle.h>
25#include <2geom/ellipse.h>
27#include <2geom/path.h>
28#include <2geom/pathvector.h>
29
30#include <glib/gi18n.h>
31
32namespace Inkscape {
33namespace LivePathEffect {
34
36 { EM_AUTO, N_("Auto ellipse"), "auto" },
37 { EM_CIRCLE, N_("Force circle"), "circle" },
38 { EM_ISOMETRIC_CIRCLE, N_("Isometric circle"), "iso_circle" },
40 { EM_PERSPECTIVE_CIRCLE, N_("Perspective circle"), "perspective_circle" },
43 { EM_STEINER_ELLIPSE, N_("Steiner ellipse"), "steiner_ellipse" },
45 { EM_STEINER_INELLIPSE, N_("Steiner inellipse"), "steiner_inellipse" }
47};
49
51 : Effect(lpeobject)
52 , method(
53 _("Method:"),
54 _("Methods to generate the ellipse\n- Auto ellipse: fits a circle (2, 3 or 4 nodes in the path) or an ellipse (at least 5 "
55 "nodes)\n- Force circle: (at least 2 nodes) always create a circle\n- Isometric circle: (3 nodes) use "
56 "first two segments as edges\n- Perspective circle: (4 nodes) circle in a square in perspective view\n- Steiner "
57 "ellipse: (3 nodes) ellipse on a triangle\n- Steiner inellipse: (3 nodes) ellipse inside a triangle"),
58 "method", EMConverter, &wr, this, EM_AUTO)
59 , gen_isometric_frame(_("_Frame (isometric rectangle)"), _("Draw parallelogram around the ellipse"),
60 "gen_isometric_frame", &wr, this, false)
61 , gen_perspective_frame(
62 _("_Perspective square"),
63 _("Draw square surrounding the circle in perspective view\n(only in method \"Perspective circle\")"),
64 "gen_perspective_frame", &wr, this, false)
65 , gen_arc(_("_Arc"),
66 _("Generate open arc (open ellipse) based on first and last node\n(only for methods \"Auto ellipse\" "
67 "and \"Force circle\")"),
68 "gen_arc", &wr, this, false)
69 , other_arc(_("_Other arc side"), _("Switch sides of the arc"), "arc_other", &wr, this, false)
70 , slice_arc(_("_Slice arc"), _("Create a circle / ellipse segment"), "slice_arc", &wr, this, false)
71 , draw_axes(_("A_xes"), _("Draw both semi-major and semi-minor axes"), "draw_axes", &wr, this, false)
72 , draw_perspective_axes(_("Perspective axes"),
73 _("Draw the axes in perspective view\n(only in method \"Perspective circle\")"),
74 "draw_perspective_axes", &wr, this, false)
75 , rot_axes(_("Axes rotation"), _("Axes rotation angle [deg]"), "rot_axes", &wr, this, 0)
76 , draw_ori_path(_("Source _path"), _("Show the original source path"), "draw_ori_path", &wr, this, false)
77{
88
89 rot_axes.param_set_range(-360, 360);
91
92 show_orig_path = true;
93
94 gsl_x = gsl_vector_alloc(8);
95 gsl_p = gsl_permutation_alloc(8);
96}
97
99 gsl_permutation_free(gsl_p);
100 gsl_vector_free(gsl_x);
101}
102
103// helper function, transforms a given value into range [0, 2pi]
104inline double range2pi(double a)
105{
106 a = fmod(a, 2 * M_PI);
107 if (a < 0) {
108 a += 2 * M_PI;
109 }
110 return a;
111}
112
113inline double deg2rad(double a) { return a * M_PI / 180.0; }
114
115inline double rad2deg(double a) { return a * 180.0 / M_PI; }
116
117// helper function, calculates the angle between a0 and a1 in ccw sense
118// examples: 0..1->1, -1..1->2, pi/4..-pi/4->1.5pi
119// full rotations: 0..2pi->2pi, -pi..pi->2pi, pi..-pi->0, 2pi..0->0
120inline double calc_delta_angle(const double a0, const double a1)
121{
122 double da = range2pi(a1 - a0);
123 if ((fabs(da) < 1e-9) && (a0 < a1)) {
124 da = 2 * M_PI;
125 }
126 return da;
127}
128
129int LPEPts2Ellipse::unit_arc_path(Geom::Path &path_in, Geom::Affine &affine, double start, double end, bool slice)
130{
131 double arc_angle = calc_delta_angle(start, end);
132 if (fabs(arc_angle) < 1e-9) {
133 g_warning("angle was 0");
134 return -1;
135 }
136
137 // the delta angle
138 double da = M_PI_2;
139 // number of segments with da length
140 int nda = (int)ceil(arc_angle / M_PI_2);
141 // recalculate da
142 da = arc_angle / (double)nda;
143
144 bool closed = false;
145 if (fabs(arc_angle - 2 * M_PI) < 1e-8) {
146 closed = true;
147 da = M_PI_2;
148 nda = 4;
149 }
150
151 double s = range2pi(start);
152 end = s + arc_angle;
153
154 double x0 = cos(s);
155 double y0 = sin(s);
156 // construct the path
157 Geom::Path path(Geom::Point(x0, y0));
158 path.setStitching(true);
159 for (int i = 0; i < nda;) {
160 double e = s + da;
161 if (e > end) {
162 e = end;
163 }
164 const double len = 4 * tan((e - s) / 4) / 3;
165 const double x1 = x0 + len * cos(s + M_PI_2);
166 const double y1 = y0 + len * sin(s + M_PI_2);
167 const double x3 = cos(e);
168 const double y3 = sin(e);
169 const double x2 = x3 + len * cos(e - M_PI_2);
170 const double y2 = y3 + len * sin(e - M_PI_2);
171 path.appendNew<Geom::CubicBezier>(Geom::Point(x1, y1), Geom::Point(x2, y2), Geom::Point(x3, y3));
172 s = (++i) * da + start;
173 x0 = cos(s);
174 y0 = sin(s);
175 }
176
177 if (slice && !closed) {
178 path.appendNew<Geom::LineSegment>(Geom::Point(0.0, 0.0));
179 }
180 path *= affine;
181
182 path_in.append(path);
183 if ((slice && !closed) || closed) {
184 path_in.close(true);
185 }
186 return 0;
187}
188
190{
191 Geom::Path rect(Geom::Point(-1, -1));
192 rect.setStitching(true);
196 rect *= affine;
197 rect.close(true);
198 path_out.push_back(rect);
199}
200
202 double projmatrix[3][3])
203{
204 Geom::Point pts0[4] = { { -1.0, -1.0 }, { +1.0, -1.0 }, { +1.0, +1.0 }, { -1.0, +1.0 } };
205 // five_pts.resize(4);
206 Geom::Affine affine2;
207 // const double rot_angle = deg2rad(rot_axes); // negative for ccw rotation
208 affine2 *= Geom::Rotate(-rot_angle);
209 for (auto &i : pts0) {
210 Geom::Point point = i;
211 point *= affine2;
212 i = projectPoint(point, projmatrix);
213 }
214
215 Geom::Path rect(pts0[0]);
216 rect.setStitching(true);
217 for (int i = 1; i < 4; i++)
218 rect.appendNew<Geom::LineSegment>(pts0[i]);
219 rect.close(true);
220 path_out.push_back(rect);
221}
222
224{
225 Geom::LineSegment clx(Geom::Point(-1, 0), Geom::Point(1, 0));
226 Geom::LineSegment cly(Geom::Point(0, -1), Geom::Point(0, 1));
227
228 Geom::Path plx, ply;
229 plx.append(clx);
230 ply.append(cly);
231 plx *= affine;
232 ply *= affine;
233
234 path_out.push_back(plx);
235 path_out.push_back(ply);
236}
237
239 double projmatrix[3][3])
240{
241 Geom::Point pts[4];
242 int h = 0;
243 double dA = 2.0 * M_PI / 4.0; // delta Angle
244 for (auto &i : pts) {
245 const double angle = rot_angle + dA * h++;
246 const Geom::Point circle_point(sin(angle), cos(angle));
247 i = projectPoint(circle_point, projmatrix);
248 }
249 {
250 Geom::LineSegment clx(pts[0], pts[2]);
251 Geom::LineSegment cly(pts[1], pts[3]);
252
253 Geom::Path plx, ply;
254 plx.append(clx);
255 ply.append(cly);
256
257 path_out.push_back(plx);
258 path_out.push_back(ply);
259 }
260}
261
262bool LPEPts2Ellipse::is_ccw(const std::vector<Geom::Point> &pts)
263{
264 // method: sum up the angles between edges
265 size_t n = pts.size();
266 // edges about vertex 0
267 Geom::Point e0(pts.front() - pts.back());
268 Geom::Point e1(pts[1] - pts[0]);
269 Geom::Coord sum = cross(e0, e1);
270 // the rest
271 for (size_t i = 1; i < n; i++) {
272 e0 = e1;
273 e1 = pts[i] - pts[i - 1];
274 sum += cross(e0, e1);
275 }
276 // edges about last vertex (closing)
277 e0 = e1;
278 e1 = pts.front() - pts.back();
279 sum += cross(e0, e1);
280
281 // close the
282 if (sum < 0) {
283 return true;
284 } else {
285 return false;
286 }
287}
288
289void endpoints2angles(const bool ccw_wind, const bool use_other_arc, const Geom::Point &p0, const Geom::Point &p1,
290 Geom::Coord &a0, Geom::Coord &a1)
291{
292 if (!p0.isZero() && !p1.isZero()) {
293 a0 = atan2(p0);
294 a1 = atan2(p1);
295 if (!ccw_wind) {
296 std::swap(a0, a1);
297 }
298 if (!use_other_arc) {
299 std::swap(a0, a1);
300 }
301 }
302}
303
310{
311 Geom::PathVector path_out;
312
313 // 1) draw original path?
314 if (draw_ori_path.get_value()) {
315 path_out.insert(path_out.end(), path_in.begin(), path_in.end());
316 }
317
318
319 // 2) get all points
320 // (from: extension/internal/odf.cpp)
321 points.resize(0);
322 for (const auto &pit : path_in) {
323 // extract first point of this path
324 points.push_back(pit.initialPoint());
325 // iterate over all curves
326 for (const auto &cit : pit) {
327 points.push_back(cit.finalPoint());
328 }
329 }
330 // avoid identical start-point and end-point
331 if (points.front() == points.back()) {
332 points.pop_back();
333 }
334
335 // 3) modify GUI based on selected method
336 // 3.1) arc options
337 switch (method) {
338 case EM_AUTO:
339 case EM_CIRCLE:
341 if (gen_arc.get_value()) {
344 } else {
347 }
348 break;
349 default:
353 }
354 // 3.2) perspective options
355 switch (method) {
359 break;
360 default:
363 }
364
365 // 4) call method specific code
366 switch (method) {
368 // special mode: Use first two edges, interpret them as two sides of a parallelogram and
369 // generate an ellipse residing inside the parallelogram. This effect is quite useful when
370 // generating isometric views. Hence, the name.
371 if (0 != genIsometricEllipse(points, path_out)) {
372 return path_in;
373 }
374 break;
376 // special mode: Use first four points, interpret them as the perspective representation of a square and
377 // draw the ellipse as it was a circle inside that square.
378 if (0 != genPerspectiveEllipse(points, path_out)) {
379 return path_in;
380 }
381 break;
383 if (0 != genSteinerEllipse(points, false, path_out)) {
384 return path_in;
385 }
386 break;
388 if (0 != genSteinerEllipse(points, true, path_out)) {
389 return path_in;
390 }
391 break;
392 default:
393 if (0 != genFitEllipse(points, path_out)) {
394 return path_in;
395 }
396 }
397 return path_out;
398}
399
406int LPEPts2Ellipse::genFitEllipse(std::vector<Geom::Point> const &pts, Geom::PathVector &path_out)
407{
408 // rotation angle based on user provided rot_axes to position the vertices
409 const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation
410 Geom::Affine affine;
411 affine *= Geom::Rotate(rot_angle);
412 Geom::Coord a0 = 0;
413 Geom::Coord a1 = 2 * M_PI;
414
415 if (pts.size() < 2) {
416 return -1;
417 } else if (pts.size() == 2) {
418 // simple line: circle in the middle of the line to the vertices
419 Geom::Point line = pts.front() - pts.back();
420 double radius = line.length() * 0.5;
421 if (radius < 1e-9) {
422 return -1;
423 }
424 Geom::Point center = middle_point(pts.front(), pts.back());
425 Geom::Circle circle(center[0], center[1], radius);
426 affine *= Geom::Scale(circle.radius());
427 affine *= Geom::Translate(circle.center());
428 Geom::Path path;
429 unit_arc_path(path, affine);
430 path_out.push_back(path);
431 } else if (pts.size() >= 5 && EM_AUTO == method) {
432 // do ellipse
433 try {
434 Geom::Ellipse ellipse;
435 ellipse.fit(pts);
436 affine *= Geom::Scale(ellipse.ray(Geom::X), ellipse.ray(Geom::Y));
437 affine *= Geom::Rotate(ellipse.rotationAngle());
438 affine *= Geom::Translate(ellipse.center());
439 if (gen_arc.get_value()) {
440 Geom::Affine inv_affine = affine.inverse();
441 Geom::Point p0 = pts.front() * inv_affine;
442 Geom::Point p1 = pts.back() * inv_affine;
443 const bool ccw_wind = is_ccw(pts);
444 endpoints2angles(ccw_wind, other_arc.get_value(), p0, p1, a0, a1);
445 }
446 Geom::Path path;
447 unit_arc_path(path, affine, a0, a1, slice_arc.get_value());
448 path_out.push_back(path);
449 } catch (...) {
450 return -1;
451 }
452 } else {
453 // do a circle (3,4 points, or only_circle set)
454 try {
455 Geom::Circle circle;
456 circle.fit(pts);
457 affine *= Geom::Scale(circle.radius());
458 affine *= Geom::Translate(circle.center());
459 if (gen_arc.get_value()) {
460 Geom::Point p0 = pts.front() - circle.center();
461 Geom::Point p1 = pts.back() - circle.center();
462 const bool ccw_wind = is_ccw(pts);
463 endpoints2angles(ccw_wind, other_arc.get_value(), p0, p1, a0, a1);
464 }
465 Geom::Path path;
466 unit_arc_path(path, affine, a0, a1, slice_arc.get_value());
467 path_out.push_back(path);
468 } catch (...) {
469 return -1;
470 }
471 }
472
473 // draw frame?
475 gen_iso_frame_paths(path_out, affine);
476 }
477
478 // draw axes?
479 if (draw_axes.get_value()) {
480 gen_axes_paths(path_out, affine);
481 }
482
483 return 0;
484}
485
486int LPEPts2Ellipse::genIsometricEllipse(std::vector<Geom::Point> const &pts, Geom::PathVector &path_out)
487
488{
489 // take the first 3 vertices for the edges
490 if (pts.size() < 3) {
491 return -1;
492 }
493 // calc edges
494 Geom::Point e0 = pts[0] - pts[1];
495 Geom::Point e1 = pts[2] - pts[1];
496
497 Geom::Coord ce = cross(e0, e1);
498 // parallel or one is zero?
499 if (fabs(ce) < 1e-9) {
500 return -1;
501 }
502 // unit vectors along edges
503 Geom::Point u0 = unit_vector(e0);
504 Geom::Point u1 = unit_vector(e1);
505 // calc angles
506 Geom::Coord a0 = atan2(e0);
507 // Coord a1=M_PI_2-atan2(e1)-a0;
508 Geom::Coord a1 = acos(dot(u0, u1)) - M_PI_2;
509 // if(fabs(a1)<1e-9) return -1;
510 if (ce < 0) {
511 a1 = -a1;
512 }
513 // lengths: l0= length of edge 0; l1= height of parallelogram
514 Geom::Coord l0 = e0.length() * 0.5;
515 Geom::Point e0n = e1 - dot(u0, e1) * u0;
516 Geom::Coord l1 = e0n.length() * 0.5;
517
518 // center of the ellipse
519 Geom::Point pos = pts[1] + 0.5 * (e0 + e1);
520
521 // rotation angle based on user provided rot_axes to position the vertices
522 const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation
523
524 // build up the affine transformation
525 Geom::Affine affine;
526 affine *= Geom::Rotate(rot_angle);
527 affine *= Geom::Scale(l0, l1);
528 affine *= Geom::HShear(-tan(a1));
529 affine *= Geom::Rotate(a0);
530 affine *= Geom::Translate(pos);
531
532 Geom::Path path;
533 unit_arc_path(path, affine);
534 path_out.push_back(path);
535
536 // draw frame?
538 gen_iso_frame_paths(path_out, affine);
539 }
540
541 // draw axes?
542 if (draw_axes.get_value()) {
543 gen_axes_paths(path_out, affine);
544 }
545
546 return 0;
547}
548
549void evalSteinerEllipse(Geom::Point const &pCenter, Geom::Point const &pCenter_Pt2, Geom::Point const &pPt0_Pt1,
550 const double &angle, Geom::Point &pRes)
551{
552 // formula for the evaluation of points on the steiner ellipse using parameter angle
553 pRes = pCenter + pCenter_Pt2 * cos(angle) + pPt0_Pt1 * sin(angle) / sqrt(3);
554}
555
556int LPEPts2Ellipse::genSteinerEllipse(std::vector<Geom::Point> const &pts, bool gen_inellipse,
557 Geom::PathVector &path_out)
558{
559 // take the first 3 vertices for the edges
560 if (pts.size() < 3) {
561 return -1;
562 }
563 // calc center
564 Geom::Point pCenter = (pts[0] + pts[1] + pts[2]) / 3;
565 // calc main directions of affine triangle
566 Geom::Point f1 = pts[2] - pCenter;
567 Geom::Point f2 = (pts[1] - pts[0]) / sqrt(3);
568
569 // calc zero angle t0
570 const double denominator = dot(f1, f1) - dot(f2, f2);
571 double t0 = 0;
572 if (fabs(denominator) > 1e-12) {
573 const double cot2t0 = 2.0 * dot(f1, f2) / denominator;
574 t0 = atan(cot2t0) / 2.0;
575 }
576
577 // calc relative points of main axes (for axis directions)
578 Geom::Point p0(0, 0), pRel0, pRel1;
579 evalSteinerEllipse(p0, pts[2] - pCenter, pts[1] - pts[0], t0, pRel0);
580 evalSteinerEllipse(p0, pts[2] - pCenter, pts[1] - pts[0], t0 + M_PI_2, pRel1);
581 Geom::Coord l0 = pRel0.length();
582 Geom::Coord l1 = pRel1.length();
583
584 // basic rotation
585 double a0 = atan2(pRel0);
586
587 bool swapped = false;
588
589 if (l1 > l0) {
590 std::swap(l0, l1);
591 a0 += M_PI_2;
592 swapped = true;
593 }
594
595 // the Steiner inellipse is just scaled down by 2
596 if (gen_inellipse) {
597 l0 /= 2;
598 l1 /= 2;
599 }
600
601 // rotation angle based on user provided rot_axes to position the vertices
602 const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation
603
604 // build up the affine transformation
605 Geom::Affine affine;
606 affine *= Geom::Rotate(rot_angle);
607 affine *= Geom::Scale(l0, l1);
608 affine *= Geom::Rotate(a0);
609 affine *= Geom::Translate(pCenter);
610
611 Geom::Path path;
612 unit_arc_path(path, affine);
613 path_out.push_back(path);
614
615 // draw frame?
617 gen_iso_frame_paths(path_out, affine);
618 }
619
620 // draw axes?
621 if (draw_axes.get_value()) {
622 gen_axes_paths(path_out, affine);
623 }
624
625 return 0;
626}
627
628// identical to lpe-perspective-envelope.cpp
630{
631 Geom::Coord x = p[0];
632 Geom::Coord y = p[1];
633 return Geom::Point(Geom::Coord((x * m[0][0] + y * m[0][1] + m[0][2]) / (x * m[2][0] + y * m[2][1] + m[2][2])),
634 Geom::Coord((x * m[1][0] + y * m[1][1] + m[1][2]) / (x * m[2][0] + y * m[2][1] + m[2][2])));
635}
636
637int LPEPts2Ellipse::genPerspectiveEllipse(std::vector<Geom::Point> const &pts, Geom::PathVector &path_out)
638{
639 using Geom::X;
640 using Geom::Y;
641 // we need at least four points!
642 if (pts.size() < 4)
643 return -1;
644
645 // 1) check if the first three edges are a valid perspective
646 // calc edge
647 Geom::Point e[] = { pts[0] - pts[1], pts[1] - pts[2], pts[2] - pts[3], pts[3] - pts[0] };
648 // calc directions
649 Geom::Coord c[] = { cross(e[0], e[1]), cross(e[1], e[2]), cross(e[2], e[3]), cross(e[3], e[0]) };
650 // is this quad not convex?
651 if (!((c[0] > 0 && c[1] > 0 && c[2] > 0 && c[3] > 0) || (c[0] < 0 && c[1] < 0 && c[2] < 0 && c[3] < 0)))
652 return -1;
653
654 // 2) solve the direct linear transformation (see e.g. lpe-perspective-envelope.cpp or
655 // https://franklinta.com/2014/09/08/computing-css-matrix3d-transforms/)
656
657 // the square points in the initial configuration (about the unit circle):
658 Geom::Point pts0[4] = { { -1.0, -1.0 }, { +1.0, -1.0 }, { +1.0, +1.0 }, { -1.0, +1.0 } };
659
660 // build equation in matrix form
661 double eqnVec[8] = { 0 };
662 double eqnMat[64] = { 0 };
663 for (unsigned int i = 0; i < 4; ++i) {
664 eqnMat[8 * (i + 0) + 0] = pts0[i][X];
665 eqnMat[8 * (i + 0) + 1] = pts0[i][Y];
666 eqnMat[8 * (i + 0) + 2] = 1;
667 eqnMat[8 * (i + 0) + 6] = -pts[i][X] * pts0[i][X];
668 eqnMat[8 * (i + 0) + 7] = -pts[i][X] * pts0[i][Y];
669 eqnMat[8 * (i + 4) + 3] = pts0[i][X];
670 eqnMat[8 * (i + 4) + 4] = pts0[i][Y];
671 eqnMat[8 * (i + 4) + 5] = 1;
672 eqnMat[8 * (i + 4) + 6] = -pts[i][Y] * pts0[i][X];
673 eqnMat[8 * (i + 4) + 7] = -pts[i][Y] * pts0[i][Y];
674 eqnVec[i] = pts[i][X];
675 eqnVec[i + 4] = pts[i][Y];
676 }
677 // solve using gsl library
678 gsl_matrix_view m = gsl_matrix_view_array(eqnMat, 8, 8);
679 gsl_vector_view b = gsl_vector_view_array(eqnVec, 8);
680 int s = 0;
681 gsl_linalg_LU_decomp(&m.matrix, gsl_p, &s);
682 gsl_linalg_LU_solve(&m.matrix, gsl_p, &b.vector, gsl_x);
683 // transfer the solution to the projection matrix for further use
684 size_t h = 0;
685 double projmatrix[3][3];
686 for (auto &matRow : projmatrix) {
687 for (double &matElement : matRow) {
688 if (h == 8) {
689 projmatrix[2][2] = 1.0;
690 } else {
691 matElement = gsl_vector_get(gsl_x, h++);
692 }
693 }
694 }
695
696 // 3) generate five points on a unit circle and project them
697 five_pts.resize(5); // reuse and avoid new/delete
698 h = 0;
699 double dA = 2.0 * M_PI / 5.0; // delta Angle
700 for (auto &i : five_pts) {
701 const double angle = dA * h++;
702 const Geom::Point circle_point(sin(angle), cos(angle));
703 i = projectPoint(circle_point, projmatrix);
704 }
705
706 // 4) fit the five points to an ellipse with the already known function inside genFitEllipse() function
707 // build up the affine transformation
708 const double rot_angle = -deg2rad(rot_axes); // negative for ccw rotation
709 Geom::Affine affine;
710 affine *= Geom::Rotate(rot_angle);
711
712 try {
713 Geom::Ellipse ellipse;
714 ellipse.fit(five_pts);
715 affine *= Geom::Scale(ellipse.ray(Geom::X), ellipse.ray(Geom::Y));
716 affine *= Geom::Rotate(ellipse.rotationAngle());
717 affine *= Geom::Translate(ellipse.center());
718 } catch (...) {
719 return -1;
720 }
721
722 Geom::Path path;
723 unit_arc_path(path, affine);
724 path_out.push_back(path);
725
726 // 5) frames and axes
727 bool ccw_wind = false;
729 ccw_wind = is_ccw(pts);
730 const double ra = ccw_wind ? rot_angle : -rot_angle;
731
732 // draw frame?
734 gen_iso_frame_paths(path_out, affine);
735 }
736
737 // draw perspective frame?
739 gen_perspective_frame_paths(path_out, ra, projmatrix);
740 }
741
742 // draw axes?
743 if (draw_axes.get_value()) {
744 gen_axes_paths(path_out, affine);
745 }
746
747 // draw perspective axes?
749 gen_perspective_axes_paths(path_out, ra, projmatrix);
750 }
751
752 return 0;
753}
754
755
756/* ######################## */
757
758} // namespace LivePathEffect
759} /* namespace Inkscape */
760
761/*
762 Local Variables:
763 mode:c++
764 c-file-style:"stroustrup"
765 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
766 indent-tabs-mode:nil
767 fill-column:99
768 End:
769*/
770// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
Path - a sequence of contiguous curves.
Circle shape.
3x3 matrix representing an affine transformation.
Definition affine.h:70
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Bezier curve with compile-time specified order.
Set of all points at a fixed distance from the center.
Definition circle.h:55
Set of points with a constant sum of distances from two foci.
Definition ellipse.h:68
Angle rotationAngle() const
Get the angle the X ray makes with the +X axis.
Definition ellipse.h:126
void fit(std::vector< Point > const &points)
Create an ellipse passing through the specified points At least five points have to be specified.
Definition ellipse.cpp:206
Coord ray(Dim2 d) const
Get one ray of the ellipse.
Definition ellipse.h:124
Point center() const
Definition ellipse.h:119
Horizontal shearing.
Definition transforms.h:257
Sequence of subpaths.
Definition pathvector.h:122
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
iterator insert(iterator pos, Path const &p)
Definition pathvector.h:179
iterator begin()
Definition pathvector.h:151
iterator end()
Definition pathvector.h:152
Sequence of contiguous curves, aka spline.
Definition path.h:353
void setStitching(bool x)
Enable or disable the throwing of exceptions when stitching discontinuities.
Definition path.h:827
void close(bool closed=true)
Set whether the path is closed.
Definition path.cpp:322
void append(Curve *curve)
Add a new curve to the end of the path.
Definition path.h:750
void appendNew(Args &&... args)
Append a new curve to the path.
Definition path.h:804
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr bool isZero() const
Check whether both coordinates are zero.
Definition point.h:227
Coord length() const
Compute the distance from origin.
Definition point.h:118
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 registerParameter(Parameter *param)
Definition effect.cpp:1710
static Geom::Point projectPoint(Geom::Point p, double m[][3])
static void gen_perspective_frame_paths(Geom::PathVector &path_out, const double rot_angle, double projmatrix[3][3])
Geom::PathVector doEffect_path(Geom::PathVector const &path_in) override
Generates an ellipse (or circle) from the vertices of a given path.
static void gen_iso_frame_paths(Geom::PathVector &path_out, const Geom::Affine &affine)
int genPerspectiveEllipse(std::vector< Geom::Point > const &points_in, Geom::PathVector &path_out)
static void gen_perspective_axes_paths(Geom::PathVector &path_out, const double rot_angle, double projmatrix[3][3])
int genIsometricEllipse(std::vector< Geom::Point > const &points_in, Geom::PathVector &path_out)
static int unit_arc_path(Geom::Path &path_in, Geom::Affine &affine, double start=0.0, double end=2.0 *M_PI, bool slice=false)
int genFitEllipse(std::vector< Geom::Point > const &points_in, Geom::PathVector &path_out)
Generates an ellipse (or circle) from the vertices of a given path.
int genSteinerEllipse(std::vector< Geom::Point > const &points_in, bool gen_inellipse, Geom::PathVector &path_out)
static void gen_axes_paths(Geom::PathVector &path_out, const Geom::Affine &affine)
static bool is_ccw(const std::vector< Geom::Point > &pts)
LPEPts2Ellipse(LivePathEffectObject *lpeobject)
virtual void param_widget_is_enabled(bool is_enabled)
Definition parameter.h:70
void param_set_range(double min, double max)
void param_set_increments(double step, double page)
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:42
double c[8][4]
Ellipse shape.
Elliptical arc curve.
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
LPE "Points to Ellipse" implementation.
Geom::Point start
Geom::Point end
SBasisN< n > cos(LinearN< n > bo, int k)
double atan2(Point const &p)
SBasisN< n > sqrt(SBasisN< n > const &a, int k)
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
Point unit_vector(Point const &a)
SBasisN< n > sin(LinearN< n > bo, int k)
Point middle_point(LineSegment const &_segment)
void endpoints2angles(const bool ccw_wind, const bool use_other_arc, const Geom::Point &p0, const Geom::Point &p1, Geom::Coord &a0, Geom::Coord &a1)
static const Util::EnumData< EllipseMethod > EllipseMethodData[]
static const Util::EnumDataConverter< EllipseMethod > EMConverter(EllipseMethodData, EM_END)
void evalSteinerEllipse(Geom::Point const &pCenter, Geom::Point const &pCenter_Pt2, Geom::Point const &pPt0_Pt1, const double &angle, Geom::Point &pRes)
double calc_delta_angle(const double a0, const double a1)
Helper class to stream background task notifications as a series of messages.
PathVector - a sequence of subpaths.
auto len
Definition safe-printf.h:21
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
double sum(const double alpha[16], const double &x, const double &y)
Simplified management of enumerations of svg items with UI labels.
Definition enums.h:27