• 判断一点是否在扇形内


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

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

      

  • 相关阅读:
    Android 定制RadioButton样式
    TabActivity的不同实现
    android源码的目录
    让我们一起来做最漂亮的Android界面
    【Android布局】在程序中设置android:gravity 和 android:layout_Gravity属性
    iPhone iPad 各种控件默认高度
    互联网的的一些历史资料收集
    Android vs iPhone icon设计指南
    android获取手机cpu是单核还是多核的方法
    前端必读:浏览器内部工作原理
  • 原文地址:https://www.cnblogs.com/newbeeyu/p/5859382.html
Copyright © 2020-2023  润新知