mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-04 17:35:42 +02:00
Trying to straighten out ring nesting
This commit is contained in:
+86
-14
@@ -3,6 +3,7 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
#include "geometry.hpp"
|
#include "geometry.hpp"
|
||||||
#include "errors.hpp"
|
#include "errors.hpp"
|
||||||
|
|
||||||
@@ -407,9 +408,29 @@ void snap_round(std::vector<segment> &segs) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawvec reassemble(std::vector<segment> const &segs) {
|
struct ring_area {
|
||||||
|
drawvec geom;
|
||||||
|
double area;
|
||||||
|
std::vector<size_t> children;
|
||||||
|
|
||||||
|
ring_area(drawvec geom_) {
|
||||||
|
geom = geom_;
|
||||||
|
area = get_area(geom, 0, geom.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(ring_area const &s) const {
|
||||||
|
// this sorts backwards, so the ring with the largest area comes first
|
||||||
|
if (std::fabs(area) > std::fabs(s.area)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<ring_area> reassemble(std::vector<segment> const &segs) {
|
||||||
std::multimap<point, segment> connections;
|
std::multimap<point, segment> connections;
|
||||||
drawvec ret;
|
std::vector<ring_area> ret;
|
||||||
|
|
||||||
for (auto const &seg : segs) {
|
for (auto const &seg : segs) {
|
||||||
connections.emplace(seg.first, seg);
|
connections.emplace(seg.first, seg);
|
||||||
@@ -564,23 +585,58 @@ drawvec reassemble(std::vector<segment> const &segs) {
|
|||||||
ring.push_back(here.first);
|
ring.push_back(here.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// these coordinates are doubled, so that `encloses` can always find
|
||||||
|
// an integer point at the center of an edge
|
||||||
drawvec out;
|
drawvec out;
|
||||||
|
|
||||||
for (size_t i = 0; i < ring.size(); i++) {
|
for (size_t i = 0; i < ring.size(); i++) {
|
||||||
out.emplace_back(i == 0 ? VT_MOVETO : VT_LINETO, std::round(ring[i].x), std::round(ring[i].y));
|
out.emplace_back(i == 0 ? VT_MOVETO : VT_LINETO, std::round(ring[i].x) * 2, std::round(ring[i].y) * 2);
|
||||||
}
|
|
||||||
out.emplace_back(VT_LINETO, std::round(ring[0].x), std::round(ring[0].y));
|
|
||||||
|
|
||||||
if (get_area(out, 0, out.size()) > 0) {
|
|
||||||
for (auto const &d : out) {
|
|
||||||
ret.push_back(d);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
out.emplace_back(VT_LINETO, std::round(ring[0].x) * 2, std::round(ring[0].y) * 2);
|
||||||
|
ret.push_back(ring_area(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool encloses(ring_area const &parent, ring_area const &child) {
|
||||||
|
if (std::fabs(child.area) > std::fabs(parent.area)) {
|
||||||
|
fprintf(stderr, "child area %f is greater than parent area %f\n", child.area, parent.area);
|
||||||
|
exit(EXIT_IMPOSSIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the edges of the child polygon are supposedly entirely contained within the parent
|
||||||
|
// (although the vertices may not be), so check the middle of an edge.
|
||||||
|
long long x = (child.geom[0].x + child.geom[1].x) / 2;
|
||||||
|
long long y = (child.geom[0].y + child.geom[1].y) / 2;
|
||||||
|
|
||||||
|
return pnpoly(parent.geom, 0, parent.geom.size(), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flatten_rings(std::vector<ring_area> &rings, size_t i, drawvec &out, ssize_t winding) {
|
||||||
|
// only the transition from winding order 0 to 1 or from 1 to 0
|
||||||
|
// is actually represented in the geometry.
|
||||||
|
//
|
||||||
|
// other transitions are outer rings nested inside other outer rings,
|
||||||
|
// or inner rings nested inside other inner rings.
|
||||||
|
if ((winding == 0 && rings[i].area > 0) ||
|
||||||
|
(winding == 1 && rings[i].area < 0)) {
|
||||||
|
for (auto const &g : rings[i].geom) {
|
||||||
|
out.emplace_back(g.op, g.x / 2, g.y / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rings[i].geom.clear();
|
||||||
|
|
||||||
|
if (rings[i].area > 0) {
|
||||||
|
winding++;
|
||||||
|
} else if (rings[i].area < 0) {
|
||||||
|
winding--;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t j = 0; j < rings[i].children.size(); j++) {
|
||||||
|
flatten_rings(rings, rings[i].children[j], out, winding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drawvec clean_polygon(drawvec const &geom, int z, int detail) {
|
drawvec clean_polygon(drawvec const &geom, int z, int detail) {
|
||||||
double scale = 1LL << (32 - detail - z);
|
double scale = 1LL << (32 - detail - z);
|
||||||
|
|
||||||
@@ -619,12 +675,28 @@ drawvec clean_polygon(drawvec const &geom, int z, int detail) {
|
|||||||
|
|
||||||
// reassemble segments into rings
|
// reassemble segments into rings
|
||||||
|
|
||||||
drawvec ret = reassemble(segments);
|
std::vector<ring_area> rings = reassemble(segments);
|
||||||
|
std::sort(rings.begin(), rings.end());
|
||||||
// remove collinear points?
|
|
||||||
|
|
||||||
// determine ring nesting
|
// determine ring nesting
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rings.size(); i++) { // from largest to smallest abs area
|
||||||
|
for (ssize_t j = i - 1; j >= 0; j--) { // from smallest to largest abs area of already examined
|
||||||
|
if (encloses(rings[j], rings[i])) {
|
||||||
|
printf("ring %zd (%f) encloses ring %zu (%f)\n", j, rings[j].area, i, rings[i].area);
|
||||||
|
rings[j].children.push_back(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
drawvec ret;
|
||||||
|
for (size_t i = 0; i < rings.size(); i++) {
|
||||||
|
flatten_rings(rings, i, ret, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove collinear points?
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
drawvec ret;
|
drawvec ret;
|
||||||
for (auto const &segment : segments) {
|
for (auto const &segment : segments) {
|
||||||
|
|||||||
Reference in New Issue
Block a user