From 318f05ad457888d42c61f2cb195530ae4ba41fcf Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Fri, 15 Dec 2023 09:35:22 -0800 Subject: [PATCH] Even-odd wagyu after scaling to counterbalance ring reversals --- clip.cpp | 11 ++++++++--- geometry.hpp | 2 +- plugin.cpp | 3 ++- tile.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/clip.cpp b/clip.cpp index 512ab2fd5..c3479b82b 100644 --- a/clip.cpp +++ b/clip.cpp @@ -249,7 +249,7 @@ static void decode_clipped(mapbox::geometry::multi_polygon &t, drawve } } -drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip, bool try_scaling) { +drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip, bool try_scaling, bool even_odd) { geom = remove_noop(geom, VT_POLYGON, 0); mapbox::geometry::multi_polygon result; @@ -308,7 +308,11 @@ drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip, bool try try { result.clear(); - wagyu.execute(mapbox::geometry::wagyu::clip_type_union, result, mapbox::geometry::wagyu::fill_type_positive, mapbox::geometry::wagyu::fill_type_positive); + if (even_odd) { + wagyu.execute(mapbox::geometry::wagyu::clip_type_union, result, mapbox::geometry::wagyu::fill_type_even_odd, mapbox::geometry::wagyu::fill_type_even_odd); + } else { + wagyu.execute(mapbox::geometry::wagyu::clip_type_union, result, mapbox::geometry::wagyu::fill_type_positive, mapbox::geometry::wagyu::fill_type_positive); + } } catch (std::runtime_error &e) { FILE *f = fopen("/tmp/wagyu.log", "w"); fprintf(f, "%s\n", e.what()); @@ -858,7 +862,8 @@ std::string overzoom(mvt_tile tile, int oz, int ox, int oy, int nz, int nx, int geom = remove_noop(geom, t, 0); if (t == VT_POLYGON) { - geom = clean_or_clip_poly(geom, 0, 0, false, false); + // this is after scaling, so even-odd to balance scaling errors between features + geom = clean_or_clip_poly(geom, 0, 0, false, false, true); geom = close_poly(geom); } diff --git a/geometry.hpp b/geometry.hpp index 1cbaa8ef1..d58c27b28 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -70,7 +70,7 @@ void to_tile_scale(drawvec &geom, int z, int detail); drawvec from_tile_scale(drawvec const &geom, int z, int detail); drawvec remove_noop(drawvec geom, int type, int shift); drawvec clip_point(drawvec &geom, int z, long long buffer); -drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip, bool try_scaling); +drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip, bool try_scaling, bool even_odd); drawvec close_poly(drawvec &geom); drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *still_needs_simplification, bool *reduced_away, double *accum_area, serial_feature *this_feature, serial_feature *tiny_feature); int clip(long long *x0, long long *y0, long long *x1, long long *y1, long long xmin, long long ymin, long long xmax, long long ymax); diff --git a/plugin.cpp b/plugin.cpp index 33407eb94..52c962beb 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -203,7 +203,8 @@ std::vector parse_layers(int fd, int z, unsigned x, unsigned y, std:: if (mb_geometry[t] == VT_POLYGON) { // we can try scaling up because these are tile coordinates - dv = clean_or_clip_poly(dv, 0, 0, false, true); + // these are already scaled, so even-odd winding + dv = clean_or_clip_poly(dv, 0, 0, false, true, true); if (dv.size() < 3) { dv.clear(); } diff --git a/tile.cpp b/tile.cpp index d94b1493a..0bbf2e1b8 100644 --- a/tile.cpp +++ b/tile.cpp @@ -578,7 +578,8 @@ double simplify_partial(partial *p, drawvec const &shared_nodes, node *shared_no // unioned exactly // // don't try to scale up because these are still world coordinates - geom = clean_or_clip_poly(geom, 0, 0, false, false); + // positive winding because these are world coordinates + geom = clean_or_clip_poly(geom, 0, 0, false, false, true); } // continues to simplify to line_detail even if we have extra detail @@ -611,7 +612,42 @@ void *partial_feature_worker(void *v) { int z = (*partials)[i].z; int out_detail = (*partials)[i].extra_detail; +#if 0 + if ((*partials)[i].geoms[0].size() < 1) { + fprintf(stderr, "expected at least one geometry in partial_feature_worker\n"); + exit(EXIT_IMPOSSIBLE); + } + for (size_t x = 1; x < (*partials)[i].geoms.size(); x++) { + for (auto const &d : (*partials)[i].geoms[x]) { + (*partials)[i].geoms[0].push_back(d); + } + } + (*partials)[i].geoms.resize(1); +#endif + + if ((*partials)[i].geoms.size() != 1) { + fprintf(stderr, "expected single geometry in partial_feature_worker\n"); + exit(EXIT_IMPOSSIBLE); + } drawvec geom = (*partials)[i].geoms[0]; + + if (t == VT_POLYGON) { + // clean multi-ring polygons with positive winding before scaling + // so that polygon dust will be unioned on positively + + size_t ring_count = 0; + for (size_t x = 0; x < geom.size(); x++) { + if (geom[x].op == VT_MOVETO) { + ring_count++; + + if (ring_count == 2) { + geom = clean_or_clip_poly(geom, 0, 0, false, false, false); + break; + } + } + } + } + to_tile_scale(geom, z, out_detail); if (t == VT_POLYGON) { @@ -620,7 +656,8 @@ void *partial_feature_worker(void *v) { { drawvec before = geom; // we can try scaling up because this is now tile scale - geom = clean_or_clip_poly(geom, 0, 0, false, true); + // even-odd since we have scaled + geom = clean_or_clip_poly(geom, 0, 0, false, true, true); if (additional[A_DEBUG_POLYGON]) { check_polygon(geom); } @@ -2293,7 +2330,8 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch for (auto &g : partials[simplified_geometry_through].geoms) { if (partials[simplified_geometry_through].t == VT_POLYGON) { // don't scale up because this is still world coordinates - g = clean_or_clip_poly(g, 0, 0, false, false); + // positive winding because this before scaling + g = clean_or_clip_poly(g, 0, 0, false, false, false); } } } @@ -2533,7 +2571,8 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch if (layer_features[x].type == VT_POLYGON) { if (layer_features[x].coalesced) { // we can try scaling up because this is tile coordinates - layer_features[x].geom = clean_or_clip_poly(layer_features[x].geom, 0, 0, false, true); + // positive winding because we are trying to union features here + layer_features[x].geom = clean_or_clip_poly(layer_features[x].geom, 0, 0, false, true, false); } layer_features[x].geom = close_poly(layer_features[x].geom);