aboutsummaryrefslogtreecommitdiff
path: root/svg-flatten/src/flatten.cpp
diff options
context:
space:
mode:
authorjaseg <git-bigdata-wsl-arch@jaseg.de>2021-04-24 17:20:00 +0200
committerjaseg <git@jaseg.de>2021-04-24 20:17:42 +0200
commit776e0bd2069af0cfff7ce794cf3b345b613e1c02 (patch)
tree04d63001eee0ed741bd6869901610ee1b1603dd2 /svg-flatten/src/flatten.cpp
parent89da2b3664ec75cf5de1e5d7ee48acd0f2847df8 (diff)
downloadgerbolyze-776e0bd2069af0cfff7ce794cf3b345b613e1c02.tar.gz
gerbolyze-776e0bd2069af0cfff7ce794cf3b345b613e1c02.tar.bz2
gerbolyze-776e0bd2069af0cfff7ce794cf3b345b613e1c02.zip
Replace cairo curve flattener from Anitgrain Graphics
This also fixes an issue where non-closed curves were not dilated properly.
Diffstat (limited to 'svg-flatten/src/flatten.cpp')
-rw-r--r--svg-flatten/src/flatten.cpp231
1 files changed, 231 insertions, 0 deletions
diff --git a/svg-flatten/src/flatten.cpp b/svg-flatten/src/flatten.cpp
new file mode 100644
index 0000000..e93f044
--- /dev/null
+++ b/svg-flatten/src/flatten.cpp
@@ -0,0 +1,231 @@
+/* Copied from Antigrain Graphics (AGG) v2.4 */
+/* Mirror: https://github.com/pelson/antigrain/blob/master/agg-2.4/src/agg_curves.cpp */
+
+#include <flatten.hpp>
+#include <cmath>
+
+using namespace gerbolyze;
+
+namespace gerbolyze {
+ const double curve_distance_epsilon = 1e-15;
+ const double curve_collinearity_epsilon = 1e-15;
+ const double curve_angle_tolerance_epsilon = 0.1;
+ constexpr unsigned curve_recursion_limit = 20;
+}
+
+static inline double calc_sq_distance(double x1, double y1, double x2, double y2)
+{
+ double dx = x2-x1;
+ double dy = y2-y1;
+ 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) {
+ m_points.clear();
+ m_points.emplace_back(d2p{x1, y1});
+ recursive_bezier(x1, y1, x2, y2, x3, y3, x4, y4, 0);
+ m_points.emplace_back(d2p{x4, y4});
+}
+
+void curve4_div::recursive_bezier(double x1, double y1,
+ double x2, double y2,
+ double x3, double y3,
+ double x4, double y4,
+ unsigned level)
+{
+ if(level > curve_recursion_limit) {
+ return;
+ }
+
+ double pi = M_PI;
+
+ // 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 x34 = (x3 + x4) / 2;
+ double y34 = (y3 + y4) / 2;
+ double x123 = (x12 + x23) / 2;
+ double y123 = (y12 + y23) / 2;
+ double x234 = (x23 + x34) / 2;
+ double y234 = (y23 + y34) / 2;
+ double x1234 = (x123 + x234) / 2;
+ double y1234 = (y123 + y234) / 2;
+
+
+ // Try to approximate the full cubic curve by a single straight line
+ //------------------
+ double dx = x4-x1;
+ double dy = y4-y1;
+
+ double d2 = fabs(((x2 - x4) * dy - (y2 - y4) * dx));
+ double d3 = fabs(((x3 - x4) * dy - (y3 - y4) * dx));
+ double da1, da2, k;
+
+ switch((int(d2 > curve_collinearity_epsilon) << 1) +
+ int(d3 > curve_collinearity_epsilon))
+ {
+ case 0:
+ // All collinear OR p1==p4
+ //----------------------
+ k = dx*dx + dy*dy;
+ if(k == 0) {
+ d2 = calc_sq_distance(x1, y1, x2, y2);
+ d3 = calc_sq_distance(x4, y4, x3, y3);
+
+ } else {
+ k = 1 / k;
+ da1 = x2 - x1;
+ da2 = y2 - y1;
+ d2 = k * (da1*dx + da2*dy);
+ da1 = x3 - x1;
+ da2 = y3 - y1;
+ d3 = k * (da1*dx + da2*dy);
+
+ if(d2 > 0 && d2 < 1 && d3 > 0 && d3 < 1) {
+ // Simple collinear case, 1---2---3---4
+ // We can leave just two endpoints
+ return;
+ }
+
+ if(d2 <= 0) {
+ d2 = calc_sq_distance(x2, y2, x1, y1);
+ } else if(d2 >= 1) {
+ d2 = calc_sq_distance(x2, y2, x4, y4);
+ } else {
+ d2 = calc_sq_distance(x2, y2, x1 + d2*dx, y1 + d2*dy);
+ }
+
+ if(d3 <= 0) {
+ d3 = calc_sq_distance(x3, y3, x1, y1);
+ } else if(d3 >= 1) {
+ d3 = calc_sq_distance(x3, y3, x4, y4);
+ } else {
+ d3 = calc_sq_distance(x3, y3, x1 + d3*dx, y1 + d3*dy);
+ }
+
+ }
+
+ if(d2 > d3) {
+ if(d2 < m_distance_tolerance_square) {
+ m_points.emplace_back(d2p{x2, y2});
+ return;
+ }
+ } else {
+ if(d3 < m_distance_tolerance_square) {
+ m_points.emplace_back(d2p{x3, y3});
+ return;
+ }
+ }
+ break;
+
+ case 1:
+ // p1,p2,p4 are collinear, p3 is significant
+ //----------------------
+ if(d3 * d3 <= m_distance_tolerance_square * (dx*dx + dy*dy)) {
+ if(m_angle_tolerance < curve_angle_tolerance_epsilon) {
+ m_points.emplace_back(d2p{x23, y23});
+ return;
+ }
+
+ // Angle Condition
+ //----------------------
+ da1 = fabs(atan2(y4 - y3, x4 - x3) - atan2(y3 - y2, x3 - x2));
+ if(da1 >= pi) da1 = 2*pi - da1;
+
+ if(da1 < m_angle_tolerance) {
+ m_points.emplace_back(d2p{x2, y2});
+ m_points.emplace_back(d2p{x3, y3});
+ return;
+ }
+
+ if(m_cusp_limit != 0.0) {
+ if(da1 > m_cusp_limit)
+ {
+ m_points.emplace_back(d2p{x3, y3});
+ return;
+ }
+ }
+ }
+ break;
+
+ case 2:
+ // p1,p3,p4 are collinear, p2 is significant
+ //----------------------
+ if(d2 * d2 <= m_distance_tolerance_square * (dx*dx + dy*dy)) {
+ if(m_angle_tolerance < curve_angle_tolerance_epsilon) {
+ m_points.emplace_back(d2p{x23, y23});
+ return;
+ }
+
+ // Angle Condition
+ //----------------------
+ da1 = fabs(atan2(y3 - y2, x3 - x2) - atan2(y2 - y1, x2 - x1));
+ if(da1 >= pi) da1 = 2*pi - da1;
+
+ if(da1 < m_angle_tolerance) {
+ m_points.emplace_back(d2p{x2, y2});
+ m_points.emplace_back(d2p{x3, y3});
+ return;
+ }
+
+ if(m_cusp_limit != 0.0) {
+ if(da1 > m_cusp_limit) {
+ m_points.emplace_back(d2p{x2, y2});
+ return;
+ }
+ }
+ }
+ break;
+
+ case 3:
+ // Regular case
+ //-----------------
+ if((d2 + d3)*(d2 + d3) <= 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{x23, y23});
+ return;
+ }
+
+ // Angle & Cusp Condition
+ //----------------------
+ k = atan2(y3 - y2, x3 - x2);
+ da1 = fabs(k - atan2(y2 - y1, x2 - x1));
+ da2 = fabs(atan2(y4 - y3, x4 - x3) - k);
+ if(da1 >= pi) da1 = 2*pi - da1;
+ if(da2 >= pi) da2 = 2*pi - da2;
+
+ if(da1 + da2 < m_angle_tolerance) {
+ // Finally we can stop the recursion
+ //----------------------
+ m_points.emplace_back(d2p{x23, y23});
+ return;
+ }
+
+ if(m_cusp_limit != 0.0) {
+ if(da1 > m_cusp_limit) {
+ m_points.emplace_back(d2p{x2, y2});
+ return;
+ }
+
+ if(da2 > m_cusp_limit) {
+ m_points.emplace_back(d2p{x3, y3});
+ return;
+ }
+ }
+ }
+ break;
+ }
+
+ // Continue subdivision
+ //----------------------
+ recursive_bezier(x1, y1, x12, y12, x123, y123, x1234, y1234, level + 1);
+ recursive_bezier(x1234, y1234, x234, y234, x34, y34, x4, y4, level + 1);
+}
+