开篇提到,判断一个图形的绕序,常规的方法是用向量叉积,对于凸多边形来说,这非常简单。
叉积等于两向量长度乘以夹角的正弦,并且其正负性反映了旋转的方向。
如上图,假设y轴向上为正,那么向量BA和BC的叉积将会为正数(这个地方就不展开解释了,你们可以自行百度),它指示从BA旋转到BC是逆时针(正)旋转,然后ABCDE的绕序与叉积结果恰好相反,为顺时针。
换到逆时针,上面红色字的结论仍然成立,自己看下图。
但是这个简单的结论在凹多边形的凹角里面就不成立了。因为在凹角处,向量的旋转在多边形外,所以会导致结果错误。
比如下图,由于向量叉积都按小的角度方向,所以就成了点的绕序跟叉积结果一致而非相反了。
要解决这个问题,要么判断到当前角的凹凸性,要么找到一个一定是凸的角(凸多边形没凹角所以不一定有凹的存在)。
其实两条路都不好走。原因在于单独的一个角无凹凸之说,它依赖于图形而存在。
另一位搞算法的同事给出的方案是通过包围盒找到一个一定是凸的角,如下图,碰到包围盒边缘的A,D和E都一定是凸角。
然后我们的项目选取了x最大者作为凸角,也就是A点。
至此问题理应得到了解决,但现实远比这残酷多了。因为我们遇到的多边形可能是长这个样子的。
B以微弱的优势当选了凸角,但是这个凸角跟180度很接近,位于凹和凸的边缘,而sin180度等于0,所以结果很容易受浮点误差的影响在正数与负数之间徘徊,比如准确结果是0.001的,但浮点误差导致它变成-0.0002,那么就误判了。
这时候要剔除接近180度的点,然后往两个方向找到不易被误判的点,这时候我们可以让A或者C来代替B点进行计算。
虽然共线点很多的时候有几率触发死循环,但还是勉强够用的。不过我们做的项目还有用圆弧线作为边的,这时候这套做法就凑合不过来了。
除了180度,还有0度角都会存在类似的浮点误差问题,加入圆弧之后就会有0度角的产生,并且不好剔除。图中AB和弧BC相切。
这个图形还是很好解决的,用连线代替切线即可。
当然了,这个做法一点都经不起推敲。请看下图。
显然,沿着圆弧走和沿着直线走绕序已经反了。
为了让这一做法还是正确的,我们又想了个方法,把弧线上的最大最小值点加上。
当然了,这样还是有问题。因为弧线上未必有最大最小值点,看看下图。
这个图我不标绕序了,自己体会下就好。
而且,即使有最大最小值点,结果也可能错的。如下图,A1-B-C沿弧线走和沿直线走绕序是不一致的。
至此得到结论,用向量这个常规做法,不仅蛋疼,而且已经是无解了。
下一篇我将给出微积分的解决方法,虽然这个做法跟向量有交集,但整个思想和套路已经发生了根本的改变。