diff --git a/geometry.cc b/geometry.cc index 0005a5c2..1b71b973 100644 --- a/geometry.cc +++ b/geometry.cc @@ -341,6 +341,163 @@ drawvec close_poly(drawvec &geom) { return out; } +static bool inside(draw d, int edge, long long area, long long buffer) { + long long clip_buffer = buffer * area / 256; + + switch (edge) { + case 0: // top + return d.y > -clip_buffer; + + case 1: // right + return d.x < area + clip_buffer; + + case 2: // bottom + return d.y < area + clip_buffer; + + case 3: // left + return d.x > -clip_buffer; + } + + fprintf(stderr, "internal error inside\n"); + exit(EXIT_FAILURE); +} + +// http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect +static draw get_line_intersection(draw p0, draw p1, draw p2, draw p3) { + double s1_x = p1.x - p0.x; + double s1_y = p1.y - p0.y; + double s2_x = p3.x - p2.x; + double s2_y = p3.y - p2.y; + + double t; + // s = (-s1_y * (p0.x - p2.x) + s1_x * (p0.y - p2.y)) / (-s2_x * s1_y + s1_x * s2_y); + t = (s2_x * (p0.y - p2.y) - s2_y * (p0.x - p2.x)) / (-s2_x * s1_y + s1_x * s2_y); + + return draw(VT_LINETO, p0.x + (t * s1_x), p0.y + (t * s1_y)); +} + +static draw intersect(draw a, draw b, int edge, long long area, long long buffer) { + long long clip_buffer = buffer * area / 256; + + switch (edge) { + case 0: // top + return get_line_intersection(a, b, draw(VT_MOVETO, -clip_buffer, -clip_buffer), draw(VT_MOVETO, area + clip_buffer, -clip_buffer)); + break; + + case 1: // right + return get_line_intersection(a, b, draw(VT_MOVETO, area + clip_buffer, -clip_buffer), draw(VT_MOVETO, area + clip_buffer, area + clip_buffer)); + break; + + case 2: // bottom + return get_line_intersection(a, b, draw(VT_MOVETO, area + clip_buffer, area + clip_buffer), draw(VT_MOVETO, -clip_buffer, area + clip_buffer)); + break; + + case 3: // left + return get_line_intersection(a, b, draw(VT_MOVETO, -clip_buffer, area + clip_buffer), draw(VT_MOVETO, -clip_buffer, -clip_buffer)); + break; + } + + 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, int z, int detail, int buffer) { + drawvec out = geom; + + long long area = 0xFFFFFFFF; + if (z != 0) { + area = 1LL << (32 - z); + } + + for (int edge = 0; edge < 4; edge++) { + if (out.size() > 0) { + drawvec in = out; + out.resize(0); + + draw S = in[in.size() - 1]; + + for (unsigned e = 0; e < in.size(); e++) { + draw E = in[e]; + + if (inside(E, edge, area, buffer)) { + if (!inside(S, edge, area, buffer)) { + out.push_back(intersect(S, E, edge, area, buffer)); + } + out.push_back(E); + } else if (inside(S, edge, area, buffer)) { + out.push_back(intersect(S, E, edge, area, buffer)); + } + + 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[0].op = VT_MOVETO; + for (unsigned i = 1; i < out.size(); i++) { + out[i].op = VT_LINETO; + } + } + + return out; +} + +drawvec simple_clip_poly(drawvec &geom, int z, int detail, int buffer) { + if (z == 0) { + return geom; + } + + drawvec out; + + for (unsigned i = 0; i < geom.size(); i++) { + if (geom[i].op == VT_MOVETO) { + unsigned j; + for (j = i + 1; j < geom.size(); j++) { + if (geom[j].op != VT_LINETO) { + break; + } + } + + drawvec tmp; + for (unsigned k = i; k < j; k++) { + tmp.push_back(geom[k]); + } + tmp = clip_poly1(tmp, z, detail, buffer); + 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); + } + } + for (unsigned k = 0; k < tmp.size(); k++) { + out.push_back(tmp[k]); + } + + i = j - 1; + } else { + fprintf(stderr, "Unexpected operation in polygon %d\n", (int) geom[i].op); + exit(EXIT_FAILURE); + } + } + + return out; +} + drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double *accum_area) { drawvec out; long long pixel = (1 << (32 - detail - z)) * 2; diff --git a/geometry.hh b/geometry.hh index 3f057ecb..385b13ab 100644 --- a/geometry.hh +++ b/geometry.hh @@ -21,6 +21,7 @@ void to_tile_scale(drawvec &geom, int z, int detail); drawvec remove_noop(drawvec geom, int type, int shift); drawvec clip_point(drawvec &geom, int z, int detail, long long buffer); drawvec clean_or_clip_poly(drawvec &geom, int z, int detail, int buffer, bool clip); +drawvec simple_clip_poly(drawvec &geom, int z, int detail, int buffer); drawvec close_poly(drawvec &geom); drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double *accum_area); drawvec clip_lines(drawvec &geom, int z, int detail, long long buffer);