【问题标题】:Is point inside polygon?点在多边形内吗?
【发布时间】:2016-10-04 13:21:06
【问题描述】:

我需要编写一个函数来计算一个点是否在多边形内(真/假)。 多边形总是包含 4 个点。我正在从 SVG 文件中读取多边形和点

<g id="polygons">
    <g id="LWPOLYLINE_183_">
        <polyline class="st10" points="37.067,24.692 36.031,23.795 35.079,24.894 36.11,25.786 37.067,24.692   " />
    </g>
    <g id="LWPOLYLINE_184_">
        <polyline class="st10" points="35.729,23.8 35.413,23.516 34.625,24.39 34.945,24.67 35.729,23.8   " />
    </g>
    <g id="LWPOLYLINE_185_">
        <polyline class="st10" points="34.483,24.368 33.975,23.925 34.743,23.047 35.209,23.454 34.483,24.368   " />
    </g>
    <g id="LWPOLYLINE_227_">
        <polyline class="st10" points="36.593,22.064 36.009,21.563 35.165,22.57 35.736,23.061 36.593,22.064   " />
    </g>
</g>
<g id="numbers">
    <g id="TEXT_1647_">
        <text transform="matrix(0.7 0 0 1 34.5876 23.8689)" class="st12 st2 st13">169</text>
    </g>
    <g id="TEXT_1646_">
        <text transform="matrix(0.7 0 0 1 35.1049 24.1273)" class="st12 st2 st13">168</text>
    </g>
    <g id="TEXT_1645_">
        <text transform="matrix(0.7 0 0 1 35.924 24.7302)" class="st12 st2 st13">167</text>
    </g>
    <g id="TEXT_1643_">
        <text transform="matrix(0.7 0 0 1 36.0102 22.4477)" class="st12 st2 st13">174</text>
    </g>
</g>

所以对于折线,它将是前 4 组坐标,对于文本 X 和 Y 是矩阵括号中的最后 2 个数字。也不知道文本的那个点是文本的中心还是左下角(假设是这个)。

到目前为止,我得到了列表中点和多边形的所有坐标,所以我正在交叉检查这种方式。

【问题讨论】:

  • 显示您迄今为止尝试过的内容以及您遇到的具体问题。 Stack 不是代码服务,您必须提供一些想法来解决您的 specifc 问题。要求编写完整的代码是这里的题外话。
  • 我是这样计算的:((lowerRightX - upperLeftX) / 2) + upperLeftX = 多边形中部附近的 X 坐标 ((upperRightY - downLeftY) /2) + downLeftY = 中部附近的 Y 坐标的多边形,然后我检查了其他列表,搜索与该计算点具有最接近 X Y 坐标的点: var most = (from entry in myDict where Math.Abs​​(entry.Key)
  • 看看这个stackoverflow.com/questions/1560492/… 现在你只需检查所有的行。
  • 如果您可以使用 GDI+,您可以构建一个 GraphicsPath 并使用 IsVisivle(point) 方法。

标签: c# math svg computational-geometry


【解决方案1】:

测试一个点是否在多边形内的一种简单方法是计算多边形边缘与源自测试点的射线之间的交点数。因为你可以选择任何你想要的射线,所以通常选择它与 X 轴平行是很方便的。代码如下所示:

public static bool IsInPolygon( this Point testPoint, IList<Point> vertices )
{
    if( vertices.Count < 3 ) return false;
    bool isInPolygon = false;
    var lastVertex = vertices[vertices.Count - 1];
    foreach( var vertex in vertices )
    {
        if( testPoint.Y.IsBetween( lastVertex.Y, vertex.Y ) )
        {
            double t = ( testPoint.Y - lastVertex.Y ) / ( vertex.Y - lastVertex.Y );
            double x = t * ( vertex.X - lastVertex.X ) + lastVertex.X;
            if( x >= testPoint.X ) isInPolygon = !isInPolygon;
        }
        else
        {
            if( testPoint.Y == lastVertex.Y && testPoint.X < lastVertex.X && vertex.Y > testPoint.Y ) isInPolygon = !isInPolygon;
            if( testPoint.Y == vertex.Y && testPoint.X < vertex.X && lastVertex.Y > testPoint.Y ) isInPolygon = !isInPolygon;
        }

        lastVertex = vertex;
    }

    return isInPolygon;
}

public static bool IsBetween( this double x, double a, double b )
{
    return ( x - a ) * ( x - b ) < 0;
}

里面塞了一些额外的代码来处理一些字面上的极端情况(如果测试射线直接击中一个顶点,那需要一些特殊处理)。

【讨论】:

    【解决方案2】:

    给定定义多边形顶点的 4 个点,按顺时针或逆时针顺序排列。通过获取每对线的叉积来确定一个点是否在第一次训练中,如果多边形是凹的。

    多边形是p1,p2,p3,p4,每个都有一个x和y。

    要得到叉积,得到 3 个点,p1,p2,p3,其中 p2 是两条线连接的顶点。

    cross = (p2.x-p1.x) * (p3.y-p2.y) - (p2.y-p1.y) * (p3.x-p2.x);
    

    {p1,p2,p3}{p2,p3,p4}{p3,p4,p1}{p4,p1,p2}

    如果所有顶点的十字符号相同(注意 0 既是负也是正),则多边形是凹的。

    如果不是全部相同,则只有一个可以不同。您需要通过将不同的点与相反的点连接起来,将多边形分成两个 3 边的多边形。

    例如 p2,p3,p4 的交叉为负数,其余为正数,则您有两个多边形 p1,p2,p3 和 p3,p4,p1

    现在你有一个或两个多边形。

    计算你在每条线上测试的点的交叉

    cross = (p2.x-p1.x) * (testPoint.y-p1.y) - (p2.y-p1.y) * (testPoint.x-p1.x);
    

    为每一行做{p1,p2},{p2,p3},{p3,p4},{p4,p1}

    如果所有线段的符号相同,则该点位于多边形内部。如果你有两个多边形,你只需要满足一个多边形的相同符号条件

    【讨论】:

    • 对多边形内的点进行更简单的测试是计算多边形边的数量,这些边穿过源自测试点并沿任意方向行进的射线。如果交叉点的数量是奇数,则该点在多边形中。如果是偶数,则它在多边形之外。这不需要对凹多边形(甚至自相交的多边形)进行任何特殊处理。
    • @kyle 确实是的,尽管我不确定哪个更有效(假设在这两种情况下首先进行边界框测试)。我将不得不对此进行测试。感谢您的提醒.. :)
    • @kyle 叉积法更好的原因是您可以将多边形(左/右)转的结果存储为位。由于 4 边多边形是对称的(就左转而言),您只需要 3 位。 7 个变体代表多边形并且是变换不变的(旋转、倾斜、缩放都可以应用)。然后,在大多数外部条件下,测试能够返回至少 2 个叉积的结果。与边缘交叉方法相比,这提供了显着的性能优势。
    【解决方案3】:

    这样解决了:

    public static bool IsPointInPolygon4(PointF[] polygon, PointF testPoint)
        {
            bool result = false;
            int j = polygon.Count() - 1;
            for (int i = 0; i < polygon.Count(); i++)
            {
                if (polygon[i].Y < testPoint.Y && polygon[j].Y >= testPoint.Y || polygon[j].Y < testPoint.Y && polygon[i].Y >= testPoint.Y)
                {
                    if (polygon[i].X + (testPoint.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) * (polygon[j].X - polygon[i].X) < testPoint.X)
                    {
                        result = !result;
                    }
                }
                j = i;
            }
            return result;
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-16
      • 1970-01-01
      • 2010-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多