60namespace detail {
namespace bezier_clipping {
66void print(std::vector<Point>
const& cp,
const char*
msg =
"")
68 std::cerr <<
msg << std::endl;
69 for (
size_t i = 0; i < cp.size(); ++i)
70 std::cerr << i <<
" : " << cp[i] << std::endl;
73template<
class charT >
74std::basic_ostream<charT> &
75operator<< (std::basic_ostream<charT> & os,
const Interval & I)
77 os <<
"[" << I.min() <<
", " << I.max() <<
"]";
81double angle (std::vector<Point>
const& A)
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);
90 double d = I.extent();
91 double e = 0.1, p = 10;
93 while (n < 16 && d < e)
106 std::cerr <<
"range assertion failed: \n"
109 <<
" range: " << m <<
", " << n << std::endl;
110 assert (k >= m && k <= n);
123 return P1[
X]*P2[
Y] - P1[
Y]*P2[
X];
132 double d =
det(P1, P2);
133 if (d == 0)
return false;
135 P[
X] =
det(Q, P2) * d;
136 P[
Y] =
det(P1, Q) * d;
161 for (
unsigned int i = 1; i < A.size(); ++i)
163 if(!
are_near(A[i], A[0], precision))
173void derivative(std::vector<Point> &
D, std::vector<Point>
const& B)
176 size_t sz = B.size();
185 for (
size_t i = 0; i < n; ++i)
187 D.push_back(n*(B[i+1] - B[i]));
196void normal(std::vector<Point> &
N, std::vector<Point>
const& B)
212 for (
size_t i = 1; i < n; ++i)
214 for (
size_t j = n-1; j > i-1 ; --j)
216 B[j] =
lerp(t, B[j-1], B[j]);
228 for (
size_t i = 1; i < n; ++i)
230 for (
size_t j = 0; j < n-i; ++j)
232 B[j] =
lerp(t, B[j], B[j+1]);
245 if (I.max() == 1)
return;
250 if (I.max() == 1)
return;
251 double t = I.extent() / (1 - I.min());
259struct intersection_point_tag;
260struct collinear_normal_tag;
261template <
typename Tag>
263 std::vector<Point>
const& B,
265template <
typename Tag>
267 std::vector<Interval>& domsB,
268 std::vector<Point>
const& A,
269 std::vector<Point>
const& B,
285 std::vector<Point>
const&
c,
288 l[0] =
c[j][
Y] -
c[i][
Y];
289 l[1] =
c[i][
X] -
c[j][
X];
291 double length = std::sqrt(l[0] * l[0] + l[1] * l[1]);
305 while (--i > 0 &&
are_near(
c[0],
c[i], precision))
332 Line line(p, (
c.back() -
c.front()).
cw() + p);
343 return a * p[
X] + b * p[
Y] +
c;
369 double dy = (p2[
Y] - p1[
Y]);
370 double s = (y - p1[
Y]) / dy;
371 return (p2[
X]-p1[
X])*s + p1[
X];
383 double n = B.size() - 1;
384 std::vector<Point>
D;
385 D.reserve (B.size());
386 for (
size_t i = 0; i < B.size(); ++i)
389 D.emplace_back(i/n, d);
397 bool plower, phigher;
398 bool clower, chigher;
399 double t, tmin = 1, tmax = 0;
402 plower = (p[0][
Y] < bound.
min());
403 phigher = (p[0][
Y] > bound.
max());
404 if (!(plower || phigher))
406 if (tmin > p[0][
X]) tmin = p[0][
X];
407 if (tmax < p[0][
X]) tmax = p[0][
X];
412 for (
size_t i = 1; i < p.
size(); ++i)
414 clower = (p[i][
Y] < bound.
min());
415 chigher = (p[i][
Y] > bound.
max());
416 if (!(clower || chigher))
418 if (tmin > p[i][
X]) tmin = p[i][
X];
419 if (tmax < p[i][
X]) tmax = p[i][
X];
424 if (clower != plower)
427 if (tmin > t) tmin = t;
428 if (tmax < t) tmax = t;
434 if (chigher != phigher)
437 if (tmin > t) tmin = t;
438 if (tmax < t) tmax = t;
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)
453 if (tmin > t) tmin = t;
454 if (tmax < t) tmax = t;
458 if (chigher != phigher)
461 if (tmin > t) tmin = t;
462 if (tmax < t) tmax = t;
467 if (tmin == 1 && tmax == 0) {
481 std::vector<Point>
const& B,
504void make_focus (std::vector<Point> & F, std::vector<Point>
const& B)
506 assert (B.size() > 2);
507 size_t n = B.size() - 1;
511 if (!
solve(
c, F[0], -F[n-1], B[n]-B[0]))
513 std::cerr <<
"make_focus: unable to make up a closed focus" << std::endl;
516 solve(
c, F[0], -F[n-1], B[n]-B[0]);
522 double n_inv = 1 / (double)(n);
524 F.push_back(
c[1] * F[n-1]);
526 for (
size_t i = n-1; i > 0; --i)
530 F[i] += (
c[1] * F[i-1]);
546 std::vector<Point>
const& B,
547 std::vector<Point>
const& F)
549 assert (B.size() > 1);
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);
556 D.reserve (B.size() * F.size());
558 std::vector<Point> dB;
560 for (
size_t k = 0; k < n; ++k)
562 dB.push_back (B[k+1] - B[k]);
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]);
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]);
576 std::vector<double> d(F.size());
579 for (
size_t i = 0; i <= r; ++i)
581 for (
size_t j = 0; j <= m; ++j)
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;
593 for (
size_t k = k0; k <= kn; ++k)
606 for (
size_t j = 0; j <= m; ++j)
609 d[j] += bc * (dBB(k,l) - dBF(k,j));
622 for (
size_t j = 0; j < m; ++j)
624 if (dmin > d[j]) dmin = d[j];
625 if (dmax < d[j]) dmax = d[j];
640 std::vector<Point>
const& F)
642 std::vector<Point>
D;
653 double t, tmin = 1, tmax = 0;
655 plower = (p[0][
Y] < 0);
658 if (tmin > p[0][
X]) tmin = p[0][
X];
659 if (tmax < p[0][
X]) tmax = p[0][
X];
664 for (
size_t i = 1; i < p.
size(); ++i)
666 clower = (p[i][
Y] < 0);
669 if (tmin > p[i][
X]) tmin = p[i][
X];
670 if (tmax < p[i][
X]) tmax = p[i][
X];
675 else if (clower != plower)
678 if (tmin > t) tmin = t;
679 if (tmax < t) tmax = t;
688 size_t last = p.
size() - 1;
689 clower = (p[0][
Y] < 0);
690 if (clower != plower)
693 if (tmin > t) tmin = t;
694 if (tmax < t) tmax = t;
698 if (tmin == 1 && tmax == 0) {
712 std::vector<Point>
const& B,
715 std::vector<Point> F;
748 std::vector<Interval>& domsB,
749 std::vector<Point>
const& A,
750 std::vector<Point>
const& B,
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;
771 std::vector<Point> pA = A;
772 std::vector<Point> pB = B;
773 std::vector<Point>* C1 = &pA;
774 std::vector<Point>* C2 = &pB;
787 domsA.push_back(domA);
788 domsB.push_back(domB);
795 && (dompA.
extent() >= precision || dompB.
extent() >= precision))
798 std::cerr <<
"iter: " << iter << std::endl;
805 std::cerr <<
"dom: empty" << std::endl;
810 std::cerr <<
"dom : " << dom << std::endl;
813 assert(dom->min() <= dom->max());
823 std::cerr <<
"both curves are constant: \n"
824 <<
"M1: " << M1 <<
"\n"
825 <<
"M2: " << M2 << std::endl;
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;
845 std::vector<Point> pC1, pC2;
852 dompC1 = dompC2 = dompA;
856 dompC1, dompB, precision);
858 dompC2, dompB, precision);
865 dompC1 = dompC2 = dompB;
869 dompC1, dompA, precision);
871 dompC2, dompA, precision);
879 std::cerr <<
"dom(pA) : " << dompA << std::endl;
880 std::cerr <<
"dom(pB) : " << dompB << std::endl;
883 domsA.push_back(dompA);
884 domsB.push_back(dompB);
907 std::vector<Interval>& domsB,
908 std::vector<Point>
const& A,
909 std::vector<Point>
const& B,
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;
930 std::vector<Point> pA = A;
931 std::vector<Point> pB = B;
932 std::vector<Point>* C1 = &pA;
933 std::vector<Point>* C2 = &pB;
944 && (dompA.
extent() >= precision || dompB.
extent() >= precision))
947 std::cerr <<
"iter: " << iter << std::endl;
953 std::cerr <<
"dom: empty" << std::endl;
958 std::cerr <<
"dom : " << dom << std::endl;
960 assert(dom->min() <= dom->max());
968 std::cerr <<
"beyond max precision limit" << std::endl;
977 std::cerr <<
"new curve portion pC1 is constant" << std::endl;
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;
992 std::vector<Point> pC1, pC2;
1005 std::cerr <<
"new curve portion pC1 is constant" << std::endl;
1013 std::cerr <<
"new curve portion pC2 is constant" << std::endl;
1017 dompC1 = dompC2 = dompA;
1021 dompC1, dompB, precision);
1023 dompC2, dompB, precision);
1036 std::cerr <<
"new curve portion pC1 is constant" << std::endl;
1044 std::cerr <<
"new curve portion pC2 is constant" << std::endl;
1048 dompC1 = dompC2 = dompB;
1052 dompC1, dompA, precision);
1054 dompC2, dompA, precision);
1062 std::cerr <<
"dom(pA) : " << dompA << std::endl;
1063 std::cerr <<
"dom(pB) : " << dompB << std::endl;
1066 domsA.push_back(dompA);
1067 domsB.push_back(dompB);
1083template <
typename Tag>
1085 std::vector<Point>
const& A,
1086 std::vector<Point>
const& B,
1089 std::pair<double, double> ci;
1090 std::vector<Interval> domsA, domsB;
1092 if (domsA.size() != domsB.size())
1094 assert (domsA.size() == domsB.size());
1097 xs.reserve(domsA.size());
1098 for (
size_t i = 0; i < domsA.size(); ++i)
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;
1108 ci.first = domsA[i].middle();
1109 ci.second = domsB[i].middle();
1129 std::vector<Point>
const& A,
1130 std::vector<Point>
const& B,
1133 using namespace detail::bezier_clipping;
1134 get_solutions<collinear_normal_tag>(xs, A, B, precision);
1150 std::vector<Point>
const &A,
1151 std::vector<Point>
const &B,
1155 if (A.size() == B.size() && (A == B || std::equal(A.begin(), A.end(), B.rbegin()))) {
1159 using namespace detail::bezier_clipping;
1160 get_solutions<intersection_point_tag>(xs, A, B, precision);
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 bool empty() const
Check whether this interval is empty.
Range of real numbers that is never empty.
constexpr Coord valueAt(Coord t) const
Map the interval [0,1] onto this one.
Infinite line on a plane.
std::vector< double > coefficients() const
Get the coefficients of the line equation as a vector.
void normalize()
Reparametrize the line so that it has unit speed.
Range of real numbers that can be empty.
Two-dimensional point that doubles as a vector.
constexpr Point cw() const
Return a point like this point but rotated +90 degrees.
Convex hull data structures.
constexpr Coord lerp(Coord t, Coord a, Coord b)
Numerically stable linear interpolation.
double Coord
Floating point type used to store coordinates.
Simple closed interval class.
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)
const double MIN_CLIPPED_SIZE_THRESHOLD
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)
const double MAX_PRECISION
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)
const OptInterval EMPTY_INTERVAL
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.
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).
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).
T dot(D2< T > const &a, D2< T > const &b)
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
D2< T > rot90(D2< T > const &a)
Point middle_point(LineSegment const &_segment)