二维计算几何基础知识
1.点积:
向量点积的定义:(x1,y1)*(x2,y2)=x1*x2+y1*y2;满足交换律
向量点积的代数意义:向量V1的模 * 向量V2的模 * cos<V1,V2>
向量点积的几何意义:α在β的投影α’与β的长度乘积
2.叉积
向量叉积定义:(x1,y1) x (x2,y2) =x1*y2 - x2*y1;满足反交换律;
向量叉积几何意义:有向面积
叉积大小为两向量围成的平行四边形的有向面积
结论:flag=a x b
- flag>0,a在b的顺时针方向(180度范围内)
- flag<0,a在b的逆时针方向(180度范围内)
- flag=0,a与b同向或反向
3.两线段判交
在平面上两直线,有平行、重合、相交三种关系
若两条线段所在的直线平行,则两条线段平行
两条线段p1p2和Q1Q2相交的必要条件:(P2-P1) x (Q1-P1) * (P2-P1) x (Q2-P1) <= 0
还有这两种情况:
在左图中只满足了一个“跨立”条件,即Q1和Q2在直线P1P2的两侧,于是我们应该做两次跨立实验才能保证它们是相交的;
而右图则是边界条件的特殊情况,不方便用跨立实验来判断;
不过通过观察两张图后,我们发现:当两线段不相交时,以这两条线段为对角线的格点矩形没有交集!
于是我们可以借助这一点来增加判交条件以保证算法的正确性,我们称之为快速排斥实验,如下图:
线段的规范相交和非规范相交
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <string.h>
#include <math.h>
using namespace std;
const double eps = 1e-8;
//三态函数:减少浮点数误差
int sgn(double x)
{
if(fabs(x) < eps)return 0;
if(x < 0)return -1;
else return 1;
}
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y)
{
x = _x;y = _y;
}
Point operator -(const Point &b)const
{
return Point(x - b.x,y - b.y);
}
//叉积
double operator ^(const Point &b)const
{
return x*b.y - y*b.x;
}
//点积
double operator *(const Point &b)const
{
return x*b.x + y*b.y;
}
};
struct Line
{
Point s,e;
Line(){}
Line(Point _s,Point _e)
{
s = _s;e = _e;
}
//两直线相交求交点
//第一个值为0表示直线重合,为1表示平行,为0表示相交,为2是相交
//只有第一个值为2时,交点才有意义
pair<int,Point> operator &(const Line &b)const
{
Point res = s;
if(sgn((s-e)^(b.s-b.e)) == 0)
{
if(sgn((s-b.e)^(b.s-b.e)) == 0)
return make_pair(0,res);//重合
else return make_pair(1,res);//平行
}
double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));
res.x += (e.x-s.x)*t;
res.y += (e.y-s.y)*t;
return make_pair(2,res);
}
};
//判断线段相交
bool inter(Line l1,Line l2)
{
return
//快速排斥实验
max(l1.s.x,l1.e.x) >= min(l2.s.x,l2.e.x) &&
max(l2.s.x,l2.e.x) >= min(l1.s.x,l1.e.x) &&
max(l1.s.y,l1.e.y) >= min(l2.s.y,l2.e.y) &&
max(l2.s.y,l2.e.y) >= min(l1.s.y,l1.e.y) &&
//跨立实验
sgn((l2.s-l1.e)^(l1.s-l1.e))*sgn((l2.e-l1.e)^(l1.s-l1.e)) <= 0 &&
sgn((l1.s-l2.e)^(l2.s-l2.e))*sgn((l1.e-l2.e)^(l2.s-l2.e)) <= 0;
}
//向量的模
typedef Point Vector;
double Length(Vector A){
return sqrt(A*A);
}
//求向量A和向量B的夹角,弧度制
double Angle(Vector A,Vector B)
{
return acos((A*B)/Length(A)/Length(B));
}
利用叉积计算点到直线距离
//利用叉积计算:点到直线距离
double DisLine(Line l,Point p)
{
Vector v1=p-l.s,v2=l.s-l.e;
return fabs(v1*v2)/Length(v2);
//平行四边形的面积除以底边即平行四边形的高
}
点到线段的距离
点到线段距离的计算根据点与直线的位置分为两大类 (第二类分为两小类)
1,如左图所示,如果点与线段的垂直线与线段所在直线 的交点在线段上,所求的距离就是点到线段的距离
2,如右图所示,如果是在射线上,就是点到射线一端的 距离,图中点到线段的距离就是P到A的距离
double DisSegment(Point P,Point A,Point B)
{
if(sgn(A.x-B.x)==0&&sgn(A.y-B.y)==0)//两点重合
return Length(A-P);//两点之间的距离
Vector v1=B-A,v2=P-A,v3=P-B;
//p点投影到射线
if(sgn(v1*v2)<0) return Length(v2);
if(sgn(v1*v3)>0) return Length(v3);
//p点投影到线段
return fabs(v1*v2)/Length(v2);//点到直线距离
}