ce5bf89bce3999a9e7a2e4e21e0ad8a7bc28bdb3
1 /*
2 * Symmetric Power Basis - Bernstein Basis conversion routines
3 *
4 * Authors:
5 * Marco Cecchetti <mrcekets at gmail.com>
6 * Nathan Hurst <njh@mail.csse.monash.edu.au>
7 *
8 * Copyright 2007-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 */
35 #include <2geom/sbasis-to-bezier.h>
36 #include <2geom/d2.h>
37 #include <2geom/choose.h>
38 #include <2geom/svg-path.h>
39 #include <2geom/exception.h>
41 #include <iostream>
46 namespace Geom
47 {
49 /*
50 * Symmetric Power Basis - Bernstein Basis conversion routines
51 *
52 * some remark about precision:
53 * interval [0,1], subdivisions: 10^3
54 * - bezier_to_sbasis : up to degree ~72 precision is at least 10^-5
55 * up to degree ~87 precision is at least 10^-3
56 * - sbasis_to_bezier : up to order ~63 precision is at least 10^-15
57 * precision is at least 10^-14 even beyond order 200
58 *
59 * interval [-1,1], subdivisions: 10^3
60 * - bezier_to_sbasis : up to degree ~21 precision is at least 10^-5
61 * up to degree ~24 precision is at least 10^-3
62 * - sbasis_to_bezier : up to order ~11 precision is at least 10^-5
63 * up to order ~13 precision is at least 10^-3
64 *
65 * interval [-10,10], subdivisions: 10^3
66 * - bezier_to_sbasis : up to degree ~7 precision is at least 10^-5
67 * up to degree ~8 precision is at least 10^-3
68 * - sbasis_to_bezier : up to order ~3 precision is at least 10^-5
69 * up to order ~4 precision is at least 10^-3
70 *
71 * references:
72 * this implementation is based on the following article:
73 * J.Sanchez-Reyes - The Symmetric Analogue of the Polynomial Power Basis
74 */
76 inline
77 double binomial(unsigned int n, unsigned int k)
78 {
79 return choose<double>(n, k);
80 }
82 inline
83 int sgn(unsigned int j, unsigned int k)
84 {
85 assert (j >= k);
86 // we are sure that j >= k
87 return ((j-k) & 1u) ? -1 : 1;
88 }
91 /** Changes the basis of p to be bernstein.
92 \param p the Symmetric basis polynomial
93 \returns the Bernstein basis polynomial
95 if the degree is even q is the order in the symmetrical power basis,
96 if the degree is odd q is the order + 1
97 n is always the polynomial degree, i. e. the Bezier order
98 sz is the number of bezier handles.
99 */
100 void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz)
101 {
102 size_t q, n;
103 bool even;
104 if (sz == 0)
105 {
106 q = sb.size();
107 if (sb[q-1][0] == sb[q-1][1])
108 {
109 even = true;
110 --q;
111 n = 2*q;
112 }
113 else
114 {
115 even = false;
116 n = 2*q-1;
117 }
118 }
119 else
120 {
121 q = (sz > 2*sb.size()-1) ? sb.size() : (sz+1)/2;
122 n = sz-1;
123 even = false;
124 }
125 bz.clear();
126 bz.resize(n+1);
127 double Tjk;
128 for (size_t k = 0; k < q; ++k)
129 {
130 for (size_t j = k; j < n-k; ++j) // j <= n-k-1
131 {
132 Tjk = binomial(n-2*k-1, j-k);
133 bz[j] += (Tjk * sb[k][0]);
134 bz[n-j] += (Tjk * sb[k][1]); // n-k <-> [k][1]
135 }
136 }
137 if (even)
138 {
139 bz[q] += sb[q][0];
140 }
141 // the resulting coefficients are with respect to the scaled Bernstein
142 // basis so we need to divide them by (n, j) binomial coefficient
143 for (size_t j = 1; j < n; ++j)
144 {
145 bz[j] /= binomial(n, j);
146 }
147 bz[0] = sb[0][0];
148 bz[n] = sb[0][1];
149 }
151 /** Changes the basis of p to be Bernstein.
152 \param p the D2 Symmetric basis polynomial
153 \returns the D2 Bernstein basis polynomial
155 sz is always the polynomial degree, i. e. the Bezier order
156 */
157 void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz)
158 {
159 Bezier bzx, bzy;
160 if(sz == 0) {
161 sz = std::max(sb[X].size(), sb[Y].size())*2;
162 }
163 sbasis_to_bezier(bzx, sb[X], sz);
164 sbasis_to_bezier(bzy, sb[Y], sz);
165 assert(bzx.size() == bzy.size());
166 size_t n = (bzx.size() >= bzy.size()) ? bzx.size() : bzy.size();
168 bz.resize(n, Point(0,0));
169 for (size_t i = 0; i < bzx.size(); ++i)
170 {
171 bz[i][X] = bzx[i];
172 }
173 for (size_t i = 0; i < bzy.size(); ++i)
174 {
175 bz[i][Y] = bzy[i];
176 }
177 }
180 /** Changes the basis of p to be sbasis.
181 \param p the Bernstein basis polynomial
182 \returns the Symmetric basis polynomial
184 if the degree is even q is the order in the symmetrical power basis,
185 if the degree is odd q is the order + 1
186 n is always the polynomial degree, i. e. the Bezier order
187 */
188 void bezier_to_sbasis (SBasis & sb, Bezier const& bz)
189 {
190 size_t n = bz.order();
191 size_t q = (n+1) / 2;
192 size_t even = (n & 1u) ? 0 : 1;
193 sb.clear();
194 sb.resize(q + even, Linear(0, 0));
195 double Tjk;
196 for (size_t k = 0; k < q; ++k)
197 {
198 for (size_t j = k; j < q; ++j)
199 {
200 Tjk = sgn(j, k) * binomial(n-j-k, j-k) * binomial(n, k);
201 sb[j][0] += (Tjk * bz[k]);
202 sb[j][1] += (Tjk * bz[n-k]); // n-j <-> [j][1]
203 }
204 for (size_t j = k+1; j < q; ++j)
205 {
206 Tjk = sgn(j, k) * binomial(n-j-k-1, j-k-1) * binomial(n, k);
207 sb[j][0] += (Tjk * bz[n-k]);
208 sb[j][1] += (Tjk * bz[k]); // n-j <-> [j][1]
209 }
210 }
211 if (even)
212 {
213 for (size_t k = 0; k < q; ++k)
214 {
215 Tjk = sgn(q,k) * binomial(n, k);
216 sb[q][0] += (Tjk * (bz[k] + bz[n-k]));
217 }
218 sb[q][0] += (binomial(n, q) * bz[q]);
219 sb[q][1] = sb[q][0];
220 }
221 sb[0][0] = bz[0];
222 sb[0][1] = bz[n];
223 }
226 /** Changes the basis of d2 p to be sbasis.
227 \param p the d2 Bernstein basis polynomial
228 \returns the d2 Symmetric basis polynomial
230 if the degree is even q is the order in the symmetrical power basis,
231 if the degree is odd q is the order + 1
232 n is always the polynomial degree, i. e. the Bezier order
233 */
234 void bezier_to_sbasis (D2<SBasis> & sb, std::vector<Point> const& bz)
235 {
236 size_t n = bz.size() - 1;
237 size_t q = (n+1) / 2;
238 size_t even = (n & 1u) ? 0 : 1;
239 sb[X].clear();
240 sb[Y].clear();
241 sb[X].resize(q + even, Linear(0, 0));
242 sb[Y].resize(q + even, Linear(0, 0));
243 double Tjk;
244 for (size_t k = 0; k < q; ++k)
245 {
246 for (size_t j = k; j < q; ++j)
247 {
248 Tjk = sgn(j, k) * binomial(n-j-k, j-k) * binomial(n, k);
249 sb[X][j][0] += (Tjk * bz[k][X]);
250 sb[X][j][1] += (Tjk * bz[n-k][X]);
251 sb[Y][j][0] += (Tjk * bz[k][Y]);
252 sb[Y][j][1] += (Tjk * bz[n-k][Y]);
253 }
254 for (size_t j = k+1; j < q; ++j)
255 {
256 Tjk = sgn(j, k) * binomial(n-j-k-1, j-k-1) * binomial(n, k);
257 sb[X][j][0] += (Tjk * bz[n-k][X]);
258 sb[X][j][1] += (Tjk * bz[k][X]);
259 sb[Y][j][0] += (Tjk * bz[n-k][Y]);
260 sb[Y][j][1] += (Tjk * bz[k][Y]);
261 }
262 }
263 if (even)
264 {
265 for (size_t k = 0; k < q; ++k)
266 {
267 Tjk = sgn(q,k) * binomial(n, k);
268 sb[X][q][0] += (Tjk * (bz[k][X] + bz[n-k][X]));
269 sb[Y][q][0] += (Tjk * (bz[k][Y] + bz[n-k][Y]));
270 }
271 sb[X][q][0] += (binomial(n, q) * bz[q][X]);
272 sb[X][q][1] = sb[X][q][0];
273 sb[Y][q][0] += (binomial(n, q) * bz[q][Y]);
274 sb[Y][q][1] = sb[Y][q][0];
275 }
276 sb[X][0][0] = bz[0][X];
277 sb[X][0][1] = bz[n][X];
278 sb[Y][0][0] = bz[0][Y];
279 sb[Y][0][1] = bz[n][Y];
280 }
283 } // end namespace Geom
286 #if 0
287 /*
288 * This version works by inverting a reasonable upper bound on the error term after subdividing the
289 * curve at $a$. We keep biting off pieces until there is no more curve left.
290 *
291 * Derivation: The tail of the power series is $a_ks^k + a_{k+1}s^{k+1} + \ldots = e$. A
292 * subdivision at $a$ results in a tail error of $e*A^k, A = (1-a)a$. Let this be the desired
293 * tolerance tol $= e*A^k$ and invert getting $A = e^{1/k}$ and $a = 1/2 - \sqrt{1/4 - A}$
294 */
295 void
296 subpath_from_sbasis_incremental(Geom::OldPathSetBuilder &pb, D2<SBasis> B, double tol, bool initial) {
297 const unsigned k = 2; // cubic bezier
298 double te = B.tail_error(k);
299 assert(B[0].IS_FINITE());
300 assert(B[1].IS_FINITE());
302 //std::cout << "tol = " << tol << std::endl;
303 while(1) {
304 double A = std::sqrt(tol/te); // pow(te, 1./k)
305 double a = A;
306 if(A < 1) {
307 A = std::min(A, 0.25);
308 a = 0.5 - std::sqrt(0.25 - A); // quadratic formula
309 if(a > 1) a = 1; // clamp to the end of the segment
310 } else
311 a = 1;
312 assert(a > 0);
313 //std::cout << "te = " << te << std::endl;
314 //std::cout << "A = " << A << "; a=" << a << std::endl;
315 D2<SBasis> Bs = compose(B, Linear(0, a));
316 assert(Bs.tail_error(k));
317 std::vector<Geom::Point> bez = sbasis_to_bezier(Bs, 2);
318 reverse(bez.begin(), bez.end());
319 if (initial) {
320 pb.start_subpath(bez[0]);
321 initial = false;
322 }
323 pb.push_cubic(bez[1], bez[2], bez[3]);
325 // move to next piece of curve
326 if(a >= 1) break;
327 B = compose(B, Linear(a, 1));
328 te = B.tail_error(k);
329 }
330 }
332 #endif
334 namespace Geom{
336 /** Make a path from a d2 sbasis.
337 \param p the d2 Symmetric basis polynomial
338 \returns a Path
340 If only_cubicbeziers is true, the resulting path may only contain CubicBezier curves.
341 */
342 void build_from_sbasis(Geom::PathBuilder &pb, D2<SBasis> const &B, double tol, bool only_cubicbeziers) {
343 if (!B.isFinite()) {
344 THROW_EXCEPTION("assertion failed: B.isFinite()");
345 }
346 if(tail_error(B, 2) < tol || sbasis_size(B) == 2) { // nearly cubic enough
347 if( !only_cubicbeziers && (sbasis_size(B) <= 1) ) {
348 pb.lineTo(B.at1());
349 } else {
350 std::vector<Geom::Point> bez;
351 sbasis_to_bezier(bez, B, 4);
352 pb.curveTo(bez[1], bez[2], bez[3]);
353 }
354 } else {
355 build_from_sbasis(pb, compose(B, Linear(0, 0.5)), tol, only_cubicbeziers);
356 build_from_sbasis(pb, compose(B, Linear(0.5, 1)), tol, only_cubicbeziers);
357 }
358 }
360 /** Make a path from a d2 sbasis.
361 \param p the d2 Symmetric basis polynomial
362 \returns a Path
364 If only_cubicbeziers is true, the resulting path may only contain CubicBezier curves.
365 */
366 Path
367 path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers) {
368 PathBuilder pb;
369 pb.moveTo(B.at0());
370 build_from_sbasis(pb, B, tol, only_cubicbeziers);
371 pb.finish();
372 return pb.peek().front();
373 }
375 /** Make a path from a d2 sbasis.
376 \param p the d2 Symmetric basis polynomial
377 \returns a Path
379 If only_cubicbeziers is true, the resulting path may only contain CubicBezier curves.
380 TODO: some of this logic should be lifted into svg-path
381 */
382 std::vector<Geom::Path>
383 path_from_piecewise(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol, bool only_cubicbeziers) {
384 Geom::PathBuilder pb;
385 if(B.size() == 0) return pb.peek();
386 Geom::Point start = B[0].at0();
387 pb.moveTo(start);
388 for(unsigned i = 0; ; i++) {
389 if(i+1 == B.size() || !are_near(B[i+1].at0(), B[i].at1(), tol)) {
390 //start of a new path
391 if(are_near(start, B[i].at1()) && sbasis_size(B[i]) <= 1) {
392 pb.closePath();
393 //last line seg already there (because of .closePath())
394 goto no_add;
395 }
396 build_from_sbasis(pb, B[i], tol, only_cubicbeziers);
397 if(are_near(start, B[i].at1())) {
398 //it's closed, the last closing segment was not a straight line so it needed to be added, but still make it closed here with degenerate straight line.
399 pb.closePath();
400 }
401 no_add:
402 if(i+1 >= B.size()) break;
403 start = B[i+1].at0();
404 pb.moveTo(start);
405 } else {
406 build_from_sbasis(pb, B[i], tol, only_cubicbeziers);
407 }
408 }
409 pb.finish();
410 return pb.peek();
411 }
413 }
415 /*
416 Local Variables:
417 mode:c++
418 c-file-style:"stroustrup"
419 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
420 indent-tabs-mode:nil
421 fill-column:99
422 End:
423 */
424 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :