最近在学计算几何,在博客园里看到一篇对于计算几何中比较常见的基础的数据结构和问题的实现代码的集合,但是并没有目录,转载到博客中为各节标题加上目录并纠正了其中的一些错误,再加上我个人的一些补充方便日后使用。随着学习还会进行不断的补充。也欢迎大家在评论区提出问题和遗漏的地方。
原文地址https://www.cnblogs.com/zutterhao/p/11694367.html
会涉及到一些基本的向量、线性代数、图形学中的知识,都是比较基础的,在这里并未给出相应原理,只有代码的总结,自己画画图也能想出来。(一些需要说明的原理可以在我的计算几何的分栏中找到,在对应问题下也给出了相应文章的链接)
#include
#include
#include
#include
#include
using namespace std;
const double PI = 3.14159265;
struct Point
{
double x; // x坐标
double y; // y坐标
double z; // z坐标(默认为0,如果需要三维点则给z赋值)
Point(double a = 0, double b = 0, double c = 0) {
x = a; y = b; z = c; } // 构造函数
};
Point add(const Point& lhs, const Point& rhs)
{
Point res;
res.x = lhs.x + rhs.x;
res.y = lhs.y + rhs.y;
res.z = lhs.z + rhs.z;
return res;
}
Point sub(const Point& lhs, const Point& rhs)
{
Point res;
res.x = lhs.x - rhs.x;
res.y = lhs.y - rhs.y;
res.z = lhs.z - rhs.z;
return res;
}
Point mul(const Point& p, double ratio)
{
Point res;
res.x = p.x * ratio;
res.y = p.y * ratio;
res.z = p.z * ratio;
return res;
}
Point div(const Point& p, double ratio)
{
Point res;
res.x = p.x / ratio;
res.y = p.y / ratio;
res.z = p.z / ratio;
return res;
}
bool equal(const Point& lhs, const Point& rhs)
{
return(lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z);
}
struct Line
{
Point s; // 起点
Point e; // 终点
bool is_seg; // 是否是线段
Line() {
}; // 默认构造函数
Line(Point a, Point b, bool _is_seg = true) {
s = a; e = b; is_seg = _is_seg; } // 构造函数(默认是线段)
};
struct Triangle
{
Point v0;
Point v1;
Point v2;
bool is_plane;
Triangle() {
}; // 默认构造函数
Triangle(Point a, Point b, Point c, bool _is_plane = false) {
v0 = a; v1 = b; v2 = c; is_plane = _is_plane; }// 构造函数(默认是三角形)
};
// 参数:p1 : 第一个点 p2: 第二个点
double distance(const Point& p1, const Point& p2)
{
return(sqrt(pow(p1.x - p2.x, 2) + pow(p1.y - p2.y, 2) + pow(p1.z - p2.z, 2)));
}
// 参数: vec 矢量
double length(const Point& vec)
{
return (sqrt(pow(vec.x, 2) + pow(vec.y, 2) + pow(vec.z, 2)));
}
// 参数: vec : 矢量
Point normalize(const Point& vec)
{
Point res;
res = div(vec, length(vec));//向量除以向量长度
return res;
}
// 参数:(p1-op)为矢量1,(p2-op)为矢量2
double dotMultiply(const Point& op, const Point& p1, const Point& p2)
{
return ((p1.x - op.x) * (p2.x - op.x) + (p1.y - op.y) * (p2.y - op.y) + (p1.z - op.z) * (p2.z - op.z));
}
// 参数:vec1为矢量1,vec2为矢量2
//
double dotMultiply(const Point& vec1, const Point& vec2)
{
return(vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z);
}
// 参数:(p1-op)为矢量1,(p2-op)为矢量2
Point multiply(const Point& op, const Point& p1, const Point& p2)
{
Point result;
result.x = (p1.y - op.y) * (p2.z - op.z) - (p2.y - op.y) * (p1.z - op.z);
result.y = (p1.z - op.z) * (p2.x - op.x) - (p2.z - op.z) * (p1.x - op.x);
result.z = (p1.x - op.x) * (p2.y - op.y) - (p2.x - op.x) * (p1.y - op.y);
return result;
}
// 参数: vec1为矢量1,vec2为矢量2
//
Point multiply(const Point& vec1, const Point& vec2)
{
Point result;
result.x = vec1.y * vec2.z - vec2.y * vec1.z;
result.y = vec1.z * vec2.x - vec2.z * vec1.x;
result.z = vec1.x * vec2.y - vec2.x * vec1.y;
return result;
}
具体原理及实现https://blog.csdn.net/derbi123123/article/details/106359505
// 参数: p : 点 l:直线
double ptolDistance(const Point& p, const Line& l)
{
Point line_vec = sub(l.e,l.s);//直线向量
Point point_vec = sub(p, l.s);//点到直线端点的向量
// 首先计算点在线段投影长度
double project_len = dotMultiply(line_vec, point_vec) / length(line_vec);
// 根据勾股定理计算点的距离
double distance = sqrt(pow(length(point_vec), 2) - pow(project_len, 2));
return distance;
}
具体原理及实现:https://blog.csdn.net/derbi123123/article/details/106359925
// 参数:p : 点 l : 线
Point ptolProjection(const Point& p, const Line& l)
{
Point line_vec = sub(l.e, l.s);
Point point_vec = sub(p, l.s);
Point unit_line_vec = normalize(line_vec);
// 计算投影长度
double project_len = dotMultiply(point_vec, unit_line_vec);
// 投影点
Point project_p = add(l.s,mul(unit_line_vec, project_len));
return project_p;
}
// 参数: p : 点 l : 对称线
Point ptolSymmetry(const Point& p, const Line& l)
{
// 首先求出点在直线上的投影点
Point project_p = ptolProjection(p, l);
// 点到投影点的向量
Point project_vec = sub(project_p, p);
// 对称点
Point symmetry_p = add(p, mul(project_vec, 2));
return symmetry_p;
}
// 线分为直线和线段,直线表示的是直线是否经过点
// 参数:p : 点 l : 线段或者线
bool isponl(const Point& p, const Line& l)
{
Point line_vec = sub(l.e, l.s);
Point point_vec1 = sub(p, l.s);
Point point_vec2 = sub(p, l.e);
Point mul_vec = multiply(line_vec, point_vec1);
double dot = dotMultiply(point_vec1, point_vec2);
// 点是否在线段上
if (l.is_seg)
{
if (equal(p,l.s) || equal(p,l.e))
return true;
return (0.0 == length(mul_vec) && dot < 0.0);//共线向量的叉乘为0
}
// 点是否在直线上
else
{
return (0.0 == length(mul_vec));
}
}
//利用行列式的几何意义,通过判断正负来判断点在直线向量的左侧还是右侧
//已知直线上的两点a、b,求c在直线的左侧还是右侧
int Area2(Point a,Point b,Point c){
return a.x*b.y-a.y*b.x+b.x*c.y-b.y*c.x+c.x*a.y-c.y*a.x;
}
bool ToLeft(Point a,Point b,Point c){
return Area2(a,b,c)>0;
}
// 参数: op : 矢量公共点 p1 : 矢量1端点 p2 : 矢量2端点
double Cos(const Point& op, const Point& p1, const Point& p2)
{
Point vec1 = sub(p1, op);
Point vec2 = sub(p2, op);
return Cos(vec1, vec2);
}
// 参数: vec1 矢量1 vec2 矢量2
double Cos(const Point& vec1, const Point& vec2)
{
Point unit_vec1 = normalize(vec1);
Point unit_vec2 = normalize(vec2);
return dotMultiply(unit_vec1, unit_vec2);
}
// 参数: op : 矢量公共点 p1 : 矢量1端点 p2 : 矢量2端点
double Sin(const Point& op, const Point& p1, const Point& p2)
{
Point vec1 = sub(p1, op);
Point vec2 = sub(p2, op);
return Sin(vec1, vec2);
}
// 参数: vec1 矢量1 vec2 矢量2
double Sin(const Point& vec1, const Point& vec2)
{
return sqrt(1.0 - pow(Cos(vec1, vec2), 2));
}
// 参数: op : 矢量公共点 p1 : 矢量1端点 p2 : 矢量2端点
double Tan(const Point& op, const Point& p1, const Point& p2)
{
Point vec1 = sub(p1, op);
Point vec2 = sub(p2, op);
return Tan(vec1, vec2);
}
// 参数: vec1 矢量1 vec2 矢量2
double Tan(const Point& vec1, const Point& vec2)
{
double cos = Cos(vec1, vec2);
double sin = Sin(vec1, vec2);
// 分母不为零
if (0.0 == cos)
return -1;
else
return (sin / cos);
}
// 参数: op : 矢量公共点 p1 : 矢量1端点 p2 : 矢量2端点 is_radian : 默认为弧度制
//
double Angle(const Point& op, const Point& p1, const Point& p2, bool is_radian)
{
double cos_value = Cos(op, p1, p2);
if (is_radian)
{
return acos(cos_value);
}
else
{
return (acos(cos_value) / PI * 180.0);
}
}
// 参数: vec1 : 矢量1 vec2 : 矢量2
//
double Angle(const Point& vec1, const Point& vec2,bool is_radian)
{
double cos_value = Cos(vec1, vec2);
if (is_radian)
{
return acos(cos_value);
}
else
{
return (acos(cos_value) / PI * 180.0);
}
}
bool isPointsCollinear(const Point& p1, const Point& p2, const Point& p3)
{
Line line(p1, p2, false);
// 判断第三个点是否在前两个点的线段上
return isponl(p3, line);
}
int Area2(Point a,Point b,Point c)
{
return a.x*b.y-a.y*b.x+b.x*c.y-b.y*c.x+c.x*a.y-c.y*a.x;
}
bool ToLeft(Point p,Line l)
{
return Area2(l.L,l.R,p)==0;
}
// 其中如果线段的端点重合或者某个线段端点在另一个线段上也算相交
// 线段判断是否相交,如果是直线则相当于判断是否平行
//
// 参数: l1 : 线段1 l2 : 线段2 inter_p : 如果相交返回交点
//
bool isSegIntersect(const Line& l1, const Line& l2,Point& inter_p)
{
Point line1 = sub(l1.e, l1.s);
Point line2 = sub(l2.e, l2.s);
Point norm1 = normalize(line1);
Point norm2 = normalize(line2);
// 线段相交
if (l1.is_seg)
{
// 端点在线段上
if (isponl(l1.s, l2))
{
inter_p = l1.s;
return true;
}
if (isponl(l1.e, l2))
{
inter_p = l1.e;
return true;
}
if (isponl(l2.s, l1))
{
inter_p = l2.s;
return true;
}
if (isponl(l2.e, l1))
{
inter_p = l2.e;
return true;
}
// 判断线段是否相互跨立
double dot1 = dotMultiply(multiply(sub(l2.s, l1.s), line1), multiply(sub(l2.e, l1.s), line1));
double dot2 = dotMultiply(multiply(sub(l1.s, l2.s), line2), multiply(sub(l1.e, l2.s), line2));
if (dot1 < 0.0 && dot2 < 0.0)
{
double t1 = length(multiply(sub(l1.s, l2.s), norm2)) / length(multiply(norm2, norm1));
double t2 = length(multiply(sub(l2.s, l1.s), norm1)) / length(multiply(norm1, norm2));
inter_p = add(l1.s, mul(norm1, t1));
return true;
}
else
{
return false;
}
}
// 直线相交
else
{
if (Cos(line1, line2) == 1.0)
return false;
double t1 = length(multiply(sub(l1.s, l2.s), norm2)) / length(multiply(norm2, norm1));
double t2 = length(multiply(sub(l2.s, l1.s), norm1)) / length(multiply(norm1, norm2));
inter_p = add(l1.s, mul(norm1, t1));
return true;
}
}
int Area2(Point a,Point b,Point c)
{
return a.x*b.y-a.y*b.x+b.x*c.y-b.y*c.x+c.x*a.y-c.y*a.x;
}
bool ToLeft(Point p,Line l)
{
//并未考虑端点就在另一直线上的情况,也就是Area2(l.L,l.R,p)=0,需要的话改一下返回类型和值即可
return Area2(l.L,l.R,p)>0;
}
bool IntersectionJudge(Line a,Line b)
{
if(ToLeft(a.L,b)!=ToLeft(a.R,b)&&ToLeft(b.L,a)!=ToLeft(b.R,a))
return true;
else
return false;
}
//
//
// 参数: l1 : 线段1 l2 : 线段2
//
double angleOfLines(const Line& l1, const Line& l2,bool is_radian)
{
Point line1 = sub(l1.e, l1.s);
Point line2 = sub(l2.e, l2.s);
return Angle(line1, line2, is_radian);
}
//
// 参数: s: 起点 e : 终点 inter_num:插值点数量(不包括起始点)
// 返回值包括起始点
//
vector<Point> firstOrderBezier(const Point& s, const Point& e, int inter_num)
{
vector<Point> res;
res.push_back(s);
for (int i = 1; i <= inter_num; ++i)
{
double a1 = double(i) / double(inter_num + 1);
double a2 = 1.0 - a1;
res.push_back(add(mul(s, a2), mul(e, a1)));
}
res.push_back(e);
return res;
}
//
// 参数: s: 起点 e : 终点 p : 控制点 inter_num:插值点数量(不包括起始点)
// 返回值包括起始点
//
vector<Point> secondOrderBezier(const Point& s, const Point& e, const Point& p, int inter_num)
{
vector<Point> res;
res.push_back(s);
for (int i = 1; i <= inter_num; ++i)
{
double a = double(i) / double(inter_num + 1);
double a1 = pow(a,2);
double a2 = 2 * a * (1.0 - a);
double a3 = pow(1.0 - a, 2);
res.push_back(add(add(mul(s, a3), mul(p, a2)), mul(e, a1)));
}
res.push_back(e);
return res;
}
//
// 参数: s: 起点 e : 终点 p1,p2 : 控制点 inter_num:插值点数量(不包括起始点)
// 返回值包括起始点
//
vector<Point> thirdOrderBezier(const Point& s, const Point& e, const Point& p1, const Point& p2, int inter_num)
{
vector<Point> res;
res.push_back(s);
for (int i = 1; i <= inter_num; ++i)
{
double a = double(i) / double(inter_num + 1);
double a1 = pow(a, 3);
double a2 = 3 * pow(a, 2) * (1.0 - a);
double a3 = 3 * pow(1.0 - a, 2) * a;
double a4 = pow(1.0 - a, 3);
res.push_back(add(add(add(mul(s, a4), mul(p1, a3)), mul(p2, a2)),mul(e,a1)));
}
res.push_back(e);
return res;
}
三阶贝塞尔曲线的可视化以及交互设计可见:https://blog.csdn.net/derbi123123/article/details/105420426
// 不共线的三个点组成一个三角形
//
// 参数: t : 三角形
//
bool isTriangle(const Triangle& t)
{
return isPointsCollinear(t.v0, t.v1, t.v2);
}
算法方法链接: https://www.cnblogs.com/graphics/archive/2010/08/05/1793393.html
//
// 参数: t : 三角形 p : 需要判断的点 u,v分别为用于表示点在两条边上投影系数
//
bool isPointInTriangle(const Triangle& t, const Point& p, double& u, double& v)
{
Point vec1 = sub(t.v1, t.v0);
Point vec2 = sub(t.v2, t.v0);
Point vec_p = sub(p, t.v0);
double dot00 = dotMultiply(vec1, vec1);
double dot01 = dotMultiply(vec1, vec2);
double dot02 = dotMultiply(vec1, vec_p);
double dot11 = dotMultiply(vec2, vec2);
double dot12 = dotMultiply(vec2, vec_p);
double inverDeno = 1 / (dot00 * dot11 - dot01 * dot01);
u = (dot11 * dot02 - dot01 * dot12) * inverDeno;
v = (dot00 * dot12 - dot01 * dot02) * inverDeno;
if (u < 0 || u > 1) return false;
if (v < 0 || v > 1) return false;
if (u + v < 1)return true;
else return false;
}
// 参数: t : 三角形平面
//
Point getUnitNormal(const Triangle& t)
{
Point vec1 = sub(t.v1, t.v0);
Point vec2 = sub(t.v2, t.v0);
return normalize(multiply(vec1, vec2));
}
//
// 参数:t : 三角形 p : 点
//
Point ptotProjection(const Triangle& t, const Point& p)
{
Point vec_p = sub(p, t.v0);
Point unit_normal = getUnitNormal(t);
double ratio = dotMultiply(vec_p, unit_normal);
return sub(p, mul(unit_normal, ratio));
}
//
// 参数: t : 三角形所在的平面 p : 需要判断的点
//
double ptotDistance(const Triangle& t, const Point& p)
{
Point project_p = ptotProjection(t, p);
return distance(p, project_p);
}
具体原理及实现:https://blog.csdn.net/derbi123123/article/details/106360861
//
// 参数: t : 三角形所在平面 l : 直线
//
Point ltotInterPoint(const Triangle& t, const Line& l)
{
Point line_vec = sub(l.e, l.s);
Point point_vec = sub(t.v0, l.s);
Point unit_plane_normal = getUnitNormal(t);
double ratio = dotMultiply(point_vec, unit_plane_normal) / dotMultiply(unit_plane_normal, line_vec);
return add(l.s, mul(line_vec, ratio));
}
//
// 参数: t : 三角形平面
//
double areaOfTriangle(const Triangle& t)
{
return (0.5 * length(multiply(sub(t.v1,t.v0),sub(t.v2,t.v0))));
}
如果没有特别说明,则默认输入多边形顶点按照逆时针排列,点为二维点即z=0
//
// 参数: polygon : 多边形点集合 flags : 标志每个点是否是凸的
//
void checkConvex(const vector<Point>& polygon, vector<bool>& flags)
{
flags.resize(polygon.size());
// 找到第一个凸的顶点
int index = 0;
for (int i = 1; i < polygon.size(); ++i)
{
if (polygon[i].y < polygon[index].y ||(polygon[i].y == polygon[index].y && polygon[i].x < polygon[index].x))
{
index = i;
}
}
/* 判断每个点的凹凸性
* 通过判断前后两个点的向量叉乘判断是否满足逆时针
*/
int size = polygon.size() - 1;
flags[index] = true;
while (size)
{
if (multiply(polygon[index], polygon[(index + 1) % size], polygon[(index + 2) % size]).z >= 0)
{
flags[(index + 1) % size] = true;
}
else
{
flags[(index + 1) % size] = false;
}
index++;
size--;
}
}
//
// 参数 : polygon : 输入的多边形顶点
//
bool isConvex(const vector<Point>& polygon)
{
vector<bool> flags;
// 判断每个点的凹凸性
checkConvex(polygon, flags);
// 如果有一个点不是凸的,则此多边形为非凸
for (auto c : flags)
if (!c)
return false;
return true;
}
法1:看不懂他的方法所以自己写了一个
int Area2(Point a,Point b,Point c)//平行四边形面积
{
return a.x*b.y-a.y*b.x+b.x*c.y-b.y*c.x+c.x*a.y-c.y*a.x;
}
double area(const vector<Point>& polygon) {
double area = 0.0D;
for (int i = 1; i < polygon.size() - 1; i++) {
area +=Area2(polygon[0], polygon[i], polygon[i+1]);
}
return area / 2;
}
法2:我也看不懂
//
// 参数: polygon : 多边形
//
double areaOfPolygon(const vector<Point>& polygon)
{
// 如果多边形点的数量少于三个则非法
int size = polygon.size();
if (size < 3) return 0;
double area(0.0);
for (int i = 0; i < size; ++i)
{
area += polygon[i].y * (polygon[(i - 1 + size) % size].x - polygon[(i + 1) % size].x);
}
return (area / 2);
}
//
// 参数: polygon : 多边形
//
bool isConterClock(const vector<Point>& polygon)
{
return area(polygon) > 0;//行列式的几何性质
}
原理:对于任意多边形内部的点,找一个外部的点与其连成的线段必定与此多边形有奇数个交点
//
// 参数: polygon : 多边形 p : 需要判断的点
//
bool isPointInPolygon(const vector<Point>& polygon, const Point& p)
{
Point down_left, up_right;
boxOfPolygon(polygon, down_left, up_right);//获得左上和右下的点
// 位于多边形外部一点
Point out_p = sub(down_left, Point(10.0, 0.0));
int cnt(0);
Line p_line(p, out_p, true);
for (int i = 0; i < polygon.size(); ++i)
{
Point s = polygon[i];
Point e = polygon[(i + 1) % polygon.size()];
Line seg(s, e, true);
Point inter_p;
if (isSegIntersect(p_line, seg, inter_p))
{
cnt++;
}
}
return (cnt % 2 == 1);
}
// 线段在多边形内部的条件是两个端点都在多边形内且不与多边形相交
//
// 参数: polygon : 多边形 l : 线段
bool isSegInPolygon(const vector<Point>& polygon, const Line& l)
{
// 首先判断线段端点是否都在多边形内部
bool is_s_in = isPointInPolygon(polygon, l.s);
bool is_e_in = isPointInPolygon(polygon, l.e);
// 然后判断线段是否相交
if (is_s_in && is_e_in)
{
for (int i = 0; i < polygon.size(); ++i)
{
Point s = polygon[i];
Point e = polygon[(i + 1) % polygon.size()];
Line seg(s, e, true);
Point inter_p;
if (isSegIntersect(l, seg, inter_p))
{
return false;
}
}
return true;
}else{
return false;
}
}
// 只有多边形所有的边都在圆的外部,圆才处于多边形内部
//
// 参数: polygon : 多边形 c : 圆心 radius : 半径
//
bool isCircleInPolygon(const vector<Point>& polygon, const Point& c, double radius)
{
for (int i = 0; i < polygon.size(); ++i)
{
const Point& p1 = polygon[i];
const Point& p2 = polygon[(i + 1) % polygon.size()];
Line line(p1, p2, true);
if (segToCircle(c, radius, line) != 2)//segToCircle函数看圆的那部分,在5中
return false;
}
return true;
}
算法链接:https://blog.csdn.net/derbi123123/article/details/106376189
// 参数: points : 平面点集
//
vector<Point> findConvexGraham(const vector<Point>& points)
{
vector<Point> result;
// 点的数量必须大于三个
if (points.size() < 3)
return result;
// 寻找最底部的点(LTL)
int index = 0;
for (int i = 0; i < points.size(); ++i)
{
if (polygon[i].y < polygon[index].y ||(polygon[i].y == polygon[index].y && polygon[i].x < polygon[index].x))
{
index = i;
}
}
Point convex_p = points[index];
// 计算每个点的极角
map<double, int> cos_map;
Point x_vec(1.0, 0.0);
for (int i = 0; i < points.size(); ++i)
{
if (i != index)
{
double cos_value = Cos(sub(points[i], convex_p), x_vec);
// 如果有多个点有相同的极角,则取最远的点
if (cos_map.count(-cos_value) != 0)
{
if (length(points[i]) > length(points[cos_map[-cos_value]]))
cos_map[-cos_value] = i;
}
else
cos_map[-cos_value] = i;
}
}
// 保存结果的栈
stack<int> result_stack;
// 存入开始的两个点
result_stack.push(index);
result_stack.push(cos_map.begin()->second);
for (auto iter = (++cos_map.begin()); iter != cos_map.end(); ++iter)
{
int first = result_stack.top();
result_stack.pop();
int second = result_stack.top();
Point vec1 = sub(points[first], points[second]);
Point vec2 = sub(points[iter->second], points[first]);
if (multiply(vec1, vec2).z >= 0)
result_stack.push(first);
result_stack.push(iter->second);
}
// 将数据从栈中读取
while (!result_stack.empty())
{
result.push_back(points[result_stack.top()]);
result_stack.pop();
}
std::reverse(result.begin(), result.end());
return result;
}
//
// 参数: points : 平面点集
//
// 点根据字典序的比较函数
bool cmp(Point a, Point b)
{
if (a.x == b.x)
return a.y < b.y;
return a.x < b.x;
}
vector<Point> findConvex(const vector<Point>& points)
{
vector<Point> result;
if (points.size() < 3)
return result;
vector<Point> tmp_points = points;
// 首先将所有点按照字典序排序
sort(tmp_points.begin(), tmp_points.end(), cmp);
// 上凸包
vector<Point> upper_hull;
// 存入第一个和第二个点
upper_hull.push_back(tmp_points[0]);
upper_hull.push_back(tmp_points[1]);
for (int i = 2; i < tmp_points.size(); ++i)
{
upper_hull.push_back(tmp_points[i]);
while (upper_hull.size() > 2 && multiply(sub(upper_hull[upper_hull.size() - 2], upper_hull[upper_hull.size() - 3]), sub(upper_hull[upper_hull.size() - 1], upper_hull[upper_hull.size() - 3])).z >= 0)
{
upper_hull.erase(upper_hull.end() - 2);
}
}
// 下凸包
vector<Point> lower_hull;
// 存入倒数第一第二个点
lower_hull.push_back(tmp_points[tmp_points.size() - 1]);
lower_hull.push_back(tmp_points[tmp_points.size() - 2]);
for (int i = tmp_points.size() - 3; i >= 0; --i)
{
lower_hull.push_back(tmp_points[i]);
while (lower_hull.size() > 2 && multiply(sub(lower_hull[lower_hull.size() - 2], lower_hull[lower_hull.size() - 3]), sub(lower_hull[lower_hull.size() - 1], lower_hull[lower_hull.size() - 3])).z >= 0)
{
lower_hull.erase(lower_hull.end() - 2);
}
}
// 删除重复点
lower_hull.erase(lower_hull.begin());
lower_hull.erase(lower_hull.end() - 1);
// 合并上下凸包
upper_hull.insert(upper_hull.end(), lower_hull.begin(), lower_hull.end());
result = upper_hull;
return result;
}
有一个解释的很好的链接:https://www.jianshu.com/p/39ef232ad531
//
// 参数: polygon : 简单多边形
//
Point centerOfPolygon(const vector<Point>& polygon)
{
double polygon_area(0.0);
Point center;
Point origin;
for (int i = 0; i < polygon.size(); ++i)
{
Point curr_p = polygon[i];
Point next_p = polygon[(i + 1) % polygon.size()];
double curr_area = (polygon[(i + 1) % polygon.size()].x * polygon[i].y - polygon[(i + 1) % polygon.size()].y * polygon[i].x) / 2.0;
polygon_area += curr_area;
//div(add(curr_p, next_p), 3)是curr_p, next_p和原点origin组成的三角形的重心
center.x += div(add(curr_p, next_p), 3).x*curr_area;
center.y += div(add(curr_p, next_p), 3).y*curr_area;
}
center.x /=polygon_area;
center.y /=polygon_area;
return center;
}
// 定理1: 每个多边形至少有一个凸顶点,
// x坐标最大、最小的点肯定是凸顶点,y坐标最大、最小的点肯定是凸顶点
// 定理2:顶点数>= 4的简单多边形至少有一条对角线
//
// 参数: polygon : 简单多边形
//
Point pointInPolygon(const vector<Point>& polygon)
{
// 凸顶点和索引
int index = 0;
Point convex_p = polygon[0];
// 寻找一个凸顶点
for (int i = 0; i < polygon.size(); ++i)
{
if (polygon[i].y < convex_p.y)
{
index = i;
convex_p = polygon[i];
}
}
// 获取凸顶点前后一个点
int size = polygon.size();
Point pre_p = polygon[(index - 1 + size) % size];
Point next_p = polygon[(index + 1) % size];
Triangle t(convex_p, pre_p, next_p);
double min_d = double(INT_MAX);
bool flag = false;
Point min_p;
for (int i = 0; i < polygon.size(); ++i)
{
if (i == index || i == ((index - 1 + size) % size) || i == ((index + 1) % size))
continue;
flag = true;
if (distance(convex_p, polygon[i]) < min_d)
{
min_p = polygon[i];
min_d = distance(convex_p, polygon[i]);
}
}
// 如何没有顶点在三角形内部,则返回前后点的中点
if (!flag)
{
return div(add(pre_p, next_p), 2);
}
// 返回最近点和凸顶点的中点
return div(add(convex_p, min_p), 2);
}
// 即多边形的最小包围盒,由左下和右上两个点表示
//
// 参数: polygon : 多边形 down_left : 左下点 up_right : 右上点
//
void boxOfPolygon(const vector<Point>& polygon, Point& down_left, Point& up_right)
{
double max_x = double(INT_MIN), min_x = double(INT_MAX);
double max_y = double(INT_MIN), min_y = double(INT_MAX);
for (auto c : polygon)
{
max_x = (c.x > max_x) ? c.x : max_x;
min_x = (c.x < min_x) ? c.x : min_x;
max_y = (c.y > max_y) ? c.y : max_y;
min_y = (c.y < min_y) ? c.y : min_y;
}
down_left = Point(min_x, min_y);
up_right = Point(max_x, max_y);
}
//
// 参数: c: 圆心 radiuns : 圆的半径 p : 判断的点
// 返回值 : 0 : 圆内 1 : 圆上 2: 圆外
//
int pointToCircle(const Point& c, double radius, const Point& p)
{
double ptoc_d = distance(c, p);
if (ptoc_d < radius)
return 0;
else if (ptoc_d == radius)
return 1;
else
return 2;
}
//
// 参数: c: 圆心 radiuns : 圆的半径 l : 判断的直线
// 返回值 : 0 : 相交 1 :相切 2: 相离
//
int lineToCircle(const Point& c, double radius, const Line& l)
{
double ctol_d = ptolDistance(c, l);
if (ctol_d < radius)
return 0;
else if (ctol_d == radius)
return 1;
else
return 2;
}
//
// 参数: c: 圆心 radiuns : 圆的半径 l : 判断的线段
// 返回值 : 0 : 圆内 1 : 与圆相交 2: 圆外
//
int segToCircle(const Point& c, double radius, const Line& l)
{
double ctol_d = ptolDistance(c, l);
if (ctol_d > radius)
return 2;
else if (ctol_d == radius)
return 1;
else
{
Point project_p = ptolProjection(c, l);
if (isponl(project_p, l))
return 1;
else
return 2;
}
}
//
// 参数: c1 : 圆1圆心,r1 圆1半径 c2 : 圆2圆心,r2 圆2半径
// 返回值:0 :内含 1:内切 2:相交 3:外切 4:外离
//
int circleToCircle(const Point& c1, double r1, const Point& c2, double r2)
{
double ctoc_d = distance(c1, c2);
if (ctoc_d < abs(r1 - r2))
return 0;
else if (ctoc_d == abs(r1 - r2))
return 1;
else if (ctoc_d > abs(r1 - r2) && ctoc_d < (r1 + r2))
return 2;
else if (ctoc_d == (r1 + r2))
return 3;
else if (ctoc_d > (r1 + r2))
return 4;
}