bool hit_test(PathType & path, double x, double y, double tol) { bool inside=false; double x0 = 0; double y0 = 0; double x1 = 0; double y1 = 0; 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; }
bool middle_point(PathType & path, double & x, double & y) { double x0 = 0; double y0 = 0; double x1 = 0; double y1 = 0; 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; }
bool hit_test(PathType & path, double x, double y, double tol) { bool inside=false; double x0 = 0; double y0 = 0; double x1 = 0; double y1 = 0; path.rewind(0); unsigned command = path.vertex(&x0, &y0); if (command == SEG_END) { return false; } unsigned count = 0; mapnik::geometry_type::types geom_type = static_cast<mapnik::geometry_type::types>(path.type()); while (SEG_END != (command = path.vertex(&x1, &y1))) { if (command == SEG_CLOSE) { continue; } ++count; if (command == SEG_MOVETO) { x0 = x1; y0 = y1; continue; } switch(geom_type) { case mapnik::geometry_type::types::Polygon: { if ((((y1 <= y) && (y < y0)) || ((y0 <= y) && (y < y1))) && (x < (x0 - x1) * (y - y1)/ (y0 - y1) + x1)) inside=!inside; break; } case mapnik::geometry_type::types::LineString: { double distance = point_to_segment_distance(x,y,x0,y0,x1,y1); if (distance < tol) return true; break; } default: break; } x0 = x1; y0 = y1; } // TODO - handle multi-point? if (count == 0) // one vertex { return distance(x, y, x0, y0) <= tol; } return inside; }
bool centroid(PathType & path, double & x, double & y) { double x0 = 0.0; double y0 = 0.0; double x1 = 0.0; double y1 = 0.0; 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.0; double xtmp = 0.0; double ytmp = 0.0; unsigned count = 1; while (SEG_END != (command = path.vertex(&x1, &y1))) { if (command == SEG_CLOSE) continue; 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; ++count; } if (count <= 2) { x = (start_x + x0) * 0.5; y = (start_y + y0) * 0.5; return true; } if (atmp != 0) { x = (xtmp/(3*atmp)) + start_x; y = (ytmp/(3*atmp)) + start_y; } else { x = x0; y = y0; } return true; }
double path_length(PathType & path) { double x0 = 0; double y0 = 0; double x1 = 0; double y1 = 0; 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; }
bool hit_test_first(PathType & path, double x, double y) { bool inside=false; double x0 = 0; double y0 = 0; double x1 = 0; double y1 = 0; 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))) { if (command == SEG_CLOSE) { break; } ++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; } return inside; }
bool interior_position(PathType & path, double & x, double & y) { // start with the centroid if (!label::centroid(path, x,y)) return false; // 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 true; // 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<double> intersections; // only need to store the X as we know the y double x0 = 0; double y0 = 0; path.rewind(0); unsigned command = path.vertex(&x0, &y0); double x1 = 0; double y1 = 0; 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 true; x0=intersections[0]; double max_width = 0; for (unsigned ii = 1; ii < intersections.size(); ++ii) { double xi=intersections[ii]; double xc=(x0+xi)/2.0; double width = std::fabs(xi-x0); if (width > max_width && hit_test(path,xc,y,0)) { x=xc; max_width = width; break; } } return true; }
bool interior_position(PathType & path, double & x, double & y) { // start with the centroid if (!label::centroid(path, x,y)) return false; // 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<double> intersections; // only need to store the X as we know the y double x0 = 0; double y0 = 0; path.rewind(0); unsigned command = path.vertex(&x0, &y0); double x1 = 0; double y1 = 0; while (SEG_END != (command = path.vertex(&x1, &y1))) { if (command == SEG_CLOSE) continue; 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 true; std::sort(intersections.begin(), intersections.end()); double max_width = 0; for (unsigned ii = 1; ii < intersections.size(); ii += 2) { double xlow = intersections[ii-1]; double xhigh = intersections[ii]; double width = xhigh - xlow; if (width > max_width) { x = (xlow + xhigh) / 2.0; max_width = width; } } return true; }