На этом шаге мы рассмотрим особенности заполнения полигонов.
Контур полигона определяется вершинами, которые соединены отрезками прямых (рисунок 1). Это - векторная форма задания фигуры.
Рис.1. Пример полигона
Рассмотрим один из наиболее популярных алгоритмов заполнения полигона. Его основная идея - закрашивание фигуры отрезками прямых линий. Наиболее удобно использовать горизонтали. Алгоритм представляет собою цикл вдоль оси у, в ходе этого цикла выполняется поиск точек сечения линии контура с соответствующими горизонталями. Этот алгоритм получил название XY [1]:
1. Найти min{уi} и max{уi} среди всех вершин Pi. 2. Выполнить цикл по у от у = min до у = max. { 3. Нахождение точек пересечения всех отрезков контура с горизонталью у. Координаты xj точек сечения записать в массив. 4. Сортировка массива {xj} по возрастанию х. 5. Вывод горизонтальных отрезков с координатами (x0, у) - (x1, у) (х2, у) - (х3, у) (х2k, у) - (x2k+1, у) Каждый отрезок выводится цветом заполнения }
В этом алгоритме использовано свойство топологии контура фигуры. Оно состоит в том, что любая прямая линия пересекает любой замкнутый контур четное количество раз (рисунок 2).
Рис.2. Заполнение полигона
Для выпуклых фигур точек пересечения с любой прямой всегда две. Таким образом, на шаге 3 этого алгоритма в массив {xj} всегда должно записываться парное число точек сечения.
При нахождении точек пересечения горизонтали с контуром необходимо принимать во внимание особые точки. Если горизонталь имеет координату (у), совпадающую с уi, вершины Pi, тогда надлежит анализировать то, как горизонталь проходит через вершину. Если горизонталь при этом пересекает контур, как, например, в вершинах Р0 или Р4, то в массив записывается одна точка сечения. Если горизонталь касается вершины контура (в этом случае вершина соответствует локальному минимуму или максимуму как, например, в вершинах Р1, Р2, Р3 или Р5), тогда координата точки касания или не записывается, или записывается в массив два раза. Это условие четности количества точек пересечения, хранящихся в массиве {xj}.
Процедура определения точек пересечения контура с горизонтальной разверткой, учитывая анализ на локальный максимум, может быть достаточно сложной. Это замедляет работу. Радикальным решением для упрощения поиска точек сечения может быть смещение координат вершин контура или горизонталей заполнения таким образом, чтобы ни одна горизонталь не попала в вершину. Смещение можно выполнять различными способами, например, ввести в растр дробные координаты для горизонталей: ymin+ 0.5, ymin+ 1.5, ..., ymax- 0.5.
Но такое упрощение процедуры нахождения точек пересечения приводит к искажению формы полигона.
Для определения координат (х) точек пересечения для каждой горизонтали необходимо перебирать все n отрезков контура. Координата пересечения отрезка pi- pk с горизонталью у равна
х = хi + (yk- y) (хk - хi) / (уk- уi).
Количество тактов работы этого алгоритма:
Nтактов ≈ (ymax - ymin) Nгор ,
Оценим величину Nгор как пропорциональную числу вершин
Nгор ≈ k * n,
Возможны модификации приведенного алгоритма для ускорения его работы. Например, можно принять во внимание то, что каждая горизонталь в большинстве случаев пересекает небольшое количество ребер контура. Поэтому если при поиске точек сечения делать предварительный отбор ребер, которые находятся вокруг каждой горизонтали, то можно добиться уменьшения количества тактов работы с Nгор= k * n до k * np, где np - число отобранных ребер. Например, разделим диапазон ymin- ymax пополам. Если для диапазона от ymin до yср составить список отрезков (или вершин), которые попадают в этот диапазон, то в этот список будет включено приблизительно вдвое меньшее количество, чем для всего диапазона от ymin до ymax. Почему приблизительно - ибо это зависит от формы полигона. Таким образом, при работе алгоритма для каждой горизонтали в диапазоне ymin до уср уже нужно не k * n тактов, а ∼ k * n/2. Аналогично для диапазона уср- ymax также можно составить список ребер, который также будет почти вдвое меньшим. Если принять, что подсчеты для каждой горизонтали теперь выполняются за вдвое меньшее количество тактов, а именно за (Nгор/2), то общее число тактов:
Nтактов ≈ (ymax - ymin) Nгор/2 + Nдоп ,
Такой способ повышения быстродействия эффективен для большого количества вершин. Контур можно делить не пополам, а на более мелкие части - соответственно повышается скорость.
Приведенные на предыдущих шагах алгоритмы заполнения могут быть использованы не только для рисования фигур. На основе алгоритмов заполнения могут быть разработаны алгоритмы для других целей. Например, для определения площади фигуры, если считать площадь пропорциональной количеству пикселей заполнения. Или, например, алгоритм для поиска объектов по внутренней точке - эта операция часто используется в векторных графических редакторах.
Со следующего шага мы начнем знакомиться со стилем линии.