From 00eb9594d6089640cbdfe242da3a16c6bdf1f73f Mon Sep 17 00:00:00 2001 From: jaseg Date: Thu, 26 Oct 2023 00:03:27 +0200 Subject: svg-flatten: Add cubic bezier support for newer usvg versions --- svg-flatten/include/flatten.hpp | 22 ++++++++ svg-flatten/include/gerbolyze.hpp | 1 + svg-flatten/src/flatten.cpp | 102 +++++++++++++++++++++++++++++++++++++- svg-flatten/src/svg_path.cpp | 21 +++++++- 4 files changed, 143 insertions(+), 3 deletions(-) (limited to 'svg-flatten') diff --git a/svg-flatten/include/flatten.hpp b/svg-flatten/include/flatten.hpp index 92cbf38..bd2d8e3 100644 --- a/svg-flatten/include/flatten.hpp +++ b/svg-flatten/include/flatten.hpp @@ -2,6 +2,28 @@ #include "gerbolyze.hpp" namespace gerbolyze { + class curve3_div { + public: + curve3_div(double distance_tolerance=0.1, double angle_tolerance=0.0, double cusp_limit=0.0) + : m_cusp_limit(cusp_limit), + m_distance_tolerance_square(0.25*distance_tolerance*distance_tolerance), + m_angle_tolerance(angle_tolerance) + { + } + + void run(double x1, double y1, double x2, double y2, double x3, double y3); + const std::vector &points() { return m_points; } + + private: + void recursive_bezier(double x1, double y1, double x2, double y2, + double x3, double y3, + unsigned level); + double m_cusp_limit; + double m_distance_tolerance_square; + double m_angle_tolerance; + std::vector m_points; + }; + class curve4_div { public: curve4_div(double distance_tolerance=0.1, double angle_tolerance=0.0, double cusp_limit=0.0) diff --git a/svg-flatten/include/gerbolyze.hpp b/svg-flatten/include/gerbolyze.hpp index 2fb7605..2b3652c 100644 --- a/svg-flatten/include/gerbolyze.hpp +++ b/svg-flatten/include/gerbolyze.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include diff --git a/svg-flatten/src/flatten.cpp b/svg-flatten/src/flatten.cpp index cb7f427..81a240e 100644 --- a/svg-flatten/src/flatten.cpp +++ b/svg-flatten/src/flatten.cpp @@ -19,7 +19,107 @@ static inline double calc_sq_distance(double x1, double y1, double x2, double y2 return dx * dx + dy * dy; } -void curve4_div::run(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { +/* Quadratic beziers */ + +void curve3_div::run(double x1, double y1, double x2, double y2, double x3, double y3) +{ + m_points.clear(); + m_points.emplace_back(d2p{x1, y1}); + recursive_bezier(x1, y1, x2, y2, x3, y3, 0); + m_points.emplace_back(d2p{x3, y3}); +} + +void curve3_div::recursive_bezier(double x1, double y1, double x2, double y2, double x3, double y3, unsigned level) +{ + if(level > curve_recursion_limit) + { + return; + } + + // Calculate all the mid-points of the line segments + //---------------------- + double x12 = (x1 + x2) / 2; + double y12 = (y1 + y2) / 2; + double x23 = (x2 + x3) / 2; + double y23 = (y2 + y3) / 2; + double x123 = (x12 + x23) / 2; + double y123 = (y12 + y23) / 2; + + double dx = x3-x1; + double dy = y3-y1; + double d = fabs(((x2 - x3) * dy - (y2 - y3) * dx)); + double da; + double pi = M_PI; + + if(d > curve_collinearity_epsilon) + { + // Regular case + //----------------- + if(d * d <= m_distance_tolerance_square * (dx*dx + dy*dy)) + { + // If the curvature doesn't exceed the distance_tolerance value + // we tend to finish subdivisions. + //---------------------- + if(m_angle_tolerance < curve_angle_tolerance_epsilon) + { + m_points.emplace_back(d2p{x123, y123}); + return; + } + + // Angle & Cusp Condition + //---------------------- + da = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1)); + if(da >= pi) da = 2*pi - da; + + if(da < m_angle_tolerance) + { + // Finally we can stop the recursion + //---------------------- + m_points.emplace_back(d2p{x123, y123}); + return; + } + } + } + else + { + // Collinear case + //------------------ + da = dx*dx + dy*dy; + if(da == 0) + { + d = calc_sq_distance(x1, y1, x2, y2); + } + else + { + d = ((x2 - x1)*dx + (y2 - y1)*dy) / da; + if(d > 0 && d < 1) + { + // Simple collinear case, 1---2---3 + // We can leave just two endpoints + return; + } + if(d <= 0) d = calc_sq_distance(x2, y2, x1, y1); + else if(d >= 1) d = calc_sq_distance(x2, y2, x3, y3); + else d = calc_sq_distance(x2, y2, x1 + d*dx, y1 + d*dy); + } + if(d < m_distance_tolerance_square) + { + m_points.emplace_back(d2p{x2, y2}); + return; + } + } + + // Continue subdivision + //---------------------- + recursive_bezier(x1, y1, x12, y12, x123, y123, level + 1); + recursive_bezier(x123, y123, x23, y23, x3, y3, level + 1); +} + + +/* Cubic beziers */ + +void curve4_div::run(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) +{ m_points.clear(); m_points.emplace_back(d2p{x1, y1}); recursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0); diff --git a/svg-flatten/src/svg_path.cpp b/svg-flatten/src/svg_path.cpp index 70d346d..b592ab0 100644 --- a/svg-flatten/src/svg_path.cpp +++ b/svg-flatten/src/svg_path.cpp @@ -77,8 +77,7 @@ static pair flatten_path(ClipperLib::Paths &stroke_open, ClipperLib: (ClipperLib::cInt)round(a[1]*clipper_scale) }); - } else { /* Curve to */ - assert(cmd == "C"); /* guaranteed by usvg */ + } else if (cmd == "C") { /* Curve to */ in >> b[0] >> b[1]; /* first control point */ in >> c[0] >> c[1]; /* second control point */ in >> d[0] >> d[1]; /* end point */ @@ -95,6 +94,24 @@ static pair flatten_path(ClipperLib::Paths &stroke_open, ClipperLib: } a = d; /* set last point to curve end point */ + + } else { /* Curve to */ + assert(cmd == "Q"); /* guaranteed by usvg */ + in >> b[0] >> b[1]; /* control point */ + in >> c[0] >> c[1]; /* end point */ + assert (!in.fail()); /* guaranteed by usvg */ + + gerbolyze::curve3_div c3div(distance_tolerance_px); + c3div.run(a[0], a[1], b[0], b[1], c[0], c[1]); + + for (auto &pt : c3div.points()) { + in_poly.emplace_back(ClipperLib::IntPoint{ + (ClipperLib::cInt)round(pt[0]*clipper_scale), + (ClipperLib::cInt)round(pt[1]*clipper_scale) + }); + } + + a = c; /* set last point to curve end point */ } first = false; -- cgit