|
- #include "geo/private/math/convex_polygon.h"
-
- #include <algorithm>
-
- 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<double>::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<PointD> repeatPoints(const std::vector<PointD>& points)
- {
- std::vector<PointD> 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<PointD> convexHull(const std::vector<PointD>& points)
- {
- std::vector<PointD> rePoints = repeatPoints(points);
- // Initialize Result
- std::vector<PointD> 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<PointD>& 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
|