diff --git a/geometry.cpp b/geometry.cpp index 13a6c3305..f2a630eb9 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1370,3 +1370,71 @@ 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 + + double cross = dx1 * dy2 - dy1 * dx2; + double sd = sqrt(dx1 * dx1 + dy1 * dy1); + double cd = sqrt(dx2 * dx2 + dy2 * dy2); + + 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)); + } + } else { + ret = asin(cross / (sd * cd)); + } + if (ret < 0) { + // make it 0 to 360, not -180 to 180 + ret += 2 * M_PI; + } + return ret; +} + +drawvec buffer_poly(drawvec const &geom, double buffer) { + drawvec out = geom; + + if (buffer != 0) { + for (size_t i = 0; i < geom.size(); i++) { + if (geom[i].op == VT_MOVETO) { + size_t j; + for (j = i + 1; j < geom.size(); j++) { + if (geom[j].op != VT_LINETO) { + break; + } + } + + 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 a1 = atan2(p1.y - p0.y, p1.x - p0.x); + + printf("at %zu %zu %zu ", (k + 0 - i) % (j - i - 1) + i, + (k + 2 - i) % (j - i - 1) + i, + (k + 3 - 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 on top of %f\n", adiff * 180 / M_PI, a1 * 180 / M_PI); + + out[(k + 2 - i) % (j - i - 1) + i].x = std::round(p1.x + buffer * cos(a1 - adiff / 2)); + out[(k + 2 - i) % (j - i - 1) + i].y = std::round(p1.y + buffer * sin(a1 - adiff / 2)); + } + + out[j - 1].x = out[i].x; + out[j - 1].y = out[i].y; + } + } + } + + return out; +} diff --git a/geometry.hpp b/geometry.hpp index edebe1fb4..fbe510113 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -96,4 +96,6 @@ double distance_from_line(long long point_x, long long point_y, long long segA_x std::string overzoom(std::string s, int oz, int ox, int oy, int nz, int nx, int ny, int detail, int buffer, std::set const &keep); +drawvec buffer_poly(drawvec const &geom, double buffer); + #endif diff --git a/tile.cpp b/tile.cpp index 4584c316a..61a932455 100644 --- a/tile.cpp +++ b/tile.cpp @@ -609,6 +609,8 @@ void *partial_feature_worker(void *v) { int out_detail = (*partials)[i].extra_detail; drawvec geom = (*partials)[i].geoms[0]; + drawvec before_scaling = geom; + to_tile_scale(geom, z, out_detail); if (t == VT_POLYGON) { @@ -622,10 +624,19 @@ void *partial_feature_worker(void *v) { check_polygon(geom); } - if (geom.size() < 3) { + if (geom.size() < 4) { if (area > 0) { - // area is in world coordinates, calculated before scaling down - geom = revive_polygon(before, area, z, out_detail); + // Try reviving the polygon by buffering it outward a little + + geom = buffer_poly(before_scaling, (1LL << (32 - z - out_detail)) * 1.0); + to_tile_scale(geom, z, out_detail); + geom = clean_or_clip_poly(geom, 0, 0, false, true); + + if (geom.size() < 4) { + // OK, that didn't work, make a placeholder + // area is in world coordinates, calculated before scaling down + geom = revive_polygon(before, area, z, out_detail); + } } else { geom.clear(); }