二维计算几何点线

废话不多说,直接入正题。

误差

	const db eps=1e-9;
	inline int sgn(T x){
		return x<-eps?-1:x<eps?0:1;
	}

我们浮点数计算计算几何的时候是存在误差的,我们用上面这个函数来修正。

对于 x<eps ,我们认为它属于负数,返回 1.

对于 epsx<eps ,我们认为它属于是零,返回 0

剩下的属于正数,返回 1.

这样我们先把比较运算写出来,然后移项成减法形式,再在外面套一层浮点数修正函数即可。

这个养成好习惯即可。

点(point)

我们已经非常熟悉的东西,由横纵坐标来组成。当然也有更高维的点,但是在这里我们暂且不提,只考虑二维的点。(不过高维的点也并不复杂,几维就是几个坐标)

我们这样存储:

struct node{
	db x,y;
	node(){}
	node(int _x,int _y){
		x=_x;y=_y;
	}
};
//这个db 是我 double 的宏定义,下同

向量(vector)

简言就是有方向的量。

我们可以认为向量是一条有向线段,但是它的其中一个点在坐标原点上,因此它既可以用来表示长度,还可以用来表示方向。

可以这么存储:

struct vect{
	db x,y;
	vect(){}
	vect(int _x,int _y){
		x=_x;y=_y;
	}
};

和物理里面的矢量差不多,好像就只有名字不一样。

所以显然,向量也是满足平行四边形法则的。具体来说就是对于两个向量,它们的和向量是满足其为两向量围成的平行四边形的对角线的。符号表示出来就是:

P1(x1,y1)+P2(x2,y2)=Psum(x1+x2,y1+y2)

同理,有:

P1(x1,y1)P2(x2,y2)=Psum(x1x2,y1y2)

显然这种运算满足加法交换律,减法的结合律等。

那么我们可以为向量添加一些重载运算符。

	inline vect operator + (const vect &a){
		return (vect){x+a.x,y+a.y};
	}
	inline vect operator - (const vect &a){
		return (vect){x-a.x,y-a.y};
	}

加减介绍完了,现在介绍一下它的特殊运算。

点积(dot product)

先提出规定: A,B 表示向量 A,B 之间的夹角。

对于向量 A(x1,y1),B(x2,y2)

dot(A,B)=AB=x1x2+y1y2=|A||B|cosA,B

那么这个玩意有什么用呢?你发现这个东西的符号和 cos<A,B> 是同号的,所以可以判断两向量夹角是第几象限角。

如果点积为正,则夹角在第一、四象限

如果点积为负,那么在第二、三象限

浅显一点,可以用来判断夹角是钝角还是锐角还是直角。但是首先保证角度 α[0,π]

如果点积为正,则夹角是锐角或者零角

如果点积为负,那么是钝角或平角

如果点积为 0,那么就是直接了

叉积(cross product)

cross(A,B)=A×B=x1y2x2y1=|A||B|sinA,B

如果叉积为正,则夹角在第一、二象限

如果叉积为负,那么在第三、四象限

显然满足 P×Q=(Q×P)P×(Q)=(P×Q)

我们还有一个性质:

P×Q>0 时,P 位于 Q 的顺时针方向

P×Q=0 时,PQ 共线,同向或反向

P×Q<0 时,P 位于 Q 的逆时针方向

然后我们发现根本不需要单独的一个点,向量可以完全代替它。为了方便,我们鸠占鹊巢,给向量的结构体改名为 point.

现在一个较为完整的向量结构体成型了。

struct point{
    db x,y;
    point(){}
    point(db _x,db _y){
        x=_x;y=_y;
    }
    inline point operator + (const point &a){
        return {x+a.x,y+a.y};
    }
    inline point operator - (const point &a){
        return {x-a.x,y-a.y};
    }
    inline bool operator == (const point &a){
        return sgn(x-a.x)==0 and sgn(y-a.y)==0;
    }
	inline db operator * (const point &a){
		return x*a.y-y*a.x;
	}
};
inline db dot(const point &a,const point &b){
    return a.x*b.x+a.y*b.y;
}

看起来挺不错的。

线(line)

对于线段,我们可以用两个向量(其端点)表示

对于直线和射线,我们考虑一个向量表示基础点,另外一个向量表示方向。但是直线不考虑正反,射线需要考虑。

然后我们判断两条线段是否相交。下面我们介绍一种方法。

设这两条线段为 P1P2Q1Q2

首先我们进行快速排斥实验,如果 P1P2 对角线形成的矩形与 Q1Q2 对角线形成的矩形没有交点,那么直接可以判断线段无交。这个很好判断,用矩形的左下角和右上角的点来判断即可。

显然如果两线段相交,会满足他们互相跨立对方。而如果 P1P2 跨立 Q1Q2 那么,肯定有 (P1Q1)(P2Q1) 都在 (Q1Q2) 两侧,即 [(P1Q1)×(Q1Q2)]×[(P2Q1)×(Q1Q2)]<0.等于 0 的情况我们考虑一下,发现:在经过的快速排斥实验的情况下,P1P2 的端点肯定在 Q1Q2 上,所以有交点。

然后再同理判断 Q1Q2 跨立 P1P2 即可。然后可以判断两者相交。

好,这是点线相关的一些计算几何,就谈到这里。

posted @   cbdsopa  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示