简体   繁体   English

您如何扰动多边形,使其多边形的两个边都不在同一条线上?

[英]How do you perturb a polygon so no two of its edges lie on the same line?

You are given a simple polygon defined by points in R 2 . 您会得到一个由R 2中的点定义的简单多边形。 You can move a point by x and y axes by some small ε (eg 1e-4). 您可以通过x和y轴将点移动一些小的ε(例如1e-4)。 What is the algorithm to move the points to make sure that no two edges of the polygon lie exactly on the same line? 有什么算法可以移动这些点以确保多边形的两个边均不完全位于同一条线上?

"Being on the same line" is usually defined as having a small enough difference between angles of two lines, but for the purpose of this particular problem I only consider segments being on the same line if they have exactly 0 difference in their angles or line equations or however you define them. 通常将“在同一条线上”定义为两条线的角度之间具有足够小的差异,但是出于这个特定问题的目的,如果它们的角度或直线上的差异恰好为0,则我仅考虑它们在同一条线上方程,或者您定义它们。

EDIT: 编辑:

Here's some code. 这是一些代码。 It solves the problem only for axis-parallel edges. 它仅针对平行轴的边缘解决了该问题。

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);
    }
}

Hint: 暗示:

For every edge, compute some scalar direction parameter (such as the angle, and as you suggest can be other, but needs to be scalar). 对于每个边缘,计算一些标量方向参数(例如角度,并且建议您设置其他参数,但需要标量)。 This will take time O(N). 这将花费时间O(N)。

Sort all parameters so obtained, in time O(N Lg(N)). 对获得的所有参数进行排序,时间为O(N Lg(N))。

Find the repeated values in the list in O(N). 在O(N)中的列表中找到重复的值。

For every group of equal values, introduce the perturbations in a way to be sure that you don't create new coincidences (find the closest neighboring values and perturb every equal values with a different multiple of a fraction of the gap size; for example, 0.1, 0.2, 0.2, 0.2, 0.4 has a repetition of 0.2, and the closest gap is 0.1; so you could perturb as 0.1, 0.2-0.001, 0.2, 0.2+0.001, 0.4). 对于每组相等的值,以一种确保不创建新巧合的方式引入扰动(找到最接近的相邻值,并以间隙大小的几分之一的不同倍数扰动每个相等的值;例如, 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)。 Or just perturb randomly. 或者只是随机干扰。

Now comes a non-bulletproof step: build the perturbed support lines and intersect them so that you find the positions of the perturbed vertexes. 现在采取非防弹措施:构建受干扰的支撑线并将它们相交,以便找到受干扰的顶点的位置。

This is not bulletproof because you could accidentally create new collinear edges this way, and it is better to restart the whole procedure to check. 这不是防弹的,因为您可能会无意间以这种方式创建新的共线边,并且最好重新启动整个过程进行检查。 If you don't get a solution after two iterations, you might be in trouble... 如果两次迭代后都没有解决方案,则可能会遇到麻烦...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在JTextPane的同一行上显示两个DIV? - How do you display two DIV's on the same line in a JTextPane? 你如何旋转多边形? - How do you rotate a polygon? 如何通过比较同一行上的三个变量来返回布尔值? - How do you return a boolean by comparing three variables on the same line? 求出同一条直线上的最大点数 - Find the maximum number of points that lie on the same straight line 您如何修复此代码,以便如果用户输入大写或小写字母,只要它是正确的字母,他们就会被告知它的索引: - How do you fix this code so that if the user inputs an uppercase or lowercase letter, as long as its the right letter, they'll be told its index: 你如何比较java中相同泛型类型的两个值? - How do you compare two values of the same generic type in java? 您如何在JavaFX中从一条线到另一条线? - How do you do a line in JavaFX from one corner to its opposite one? 在Spring中如何处理两个同名的bean? - How do you handle two beans with the same name in Spring? 如何合并到while循环,以便获得与两个while循环相同的输出? - How do I combine to while loops so that I get the same output as I do with two while loops? 如何在JUNG中添加具有相同标签(但不同端点)的两条边? - How to add two edges having the same label (but different endpoints) in JUNG?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM