【问题标题】:Parallelogram contains Point平行四边形包含点
【发布时间】:2010-11-16 02:21:31
【问题描述】:

判断一个点是否在平行四边形/菱形内最快的方法是什么?

【问题讨论】:

    标签: actionscript-3 performance math mathematical-optimization


    【解决方案1】:

    再次您好,感谢您的所有回答。与此同时,我自己想出了一些我认为会相当快的方法:

    假设我们有一个由 PQ 和 PR 跨越的平行四边形,其中 PQ 和 PR 是向量(P、Q 和 R 是角)。此外,我们还有要检查的点 A。

    我们知道Vector PA可以拆分为平行于PQ和PR的两个向量:

    PA=n*PQ+m*PR
    

    现在我们知道 n 和 m 必须在区间 [0; 1],我们求解n和m:

    n = -det(PA, PQ)/det(PQ, PR)
    m = det(PA, PR)/det(PQ, PR)
    

    其中 det(PA, PQ) 是向量 PA 和 PQ 的行列式:

    det(PA, PQ) = PA.x*PQ.y-PQ.x*PA.y
    

    如果点 A 在平行四边形内,则 0

    var d:Number = det(PQ, PR);
    if (0 <= -det(PA, PQ)/d <= 1 && 0 <= det(PA, PR)/d <= 1)
    {
        //inside
    }
    else
    {
        //outside
    }
    

    【讨论】:

    • 对不起,这是不正确的。问题是PAPQ 的投影可能是负的,即使A 在平行四边形内。例如,如果PQPR 是一个钝角,并且A 是一个非常靠近R 的内点,那么PAPQ 的投影是负的。
    • 谢谢你,Anton,我算得很快,并没有多想。现在我已经更正了,我的答案应该是正确的:D
    • @sigvardsen:我认为还是有问题。 nm 的公式不正确。据我所知,不依赖于PA.y。如果PR=(0,1)PQ=(1,0)PA=(.5,1000),那么你的算法会说A 在里面。
    • 完美。您编写解决方案的方式也承认了一个非常漂亮的解释。 det(PR,PQ)=+-Area(PR,PQ) 是由PRPQ 跨越的平行四边形的面积(如果PRPQ 处于“错误”顺序,则为负数)。所以你的公式说A 在平行四边形中,平行四边形的面积必须在PRPA 之间@ 和 PQ 必须介于 0Area(PR,PQ) 之间。如果您绘制图片,这是非常直观的。不错的工作。 +1
    • @sigvardsen 我几乎完全理解这一点以及它是如何工作的,除了一件事。为什么 det(PA, PQ)/det(PQ,PR) 对 n 取反?在我自己的测试中,我似乎让它工作,但只有当我不否定它时。我错过了什么?
    【解决方案2】:

    想象一下从你的点向一个方向发出的光线。如果那条光线穿过你的形状线奇数次,它就在形状内部。如果它穿过偶数次,它就在形状之外。

    因此,在您的程序中,您只需创建一条不可见的线并查看它交叉的频率。我想,Actionscript 可能有一个内置函数来执行此操作。

    现在,如果您有大量的对象并且点只能在一个中,您可以使用Binary Space Partition 来存储对象的位置来加快速度。这样一来,您就不必将您的观点与每个对象进行比较,只需将其与附近的对象进行比较即可。

    【讨论】:

    • 这个解决方案对所有封闭的多边形都是准确的。
    • 这个解决方案对于任何封闭的形状都是准确的,但必须注意的是,您选择的光线可能切向地击中形状的边界,在这种情况下,这将是一个错误把它算作一个简单的交叉点。对于一个看着迷宫般的曲线的人来说,这是一个非常好的解决方案,但对于一个计算机程序来说,计算这些交叉点并不总是最直接的方法。
    • 安东:这似乎不太可能。对于多边形(如问题所示),只有当点和线段在同一条线上时才会发生这种情况。在那种情况下,不会有一个解决方案,而是无限多个。这种情况应该很明显,你可以忽略它。对于曲线的切线,您可以简单地计算交点处的切线并查看它是否等于直线。如果是这样,请丢弃它。但似乎不太可能忽略它是安全的。
    • @Chad Okere:当然这不太可能,但是当你让算法做某事时考虑退化情况很重要(假设你希望算法总是有效;不仅仅是通常 工作)。另一种可能发生的方式(除了你描述的方式)是如果点在多边形之外并且光线穿过多边形的 corner。至于曲线的切线,您不想只丢弃切线与射线一致的交点,因为射线可能正在通过拐点,在这种情况下,您应该计算一个交点.
    【解决方案3】:

    查看我对this question 的回答,非常相似。在那里,如果平行四边形的一个角在(0,0) 处,我给出了我认为非常简单的测试,因为它使解释更容易查看,但修改它以使其正常工作并不难。

    编辑:由于问题所有者熟悉向量,我基本上会用那种语言重写我的答案。假设平行四边形由向量PQPR 组成,其中PQR 是角。符号* 将表示点积。选择一个点q 使得PQ 垂直于Pq(即Pq*PQ=0)和PR*Pq&gt;0(例如,您可以通过将Q 围绕P 旋转90 度得到q) .还要选择一个点r,这样PR*Pr=0PQ*Pr&gt;0。那么一个点A 在内部当且仅当(0 &lt; Pr*PA &lt; Pr*PQ) &amp;&amp; (0 &lt; Pq*PA &lt; Pq*PR)

    【讨论】:

      【解决方案4】:

      paper 描述了一种确定光线和四边形相交位置的方法。如果四边形是平行四边形,则可以进一步简化。

      如果你有一个平行四边形,其相邻边由向量 ABAC 描述。平行四边形平面上的任意一点都可以用下面的向量来描述

      T(a, b) = A + a * AB + b * AC
      

      任何射线都可以描述为原点O和方向D

      R(t) = O + t * D
      

      2的交集是当T(a, b) == R(t)

      O + t * D = A + a * AB + b * AC
      

      解决ab 的问题,并检查它们是否都在0 和1 之间。请参阅本文末尾的伪代码了解如何实现。

      【讨论】:

        【解决方案5】:

        直线的标准方程为 ax+bx+c=0 .. 但有趣的是,如果您采用该表达式 ax+bx+c,并在给定 a,b,c 的情况下计算点 x,y在特定的行中,您会发现表达式将平面分成两半,一半在表达式大于零的位置,另一半在小于零的位置。

        现在,如果您取平行四边形的四个点,并计算构成平行四边形一侧的每条线的 a、b、c 系数,您可以计算所讨论的 x、y 的每个表达式并找到该点在每条线的哪一侧。平行四边形的 inside 将是特定边的逻辑与。

        也就是说,一旦你为四行中的每一行设置了 a、b、c,你就可以进行测试 something like

        if ( ((a1*x+b1*y+c1)>0) && ((a2*x+b2*y+c2)<0) && 
                ((a3*x+b3*y+c3)<0) && ((a4*x+b4*y+c4)>0) {
            // it's in!
        }
        

        .. 唯一剩下的技巧是您必须确定每个符号测试的“极性”,即大于或小于零。执行此操作的简单方法是评估 0,0,并查看它是否在您想要的一侧,这相当于说“c”的符号告诉您测试的方式。

        诚然,这是一种蛮力的方法,但它可以扩展到任何凸多边形......或者,删除一个点,它也适用于三角形。

        【讨论】:

          【解决方案6】:

          我对这个问题的第一个观察是矩形(与轴对齐)是一个简单的退化案例。如果该矩形的两个角是:(x1,y1) 和 (x2,y2),那么您只需测试,给定一个点 (x3,y3),min(x1,x2)

          这也可能是一个有用的优化。如果我们找到平行四边形的轴对齐边界矩形,那么我们可以开始快速测试任何给定点。

          如果我们的平行线具有非零斜率,那么我们计算边界线的轴交点以及在这些斜率处通过相关点的线的交点。如果我们的两个点的交点(由两个斜率定义)都位于边界线的交点之间,那么我们就在其中。如果其中任何一个不在这些范围内,那么我们不在。

          我没有时间编写示例,但计算这些斜率和交叉点是第一年的代数。

          [附录]

          我现在看到第一篇文章(关于从要测试的点发出的光线并沿着任意斜率延伸)是对任何封闭平面多边形的这个问题的一般解决方案的参考......或者,事实上,对于任何闭合的平面曲线。它还可以扩展到封闭曲面的三个维度。

          但是,有一个警告适用于平行四边形或菱形。在凹多边形(或其他一些曲线)的情况下,如果光线击中任何顶点(角),那么测试可能会返回偶数个“线”交叉点。换句话说,同时包含在多边形多个“边”中的“曲线”的任何部分都可能返回误报。

          两种解决方案是:明确测试线段限制(角/顶点)处的交点,或将每个线段视为一端以 (point + epsilon) 为界(这样我们的计算将找不到任何点任何两侧的共同点)。

          我找到一个边界矩形的想法仍然是一个有用的快速测试,并且通常扩展到所有多边形。我们找到 min() 和 max() x 来找到左右边界,而 min() 和 max() y 来找到上下边界。这也可以扩展到三个维度......一位朋友告诉我,标准图形库在大多数虚拟现实和 MMORPG 等中广泛使用它来进行碰撞检测。当在边界框中找到碰撞时,它们会进行更细粒度的计算在其中包含的多边形上。

          【讨论】:

            【解决方案7】:

            如果平行四边形是凸的(并且给定平行四边形的定义,它必须是 xD),那么任何测试它是否在其边界内的算法都应该这样做,您可以提高展开循环的效率,因为您知道只有 4 个顶点,例如。

            这是一个简单的算法,它根据矢量乘积的右手定则测试点在所有线段的同一侧(您也可以通过用简单的符号测试替换除法以标准化矢量来优化它):

            How to test if a point is inside of a convex polygon in 2D integer coordinates?

            另一种选择,如果您要对同一个平行四边形进行大量比较,则将其归一化为正方形,获取进行转换的矩阵,每次得到要测试的点时,将其乘以矩阵然后检查转换后的点是否在归一化正方形内(这应该容易得多)。

            【讨论】:

              【解决方案8】:
              1. 获取多边形的轮廓
              2. 检查点是否在计数中

              dist1 = cv2.pointPolygonTest(contours[0], (50, 70), True)

              dist 返回以下三个之一:

              • 如果点在轮廓内,则为正值
              • 如果点在轮廓之外,则为负值
              • 如果点在轮廓上,则为零

              How to check if point is placed inside contour?

              【讨论】:

                【解决方案9】:

                y 坐标是最简单的,所以从它开始。如果该点的 y 坐标在形状的顶部和底部之间,则继续到 x 坐标。在该点的y坐标处计算形状左右两侧的x坐标,并检查该点的x坐标是否在它们之间。

                编辑:

                给定左上角、右上角、右下角和左下角四个坐标:

                if (y >= y1 && y <= y3) {
                   var k = (x4 - x1) / (y4 - y1);
                   var m = x1 - k * y1;
                   if (x >= k * y + m) {
                     k = (x3 - x2) / (y3 - y2);
                     m = x2 - k * y2;
                     if (x <= k * y + m) {
                       // inside
                     }
                   }
                }
                

                【讨论】:

                  猜你喜欢
                  • 2010-09-19
                  • 1970-01-01
                  • 2021-10-10
                  • 2019-12-14
                  • 1970-01-01
                  • 2019-06-04
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多