判断一点是否在扇形内

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)内即可。

 

  水平有限,难免出错,欢迎指正与指点。 希望通过阅读本文能给读者提供解决问题的思路。

 

  

 

posted @ 2016-09-10 14:35  mr_yu  阅读(8016)  评论(0编辑  收藏  举报