【问题标题】:How do you perturb a polygon so no two of its edges lie on the same line?你如何扰动一个多边形,使它的两条边都不在同一条线上?
【发布时间】:2014-11-07 21:36:31
【问题描述】:

给定一个由 R2 中的点定义的简单多边形。您可以将一个点按 x 轴和 y 轴移动一些小的 ε(例如 1e-4)。移动点以确保多边形的没有两条边完全位于同一条线上的算法是什么?

“在同一条线上”通常被定义为两条线的角度之间的差异足够小,但为了解决这个特定问题,我只考虑线段在同一条线上,如果它们的差异正好为 0角度或直线方程,或者您如何定义它们。

编辑:

这里有一些代码。它仅解决了轴平行边缘的问题。

package org.tendiwa.geometry;

import com.google.common.collect.ImmutableSet;
import org.jgrapht.UndirectedGraph;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Set;

public final class SameLineGraphEdgesPerturbations {
    private static Comparator<Segment2D> HORIZONTAL_COMPARATOR = (a, b) -> {
        assert a.start.y == a.end.y && b.start.y == b.end.y;
        double d = a.start.y - b.start.y;
        if (d < 0) {
            return -1;
        }
        if (d > 0) {
            return 1;
        }
        return 0;
    };
    private static Comparator<Segment2D> VERTICAL_COMPARATOR = (a, b) -> {
        assert a.start.x == a.end.x && b.start.x == b.end.x;
        double d = a.start.x - b.start.x;
        if (d < 0) {
            return -1;
        }
        if (d > 0) {
            return 1;
        }
        return 0;
    };

    /**
     * Checks if some of graph's edges are segments of the same line, and perturbs vertices and edges of this graph
     * so it contains no such segments.
     * <p>
     * This class is designed to work with graphs that represent simple polygons. You can use it with other classes
     * of graphs, but that probably won't be useful.
     * 
     *
     * @param graph
     *  A planar graph to be mutated.
     */
    public static void perturbIfHasSameLineEdges(UndirectedGraph<Point2D, Segment2D> graph, double magnitude) {
        ArrayList<Segment2D> verticalEdges = new ArrayList<>(graph.edgeSet().size());
        ArrayList<Segment2D> horizontalEdges = new ArrayList<>(graph.edgeSet().size());
        for (Segment2D edge : graph.edgeSet()) {
            if (edge.start.x == edge.end.x) {
                verticalEdges.add(edge);
            } else if (edge.start.y == edge.end.y) {
                horizontalEdges.add(edge);
            }
        }
        verticalEdges.sort(VERTICAL_COMPARATOR);
        horizontalEdges.sort(HORIZONTAL_COMPARATOR);
        /*
         The algorithm is the following:
         For each axis-parallel edge in a list of edges sorted by static coordinate,
         perturb its start if the next edge in list has the same static coordinate (i.e., lies on the same line).
         That way if we have N same line axis-parallel edges (placed consecutively in an array because it is sorted),
         N-1 of those will be perturbed, except for the last one (because there is no next edge for the last one).
         Perturbing the last one is not necessary because bu perturbing other ones the last one becomes non-parallel
         to each of them.
          */
        int size = verticalEdges.size() - 1;
        for (int i = 0; i < size; i++) {
            Point2D vertex = verticalEdges.get(i).start; // .end would be fine too
            if (vertex.x == verticalEdges.get(i + 1).start.x) {
                perturbVertexAndItsEdges(vertex, graph, magnitude);
            }
        }
        size = horizontalEdges.size() - 1;
        for (int i = 0; i < size; i++) {
            Point2D vertex = horizontalEdges.get(i).start; // .end would be fine too
            if (vertex.y == horizontalEdges.get(i + 1).start.y) {
                if (!graph.containsVertex(vertex)) {
                    // Same edge could already be perturbed in a loop over vertical edges.
                    continue;
                }
                perturbVertexAndItsEdges(vertex, graph, magnitude);
            }
        }
    }

    private static void perturbVertexAndItsEdges(
        Point2D vertex,
        UndirectedGraph<Point2D, Segment2D> graph,
        double magnitude
    ) {
        Set<Segment2D> edges = ImmutableSet.copyOf(graph.edgesOf(vertex));
        assert edges.size() == 2 : edges.size();
        // We move by both axes so both vertical and
        // horizontal edges will become not on the same line
        // with those with which they were on the same line.
        Point2D newVertex = vertex.moveBy(magnitude, magnitude);
        graph.addVertex(newVertex);
        for (Segment2D edge : edges) {
            boolean removed = graph.removeEdge(edge);
            assert removed;
            // It should be .end, not .start, because in perturbIfHasSameLineEdges we used
            // vertex = edges.get(i).start
            if (edge.start == vertex) {
                graph.addEdge(newVertex, edge.end);
            } else {
                assert edge.end == vertex;
                graph.addEdge(newVertex, edge.start);
            }
        }
        assert graph.degreeOf(vertex) == 0 : graph.degreeOf(vertex);
        graph.removeVertex(vertex);
    }
}

【问题讨论】:

  • 请求家庭作业帮助的问题必须包括您迄今为止为解决问题所做的工作的总结,以及您在解决问题时遇到的困难的描述。
  • @KenWhite 这不是作业。
  • 您复制/粘贴了看似是作业的内容,要求您提供答案,但没有提供任何内容表明您已为解决问题付出了任何努力。它确实读起来就像一个家庭作业,你说它不是并不会改变这种外观。你甚至从作业的角度说:“你是给定的”和“为了这个特定问题我的目的”,这听起来肯定是一位教师写的。
  • 很抱歉问这个而不是回答,但你的目的是什么?如果您想要确保可以找到任何两条边的交点并避免退化,那么您可能会通过引入扰动来使问题恶化,因为计算交点是不稳定的。我们可以有更多的上下文吗?
  • 奇怪,算法被认为是健壮的。它如何退化为平行边,在这种情况下,至少应该有两个其他边形成一个四边形。您应该查明算法中出错的确切位置。

标签: java polygon computational-geometry floating-point-precision


【解决方案1】:

提示:

对于每条边,计算一些标量方向参数(例如角度,并且您建议可以是其他参数,但需要是标量)。这需要时间 O(N)。

对如此获得的所有参数进行排序,时间为 O(N Lg(N))。

在 O(N) 的列表中找到重复的值。

对于每组相等的值,引入扰动以确保不会产生新的巧合(找到最接近的相邻值并以间隙大小的分数的不同倍数扰动每个相等的值;例如,0.1, 0.2, 0.2, 0.2, 0.4 的重复次数为 0.2,最近的间隙为 0.1;因此您可以扰动为 0.1, 0.2-0.001, 0.2, 0.2+0.001, 0.4)。或者只是随机扰动。

现在是一个非防弹步骤:构建扰动支撑线并将它们相交,以便找到扰动顶点的位置。

这不是万无一失的,因为这样可能会意外创建新的共线边,最好重新启动整个过程进行检查。如果两次迭代都没有得到解决方案,你可能会遇到麻烦......

【讨论】:

  • 这正是我发布问题后的想法。是的,这根本不是防弹的。经过一段时间的思考,我认为在±ε内随机移动顶点,然后检查你所拥有的,然后再次随机移动那些仍然看起来共线的边会更简单和有效。如果这一系列动作没有消除N 迭代中的所有共线边,则抛出异常。我想,获得例外的机会会非常低。还是我错了?
猜你喜欢
  • 1970-01-01
  • 2021-07-05
  • 2015-03-17
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多