Jak obliczyć obszar o nieregularnym kształcie?

17

Mam obiekt pokoju zdefiniowany przez kolekcję zapętlonych segmentów linii, dla których muszę obliczyć powierzchnię. Klasy można opisać następująco (w pseudokodzie):

class Point {
    float x; 
    float y;
    ...
    float distanceFrom(Point p);
}

class Segment {
    Point start;
    Point end;
    ...
    float length();
}

class Room {
    List<Segment> walls;
    ...
    float area();
}

Ściany pokoju nigdy nie mogą się przecinać, ale w punktach końcowych segmentów, a wszelkie utworzone „pętle podrzędne” również zostaną rozdzielone na nowe pomieszczenie. Rozwiązanie nie musi być idealnie dokładne (dopuszczalny margines błędu 10%), a także nie jest obliczane bardzo często (<1 / s).


źródło
7
Bardziej sensowne byłoby zawarcie Roomlisty Points, a następnie pobranie segmentów przez połączenie każdego punktu razem, a następnie zapętlenie go z powrotem. W przeciwnym razie, przy obecnej konfiguracji, otrzymanie nieprawidłowych wartości (np. Niezamknięty pokój, pokój ze ścianą pośrodku itp.) Jest bardzo na wschód. To byłaby najlepsza opcja.
MCMastery
Inną opcją jest trójkąt górny i obliczanie powierzchni każdego trójkąta. Najtrudniejszą częścią jest triangulacja. Wykonalne, ale nie zawsze ładne. Odpowiedź na sznurowadło jest wciąż znacznie lepsza.
Draco18s nie ufa już
@MCMastery To rozwiązanie nie będzie działać, ponieważ wymaga, aby Rooms było zawsze kompletne, i może się tak nie zdarzyć, jeśli odtwarzacz zbuduje je Roomza pomocą Segments. Ponadto, funkcja zamkniętego pokoju jest łatwa do zdefiniowania (po prostu przejdź przez Segments i upewnij się, że tworzą pokój).

Odpowiedzi:

31

Możesz użyć wzoru sznurowadła Gaussa :

Musisz wziąć współrzędną x każdego punktu, pomnożyć je przez współrzędną y następnego punktu, a następnie odjąć współrzędną y bieżącego punktu pomnożoną przez współrzędną x następnego punktu od wyniku i dodać je do całkowitego obszaru. Po wykonaniu tego dla każdego punktu, zmniejsz o połowę łączną powierzchnię, aby uzyskać rzeczywistą powierzchnię wielokąta. Jeśli bieżący punkt jest ostatnim, następny jest pierwszy.

A = 0

for (i = 0; i < points.length; i++) do

    A += points[i].x * points[(i + 1) % points.length].y - points[i].y * points[(i + 1) % points.length].x

end

A /= 2
Bálint
źródło
2
Zawsze używałem tego do obliczania iloczynu krzyżowego dwóch wektorów, ale nigdy nie wiedziałem, że nazywa się to algorytmem sznurowadła
Sidar,
3
Zauważ, że można to rozszerzyć, aby obliczyć objętość nieregularnego obiektu 3D wykonanego z trójkątów, i można to uznać za trywialny przypadek podstawowego twierdzenia rachunku różniczkowego.
Dietrich Epp
5
Obszar tutaj jest podpisany. Przejdź przez punkty w innym kierunku, a finał Azostanie zanegowany. W zależności od celu A = |A|może być konieczne. Przy ujemnym numerze kierunkowym można znaleźć obszar na nieregularnym pączku, korzystając z wewnętrznej i zewnętrznej listy punktów (jeden w odwrotnej kolejności).
chux - Przywróć Monikę
6
Ponieważ oczywiście Gauss lub Euler ma na to formułę.
corsiKa
0

Możemy również zastosować metodę Monte Carlo.

Narysuj prostokąt wokół dowolnego kształtu. Weź równomiernie rozłożone źródło PRNG, np. mersenne twister, a następnie związano wynik za pomocą długości X, Y prostokąta za pomocą funkcji modulo. Policz nie. losowych punktów, które wylądują w twoim kształcie. Podziel przez całkowitą liczbę wygenerowanych punktów. Pomnóż ten iloraz przez pole prostokąta. Z każdą iteracją zbliżasz się do prawdziwego obszaru. Algorytm jest absurdalnie paralelizowalny i może być używany do obliczania „objętości” dowolnych kształtów wymiarowych, o ile można ustalić, czy współrzędna R ^ N mieści się w granicy R ^ N kształtu.

.

Tutaj ktoś używa tej metody znajdowania obszaru koła, a następnie używa go do obliczenia pi https://www.youtube.com/watch?v=VJTFfIqO4TU

FranG
źródło
2
-1: Nie chcesz używać modulo, aby uzyskać zasięg, chcesz zastosować równomierny rozkład lub inny rozkład, robiąc to modulo ma wiele problemów statystycznych.
user1997744,
12
Ta metoda może być korzystna, gdy nie mamy prostego wielokąta, a raczej jakiś domyślny kształt, którego obramowanie jest trudne do wyrażenia, jak fraktal lub metaball blob. W przypadku wielokąta, jak w pytaniu, wydaje się, że byłby on niepotrzebnie drogi.
DMGregory
Jak zauważył @DMGregory, nie tego szukałem. Myślę jednak, że zasługuje na +1, na wypadek, gdyby ktoś tego potrzebował.
To ciekawe, ale czy koszt testów włączenia nie stałby się zbyt wysoki? Tj. Jeśli masz kształt, który jest wystarczająco złożony, aby uzasadnić to podejście, czy testy włączenia również nie byłyby naprawdę drogie, więc nie chciałbyś robić ich mnóstwa? (zakładając wielokąty)
Mattia
Ok modulo jest rzeczywiście problematyczne, ale jest to proste rozwiązanie. To, co naprawdę otrzymujemy, to losowe P = 1/2 bity 0/1, więc otrzymujemy równomierny rozkład liczb, np. dla 3 bitów od 0 do 7. Wykonanie rand% 5, jeśli liczba losowa przyjmuje wartość 6 lub 7, zostaje zamapowana na 1 lub 2, skutecznie zwiększając częstotliwość 1,2, co powoduje, że rozkład nie jest jednolity. Aby tego uniknąć, potrzebujesz czegoś takiego jak automat stanów, który obraca mapowanie np. 6,7 odwzorowuje na 1,2, a następnie na 3,4, a następnie na 5,0 i trwa. Możemy też wyrzucić 6,7, gdy tylko się pojawią. W każdym razie jest to problem z implementacją biblioteki.
FranG
-1

Inne podejście: nie.

Zamiast:

while (Segments.Count > 3)
{
    Segment A = Segments[Segments.Count - 2];
    Segment B = Segments[Segments.Count - 1];
    Segment C = new Segment(B.End, A.Start);
    Triangle T = new Triangle(A, B, C);
    Segments[Segments.Count - 2] = C;
    Segments.RemoveAt(Segments.Count - 1);
    if (B is inside the new shape Segments)
        Area -= T.Area;
    else
        Area += T.Area;
}
Area += new Triangle(Segments[0], Segments[1], Segments[2]).Area;

Zasadniczo odetnij trójkąt. Obszar trójkąta jest prosty i dzięki temu zmniejszyliśmy liczbę pozostałych segmentów o jeden. Powtarzaj, aż pozostanie trójkąt.

Loren Pechtel
źródło
2
Formuła Sznurowadła Gaussa jest skrótem tego, który zmniejsza o połowę lub jedną trzecią liczbę obliczeń. Rozpracuj to.
Pieter Geerkens,