[core] Don't earcut more than 500 inner rings
This commit is contained in:
committed by
John Firebaugh
parent
517218204f
commit
a800d33dd8
@@ -35,7 +35,10 @@ FillBucket::~FillBucket() {
|
||||
}
|
||||
|
||||
void FillBucket::addGeometry(const GeometryCollection& geometry) {
|
||||
for (const auto& polygon : classifyRings(geometry)) {
|
||||
for (auto& polygon : classifyRings(geometry)) {
|
||||
// Optimize polygons with many interior rings for earcut tesselation.
|
||||
limitHoles(polygon, 500);
|
||||
|
||||
std::size_t totalVertices = 0;
|
||||
|
||||
for (const auto& ring : polygon) {
|
||||
|
||||
@@ -114,6 +114,18 @@ std::vector<GeometryCollection> classifyRings(const GeometryCollection& rings) {
|
||||
return polygons;
|
||||
}
|
||||
|
||||
void limitHoles(GeometryCollection& polygon, uint32_t maxHoles) {
|
||||
if (polygon.size() > 1 + maxHoles) {
|
||||
std::nth_element(polygon.begin() + 1,
|
||||
polygon.begin() + 1 + maxHoles,
|
||||
polygon.end(),
|
||||
[] (const auto& a, const auto& b) {
|
||||
return signedArea(a) > signedArea(b);
|
||||
});
|
||||
polygon.resize(1 + maxHoles);
|
||||
}
|
||||
}
|
||||
|
||||
static Feature::geometry_type convertGeometry(const GeometryTileFeature& geometryTileFeature, const CanonicalTileID& tileID) {
|
||||
const double size = util::EXTENT * std::pow(2, tileID.z);
|
||||
const double x0 = util::EXTENT * tileID.x;
|
||||
|
||||
@@ -91,6 +91,9 @@ public:
|
||||
// classifies an array of rings into polygons with outer rings and holes
|
||||
std::vector<GeometryCollection> classifyRings(const GeometryCollection&);
|
||||
|
||||
// Truncate polygon to the largest `maxHoles` inner rings by area.
|
||||
void limitHoles(GeometryCollection&, uint32_t maxHoles);
|
||||
|
||||
// convert from GeometryTileFeature to Feature (eventually we should eliminate GeometryTileFeature)
|
||||
Feature convertFeature(const GeometryTileFeature&, const CanonicalTileID&);
|
||||
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
'math/minmax.cpp',
|
||||
'math/clamp.cpp',
|
||||
|
||||
'tile/geometry_tile.cpp',
|
||||
'tile/tile_id.cpp',
|
||||
|
||||
'storage/offline.cpp',
|
||||
|
||||
61
test/tile/geometry_tile.cpp
Normal file
61
test/tile/geometry_tile.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include <mbgl/test/util.hpp>
|
||||
#include <mbgl/tile/geometry_tile.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TEST(GeometryTile, classifyRings1) {
|
||||
std::vector<GeometryCollection> polygons = classifyRings({
|
||||
{ {0, 0}, {0, 40}, {40, 40}, {40, 0}, {0, 0} }
|
||||
});
|
||||
|
||||
// output: 1 polygon
|
||||
ASSERT_EQ(polygons.size(), 1);
|
||||
// output: polygon 1 has 1 exterior
|
||||
ASSERT_EQ(polygons[0].size(), 1);
|
||||
}
|
||||
|
||||
TEST(GeometryTile, classifyRings2) {
|
||||
std::vector<GeometryCollection> polygons = classifyRings({
|
||||
{ {0, 0}, {0, 40}, {40, 40}, {40, 0}, {0, 0} },
|
||||
{ {10, 10}, {20, 10}, {20, 20}, {10, 10} }
|
||||
});
|
||||
|
||||
// output: 1 polygon
|
||||
ASSERT_EQ(polygons.size(), 1);
|
||||
// output: polygon 1 has 1 exterior, 1 interior
|
||||
ASSERT_EQ(polygons[0].size(), 2);
|
||||
}
|
||||
|
||||
TEST(GeometryTile, limitHoles1) {
|
||||
GeometryCollection polygon = {
|
||||
{ {0, 0}, {0, 40}, {40, 40}, {40, 0}, {0, 0} },
|
||||
{ {30, 30}, {32, 30}, {32, 32}, {30, 30} },
|
||||
{ {10, 10}, {20, 10}, {20, 20}, {10, 10} }
|
||||
};
|
||||
|
||||
limitHoles(polygon, 1);
|
||||
|
||||
// output: polygon 1 has 1 exterior, 1 interior
|
||||
ASSERT_EQ(polygon.size(), 2);
|
||||
|
||||
// ensure we've kept the right rings (ones with largest areas)
|
||||
ASSERT_EQ(polygon[0][0].x, 0);
|
||||
ASSERT_EQ(polygon[1][0].x, 10);
|
||||
}
|
||||
|
||||
TEST(GeometryTile, limitHoles2) {
|
||||
GeometryCollection polygon = {
|
||||
{ {0, 0}, {0, 40}, {40, 40}, {40, 0}, {0, 0} },
|
||||
{ {10, 10}, {20, 10}, {20, 20}, {10, 10} },
|
||||
{ {30, 30}, {32, 30}, {32, 32}, {30, 30} }
|
||||
};
|
||||
|
||||
limitHoles(polygon, 1);
|
||||
|
||||
// output: polygon 1 has 1 exterior, 1 interior
|
||||
ASSERT_EQ(polygon.size(), 2);
|
||||
|
||||
// ensure we've kept the right rings (ones with largest areas)
|
||||
ASSERT_EQ(polygon[0][0].x, 0);
|
||||
ASSERT_EQ(polygon[1][0].x, 10);
|
||||
}
|
||||
Reference in New Issue
Block a user