aboutsummaryrefslogtreecommitdiff
path: root/svg-flatten
diff options
context:
space:
mode:
authorjaseg <git@jaseg.de>2023-10-26 00:03:27 +0200
committerjaseg <git@jaseg.de>2023-10-26 00:04:17 +0200
commit00eb9594d6089640cbdfe242da3a16c6bdf1f73f (patch)
tree86a40a5471d1686cd80d3a54cfcbc3ffefc3f880 /svg-flatten
parent8ab0c9fa017b5ead2e3f5cfc892b242cd0bbc908 (diff)
downloadgerbolyze-00eb9594d6089640cbdfe242da3a16c6bdf1f73f.tar.gz
gerbolyze-00eb9594d6089640cbdfe242da3a16c6bdf1f73f.tar.bz2
gerbolyze-00eb9594d6089640cbdfe242da3a16c6bdf1f73f.zip
svg-flatten: Add cubic bezier support for newer usvg versions
Diffstat (limited to 'svg-flatten')
-rw-r--r--svg-flatten/include/flatten.hpp22
-rw-r--r--svg-flatten/include/gerbolyze.hpp1
-rw-r--r--svg-flatten/src/flatten.cpp102
-rw-r--r--svg-flatten/src/svg_path.cpp21
4 files changed, 143 insertions, 3 deletions
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<d2p> &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<d2p> 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 <iostream>
#include <string>
#include <array>
+#include <cstdint>
#include <pugixml.hpp>
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<bool, bool> 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<bool, bool> 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;