Проблема
Я пытаюсь понять и внедрить версию Forster-Overfelt алгоритм обрезки полигонов Грейнера-Хормана. Я прочитал другой пост Stackoverflow о разъяснении этого алгоритма, но я все еще не могу Кажется, это не работает.
Я знаю, что что-то не так, потому что он создает неправильное пересечение двух полигонов даже для простого примера, в котором нет вырождений:
subjpoly = [(0,0),(6,0),(6,6),(0,6),(0,0)]
clippoly = [(1,4),(3,8),(5,4),(5,10),(1,10),(1,4)]
который производит пересечение:
[ [(5.0, 6.0), (4.0, 6.0), (5, 4), (5.0, 6.0)],
[(1.0, 6.0), (2.0, 6.0), (4.0, 6.0)] ]
Визуально это выглядит так:
Таким образом, этот вопрос касается не конкретного фрагмента кода или синтаксиса языка, а понимания алгоритма и воплощения его в псевдокоде. Заранее спасибо!
Алгоритм
Представленный здесь алгоритм основан непосредственно на алгоритме, описанном в разделе 4.2 статьи Форстера-Оберфельта, и должен имитировать его, но, очевидно, я что-то упускаю, что дает неверные результаты.
Часть 1:
Начните с зацикливания subj и clip и пометьте расположение каждой вершины как «внутри», «вне» или «на» другом полигоне.
for s in subj.iter():
s.loc = testLocation(s, clip)
for c in clip.iter():
c.loc = testLocation(c, subj)
Часть 2:
Продолжайте зацикливать точки пересечения многоугольника subj.
for s in subj.iter():
if s.intersect:
Подчасть 2.1:
Обработайте каждое пересечение subj, либо сняв пометку с него как пересечение, либо пометив его как вход или выход, и сделайте то же самое для соседней точки пересечения. ПРИМЕЧАНИЕ. Алгоритм, описанный в статье, объясняет только, как пометить основной многоугольник объекта, но никогда не говорит, как пометить соседний полигон, поэтому здесь я просто предполагаю, что оба полигона отмечены с использованием одного и того же набора правил.
mark(s)
mark(s.neighbour)
Где правила обработки mark() определены как:
def mark(s):
curlocs = (s.prev.loc,s.next.loc)
neighlocs = (s.neighbour.prev.loc,s.neighbour.next.loc)
# in in
if curlocs == ("in","in"):
if neighlocs == ("in","in")\
or neighlocs == ("out","out")\
or neighlocs == ("on","on"):
s.intersect = False
else:
s.entry = True
# out out
elif curlocs == ("out","out"):
if neighlocs == ("in","in")\
or neighlocs == ("out","out")\
or neighlocs == ("on","on"):
s.intersect = False
else:
s.entry = False
# on on
elif curlocs == ("on","on"):
if neighlocs == ("in","in")\
or neighlocs == ("out","out")\
or neighlocs == ("on","on"):
s.intersect = False
else:
# label opposite of neighbour
# NOTE: this is not specified in the article,
# but one cannot take the opposite of the neighbour's entry flag
# if the neighbour hasn't been marked yet,
# thus the decision to mark the neighbour first
mark(s.neighbour)
s.entry = not s.neighbour
# partial exit
elif curlocs == ("in","on")\
or curlocs == ("on","out"):
s.entry = False
# partial entry
elif curlocs == ("on","in")\
or curlocs == ("out","on"):
s.entry = True
# normal exit
elif curlocs == ("in","out"):
s.entry = False
# normal entry
elif curlocs == ("out","in"):
s.entry = True
Подчасть 2.2:
Наконец, убедитесь, что curr и сосед не имеют одинаковых входных и выходных флагов; если они отключают свой флаг пересечения и меняют флаги местоположения.
if s.entry and s.neighbour.entry:
s.intersect = False
s.neighbour.intersect = False
s.loc = "in"
elif not s.entry and not s.neighbour.entry:
s.intersect = False
s.neighbour.intersect = False
s.loc = "out"
Бонусный вопрос
Бонусный вопрос заключается в том, как заставить этот алгоритм поддерживать как операции объединения, так и операции пересечения, поскольку поддержка объединения исходным алгоритмом Грейнера заключалась в простом инвертировании начального флага входа/выхода, но этот алгоритм Форстера не использует такой флаг?