Как обработать все исключительные случаи алгоритма заливки XOR?
Занимался реализацией алгоритма XOR для заливки многоугольников (в растре). Предполагается, что точки растра имеют целые координаты. Итак, что я уже сделал:
- Алгоритм Брезенхема для растеризации отрезков
- Исключение вершин, которые являются локальными экстремумами
- Игнорирование граней, параллельных оси Ox
Поскольку для воспроизведения работы алгоритма потребуется полная версия исходного кода, прикрепляю ссылку на Pastebin.
Возникла проблема, например, при такой картинке:
Желтым отмечены проигнорированные при работе алгоритма точки растра. Однако, этого оказалось недостаточно. Некоторые грани растеризируются очень "плохо", с двумя точками подряд. Затем впритык проходит еще одна грань -- вот уже три точки подряд. Естественно, к такому алгоритм просто не готов.
Вопрос заключается в следующем: как заставить алгоритм корректно работать во всех возможных случаях?
# Необходимо различать каждую грань:
# У каждой грани свой набор точек растра
# Если встречены две точки подряд, то
# а) Это две точки одной грани -> не переключать режим
# б) Это две точки из двух разных граней -> переключить режим
# В случае (б) заливка заденет грань, но кому какое дело)0
# Список создан только для реализации "пошаговой отрисовки"
filling_points = []
# Идея алгоритма: идти по точкам сверху вниз, слева направо
# Если встретили границу -- переключить режим закраски (например, начинаем закрашивать)
# Встретили снова -- переключаем (например, заканчиваем закрашивать)
# .whichEdge() возвращает номер границы, если такая точка отрисована,
# либо -1, если такой точки на экране нет (незакрашенный "пиксель")
def xor_filling(plg: Polygon):
global filling_points
plotMode = -1
# Размер "экрана": 61x61 или около того
for line in range(30, -31, -1):
for pixel in range(-30, 31):
if plotMode == 1:
filling_points.append([pixel, line])
np_points = np.array(filling_points)
# Рисуем следующий кадр
snap_next_frame(polygon, good_polygon, rasterized_polygon, bad_points, np_points)
print('plotting point: ', pixel, line)
# Находим номер грани, которой принадлежит текущая точка
currentPointEdge = plg.whichEdge([pixel, line])
# Аналогично для следующей точки
nextPointEdge = plg.whichEdge([pixel + 1, line])
# Если встретили границу в виде точки, за которой ничего не следует..
if currentPointEdge != -1 and nextPointEdge == -1:
# Переключаем режим
plotMode *= -1
# Иначе, если две точки идут подряд..
elif currentPointEdge != -1 and nextPointEdge != -1:
# Если это точки из разных граней..
if currentPointEdge != nextPointEdge:
plotMode *= -1
# Выключаем режим заполнения перед переходом на следующую строку
plotMode = -1
print('All done! Rendering...')
Можно заметить, что попытка обработать исключительный случай с двумя точками подряд уже предпринималась путем различения граней ("а вот эти две точки -- принадлежат каким граням?"). Но оказалось, что это не более, чем комбайн из костылей, решающий проблему с одним набором тестовых данных, но не с другими.
Формат ввода (из файла input.txt):
p1_x p1_y
p2_x p2_y
p3_x p3_y
... и так далее ...
pN_x pN_y
где pN_x, pN_y -- координаты вершин многоугольника.
Некоторые наборы тестовых данных:
# Поломанное сердечко (с ним все плохо)
0 0
2 4
8 0
0 -8
-8 0
-4 2
# Сердечко
4 10
9 7
14 10
18 5
9 0
0 5
# Прямоугольник
0 0
0 4
4 4
4 0
# Буква М
0 0
10 10
14 10
14 4
20 10
24 10
24 0
20 0
20 6
14 0
10 0
10 6
4 0
# Буква С
0 0
10 0
10 6
6 6
6 2
2 2
2 20
6 20
6 16
10 16
10 22
0 22