【问题标题】:How to check if a 2d point is inside a 2D polygon in Java?如何检查 2d 点是否在 Java 中的 2D 多边形内?
【发布时间】:2020-07-23 22:03:52
【问题描述】:

在我的 Java 应用程序中,我使用顶点数组创建 2D 多边形。例如,我想用这 4 个顶点创建一个简单的正方形

[-130, -74], [-125, -74], [-125, -70], [-130, -70]

然后我想检查一个点是否在生成的多边形内。但是,例如,如果我检查这一点

[-125, -73]

使用polygon.contains(x, z) 表示不在多边形内。即使我检查一个角落,比如[-125, -74] 是返回false。对我来说奇怪的是,我检查了这个点 [-126, -74] 是否返回 true,所以有些点实际上被视为多边形内部,而另一些则不是,我不明白为什么会这样。这是我为测试而设置的示例代码,没什么特别的

public static void main(String[] args) {
   Polygon polygon = new Polygon(new int[]{-130, -125, -125, -130}, new int[]{-74, -74, -70, -70}, 4);
   System.out.println("" + polygon.contains(-125, -73));
   System.out.println("" + polygon.contains(-125, -74));
   System.out.println("" + polygon.contains(-126, -74));
}

还有输出

false
false
true

我还要指出这只是一个简单的例子,但多边形可能是一个非常复杂的形状,例如像这样疯狂的东西

【问题讨论】:

标签: java polygon points


【解决方案1】:

文件Polygon

这个多边形是用奇偶缠绕规则定义的。有关奇偶缠绕规则的定义,请参阅WIND_EVEN_ODD

WIND_EVEN_ODD
用于指定用于确定路径内部的奇偶规则的缠绕规则常量。奇偶规则指定如果在从该点到无穷远的任何方向绘制的光线被路径段奇数次穿过,则该点位于路径内。

所以你可以这样做。

static Polygon mirror(Polygon p) {
    int npoints = p.npoints;
    int[] xpoints = new int[npoints];
    int[] ypoints = new int[npoints];
    for (int i = 0; i < npoints; ++i) {
        xpoints[i] = -p.xpoints[i];
        ypoints[i] = -p.ypoints[i];
    }
    return new Polygon(xpoints, ypoints, npoints);
}

static boolean onVertex(Polygon p, int x, int y) {
    int npoints = p.npoints;
    for (int i = 0; i < npoints; ++i)
        if (p.xpoints[i] == x && p.ypoints[i] == y)
            return true;
    return false;
}

static boolean contains(Polygon p, int x, int y) {
    return p.contains(x, y)
        || onVertex(p, x, y)
        || mirror(p).contains(-x, -y);
}

Polygon polygon = new Polygon(new int[]{-130, -125, -125, -130}, new int[]{-74, -74, -70, -70}, 4);
System.out.println("" + contains(polygon, -125, -73));
System.out.println("" + contains(polygon, -125, -74));
System.out.println("" + contains(polygon, -126, -74));

输出:

true
true
true

一个有洞的多边形的测试。

int width = 100, height = 100;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
int[] xs = {20, 80, 80, 20, 20, 40, 60, 60, 40, 40};
int[] ys = {20, 20, 80, 80, 20, 40, 40, 60, 60, 40};
Polygon p = new Polygon(xs, ys, xs.length);
Graphics2D g = image.createGraphics();
try (Closeable c = () -> g.dispose()) {
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, width, height);
    g.setColor(Color.BLACK);
    g.drawPolygon(p);
    g.setColor(Color.RED);
    for (int x = 0; x < width; ++x)
        for (int y = 0; y < height; ++y)
            if (contains(p, x, y))
                g.fillRect(x, y, 1, 1);
}
ImageIO.write(image, "png", new File("data/testPolygon.png"));

输出

如果contains(p, x, y) -> p.contains(x, y) 那么

【讨论】:

  • 我已经测试过这个解决方案,但它似乎只适用于其中没有孔的多边形。例如,假设您有这样的情况imgur.com/3BVCiXo 如果您检查“内部正方形内部”的点(因此实际上在多边形外部)它返回 true,而外部正方形内的其他点返回 false
【解决方案2】:

从点 P(x, y) 射出一条射线并计算与边的交点,如果交点数为奇数,则 P 在多边形内。

但是,如果光线与其中一个顶点相交,则可能由于舍入问题而难以确定交点。因此,您可以按照以下步骤操作:

  • 向任意方向发射光线,使光线不会直接撞击任何顶点。
  • 对于多边形中的每条边,确定光线是否与边相交,如果是 - 增加计数器
  • 检查完所有边后,如果相交计数器为奇数,则 P inside。

https://en.wikipedia.org/wiki/Point_in_polygon

【讨论】:

  • 我也在 AlphaConqueror 评论中看到了这个解决方案。似乎这可以工作,只需注意我使用 int 作为坐标,所以我认为不应该出现舍入问题。但我仍然想知道为什么似乎没有“香草”方法来测试它,因为我认为这是处理多边形时的常见问题
  • 即使使用int 值,您也需要使用除法来确定交点。它可能会导致非整数数字,如果将数字转换回整数,可能会导致结果不准确。
猜你喜欢
  • 1970-01-01
  • 2021-07-31
  • 2016-11-02
  • 2015-07-24
  • 1970-01-01
  • 2012-09-16
  • 2014-04-26
相关资源
最近更新 更多