Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
elliptiarc-3point-center-fitting.cpp
Go to the documentation of this file.
1/*
2 * make up an elliptical arc knowing 3 points lying on the arc
3 * and the ellipse centre
4 *
5 * Authors:
6 * Marco Cecchetti <mrcekets at gmail.com>
7 *
8 * Copyright 2008 authors
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it either under the terms of the GNU Lesser General Public
12 * License version 2.1 as published by the Free Software Foundation
13 * (the "LGPL") or, at your option, under the terms of the Mozilla
14 * Public License Version 1.1 (the "MPL"). If you do not alter this
15 * notice, a recipient may use your version of this file under either
16 * the MPL or the LGPL.
17 *
18 * You should have received a copy of the LGPL along with this library
19 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * You should have received a copy of the MPL along with this library
22 * in the file COPYING-MPL-1.1
23 *
24 * The contents of this file are subject to the Mozilla Public License
25 * Version 1.1 (the "License"); you may not use this file except in
26 * compliance with the License. You may obtain a copy of the License at
27 * http://www.mozilla.org/MPL/
28 *
29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
30 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
31 * the specific language governing rights and limitations.
32 */
33
34#include <toys/path-cairo.h>
36
39
40namespace Geom
41{
42
44 Point const& centre,
45 Point const& initial,
46 Point const& final,
47 Point const& inner )
48{
49
50 Point p[3] = { initial, inner, final };
51 double x1, x2, x3, x4;
52 double y1, y2, y3, y4;
53 double x1y1, x2y2, x3y1, x1y3;
54 NL::Matrix m(3,3);
55 NL::Vector v(3);
56 NL::LinearSystem ls(m, v);
57
58 m.set_all(0);
59 v.set_all(0);
60 for (auto & k : p)
61 {
62 // init_x_y
63 x1 = k[X] - centre[X]; x2 = x1 * x1; x3 = x2 * x1; x4 = x3 * x1;
64 y1 = k[Y] - centre[Y]; y2 = y1 * y1; y3 = y2 * y1; y4 = y3 * y1;
65 x1y1 = x1 * y1;
66 x2y2 = x2 * y2;
67 x3y1 = x3 * y1; x1y3 = x1 * y3;
68
69 // init linear system
70 m(0,0) += x4;
71 m(0,1) += x3y1;
72 m(0,2) += x2y2;
73
74 m(1,0) += x3y1;
75 m(1,1) += x2y2;
76 m(1,2) += x1y3;
77
78 m(2,0) += x2y2;
79 m(2,1) += x1y3;
80 m(2,2) += y4;
81
82 v[0] += x2;
83 v[1] += x1y1;
84 v[2] += y2;
85 }
86
87 ls.SV_solve();
88
89 double A = ls.solution()[0];
90 double B = ls.solution()[1];
91 double C = ls.solution()[2];
92
93
94 //evaluate ellipse rotation angle
95 double rot = std::atan2( -B, -(A - C) )/2;
96 std::cerr << "rot = " << rot << std::endl;
97 bool swap_axes = false;
98 if ( are_near(rot, 0) ) rot = 0;
99 if ( are_near(rot, M_PI/2) || rot < 0 )
100 {
101 swap_axes = true;
102 }
103
104 // evaluate the length of the ellipse rays
105 double cosrot = std::cos(rot);
106 double sinrot = std::sin(rot);
107 double cos2 = cosrot * cosrot;
108 double sin2 = sinrot * sinrot;
109 double cossin = cosrot * sinrot;
110
111 double den = A * cos2 + B * cossin + C * sin2;
112 if ( den <= 0 )
113 {
114 std::cerr << "!(den > 0) error" << std::endl;
115 std::cerr << "evaluating rx" << std::endl;
116 return false;
117 }
118 double rx = std::sqrt(1/den);
119
120 den = C * cos2 - B * cossin + A * sin2;
121 if ( den <= 0 )
122 {
123 std::cerr << "!(den > 0) error" << std::endl;
124 std::cerr << "evaluating ry" << std::endl;
125 return false;
126 }
127 double ry = std::sqrt(1/den);
128
129
130 // the solution is not unique so we choose always the ellipse
131 // with a rotation angle between 0 and PI/2
132 if ( swap_axes ) std::swap(rx, ry);
133 if ( are_near(rot, M_PI/2)
134 || are_near(rot, -M_PI/2)
135 || are_near(rx, ry) )
136 {
137 rot = 0;
138 }
139 else if ( rot < 0 )
140 {
141 rot += M_PI/2;
142 }
143
144 std::cerr << "swap axes: " << swap_axes << std::endl;
145 std::cerr << "rx = " << rx << " ry = " << ry << std::endl;
146 std::cerr << "rot = " << deg_from_rad(rot) << std::endl;
147 std::cerr << "centre: " << centre << std::endl;
148
149
150 // find out how we should set the large_arc_flag and sweep_flag
151 bool large_arc_flag = true;
152 bool sweep_flag = true;
153
154 Point sp_cp = initial - centre;
155 Point ep_cp = final - centre;
156 Point ip_cp = inner - centre;
157
158 double angle1 = angle_between(sp_cp, ep_cp);
159 double angle2 = angle_between(sp_cp, ip_cp);
160 double angle3 = angle_between(ip_cp, ep_cp);
161
162 if ( angle1 > 0 )
163 {
164 if ( angle2 > 0 && angle3 > 0 )
165 {
166 large_arc_flag = false;
167 sweep_flag = true;
168 }
169 else
170 {
171 large_arc_flag = true;
172 sweep_flag = false;
173 }
174 }
175 else
176 {
177 if ( angle2 < 0 && angle3 < 0 )
178 {
179 large_arc_flag = false;
180 sweep_flag = false;
181 }
182 else
183 {
184 large_arc_flag = true;
185 sweep_flag = true;
186 }
187 }
188
189 // finally we're going to create the elliptical arc!
190 try
191 {
192 ea.set( initial, rx, ry, rot,
193 large_arc_flag, sweep_flag, final );
194 }
195 catch (RangeError const &e)
196 {
197 std::cerr << e.what() << std::endl;
198 return false;
199 }
200
201 return true;
202}
203
204
205}
206
207
208
209using namespace Geom;
210
211class ElliptiArcMaker : public Toy
212{
213 private:
214 void draw( cairo_t *cr, std::ostringstream *notify,
215 int width, int height, bool save, std::ostringstream *timer_stream) override
216 {
217 cairo_set_line_width (cr, 0.3);
218 cairo_set_source_rgb(cr, 0,0,0.3);
219 draw_text(cr, O.pos, "centre");
220 draw_text(cr, A.pos, "initial");
221 draw_text(cr, B.pos, "final");
222 draw_text(cr, C.pos, "inner");
223 cairo_stroke(cr);
224 cairo_set_source_rgb(cr, 0.7,0,0);
225 bool status
226 = make_elliptical_arc(ea, O.pos, A.pos, B.pos, C.pos);
227 if (status)
228 {
229 D2<Geom::SBasis> easb = ea.toSBasis();
230 cairo_d2_sb(cr, easb);
231 }
232 cairo_stroke(cr);
233 Toy::draw(cr, notify, width, height, save,timer_stream);
234 }
235
236 public:
237 ElliptiArcMaker()
238 : O(443, 441),
239 A(516, 275),
240 B(222, 455),
241 C(190, 234)
242 {
243 handles.push_back(&O);
244 handles.push_back(&A);
245 handles.push_back(&B);
246 handles.push_back(&C);
247 }
248
249 private:
250 PointHandle O, A, B, C;
251 EllipticalArc ea;
252};
253
254
255
256
257
258
259
260
261int main(int argc, char **argv)
262{
263 init( argc, argv, new ElliptiArcMaker() );
264 return 0;
265}
266
int main()
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
D2< SBasis > toSBasis() const
Definition d2.h:148
Elliptical arc curve.
void set(Point const &ip, double rx, double ry, double rot_angle, bool large_arc, bool sweep, Point const &fp)
Change all of the arc's parameters.
const char * what() const noexcept override
Definition exception.h:63
const Vector & SV_solve()
const Vector & solution() const
void set_all(double x)
Definition matrix.h:225
Two-dimensional point that doubles as a vector.
Definition point.h:66
vector< Handle * > handles
virtual void save(FILE *f)
virtual void draw(cairo_t *cr, std::ostringstream *notify, int w, int h, bool save, std::ostringstream *timing_stream)
double inner(valarray< double > const &x, valarray< double > const &y)
Elliptical arc curve.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Various utility functions.
Definition affine.h:22
bool make_elliptical_arc(EllipticalArc &ea, Point const &centre, Point const &initial, Point const &final, Point const &inner)
double angle_between(Line const &l1, Line const &l2)
Definition line.h:456
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
struct _cairo cairo_t
Definition path-cairo.h:16
void cairo_d2_sb(cairo_t *cr, Geom::D2< Geom::SBasis > const &p)
double height
double width
void draw_text(cairo_t *cr, Geom::Point pos, const char *txt, bool bottom=false, const char *fontdesc="Sans")
void init(int argc, char **argv, Toy *t, int width=600, int height=600)