Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
bezier-clipping.cpp
Go to the documentation of this file.
1/*
2 * Implement the Bezier clipping algorithm for finding
3 * Bezier curve intersection points and collinear normals
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
35
36
38#include <2geom/choose.h>
39#include <2geom/point.h>
40#include <2geom/interval.h>
41#include <2geom/bezier.h>
43#include <2geom/convex-hull.h>
44#include <2geom/line.h>
45
46#include <cassert>
47#include <vector>
48#include <algorithm>
49#include <utility>
50//#include <iomanip>
51
52using std::swap;
53
54
55#define VERBOSE 0
56#define CHECK 0
57
58namespace Geom {
59
60namespace detail { namespace bezier_clipping {
61
63// for debugging
64//
65
66void print(std::vector<Point> const& cp, const char* msg = "")
67{
68 std::cerr << msg << std::endl;
69 for (size_t i = 0; i < cp.size(); ++i)
70 std::cerr << i << " : " << cp[i] << std::endl;
71}
72
73template< class charT >
74std::basic_ostream<charT> &
75operator<< (std::basic_ostream<charT> & os, const Interval & I)
76{
77 os << "[" << I.min() << ", " << I.max() << "]";
78 return os;
79}
80
81double angle (std::vector<Point> const& A)
82{
83 size_t n = A.size() -1;
84 double a = std::atan2(A[n][Y] - A[0][Y], A[n][X] - A[0][X]);
85 return (180 * a / M_PI);
86}
87
88size_t get_precision(Interval const& I)
89{
90 double d = I.extent();
91 double e = 0.1, p = 10;
92 int n = 0;
93 while (n < 16 && d < e)
94 {
95 p *= 10;
96 e = 1/p;
97 ++n;
98 }
99 return n;
100}
101
102void range_assertion(int k, int m, int n, const char* msg)
103{
104 if ( k < m || k > n)
105 {
106 std::cerr << "range assertion failed: \n"
107 << msg << std::endl
108 << "value: " << k
109 << " range: " << m << ", " << n << std::endl;
110 assert (k >= m && k <= n);
111 }
112}
113
114
116// numerical routines
117
118/*
119 * Compute the determinant of the 2x2 matrix with column the point P1, P2
120 */
121double det(Point const& P1, Point const& P2)
122{
123 return P1[X]*P2[Y] - P1[Y]*P2[X];
124}
125
126/*
127 * Solve the linear system [P1,P2] * P = Q
128 * in case there isn't exactly one solution the routine returns false
129 */
130bool solve(Point & P, Point const& P1, Point const& P2, Point const& Q)
131{
132 double d = det(P1, P2);
133 if (d == 0) return false;
134 d = 1 / d;
135 P[X] = det(Q, P2) * d;
136 P[Y] = det(P1, Q) * d;
137 return true;
138}
139
141// interval routines
142
143/*
144 * Map the sub-interval I in [0,1] into the interval J and assign it to J
145 */
146void map_to(Interval & J, Interval const& I)
147{
148 J.setEnds(J.valueAt(I.min()), J.valueAt(I.max()));
149}
150
152// bezier curve routines
153
154/*
155 * Return true if all the Bezier curve control points are near,
156 * false otherwise
157 */
158// Bezier.isConstant(precision)
159bool is_constant(std::vector<Point> const& A, double precision)
160{
161 for (unsigned int i = 1; i < A.size(); ++i)
162 {
163 if(!are_near(A[i], A[0], precision))
164 return false;
165 }
166 return true;
167}
168
169/*
170 * Compute the hodograph of the bezier curve B and return it in D
171 */
172// derivative(Bezier)
173void derivative(std::vector<Point> & D, std::vector<Point> const& B)
174{
175 D.clear();
176 size_t sz = B.size();
177 if (sz == 0) return;
178 if (sz == 1)
179 {
180 D.resize(1, Point(0,0));
181 return;
182 }
183 size_t n = sz-1;
184 D.reserve(n);
185 for (size_t i = 0; i < n; ++i)
186 {
187 D.push_back(n*(B[i+1] - B[i]));
188 }
189}
190
191/*
192 * Compute the hodograph of the Bezier curve B rotated of 90 degree
193 * and return it in D; we have N(t) orthogonal to B(t) for any t
194 */
195// rot90(derivative(Bezier))
196void normal(std::vector<Point> & N, std::vector<Point> const& B)
197{
198 derivative(N,B);
199 for (auto & i : N)
200 {
201 i = rot90(i);
202 }
203}
204
205/*
206 * Compute the portion of the Bezier curve "B" wrt the interval [0,t]
207 */
208// portion(Bezier, 0, t)
209void left_portion(Coord t, std::vector<Point> & B)
210{
211 size_t n = B.size();
212 for (size_t i = 1; i < n; ++i)
213 {
214 for (size_t j = n-1; j > i-1 ; --j)
215 {
216 B[j] = lerp(t, B[j-1], B[j]);
217 }
218 }
219}
220
221/*
222 * Compute the portion of the Bezier curve "B" wrt the interval [t,1]
223 */
224// portion(Bezier, t, 1)
225void right_portion(Coord t, std::vector<Point> & B)
226{
227 size_t n = B.size();
228 for (size_t i = 1; i < n; ++i)
229 {
230 for (size_t j = 0; j < n-i; ++j)
231 {
232 B[j] = lerp(t, B[j], B[j+1]);
233 }
234 }
235}
236
237/*
238 * Compute the portion of the Bezier curve "B" wrt the interval "I"
239 */
240// portion(Bezier, I)
241void portion (std::vector<Point> & B , Interval const& I)
242{
243 if (I.min() == 0)
244 {
245 if (I.max() == 1) return;
246 left_portion(I.max(), B);
247 return;
248 }
249 right_portion(I.min(), B);
250 if (I.max() == 1) return;
251 double t = I.extent() / (1 - I.min());
252 left_portion(t, B);
253}
254
255
257// tags
258
259struct intersection_point_tag;
260struct collinear_normal_tag;
261template <typename Tag>
262OptInterval clip(std::vector<Point> const& A,
263 std::vector<Point> const& B,
264 double precision);
265template <typename Tag>
266void iterate(std::vector<Interval>& domsA,
267 std::vector<Interval>& domsB,
268 std::vector<Point> const& A,
269 std::vector<Point> const& B,
270 Interval const& domA,
271 Interval const& domB,
272 double precision );
273
274
276// intersection
277
278/*
279 * Make up an orientation line using the control points c[i] and c[j]
280 * the line is returned in the output parameter "l" in the form of a 3 element
281 * vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
282 */
283// Line(c[i], c[j])
284void orientation_line (std::vector<double> & l,
285 std::vector<Point> const& c,
286 size_t i, size_t j)
287{
288 l[0] = c[j][Y] - c[i][Y];
289 l[1] = c[i][X] - c[j][X];
290 l[2] = cross(c[j], c[i]);
291 double length = std::sqrt(l[0] * l[0] + l[1] * l[1]);
292 assert (length != 0);
293 l[0] /= length;
294 l[1] /= length;
295 l[2] /= length;
296}
297
298/*
299 * Pick up an orientation line for the Bezier curve "c" and return it in
300 * the output parameter "l"
301 */
302Line pick_orientation_line (std::vector<Point> const &c, double precision)
303{
304 size_t i = c.size();
305 while (--i > 0 && are_near(c[0], c[i], precision))
306 {}
307
308 // this should never happen because when a new curve portion is created
309 // we check that it is not constant;
310 // however this requires that the precision used in the is_constant
311 // routine has to be the same used here in the are_near test
312 assert(i != 0);
313
314 Line line(c[0], c[i]);
315 return line;
316 //std::cerr << "i = " << i << std::endl;
317}
318
319/*
320 * Make up an orientation line for constant bezier curve;
321 * the orientation line is made up orthogonal to the other curve base line;
322 * the line is returned in the output parameter "l" in the form of a 3 element
323 * vector : l[0] * x + l[1] * y + l[2] == 0; the line is normalized.
324 */
325Line orthogonal_orientation_line (std::vector<Point> const &c,
326 Point const &p,
327 double precision)
328{
329 // this should never happen
330 assert(!is_constant(c, precision));
331
332 Line line(p, (c.back() - c.front()).cw() + p);
333 return line;
334}
335
336/*
337 * Compute the signed distance of the point "P" from the normalized line l
338 */
339double signed_distance(Point const &p, Line const &l)
340{
341 Coord a, b, c;
342 l.coefficients(a, b, c);
343 return a * p[X] + b * p[Y] + c;
344}
345
346/*
347 * Compute the min and max distance of the control points of the Bezier
348 * curve "c" from the normalized orientation line "l".
349 * This bounds are returned through the output Interval parameter"bound".
350 */
351Interval fat_line_bounds (std::vector<Point> const &c,
352 Line const &l)
353{
354 Interval bound(0, 0);
355 for (auto i : c) {
356 bound.expandTo(signed_distance(i, l));
357 }
358 return bound;
359}
360
361/*
362 * return the x component of the intersection point between the line
363 * passing through points p1, p2 and the line Y = "y"
364 */
365double intersect (Point const& p1, Point const& p2, double y)
366{
367 // we are sure that p2[Y] != p1[Y] because this routine is called
368 // only when the lower or the upper bound is crossed
369 double dy = (p2[Y] - p1[Y]);
370 double s = (y - p1[Y]) / dy;
371 return (p2[X]-p1[X])*s + p1[X];
372}
373
374/*
375 * Clip the Bezier curve "B" wrt the fat line defined by the orientation
376 * line "l" and the interval range "bound", the new parameter interval for
377 * the clipped curve is returned through the output parameter "dom"
378 */
379OptInterval clip_interval (std::vector<Point> const& B,
380 Line const &l,
381 Interval const &bound)
382{
383 double n = B.size() - 1; // number of sub-intervals
384 std::vector<Point> D; // distance curve control points
385 D.reserve (B.size());
386 for (size_t i = 0; i < B.size(); ++i)
387 {
388 const double d = signed_distance(B[i], l);
389 D.emplace_back(i/n, d);
390 }
391 //print(D);
392
393 ConvexHull p;
394 p.swap(D);
395 //print(p);
396
397 bool plower, phigher;
398 bool clower, chigher;
399 double t, tmin = 1, tmax = 0;
400// std::cerr << "bound : " << bound << std::endl;
401
402 plower = (p[0][Y] < bound.min());
403 phigher = (p[0][Y] > bound.max());
404 if (!(plower || phigher)) // inside the fat line
405 {
406 if (tmin > p[0][X]) tmin = p[0][X];
407 if (tmax < p[0][X]) tmax = p[0][X];
408// std::cerr << "0 : inside " << p[0]
409// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
410 }
411
412 for (size_t i = 1; i < p.size(); ++i)
413 {
414 clower = (p[i][Y] < bound.min());
415 chigher = (p[i][Y] > bound.max());
416 if (!(clower || chigher)) // inside the fat line
417 {
418 if (tmin > p[i][X]) tmin = p[i][X];
419 if (tmax < p[i][X]) tmax = p[i][X];
420// std::cerr << i << " : inside " << p[i]
421// << " : tmin = " << tmin << ", tmax = " << tmax
422// << std::endl;
423 }
424 if (clower != plower) // cross the lower bound
425 {
426 t = intersect(p[i-1], p[i], bound.min());
427 if (tmin > t) tmin = t;
428 if (tmax < t) tmax = t;
429 plower = clower;
430// std::cerr << i << " : lower " << p[i]
431// << " : tmin = " << tmin << ", tmax = " << tmax
432// << std::endl;
433 }
434 if (chigher != phigher) // cross the upper bound
435 {
436 t = intersect(p[i-1], p[i], bound.max());
437 if (tmin > t) tmin = t;
438 if (tmax < t) tmax = t;
439 phigher = chigher;
440// std::cerr << i << " : higher " << p[i]
441// << " : tmin = " << tmin << ", tmax = " << tmax
442// << std::endl;
443 }
444 }
445
446 // we have to test the closing segment for intersection
447 size_t last = p.size() - 1;
448 clower = (p[0][Y] < bound.min());
449 chigher = (p[0][Y] > bound.max());
450 if (clower != plower) // cross the lower bound
451 {
452 t = intersect(p[last], p[0], bound.min());
453 if (tmin > t) tmin = t;
454 if (tmax < t) tmax = t;
455// std::cerr << "0 : lower " << p[0]
456// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
457 }
458 if (chigher != phigher) // cross the upper bound
459 {
460 t = intersect(p[last], p[0], bound.max());
461 if (tmin > t) tmin = t;
462 if (tmax < t) tmax = t;
463// std::cerr << "0 : higher " << p[0]
464// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
465 }
466
467 if (tmin == 1 && tmax == 0) {
468 return OptInterval();
469 } else {
470 return Interval(tmin, tmax);
471 }
472}
473
474/*
475 * Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating
476 * intersection points the new parameter interval for the clipped curve
477 * is returned through the output parameter "dom"
478 */
479template <>
480OptInterval clip<intersection_point_tag> (std::vector<Point> const& A,
481 std::vector<Point> const& B,
482 double precision)
483{
484 Line bl;
485 if (is_constant(A, precision)) {
486 Point M = middle_point(A.front(), A.back());
487 bl = orthogonal_orientation_line(B, M, precision);
488 } else {
489 bl = pick_orientation_line(A, precision);
490 }
491 bl.normalize();
492 Interval bound = fat_line_bounds(A, bl);
493 return clip_interval(B, bl, bound);
494}
495
496
498// collinear normal
499
500/*
501 * Compute a closed focus for the Bezier curve B and return it in F
502 * A focus is any curve through which all lines perpendicular to B(t) pass.
503 */
504void make_focus (std::vector<Point> & F, std::vector<Point> const& B)
505{
506 assert (B.size() > 2);
507 size_t n = B.size() - 1;
508 normal(F, B);
509 Point c(1, 1);
510#if VERBOSE
511 if (!solve(c, F[0], -F[n-1], B[n]-B[0]))
512 {
513 std::cerr << "make_focus: unable to make up a closed focus" << std::endl;
514 }
515#else
516 solve(c, F[0], -F[n-1], B[n]-B[0]);
517#endif
518// std::cerr << "c = " << c << std::endl;
519
520
521 // B(t) + c(t) * N(t)
522 double n_inv = 1 / (double)(n);
523 Point c0ni;
524 F.push_back(c[1] * F[n-1]);
525 F[n] += B[n];
526 for (size_t i = n-1; i > 0; --i)
527 {
528 F[i] *= -c[0];
529 c0ni = F[i];
530 F[i] += (c[1] * F[i-1]);
531 F[i] *= (i * n_inv);
532 F[i] -= c0ni;
533 F[i] += B[i];
534 }
535 F[0] *= c[0];
536 F[0] += B[0];
537}
538
539/*
540 * Compute the projection on the plane (t, d) of the control points
541 * (t, u, D(t,u)) where D(t,u) = <(B(t) - F(u)), B'(t)> with 0 <= t, u <= 1
542 * B is a Bezier curve and F is a focus of another Bezier curve.
543 * See Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping.
544 */
545void distance_control_points (std::vector<Point> & D,
546 std::vector<Point> const& B,
547 std::vector<Point> const& F)
548{
549 assert (B.size() > 1);
550 assert (!F.empty());
551 const size_t n = B.size() - 1;
552 const size_t m = F.size() - 1;
553 const size_t r = 2 * n - 1;
554 const double r_inv = 1 / (double)(r);
555 D.clear();
556 D.reserve (B.size() * F.size());
557
558 std::vector<Point> dB;
559 dB.reserve(n);
560 for (size_t k = 0; k < n; ++k)
561 {
562 dB.push_back (B[k+1] - B[k]);
563 }
564 NL::Matrix dBB(n,B.size());
565 for (size_t i = 0; i < n; ++i)
566 for (size_t j = 0; j < B.size(); ++j)
567 dBB(i,j) = dot (dB[i], B[j]);
568 NL::Matrix dBF(n, F.size());
569 for (size_t i = 0; i < n; ++i)
570 for (size_t j = 0; j < F.size(); ++j)
571 dBF(i,j) = dot (dB[i], F[j]);
572
573 size_t l;
574 double bc;
575 Point dij;
576 std::vector<double> d(F.size());
577 int rci = 1;
578 int b1 = 1;
579 for (size_t i = 0; i <= r; ++i)
580 {
581 for (size_t j = 0; j <= m; ++j)
582 {
583 d[j] = 0;
584 }
585 const size_t k0 = std::max(i, n) - n;
586 const size_t kn = std::min(i, n-1);
587 const double bri = (double)n / rci;
588
589 // assert(rci == binomial(r, i));
590 binomial_increment_k(rci, r, i);
591
592 int b2 = b1;
593 for (size_t k = k0; k <= kn; ++k)
594 {
595 //if (k > i || (i-k) > n) continue;
596 l = i - k;
597#if CHECK
598 assert (l <= n);
599#endif
600 bc = bri * b2;
601
602 // assert(b2 == binomial(n, l) * binomial(n - 1, k));
603 binomial_decrement_k(b2, n, l);
604 binomial_increment_k(b2, n - 1, k);
605
606 for (size_t j = 0; j <= m; ++j)
607 {
608 //d[j] += bc * dot(dB[k], B[l] - F[j]);
609 d[j] += bc * (dBB(k,l) - dBF(k,j));
610 }
611 }
612
613 // assert(b1 == binomial(n, i - k0) * binomial(n - 1, k0));
614 if (i < n) {
615 binomial_increment_k(b1, n, i);
616 } else {
617 binomial_increment_k(b1, n - 1, k0);
618 }
619
620 double dmin, dmax;
621 dmin = dmax = d[m];
622 for (size_t j = 0; j < m; ++j)
623 {
624 if (dmin > d[j]) dmin = d[j];
625 if (dmax < d[j]) dmax = d[j];
626 }
627 dij[0] = i * r_inv;
628 dij[1] = dmin;
629 D.push_back (dij);
630 dij[1] = dmax;
631 D.push_back (dij);
632 }
633}
634
635/*
636 * Clip the Bezier curve "B" wrt the focus "F"; the new parameter interval for
637 * the clipped curve is returned through the output parameter "dom"
638 */
639OptInterval clip_interval (std::vector<Point> const& B,
640 std::vector<Point> const& F)
641{
642 std::vector<Point> D; // distance curve control points
644 //print(D, "D");
645// ConvexHull chD(D);
646// std::vector<Point>& p = chD.boundary; // convex hull vertices
647
648 ConvexHull p;
649 p.swap(D);
650 //print(p, "CH(D)");
651
652 bool plower, clower;
653 double t, tmin = 1, tmax = 0;
654
655 plower = (p[0][Y] < 0);
656 if (p[0][Y] == 0) // on the x axis
657 {
658 if (tmin > p[0][X]) tmin = p[0][X];
659 if (tmax < p[0][X]) tmax = p[0][X];
660// std::cerr << "0 : on x axis " << p[0]
661// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
662 }
663
664 for (size_t i = 1; i < p.size(); ++i)
665 {
666 clower = (p[i][Y] < 0);
667 if (p[i][Y] == 0) // on x axis
668 {
669 if (tmin > p[i][X]) tmin = p[i][X];
670 if (tmax < p[i][X]) tmax = p[i][X];
671// std::cerr << i << " : on x axis " << p[i]
672// << " : tmin = " << tmin << ", tmax = " << tmax
673// << std::endl;
674 }
675 else if (clower != plower) // cross the x axis
676 {
677 t = intersect(p[i-1], p[i], 0);
678 if (tmin > t) tmin = t;
679 if (tmax < t) tmax = t;
680 plower = clower;
681// std::cerr << i << " : lower " << p[i]
682// << " : tmin = " << tmin << ", tmax = " << tmax
683// << std::endl;
684 }
685 }
686
687 // we have to test the closing segment for intersection
688 size_t last = p.size() - 1;
689 clower = (p[0][Y] < 0);
690 if (clower != plower) // cross the x axis
691 {
692 t = intersect(p[last], p[0], 0);
693 if (tmin > t) tmin = t;
694 if (tmax < t) tmax = t;
695// std::cerr << "0 : lower " << p[0]
696// << " : tmin = " << tmin << ", tmax = " << tmax << std::endl;
697 }
698 if (tmin == 1 && tmax == 0) {
699 return OptInterval();
700 } else {
701 return Interval(tmin, tmax);
702 }
703}
704
705/*
706 * Clip the Bezier curve "B" wrt the Bezier curve "A" for individuating
707 * points which have collinear normals; the new parameter interval
708 * for the clipped curve is returned through the output parameter "dom"
709 */
710template <>
711OptInterval clip<collinear_normal_tag> (std::vector<Point> const& A,
712 std::vector<Point> const& B,
713 double /*precision*/)
714{
715 std::vector<Point> F;
716 make_focus(F, A);
717 return clip_interval(B, F);
718}
719
720
721
722const double MAX_PRECISION = 1e-8;
723const double MIN_CLIPPED_SIZE_THRESHOLD = 0.8;
726const Interval H1_INTERVAL(0, 0.5);
727const Interval H2_INTERVAL(nextafter(0.5, 1.0), 1.0);
728
729/*
730 * iterate
731 *
732 * input:
733 * A, B: control point sets of two bezier curves
734 * domA, domB: real parameter intervals of the two curves
735 * precision: required computational precision of the returned parameter ranges
736 * output:
737 * domsA, domsB: sets of parameter intervals
738 *
739 * The parameter intervals are computed by using a Bezier clipping algorithm,
740 * in case the clipping doesn't shrink the initial interval more than 20%,
741 * a subdivision step is performed.
742 * If during the computation both curves collapse to a single point
743 * the routine exits independently by the precision reached in the computation
744 * of the curve intervals.
745 */
746template <>
747void iterate<intersection_point_tag> (std::vector<Interval>& domsA,
748 std::vector<Interval>& domsB,
749 std::vector<Point> const& A,
750 std::vector<Point> const& B,
751 Interval const& domA,
752 Interval const& domB,
753 double precision )
754{
755 // in order to limit recursion
756 static size_t counter = 0;
757 if (domA.extent() == 1 && domB.extent() == 1) counter = 0;
758 if (++counter > 100) return;
759#if VERBOSE
760 std::cerr << std::fixed << std::setprecision(16);
761 std::cerr << ">> curve subdision performed <<" << std::endl;
762 std::cerr << "dom(A) : " << domA << std::endl;
763 std::cerr << "dom(B) : " << domB << std::endl;
764// std::cerr << "angle(A) : " << angle(A) << std::endl;
765// std::cerr << "angle(B) : " << angle(B) << std::endl;
766#endif
767
768 if (precision < MAX_PRECISION)
769 precision = MAX_PRECISION;
770
771 std::vector<Point> pA = A;
772 std::vector<Point> pB = B;
773 std::vector<Point>* C1 = &pA;
774 std::vector<Point>* C2 = &pB;
775
776 Interval dompA = domA;
777 Interval dompB = domB;
778 Interval* dom1 = &dompA;
779 Interval* dom2 = &dompB;
780
781 OptInterval dom;
782
783 if ( is_constant(A, precision) && is_constant(B, precision) ){
784 Point M1 = middle_point(C1->front(), C1->back());
785 Point M2 = middle_point(C2->front(), C2->back());
786 if (are_near(M1,M2)){
787 domsA.push_back(domA);
788 domsB.push_back(domB);
789 }
790 return;
791 }
792
793 size_t iter = 0;
794 while (++iter < 100
795 && (dompA.extent() >= precision || dompB.extent() >= precision))
796 {
797#if VERBOSE
798 std::cerr << "iter: " << iter << std::endl;
799#endif
800 dom = clip<intersection_point_tag>(*C1, *C2, precision);
801
802 if (dom.empty())
803 {
804#if VERBOSE
805 std::cerr << "dom: empty" << std::endl;
806#endif
807 return;
808 }
809#if VERBOSE
810 std::cerr << "dom : " << dom << std::endl;
811#endif
812 // all other cases where dom[0] > dom[1] are invalid
813 assert(dom->min() <= dom->max());
814
815 map_to(*dom2, *dom);
816
817 portion(*C2, *dom);
818 if (is_constant(*C2, precision) && is_constant(*C1, precision))
819 {
820 Point M1 = middle_point(C1->front(), C1->back());
821 Point M2 = middle_point(C2->front(), C2->back());
822#if VERBOSE
823 std::cerr << "both curves are constant: \n"
824 << "M1: " << M1 << "\n"
825 << "M2: " << M2 << std::endl;
826 print(*C2, "C2");
827 print(*C1, "C1");
828#endif
829 if (are_near(M1,M2))
830 break; // append the new interval
831 else
832 return; // exit without appending any new interval
833 }
834
835
836 // if we have clipped less than 20% than we need to subdive the curve
837 // with the largest domain into two sub-curves
838 if (dom->extent() > MIN_CLIPPED_SIZE_THRESHOLD)
839 {
840#if VERBOSE
841 std::cerr << "clipped less than 20% : " << dom->extent() << std::endl;
842 std::cerr << "angle(pA) : " << angle(pA) << std::endl;
843 std::cerr << "angle(pB) : " << angle(pB) << std::endl;
844#endif
845 std::vector<Point> pC1, pC2;
846 Interval dompC1, dompC2;
847 if (dompA.extent() > dompB.extent())
848 {
849 pC1 = pC2 = pA;
850 portion(pC1, H1_INTERVAL);
851 portion(pC2, H2_INTERVAL);
852 dompC1 = dompC2 = dompA;
853 map_to(dompC1, H1_INTERVAL);
854 map_to(dompC2, H2_INTERVAL);
855 iterate<intersection_point_tag>(domsA, domsB, pC1, pB,
856 dompC1, dompB, precision);
857 iterate<intersection_point_tag>(domsA, domsB, pC2, pB,
858 dompC2, dompB, precision);
859 }
860 else
861 {
862 pC1 = pC2 = pB;
863 portion(pC1, H1_INTERVAL);
864 portion(pC2, H2_INTERVAL);
865 dompC1 = dompC2 = dompB;
866 map_to(dompC1, H1_INTERVAL);
867 map_to(dompC2, H2_INTERVAL);
868 iterate<intersection_point_tag>(domsB, domsA, pC1, pA,
869 dompC1, dompA, precision);
870 iterate<intersection_point_tag>(domsB, domsA, pC2, pA,
871 dompC2, dompA, precision);
872 }
873 return;
874 }
875
876 swap(C1, C2);
877 swap(dom1, dom2);
878#if VERBOSE
879 std::cerr << "dom(pA) : " << dompA << std::endl;
880 std::cerr << "dom(pB) : " << dompB << std::endl;
881#endif
882 }
883 domsA.push_back(dompA);
884 domsB.push_back(dompB);
885}
886
887
888/*
889 * iterate
890 *
891 * input:
892 * A, B: control point sets of two bezier curves
893 * domA, domB: real parameter intervals of the two curves
894 * precision: required computational precision of the returned parameter ranges
895 * output:
896 * domsA, domsB: sets of parameter intervals
897 *
898 * The parameter intervals are computed by using a Bezier clipping algorithm,
899 * in case the clipping doesn't shrink the initial interval more than 20%,
900 * a subdivision step is performed.
901 * If during the computation one of the two curve interval length becomes less
902 * than MAX_PRECISION the routine exits independently by the precision reached
903 * in the computation of the other curve interval.
904 */
905template <>
906void iterate<collinear_normal_tag> (std::vector<Interval>& domsA,
907 std::vector<Interval>& domsB,
908 std::vector<Point> const& A,
909 std::vector<Point> const& B,
910 Interval const& domA,
911 Interval const& domB,
912 double precision)
913{
914 // in order to limit recursion
915 static size_t counter = 0;
916 if (domA.extent() == 1 && domB.extent() == 1) counter = 0;
917 if (++counter > 100) return;
918#if VERBOSE
919 std::cerr << std::fixed << std::setprecision(16);
920 std::cerr << ">> curve subdision performed <<" << std::endl;
921 std::cerr << "dom(A) : " << domA << std::endl;
922 std::cerr << "dom(B) : " << domB << std::endl;
923// std::cerr << "angle(A) : " << angle(A) << std::endl;
924// std::cerr << "angle(B) : " << angle(B) << std::endl;
925#endif
926
927 if (precision < MAX_PRECISION)
928 precision = MAX_PRECISION;
929
930 std::vector<Point> pA = A;
931 std::vector<Point> pB = B;
932 std::vector<Point>* C1 = &pA;
933 std::vector<Point>* C2 = &pB;
934
935 Interval dompA = domA;
936 Interval dompB = domB;
937 Interval* dom1 = &dompA;
938 Interval* dom2 = &dompB;
939
940 OptInterval dom;
941
942 size_t iter = 0;
943 while (++iter < 100
944 && (dompA.extent() >= precision || dompB.extent() >= precision))
945 {
946#if VERBOSE
947 std::cerr << "iter: " << iter << std::endl;
948#endif
949 dom = clip<collinear_normal_tag>(*C1, *C2, precision);
950
951 if (dom.empty()) {
952#if VERBOSE
953 std::cerr << "dom: empty" << std::endl;
954#endif
955 return;
956 }
957#if VERBOSE
958 std::cerr << "dom : " << dom << std::endl;
959#endif
960 assert(dom->min() <= dom->max());
961
962 map_to(*dom2, *dom);
963
964 // it's better to stop before losing computational precision
965 if (iter > 1 && (dom2->extent() <= MAX_PRECISION))
966 {
967#if VERBOSE
968 std::cerr << "beyond max precision limit" << std::endl;
969#endif
970 break;
971 }
972
973 portion(*C2, *dom);
974 if (iter > 1 && is_constant(*C2, precision))
975 {
976#if VERBOSE
977 std::cerr << "new curve portion pC1 is constant" << std::endl;
978#endif
979 break;
980 }
981
982
983 // if we have clipped less than 20% than we need to subdive the curve
984 // with the largest domain into two sub-curves
985 if ( dom->extent() > MIN_CLIPPED_SIZE_THRESHOLD)
986 {
987#if VERBOSE
988 std::cerr << "clipped less than 20% : " << dom->extent() << std::endl;
989 std::cerr << "angle(pA) : " << angle(pA) << std::endl;
990 std::cerr << "angle(pB) : " << angle(pB) << std::endl;
991#endif
992 std::vector<Point> pC1, pC2;
993 Interval dompC1, dompC2;
994 if (dompA.extent() > dompB.extent())
995 {
996 if ((dompA.extent() / 2) < MAX_PRECISION)
997 {
998 break;
999 }
1000 pC1 = pC2 = pA;
1001 portion(pC1, H1_INTERVAL);
1002 if (false && is_constant(pC1, precision))
1003 {
1004#if VERBOSE
1005 std::cerr << "new curve portion pC1 is constant" << std::endl;
1006#endif
1007 break;
1008 }
1009 portion(pC2, H2_INTERVAL);
1010 if (is_constant(pC2, precision))
1011 {
1012#if VERBOSE
1013 std::cerr << "new curve portion pC2 is constant" << std::endl;
1014#endif
1015 break;
1016 }
1017 dompC1 = dompC2 = dompA;
1018 map_to(dompC1, H1_INTERVAL);
1019 map_to(dompC2, H2_INTERVAL);
1020 iterate<collinear_normal_tag>(domsA, domsB, pC1, pB,
1021 dompC1, dompB, precision);
1022 iterate<collinear_normal_tag>(domsA, domsB, pC2, pB,
1023 dompC2, dompB, precision);
1024 }
1025 else
1026 {
1027 if ((dompB.extent() / 2) < MAX_PRECISION)
1028 {
1029 break;
1030 }
1031 pC1 = pC2 = pB;
1032 portion(pC1, H1_INTERVAL);
1033 if (is_constant(pC1, precision))
1034 {
1035#if VERBOSE
1036 std::cerr << "new curve portion pC1 is constant" << std::endl;
1037#endif
1038 break;
1039 }
1040 portion(pC2, H2_INTERVAL);
1041 if (is_constant(pC2, precision))
1042 {
1043#if VERBOSE
1044 std::cerr << "new curve portion pC2 is constant" << std::endl;
1045#endif
1046 break;
1047 }
1048 dompC1 = dompC2 = dompB;
1049 map_to(dompC1, H1_INTERVAL);
1050 map_to(dompC2, H2_INTERVAL);
1051 iterate<collinear_normal_tag>(domsB, domsA, pC1, pA,
1052 dompC1, dompA, precision);
1053 iterate<collinear_normal_tag>(domsB, domsA, pC2, pA,
1054 dompC2, dompA, precision);
1055 }
1056 return;
1057 }
1058
1059 swap(C1, C2);
1060 swap(dom1, dom2);
1061#if VERBOSE
1062 std::cerr << "dom(pA) : " << dompA << std::endl;
1063 std::cerr << "dom(pB) : " << dompB << std::endl;
1064#endif
1065 }
1066 domsA.push_back(dompA);
1067 domsB.push_back(dompB);
1068}
1069
1070
1071/*
1072 * get_solutions
1073 *
1074 * input: A, B - set of control points of two Bezier curve
1075 * input: precision - required precision of computation
1076 * input: clip - the routine used for clipping
1077 * output: xs - set of pairs of parameter values
1078 * at which the clipping algorithm converges
1079 *
1080 * This routine is based on the Bezier Clipping Algorithm,
1081 * see: Sederberg - Computer Aided Geometric Design
1082 */
1083template <typename Tag>
1084void get_solutions (std::vector< std::pair<double, double> >& xs,
1085 std::vector<Point> const& A,
1086 std::vector<Point> const& B,
1087 double precision)
1088{
1089 std::pair<double, double> ci;
1090 std::vector<Interval> domsA, domsB;
1091 iterate<Tag> (domsA, domsB, A, B, UNIT_INTERVAL, UNIT_INTERVAL, precision);
1092 if (domsA.size() != domsB.size())
1093 {
1094 assert (domsA.size() == domsB.size());
1095 }
1096 xs.clear();
1097 xs.reserve(domsA.size());
1098 for (size_t i = 0; i < domsA.size(); ++i)
1099 {
1100#if VERBOSE
1101 std::cerr << i << " : domA : " << domsA[i] << std::endl;
1102 std::cerr << "extent A: " << domsA[i].extent() << " ";
1103 std::cerr << "precision A: " << get_precision(domsA[i]) << std::endl;
1104 std::cerr << i << " : domB : " << domsB[i] << std::endl;
1105 std::cerr << "extent B: " << domsB[i].extent() << " ";
1106 std::cerr << "precision B: " << get_precision(domsB[i]) << std::endl;
1107#endif
1108 ci.first = domsA[i].middle();
1109 ci.second = domsB[i].middle();
1110 xs.push_back(ci);
1111 }
1112}
1113
1114} /* end namespace bezier_clipping */ } /* end namespace detail */
1115
1116
1117/*
1118 * find_collinear_normal
1119 *
1120 * input: A, B - set of control points of two Bezier curve
1121 * input: precision - required precision of computation
1122 * output: xs - set of pairs of parameter values
1123 * at which there are collinear normals
1124 *
1125 * This routine is based on the Bezier Clipping Algorithm,
1126 * see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
1127 */
1128void find_collinear_normal (std::vector< std::pair<double, double> >& xs,
1129 std::vector<Point> const& A,
1130 std::vector<Point> const& B,
1131 double precision)
1132{
1133 using namespace detail::bezier_clipping;
1134 get_solutions<collinear_normal_tag>(xs, A, B, precision);
1135}
1136
1137
1138/*
1139 * find_intersections_bezier_clipping
1140 *
1141 * input: A, B - set of control points of two Bezier curve
1142 * input: precision - required precision of computation
1143 * output: xs - set of pairs of parameter values
1144 * at which crossing happens
1145 *
1146 * This routine is based on the Bezier Clipping Algorithm,
1147 * see: Sederberg, Nishita, 1990 - Curve intersection using Bezier clipping
1148 */
1149void find_intersections_bezier_clipping(std::vector<std::pair<double, double>> &xs,
1150 std::vector<Point> const &A,
1151 std::vector<Point> const &B,
1152 double precision)
1153{
1154 // Handle beziers with identical control points (up to reversal).
1155 if (A.size() == B.size() && (A == B || std::equal(A.begin(), A.end(), B.rbegin()))) {
1156 return;
1157 }
1158
1159 using namespace detail::bezier_clipping;
1160 get_solutions<intersection_point_tag>(xs, A, B, precision);
1161}
1162
1163} // namespace Geom
1164
1165/*
1166 Local Variables:
1167 mode:c++
1168 c-file-style:"stroustrup"
1169 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1170 indent-tabs-mode:nil
1171 fill-column:99
1172 End:
1173*/
1174// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Cartesian point / 2D vector and related operations.
Basic intersection routines.
Bernstein-Bezier polynomial.
Calculation of binomial cefficients.
Convex hull based on the Andrew's monotone chain algorithm.
void swap(ConvexHull &other)
size_t size() const
Get the number of points in the hull.
constexpr C extent() const
constexpr void setEnds(C a, C b)
Set both ends of the interval simultaneously.
constexpr void expandTo(C val)
Extend the interval to include the given number.
constexpr C min() const
constexpr C max() const
constexpr bool empty() const
Check whether this interval is empty.
Range of real numbers that is never empty.
Definition interval.h:59
constexpr Coord valueAt(Coord t) const
Map the interval [0,1] onto this one.
Definition interval.h:99
Infinite line on a plane.
Definition line.h:53
std::vector< double > coefficients() const
Get the coefficients of the line equation as a vector.
Definition line.cpp:120
void normalize()
Reparametrize the line so that it has unit speed.
Definition line.h:204
Range of real numbers that can be empty.
Definition interval.h:199
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Point cw() const
Return a point like this point but rotated +90 degrees.
Definition point.h:137
Convex hull data structures.
Glib::ustring msg
double c[8][4]
constexpr Coord lerp(Coord t, Coord a, Coord b)
Numerically stable linear interpolation.
Definition coord.h:97
double Coord
Floating point type used to store coordinates.
Definition coord.h:76
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Simple closed interval class.
Infinite straight line.
Interval fat_line_bounds(std::vector< Point > const &c, Line const &l)
size_t get_precision(Interval const &I)
void right_portion(Coord t, std::vector< Point > &B)
void portion(std::vector< Point > &B, Interval const &I)
void map_to(Interval &J, Interval const &I)
double intersect(Point const &p1, Point const &p2, double y)
void derivative(std::vector< Point > &D, std::vector< Point > const &B)
void iterate< collinear_normal_tag >(std::vector< Interval > &domsA, std::vector< Interval > &domsB, std::vector< Point > const &A, std::vector< Point > const &B, Interval const &domA, Interval const &domB, double precision)
void range_assertion(int k, int m, int n, const char *msg)
const Interval H2_INTERVAL(nextafter(0.5, 1.0), 1.0)
Line orthogonal_orientation_line(std::vector< Point > const &c, Point const &p, double precision)
void normal(std::vector< Point > &N, std::vector< Point > const &B)
double det(Point const &P1, Point const &P2)
const Interval H1_INTERVAL(0, 0.5)
OptInterval clip_interval(std::vector< Point > const &B, Line const &l, Interval const &bound)
void print(std::vector< Point > const &cp, const char *msg="")
void iterate(std::vector< Interval > &domsA, std::vector< Interval > &domsB, std::vector< Point > const &A, std::vector< Point > const &B, Interval const &domA, Interval const &domB, double precision)
double signed_distance(Point const &p, Line const &l)
OptInterval clip< intersection_point_tag >(std::vector< Point > const &A, std::vector< Point > const &B, double precision)
const Interval UNIT_INTERVAL(0, 1)
bool solve(Point &P, Point const &P1, Point const &P2, Point const &Q)
double angle(std::vector< Point > const &A)
bool is_constant(std::vector< Point > const &A, double precision)
void left_portion(Coord t, std::vector< Point > &B)
void get_solutions(std::vector< std::pair< double, double > > &xs, std::vector< Point > const &A, std::vector< Point > const &B, double precision)
void iterate< intersection_point_tag >(std::vector< Interval > &domsA, std::vector< Interval > &domsB, std::vector< Point > const &A, std::vector< Point > const &B, Interval const &domA, Interval const &domB, double precision)
void make_focus(std::vector< Point > &F, std::vector< Point > const &B)
OptInterval clip< collinear_normal_tag >(std::vector< Point > const &A, std::vector< Point > const &B, double)
Line pick_orientation_line(std::vector< Point > const &c, double precision)
OptInterval clip(std::vector< Point > const &A, std::vector< Point > const &B, double precision)
void distance_control_points(std::vector< Point > &D, std::vector< Point > const &B, std::vector< Point > const &F)
void orientation_line(std::vector< double > &l, std::vector< Point > const &c, size_t i, size_t j)
Various utility functions.
Definition affine.h:22
Coord length(LineSegment const &seg)
void find_intersections_bezier_clipping(std::vector< std::pair< double, double > > &xs, std::vector< Point > const &A, std::vector< Point > const &B, double precision=EPSILON)
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 find_collinear_normal(std::vector< std::pair< double, double > > &xs, std::vector< Point > const &A, std::vector< Point > const &B, double precision=EPSILON)
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
constexpr void binomial_decrement_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:70
T dot(D2< T > const &a, D2< T > const &b)
Definition d2.h:355
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
D2< T > rot90(D2< T > const &a)
Definition d2.h:397
Point middle_point(LineSegment const &_segment)
static gint counter
Definition box3d.cpp:39
size_t N