64 if (!containsXrange) {
65 a = 3 * x000 - 9 * x001 + 9 * x011 - 3 * x111;
66 b = 6 * x001 - 12 * x011 + 6 * x111;
67 c = 3 * x011 - 3 * x111;
77 if ((s > 0.0) && (s < 1.0)) {
79 double xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
85 D = b * b - 4 * a *
c;
90 s = (-b + d) / (2 * a);
91 if ((s > 0.0) && (s < 1.0)) {
93 xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
96 s = (-b - d) / (2 * a);
97 if ((s > 0.0) && (s < 1.0)) {
99 xttt = s * s * s * x000 + 3 * s * s * t * x001 + 3 * s * t * t * x011 + t * t * t * x111;
106 if (!containsYrange) {
107 a = 3 * y000 - 9 * y001 + 9 * y011 - 3 * y111;
108 b = 6 * y001 - 12 * y011 + 6 * y111;
109 c = 3 * y011 - 3 * y111;
116 if ((s > 0.0) && (s < 1.0)) {
118 double yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
124 D = b * b - 4 * a *
c;
129 s = (-b + d) / (2 * a);
130 if ((s > 0.0) && (s < 1.0)) {
132 yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
135 s = (-b - d) / (2 * a);
136 if ((s > 0.0) && (s < 1.0)) {
138 yttt = s * s * s * y000 + 3 * s * s * t * y001 + 3 * s * t * t * y011 + t * t * t * y111;
164 for (
auto const &path : pv) {
165 bbox.expandTo(path.initialPoint() * t);
169 curve->expandToTransformed(bbox, t);
185 for (
size_t i = 0; i < totala; i++) {
186 for (
auto f : { 0.2, 0.4, 0.0 }) {
201 if (pathv.
size() != 1) {
205 auto const &path = pathv.
front();
207 if (!path.closed() || path.size() != 4 ||
208 std::any_of(path.begin(), path.end(), [](
auto const &
curve) { return !curve.isLineSegment(); }))
216 for (
auto const &
curve : path) {
220 if (change == 0 || change == prev_change) {
225 prev_change = change;
229 auto const p1 = path[1].finalPoint();
250 s = ((Px - Ax) * Dx + (Py - Ay) * Dy) / (Dx * Dx + Dy * Dy);
252 dist2 = (Px - Ax) * (Px - Ax) + (Py - Ay) * (Py - Ay);
253 }
else if (s >= 1.0) {
254 dist2 = (Px - Bx) * (Px - Bx) + (Py - By) * (Py - By);
259 dist2 = (Px - Qx) * (Px - Qx) + (Py - Qy) * (Py - Qy);
262 if (dist2 < (*best * *best)) *best = sqrt (dist2);
267 if ((Ax >= Px) && (Bx >= Px))
return;
268 if ((Ay >= Py) && (By >= Py))
return;
269 if ((Ay < Py) && (By < Py))
return;
270 if (Ay == By)
return;
273 if (Ax < Px) *wind -= 1;
275 }
else if (By == Py) {
276 if (Bx < Px) *wind += 1;
281 Qx = Ax + Dx * (Py - Ay) / Dy;
283 *wind += (Dy > 0.0) ? 1 : -1;
299 int needdist, needwind;
307 if (bbox)
cubic_bbox (x000, y000, x001, y001, x011, y011, x111, y111, *bbox);
309 x0 = std::min (x000, x001);
310 x0 = std::min (x0, x011);
311 x0 = std::min (x0, x111);
312 y0 = std::min (y000, y001);
313 y0 = std::min (y0, y011);
314 y0 = std::min (y0, y111);
315 x1 = std::max (x000, x001);
316 x1 = std::max (x1, x011);
317 x1 = std::max (x1, x111);
318 y1 = std::max (y000, y001);
319 y1 = std::max (y1, y011);
320 y1 = std::max (y1, y111);
324 len2 = (x000 - Px) * (x000 - Px) + (y000 - Py) * (y000 - Py);
325 if (len2 < (*best * *best)) *best = (
Geom::Coord) sqrt (len2);
326 len2 = (x111 - Px) * (x111 - Px) + (y111 - Py) * (y111 - Py);
327 if (len2 < (*best * *best)) *best = (
Geom::Coord) sqrt (len2);
329 if (((x0 - Px) < *best) && ((y0 - Py) < *best) && ((Px - x1) < *best) && ((Py - y1) < *best)) {
333 if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {
338 if (!needdist && wind) {
339 if ((y1 >= Py) && (y0 < Py) && (x0 < Px)) {
343 if (((y1 - y0) > 5.0) || ((x1 - x0) > 5.0)) {
349 if (needdist || needwind) {
357 x00t = s * x000 + t * x001;
358 x01t = s * x001 + t * x011;
359 x11t = s * x011 + t * x111;
360 x0tt = s * x00t + t * x01t;
361 x1tt = s * x01t + t * x11t;
362 xttt = s * x0tt + t * x1tt;
364 y00t = s * y000 + t * y001;
365 y01t = s * y001 + t * y011;
366 y11t = s * y011 + t * y111;
367 y0tt = s * y00t + t * y01t;
368 y1tt = s * y01t + t * y11t;
369 yttt = s * y0tt + t * y1tt;
371 geom_cubic_bbox_wind_distance (x000, y000, x00t, y00t, x0tt, y0tt, xttt, yttt, pt,
nullptr, wind, best, tolerance);
372 geom_cubic_bbox_wind_distance (xttt, yttt, x1tt, y1tt, x11t, y11t, x111, y111, pt,
nullptr, wind, best, tolerance);
405 else if (
order == 3) {
422 bbox, wind, dist, tolerance);
435 for (
const auto & iter : sbasis_path) {
440 g_warning(
"Error parsing curve: %s", e.
what());
451 double denominator = (p1[X]*(p2[Y] - p3[Y]) + p1[Y]*(p3[X] - p2[X]) + p2[X]*p3[Y] - p2[Y]*p3[X]);
452 double t1 = (p[X]*(p3[Y] - p1[Y]) + p[Y]*(p1[X] - p3[X]) - p1[X]*p3[Y] + p1[Y]*p3[X]) / denominator;
453 double t2 = (p[X]*(p2[Y] - p1[Y]) + p[Y]*(p1[X] - p2[X]) - p1[X]*p2[Y] + p1[Y]*p2[X]) / -denominator;
456 return 0 <= t1 && t1 <= 1 && 0 <= t2 && t2 <= 1 && s <= 1;
480 bool start_set =
false;
482 for (
const auto & it : pathv) {
485 if (wind && (p0 != p_start))
488 p0 = it.initialPoint() * m;
502 if (wind && (p0 != p_start))
517 if (!intersected_bounds) {
537 if (crossings.empty()) {
540 auto is_empty = [](
Geom::Crossings const &xings) ->
bool {
return xings.empty(); };
541 if (!std::all_of(crossings.begin(), crossings.end(), is_empty)) {
565 if (!a.
intersect(b, precision).empty()) {
571 for (
auto const &path : b) {
590 for (
const auto & pit : pathv) {
593 output.
back().
start( pit.initialPoint() );
608 cubicbezier_path.
close(
false);
612 g_warning(
"Error parsing curve: %s", e.
what());
641 for (
const auto & pit : tmppath) {
643 output.
back().
start( pit.initialPoint() );
653 std::vector<Geom::Point> bzrpoints =
curve->controlPoints();
658 std::vector<Geom::Point> pointlist;
659 pointlist.push_back(A);
667 pointlist.push_back(
D);
669 for (
unsigned int i=1; i<pointlist.size();i++){
693 for (
const auto & pit : pathv) {
698 output.
back().
start( pit.initialPoint() );
700 bool end_open =
false;
702 auto const &closingline = pit.back_closed();
703 if (!are_near(closingline.initialPoint(), closingline.finalPoint())) {
708 if(end_open && pit.closed()){
709 pitCubic.
close(
false);
711 pitCubic.
close(
true);
717 Geom::CubicBezier b(cit->initialPoint(), cit->pointAt(0.3334), cit->finalPoint(), cit->finalPoint());
740 for (
auto const &subpath : pathv) {
749 for (
auto const &subpath : pathv) {
758 for (
auto const &subpath : pathv) {
767 std::cerr <<
"count_path_degenerates: path is empty!" << std::endl;
779 if (are_near(closingline.initialPoint(), closingline.finalPoint())) {
787 while (curve_it != curve_endit) {
799 std::cerr <<
"count_path_nodes: path is empty!" << std::endl;
810 if (!closingline.isDegenerate() && are_near(closingline.initialPoint(), closingline.finalPoint())) {
824 std::cerr <<
"count_path_curves: path is empty!" << std::endl;
833 if (!closingline.isDegenerate() && are_near(closingline.initialPoint(), closingline.finalPoint())) {
870 const double x2,
const double y2,
871 const double x3,
const double y3,
872 const double x4,
const double y4,
873 std::vector<Geom::Point> &m_points,
877 const double curve_collinearity_epsilon = 1e-30;
878 const double curve_angle_tolerance_epsilon = 0.01;
879 double m_cusp_limit = 0.0;
880 double m_angle_tolerance = 0.0;
881 double m_approximation_scale = 1.0;
882 double m_distance_tolerance_square = 0.5 / m_approximation_scale;
883 m_distance_tolerance_square *= m_distance_tolerance_square;
884 enum curve_recursion_limit_e { curve_recursion_limit = 32 };
885#define calc_sq_distance(A,B,C,D) ((A-C)*(A-C) + (B-D)*(B-D))
887 if(level > curve_recursion_limit)
895 double x12 = (x1 + x2) / 2;
896 double y12 = (y1 + y2) / 2;
897 double x23 = (x2 + x3) / 2;
898 double y23 = (y2 + y3) / 2;
899 double x34 = (x3 + x4) / 2;
900 double y34 = (y3 + y4) / 2;
901 double x123 = (x12 + x23) / 2;
902 double y123 = (y12 + y23) / 2;
903 double x234 = (x23 + x34) / 2;
904 double y234 = (y23 + y34) / 2;
905 double x1234 = (x123 + x234) / 2;
906 double y1234 = (y123 + y234) / 2;
914 double d2 = fabs(((x2 - x4) * dy - (y2 - y4) * dx));
915 double d3 = fabs(((x3 - x4) * dy - (y3 - y4) * dx));
918 switch((
int(d2 > curve_collinearity_epsilon) << 1) +
919 int(d3 > curve_collinearity_epsilon))
927 d2 = calc_sq_distance(x1, y1, x2, y2);
928 d3 = calc_sq_distance(x4, y4, x3, y3);
935 d2 = k * (da1*dx + da2*dy);
938 d3 = k * (da1*dx + da2*dy);
939 if(d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1)
945 if(d2 <= 0) d2 = calc_sq_distance(x2, y2, x1, y1);
946 else if(d2 >= 1) d2 = calc_sq_distance(x2, y2, x4, y4);
947 else d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy);
949 if(d3 <= 0) d3 = calc_sq_distance(x3, y3, x1, y1);
950 else if(d3 >= 1) d3 = calc_sq_distance(x3, y3, x4, y4);
951 else d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy);
955 if(d2 < m_distance_tolerance_square)
957 m_points.emplace_back(x2, y2);
963 if(d3 < m_distance_tolerance_square)
965 m_points.emplace_back(x3, y3);
974 if(d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy))
976 if(m_angle_tolerance < curve_angle_tolerance_epsilon)
978 m_points.emplace_back(x23, y23);
984 da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2));
985 if(da1 >= M_PI) da1 = 2*M_PI - da1;
987 if(da1 < m_angle_tolerance)
989 m_points.emplace_back(x2, y2);
990 m_points.emplace_back(x3, y3);
994 if(m_cusp_limit != 0.0)
996 if(da1 > m_cusp_limit)
998 m_points.emplace_back(x3, y3);
1008 if(d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy))
1010 if(m_angle_tolerance < curve_angle_tolerance_epsilon)
1012 m_points.emplace_back(x23, y23);
1018 da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1));
1019 if(da1 >= M_PI) da1 = 2*M_PI - da1;
1021 if(da1 < m_angle_tolerance)
1023 m_points.emplace_back(x2, y2);
1024 m_points.emplace_back(x3, y3);
1028 if(m_cusp_limit != 0.0)
1030 if(da1 > m_cusp_limit)
1032 m_points.emplace_back(x2, y2);
1042 if((d2 + d3)*(d2 + d3) <= m_distance_tolerance_square * (dx*dx + dy*dy))
1047 if(m_angle_tolerance < curve_angle_tolerance_epsilon)
1049 m_points.emplace_back(x23, y23);
1055 k = atan2(y3 - y2, x3 - x2);
1056 da1 = fabs(k - atan2(y2 - y1, x2 - x1));
1057 da2 = fabs(atan2(y4 - y3, x4 - x3) - k);
1058 if(da1 >= M_PI) da1 = 2*M_PI - da1;
1059 if(da2 >= M_PI) da2 = 2*M_PI - da2;
1061 if(da1 + da2 < m_angle_tolerance)
1065 m_points.emplace_back(x23, y23);
1069 if(m_cusp_limit != 0.0)
1071 if(da1 > m_cusp_limit)
1073 m_points.emplace_back(x2, y2);
1077 if(da2 > m_cusp_limit)
1079 m_points.emplace_back(x3, y3);
1089 recursive_bezier4(x1, y1, x12, y12, x123, y123, x1234, y1234, m_points, level + 1);
1090 recursive_bezier4(x1234, y1234, x234, y234, x34, y34, x4, y4, m_points, level + 1);
1100 if (std::abs(affine[4]) > eps || std::abs(affine[5]) > eps)
return false;
1103 std::array<int, 4> arr;
1104 for (
int i = 0; i < 4; i++) {
1105 arr[i] = std::round(affine[i]);
1106 if (std::abs(affine[i] - arr[i]) > eps)
return false;
1107 arr[i] = std::abs(arr[i]);
1111 return arr == std::array {1, 0, 0, 1 } || arr == std::array{ 0, 1, 1, 0 };
bool is_point_inside(FillRule fill_rule, int winding)
Return whether a point is inside a shape, given the point's winding number and the shape's fill rule.
3x3 matrix representing an affine transformation.
Bezier curve with compile-time specified order.
Two-dimensional Bezier curve of arbitrary order.
Abstract continuous curve on a plane defined on [0,1].
Base exception class, all 2geom exceptions should be derived from this one.
const char * what() const noexcept override
bool contains(CRect const &r) const
Check whether the rectangle includes all points in the given rectangle.
bool contains(GenericRect< C > const &r) const
Check whether the rectangle includes all points in the given rectangle.
bool intersects(GenericRect< C > const &r) const
Check whether the rectangles have any common points.
void expandTo(CPoint const &p)
Enlarge the rectangle to contain the given point.
Axis-aligned rectangle that can be empty.
size_type size() const
Get the number of paths in the vector.
std::vector< Point > nodes() const
void push_back(Path const &path)
Append a path at the end.
OptRect boundsExact() const
int winding(Point const &p) const
Determine the winding number at the specified point.
Point pointAt(Coord t) const
Point initialPoint() const
Get the first point in the first path of the vector.
OptRect boundsFast() const
bool empty() const
Check whether the vector contains any paths.
size_type curveCount() const
Get the total number of curves in the vector.
std::vector< PVIntersection > intersect(PathVector const &other, Coord precision=EPSILON) const
Sequence of contiguous curves, aka spline.
bool closed() const
Check whether the path is closed.
void setStitching(bool x)
Enable or disable the throwing of exceptions when stitching discontinuities.
void close(bool closed=true)
Set whether the path is closed.
bool empty() const
Check whether path is empty.
Curve const & back_closed() const
const_iterator end_open() const
size_type size_default() const
Natural size of the path.
void append(Curve *curve)
Add a new curve to the end of the path.
Point initialPoint() const
Get the first point in the path.
const_iterator begin() const
void appendNew(Args &&... args)
Append a new curve to the path.
const_iterator end_default() const
void start(Point const &p)
Two-dimensional point that doubles as a vector.
constexpr Coord y() const noexcept
constexpr Coord x() const noexcept
Axis aligned, non-empty rectangle.
Convex hull data structures.
Specific curve type functions for Inkscape, not provided by lib2geom.
bool is_straight_curve(Geom::BezierCurve const &c)
constexpr Coord infinity()
Get a value representing infinity.
double Coord
Floating point type used to store coordinates.
constexpr Coord EPSILON
Default "acceptably small" value.
bool pathv_fully_contains(Geom::PathVector const &a, Geom::PathVector const &b, FillRule fill_rule, double precision)
Checks whether the filled region defined by pathvector a and fill rule fill_rule completely contains ...
size_t count_pathvector_nodes(Geom::PathVector const &pathv)
Geom::OptRect bounds_exact_transformed(Geom::PathVector const &pv, Geom::Affine const &t)
bool pointInTriangle(Geom::Point const &p, Geom::Point const &p1, Geom::Point const &p2, Geom::Point const &p3)
bool approx_dihedral(Geom::Affine const &affine, double eps)
Returns whether an affine transformation is approximately a dihedral transformation,...
size_t count_path_nodes(Geom::Path const &path)
size_t count_path_curves(Geom::Path const &path)
static void geom_curve_bbox_wind_distance(Geom::Curve const &c, Geom::Affine const &m, Geom::Point const &pt, Geom::Rect *bbox, int *wind, Geom::Coord *dist, Geom::Coord tolerance, Geom::Rect const *viewbox, Geom::Point &p0)
bool pathv_similar(Geom::PathVector const &apv, Geom::PathVector const &bpv, double precision)
size_t count_path_degenerations(Geom::Path const &path)
bool pathvs_have_nonempty_overlap(Geom::PathVector const &a, Geom::PathVector const &b)
An exact check for whether the two pathvectors intersect or overlap, including the case of a line cro...
static void geom_cubic_bbox_wind_distance(Geom::Coord x000, Geom::Coord y000, Geom::Coord x001, Geom::Coord y001, Geom::Coord x011, Geom::Coord y011, Geom::Coord x111, Geom::Coord y111, Geom::Point const &pt, Geom::Rect *bbox, int *wind, Geom::Coord *best, Geom::Coord tolerance)
void recursive_bezier4(const double x1, const double y1, const double x2, const double y2, const double x3, const double y3, const double x4, const double y4, std::vector< Geom::Point > &m_points, int level)
static void geom_line_wind_distance(Geom::Coord x0, Geom::Coord y0, Geom::Coord x1, Geom::Coord y1, Geom::Point const &pt, int *wind, Geom::Coord *best)
Geom::PathVector pathv_to_cubicbezier(Geom::PathVector const &pathv, bool nolines)
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
size_t count_pathvector_curves(Geom::PathVector const &pathv)
Geom::PathVector pathv_to_linear(Geom::PathVector const &pathv, double)
static void cubic_bbox(Geom::Coord x000, Geom::Coord y000, Geom::Coord x001, Geom::Coord y001, Geom::Coord x011, Geom::Coord y011, Geom::Coord x111, Geom::Coord y111, Geom::Rect &bbox)
void pathv_matrix_point_bbox_wind_distance(Geom::PathVector const &pathv, Geom::Affine const &m, Geom::Point const &pt, Geom::Rect *bbox, int *wind, Geom::Coord *dist, Geom::Coord tolerance, Geom::Rect const *viewbox)
Geom::OptRect check_simple_rect(Geom::PathVector const &pathv, double precision)
Geom::OptRect bounds_fast_transformed(Geom::PathVector const &pv, Geom::Affine const &t)
size_t count_pathvector_degenerations(Geom::PathVector const &pathv)
Specific geometry functions for Inkscape, not provided my lib2geom.
Inkscape::XML::Node * node
Path cubicbezierpath_from_sbasis(D2< SBasis > const &B, double tol)
std::vector< Crossing > Crossings
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Conversion between SBasis and Bezier basis polynomials.
Crossings crossings(Curve const &a, Curve const &b)
A simple wrapper around pair_intersect.