计算几何

计算几何

基础两点:

  • 实数的输入和输出。计算几何一般都是实数,编程时用double型
  • 实数的精度判断。两个函数:sgn 和 dcmp
const double pi = acos(-1.0);
const double eps = 1e-8;

int sgn(double x){//判断x的大小
	if(fabs(x)<eps) return 0;
	else return x<0?-1:1;
}

// int dcmp(double x, double y){//比较两个浮点数
// 	if(fabs(x-y)<eps) return 0;
// 	else return x<y?-1:1;
// }

int dcmp(double x, double y){//比较两个浮点数
	return sgn(x - y);
}

二维几何

完整板子



点和向量

  • 集合
//>>>>点和向量
struct Point{
	double x,y;
	Point(){}
	Point(double x, double y) : x(x),y(y){}
	Point operator + (Point B){return Point(x + B.x, y + B.y);}
	Point operator - (Point B){return Point(x - B.x, y - B.y);}
	Point operator * (double k){return Point(x * k, y * k);}
	Point operator / (double k){return Point(x / k, y / k);}
	bool operator == (Point B){return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; }
};
//两点之间的距离
double Distance(Point A,Point B){ return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y)); }

typedef Point Vector;//命名向量


  • 二维平面中的点,用坐标(x,y)来表示
struct Point{
	double x, y;
	Point(){}
	Point(double x, double y):x(x), y(y){}
};
  • 两点之间的距离
    勾股定理,三角形斜边
double Distance(Point A, Point B){ return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y)); }
  • 向量
typedef Point Vector;
  • 向量的运算
    在 Point 中重载+ - * / ==
Point operator + (Point B){return Point(x+B.x,y+B.y);}
Point operator - (Point B){return Point(x-B.x,y-B.y);}
Point operator * (double k){return Point(x*k,y*k);}
Point operator / (double k){return Point(x/k,y/k);}
bool operator == (Point B){return sgn(x-B.x)==0 && sgn(y-B.y)==0;}

点积和叉积

  • 集合
//>>>>点积和叉积
double Dot(Vector A, Vector B){ return A.x * B.x + A.y * B.y; }//点积
double Len(Vector A){return sqrt(Dot(A, A));}//求向量长度
double Len2(Vector A){return Dot(A, A);}//求向量长度的平方,避免精度问题
double Angle(Vector A, Vector B){return acos(Dot(A, B) / Len(A) / Len(B));}//求AB角度

double Cross(Vector A, Vector B){return A.x * B.y - A.y * B.x;}//叉积
double Area2(Point A, Point B, Point C){ return Cross(B - A, C - A);}//四边形面积
double Area(Point A, Point B, Point C){ return Area2(A, B, C) / 2.0;}//三角形面积
bool Parallel(Vector A, Vector B){return sgn(Cross(A, B)) == 0;}  //返回true表示平行或重合

  • 点积 Dot Product
    $A·B = |A| |B| \cos\theta = A.x \times B.x + A.y \times B.y $
double Dot(Vector A,Vector B){ return A.x*B.x + A.y*B.y; }
  • 点积的应用
  1. 判向量 A 与 B 的角度
    \(Dot(A,B) > 0\),A 与 B 的夹角为锐角
    \(Dot(A,B) = 0\),A 与 B 的夹角为直角
    \(Dot(A,B) < 0\),A 与 B 的夹角为钝角
  2. 求向量长度 \(|A| = Dot(A,A)\)
//求长度
double Len(Vector A){return sqrt(Dot(A,A));}
//求长度的平方,避免精度问题
double Len2(Vector A){return Dot(A,A);}
  1. 求向量 A 与 B 的夹角大小
    $\theta = \arccos( \frac{AB}{|A||B|} ) $
double Angle(Vector A,Vector B){return acos(Dot(A,B)/Len(A)/Len(B));}
  • 叉积 Cross Product
    $A·B = |A| |B| \sin\theta = A.x \times B.y - A.y \times B.x $
double Cross(Vector A,Vector B){return A.x*B.y – A.y*B.x;}

A×B与B×A相反

  • 叉积的应用
  1. 判断向量 A、B 的方向关系
    若 $A\times B > 0 $,则 B 在 A 的顺时针方向
    若 $A\times B = 0 $,则 A 与 B 共线
    若 $A\times B < 0 $,则 B 在 A 的逆时针方向
  2. 计算两向量构成的平行四边形有向面积
    三个点A、B、C,以A为公共点,得到2个向量B-A和C-A,它们构成的平行四边形,面积是:
double Area2(Point A,Point B,Point C){ return Cross(B-A, C-A);}
  1. 三角形面积为上一方法面积的一半
  2. 向量旋转(待确定)
Vector Rotate(Vector A, double rad){
	return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}
  1. 用叉积检查两个向量是否平行或重合
bool Parallel(Vector A, Vector B){return sgn(Cross(A,B)) == 0;}  //返回true表示平行或重合

点和线

  • 集合
//>>>>直线的表示
struct Line{
	Point p1, p2;							//(1)线上的两个点
	Line(){}
	Line(Point p1, Point p2):p1(p1),p2(p2){}
	Line(Point p, double angle){			//(2)根据一个点和倾斜角 angle 确定直线,0<=angle<pi
		p1 = p;
		if(sgn(angle - pi / 2) == 0){p2 = (p1 + Point(0, 1));}
		else{p2 = (p1 + Point(1, tan(angle)));}
	}
	Line(double a, double b, double c){     //(3)ax+by+c=0
		if(sgn(a) == 0){
			p1 = Point(0, -c / b);
			p2 = Point(1, -c / b);
		}
		else if(sgn(b) == 0){
			p1 = Point(-c / a, 0);
			p2 = Point(-c / a, 1);
		}
		else{
			p1 = Point(0, -c / b);
			p2 = Point(1, (-c - a) / b);
		}
	}
};

typedef Line Segment;//命名线段
//点和直线的位置关系
int Point_line_relation(Point p, Line v){
	int c = sgn(Cross(p - v.p1, v.p2 - v.p1));
	if(c < 0)return 1;              //1:p在v的左边
	if(c > 0)return 2;              //2:p在v的右边
	return 0;                       //0:p在v上
}
//点和线段的位置关系
bool Point_on_seg(Point p, Line v){ //点和线段:0 点不在线段v上;1 点在线段v上
	return sgn(Cross(p - v.p1, v.p2 - v.p1)) == 0 && sgn(Dot(p - v.p1, p - v.p2)) <= 0;
}
//点在直线上的投影
Point Point_line_proj(Point p, Line v){
	double k = Dot(v.p2 - v.p1, p - v.p1) / Len2(v.p2 - v.p1);
	return v.p1 + (v.p2 - v.p1) * k;
}
//点关于直线的对称点
Point Point_line_symmetry(Point p, Line v){
	Point q = Point_line_proj(p,v);
	return Point(2*q.x-p.x,2*q.y-p.y);
}
//点到直线的距离
double Dis_point_line(Point p, Line v){
	return fabs(Cross(p - v.p1, v.p2 - v.p1)) / Distance(v.p1, v.p2);
}
//点到线段的距离
double Dis_point_seg(Point p, Segment v){
	if(sgn(Dot(p - v.p1, v.p2 - v.p1)) < 0 || sgn(Dot(p - v.p2, v.p1 - v.p2)) < 0)
	return min(Distance(p, v.p1), Distance(p, v.p2));
	return Dis_point_line(p, v);           //点的投影在线段上
}
//两条直线的位置关系
int Line_relation(Line v1, Line v2){
	if(sgn(Cross(v1.p2 - v1.p1, v2.p2 - v2.p1)) == 0){
		if(Point_line_relation(v1.p1,v2) == 0) return 1;  //1 重合
		else return 0;                                  //0 平行
	}
	return 2;                                           //2 相交
}
//两条直线的交点
Point Cross_point(Point a,Point b,Point c,Point d){  //Line1:ab,  Line2:cd
	double s1 = Cross(b - a, c - a);
	double s2 = Cross(b - a, d - a);                    //叉积有正负
	return Point(c.x * s2 - d.x * s1, c.y * s2 - d.y * s1) / (s2 - s1);
}
//判断两个线段是否相交
bool Cross_segment(Point a,Point b,Point c,Point d){    //Line1:ab,  Line2:cd
	double c1 = Cross(b - a, c - a), c2 = Cross(b - a, d - a);
	double d1 = Cross(d - c, a - c), d2 = Cross(d - c, b - c);
	return sgn(c1) * sgn(c2) < 0 && sgn(d1) * sgn(d2) < 0;  //1相交;0不相交
}

  • 直线的表示
struct Line{
	Point p1,p2;                  //(1)线上的两个点
	Line(){}
	Line(Point p1,Point p2):p1(p1),p2(p2){}
	Line(Point p,double angle){    //(2)根据一个点和倾斜角 angle 确定直线,0<=angle<pi
		p1 = p;
		if(sgn(angle – pi/2) == 0){p2 = (p1 + Point(0,1));}
		else{p2 = (p1 + Point(1,tan(angle)));}
	}
	Line(double a,double b,double c){     //(3)ax+by+c=0
		if(sgn(a) == 0){
			p1 = Point(0,-c/b);
			p2 = Point(1,-c/b);
		}
		else if(sgn(b) == 0){
			p1 = Point(-c/a,0);
			p2 = Point(-c/a,1);
		}
		else{
			p1 = Point(0,-c/b);
			p2 = Point(1,(-c-a)/b);
		}
	}
};

  • 线段的表示
    一个点为起点,一个点为终点
typedef Line Segment;
  • 点和直线的位置关系
    三种关系:点在直线左侧,点在直线上,点在直线右侧
    用直线上两点和点 P 构成两个向量(同一起点),再利用叉积正负判断方向
int Point_line_relation(Point p, Line v){
	int c = sgn(Cross(p-v.p1,v.p2-v.p1));
	if(c < 0)return 1;              //1:p在v的左边
	if(c > 0)return 2;              //2:p在v的右边
	return 0;                       //0:p在v上
}
  • 点和线段的位置关系
    点在不在线段上:先用叉积判断是否共线,再利用点积判断点 P 与线段上两点构成的角是否为钝角(其实应该为\(180^0\)
bool Point_on_seg(Point p, Line v){ //点和线段:0 点不在线段v上;1 点在线段v上
	return sgn(Cross(p-v.p1, v.p2-v.p1)) == 0 && sgn(Dot(p – v.p1,p- v.p2)) <= 0;
}
  • 点到直线的距离
    先利用叉积算$p, p_1, p_2 $构成的平行四边形的面积,然后再用面积除以平行四边形的底边长,也就是线段 \(p_1p_2\) 的长度,就得到了高,即为点到直线的距离
double Dis_point_line(Point p, Line v){
	return fabs(Cross(p-v.p1,v.p2-v.p1))/Distance(v.p1,v.p2);
}
  • 点到线段的距离
    求点到线段的距离,在以下3个距离中取最小值:p 到 A 的距离;p 到 B 的距离;p 向 AB 作垂线,如果交点在线段上,那么这个距离就是最小值。
double Dis_point_seg(Point p, Segment v){
	if(sgn(Dot(p- v.p1,v.p2-v.p1))<0 || sgn(Dot(p- v.p2,v.p1-v.p2))<0)
	return min(Distance(p,v.p1),Distance(p,v.p2));
	return Dis_point_line(p,v);           //点的投影在线段上
}
  • 点在直线上的投影(原理待补充,利用点积)
    即为垂线段的交点
Point Point_line_proj(Point p, Line v){
	double k = Dot(v.p2-v.p1,p-v.p1)/Len2(v.p2-v.p1);
	return v.p1+(v.p2-v.p1)*k;
}
  • 点关于直线的对称点
    先求投影,再求对称点
Point Point_line_symmetry(Point p, Line v){
	Point q = Point_line_proj(p,v);
	return Point(2*q.x-p.x,2*q.y-p.y);
}
  • 两条直线的位置关系
int Line_relation(Line v1, Line v2){
	if(sgn(Cross(v1.p2-v1.p1,v2.p2-v2.p1)) == 0){
		if(Point_line_relation(v1.p1,v2)==0) return 1;  //1 重合
		else return 0;                                  //0 平行
	}
	return 2;                                           //2 相交
}
  • 两条直线的交点
Point Cross_point(Point a,Point b,Point c,Point d){  //Line1:ab,  Line2:cd
	double s1 = Cross(b-a,c-a);
	double s2 = Cross(b-a,d-a);                    //叉积有正负
	return Point(c.x*s2-d.x*s1,c.y*s2-d.y*s1)/(s2-s1);
}
  • 判断两个线段是否相交
bool Cross_segment(Point a,Point b,Point c,Point d){    //Line1:ab,  Line2:cd
	double c1 = Cross(b-a,c-a),c2=Cross(b-a,d-a);
	double d1 = Cross(d-c,a-c),d2=Cross(d-c,b-c);
	return sgn(c1)*sgn(c2) < 0 && sgn(d1)*sgn(d2) < 0;  //1相交;0不相交
}
  • 两条线段的交点
    先判断两线段是否相交,若相交,则问题转化为直线相交

多边形

  • 集合

三维几何

例题

二维几何

posted on 2023-07-13 20:53  Qiansui  阅读(12)  评论(0编辑  收藏  举报