向量叉积
叉积
叉积的计算是线段方法的核心,考虑下图所示的向量P1和P2.我们可以把叉积理解为由原点(0,0),P1,P2和P1+P2所构成的平行四边形的有向面积.另一种与之等价但是更有效的定义方式是将叉积看做行列式矩阵:
P1=(x1,y1)
P2=(x2,y2)
若P1与P2的叉积为正,说明相对于原点,P1位于P2的顺时针方向,反之P1位于P2的逆时针方向,若为0,表明两个向量共线,方向可能相同,也可能相反.
上代码:
1 #ifndef GEO_GEOMETRY_H_ 2 #define GEO_GEOMETRY_H_ 3 4 #define MIN(x,y) (x>y?y:x) 5 #define MAX(x,y) (x>y?x:y) 6 7 struct Point 8 { 9 float x,y; 10 Point() 11 { 12 x=0; 13 y=0; 14 } 15 Point(float px,float py) 16 { 17 x = px; 18 y = py; 19 } 20 //叉乘,返回一个数值 21 float CrossMulti(Point v) 22 { 23 return x*v.y - v.x*y; 24 } 25 // 减法 26 Point operator-(Point b) 27 { 28 return Point(x-b.x,y-b.y); 29 } 30 // 加法 31 Point operator+(Point b) 32 { 33 return Point(x+b.x,y+b.y); 34 } 35 }; 36 37 #define Vector Point; 38 39 // 判断点(pAim)与直线(pStart--pEnd)的关系 40 // >0 pAim在顺时针 41 // <0 pAmi在逆时针 42 // =0 三点共线 43 static float Direction(Point pStart,Point pEnd,Point pAim) 44 { 45 return (pAim-pStart).CrossMulti(pEnd-pStart); 46 } 47 48 // 判断点(pAim)是否在线段(pStart--pEnd)上 49 static bool OnSegment(Point pStart,Point pEnd,Point pAim) 50 { 51 if( MIN(pStart.x,pEnd.x) <= pAim.x && pAim.x <= MAX(pStart.x,pEnd.x) 52 && MIN(pStart.y,pEnd.y) <= pAim.y && pAim.y <= MAX(pStart.y,pEnd.y)) 53 return true; 54 return false; 55 } 56 57 // 判断线段(p1-p2)和线段(p3-p4)是否相交 58 static bool SegmentIntersect(Point p1,Point p2,Point p3,Point p4) 59 { 60 float d1 = Direction(p3,p4,p1); 61 float d2 = Direction(p3,p4,p2); 62 float d3 = Direction(p1,p2,p3); 63 float d4 = Direction(p1,p2,p4); 64 // 如果互相分隔,一定相交 65 if( (d1 < 0 && d2 > 0 || d1 >0 && d2 < 0 ) 66 && ( d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0)) 67 return true; 68 // 若有三点共线,需判断点是否在线段内 69 else if( d1 == 0 && OnSegment(p3,p4,p1)) 70 return true; 71 else if( d2 == 0 && OnSegment(p3,p4,p2)) 72 return true; 73 else if( d3 == 0 && OnSegment(p1,p2,p3)) 74 return true; 75 else if( d4 == 0 && OnSegment(p1,p2,p4)) 76 return true; 77 else 78 return false; 79 } 80 81 #endif
利用上述性质,我们可以判断连续线段P0P1和P1P2是左拐还是右拐,不需要计算角度,我们只需要判断向量P0P2在向量P0P1的顺时针方向还是逆时针方向就好了.设m=(P2-P0)×(P1-P0),如果m>0说明,则在顺时针方向,在P1处右拐,若m<0,则在逆时针方向,在P1处左拐