Как перевернуть многоугольник так, чтобы никакие два его ребра не лежали на одной прямой?

Вам дан простой многоугольник, определяемый точками в 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);
    }
}

person gvlasov    schedule 07.11.2014    source источник
comment
Вопросы, требующие помощи в выполнении домашнего задания, должны включать краткое изложение работы, проделанной вами для решения проблемы, и описание трудности, с которой вы столкнулись при ее решении.   -  person Ken White    schedule 08.11.2014
comment
@KenWhite Это не домашнее задание.   -  person gvlasov    schedule 08.11.2014
comment
Вы скопировали/вставили то, что выглядит как задание, попросили дать ответ и ничего не предоставили, чтобы показать, что вы приложили какие-либо усилия для решения проблемы. Это, безусловно, читается как домашнее задание, и ваше утверждение, что это не так, не меняет этого внешнего вида. Вы даже изложили это с точки зрения задания: Вам дано и для целей этой конкретной задачи я, что, конечно, звучит так, как будто это написал преподаватель.   -  person Ken White    schedule 08.11.2014
comment
@KenWhite Изменит ли это код? Должен ли я опубликовать ссылку на мой активно обновляемый репозиторий github с этим кодом?   -  person gvlasov    schedule 08.11.2014
comment
@KenWhite Если этого достаточно, чтобы доказать, что у меня минимальное понимание проблемы и т. Д., Я был бы признателен, если бы вы удалили свой ненужный закрытый голос.   -  person gvlasov    schedule 08.11.2014
comment
Извините, что спрашиваю вместо ответа, но какова ваша цель? Если вы хотите убедиться, что вы можете найти точку пересечения любых двух ребер и избежать вырождения, вы можете усугубить проблему, введя возмущения, потому что вычисление пересечений нестабильно. Можно больше контекста?   -  person Yves Daoust    schedule 08.11.2014
comment
@YvesDaoust Цель в том, что я хочу применить алгоритм скелетизации к многоугольнику: code.google.com/ p/campskeleton К сожалению, этот алгоритм иногда может не завершиться успешно из-за того, что два ребра параллельны. Я не вижу способа заранее определить, какие ребра будут проблемными, поэтому самое простое, что я мог придумать, это слегка потревожить все вершины многоугольника, чтобы его скелет не сильно изменился в своей форме. , но вырождений нет.   -  person gvlasov    schedule 08.11.2014
comment
Странно, алгоритм считается надежным. Как он может вырождаться с параллельными сторонами, в таком случае должны быть переплетены хотя бы две другие стороны, чтобы получился четырехугольник. Вы должны указать точное место в алгоритме, где что-то идет не так.   -  person Yves Daoust    schedule 08.11.2014
comment
@YvesDaoust Автор алгоритма описывает этот случай как открытую проблему: twak.blogspot.ru/2011/01/degeneracy-in-weighted-straight.html См. рисунки 21, 25, 26, 27.   -  person gvlasov    schedule 08.11.2014


Ответы (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). Или просто возмущать случайно.

Теперь следует не пуленепробиваемый шаг: построить возмущенные линии поддержки и пересечь их так, чтобы найти положения возмущенных вершин.

Это не является пуленепробиваемым, потому что вы можете случайно создать новые коллинеарные ребра таким образом, и лучше перезапустить всю процедуру для проверки. Если вы не получите решение после двух итераций, у вас могут быть проблемы...

person Yves Daoust    schedule 08.11.2014
comment
Это именно то, что я придумал с тех пор, как опубликовал вопрос. Да это вообще не пуленепробиваемый. Поразмыслив некоторое время, я пришел к выводу, что было бы гораздо проще и эффективнее просто случайным образом сдвигать вершины в пределах ±ε, а затем проверять, что у вас получилось, а затем снова случайным образом сдвигать те ребра, которые все еще кажутся коллинеарными. И затем сгенерируйте исключение, если эта последовательность действий не избавит вас от всех коллинеарных ребер за N итераций. Думаю, шансы получить исключение были бы астрономически малы. Или я ошибаюсь? - person gvlasov; 08.11.2014