雨亭

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

1.利用三角形ABC面积,1/2*|A||B|Sin(a); 任意一点P,分别向三角形(A,B,C)三个顶点连线,形成三个三角形(PAB,PBC,PAC)。如果S(ABC)==S(PAB)+S(PBC)+S(PAC),则点在三角形内,反之,在三角形外

2.判断点是否在三条边的同一层

 

先说一下叉乘:

设矢量P   =   (x1,y1)   ,Q   =   (x2,y2)
则矢量叉积定义为:     P   ×   Q   =   x1*y2   –   x2*y1       得到的是一个标量
显然有性质   P   ×   Q   =   –   (   Q   ×   P   )       P   ×   (   –   Q   )   =   –   (   P   ×   Q   )
如不加说明,下面所有的点都看作矢量,点的乘法看作矢量叉积;
叉乘的重要性质:
 若   P   ×   Q     >   0   ,     则P   在Q的顺时针方向
&#61548; 若   P   ×   Q     <   0   ,     则P   在Q的逆时针方向
&#61548; 若   P   ×   Q     =   0   ,     则P   与Q共线,但可能同向也可能反向

我提出的算法中有一步要判断顺时针逆时针的方向,其实很简单。假设有两端连续的险段,其公共端点是P0,其他的两个端点是P1和P1,计算向量 <P0,P1> 和 <P0,P2> 的叉乘,如果若结果为正,则 <P0,P1> 在 <P0,P2> 的顺时针方向;若为0则 <P0,P1> <P0,P2> 共线(可能同向和反向);若为负则 <P0,P1> 在 <P0,P2> 的在逆时针方向。因此可以根据这个判断p0p1和p1p2在p1处是左转还是右转,只要求(p2-p0)*(p1-p0),若 <0则左转,> 0则右转,=0则共线。


下面的简单的函数可以计算叉乘。


#define   infinity   1e20
#define   EP   1e-10

struct   TPoint   {
        float   x,y;
};

struct   TLineSeg   {
        TPoint   a,b;
};


/********************************************************   *                                                                                                            
  *     返回(P1-P0)*(P2-P0)的叉积。                                                  
  *     若结果为正,则 <P0,P1> 在 <P0,P2> 的顺时针方向;                
  *     若为0则 <P0,P1> <P0,P2> 共线;                                                
  *     若为负则 <P0,P1> 在 <P0,P2> 的在逆时针方向;                          
  *     可以根据这个函数确定两条线段在交点处的转向,                  
  *     比如确定p0p1和p1p2在p1处是左转还是右转,只要求            
  *     (p2-p0)*(p1-p0),若 <0则左转,> 0则右转,=0则共线        
  *                                                                                                      
\********************************************************/

float   multiply(TPoint   p1,   TPoint   p2,   TPoint   p0)
{
        return   (   (   p1.x   -   p0.x   )   *   (   p2.y   -   p0.y   )   -   (   p2.x   -   p0.x   )   *   (   p1.y   -   p0.y   )   );        
}

叉乘还有很多用途,举例如下:

1。判断点在线段上
设点为Q,线段为P1P2   ,判断点Q在该线段上的依据是:
(   Q   –   P1   )   ×   (   P2   –   P1   )   =   0     且   Q   在以   P1,P2为对角顶点的矩形内

//   判断点p是否在线段l上
int   online(TLineSeg   l,TPoint   p)
{
        return(   (multiply(l.b,p,l.a)==0)&&(   ((p.x-l.a.x)*(p.x-l.b.x) <0   )||(   (p.y-l.a.y)*(p.y-l.b.y) <0   ))   );
}


2。判断两线段是否相交
我们分两步确定两条线段是否相交:
1.快速排斥试验
设以线段   P1P2   为对角线的矩形为R,   设以线段   Q1Q2   为对角线的矩形为T,如果R和T不相交,显然两线段不会相交;
2.跨立试验
如果两线段相交,则两线段必然相互跨立对方,如图1所示(可惜这里无法显示图:(     )。在图1中,P1P2跨立Q1Q2   ,则矢量   (   P1   –   Q1   )   和(   P2   –   Q1   )位于矢量(   Q2   –   Q1   )   的两侧,即
(   P1   –   Q1   )   ×   (   Q2   –   Q1   )     *     (   P2   –   Q1   )   ×   (   Q2   –   Q1   )     <     0
上式可改写成
(   P1   –   Q1   )   ×   (   Q2   –   Q1   )     *     (   Q2   –   Q1   )   ×   (   P2   –   Q1   )     >     0
当   (   P1   –   Q1   )   ×   (   Q2   –   Q1   )   =   0   时,说明   (   P1   –   Q1   )   和   (   Q2   –   Q1   )共线,但是因为已经通过快速排斥试验,所以   P1   一定在线段   Q1Q2上;同理,(   Q2   –   Q1   )   ×(   P2   –   Q1   )     =   0   说明   P2   一定在线段   Q1Q2上。
所以判断P1P2跨立Q1Q2的依据是:
(   P1   –   Q1   )   ×   (   Q2   –   Q1   )     *     (   Q2   –   Q1   )   ×   (   P2   –   Q1   )     ≥     0
同理判断Q1Q2跨立P1P2的依据是:
(   Q1   –   P1   )   ×   (   P2   –   P1   )     *     (   P2   –   P1   )   ×   (   Q2   –   P1   )     ≥     0
至此已经完全解决判断线段是否相交的问题。


//确定两条线段是否相交
int   intersect(TLineSeg   u,TLineSeg   v)
{
        return(   (max(u.a.x,u.b.x)> =min(v.a.x,v.b.x))&&
                        (max(v.a.x,v.b.x)> =min(u.a.x,u.b.x))&&
                        (max(u.a.y,u.b.y)> =min(v.a.y,v.b.y))&&
                        (max(v.a.y,v.b.y)> =min(u.a.y,u.b.y))&&
                        (multiply(v.a,u.b,u.a)*multiply(u.b,v.b,u.a)> =0)&&
                        (multiply(u.a,v.b,v.a)*multiply(v.b,u.b,v.a)> =0));
}

posted on 2012-08-29 00:44  雨亭  阅读(482)  评论(0编辑  收藏  举报