[英]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.