Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
ellipse-area-minimizer.cpp
Go to the documentation of this file.
1/*
2 * Ellipse and Elliptical Arc Fitting Example
3 *
4 * Authors:
5 * Marco Cecchetti <mrcekets at gmail.com>
6 *
7 * Copyright 2008 authors
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it either under the terms of the GNU Lesser General Public
11 * License version 2.1 as published by the Free Software Foundation
12 * (the "LGPL") or, at your option, under the terms of the Mozilla
13 * Public License Version 1.1 (the "MPL"). If you do not alter this
14 * notice, a recipient may use your version of this file under either
15 * the MPL or the LGPL.
16 *
17 * You should have received a copy of the LGPL along with this library
18 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * You should have received a copy of the MPL along with this library
21 * in the file COPYING-MPL-1.1
22 *
23 * The contents of this file are subject to the Mozilla Public License
24 * Version 1.1 (the "License"); you may not use this file except in
25 * compliance with the License. You may obtain a copy of the License at
26 * http://www.mozilla.org/MPL/
27 *
28 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
29 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
30 * the specific language governing rights and limitations.
31 */
32
33
36
37#include <2geom/ellipse.h>
39#include <2geom/utils.h>
40
41#include <toys/path-cairo.h>
43
44#include <stdio.h>
45
46#include <gsl/gsl_errno.h>
47#include <gsl/gsl_math.h>
48#include <gsl/gsl_min.h>
49
50
51
52
53using namespace Geom;
54
55
56class LFMEllipseArea
57 : public NL::LinearFittingModelWithFixedTerms<Point, double, Ellipse>
58{
59 public:
60 LFMEllipseArea(double coeff)
61 : m_coeff(coeff*coeff)
62 {
63 }
64 void feed( NL::VectorView & coeff, double & fixed_term, Point const& p ) const
65 {
66 coeff[0] = p[X] * p[Y];
67 coeff[1] = p[X];
68 coeff[2] = p[Y];
69 coeff[3] = 1;
70 fixed_term = p[X] * p[X] + m_coeff * p[Y] * p[Y];
71 }
72
73 size_t size() const
74 {
75 return 4;
76 }
77
78 void instance(Ellipse & e, NL::ConstVectorView const& coeff) const
79 {
80// std::cerr << " B = " << coeff[0]
81// << " C = " << decimal_round(m_coeff,10)
82// << " D = " << coeff[1]
83// << " E = " << coeff[2]
84// << " F = " << coeff[3]
85// << std::endl;
86 e.setCoefficients(1, coeff[0], m_coeff, coeff[1], coeff[2], coeff[3]);
87 }
88
89 private:
90 double m_coeff;
91};
92
93inline
94Ellipse fitting(std::vector<Point> const& points, double coeff)
95{
96 size_t sz = points.size();
97 if (sz != 4)
98 {
99 THROW_RANGEERROR("fitting error: too few points passed");
100 }
101 LFMEllipseArea model(coeff);
103
104 for (size_t i = 0; i < sz; ++i)
105 {
106 fitter.append(points[i]);
107 }
108 fitter.update();
109
110 NL::Vector z(sz, 0.0);
111 Ellipse e;
112 model.instance(e, fitter.result(z));
113 return e;
114}
115
116
117inline
118double area_goal(double coeff, void* params)
119{
120 typedef std::vector<Point> point_set_t;
121 const point_set_t & points = *static_cast<point_set_t*>(params);
122 Ellipse e;
123 try
124 {
125 e = fitting(points, coeff);
126 }
127 catch (LogicalError const &exc)
128 {
129 //std::cerr << exc.what() << std::endl;
130 return 1e18;
131 }
132 return e.ray(X) * e.ray(Y);
133}
134
135
136inline
137double perimeter_goal(double coeff, void* params)
138{
139 typedef std::vector<Point> point_set_t;
140 const point_set_t & points = *static_cast<point_set_t*>(params);
141 Ellipse e;
142 try
143 {
144 e = fitting(points, coeff);
145 }
146 catch (LogicalError const &exc)
147 {
148 //std::cerr << exc.what() << std::endl;
149 return 1e18;
150 }
151 return e.ray(X) + e.ray(Y);
152}
153
154void no_minimum_error_handler (const char * reason,
155 const char * file,
156 int line,
157 int gsl_errno)
158{
159 if (gsl_errno == GSL_EINVAL)
160 {
161 std::cerr << "gsl: " << file << ":" << line << " ERROR: " << reason << std::endl;
162 }
163 else
164 {
165 gsl_error(reason, file, line, gsl_errno);
166 }
167}
168
169typedef double goal_function_type(double coeff, void* params);
170
171double minimizer (std::vector<Point> & points, goal_function_type* gf)
172{
173 int status;
174 int iter = 0, max_iter = 1000;
175 const gsl_min_fminimizer_type *T;
176 gsl_min_fminimizer *s;
177 double m = 1.0;
178 double a = 1e-2, b = 1e2;
179 gsl_function F;
180
181 F.function = gf;
182 F.params = static_cast<void*>(&points);
183
184 //T = gsl_min_fminimizer_goldensection;
185 T = gsl_min_fminimizer_brent;
186 s = gsl_min_fminimizer_alloc (T);
187 gsl_min_fminimizer_set (s, &F, m, a, b);
188
189// printf ("using %s method\n",
190// gsl_min_fminimizer_name (s));
191//
192// printf ("%5s [%9s, %9s] %9s %10s %9s\n",
193// "iter", "lower", "upper", "min",
194// "err", "err(est)");
195//
196// printf ("%5d [%.7f, %.7f] %.7f %+.7f %.7f\n",
197// iter, a, b,
198// m, m - m_expected, b - a);
199
200 do
201 {
202 iter++;
203 status = gsl_min_fminimizer_iterate (s);
204
205 m = gsl_min_fminimizer_x_minimum (s);
206 a = gsl_min_fminimizer_x_lower (s);
207 b = gsl_min_fminimizer_x_upper (s);
208
209 status
210 = gsl_min_test_interval (a, b, 1e-3, 0.0);
211
212// if (status == GSL_SUCCESS)
213// printf ("Converged:\n");
214//
215// printf ("%5d [%.7f, %.7f] "
216// "%.7f %+.7f %.7f\n",
217// iter, a, b,
218// m, m - m_expected, b - a);
219 }
220 while (status == GSL_CONTINUE && iter < max_iter);
221
222 gsl_min_fminimizer_free (s);
223
224 if (status != GSL_SUCCESS) return 0;
225
226 return m;
227}
228
229
230
231class EllipseAreaMinimizer : public Toy
232{
233 public:
234 void draw( cairo_t *cr, std::ostringstream *notify,
235 int width, int height, bool save, std::ostringstream *timer_stream) override
236 {
237 Point toggle_sp( 300, height - 50);
238 toggles[0].bounds = Rect( toggle_sp, toggle_sp + Point(135,25) );
239 ConvexHull ch(psh.pts);
240 bool non_convex = false;
241 for(auto & pt : psh.pts) {
242 if (ch.contains(pt))
243 non_convex = true;
244 }
245
246 if(non_convex) {
247 Circle circ;
248 std::vector<Point> boundary(ch.begin(), ch.end());
249 circ.fit(boundary);
250 e = Ellipse(circ);
251 } else {
253 if (!toggles[0].on) gf = &perimeter_goal;
254 double coeff = minimizer(psh.pts, gf);
255
256 try
257 {
258 e = fitting(psh.pts, coeff);
259 }
260 catch (LogicalError const &exc)
261 {
262 std::cerr << exc.what() << std::endl;
263 Toy::draw(cr, notify, width, height, save,timer_stream);
264 return;
265 }
266 }
267 cairo_set_source_rgba(cr, 0.3, 0.3, 0.3, 1.0);
268 cairo_set_line_width (cr, 0.3);
269 draw_elliptical_arc_with_cairo( cr,
270 e.center(X), e.center(Y),
271 e.ray(X), e.ray(Y),
272 0, 2*M_PI,
273 e.rotationAngle() );
274 if (toggles[0].on)
275 *notify << "Area: " << e.ray(X)*e.ray(Y);
276 else
277 *notify << "Perimeter: " << 2* (e.ray(X) + e.ray(Y));
278 cairo_stroke(cr);
279
280 Toy::draw(cr, notify, width, height, save,timer_stream);
281 }
282
283 void draw_elliptical_arc_with_cairo(
284 cairo_t *cr,
285 double _cx, double _cy,
286 double _rx, double _ry,
287 double _sa, double _ea,
288 double _ra = 0
289 ) const
290 {
291 double cos_rot_angle = std::cos(_ra);
292 double sin_rot_angle = std::sin(_ra);
293 cairo_matrix_t transform_matrix;
294 cairo_matrix_init( &transform_matrix,
295 _rx * cos_rot_angle, _rx * sin_rot_angle,
296 -_ry * sin_rot_angle, _ry * cos_rot_angle,
297 _cx, _cy
298 );
299 cairo_save(cr);
300 cairo_transform(cr, &transform_matrix);
301 cairo_arc(cr, 0, 0, 1, _sa, _ea);
302 cairo_restore(cr);
303 }
304
305 public:
306 EllipseAreaMinimizer()
307 {
308 gsl_set_error_handler(&no_minimum_error_handler);
309
310 first_time = true;
311
312 psh.pts.resize(4);
313 psh.pts[0] = Point(450, 250);
314 psh.pts[1] = Point(250, 100);
315 psh.pts[2] = Point(250, 400);
316 psh.pts[3] = Point(400, 320);
317
318 handles.push_back(&psh);
319
320 toggles.emplace_back("Area/Perimeter", true);
321 handles.push_back(&(toggles[0]));
322 }
323
324 public:
325 Ellipse e;
326 bool first_time;
327 PointSetHandle psh;
328 std::vector<Toggle> toggles;
329};
330
331
332
333
334int main(int argc, char **argv)
335{
336 init( argc, argv, new EllipseAreaMinimizer(), 600, 600 );
337 return 0;
338}
339
340
341
342
343/*
344 Local Variables:
345 mode:c++
346 c-file-style:"stroustrup"
347 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
348 indent-tabs-mode:nil
349 fill-column:99
350 End:
351*/
352// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Various utility functions.
int main()
Set of all points at a fixed distance from the center.
Definition circle.h:55
void fit(std::vector< Point > const &points)
Fit the circle to the passed points using the least squares method.
Definition circle.cpp:282
Convex hull based on the Andrew's monotone chain algorithm.
Set of points with a constant sum of distances from two foci.
Definition ellipse.h:68
Coord ray(Dim2 d) const
Get one ray of the ellipse.
Definition ellipse.h:124
void setCoefficients(double A, double B, double C, double D, double E, double F)
Set an ellipse by solving its implicit equation.
Definition ellipse.cpp:48
const char * what() const noexcept override
Definition exception.h:63
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
virtual void first_time(int, char **)
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)
Geom::IntPoint size
void no_minimum_error_handler(const char *reason, const char *file, int line, int gsl_errno)
double perimeter_goal(double coeff, void *params)
double area_goal(double coeff, void *params)
Ellipse fitting(std::vector< Point > const &points, double coeff)
double minimizer(std::vector< Point > &points, goal_function_type *gf)
double goal_function_type(double coeff, void *params)
Ellipse shape.
Elliptical arc curve.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Various utility functions.
Definition affine.h:22
struct _cairo cairo_t
Definition path-cairo.h:16
double height
double width
void cairo_set_source_rgba(cairo_t *cr, colour c)
void init(int argc, char **argv, Toy *t, int width=600, int height=600)