КГ_3глава (1024106), страница 3
Текст из файла (страница 3)
Рис. 3.12. Количество циклов OneStep
Алгоритм закрашивания линиями. Данный алгоритм получил широкое распространение в компьютерной графике. От приведенного ранее простейшего рекурсивного алгоритма он отличается тем, что на каждом шаге закрашивания рисуется горизонтальная линия, которая размещается между пикселами контура. Алгоритм рекурсивный, но поскольку вызов функции осуществляется для линии, а не для каждого отдельного пиксела, то количество вложенных вызовов уменьшается пропорционально длине линии. Это уменьшает нагрузку на стековую память компьютера.
Приведем запись алгоритма на языке C++. Этот пример взят из [32], незначительно усовершенствован и переработан для использования в среде Windows.
В программах функция LineFiii используется таким образом:
Пример работы алгоритма закрашивания линиями приведен на рис. 3.13.
Рис. 3.13. Количество циклов LineFiii
Так же, но немного быстрее, работает функция FloodFiii API Windows. Разница в быстродействии обусловлена тем, что для LineFiii мы использовали стандартные функции для интерфейса прикладных программ, a FloodFiii оптимизирована на системном уровне.
Алгоритмы заполнения, которые используют математическое описание контура
Математическим описанием контура фигуры может служить уравнение у = f(x) для окружности, эллипса или другой кривой. Для многоугольника (полигона) контур задается множеством координат вершин (хi, уi). Возможны и другие формы описания контура. В любом случае алгоритмы данного класса не предусматривают обязательное предварительное создание пикселов контура растра — контур может совсем не выводиться в растр. Рассмотрим некоторые из подобных алгоритмов заполнения.
Заполнение прямоугольников. Среди всех фигур прямоугольник заполнять наиболее просто. Если прямоугольник задан координатами противоположных углов, например, левого верхнего (x1, у1) и правого нижнего {х2, у2), тогда алгоритм может заключаться в последовательном рисовании горизонтальных линий заданного цвета (рис. 3.14).
Рис. 3.14. Заполнение прямоугольника
Заполнение круга. Для заполнения круга можно использовать алгоритм вывода контура, который мы уже рассмотрели в разд. 3.2. В процессе выполнения этого алгоритма последовательно вычисляются координаты пикселов контура в границах одного октанта. Для заполнения надлежит выводить горизонтали, которые соединяют пары точек на контуре, расположенные симметрично относительно оси y (рис. 3.15).
Так же может быть построен и алгоритм заполнения эллипса.
Рис. 3.15. Заполнение круга
Заполнение полигонов, контур полигона определяется вершинами, которые соединены отрезками прямых (рис. 3.16). Это— векторная форма задания
фигуры.
Рис. 3.16. Пример полигона
Рассмотрим один из наиболее популярных алгоритмов заполнения полигона. Его основная идея — закрашивание фигуры отрезками прямых линий. Наиболее удобно использовать горизонтали. Алгоритм представляет собою цикл вдоль оси у, в ходе этого цикла выполняется поиск точек сечения линии контура с соответствующими горизонталями. Этот алгоритм получил название XY [33]:
1. Найти min{y±} и юах{у±} среди всех вершин Pi.
2. Выполнить цикл по у от у = min до у = max .
{
3. Нахождение точек пересечения всех отрезков контура
с горизонталью у. Координаты xj точек сечения записать в массив.
4. Сортировка массива { xj } по возрастанию x.
В этом алгоритме использовано свойство топологии контура фигуры. Оно состоит в том, что любая прямая линия пересекает любой замкнутый контур четное количество раз (рис. 3.17). Для выпуклых фигур точек пересечения с любой прямой всегда две. Таким образом, на шаге 3 этого алгоритма в массив { Xj } всегда должно записываться парное число точек сечения.
Рис. 3.17. Заполнение полигона
При нахождении точек пересечения горизонтали с контуром необходимо принимать во внимание особые точки. Если горизонталь имеет координату (у), совпадающую с уi вершины Р I тогда надлежит анализировать то, как горизонталь проходит через вершину. Если горизонталь при этом пересекает контур, как, например, в вершинах Ро или Р4, то в массив записывается одна точка сечения. Если горизонталь касается вершины контура (в этом случае вершина соответствует локальному минимуму или максимуму как, например, в вершинах P1 , P2, , Р3 или Р5), тогда координата точки касания или не записывается, или записывается в массив два раза. Это условие четности количества точек пересечения, хранящихся в массиве { xj }.
Процедура определения точек пересечения контура с горизонтальной разверткой, учитывая анализ на локальный максимум, может быть достаточно сложной. Это замедляет работу. Радикальным решением для упрощения поиска точек сечения может быть смещение координат вершин контура или горизонталей заполнения таким образом, чтобы ни одна горизонталь не попала в вершину. Смещение можно выполнять различными способами, например, ввести в растр дробные координаты для горизонталей: ymin + 0.5,
ymin + 1.5, .... . ymax - 0,5.
Но такое упрощение процедуры нахождения точек пересечения приводит к искажению формы полигона.
Для определения координат (х) точек пересечения для каждой горизонтали необходимо перебирать все п отрезков контура. Координата пересечения отрезка p i —рк с горизонталью у равна
Количество тактов работы этого алгоритма:
где ymax , ymin — диапазон координат у, Nгор — число тактов, необходимых
для одной горизонтали.
Оценим величину Nsop как пропорциональную числу вершин
где к — коэффициент пропорциональности, п — число вершин полигона. Возможны модификации приведенного алгоритма для ускорения его работы. Например, можно принять во внимание то, что каждая горизонталь в большинстве случаев пересекает небольшое количество ребер контура. Поэтому если при поиске точек сечения делать предварительный отбор ребер, которые находятся вокруг каждой горизонтали, то можно добиться уменьшения количества тактов работы с Nгор = к п до к пр, где пр — число отобранных ребер. Например, разделим диапазон ymin - ymax пополам. Если для диапазона от ymin до уср составить список отрезков (или вершин), которые попадают в этот диапазон, то в этот список будет включено приблизительно вдвое меньшее количество, чем для всего диапазона от ymin до утax. Почему приблизительно — ибо это зависит от формы полигона. Таким образом, при работе алгоритма для каждой горизонтали в диапазоне ymin до уср уже нужно не кп тактов, а ~ к -п/2.
Аналогично для диапазона уср – ymax также можно составить список ребер, который также будет почти вдвое меньшим. Если принять, что подсчеты для каждой горизонтали теперь выполняются за вдвое меньшее количество тактов, а именно за (Nгор /2), то общее число тактов:
где Ndon — количество тактов для создания списка ребер.
Такой способ повышения быстродействия эффективен для большого количества вершин. Контур можно делить не пополам, а на более мелкие части — соответственно повышается скорость.
Приведенные выше алгоритмы заполнения могут быть использованы не только для рисования фигур. На основе алгоритмов заполнения могут быть разработаны алгоритмы для других целей. Например, для определения площади фигуры, если считать площадь пропорциональной количеству пикселов заполнения. Или, например, алгоритм для поиска объектов по внутренней точке — эта операция часто используется в векторных графических редакторах.
3.6. Стиль линии. Перо
Что общее и что разное у объектов, изображенных на рис. 3.18?
Рис. 3.18. Примеры стилей линий
Общее — линия оси, описывающая каждый из восьми объектов. Разное — элементы вдоль линии оси. Для описания различных по виду изображений на основе линий используют термин стиль линий или перо. Термин перо иногда делает более понятной суть алгоритма вывода линий для некоторых стилей — в особенности для толстых линий. Например, если для тонкой непрерывной линии перо соответствует одному пикселу, то для толстых линий перо можно представить себе как фигуру или отрезок линии, который скользит вдоль оси линии, оставляя за собой след (табл. 3.1).
Таблица 3.1
Алгоритмы вывода толстой линии
Взяв за основу любой алгоритм вывода обычных тонких линий (например, алгоритм Брезенхэма), запишем его в следующем обобщенном виде:
Вывод пиксела (х, у)
Можно представить себе такой алгоритм, как цикл, в котором определяются координаты (х,у) каждого пиксела. Этот алгоритм можно модифицировать для вывода толстой линии следующим образом: