mirror of
https://github.com/mapbox/tippecanoe.git
synced 2025-02-02 01:08:14 +00:00
182 lines
5.3 KiB
C++
182 lines
5.3 KiB
C++
#pragma once
|
|
|
|
#include <mapbox/geometry/line_string.hpp>
|
|
#include <mapbox/geometry/point.hpp>
|
|
#include <mapbox/geometry/polygon.hpp>
|
|
|
|
#include <mapbox/geometry/wagyu/config.hpp>
|
|
#include <mapbox/geometry/wagyu/edge.hpp>
|
|
#include <mapbox/geometry/wagyu/util.hpp>
|
|
|
|
namespace mapbox {
|
|
namespace geometry {
|
|
namespace wagyu {
|
|
|
|
template <typename T>
|
|
bool point_2_is_between_point_1_and_point_3(mapbox::geometry::point<T> const& pt1,
|
|
mapbox::geometry::point<T> const& pt2,
|
|
mapbox::geometry::point<T> const& pt3) {
|
|
if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) {
|
|
return false;
|
|
} else if (pt1.x != pt3.x) {
|
|
return (pt2.x > pt1.x) == (pt2.x < pt3.x);
|
|
} else {
|
|
return (pt2.y > pt1.y) == (pt2.y < pt3.y);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
bool build_edge_list(mapbox::geometry::linear_ring<T> const& path_geometry, edge_list<T>& edges) {
|
|
|
|
if (path_geometry.size() < 3) {
|
|
return false;
|
|
}
|
|
|
|
// As this is a loop, we need to first go backwards from end to try and find
|
|
// the proper starting point for the iterators before the beginning
|
|
|
|
auto itr_rev = path_geometry.rbegin();
|
|
auto itr = path_geometry.begin();
|
|
mapbox::geometry::point<T> pt1 = *itr_rev;
|
|
mapbox::geometry::point<T> pt2 = *itr;
|
|
|
|
// Find next non repeated point going backwards from
|
|
// end for pt1
|
|
while (pt1 == pt2) {
|
|
++itr_rev;
|
|
if (itr_rev == path_geometry.rend()) {
|
|
return false;
|
|
}
|
|
pt1 = *itr_rev;
|
|
}
|
|
++itr;
|
|
mapbox::geometry::point<T> pt3 = *itr;
|
|
auto itr_last = itr_rev.base();
|
|
mapbox::geometry::point<T> front_pt;
|
|
mapbox::geometry::point<T> back_pt;
|
|
while (true) {
|
|
if (pt3 == pt2) {
|
|
// Duplicate point advance itr, but do not
|
|
// advance other points
|
|
if (itr == itr_last) {
|
|
break;
|
|
}
|
|
++itr;
|
|
if (itr == itr_last) {
|
|
if (edges.empty()) {
|
|
break;
|
|
}
|
|
pt3 = front_pt;
|
|
} else {
|
|
pt3 = *itr;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Now check if slopes are equal between two segments - either
|
|
// a spike or a collinear point - if so drop point number 2.
|
|
if (slopes_equal(pt1, pt2, pt3)) {
|
|
// We need to reconsider previously added points
|
|
// because the point it was using was found to be collinear
|
|
// or a spike
|
|
pt2 = pt1;
|
|
if (!edges.empty()) {
|
|
edges.pop_back(); // remove previous edge (pt1)
|
|
}
|
|
if (!edges.empty()) {
|
|
if (back_pt == edges.back().top) {
|
|
pt1 = edges.back().bot;
|
|
} else {
|
|
pt1 = edges.back().top;
|
|
}
|
|
back_pt = pt1;
|
|
} else {
|
|
// If this occurs we must look to the back of the
|
|
// ring for new points.
|
|
while (*itr_rev == pt2) {
|
|
++itr_rev;
|
|
if ((itr + 1) == itr_rev.base()) {
|
|
return false;
|
|
}
|
|
}
|
|
pt1 = *itr_rev;
|
|
itr_last = itr_rev.base();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (edges.empty()) {
|
|
front_pt = pt2;
|
|
}
|
|
edges.emplace_back(pt2, pt3);
|
|
back_pt = pt2;
|
|
if (itr == itr_last) {
|
|
break;
|
|
}
|
|
pt1 = pt2;
|
|
pt2 = pt3;
|
|
++itr;
|
|
if (itr == itr_last) {
|
|
if (edges.empty()) {
|
|
break;
|
|
}
|
|
pt3 = front_pt;
|
|
} else {
|
|
pt3 = *itr;
|
|
}
|
|
}
|
|
|
|
bool modified = false;
|
|
do {
|
|
modified = false;
|
|
if (edges.size() < 3) {
|
|
return false;
|
|
}
|
|
auto& f = edges.front();
|
|
auto& b = edges.back();
|
|
if (slopes_equal(f, b)) {
|
|
if (f.bot == b.top) {
|
|
if (f.top == b.bot) {
|
|
edges.pop_back();
|
|
edges.erase(edges.begin());
|
|
} else {
|
|
f.bot = b.bot;
|
|
edges.pop_back();
|
|
}
|
|
modified = true;
|
|
} else if (f.top == b.bot) {
|
|
f.top = b.top;
|
|
edges.pop_back();
|
|
modified = true;
|
|
} else if (f.top == b.top && f.bot == b.bot) {
|
|
edges.pop_back();
|
|
edges.erase(edges.begin());
|
|
modified = true;
|
|
} else if (f.top == b.top) {
|
|
if (point_2_is_between_point_1_and_point_3(f.top, f.bot, b.bot)) {
|
|
b.top = f.bot;
|
|
edges.erase(edges.begin());
|
|
} else {
|
|
f.top = b.bot;
|
|
edges.pop_back();
|
|
}
|
|
modified = true;
|
|
} else if (f.bot == b.bot) {
|
|
if (point_2_is_between_point_1_and_point_3(f.bot, f.top, b.top)) {
|
|
b.bot = f.top;
|
|
edges.erase(edges.begin());
|
|
} else {
|
|
f.bot = b.top;
|
|
edges.pop_back();
|
|
}
|
|
modified = true;
|
|
}
|
|
}
|
|
} while (modified);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|