首先我們要理解什麼是向量,向量就是有大小和方向的量。在平面座標系中,向量用x,y表示。等於向量起點到終點的位移。以下是它們的常用定義:
- struct Point
- {
- double x,y;
- Point(double x=0,double y=0):x(x),y(y){}//構造函數
- };
- typedef Point Vector;
- //向量+向量=向量,點+向量=點
- Point operator+(Point A,Point B)
- {
- return Point(A.x+B.x,A.y+B.y);
- }
- //點-點=向量
- Point operator-(Point A,Point B)
- {
- return Point(A.x-B.x,A.y-B.y);
- }
- //向量*數=向量
- Point operator*(Point A,double p)
- {
- return Point(A.x*p,A.y*p);
- }
- //向量/數=向量
- Point operator/(Point A,double p)
- {
- return Point(A.x/p,A.y/p);
- }
- double eps=1e-10;
- //如果等於0,返回0,小於0返回-1,大於0,返回1
- int dcmp(double x)
- {
- if(fabs(x)<eps)return 0;else return x<0?-1:1;
- }
- //判斷是否相等
- bool operator==(const Point& a,const Point &b)
- {
- return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
- }
- int main()
- {
- Point a,b,c;
- double p=2;
- a.x=4;
- a.y=3;
- b.x=2;
- b.y=5;
- c=a+b;//向量+向量=向量
- c=a-b;//點-點=向量
- c=a/p;//向量/數=向量
- c=a*p;//向量*數=向量
- return 0;
- }
struct Point
{
double x,y;
Point(double x=0,double y=0):x(x),y(y){}//構造函數
};
typedef Point Vector;
//向量+向量=向量,點+向量=點
Point operator+(Point A,Point B)
{
return Point(A.x+B.x,A.y+B.y);
}
//點-點=向量
Point operator-(Point A,Point B)
{
return Point(A.x-B.x,A.y-B.y);
}
//向量*數=向量
Point operator*(Point A,double p)
{
return Point(A.x*p,A.y*p);
}
//向量/數=向量
Point operator/(Point A,double p)
{
return Point(A.x/p,A.y/p);
}
double eps=1e-10;
//如果等於0,返回0,小於0返回-1,大於0,返回1
int dcmp(double x)
{
if(fabs(x)<eps)return 0;else return x<0?-1:1;
}
//判斷是否相等
bool operator==(const Point& a,const Point &b)
{
return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}
int main()
{
Point a,b,c;
double p=2;
a.x=4;
a.y=3;
b.x=2;
b.y=5;
c=a+b;//向量+向量=向量
c=a-b;//點-點=向量
c=a/p;//向量/數=向量
c=a*p;//向量*數=向量
return 0;
}
基本運算
點積是兩個向量v和w的點積等於二者長度的乘積在乘上它們夾角的餘弦。如果兩向量垂直,點積就等於0。兩個向量OA和OB的點積等於xa*xb+ya*yb。下面是點積計算方法,以及用點積計算向量長度和夾角的函數。
- //點積計算方法
- double Dot(Point A,Point B)
- {
- return A.x*B.x+A.y*B.y;
- }
- //點到原點的長度
- double Lenth(Point A)
- {
- return sqrt(Dot(A,A));
- }
- //角度計算
- double Angle(Point A,Point B)
- {
- return acos(Dot(A,B)/Lenth(A)/Lenth(B));
- }
//點積計算方法
double Dot(Point A,Point B)
{
return A.x*B.x+A.y*B.y;
}
//點到原點的長度
double Lenth(Point A)
{
return sqrt(Dot(A,A));
}
//角度計算
double Angle(Point A,Point B)
{
return acos(Dot(A,B)/Lenth(A)/Lenth(B));
}
叉積就是兩個向量v和w組成的三角形的有向面積的兩倍。叉積的計算方法及三角形面積的兩倍的計算方法如下:
- //就算OA和OB的叉積
- double Cross(Point A,Point B)
- {
- return A.x*B.y-A.y*B.x;
- }
- //計算三角形的面積的兩倍
- double Area(Point A,Point B,Point C)
- {
- return Cross(B-A,C-A);
- }
//就算OA和OB的叉積
double Cross(Point A,Point B)
{
return A.x*B.y-A.y*B.x;
}
//計算三角形的面積的兩倍
double Area(Point A,Point B,Point C)
{
return Cross(B-A,C-A);
}
向量的旋轉。向量可以繞起點旋轉,公式爲x'=x*cosa-y*sina,y'=x*sina+y*cosa。其中a爲逆時針旋轉的角。代碼如下:
- //向量的旋轉
- Point Rotate(Point A,double rad)//rad是弧度
- {
- return Point(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));
- }
//向量的旋轉
Point Rotate(Point A,double rad)//rad是弧度
{
return Point(A.x*cos(rad)-A.y*sin(rad),A.x*sin(rad)+A.y*cos(rad));
}
下面的函數計算向量的單位法線,即左轉90度以後把長度歸一化。
- //計算向量單位法線
- Point Normal(Point A)
- {
- double l=Lenth(A);
- return Point(-A.y/l,A.x/l);
- }
//計算向量單位法線
Point Normal(Point A)
{
double l=Lenth(A);
return Point(-A.y/l,A.x/l);
}
點和直線
直線上一點可以用直線上一點p0和方向向量v來表示。直線上所有點p=p0+t*v.其中t爲參數。如果已知直線上的兩個不同點A和B,則方向向量爲B-A,所以參數方程A+(B-A)*t。對於直線t沒有限制,射線t>0,線段0<t<1。
直線交點。設直線分別爲P+tv和Q+tw,設向量u=P-Q,交點在第一條直線的參數爲t1,第二條直線的參數爲t2,則x和y座標可以列出一個方程,解得:
t1=cross(w,u)/cross(v,w),t2=cross(v,u)/cross(v,w)。
代碼如下:
- //直線交點公式
- Point GetLineIntersection(Point P,Point V,Point Q,Point W)
- {
- Point u=P-Q;
- double t=Cross(W,u)/Cross(V,W);
- return P+V*t;
- }
//直線交點公式
Point GetLineIntersection(Point P,Point V,Point Q,Point W)
{
Point u=P-Q;
double t=Cross(W,u)/Cross(V,W);
return P+V*t;
}
點到直線的距離。點到直線的距離是一個常用函數,可以用叉積算出,即用平行四邊形的面積除以底,代碼如下:
- //點到直線的距離
- double DistanceToLine(Point P,Point A,Point B)
- {
- Point v1=B-A,v2=P-A;
- return fabs(Cross(v1,v2))/Lenth(v1);
- }
//點到直線的距離
double DistanceToLine(Point P,Point A,Point B)
{
Point v1=B-A,v2=P-A;
return fabs(Cross(v1,v2))/Lenth(v1);
}
點到線段的距離有兩種情況,代碼如下:
- //點到線段的距離
- double DistanceToSegment(Point P,Point A,Point B)
- {
- if(A==B) return Lenth((P-A));
- Point v1=B-A,v2=P-A,v3=P-B;
- if(dcmp(Dot(v1,v2))<0)return Lenth(v2);
- else if(dcmp(Dot(v1,v3))>0)return Lenth((v3));
- else return fabs(Cross(v1,v2))/Lenth(v1);
- }
//點到線段的距離
double DistanceToSegment(Point P,Point A,Point B)
{
if(A==B) return Lenth((P-A));
Point v1=B-A,v2=P-A,v3=P-B;
if(dcmp(Dot(v1,v2))<0)return Lenth(v2);
else if(dcmp(Dot(v1,v3))>0)return Lenth((v3));
else return fabs(Cross(v1,v2))/Lenth(v1);
}
點在直線上的投影,代碼如下:
- //點P在直線AB上的投影
- Point GetLineProjecton(Point P,Point A,Point B)
- {
- Point v=B-A;
- return A+v*(Dot(v,P-A)/Dot(v,v));
- }
//點P在直線AB上的投影
Point GetLineProjecton(Point P,Point A,Point B)
{
Point v=B-A;
return A+v*(Dot(v,P-A)/Dot(v,v));
}
線段相交判定。給定兩條線段,判斷是否相交。我們定義“規範相交”爲兩線段恰好有一個公共點,且不在任何一條線段的端點。線段規範相交的充要條件是:每條線段的兩個端點都在另一條線段的兩側。代碼如下:
- //判斷線段相交(不含端點)
- bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2)
- {
- double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1),
- c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1);
- return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
- }
//判斷線段相交(不含端點)
bool SegmentProperIntersection(Point a1,Point a2,Point b1,Point b2)
{
double c1=Cross(a2-a1,b1-a1),c2=Cross(a2-a1,b2-a1),
c3=Cross(b2-b1,a1-b1),c4=Cross(b2-b1,a2-b1);
return dcmp(c1)*dcmp(c2)<0&&dcmp(c3)*dcmp(c4)<0;
}
如果允許在端點出相交,情況就比較複雜了,有可能共線,還有可能某個端點在另外一條線段上。爲了判斷上述情況是否發生,還需要如下一段判斷一個點是否在一條線段上(不含端點)的代碼:
- //判斷一個點P是否在一條線段a1a2上
- bool OnSegment(Point p,Point a1,Point a2)
- {
- return dcmp(Cross(a1-p,a2-p))==0&&dcmp(Dot(a1-p,a2-p));
- }
//判斷一個點P是否在一條線段a1a2上
bool OnSegment(Point p,Point a1,Point a2)
{
return dcmp(Cross(a1-p,a2-p))==0&&dcmp(Dot(a1-p,a2-p));
}
多邊形
計算多邊形的面積。如果多變形是凸的,可以從第一個頂點出發把凸多邊形分成n-2個三角形,然後把面積加起來。代碼如下:
- //計算多邊形的面積
- double ConvexPolygonArea(Point* p,int n)
- {
- double area=0;
- for(int i=1;i<n-1;i++)
- area+=Cross(p[i]-p[0],p[i+1]-p[0]);
- return area/2;
- }
//計算多邊形的面積
double ConvexPolygonArea(Point* p,int n)
{
double area=0;
for(int i=1;i<n-1;i++)
area+=Cross(p[i]-p[0],p[i+1]-p[0]);
return area/2;
}
可以另取p[0]點爲劃分頂點,一方面可以少算兩個叉積,另一方面也減少乘法溢出的可能性,還不用特殊處理。代碼如下:
- double PolygonArea(Point* p,int n)//p是一個結構體數組
- {
- double area=0;
- for(int i=1;i<n-1;i++)
- area+=Cross(p[i]-p[0],p[i+1]-p[0]);
- return area/2;
- }
- int main()
- {
- Point a[4];
- a[0].x=0;
- a[0].y=0;
- a[1].x=3;
- a[1].y=0;
- a[2].x=3;
- a[2].y=3;
- a[3].x=0;
- a[3].y=3;
- double area=PolygonArea(a,4);
- printf("%lf",area);
- return 0;
- }