From 4811a2b69b91f6127a75e780de6e2113609ddabb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Tue, 6 Sep 2022 15:56:24 +0200 Subject: [PATCH] Port bugfixes 2.2.4 (#102) * more microoptimizations * one more microoptimization * fix infinite loops, simplify hole elimination Co-Authored-By: Juha Laukala <104564021+JuhaLaukala@users.noreply.github.com> * minor cleanup in findHoleBridge Co-authored-by: Juha Laukala <104564021+JuhaLaukala@users.noreply.github.com> --- include/mapbox/earcut.hpp | 25 +++++++++---------------- test/fixtures/filtered_bridge_jhl.cpp | 17 +++++++++++++++++ test/fixtures/infinite_loop_jhl.cpp | 14 ++++++++++++++ test/fixtures/issue142.cpp | 14 ++++++++++++++ test/fixtures/issue149.cpp | 14 ++++++++++++++ 5 files changed, 68 insertions(+), 16 deletions(-) create mode 100644 test/fixtures/filtered_bridge_jhl.cpp create mode 100644 test/fixtures/infinite_loop_jhl.cpp create mode 100644 test/fixtures/issue142.cpp create mode 100644 test/fixtures/issue149.cpp diff --git a/include/mapbox/earcut.hpp b/include/mapbox/earcut.hpp index e7d4308..a73ae87 100644 --- a/include/mapbox/earcut.hpp +++ b/include/mapbox/earcut.hpp @@ -177,9 +177,9 @@ void Earcut::operator()(const Polygon& points) { p = p->next; } while (p != outerNode); - // minX, minY and size are later used to transform coords into integers for z-order calculation + // minX, minY and inv_size are later used to transform coords into integers for z-order calculation inv_size = std::max(maxX - minX, maxY - minY); - inv_size = inv_size != .0 ? (1. / inv_size) : .0; + inv_size = inv_size != .0 ? (32767. / inv_size) : .0; } earcutLinked(outerNode); @@ -443,7 +443,6 @@ Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { // process holes from left to right for (size_t i = 0; i < queue.size(); i++) { outerNode = eliminateHole(queue[i], outerNode); - outerNode = filterPoints(outerNode, outerNode->next); } return outerNode; @@ -461,11 +460,10 @@ Earcut::eliminateHole(Node* hole, Node* outerNode) { Node* bridgeReverse = splitPolygon(bridge, hole); // filter collinear points around the cuts - Node* filteredBridge = filterPoints(bridge, bridge->next); filterPoints(bridgeReverse, bridgeReverse->next); // Check if input node was removed by the filtering - return outerNode == bridge ? filteredBridge : outerNode; + return filterPoints(bridge, bridge->next); } // David Eberly's algorithm for finding a bridge between hole and outer polygon @@ -485,11 +483,8 @@ Earcut::findHoleBridge(Node* hole, Node* outerNode) { double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); if (x <= hx && x > qx) { qx = x; - if (x == hx) { - if (hy == p->y) return p; - if (hy == p->next->y) return p->next; - } m = p->x < p->next->x ? p : p->next; + if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint } } p = p->next; @@ -497,8 +492,6 @@ Earcut::findHoleBridge(Node* hole, Node* outerNode) { if (!m) return 0; - if (hx == qx) return m; // hole touches outer segment; pick leftmost endpoint - // look for points inside the triangle of hole Vertex, segment intersection and endpoint; // if there are no points found, we have a valid connection; // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex @@ -628,8 +621,8 @@ Earcut::sortLinked(Node* list) { template int32_t Earcut::zOrder(const double x_, const double y_) { // coords are transformed into non-negative 15-bit integer range - int32_t x = static_cast(32767.0 * (x_ - minX) * inv_size); - int32_t y = static_cast(32767.0 * (y_ - minY) * inv_size); + int32_t x = static_cast((x_ - minX) * inv_size); + int32_t y = static_cast((y_ - minY) * inv_size); x = (x | (x << 8)) & 0x00FF00FF; x = (x | (x << 4)) & 0x0F0F0F0F; @@ -662,9 +655,9 @@ Earcut::getLeftmost(Node* start) { // check if a point lies within a convex triangle template bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { - return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && - (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && - (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; + return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && + (ax - px) * (by - py) >= (bx - px) * (ay - py) && + (bx - px) * (cy - py) >= (cx - px) * (by - py); } // check if a diagonal between two polygon nodes is valid (lies in polygon interior) diff --git a/test/fixtures/filtered_bridge_jhl.cpp b/test/fixtures/filtered_bridge_jhl.cpp new file mode 100644 index 0000000..2b8c3fe --- /dev/null +++ b/test/fixtures/filtered_bridge_jhl.cpp @@ -0,0 +1,17 @@ +// This file is auto-generated, manual changes will be lost if the code is regenerated. + +#include "geometries.hpp" + +namespace mapbox { +namespace fixtures { + +static const Fixture filtered_bridge_jhl("filtered_bridge_jhl", 25, 1e-14, 0.000001, { + {{22,14},{17,12},{5,12},{0,12},{0,0},{22,0}}, + {{9,4},{10,4},{10,3},{9,3}}, + {{6,9},{7,9},{7,8},{6,8}}, + {{7,10},{17,10},{17,5},{8,5}}, + {{13,4},{14,4},{14,3},{13,3}}, +}); + +} +} diff --git a/test/fixtures/infinite_loop_jhl.cpp b/test/fixtures/infinite_loop_jhl.cpp new file mode 100644 index 0000000..cf393ae --- /dev/null +++ b/test/fixtures/infinite_loop_jhl.cpp @@ -0,0 +1,14 @@ +// This file is auto-generated, manual changes will be lost if the code is regenerated. + +#include "geometries.hpp" + +namespace mapbox { +namespace fixtures { + +static const Fixture infinite_loop_jhl("infinite_loop_jhl", 0, 1e-14, Infinity, { + {{-1,2},{0,0},{2,-1}}, + {{2,-1},{0,1e-28},{-1,2}}, +}); + +} +} diff --git a/test/fixtures/issue142.cpp b/test/fixtures/issue142.cpp new file mode 100644 index 0000000..cdba285 --- /dev/null +++ b/test/fixtures/issue142.cpp @@ -0,0 +1,14 @@ +// This file is auto-generated, manual changes will be lost if the code is regenerated. + +#include "geometries.hpp" + +namespace mapbox { +namespace fixtures { + +static const Fixture issue142("issue142", 4, 0.12754968037146489, 8.53067013, { + {{5.62675358811389,31.94879819160804},{-16.369709114391867,28.341954255099814},{-10.786562672455382,-1.2779295357476745},{10.819423740334923,2.069348113719755}}, + {{3.220439475288522,4.197526331591453},{5.024815373142793,1.1716264034331543},{10.819423740334923,2.069348113719755},{5.62675358811389,31.94879819160804},{-16.369709114391867,28.341954255099814},{-10.786562672455382,-1.2779295357476745},{-6.833718161055838,-0.6655405509524673},{-8.602352370111433,2.142874784407777},{-5.34630560403934,6.768689248602321},{-1.4053749889060216,7.453573097663546}}, +}); + +} +} diff --git a/test/fixtures/issue149.cpp b/test/fixtures/issue149.cpp new file mode 100644 index 0000000..70fe589 --- /dev/null +++ b/test/fixtures/issue149.cpp @@ -0,0 +1,14 @@ +// This file is auto-generated, manual changes will be lost if the code is regenerated. + +#include "geometries.hpp" + +namespace mapbox { +namespace fixtures { + +static const Fixture issue149("issue149", 2, 1e-14, 0.000001, { + {{1888,5504},{1872,5504},{1872,5536},{1856,5536},{1856,5520},{1840,5520},{1840,5504},{1856,5504},{1856,5520},{1872,5520},{1872,5504},{1888,5504}}, + {{1856,5520},{1856,5536},{1872,5536},{1872,5520}}, +}); + +} +}