判断一点是否在扇形内
1.问题描述:
最近在做一个watch上的电子宠物的项目,整个主功能界面是个圆形,然后每个功能(喂食,清洁,愉悦之类的)各自是圆上的一部分也就是个扇形。然后点击每个扇形,出现对应的子功能界面,格式和主功能界面一致。
现在要实现点击对应的扇形会出现对应的子功能界面,子功能界面和这个类似。换而言之就是点击一下界面,需要判断出接触点落在哪个扇形内。
当时一个简单的想法是在扇形区域内放置一个方形的按钮,点击后调用回调就可以,但是缺点是精确度不够,因为按钮不能覆盖整个扇形,所以这种方法不可取。
于是需要实现点击任意区域,都要能判断到接触点属于哪个区域。
2.分析:
现在需求已经很明确,就是判断接触点位于圆内的哪个扇形内。以判断一点是否在A0B扇形内为例。
通过和美术沟通得知,每个扇形在圆上的两点坐标和圆心坐标是知道的。假设圆心坐标 O(0, 0), 上图中A,B点坐标A(x1, y1), B(x2, y2) ,以及可以在点击时得到的任一点坐标P(x, y) 。
现在讨论(0 <= 角AOB <= 180)度的情况。
判断点在扇形内要满足以下三个条件:
1. 扇形的"start arm" 要逆时针旋转经过P点。
2. 扇形的"end arm" 要顺时针旋转经过P点。
3. 点在圆内。
如下图:
证明1 "start arm" 0A向量到OP向量是顺时针旋转:
1.)做“start arm” OA向量 "逆时针方向"转过90度 的法向量(normal vector),则利用两个垂直向量的 点乘 为 0 的性质 则该法向量可以表示为n1(-y1, x1).
2. )n1 和 OP 两向量做点积 n1*OP = |n1|*|OP|*cosa (a为n1和OP间的夹角) ,如果点积的结果大于0,则表明0 < a < 90, 而OA和n1 夹角= 90. 这时可以证明0A向量到OP向量是顺时针旋转。
反之点积结果小于0,表示0A向量到OP向量是逆时针旋转. 这里给出点积的百科: http://baike.baidu.com/view/2744555.htm
可以这样理解,OA逆时针旋转90度得到n1,倘若OP与n1的夹角小于90度,则OA逆时针旋转到OP。
如下图
接下来证明"end arm" 到OP是顺时针旋转:
1.)做“end arm” OB向量 "逆时针方向"转过90度 的法向量(normal vector),则利用两个垂直向量的 点乘 为 0 的性质 则该法向量可以表示为n2(-y2, x2).
2. )n2 和 OP 两向量做点积 n2*OP = |n2|*|OP|*cosb (b为n1和OP间的夹角) ,如果点积的结果小于0,则表明90 < b < 180, 而OB和n2 夹角= 90. 这时可以证明0B向量到OP向量是顺时针旋转。
反之点积结果大于0,表示0B向量到OP向量是逆时针旋转.
如下图:
下面给出一个判断扇形的"start arm" 或 “end arm” 到OP是顺时针还是逆时针的函数:
function areClockWise(Varm, Vop)
{
return -Varm.y * Vop.x + Varm.x * Vop.y > 0;
}
说明:Varm的垂直向量表示为 (-Varm.y, Varm.x),所以上边就是两个向量的点积。
接下来判断点P是否在圆内,这个就很简单了
function isWithinRadius(v, radiusSquared)
{
return v.x*v.x + v.y*v.y <= radiusSquared;
}
那么三个的证明如上,下边是总体的代码,当然可以用任何语言实现:
function isInsideSector(point, center, sectorStart, sectorEnd, radiusSquared) { //point即为任意一点,sectorStart即为"start arm", 同理sectorEnd. var relPoint = { x: point.x - center.x, y: point.y - center.y }; return !areClockwise(sectorStart, relPoint) && areClockwise(sectorEnd, relPoint) && isWithinRadius(relPoint, radiusSquared); }
说明:以上判断扇形的sectorStart和sectorEnd为逆时针顺序, 且(0 <= 角AOB <= 180)度的情况,如果任一点恰好和OA或OB重合,则需根据具体需求判断算不算在扇形内.
原文地址: http://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector
----------------------------------------------------------------------------------------------------------------分割线----------------------------------------------------------------------------------------------------------------------------
另一种思路:
本着尽量不用三角函数的原则,这里还可以通过比较斜率大小来判断点是否在扇形内。在y轴的左侧,斜率逆时针方向是递增的,y轴右侧,斜率也是递增的,利用这个性质可以得出相应的方法。
当然,还需讨论扇形包含y轴的情况。 本来没发现上边的方法之前,是要用这个思路实现的。
延伸:
1.如何利用上边方法,判断给出的两个“arm”围成的扇形角度是否大于180呢?
1)首先,做OA的反向延长线,交圆于点C,得到OC向量(-x1, -y1)。
2)如果OA和OB夹角大于180,则OC会用过逆时针旋转转到OB,这时用判断“条件1”的步骤计算,若得到的点积大于0则表示 OA,OB所围成的扇形大于180,反之则小于180.
2.当不知道OA,0B所围成扇形是否大于180时,如何正确判断点P是否在扇形中呢?
1)先用上边的方法判断OA,OB所围成扇形是否>180.若大于180,这时只需判断p点是否在OB,OA所谓称的扇形(即star tarm = OB, end arm = OA)内即可。
水平有限,难免出错,欢迎指正与指点。 希望通过阅读本文能给读者提供解决问题的思路。