Алгоритм выпуклой оболочки для определения точек внутри треугольника - PullRequest
0 голосов
/ 03 ноября 2019

У меня есть бихроматическое множество S с красными и зелеными точками в общем положении плоскости (никакие три точки S не коллинеарны). Красный треугольник сделан так, что все его три вершины являются красными точками S. Зеленая точка p из S называется красной треугольником, если p лежит внутри этого треугольника.

Я должен создать алгоритм, который находит все зеленые точки внутри треугольников. example Я попытался создать свой собственный алгоритм, посмотрев немного из интернет-ресурсов, таких как: http://totologic.blogspot.com/2014/01/accurate-point-in-triangle-test.html

А вот мое «решение»:

green_inside = all_the_greens
for each triangle T in get_all_triangles()
  for each greenie G in green_inside
    if G in T
      remove G from green_inside

greenies_in_triangles = all_the_greens - green_inside

Я не знаюработает ли этот алгоритм. И его время выполнения - n^3. Кроме того, я не уверен, могу ли я просто сказать get_all_triangles. Этот алгоритм, который я должен создать, должен быть самым эффективным, который я могу придумать, поэтому, пожалуйста, любые другие решения или предложения приветствуются!

Ответы [ 2 ]

4 голосов
/ 03 ноября 2019

Если все возможные треугольники (все возможные три варианта выбора красных точек), Вы можете сделать это проще:

Create the convex hull (CH) of the red points.
Check each of the green points is in the CH or not.
    If it is, there is a red triangle that contains that point.
    If it is not, there is no red triangle that contains the point.

Создание выпуклой оболочки из N красных точек в O(N log(N)) и проверкаточка внутри выпуклого многоугольника может быть сделана в O(log(N)). Следовательно, если число зеленых точек равно M, общая сложность вышеуказанного алгоритма составляет O((M+N)log(N)).

0 голосов
/ 04 ноября 2019

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

Это превращает вопрос в:

Учитывая набор (зеленых) точек и набор треугольников, сообщите все точки, которые находятся внутрилюбой из треугольников.

Я надеюсь, что это то, что вы ищете.

Согласно закону Мёрфиса, внутри каждой маленькой проблемы скрывается куча больших проблем, изо всех сил пытающихся выбраться. ,Здесь первая большая проблема заключается в том, как проверить, находится ли точка внутри треугольника. Вики, гугл и т. Д. Показывают, что существуют различные варианты решения проблемы. Для данного вопроса я считаю подход Барицентрической системы координат хорошим (лучшим?) Кандидатом, учитывая, что мы должны повторно протестировать для каждой точки, и этот подход позволяет вычислить набор параметров для каждого треугольника, который затем повторно используется в каждомточечный тест.

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

  • наивный: #Tests = N * K (проверить каждую точку на соответствие всем треугольникам). (Я не могу понять, как O (N ^ 3) входит в это, но тогда автор вопроса не говорит, что такое N).

  • улучшено: попытаться найти способтолько для проверки точек против подмножества треугольников. Подмножеством являются треугольники, которые "вероятны": #Tests = K * N', где N', мы надеемся, меньше или равны N. K здесь не меняется - в конце концов нам нужно взглянуть на каждую точку.

Итак, я предлагаю алгоритм линии сканирования в качестве (потенциально) улучшенного решения: посмотрите на изображениев этом вопросе и представьте вертикальную линию, идущую слева направо по изображению, переходя к следующей зеленой точке, всякий раз, когда она движется.
Теперь, в зависимости от того, где находится эта вертикальная линия сканирования в данный момент, только треугольники, которые пересекаютсяс линией сканирования - кандидаты на тестирование попадания в точку, в которой расположена линия сканирования. Это наше подмножество треугольников, с которым мы хотим проверить точку.

Чтобы реализовать алгоритм линии развертки, нам нужно сначала выполнить некоторое переупорядочение.

  • сортировка всех зеленых точек по координате х в порядке возрастания. (O (K log K)).
  • отсортировать все вершины треугольника каждого треугольника в порядке возрастания x.
  • отсортировать все треугольники по координате x их самых левых точек. (O (N log N)).
    Then, for each green point P:
        * drop all of the remaining triangles which are entirely to the left of P.
        * select all of the remaining triangles, which start but do not end to the left of P. 
          (open triangles)
        * test P against each of the open triangles. 
          P is contained if it is contained in any of those open triangles.

Хотя он вырос немного дольше, чем я ожидал, вот моя реализация прототипа на любом любимом языке Haskell:


import Data.List
-- While colors are part of the question, we have no real use for them in the solution.
-- data Color = Red | Green deriving (Show,Eq)
-- data Vertex = Vertex { loc :: Point, col :: Color } deriving (Show,Eq)

data Point = Point { x :: Float, y :: Float } deriving (Show,Eq)
data Triangle = Triangle [Point] deriving (Show,Eq)
data BaryParms = BaryParms 
    { dt :: Float   -- the DeTerminant.
    , dy23 :: Float
    , dx32 :: Float
    , dy31 :: Float
    , dx13 :: Float
    , x3 :: Float
    , y3 :: Float
    } deriving (Show,Eq)

-- Get the list of points from a triangle. Of course that list is always of length 3.    
triPoints :: Triangle -> [Point]    
triPoints (Triangle ps) = ps

-- We do some sorting (of triangles and points) and this is our 
-- ordering (left to right on the x-coordinates of a point.)
leftToRight :: Point -> Point -> Ordering
leftToRight p1 p2 
    | x p1 < x p2 = LT
    | x p1 == x p2 = EQ
    | otherwise = GT

-- Shortcut function, since we use it a few times.
-- Left To Right Sort.    
ltrSort = sortBy leftToRight

-- rearrange the points of a triangle in left to right manner.
ltrTriangle :: Triangle -> Triangle
ltrTriangle (Triangle points) = Triangle $ ltrSort points

-- compute the bary coordinates for a point with respect to 
-- the bary parameters of a given triangle.
toBaryCoords :: BaryParms -> Point -> (Float,Float,Float)
toBaryCoords bp p =
    (l1,l2,l3)
    where
        l1 = (dy23 bp * (x p - x3 bp) + (dx32 bp * (y p - y3 bp))) / dt bp
        l2 = (dy31 bp * (x p - x3 bp) + (dx13 bp * (y p - y3 bp))) / dt bp
        l3 = 1 - l1 - l2

-- precompute some values we need for testing points against a triangle.        
triToBaryParms :: Triangle -> BaryParms
triToBaryParms (Triangle [r1,r2,r3]) =
    BaryParms 
    { 
        dt = det (x r1 - x r3) (x r2 - x r3) (y r1 - y r3) (y r2 - y r3), 
        dy23 = y r2 - y r3,
        dx32 = x r3 - x r2,
        dy31 = y r3 - y r1,
        dx13 = x r1 - x r3,
        x3 = x r3,
        y3 = y r3
    }
    where
        det x11 x12 x21 x22 = x11 * x22 - x12 * x21

-- test if a (green) point is contained in a given triangle.
triangleContainsPoint :: Point -> BaryParms -> Bool
triangleContainsPoint p parms  =    
    l1 >= 0.0 && l2 >= 0.0 && l3 >= 0.0
    where
        (l1,l2,l3) = toBaryCoords parms p

-- our improved (?) scan line algorithm.
reportGreensInTriangles :: [Point] -> [Triangle] -> [Point]
reportGreensInTriangles greens triangles =  
    scan sgreens stris []
    where 
        -- sort the green points left to right.
        sgreens :: [Point]
        sgreens = ltrSort greens
        -- sort the triangles left to right by their leftmost vertex.
        -- and compute the barycentric coordinate parameters.
        stris :: [(Triangle,BaryParms)]
        stris = 
            fmap (\t -> (t,triToBaryParms t))
            . sortBy (\t1 t2 -> leftToRight (head . triPoints $ t1) (head . triPoints $ t2)) 
            . fmap ltrTriangle
                $ triangles
        -- recursive iteration over each (green) point. It's tail recursive. 
        scan :: [Point] -> [(Triangle,BaryParms)] -> [Point] -> [Point]
        scan [] _ found = found  -- no more points to test
        scan (g:gs) remainingTris found =
            scan gs remainingTris' found'
            where
                found' = 
                    if any (triangleContainsPoint g . snd) open 
                    then g : found
                    else found
                inScope (Triangle [r1,_,r3],_) = x r1 <= x g && x r3 >= x g
                -- the list of open triangles
                open = takeWhile inScope remainingTris
                -- triangles not discarded yet.
                remainingTris' = dropWhile (\ (Triangle [_, _, r3]  ,_) -> x r3 < x g ) remainingTris


...