Как обработать все исключительные случаи алгоритма заливки XOR?

Рейтинг: 1Ответов: 0Опубликовано: 24.04.2023

Занимался реализацией алгоритма 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

Ответы

Ответов пока нет.