Add --buffer-polygons-outward

This commit is contained in:
Erica Fischer
2023-10-27 22:39:01 -07:00
parent 089be8aff1
commit 6c58ae4162
7 changed files with 84 additions and 75 deletions
+71 -61
View File
@@ -197,6 +197,23 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *still_needs_sim
double area = get_area(geom, i, j);
long long minx = LLONG_MAX;
long long maxx = LLONG_MIN;
long long miny = LLONG_MAX;
long long maxy = LLONG_MIN;
for (auto const &d : geom) {
minx = std::min(minx, (long long) d.x);
maxx = std::max(maxx, (long long) d.x);
miny = std::min(miny, (long long) d.y);
maxy = std::max(maxy, (long long) d.y);
}
if (area > 0 && area <= pixel * pixel && area < (maxx - minx) * (maxy - miny) / 5) {
// if the polygon doesn't use most of its area,
// don't let it be dust, because the shape is
// probably something weird and interesting.
area = pixel * pixel * 2;
}
// XXX There is an ambiguity here: If the area of a ring is 0 and it is followed by holes,
// we don't know whether the area-0 ring was a hole too or whether it was the outer ring
// that these subsequent holes are somehow being subtracted from. I hope that if a polygon
@@ -485,6 +502,26 @@ drawvec impose_tile_boundaries(drawvec &geom, long long extent) {
return out;
}
bool is_shared_node(draw d, int z, int tx, int ty, struct node *shared_nodes_map, size_t nodepos) {
if (nodepos > 0) {
// offset to global
if (z != 0) {
d.x += tx * (1LL << (32 - z));
d.y += ty * (1LL << (32 - z));
}
// to quadkey
struct node n;
n.index = encode_quadkey((unsigned) d.x, (unsigned) d.y);
if (bsearch(&n, shared_nodes_map, nodepos / sizeof(node), sizeof(node), nodecmp) != NULL) {
return true;
}
}
return false;
}
drawvec simplify_lines(drawvec &geom, int z, int tx, int ty, int detail, bool mark_tile_bounds, double simplification, size_t retain, drawvec const &shared_nodes, struct node *shared_nodes_map, size_t nodepos) {
int res = 1 << (32 - detail - z);
long long area = 1LL << (32 - z);
@@ -514,21 +551,8 @@ drawvec simplify_lines(drawvec &geom, int z, int tx, int ty, int detail, bool ma
geom[i].necessary = true;
}
if (nodepos > 0) {
// offset to global
draw d = geom[i];
if (z != 0) {
d.x += tx * (1LL << (32 - z));
d.y += ty * (1LL << (32 - z));
}
// to quadkey
struct node n;
n.index = encode_quadkey((unsigned) d.x, (unsigned) d.y);
if (bsearch(&n, shared_nodes_map, nodepos / sizeof(node), sizeof(node), nodecmp) != NULL) {
geom[i].necessary = true;
}
if (is_shared_node(geom[i], z, tx, ty, shared_nodes_map, nodepos)) {
geom[i].necessary = true;
}
}
}
@@ -1399,33 +1423,24 @@ drawvec checkerboard_anchors(drawvec const &geom, int tx, int ty, int z, unsigne
return out;
}
static double anglediff(double dx1, double dy1, double dx2, double dy2) {
// https://gist.github.com/e-n-f/417cd85f6d9b00dab887276376e77f30
// https://twitter.com/enf/status/795798781186830341
bool is_continous_poly(drawvec const &geom, size_t i, size_t j, int z, int tx, int ty, struct node *shared_nodes_map, size_t nodepos) {
size_t count = 0;
double cross = dx1 * dy2 - dy1 * dx2;
double sd = sqrt(dx1 * dx1 + dy1 * dy1);
double cd = sqrt(dx2 * dx2 + dy2 * dy2);
// j - 1 to avoid double-counting the duplicate last node
for (; i < j - 1; i++) {
if (is_shared_node(geom[i], z, tx, ty, shared_nodes_map, nodepos)) {
count++;
double dot = dx1 * dx2 + dy1 * dy2;
double ret;
if (dot < 0) {
if (cross < 0) {
ret = -M_PI - asin(cross / (sd * cd));
} else {
ret = M_PI - asin(cross / (sd * cd));
if (count >= 4) {
return true;
}
}
} else {
ret = asin(cross / (sd * cd));
}
if (ret < 0) {
// make it 0 to 360, not -180 to 180
ret += 2 * M_PI;
}
return ret;
return false;
}
drawvec buffer_poly(drawvec const &geom, double buffer) {
drawvec buffer_poly(drawvec const &geom, double buffer, int z, int tx, int ty, struct node *shared_nodes_map, size_t nodepos) {
drawvec out = geom;
if (buffer != 0) {
@@ -1438,38 +1453,33 @@ drawvec buffer_poly(drawvec const &geom, double buffer) {
}
}
for (size_t k = i; k < j - 1; k++) {
draw p0 = geom[(k + 0 - i) % (j - i - 1) + i];
draw p1 = geom[(k + 1 - i) % (j - i - 1) + i];
draw p2 = geom[(k + 2 - i) % (j - i - 1) + i];
if (!is_continous_poly(geom, i, j, z, tx, ty, shared_nodes_map, nodepos)) {
for (size_t k = i; k < j - 1; k++) {
draw p0 = geom[(k + 0 - i) % (j - i - 1) + i];
draw p1 = geom[(k + 1 - i) % (j - i - 1) + i];
draw p2 = geom[(k + 2 - i) % (j - i - 1) + i];
double adiff = 2 * M_PI - anglediff(p0.x - p1.x, p0.y - p1.y,
p2.x - p1.x, p2.y - p1.y);
double a10 = atan2(p1.y - p0.y, p1.x - p0.x);
double a21 = atan2(p2.y - p1.y, p2.x - p1.x);
double a1 = atan2(p1.y - p0.y, p1.x - p0.x);
double dx = cos(a10 - 90 * M_PI / 180) + cos(a21 - 90 * M_PI / 180);
double dy = sin(a10 - 90 * M_PI / 180) + sin(a21 - 90 * M_PI / 180);
printf("at %zu %zu %zu ",
(k + 0 - i) % (j - i - 1) + i,
(k + 1 - i) % (j - i - 1) + i,
(k + 2 - i) % (j - i - 1) + i);
printf("%lld,%lld to %lld,%lld to %lld,%lld: ", p0.x, p0.y, p1.x, p1.y, p2.x, p2.y);
printf("angle %f then %f\n",
atan2(p1.y - p0.y, p1.x - p0.x) * 180 / M_PI,
atan2(p2.y - p1.y, p2.x - p1.x) * 180 / M_PI);
// the angle halfway between the angles
// perpendicular to a0->a1 (a10) and a1->a2 (a21)
double a2 = atan2(dy, dx);
double a10 = atan2(p1.y - p0.y, p1.x - p0.x);
double a21 = atan2(p2.y - p1.y, p2.x - p1.x);
out[(k + 1 - i) % (j - i - 1) + i].x = std::round(p1.x + buffer * cos(a2));
out[(k + 1 - i) % (j - i - 1) + i].y = std::round(p1.y + buffer * sin(a2));
}
double dx = cos(a10 - 90 * M_PI / 180) + cos(a21 - 90 * M_PI / 180);
double dy = sin(a10 - 90 * M_PI / 180) + sin(a21 - 90 * M_PI / 180);
double a2 = atan2(dy, dx);
out[(k + 1 - i) % (j - i - 1) + i].x = std::round(p1.x + buffer * cos(a2));
out[(k + 1 - i) % (j - i - 1) + i].y = std::round(p1.y + buffer * sin(a2));
out[j - 1].x = out[i].x;
out[j - 1].y = out[i].y;
} else {
printf("skipping continuous ring %zu to %zu\n", i, j);
}
out[j - 1].x = out[i].x;
out[j - 1].y = out[i].y;
i = j - 1;
}
}
}