Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sbasis-to-bezier.cpp
Go to the documentation of this file.
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 */
33
34
36#include <2geom/d2.h>
37#include <2geom/choose.h>
38#include <2geom/path-sink.h>
39#include <2geom/exception.h>
40#include <2geom/convex-hull.h>
41
42#include <iostream>
43
44
45
46
47namespace Geom
48{
49
50/*
51 * Symmetric Power Basis - Bernstein Basis conversion routines
52 *
53 * some remark about precision:
54 * interval [0,1], subdivisions: 10^3
55 * - bezier_to_sbasis : up to degree ~72 precision is at least 10^-5
56 * up to degree ~87 precision is at least 10^-3
57 * - sbasis_to_bezier : up to order ~63 precision is at least 10^-15
58 * precision is at least 10^-14 even beyond order 200
59 *
60 * interval [-1,1], subdivisions: 10^3
61 * - bezier_to_sbasis : up to degree ~21 precision is at least 10^-5
62 * up to degree ~24 precision is at least 10^-3
63 * - sbasis_to_bezier : up to order ~11 precision is at least 10^-5
64 * up to order ~13 precision is at least 10^-3
65 *
66 * interval [-10,10], subdivisions: 10^3
67 * - bezier_to_sbasis : up to degree ~7 precision is at least 10^-5
68 * up to degree ~8 precision is at least 10^-3
69 * - sbasis_to_bezier : up to order ~3 precision is at least 10^-5
70 * up to order ~4 precision is at least 10^-3
71 *
72 * references:
73 * this implementation is based on the following article:
74 * J.Sanchez-Reyes - The Symmetric Analogue of the Polynomial Power Basis
75 */
76
86void sbasis_to_bezier (Bezier & bz, SBasis const& sb, size_t sz)
87{
88 assert(sb.size() > 0);
89
90 size_t q, n;
91 bool even;
92 if (sz == 0)
93 {
94 q = sb.size();
95 if (sb[q-1][0] == sb[q-1][1])
96 {
97 even = true;
98 --q;
99 n = 2*q;
100 }
101 else
102 {
103 even = false;
104 n = 2*q-1;
105 }
106 }
107 else
108 {
109 q = (sz > 2*sb.size()-1) ? sb.size() : (sz+1)/2;
110 n = sz-1;
111 even = false;
112 }
113 bz.clear();
114 bz.resize(n+1);
115 for (size_t k = 0; k < q; ++k)
116 {
117 int Tjk = 1;
118 for (size_t j = k; j < n-k; ++j) // j <= n-k-1
119 {
120 bz[j] += (Tjk * sb[k][0]);
121 bz[n-j] += (Tjk * sb[k][1]); // n-k <-> [k][1]
122 // assert(Tjk == binomial(n-2*k-1, j-k));
123 binomial_increment_k(Tjk, n-2*k-1, j-k);
124 }
125 }
126 if (even)
127 {
128 bz[q] += sb[q][0];
129 }
130 // the resulting coefficients are with respect to the scaled Bernstein
131 // basis so we need to divide them by (n, j) binomial coefficient
132 int bcj = n;
133 for (size_t j = 1; j < n; ++j)
134 {
135 bz[j] /= bcj;
136 // assert(bcj == binomial(n, j));
137 binomial_increment_k(bcj, n, j);
138 }
139 bz[0] = sb[0][0];
140 bz[n] = sb[0][1];
141}
142
143void sbasis_to_bezier(D2<Bezier> &bz, D2<SBasis> const &sb, size_t sz)
144{
145 if (sz == 0) {
146 sz = std::max(sb[X].size(), sb[Y].size())*2;
147 }
148 sbasis_to_bezier(bz[X], sb[X], sz);
149 sbasis_to_bezier(bz[Y], sb[Y], sz);
150}
151
158void sbasis_to_bezier (std::vector<Point> & bz, D2<SBasis> const& sb, size_t sz)
159{
160 D2<Bezier> bez;
161 sbasis_to_bezier(bez, sb, sz);
162 bz = bezier_points(bez);
163}
164
174void sbasis_to_cubic_bezier (std::vector<Point> & bz, D2<SBasis> const& sb)
175{
176 double delx[2], dely[2];
177 double xprime[2], yprime[2];
178 double midx = 0;
179 double midy = 0;
180 double midx_0, midy_0;
181 double numer[2], numer_0[2];
182 double denom;
183 double div;
184
185 if ((sb[X].size() == 0) || (sb[Y].size() == 0)) {
186 THROW_RANGEERROR("size of sb is too small");
187 }
188
189 sbasis_to_bezier(bz, sb, 4); // zeroth-order estimate
190 if ((sb[X].size() < 3) && (sb[Y].size() < 3))
191 return; // cubic bezier estimate is exact
192 Geom::ConvexHull bezhull(bz);
193
194// calculate first derivatives of x and y wrt t
195
196 for (int i = 0; i < 2; ++i) {
197 xprime[i] = sb[X][0][1] - sb[X][0][0];
198 yprime[i] = sb[Y][0][1] - sb[Y][0][0];
199 }
200 if (sb[X].size() > 1) {
201 xprime[0] += sb[X][1][0];
202 xprime[1] -= sb[X][1][1];
203 }
204 if (sb[Y].size() > 1) {
205 yprime[0] += sb[Y][1][0];
206 yprime[1] -= sb[Y][1][1];
207 }
208
209// calculate midpoint at t = 0.5
210
211 div = 2;
212 for (auto i : sb[X]) {
213 midx += (i[0] + i[1])/div;
214 div *= 4;
215 }
216
217 div = 2;
218 for (auto i : sb[Y]) {
219 midy += (i[0] + i[1])/div;
220 div *= 4;
221 }
222
223// is midpoint in hull: if not, the solution will be ill-conditioned, LP Bug 1428683
224
225 if (!bezhull.contains(Geom::Point(midx, midy)))
226 return;
227
228// calculate Bezier control arms
229
230 midx = 8*midx - 4*bz[0][X] - 4*bz[3][X]; // re-define relative to center
231 midy = 8*midy - 4*bz[0][Y] - 4*bz[3][Y];
232 midx_0 = sb[X].size() > 1 ? sb[X][1][0] + sb[X][1][1] : 0; // zeroth order estimate
233 midy_0 = sb[Y].size() > 1 ? sb[Y][1][0] + sb[Y][1][1] : 0;
234
235 if ((std::abs(xprime[0]) < EPSILON) && (std::abs(yprime[0]) < EPSILON)
236 && ((std::abs(xprime[1]) > EPSILON) || (std::abs(yprime[1]) > EPSILON))) { // degenerate handle at 0 : use distance of closest approach
237 numer[0] = midx*xprime[1] + midy*yprime[1];
238 denom = 3.0*(xprime[1]*xprime[1] + yprime[1]*yprime[1]);
239 delx[0] = 0;
240 dely[0] = 0;
241 delx[1] = -xprime[1]*numer[0]/denom;
242 dely[1] = -yprime[1]*numer[0]/denom;
243 } else if ((std::abs(xprime[1]) < EPSILON) && (std::abs(yprime[1]) < EPSILON)
244 && ((std::abs(xprime[0]) > EPSILON) || (std::abs(yprime[0]) > EPSILON))) { // degenerate handle at 1 : ditto
245 numer[1] = midx*xprime[0] + midy*yprime[0];
246 denom = 3.0*(xprime[0]*xprime[0] + yprime[0]*yprime[0]);
247 delx[0] = xprime[0]*numer[1]/denom;
248 dely[0] = yprime[0]*numer[1]/denom;
249 delx[1] = 0;
250 dely[1] = 0;
251 } else if (std::abs(xprime[1]*yprime[0] - yprime[1]*xprime[0]) > // general case : fit mid fxn value
252 0.002 * std::abs(xprime[1]*xprime[0] + yprime[1]*yprime[0])) { // approx. 0.1 degree of angle
253 double test1 = (bz[1][Y] - bz[0][Y])*(bz[3][X] - bz[0][X]) - (bz[1][X] - bz[0][X])*(bz[3][Y] - bz[0][Y]);
254 double test2 = (bz[2][Y] - bz[0][Y])*(bz[3][X] - bz[0][X]) - (bz[2][X] - bz[0][X])*(bz[3][Y] - bz[0][Y]);
255 if (test1*test2 < 0) // reject anti-symmetric case, LP Bug 1428267 & Bug 1428683
256 return;
257 denom = 3.0*(xprime[1]*yprime[0] - yprime[1]*xprime[0]);
258 for (int i = 0; i < 2; ++i) {
259 numer_0[i] = xprime[1 - i]*midy_0 - yprime[1 - i]*midx_0;
260 numer[i] = xprime[1 - i]*midy - yprime[1 - i]*midx;
261 delx[i] = xprime[i]*numer[i]/denom;
262 dely[i] = yprime[i]*numer[i]/denom;
263 if (numer_0[i]*numer[i] < 0) // check for reversal of direction, LP Bug 1544680
264 return;
265 }
266 if (std::abs((numer[0] - numer_0[0])*numer_0[1]) > 10.0*std::abs((numer[1] - numer_0[1])*numer_0[0]) // check for asymmetry
267 || std::abs((numer[1] - numer_0[1])*numer_0[0]) > 10.0*std::abs((numer[0] - numer_0[0])*numer_0[1]))
268 return;
269 } else if ((xprime[0]*xprime[1] < 0) || (yprime[0]*yprime[1] < 0)) { // symmetric case : use distance of closest approach
270 numer[0] = midx*xprime[0] + midy*yprime[0];
271 denom = 6.0*(xprime[0]*xprime[0] + yprime[0]*yprime[0]);
272 delx[0] = xprime[0]*numer[0]/denom;
273 dely[0] = yprime[0]*numer[0]/denom;
274 delx[1] = -delx[0];
275 dely[1] = -dely[0];
276 } else { // anti-symmetric case : fit mid slope
277 // calculate slope at t = 0.5
278 midx = 0;
279 div = 1;
280 for (auto i : sb[X]) {
281 midx += (i[1] - i[0])/div;
282 div *= 4;
283 }
284 midy = 0;
285 div = 1;
286 for (auto i : sb[Y]) {
287 midy += (i[1] - i[0])/div;
288 div *= 4;
289 }
290 if (midx*yprime[0] != midy*xprime[0]) {
291 denom = midx*yprime[0] - midy*xprime[0];
292 numer[0] = midx*(bz[3][Y] - bz[0][Y]) - midy*(bz[3][X] - bz[0][X]);
293 for (int i = 0; i < 2; ++i) {
294 delx[i] = xprime[0]*numer[0]/denom;
295 dely[i] = yprime[0]*numer[0]/denom;
296 }
297 } else { // linear case
298 for (int i = 0; i < 2; ++i) {
299 delx[i] = (bz[3][X] - bz[0][X])/3;
300 dely[i] = (bz[3][Y] - bz[0][Y])/3;
301 }
302 }
303 }
304 bz[1][X] = bz[0][X] + delx[0];
305 bz[1][Y] = bz[0][Y] + dely[0];
306 bz[2][X] = bz[3][X] - delx[1];
307 bz[2][Y] = bz[3][Y] - dely[1];
308}
309
318void bezier_to_sbasis (SBasis & sb, Bezier const& bz)
319{
320 size_t n = bz.order();
321 size_t q = (n+1) / 2;
322 size_t even = (n & 1u) ? 0 : 1;
323 sb.clear();
324 sb.resize(q + even, Linear(0, 0));
325 int nck = 1;
326 for (size_t k = 0; k < q; ++k)
327 {
328 int Tjk = nck;
329 for (size_t j = k; j < q; ++j)
330 {
331 sb[j][0] += (Tjk * bz[k]);
332 sb[j][1] += (Tjk * bz[n-k]); // n-j <-> [j][1]
333 // assert(Tjk == sgn(j, k) * binomial(n-j-k, j-k) * binomial(n, k));
334 binomial_increment_k(Tjk, n-j-k, j-k);
335 binomial_decrement_n(Tjk, n-j-k, j-k+1);
336 Tjk = -Tjk;
337 }
338 Tjk = -nck;
339 for (size_t j = k+1; j < q; ++j)
340 {
341 sb[j][0] += (Tjk * bz[n-k]);
342 sb[j][1] += (Tjk * bz[k]); // n-j <-> [j][1]
343 // assert(Tjk == sgn(j, k) * binomial(n-j-k-1, j-k-1) * binomial(n, k));
344 binomial_increment_k(Tjk, n-j-k-1, j-k-1);
345 binomial_decrement_n(Tjk, n-j-k-1, j-k);
346 Tjk = -Tjk;
347 }
348 // assert(nck == binomial(n, k));
349 binomial_increment_k(nck, n, k);
350 }
351 if (even)
352 {
353 int Tjk = q & 1 ? -1 : 1;
354 for (size_t k = 0; k < q; ++k)
355 {
356 sb[q][0] += (Tjk * (bz[k] + bz[n-k]));
357 // assert(Tjk == sgn(q,k) * binomial(n, k));
358 binomial_increment_k(Tjk, n, k);
359 Tjk = -Tjk;
360 }
361 // assert(Tjk == binomial(n, q));
362 sb[q][0] += Tjk * bz[q];
363 sb[q][1] = sb[q][0];
364 }
365 sb[0][0] = bz[0];
366 sb[0][1] = bz[n];
367}
368
377void bezier_to_sbasis (D2<SBasis> & sb, std::vector<Point> const& bz)
378{
379 size_t n = bz.size() - 1;
380 size_t q = (n+1) / 2;
381 size_t even = (n & 1u) ? 0 : 1;
382 sb[X].clear();
383 sb[Y].clear();
384 sb[X].resize(q + even, Linear(0, 0));
385 sb[Y].resize(q + even, Linear(0, 0));
386 int nck = 1;
387 for (size_t k = 0; k < q; ++k)
388 {
389 int Tjk = nck;
390 for (size_t j = k; j < q; ++j)
391 {
392 sb[X][j][0] += (Tjk * bz[k][X]);
393 sb[X][j][1] += (Tjk * bz[n-k][X]);
394 sb[Y][j][0] += (Tjk * bz[k][Y]);
395 sb[Y][j][1] += (Tjk * bz[n-k][Y]);
396 // assert(Tjk == sgn(j, k) * binomial(n-j-k, j-k) * binomial(n, k));
397 binomial_increment_k(Tjk, n-j-k, j-k);
398 binomial_decrement_n(Tjk, n-j-k, j-k+1);
399 Tjk = -Tjk;
400 }
401 Tjk = -nck;
402 for (size_t j = k+1; j < q; ++j)
403 {
404 sb[X][j][0] += (Tjk * bz[n-k][X]);
405 sb[X][j][1] += (Tjk * bz[k][X]);
406 sb[Y][j][0] += (Tjk * bz[n-k][Y]);
407 sb[Y][j][1] += (Tjk * bz[k][Y]);
408 // assert(Tjk == sgn(j, k) * binomial(n-j-k-1, j-k-1) * binomial(n, k));
409 binomial_increment_k(Tjk, n-j-k-1, j-k-1);
410 binomial_decrement_n(Tjk, n-j-k-1, j-k);
411 Tjk = -Tjk;
412 }
413 // assert(nck == binomial(n, k));
414 binomial_increment_k(nck, n, k);
415 }
416 if (even)
417 {
418 int Tjk = q & 1 ? -1 : 1;
419 for (size_t k = 0; k < q; ++k)
420 {
421 sb[X][q][0] += (Tjk * (bz[k][X] + bz[n-k][X]));
422 sb[Y][q][0] += (Tjk * (bz[k][Y] + bz[n-k][Y]));
423 // assert(Tjk == sgn(q,k) * binomial(n, k));
424 binomial_increment_k(Tjk, n, k);
425 Tjk = -Tjk;
426 }
427 // assert(Tjk == binomial(n, q));
428 sb[X][q][0] += Tjk * bz[q][X];
429 sb[X][q][1] = sb[X][q][0];
430 sb[Y][q][0] += Tjk * bz[q][Y];
431 sb[Y][q][1] = sb[Y][q][0];
432 }
433 sb[X][0][0] = bz[0][X];
434 sb[X][0][1] = bz[n][X];
435 sb[Y][0][0] = bz[0][Y];
436 sb[Y][0][1] = bz[n][Y];
437}
438
439} // namespace Geom
440
441#if 0
442/*
443* This version works by inverting a reasonable upper bound on the error term after subdividing the
444* curve at $a$. We keep biting off pieces until there is no more curve left.
445*
446* Derivation: The tail of the power series is $a_ks^k + a_{k+1}s^{k+1} + \ldots = e$. A
447* subdivision at $a$ results in a tail error of $e*A^k, A = (1-a)a$. Let this be the desired
448* tolerance tol $= e*A^k$ and invert getting $A = e^{1/k}$ and $a = 1/2 - \sqrt{1/4 - A}$
449*/
450void
451subpath_from_sbasis_incremental(Geom::OldPathSetBuilder &pb, D2<SBasis> B, double tol, bool initial) {
452 const unsigned k = 2; // cubic bezier
453 double te = B.tail_error(k);
454 assert(B[0].std::isfinite());
455 assert(B[1].std::isfinite());
456
457 //std::cout << "tol = " << tol << std::endl;
458 while(1) {
459 double A = std::sqrt(tol/te); // pow(te, 1./k)
460 double a = A;
461 if(A < 1) {
462 A = std::min(A, 0.25);
463 a = 0.5 - std::sqrt(0.25 - A); // quadratic formula
464 if(a > 1) a = 1; // clamp to the end of the segment
465 } else
466 a = 1;
467 assert(a > 0);
468 //std::cout << "te = " << te << std::endl;
469 //std::cout << "A = " << A << "; a=" << a << std::endl;
470 D2<SBasis> Bs = compose(B, Linear(0, a));
471 assert(Bs.tail_error(k));
472 std::vector<Geom::Point> bez = sbasis_to_bezier(Bs, 2);
473 reverse(bez.begin(), bez.end());
474 if (initial) {
475 pb.start_subpath(bez[0]);
476 initial = false;
477 }
478 pb.push_cubic(bez[1], bez[2], bez[3]);
479
480// move to next piece of curve
481 if(a >= 1) break;
482 B = compose(B, Linear(a, 1));
483 te = B.tail_error(k);
484 }
485}
486
487#endif
488
489namespace Geom{
490
497void build_from_sbasis(Geom::PathBuilder &pb, D2<SBasis> const &B, double tol, bool only_cubicbeziers) {
498 if (!B.isFinite()) {
499 THROW_EXCEPTION("assertion failed: B.isFinite()");
500 }
501 if(tail_error(B, 3) < tol || sbasis_size(B) == 2) { // nearly cubic enough
502 if( !only_cubicbeziers && (sbasis_size(B) <= 1) ) {
503 pb.lineTo(B.at1());
504 } else {
505 std::vector<Geom::Point> bez;
506// sbasis_to_bezier(bez, B, 4);
508 pb.curveTo(bez[1], bez[2], bez[3]);
509 }
510 } else {
511 build_from_sbasis(pb, compose(B, Linear(0, 0.5)), tol, only_cubicbeziers);
512 build_from_sbasis(pb, compose(B, Linear(0.5, 1)), tol, only_cubicbeziers);
513 }
514}
515
522Path
523path_from_sbasis(D2<SBasis> const &B, double tol, bool only_cubicbeziers) {
524 PathBuilder pb;
525 pb.moveTo(B.at0());
526 build_from_sbasis(pb, B, tol, only_cubicbeziers);
527 pb.flush();
528 return pb.peek().front();
529}
530
538PathVector
539path_from_piecewise(Geom::Piecewise<Geom::D2<Geom::SBasis> > const &B, double tol, bool only_cubicbeziers) {
541 if(B.size() == 0) return pb.peek();
542 Geom::Point start = B[0].at0();
543 pb.moveTo(start);
544 for(unsigned i = 0; ; i++) {
545 if ( (i+1 == B.size())
546 || !are_near(B[i+1].at0(), B[i].at1(), tol) )
547 {
548 //start of a new path
549 if (are_near(start, B[i].at1()) && sbasis_size(B[i]) <= 1) {
550 pb.closePath();
551 //last line seg already there (because of .closePath())
552 goto no_add;
553 }
554 build_from_sbasis(pb, B[i], tol, only_cubicbeziers);
555 if (are_near(start, B[i].at1())) {
556 //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.
557 pb.closePath();
558 }
559 no_add:
560 if (i+1 >= B.size()) {
561 break;
562 }
563 start = B[i+1].at0();
564 pb.moveTo(start);
565 } else {
566 build_from_sbasis(pb, B[i], tol, only_cubicbeziers);
567 }
568 }
569 pb.flush();
570 return pb.peek();
571}
572
573}
574
575/*
576 Local Variables:
577 mode:c++
578 c-file-style:"stroustrup"
579 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
580 indent-tabs-mode:nil
581 fill-column:99
582 End:
583*/
584// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Defines the different types of exceptions that 2geom can throw.
Calculation of binomial cefficients.
Polynomial in Bernstein-Bezier basis.
Definition bezier.h:126
void resize(unsigned int n, Coord v=0)
Definition bezier.h:245
void clear()
Definition bezier.h:248
unsigned order() const
Definition bezier.h:145
Convex hull based on the Andrew's monotone chain algorithm.
bool contains(Point const &p) const
Check whether the given point is inside the hull.
Adaptor that creates 2D functions from 1D ones.
Definition d2.h:55
bool isFinite() const
Definition d2.h:117
Point at1() const
Definition d2.h:125
Point at0() const
Definition d2.h:121
Function that interpolates linearly between two values.
Definition linear.h:55
Store paths to a PathVector.
Definition path-sink.h:226
PathVector const & peek() const
Retrieve the path.
Definition path-sink.h:236
void moveTo(Point const &p) override
Move to a different point without creating a segment.
Definition path-sink.h:121
void lineTo(Point const &p) override
Output a line segment.
Definition path-sink.h:137
void flush() override
Flush any internal state of the generator.
Definition path-sink.h:196
void curveTo(Point const &c0, Point const &c1, Point const &p) override
Output a quadratic Bezier segment.
Definition path-sink.h:153
void closePath() override
Close the current path with a line segment.
Definition path-sink.h:189
Function defined as discrete pieces.
Definition piecewise.h:71
Two-dimensional point that doubles as a vector.
Definition point.h:66
Polynomial in symmetric power basis.
Definition sbasis.h:70
size_t size() const
Definition sbasis.h:76
void resize(unsigned n)
Definition sbasis.h:98
void clear()
Definition sbasis.h:101
Path and its polyline approximation.
Definition Path.h:93
Convex hull data structures.
SBasisOf< double > compose(SBasisOf< SBasisOf< double > > const &f, SBasisOf< double > const &x, SBasisOf< double > const &y)
Definition convole.cpp:97
Geom::IntPoint size
Lifts one dimensional objects into 2D.
constexpr Coord EPSILON
Default "acceptably small" value.
Definition coord.h:84
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Geom::Point start
Various utility functions.
Definition affine.h:22
Path path_from_sbasis(D2< SBasis > const &B, double tol, bool only_cubicbeziers=false)
Make a path from a d2 sbasis.
std::vector< Point > bezier_points(const D2< Bezier > &a)
Definition bezier.h:352
constexpr void binomial_decrement_n(T &b, int n, int k)
Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n - 1,...
Definition choose.h:52
void sbasis_to_cubic_bezier(std::vector< Point > &bz, D2< SBasis > const &sb)
Changes the basis of p to be Bernstein.
constexpr void binomial_increment_k(T &b, int n, int k)
Given a multiple of binomial(n, k), modify it to the same multiple of binomial(n, k + 1).
Definition choose.h:61
void build_from_sbasis(PathBuilder &pb, D2< SBasis > const &B, double tol, bool only_cubicbeziers)
Make a path from a d2 sbasis.
unsigned sbasis_size(D2< SBasis > const &a)
Definition d2-sbasis.cpp:56
PathVector path_from_piecewise(Piecewise< D2< SBasis > > const &B, double tol, bool only_cubicbeziers=false)
Make a path from a d2 sbasis.
double tail_error(D2< SBasis > const &a, unsigned tail)
Definition d2-sbasis.cpp:61
D2< T > compose(D2< T > const &a, T const &b)
Definition d2.h:405
void sbasis_to_bezier(Bezier &bz, SBasis const &sb, size_t sz=0)
Changes the basis of p to be bernstein.
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
SBasis bezier_to_sbasis(Coord const *handles, unsigned order)
callback interface for SVG path data
double test1[][4]
double test2[][4]
void subpath_from_sbasis_incremental(Geom::OldPathSetBuilder &pb, D2< SBasis > B, double tol, bool initial)
Conversion between SBasis and Bezier basis polynomials.