#include "geo/private/math/convex_polygon.h" #include namespace ns { namespace geo { // 从下面的网址搬运过来修改的计算点集的外接多边形 // https://www.geeksforgeeks.org/convex-hull-set-1-jarviss-algorithm-or-wrapping/ // 此处小数点后的精确度必须精确到10位以后结果才会准确 // const double EPSION = 0.0000000000000001; const double EPSION = std::numeric_limits::min(); // A C++ program to find convex hull of a set of points. Refer // https://www.geeksforgeeks.org/orientation-3-ordered-points/ // To find orientation of ordered triplet (p, q, r). // The function returns following values // 0 --> p, q and r are colinear // 1 --> Clockwise // 2 --> Counterclockwise namespace { int orientation(const PointD& p, const PointD& q, const PointD& r) { double val = (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y); if (std::abs(val) <= EPSION) return 0; // colinear 共线 return (val > EPSION) ? 1 : 2; // clock or counterclock wise 顺时针或者逆时针方向 } // 如果有重复点去除重复点 std::vector repeatPoints(const std::vector& points) { std::vector rePoints; for (size_t i = 0; i < points.size(); i++) { PointD tmpPoint(points[i].x, points[i].y, points[i].z); auto iter = std::find(rePoints.begin(), rePoints.end(), tmpPoint); if (iter == rePoints.end()) // 去除重复点 { rePoints.emplace_back(tmpPoint); } } return rePoints; } } // namespace // Prints convex hull of a set of n points. std::vector convexHull(const std::vector& points) { std::vector rePoints = repeatPoints(points); // Initialize Result std::vector hull; // There must be at least 3 points size_t n = rePoints.size(); if (n < 3) { return hull; } // Find the leftmost point size_t l = 0; for (size_t i = 1; i < n; i++) { if (rePoints[i].x < rePoints[l].x) { l = i; } } // Start from leftmost point, keep moving counterclockwise // until reach the start point again. This loop runs O(h) // times where h is number of points in result or output. size_t p = l, q, count = 0; do { // Add current point to result hull.push_back(rePoints[p]); // Search for a point 'q' such that orientation(p, x, // q) is counterclockwise for all points 'x'. The idea // is to keep track of last visited most counterclock- // wise point in q. If any point 'i' is more counterclock- // wise than q, then update q. q = (p + 1) % n; for (size_t i = 0; i < n; i++) { // If i is more counterclockwise than current q, then // update q if (orientation(rePoints[p], rePoints[i], rePoints[q]) == 2) // 按照逆时针方向循环 { q = i; } } // Now q is the most counterclockwise with respect to p // Set p as q for next iteration, so that q is added to // result 'hull' p = q; count++; if (count == n) // 最多遍历n次跳出循环,避免出现死循环的现象 { p = l; } } while (p != l); // While we don't come to first point return hull; } // 下面这个判断一个点是否在多边形内的方法,比较复杂,想了解原理直接看下面的参考网址 // 参考网址:http://alienryderflex.com/polygon/ bool pointInPolygon(const std::vector& points, const PointD& point) { size_t j = points.size() - 1; bool oddNodes = false; for (size_t i = 0; i < points.size(); i++) { PointD tmpPointI = points[i]; PointD tmpPointJ = points[j]; if (((tmpPointI.y < point.y && tmpPointJ.y >= point.y) || (tmpPointJ.y < point.y && tmpPointI.y >= point.y)) && (tmpPointI.x <= point.x || tmpPointJ.x <= point.x)) { oddNodes ^= (tmpPointI.x + (point.y - tmpPointI.y) / (tmpPointJ.y - tmpPointI.y) * (tmpPointJ.x - tmpPointI.x) < point.x); } j = i; } return oddNodes; } } // namespace geo } // namespace ns