【问题标题】:Find area within set distance from point in a complicated polygon在复杂多边形中查找距点设定距离内的区域
【发布时间】:2021-06-18 03:30:36
【问题描述】:

给定一个简单的(不相交的)多边形,例如平面图(房间之间的门缺失,以提供 1 个简单的不间断边界)。如何找到从(x,y)点(多边形边界内或多边形边界上)可到达的多边形内的所有区域?理想情况下,我希望从中返回一个多边形,然后可以将其覆盖以显示所有可到达的区域。

我考虑过 A* 搜索类型的方法,我会搜索最短路径,迭代位于多边形周边(作为目的地)上的所有点,然后沿着最短路径折线在设定的距离限制处绘制新点以给出新的多边形外壳。

我也考虑过将波传播作为一种方法。

我想知道我是否在库/方法方面遗漏了一些明显的东西,以及是否有人对我如何实现这一点有任何其他想法。

给定一个像这样的多边形:

我正在创建一个显示内部空间(不包括内门)的多边形,如下所示:

这是我的问题所指的部分。我想从多边形边界上的给定点以距该点设置的最大行进距离(在下面用红色正方形捐赠)找到多边形内的所有可到达点(以红色显示为新多边形),如下所示:

【问题讨论】:

  • 我无法理解您的第一句话。你能举个例子说明我们在这里得到了什么。像示例平面图/多边形和示例结果。
  • 请查看更新后的问题以增加清晰度@Thomas
  • 对不起,不能给你解决方案,只是一个方向。看看游戏中用于寻路的导航网格。基本上是相邻凸包的图。也看看这个:gamedev.stackexchange.com/questions/185828/…
  • 以下是关于点对点最短路径的一些说明:graphics.stanford.edu/courses/cs268-09-winter/notes/…。我会寻找一个实现漏斗算法的库,因为它不是微不足道的。

标签: javascript algorithm math geometry polygon


【解决方案1】:
  1. 对多边形进行三角剖分。
    • 如果您选择的 origin vertex 不是多边形顶点(即它是多边形内的一个点),请将该点作为 steiner 点 包含在三角剖分中。李>
  2. 根据三角剖分的顶点和受约束边(其中图形边权重是三角剖分边的长度)构建无向加权图。
    • 约束边是不位于多边形之外的边。
  3. 计算从原点到图中所有其他顶点的最短路径(使用DijkstraBellman-Ford算法)。从原点到顶点的路径距离是该顶点的 Z 值。
  4. 更新/创建另一个三角剖分网格,使用之前计算的 Z 值相同的顶点。
  5. 通过在三角形内/三角形之间插值计算每个像素的距离值(根据每个三角形顶点的 Z 值进行插值)。这很容易通过使用barycentric coordinates 完成。坐标的插值输出为您提供从原点位置到该坐标的距离。

对于下面的插图,我使用了 TinFour Java 库中的 NaturalNeighborInterpolator。它通过对三角剖分进行操作来简化插值步骤——我只需在每个像素坐标处调用插值器,最后用原始多边形掩盖输出(因为它有效地计算了多边形的凸包)。

说明性代码

图形和 Dijkstra 实现使用 JGraphT 库。

IncrementalTin tin = new IncrementalTin();
tin.add(listOfPolygonVertices); // triangulates upon insertion

Graph<Vertex, IQuadEdge> graph = new DefaultUndirectedWeightedGraph<>(IQuadEdge.class);

tin.edges().forEach(e -> {
    if (e.isConstrainedRegionInterior() || e.isConstrainedRegionBorder()) {
        graph.addVertex(e.getA());
        graph.addVertex(e.getB());
        graph.addEdge(e.getA(), e.getB(), e);
        graph.setEdgeWeight(e.getA(), e.getB(), e.getLength());
    }
});

DijkstraShortestPath<Vertex, IQuadEdge> shortestPaths = new DijkstraShortestPath<>(graph);
Vertex originVertex = tin.getNavigator().getNearestVertex(originX, originY);

var paths = shortestPaths.getPaths(originVertex);

IncrementalTin distanceMesh = new IncrementalTin();
for (Vertex v : graph.vertexSet()) {
    var d = paths.getWeight(v);
    distanceMesh.add(new Vertex(v.x, v.y, d)); // add vertices with Z to new tin
}

IInterpolatorOverTin interpolator = new NaturalNeighborInterpolator(distanceMesh);

for (int x = 0; x < width; x++) {
    for (int y = 0; y < height; y++) {
        double z = interpolator.interpolate(x, y, null);
        if (!Double.isNaN(z)) {
            pixels[y * width + x] = someColour;
        }
    }
}

更新:距离边界顶点

如果您只需要距离边界线,则可以放弃第 5 步。而是根据所需距离为每个三角形计算(如果适用)isoline。如果等值线穿过三角形(如下图所示),它将与三角形的两条边相交 - 在每个此类三角形的每对相交点之间绘制一条线段,即可获得距离边界。

为三角剖分中的每个约束三角形的每条边调用一个方法(如下所示)。如果距离等值线穿过三角形,您将得到该三角形的两个交点;否则没有。

/**
 * Compute isoline vertex (if applicable) for a triangle side given by two vertices
 */
Vertex isoVertex(Vertex a, Vertex b, double d) {
    Vertex min, max;

    if (a.getZ() > b.getZ()) {
        max = a;
        min = b;
    } else {
        max = b;
        min = a;
    }

    if (d > min.getZ() && d < max.getZ()) {
        double diff = max.getZ() - min.getZ();
        double numerator = d - min.getZ();

        double fract = numerator / diff;
        double xDiff = max.getX() - min.getX();
        double yDiff = max.getY() - min.getY();
        
        return new Vertex(min.getX() + fract * xDiff, min.getY() + fract * yDiff);
    }
    return null;
}

【讨论】:

  • 这看起来很有希望,谢谢。它可以处理多边形中的孔吗?
  • @Ant 是的!检查新图像。
  • 就是这样,感谢您的解决方案。一分钟后我会检查所有内容并标记为完成。
  • 是否有可能返回覆盖多边形的点列表(限制在原始边界内),就像在您的纯红色恐龙示例中一样,距离原点有一定距离? (而不是距离内的像素)。我快到了。感谢您的帮助。
  • @Ant 在这种情况下,首先细分三角剖分可能是个好主意,这样您就可以处理较小的三角形(要在受约束的形状内插入点;泊松盘采样会生成合适的点为此的分布集)。然后,您基本上想要在三角剖分中计算单个 isoline 而不是第 5 步。如果等值线的 3 条边之一具有给定距离的值,则等值线将穿过三角形(使用每对顶点的 z 值来确定是否是这种情况)。
猜你喜欢
  • 1970-01-01
  • 2010-10-28
  • 2021-07-20
  • 2012-06-14
  • 2020-06-17
  • 2016-08-26
  • 1970-01-01
  • 2012-11-02
  • 2022-08-06
相关资源
最近更新 更多