diff --git a/include/mapnik/offset_converter.hpp b/include/mapnik/offset_converter.hpp index 51d43e347..a5ac57c3e 100644 --- a/include/mapnik/offset_converter.hpp +++ b/include/mapnik/offset_converter.hpp @@ -35,6 +35,7 @@ #include #include #include +#include namespace mapnik { @@ -261,7 +262,15 @@ private: v.cmd = u.cmd; } - void displace2(vertex2d & v, double a, double b) const + int point_line_position(vertex2d const& a, vertex2d const& b, vertex2d const& point) const + { + double position = (b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x); + if (position > 1e-6) return 1; + if (position < -1e-6) return -1; + return 0; + } + + void displace2(vertex2d & v1, vertex2d const& v0, vertex2d const& v2, double a, double b) const { double sa = offset_ * std::sin(a); double ca = offset_ * std::cos(a); @@ -269,26 +278,60 @@ private: double hsa = h * sa; double hca = h * ca; double abs_offset = std::abs(offset_); - if (hsa > 1.0 * abs_offset) + double hsaca = ca-hsa; + double hcasa = -sa-hca; + double abs_hsaca = std::abs(hsaca); + double abs_hcasa = std::abs(hcasa); + double abs_hsa = std::abs(hsa); + double abs_hca = std::abs(hca); + + vertex2d v_tmp(vertex2d::no_init); + v_tmp.x = v1.x - sa - hca; + v_tmp.y = v1.y + ca - hsa; + v_tmp.cmd = v1.cmd; + + int same = point_line_position(v0, v2, v_tmp)*point_line_position(v0, v2, v1); + + if (same >= 0 && std::abs(h) < 10) { - hsa = 1.0 * abs_offset; + v1.x = v_tmp.x; + v1.y = v_tmp.y; } - else if (hsa < -1.0 * abs_offset) + else if ((v0.x-v1.x)*(v0.x-v1.x) + (v0.y-v1.y)*(v0.y-v1.y) + + (v0.x-v2.x)*(v0.x-v2.x) + (v0.y-v2.y)*(v0.y-v2.y) > offset_*offset_) { - hsa = -1.0 * abs_offset; + if (abs_hsa > abs_offset || abs_hca > abs_offset) + { + double scale = std::max(abs_hsa,abs_hca); + scale = scale < 1e-6 ? 1. : abs_offset / scale; + // interpolate hsa, hca to <0,abs_offset> + hsa = hsa * scale; + sa = sa * scale; + hca = hca * scale; + ca = ca * scale; + } + v1.x = v1.x - sa - hca; + v1.y = v1.y + ca - hsa; } - if (hca > 1.0 * abs_offset) - { - hca = 1.0 * abs_offset; + else + { + if (abs_hsaca*abs_hsaca + abs_hcasa*abs_hcasa > abs_offset*abs_offset) + { + double d = (abs_hsaca*abs_hsaca + abs_hcasa*abs_hcasa); + d = d < 1e-6 ? 1. : d; + double scale = (abs_offset*abs_offset)/d; + v1.x = v1.x + hcasa*scale; + v1.y = v1.y + hsaca*scale; + } + else + { + v1.x = v1.x + hcasa; + v1.y = v1.y + hsaca; + } } - else if (hca < -1.0 * abs_offset) - { - hca = -1.0 * abs_offset; - } - v.x = v.x - sa - hca; - v.y = v.y + ca - hsa; } - + + status init_vertices() { if (status_ != initial) // already initialized @@ -412,7 +455,7 @@ private: if (bulge_steps == 0) { - displace2(v1, angle_a, angle_b); + displace2(v1, v0, v2, angle_a, angle_b); push_vertex(v1); } else @@ -439,7 +482,9 @@ private: } start_v2.x = v2.x; start_v2.y = v2.y; - bool continue_loop = true; + bool continue_loop = true; + vertex2d tmp_prev(vertex2d::no_init); + while (i < points.size()) { v1 = v2; @@ -530,11 +575,15 @@ private: << " degrees ((< with " << bulge_steps << " segments"; } #endif + tmp_prev.cmd = v1.cmd; + tmp_prev.x = v1.x; + tmp_prev.y = v1.y; + if (v1.cmd == SEG_MOVETO) { if (bulge_steps == 0) { - displace2(v1, angle_a, angle_b); + displace2(v1, v0, v2, angle_a, angle_b); push_vertex(v1); } else @@ -547,7 +596,7 @@ private: { if (bulge_steps == 0) { - displace2(v1, angle_a, angle_b); + displace2(v1, v0, v2, angle_a, angle_b); push_vertex(v1); } else @@ -565,6 +614,9 @@ private: push_vertex(v1); } } + v0.cmd = tmp_prev.cmd; + v0.x = tmp_prev.x; + v0.y = tmp_prev.y; } // last vertex