Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
arc-length.cpp
Go to the documentation of this file.
1/*
2 * arc-length.cpp
3 *
4 * Copyright 2006 Nathan Hurst <njh@mail.csse.monash.edu.au>
5 * Copyright 2006 Michael G. Sloan <mgsloan@gmail.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it either under the terms of the GNU Lesser General Public
9 * License version 2.1 as published by the Free Software Foundation
10 * (the "LGPL") or, at your option, under the terms of the Mozilla
11 * Public License Version 1.1 (the "MPL"). If you do not alter this
12 * notice, a recipient may use your version of this file under either
13 * the MPL or the LGPL.
14 *
15 * You should have received a copy of the LGPL along with this library
16 * in the file COPYING-LGPL-2.1; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * You should have received a copy of the MPL along with this library
19 * in the file COPYING-MPL-1.1
20 *
21 * The contents of this file are subject to the Mozilla Public License
22 * Version 1.1 (the "License"); you may not use this file except in
23 * compliance with the License. You may obtain a copy of the License at
24 * http://www.mozilla.org/MPL/
25 *
26 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY
27 * OF ANY KIND, either express or implied. See the LGPL or the MPL for
28 * the specific language governing rights and limitations.
29 *
30 */
31
32#include <2geom/arc-length.h>
33#include <2geom/bezier-utils.h>
34#include <2geom/polynomial.h>
35using namespace Geom;
36
40double cubic_length_subdividing(Path::Elem const & e, double tol) {
41 Point v[3];
42 for(int i = 0; i < 3; i++)
43 v[i] = e[i+1] - e[0];
44 Point orth = v[2]; // unit normal to path line
45 rot90(orth);
46 orth.normalize();
47 double err = fabs(dot(orth, v[1])) + fabs(dot(orth, v[0]));
48 if(err < tol) {
49 return distance(e.first(), e.last()); // approximately a line
50 } else {
51 Point mid[3];
52 double result;
53 for(int i = 0; i < 3; i++)
54 mid[i] = lerp(0.5, e[i], e[i+1]);
55 Point midmid[2];
56 for(int i = 0; i < 2; i++)
57 midmid[i] = lerp(0.5, mid[i], mid[i+1]);
58 Point midmidmid = lerp(0.5, midmid[0], midmid[1]);
59 {
60 Point curve[4] = {e[0], mid[0], midmid[0], midmidmid};
61 Path::Elem e0(cubicto, std::vector<Point>::const_iterator(curve), std::vector<Point>::const_iterator(curve) + 4);
63 } {
64 Point curve[4] = {midmidmid, midmid[1], mid[2], e[3]};
65 Path::Elem e1(cubicto, std::vector<Point>::const_iterator(curve), std::vector<Point>::const_iterator(curve) + 4);
66 return result + cubic_length_subdividing(e1, tol);
67 }
68 }
69}
70
75double arc_length_subdividing(Path const & p, double tol) {
76 double result = 0;
77
78 for(Path::const_iterator iter(p.begin()), end(p.end()); iter != end; ++iter) {
79 if(dynamic_cast<LineTo *>(iter.cmd()))
80 result += distance((*iter).first(), (*iter).last());
81 else if(dynamic_cast<CubicTo *>(iter.cmd()))
82 result += cubic_length_subdividing(*iter, tol);
83 else
84 ;
85 }
86
87 return result;
88}
89
90
91#ifdef HAVE_GSL
92#include <gsl/gsl_integration.h>
93static double poly_length_integrating(double t, void* param) {
94 Poly* pc = (Poly*)param;
95 return hypot(pc[0].eval(t), pc[1].eval(t));
96}
97
105void arc_length_integrating(Path::Elem pe, double t, double tol, double &result, double &abs_error) {
106 if(dynamic_cast<LineTo *>(iter.cmd()))
107 result += distance(pe.first(), pe.last()) * t;
108 else if(dynamic_cast<QuadTo *>(iter.cmd()) ||
109 dynamic_cast<CubicTo *>(iter.cmd())) {
110 Poly B[2] = {get_parametric_poly(pe, X), get_parametric_poly(pe, Y)};
111 for(int i = 0; i < 2; i++)
112 B[i] = derivative(B[i]);
113
114 gsl_function F;
115 gsl_integration_workspace * w
116 = gsl_integration_workspace_alloc (20);
117 F.function = &poly_length_integrating;
118 F.params = (void*)B;
119 double quad_result, err;
120 /* We could probably use the non adaptive code here if we removed any cusps first. */
121 int returncode =
122 gsl_integration_qag (&F, 0, t, 0, tol, 20,
123 GSL_INTEG_GAUSS21, w, &quad_result, &err);
124
125 abs_error += err;
126 result += quad_result;
127 } else
128 return;
129}
130
132double arc_length_integrating(Path const & p, double tol) {
133 double result = 0, abserr = 0;
134
135 for(Path::const_iterator iter(p.begin()), end(p.end()); iter != end; ++iter) {
136 arc_length_integrating(*iter, 1.0, tol, result, abserr);
137 }
138 //printf("got %g with err %g\n", result, abserr);
139
140 return result;
141}
142
144double arc_length_integrating(Path const & p, Path::Location const & pl, double tol) {
145 double result = 0, abserr = 0;
146 ptrdiff_t offset = pl.it - p.begin();
147
148 assert(offset >= 0);
149 assert(offset < p.size());
150
151 for(Path::const_iterator iter(p.begin()), end(p.end());
152 (iter != pl.it); ++iter) {
153 arc_length_integrating(*iter, 1.0, tol, result, abserr);
154 }
155 arc_length_integrating(*pl.it, pl.t, tol, result, abserr);
156
157 return result;
158}
159
160/* We use a somewhat surprising result for this that s'(t) = |p'(t)|
161 Thus, we can use a derivative based root finder.
162*/
163
164#include <stdio.h>
165#include <gsl/gsl_errno.h>
166#include <gsl/gsl_math.h>
167#include <gsl/gsl_roots.h>
168
169struct arc_length_params
170{
171 Path::Elem pe;
172 double s,tol, result, abs_error;
173 double left, right;
174};
175
176double
177arc_length (double t, void *params)
178{
179 struct arc_length_params *p
180 = (struct arc_length_params *) params;
181
182 double result = 0, abs_error = 0;
183 if(t < 0) t = 0;
184 if(t > 1) t = 1;
185 if(!((t >= 0) && (t <= 1))) {
186 printf("t = %g\n", t);
187 }
188 assert((t >= 0) && (t <= 1));
189 arc_length_integrating(p->pe, t, p->tol, result, abs_error);
190 return result - p->s ;
191}
192
193double
194arc_length_deriv (double t, void *params)
195{
196 struct arc_length_params *p
197 = (struct arc_length_params *) params;
198
199 Point pos, tgt, acc;
200 p->pe.point_tangent_acc_at(t, pos, tgt, acc);
201 return L2(tgt);
202}
203
204void
205arc_length_fdf (double t, void *params,
206 double *y, double *dy)
207{
208 *y = arc_length(t, params);
209 *dy = arc_length_deriv(t, params);
210}
211
212double polish_brent(double t, arc_length_params &alp) {
213 int status;
214 int iter = 0, max_iter = 10;
215 const gsl_root_fsolver_type *T;
216 gsl_root_fsolver *solver;
217 double x_lo = 0.0, x_hi = 1.0;
218 gsl_function F;
219
220 F.function = &arc_length;
221 F.params = &alp;
222
223 T = gsl_root_fsolver_brent;
224 solver = gsl_root_fsolver_alloc (T);
225 gsl_root_fsolver_set (solver, &F, x_lo, x_hi);
226
227 do
228 {
229 iter++;
230 status = gsl_root_fsolver_iterate (solver);
231 t = gsl_root_fsolver_root (solver);
232 x_lo = gsl_root_fsolver_x_lower (solver);
233 x_hi = gsl_root_fsolver_x_upper (solver);
234 status = gsl_root_test_interval (x_lo, x_hi,
235 0, alp.tol);
236
237 //if (status == GSL_SUCCESS)
238 // printf ("Converged:\n");
239
240 }
241 while (status == GSL_CONTINUE && iter < max_iter);
242 return t;
243}
244
245double polish (double t, arc_length_params &alp) {
246 int status;
247 int iter = 0, max_iter = 5;
248 const gsl_root_fdfsolver_type *T;
249 gsl_root_fdfsolver *solver;
250 double t0;
251 gsl_function_fdf FDF;
252
253 FDF.f = &arc_length;
254 FDF.df = &arc_length_deriv;
255 FDF.fdf = &arc_length_fdf;
256 FDF.params = &alp;
257
258 T = gsl_root_fdfsolver_newton;
259 solver = gsl_root_fdfsolver_alloc (T);
260 gsl_root_fdfsolver_set (solver, &FDF, t);
261
262 do
263 {
264 iter++;
265 status = gsl_root_fdfsolver_iterate (solver);
266 t0 = t;
267 t = gsl_root_fdfsolver_root (solver);
268 status = gsl_root_test_delta (t, t0, 0, alp.tol);
269
270 if (status == GSL_SUCCESS)
271 ;//printf ("Converged:\n");
272
273 printf ("%5d %10.7f %+10.7f\n",
274 iter, t, t - t0);
275 }
276 while (status == GSL_CONTINUE && iter < max_iter);
277 return t;
278}
279
280
281#endif
282
283/*
284 Local Variables:
285 mode:c++
286 c-file-style:"stroustrup"
287 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
288 indent-tabs-mode:nil
289 fill-column:99
290 End:
291*/
292// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double arc_length_subdividing(Path const &p, double tol)
Calculates the length of a path through iteration and subsequent subdivision.
double polish(double t, arc_length_params &alp)
void arc_length_integrating(Path::Elem pe, double t, double tol, double &result, double &abs_error)
Calculates the length of a path Element through gsl integration.
static double poly_length_integrating(double t, void *param)
double polish_brent(double t, arc_length_params &alp)
double cubic_length_subdividing(Path::Elem const &e, double tol)
Calculates the length of a cubic element through subdivision.
double arc_length_deriv(double t, void *params)
double arc_length(double t, void *params)
void arc_length_fdf(double t, void *params, double *y, double *dy)
Bezier fitting algorithms.
Sequence of contiguous curves, aka spline.
Definition path.h:353
const_iterator end() const
Definition path.h:465
size_type size() const
Natural size of the path.
Definition path.h:490
const_iterator begin() const
Definition path.h:464
Two-dimensional point that doubles as a vector.
Definition point.h:66
void normalize()
Normalize the vector representing the point.
Definition point.cpp:96
Polynomial in canonical (monomial) basis.
Definition polynomial.h:50
const double w
Definition conic-4.cpp:19
Css & result
constexpr Coord lerp(Coord t, Coord a, Coord b)
Numerically stable linear interpolation.
Definition coord.h:97
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
double offset
Geom::Point end
Various utility functions.
Definition affine.h:22
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
Bezier derivative(Bezier const &a)
Definition bezier.cpp:282
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
Polynomial in canonical (monomial) basis.
Definition curve.h:24