Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] Don't earcut more than 500 inner rings
Browse files Browse the repository at this point in the history
  • Loading branch information
yhahn authored and jfirebaugh committed May 28, 2016
1 parent 5ba9ebe commit 354789f
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/mbgl/renderer/fill_bucket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
12 changes: 12 additions & 0 deletions src/mbgl/tile/geometry_tile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions src/mbgl/tile/geometry_tile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class GeometryTileMonitor : private util::noncopyable {
// 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&);

Expand Down
1 change: 1 addition & 0 deletions test/test.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
'math/minmax.cpp',
'math/clamp.cpp',

'tile/geometry_tile.cpp',
'tile/tile_id.cpp',

'storage/offline.cpp',
Expand Down
61 changes: 61 additions & 0 deletions test/tile/geometry_tile.cpp
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 354789f

Please sign in to comment.