From 2fdec7d2e4666cc1644bb30a96aca7011d301b80 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Fri, 6 Jan 2017 16:24:09 -0800 Subject: [PATCH 1/2] Try using Wagyu's quick_clip --- Makefile | 2 +- geometry.cpp | 31 +++++++++++++++++++++---------- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 581c541..23e2d71 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ PG= H = $(wildcard *.h) $(wildcard *.hpp) C = $(wildcard *.c) $(wildcard *.cpp) -INCLUDES = -I/usr/local/include -I. +INCLUDES = -I../wagyu/include -I/usr/local/include -I. LIBS = -L/usr/local/lib tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o serial.o main.o text.o diff --git a/geometry.cpp b/geometry.cpp index 536432c..ed077dc 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include "geometry.hpp" #include "projection.hpp" #include "serial.hpp" @@ -520,6 +521,10 @@ static drawvec clip_poly1(drawvec &geom, long long minx, long long miny, long lo drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long long maxx, long long maxy) { drawvec out; + mapbox::geometry::point min(minx, miny); + mapbox::geometry::point max(maxx, maxy); + mapbox::geometry::box bbox(min, max); + for (size_t i = 0; i < geom.size(); i++) { if (geom[i].op == VT_MOVETO) { size_t j; @@ -529,19 +534,25 @@ drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long lon } } - drawvec tmp; + mapbox::geometry::linear_ring ring; for (size_t k = i; k < j; k++) { - tmp.push_back(geom[k]); + ring.push_back(mapbox::geometry::point(geom[k].x, geom[k].y)); } - tmp = clip_poly1(tmp, minx, miny, maxx, maxy); - if (tmp.size() > 0) { - if (tmp[0].x != tmp[tmp.size() - 1].x || tmp[0].y != tmp[tmp.size() - 1].y) { - fprintf(stderr, "Internal error: Polygon ring not closed\n"); - exit(EXIT_FAILURE); + + optional_linear_ring lr = mapbox::geometry::wagyu::quick_clip::quick_lr_clip(ring, bbox); + + if (lr) { + for (size_t k = 0; k < lr->size(); k++) { + if (k == 0) { + out.push_back(draw(VT_MOVETO, (*lr)[k].x, (*lr)[k].y)); + } else { + out.push_back(draw(VT_LINETO, (*lr)[k].x, (*lr)[k].y)); + } + } + + if (lr->size() > 0 && (*lr)[0] != (*lr)[lr->size() - 1]) { + out.push_back(draw(VT_LINETO, (*lr)[0].x, (*lr)[0].y)); } - } - for (size_t k = 0; k < tmp.size(); k++) { - out.push_back(tmp[k]); } i = j - 1; From 72086b7e92be5159fd594e21a935144199439c32 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Wed, 11 Jan 2017 15:43:07 -0800 Subject: [PATCH 2/2] Upgrade Wagyu to cfc895 and use its Sutherland-Hodgman implementation --- CHANGELOG.md | 4 + Makefile | 2 +- geometry.cpp | 94 ------------------ mapbox/geometry/wagyu/quick_clip.hpp | 137 +++++++++++++++++++++++++++ mapbox/geometry/wagyu/ring_util.hpp | 6 +- version.hpp | 2 +- 6 files changed, 146 insertions(+), 99 deletions(-) create mode 100644 mapbox/geometry/wagyu/quick_clip.hpp diff --git a/CHANGELOG.md b/CHANGELOG.md index cf8983c..41bc684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.16.4 + +* Use Wagyu's quick_lr_clip() instead of a separate implementation + ## 1.16.3 * Upgrade Wagyu to bfbf2893 diff --git a/Makefile b/Makefile index 23e2d71..581c541 100644 --- a/Makefile +++ b/Makefile @@ -43,7 +43,7 @@ PG= H = $(wildcard *.h) $(wildcard *.hpp) C = $(wildcard *.c) $(wildcard *.cpp) -INCLUDES = -I../wagyu/include -I/usr/local/include -I. +INCLUDES = -I/usr/local/include -I. LIBS = -L/usr/local/lib tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o serial.o main.o text.o diff --git a/geometry.cpp b/geometry.cpp index ed077dc..b8858cc 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -424,100 +424,6 @@ drawvec close_poly(drawvec &geom) { return out; } -static bool inside(draw d, int edge, long long minx, long long miny, long long maxx, long long maxy) { - switch (edge) { - case 0: // top - return d.y > miny; - - case 1: // right - return d.x < maxx; - - case 2: // bottom - return d.y < maxy; - - case 3: // left - return d.x > minx; - } - - fprintf(stderr, "internal error inside\n"); - exit(EXIT_FAILURE); -} - -static draw intersect(draw a, draw b, int edge, long long minx, long long miny, long long maxx, long long maxy) { - // The casts to double are because the product of coordinates - // can overflow a long long if the tile buffer is large. - - switch (edge) { - case 0: // top - return draw(VT_LINETO, a.x + (double) (b.x - a.x) * (miny - a.y) / (b.y - a.y), miny); - - case 1: // right - return draw(VT_LINETO, maxx, a.y + (double) (b.y - a.y) * (maxx - a.x) / (b.x - a.x)); - - case 2: // bottom - return draw(VT_LINETO, a.x + (double) (b.x - a.x) * (maxy - a.y) / (b.y - a.y), maxy); - - case 3: // left - return draw(VT_LINETO, minx, a.y + (double) (b.y - a.y) * (minx - a.x) / (b.x - a.x)); - } - - fprintf(stderr, "internal error intersecting\n"); - exit(EXIT_FAILURE); -} - -// http://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm -static drawvec clip_poly1(drawvec &geom, long long minx, long long miny, long long maxx, long long maxy) { - drawvec out = geom; - - for (int edge = 0; edge < 4; edge++) { - if (out.size() > 0) { - drawvec in = out; - out.resize(0); - - draw S = in[in.size() - 1]; - - for (size_t e = 0; e < in.size(); e++) { - draw E = in[e]; - - if (inside(E, edge, minx, miny, maxx, maxy)) { - if (!inside(S, edge, minx, miny, maxx, maxy)) { - out.push_back(intersect(S, E, edge, minx, miny, maxx, maxy)); - } - out.push_back(E); - } else if (inside(S, edge, minx, miny, maxx, maxy)) { - out.push_back(intersect(S, E, edge, minx, miny, maxx, maxy)); - } - - S = E; - } - } - } - - if (out.size() > 0) { - // If the polygon begins and ends outside the edge, - // the starting and ending points will be left as the - // places where it intersects the edge. Need to add - // another point to close the loop. - - if (out[0].x != out[out.size() - 1].x || out[0].y != out[out.size() - 1].y) { - out.push_back(out[0]); - } - - if (out.size() < 3) { - // fprintf(stderr, "Polygon degenerated to a line segment\n"); - out.clear(); - return out; - } - - out[0].op = VT_MOVETO; - for (size_t i = 1; i < out.size(); i++) { - out[i].op = VT_LINETO; - } - } - - return out; -} - drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long long maxx, long long maxy) { drawvec out; diff --git a/mapbox/geometry/wagyu/quick_clip.hpp b/mapbox/geometry/wagyu/quick_clip.hpp new file mode 100644 index 0000000..9153bef --- /dev/null +++ b/mapbox/geometry/wagyu/quick_clip.hpp @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include + +#include + +template +using optional_linear_ring = std::experimental::optional>; + +namespace mapbox { +namespace geometry { +namespace wagyu { +namespace quick_clip { + +template +mapbox::geometry::point intersect(mapbox::geometry::point a, + mapbox::geometry::point b, + size_t edge, + mapbox::geometry::box const& box) { + switch (edge) { + case 0: + return mapbox::geometry::point( + static_cast(a.x + static_cast(b.x - a.x) * (box.min.y - a.y) / (b.y - a.y)), + box.min.y); + + case 1: + return mapbox::geometry::point( + box.max.x, + static_cast(a.y + static_cast(b.y - a.y) * (box.max.x - a.x) / (b.x - a.x))); + + case 2: + return mapbox::geometry::point( + static_cast(a.x + static_cast(b.x - a.x) * (box.max.y - a.y) / (b.y - a.y)), + box.max.y); + + default: // case 3 + return mapbox::geometry::point( + box.min.x, + static_cast(a.y + static_cast(b.y - a.y) * (box.min.x - a.x) / (b.x - a.x))); + } +} + +template +bool inside(mapbox::geometry::point p, size_t edge, mapbox::geometry::box const& b) { + switch (edge) { + case 0: + return p.y > b.min.y; + + case 1: + return p.x < b.max.x; + + case 2: + return p.y < b.max.y; + + default: // case 3 + return p.x > b.min.x; + } +} + +template +optional_linear_ring quick_lr_clip(mapbox::geometry::linear_ring const& ring, + mapbox::geometry::box const& b) { + mapbox::geometry::linear_ring out = ring; + + for (size_t edge = 0; edge < 4; edge++) { + if (out.size() > 0) { + mapbox::geometry::linear_ring in = out; + mapbox::geometry::point S = in[in.size() - 1]; + out.resize(0); + + for (size_t e = 0; e < in.size(); e++) { + mapbox::geometry::point E = in[e]; + + if (inside(E, edge, b)) { + if (!inside(S, edge, b)) { + out.push_back(intersect(S, E, edge, b)); + } + out.push_back(E); + } else if (inside(S, edge, b)) { + out.push_back(intersect(S, E, edge, b)); + } + + S = E; + } + } + } + + if (out.size() < 3) { + return optional_linear_ring(); + } + // Close the ring if the first/last point was outside + if (out[0] != out[out.size() - 1]) { + out.push_back(out[0]); + } + return optional_linear_ring(std::move(out)); +} +} + +template +mapbox::geometry::multi_polygon clip(mapbox::geometry::polygon const& poly, + mapbox::geometry::box const& b, + fill_type subject_fill_type) { + mapbox::geometry::multi_polygon result; + wagyu clipper; + for (auto const& lr : poly) { + auto new_lr = quick_clip::quick_lr_clip(lr, b); + if (new_lr) { + clipper.add_ring(*new_lr, polygon_type_subject); + } + } + clipper.execute(clip_type_union, result, subject_fill_type, fill_type_even_odd); + return result; +} + +template +mapbox::geometry::multi_polygon clip(mapbox::geometry::multi_polygon const& mp, + mapbox::geometry::box const& b, + fill_type subject_fill_type) { + mapbox::geometry::multi_polygon result; + wagyu clipper; + for (auto const& poly : mp) { + for (auto const& lr : poly) { + auto new_lr = quick_clip::quick_lr_clip(lr, b); + if (new_lr) { + clipper.add_ring(*new_lr, polygon_type_subject); + } + } + } + clipper.execute(clip_type_union, result, subject_fill_type, fill_type_even_odd); + return result; +} +} +} +} diff --git a/mapbox/geometry/wagyu/ring_util.hpp b/mapbox/geometry/wagyu/ring_util.hpp index 85c52d5..aab6d56 100644 --- a/mapbox/geometry/wagyu/ring_util.hpp +++ b/mapbox/geometry/wagyu/ring_util.hpp @@ -485,15 +485,15 @@ bool first_is_bottom_point(const_point_ptr btmPt1, const_point_ptr btmPt2) template point_ptr get_bottom_point(point_ptr pp) { - point_ptr dups = 0; + point_ptr dups = nullptr; point_ptr p = pp->next; while (p != pp) { if (p->y > pp->y) { pp = p; - dups = 0; + dups = nullptr; } else if (p->y == pp->y && p->x <= pp->x) { if (p->x < pp->x) { - dups = 0; + dups = nullptr; pp = p; } else { if (p->next != pp && p->prev != pp) { diff --git a/version.hpp b/version.hpp index 7dd4616..12ba99c 100644 --- a/version.hpp +++ b/version.hpp @@ -1 +1 @@ -#define VERSION "tippecanoe v1.16.3\n" +#define VERSION "tippecanoe v1.16.4\n"