计算几何的直线、线段的相交判断

矢量

      如果一条线段的端点是有次序之分的话,那么这种线段就称为 有向线段,如果有向线段p1p2的起点p1在坐标的原点,则可以把它称为矢量 p2

矢量的加减

      设二维矢量 P = (x1, y1), Q = (x2, y2),则 P + Q = (x1 + x2, y1 + y2), P - Q = (x1 - x2, y1 - y2),且有 P + Q = Q + P, P - Q = -(Q - P)

矢量叉积

      设矢量 P = (x1, y1), Q = (x2, y2),则 P * Q = x1 * y2 - x2 * y1; 其结果是一个由 (0, 0), P, Q, P + Q 所组成的平行四边形的 带符号的面积,P * Q = -(Q * P), P * (- Q) = -(P * Q)

      叉积的一个非常重要的性质是可以通过它的符号来判断两矢量相互之间的顺逆时针关系:

            若 P * Q > 0,则 P 在 Q 的顺时针方向

            若 P * Q < 0, 则 P 在 Q 的逆时针方向

            若 P * Q = 0,则 P 与 Q 共线,但不确定 P, Q 的方向是否相同

折线段的拐向判断

      如图,假设有折线段 p0p1p2 ,要确定 p1p2 相对于 p0p1 是向左拐还是向右拐,可以通过计算(p2 - p0)*(p1 - p0) 的符号来确定折线的拐向(点 p2 - p0 实际上就是向量 p2,但这里要注意线段和矢量的区别)


判断点是否在线段上

      设点 Q = (x, y), P1 = (x1, y1), P2 = (x2, y2),若 (Q - P1)*(P2 - P1) = 0 且 min(x1, x2) <= x <= max(x1, x2) && min(y1, y2) <= y <= max(y1, y2),则点 Q 在线段 P1P2 上

判断两线段是否相交
1)快速排斥试验

      设以线段 P1P2 为对角线的矩形为 R,设以线段 Q1Q2 为对角线的矩形为 T,若 R、T 不相交,则两线段不可能相交

      假设 P1 = (x1, y1), P2 = (x2, y2), Q1 = (x3, y3), Q2 = (x4, y4),设矩形 R 的 x 坐标的最小边界为 minRX = min(x1, x2),以此类推,将矩形表示为 R = (minRX, minRY, maxRX, maxRY) 的形式,若两矩形相交,则相交的部分构成了一个新的矩形 F,如图所示,我们可以知道 F 的 minFX = max(minRX, minTX), minFY = max(minRY, minTY), maxFX = min(maxRX, maxTX), maxFY = min(maxRY, maxTX),得到 F 的各个值之后,只要判断矩形 F 是否成立就知道 R 和 T 到底有没有相交了,若 minFX > maxFX 或 minFY > maxFy 则 F 无法构成,RT不相交,否则 RT相交


2)跨立试验

   若 P1P2 跨立 Q1Q2,则 P1,P2 分别在 Q1Q2 所在直线的两端,则有 (P1 - Q1)*(Q2 - Q1)*(Q2 - Q1)*(P2 - Q1) > 0,当 (P1 - Q1)*(Q2 - Q1) = 0 时,说明 (P1 - Q1) 与 (Q2 - Q1) 共线,但由于已经经过快速排斥试验,所以 Q1 必为 P1P2 与 Q1Q2 的交点,依然满足线段相交的条件,则跨立试验可改为:
      当 (P1 - Q1)*(Q2 - Q1)
*(Q2 - Q1)*(P2 - Q1) >= 0,则 P1P2 跨立 Q1Q2,当 Q1Q2 也跨立 P1P2 的时候,则 P1P2 相交
      (注意式子中的
* 代表两个叉积的值的相乘,而另外的两个 * 则代表计算矢量叉积)

代码如下:

/*
向量叉乘的性质(乘积的正负性可以表示两个向量之间的相对转动方向,如a×b为正,则b通过顺时针旋转可以与a共线,
否则通过逆时针旋转可以与a共线)就可以很方便的解决这个问题。 
快速排斥实验:包含两条线段的最小矩形是否相交,不相交则肯定不行,这样可省掉一些计算。  
跨立试验:如果两线段相交,则两线段必然相互跨立对方。若a1b1跨立a2b2,矢量(a1-a2)和(b1-a2)位于矢量(a2-b2) 的两侧,
即     (a1-a2)×(a1-b1)*(a1-b1)×(a1-b2)>=0 
同理,a2b2跨立a1b1    
        (a2-a1)×(a2-b2)*(a2-b2)×(a2-b1)>=0 
*/
typedef struct SDL_Point{ 
	Sint16 x;  
	Sint16 y; 
} SDL_Point;
int max(int x, int y)
{  //比较两个数的大小,返回大的数  
	return x>y?x:y;	
}           
int min(int x, int y)
{  //比较两个数的大小,返回小的数  
	return x<y?x:y;  
}  
int multi(SDL_Point p1, SDL_Point p2, SDL_Point p0)     
{  //求矢量[p0, p1], [p0, p2]的叉积 
	return (p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y); 
	//p0是顶点          
	//若结果等于0,则这三点共线
	//若结果大于0,则p0p2在p0p1的逆时针方向  
	//若结果小于0,则p0p2在p0p1的顺时针方向  
}      
bool isIntersected(SDL_Point s1, SDL_Point e1, SDL_Point s2, SDL_Point e2)    
{   //判断线段是否相交  
	//1.快速排斥试验判断以两条线段为对角线的两个矩形是否相交    
	//2.跨立试验      
	if( (max(s1.x, e1.x) > min(s2.x, e2.x)) &&
		(max(s2.x, e2.x) > min(s1.x, e1.x)) &&
		(max(s1.y, e1.y) > min(s2.y, e2.y)) &&
		(max(s2.y, e2.y) > min(s1.y, e1.y)) &&
		(multi(s2, e1, s1) * multi(e1, e2, s1) >= 0) &&
		(multi(s1, e2, s2) * multi(e2, e1, s2) >= 0) )  
		return true;    
	return false;  
}

判断两直线是否相交
1)斜率
判断斜率,这个在中学时代就学过了,不过斜率需要考虑垂直的特殊情况,比较麻烦。
2)计算两个向量的叉积
计算两个向量的叉积,如果为0,则是平行或者重合的,否则两直线相交。 
代码如下:

struct point{ 
     int x;  
     int y;
};    
struct v{  
	point start; 
	point end; 
};
int crossProduct(v* v1, v* v2){    
	v vt1, vt2;
	int result = 0; 
	vt1.start.x = 0;  vt2.start.x = 0; 
	vt1.start.y = 0;   vt2.start.y = 0;
	vt1.end.x = v1->end.x - v1->start.x;  vt2.end.x = v2->end.x - v2->start.x;
	vt1.end.y = v1->end.y - v1->start.y;  vt2.end.y = v2->end.y - v2->start.y;     
	result = vt1.end.x * vt2.end.y - vt2.end.x * vt1.end.y;   
	return result; 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值