Как се смущава многоъгълник, така че два от ръбовете му да не лежат на една и съща линия?

Даден ви е прост многоъгълник, определен от точки в 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; Или просто смущавайте произволно.

Сега идва стъпка, която не е бронирана: изградете смутените опорни линии и ги пресечете, така че да намерите позициите на смутените върхове.

Това не е бронирано, защото можете случайно да създадете нови колинеарни ръбове по този начин и е по-добре да рестартирате цялата процедура, за да проверите. Ако не получите решение след две итерации, може да имате проблеми...

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