From f6fa57da2641488f444b277199a08ded5f675eae Mon Sep 17 00:00:00 2001 From: artemp Date: Thu, 19 Jul 2012 16:31:47 +0100 Subject: [PATCH] + add VertexSource based implementations of label position algos --- include/mapnik/geom_util.hpp | 201 +++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/include/mapnik/geom_util.hpp b/include/mapnik/geom_util.hpp index ce9440dcc..4d90ae380 100644 --- a/include/mapnik/geom_util.hpp +++ b/include/mapnik/geom_util.hpp @@ -32,6 +32,7 @@ // stl #include +#include namespace mapnik { @@ -216,6 +217,206 @@ struct filter_at_point return extent.contains(pt_); } }; + +//////////////////////////////////////////////////////////////////////////// +template +double path_length(PathType & path) +{ + double x0,y0,x1,y1; + path.rewind(0); + unsigned command = path.vertex(&x0,&y0); + if (command == SEG_END) return 0; + double length = 0; + while (SEG_END != (command = path.vertex(&x1, &y1))) + { + length += distance(x0,y0,x1,y1); + x0 = x1; + y0 = y1; + } + return length; +} + +template +bool middle_point(PathType & path, double & x, double & y) +{ + double x0,y0,x1,y1; + double mid_length = 0.5 * path_length(path); + path.rewind(0); + unsigned command = path.vertex(&x0,&y0); + if (command == SEG_END) return false; + double dist = 0.0; + while (SEG_END != (command = path.vertex(&x1, &y1))) + { + double seg_length = distance(x0, y0, x1, y1); + + if ( dist + seg_length >= mid_length) + { + double r = (mid_length - dist)/seg_length; + x = x0 + (x1 - x0) * r; + y = y0 + (y1 - y0) * r; + break; + } + dist += seg_length; + x0 = x1; + y0 = y1; + } + return true; +} + +template +bool centroid(PathType & path, double & x, double & y) +{ + double x0; + double y0; + double x1; + double y1; + double start_x; + double start_y; + + path.rewind(0); + unsigned command = path.vertex(&x0, &y0); + if (command == SEG_END) return false; + + start_x = x0; + start_y = y0; + + double atmp = 0; + double xtmp = 0; + double ytmp = 0; + + while (SEG_END != (command = path.vertex(&x1, &y1))) + { + double dx0 = x0 - start_x; + double dy0 = y0 - start_y; + double dx1 = x1 - start_x; + double dy1 = y1 - start_y; + + double ai = dx0 * dy1 - dx1 * dy0; + atmp += ai; + xtmp += (dx1 + dx0) * ai; + ytmp += (dy1 + dy0) * ai; + x0 = x1; + y0 = y1; + } + + if (atmp != 0) + { + x = (xtmp/(3*atmp)) + start_x; + y = (ytmp/(3*atmp)) + start_y; + } + else + { + x = x0; + y = y0; + } + return true; +} + +template +bool hit_test(PathType & path, double x, double y, double tol) +{ + bool inside=false; + double x0, y0, x1, y1; + path.rewind(0); + unsigned command = path.vertex(&x0, &y0); + if (command == SEG_END) return false; + unsigned count = 0; + while (SEG_END != (command = path.vertex(&x1, &y1))) + { + ++count; + if (command == SEG_MOVETO) + { + x0 = x1; + y0 = y1; + continue; + } + if ((((y1 <= y) && (y < y0)) || + ((y0 <= y) && (y < y1))) && + (x < (x0 - x1) * (y - y1)/ (y0 - y1) + x1)) + inside=!inside; + + x0 = x1; + y0 = y1; + } + + if (count == 0) // one vertex + { + return distance(x, y, x0, y0) <= fabs(tol); + } + return inside; +} + +template +void label_interior_position(PathType & path, double & x, double & y) +{ + // start with the centroid + centroid(path, x,y); + + // if we are not a polygon, or the default is within the polygon we are done + if (hit_test(path,x,y,0.001)) + return; + + // otherwise we find a horizontal line across the polygon and then return the + // center of the widest intersection between the polygon and the line. + + std::vector intersections; // only need to store the X as we know the y + + double x0; + double y0; + path.rewind(0); + unsigned command = path.vertex(&x0, &y0); + double x1,y1; + while (SEG_END != (command = path.vertex(&x1, &y1))) + { + if (command != SEG_MOVETO) + { + // if the segments overlap + if (y0==y1) + { + if (y0==y) + { + double xi = (x0+x1)/2.0; + intersections.push_back(xi); + } + } + // if the path segment crosses the bisector + else if ((y0 <= y && y1 >= y) || + (y0 >= y && y1 <= y)) + { + // then calculate the intersection + double xi = x0; + if (x0 != x1) + { + double m = (y1-y0)/(x1-x0); + double c = y0 - m*x0; + xi = (y-c)/m; + } + + intersections.push_back(xi); + } + } + x0 = x1; + y0 = y1; + } + // no intersections we just return the default + if (intersections.empty()) + return; + x0=intersections[0]; + double max_width = 0; + for (unsigned ii = 1; ii < intersections.size(); ++ii) + { + double x1=intersections[ii]; + double xc=(x0+x1)/2.0; + double width = std::fabs(x1-x0); + if (width > max_width && hit_test(path,xc,y,0)) + { + x=xc; + max_width = width; + break; + } + } +} + } #endif // MAPNIK_GEOM_UTIL_HPP