Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Архив1 / docx57 / отчет №3 кг

.docx
Скачиваний:
31
Добавлен:
01.08.2013
Размер:
36.91 Кб
Скачать

Министерство образования и науки Российской Федерации

Федеральное агентство по образованию

Поволжский государственный технологический университет

Кафедра ИВС

Отчет по лабораторной работе №3

МЕТОДЫ ЗАКРАСКИ ОБЪЕКТОВ

По дисциплине

«Компьютерная графика»

Выполнили:

студенты группы ВМ-31

_Мочалов О.М. ___________ _______

(ФИО) (подпись) (дата)

Проверил:

преподаватель каф. ИВС

_Морохин Д. В.__ ___________ _______

(ФИО) (подпись) (дата)

Йошкар-Ола,

2012 г.

Цель работы: изучение методов заполнения области определенным цветом, приобретение навыков использования алгоритмов при составлении графических программ.

теоретические сведения

В большинстве приложений используется одно из существенных достоинств растровых устройств – возможность заполнения областей экрана.

Существуют две разновидности заполнения:

 первая, связанная как с интерактивной работой, так и с программным синтезом изображения, служит для заполнения внутренней части многоугольника, заданного координатами его вершин;

 вторая, связанная в первую очередь с интерактивной работой, служит для заливки области, которая либо очерчена границей с кодом пикселя, отличающимся от кодов любых пикселей внутри области, либо закрашена пикселями с заданным кодом;

Простейший способ заполнения многоугольника, заданного координатами вершин, заключается в определении, принадлежит ли текущий пиксель внутренней части многоугольника. Если принадлежит, то пиксель заносится.

Определить принадлежность пикселя многоугольнику можно, например, подсчетом суммарного угла с вершиной на пикселе при обходе контура многоугольника. Если пиксель внутри, то угол будет равен 360, если вне - 0 (рис. 3.1).

Рис. 3.1: Определение принадлежности пикселя многоугольнику:

а) Суммарный угол равен AoB+BoC+CoD+DoA=360˚; b) Угол AoB < 0, так как обход производится по часовой стрелке, и равен сумме BoC+CoD+DoA

Вычисление принадлежности должно производиться для всех пикселей экрана, и так как большинство пикселей, скорее всего, находится вне многоугольников, то данный способ слишком расточителен. Объем лишних вычислений в некоторых случаях можно сократить использованием прямоугольной оболочки – минимального прямоугольника, объемлющего интересующий объект, но все равно вычислений будет много. Другой метод определения принадлежности точки внутренней части многоугольника будет рассмотрен ниже при изучении отсечения отрезков по алгоритму Кируса-Бека.

Построчное заполнение

Реально используются алгоритмы построчного заполнения, основанные на том, что соседние пиксели в строке, скорее всего, одинаковы и меняются только там, где строка пересекается с ребром многоугольника. Это называется когерентностью растровых строк (строки сканирования Yi, Yi+1, Yi+2 на рис. 3.2). При этом достаточно определить X-координаты пересечений строк сканирования с ребрами. Пары отсортированных точек пересечения задают интервалы заливки.

Рис. 3.2. Построчная закраска многоугольника

Кроме того, если какие-либо ребра пересекались i-й строкой, то они, скорее всего, будут пересекаться также и строкой i+1 (строки сканирования Yi и Yi+1 на рис. 3.2). Это называется когерентностью ребер. При переходе к новой строке легко вычислить новую X-координату точки пересечения ребра, используя X-координату старой точки пересечения и тангенс угла наклона ребра:

Xi+1 = Xi + 1/k

(тангенс угла наклона ребра k = dy/dx; так как dy = 1, то 1/k = dx).

Смена же количества интервалов заливки происходит только тогда, когда в строке сканирования появляется вершина.

Учет когерентности строк и ребер позволяет построить для заполнения многоугольников различные высокоэффективные алгоритмы построчного сканирования. Для каждой строки сканирования рассматриваются только те ребра, которые пересекают строку. Они задаются списком активных ребер (САР). При переходе к следующей строке для пересекаемых ребер перевычисляются X-координаты пересечений. При появлении в строке сканирования вершин производится перестройка САР. Ребра, которые перестали пересекаться, удаляются из САР, а все новые ребра, пересекаемые строкой, заносятся в него.

Общая схема алгоритма, динамически формирующего список активных ребер и заполняющего многоугольник снизу-вверх, следующая:

  1. Подготовить служебные целочисленные массивы Y-координат вершин и номеров вершин.

  2. Совместно отсортировать Y-координаты по возрастанию и массив номеров вершин для того, чтобы можно было определить исходный номер вершины.

  3. Определить пределы заполнения по оси Y – Y_min и Y_max. Стартуя с текущим значением Y_tek = Y_min, исполнять пункты 4-9 до завершения раскраски.

  4. Определить число вершин, расположенных на строке Y_tek - текущей строке сканирования.

  5. Если вершины есть, то для каждой из вершин дополнить список активных ребер, используя информацию о соседних вершинах.

  6. Для каждого ребра в список активных ребер заносятся:

  • максимальное значение Y-координаты ребра,

  • приращение X-координаты при увеличении Y на 1,

  • начальное значение X-координаты.

  • Если обнаруживаются горизонтальные ребра, то они просто закрашиваются и информация о них в список активных ребер не заносится. Если после этого обнаруживается, что список активных ребер пуст, то заполнение закончено.

  • По списку активных ребер определяется Y_следY-координата ближайшей вершины. (Вплоть до Y_след можно не заботиться о модификации САР а только менять X-координаты пересечений строки сканирования с активными ребрами).

  • В цикле от Y_tek до Y_след:

    • выбрать из списка активных ребер и отсортировать X-координаты пересечений активных ребер со строкой сканирования;

    • определить интервалы и выполнить закраску;

    • перевычислить координаты пересечений для следующей строки сканирования.

  • Проверить, не достигли ли максимальной Y-координаты. Если достигли, то заливка закончена, иначе выполнить пункт 8.

  • Очистить список активных ребер от ребер, закончившихся на строке Y_след и перейти к пункту 4.

    Заливка области с затравкой

    Как уже отмечалось, для приложений, связанных в основном с интерактивной работой, используются алгоритмы заполнения области с затравкой.

    При этом тем или иным образом задается заливаемая (перекрашиваемая) область, код пикселя, которым будет выполняться заливка и начальная точка в области, начиная с которой начнется заливка.

    По способу задания области делятся на два типа:

     гранично-определенные, задаваемые своей (замкнутой) границей такой, что коды пикселей границы отличны от кодов внутренней, перекрашиваемой части области. На коды пикселей внутренней части области налагается условие - они должны быть отличны от кода пикселей границы и кода пикселя перекраски. Если внутри гранично-определенной области имеется еще одна граница, нарисованная пикселями с тем же кодом, что и внешняя граница, то соответствующая часть области не должна перекрашиваться;

     внутренне-определенные, нарисованные одним определенным кодом пикселя. При заливке этот код заменяется на новый код закраски.

    В этом состоит основное отличие заливки области с затравкой от заполнения многоугольника. В последнем случае мы сразу имеем всю информацию о предельных размерах части экрана, занятой многоугольником. Поэтому определение принадлежности пикселя многоугольнику базируется на быстро работающих алгоритмах, использующих когерентность строк и ребер (см. предыдущий раздел). В алгоритмах же заливки области с затравкой нам вначале надо прочитать пиксель, затем определить принадлежность области, и если принадлежит, то перекрасить.

    Заливаемая область или ее граница – некоторое связное множество пикселей. По способам доступа к соседним пикселям области делятся на 4- и 8-связные. В 4-связных областях доступ к соседним пикселям осуществляется по четырем направлениям - горизонтально влево и вправо и вертикально вверх и вниз. В 8-связных областях к этим направлениям добавляются еще 4 диагональных. Используя связность, мы можем, двигаясь от точки затравки, достичь и закрасить все пиксели области.

    Важно отметить, что для 4-связной прямоугольной области граница 8-связна (рис. 3.3,а) и наоборот у 8-связной области граница 4-связна (см. рис. 3.3,б). Поэтому заполнение 4-связной области 8-связным алгоритмом может привести к "просачиванию" через границу и заливке пикселей в примыкающей области.

    В общем, 4-связную область мы можем заполнить как 4-, так и 8-связным алгоритмом. Обратное же неверно. Так, область на рис. 3.3,а мы можем заполнить любым алгоритмом, а область на рис. 3.3,б, состоящую из двух примыкающих 4-связных областей можно заполнить только 8-связным алгоритмом.

    а) б)

    – пиксели области – пиксели границы

    Рис. 3: Связность областей и их границ:

    а) четырехсвязная; б) восьмисвязная

    С использованием связности областей и стека можно построить простые алгоритмы закраски как внутренне-оределенной, так и гранично-определенной области.

    Простой алгоритм заливки

    Рассмотрим простой алгоритм заливки гранично-определенной 4-связной области:

    Заливка выполняется следующим образом:

     определяется, является ли пиксель граничным или уже закрашенным;

     если не является, то пиксель перекрашивается, затем проверяются, и если надо, перекрашиваются 4 соседних пикселя.

    Понятно, что, несмотря на простоту и изящество программы, рекурсивная реализация проигрывает итеративной в том, что требуется много памяти для вложенных вызовов.

    Логика работы алгоритма следующая:

    1. поместить координаты затравки в стек;

    2. пока стек не пуст;

    3. извлечь координаты пикселя из стека;

    4. перекрасить пиксель;

    5. для всех четырех соседних пикселей проверить, является ли он граничным или уже перекрашен;

    6. если пиксель не является граничным или перекрашенным, то занести его координаты в стек.

    Ясно, что такой алгоритм экономнее, так как в стек надо помещать только координаты.

    Рассмотренный алгоритм легко модифицировать для работы с 8-связными гранично-определенными или с внутренне-определенными областями.

    Как уже отмечалось, очевидный недостаток алгоритмов, непосредственно использующих связность закрашиваемой области, – большие затраты памяти на стек, так как на каждый закрашенный пиксель в стеке по максимуму будет занесена информация о еще трех соседних. Кроме того, информация о некоторых пикселях может записываться в стек многократно. Это приведет не только к перерасходу памяти, но и потере быстродействия за счет многократной раскраски одного и того же пикселя. Значительно более экономен далее рассмотренный построчный алгоритм заливки.

    Построчный алгоритм заливки с затравкой

    Данный алгоритм использует пространственную когерентность:

     пиксели в строке меняются только на границах;

     при перемещении к следующей строке размер заливаемой строки скорее всего или неизменен, или меняется на 1 пиксель.

    Таким образом, на каждый закрашиваемый фрагмент строки в стеке хранятся координаты только одного начального пикселя, что приводит к существенному уменьшению размера стека.

    Последовательность работы алгоритма для гранично-определенной области следующая:

    1. Координата затравки помещается в стек, затем до исчерпания стека выполняются пункты 2-4.

    2. Координата очередной затравки извлекается из стека, и выполняется максимально возможное закрашивание вправо и влево по строке с затравкой, т.е. пока не попадется граничный пиксель. Пусть это Хлев и Хправ, соответственно.

    3. Анализируется строка ниже закрашиваемой в пределах от Хлев до Хправ, и в ней находятся крайние правые пиксели всех незакрашенных фрагментов. Их координаты заносятся в стек.

    4. То же самое проделывается для строки выше закрашиваемой.

    Алгоритм выполнения программы

    Данная программа написана на языке С++. Ввод данных осуществляется через командную строку.

    Закрашивает согласно варианту.

    Описание с начала работы программы:

    1) Этап 1: подключение библиотек, запуск графического режима, объявление переменных, рисование в окне координатной оси с обозначенными на ней значениями по Х и по Y.

    #include <graphics.h>

    #include <stdio.h>

    #include <string>

    #include <iostream.h>

    #include <math.h>

    #include <stdlib.h>

    void setka ()

    {

    int i;

    for(i = 0; i < 800; i = i + 10 ) {

    setcolor(COLOR(71,71,71));

    line(10+i, 10, 10+i, 590);

    line(10, 10+i, 790, 10+i);

    }

    for(i = 0; i < 800; i = i + 50 ) {

    setcolor(COLOR(251,151,151));

    line(50+i,295,50+i,305);

    }

    for(i = 0; i < 600; i = i + 50 ) {

    setcolor(COLOR(251,151,151));

    line(395,50+i,405,50+i);

    }

    setcolor(COLOR(151,151,151));

    line(10,300,790,300);

    line(400,10,400,590);

    line(790,300,780,305);

    line(790,300,780,295);

    line(400,10,395,20);

    line(400,10,405,20);

    outtextxy(380, 3, "y");

    outtextxy(780, 307, "x");

    outtextxy(385, 305, "0");

    outtextxy(443, 305, "50");

    outtextxy(489, 305, "100");

    outtextxy(340, 305, "-50");

    outtextxy(380, 242, "50");

    }

    2) Этап 2: Приводим в числа в целый вид, необходимый для построения точек.

    void zapolnenie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,int x5,int y5,int x6,int y6)

    {

    setcolor(COLOR(251,151,151));

    int a[100][3],i,j,m,n,xt,yt,ys,miny,k,ymin,ymax,b[30],q,minx,o,p,e;

    float cap[100][3],z1,z2,z3,z4,x[30],y[30],t;

    n=6; //кол-во вершин

    x[1]=x1;

    x[2]=x2;

    x[3]=x3;

    x[4]=x4;

    x[5]=x5;

    x[6]=x6;

    y[1]=y1;

    y[2]=y2;

    y[3]=y3;

    y[4]=y4;

    y[5]=y5;

    y[6]=y6;

    3) Этап 3: Подготовим служебные целочисленные массивы Y-координат вершин и номеров вершин.

    for(i=1;i<=n;i++)

    {

    a[i][1]=x[i];

    a[i][2]=y[i];

    a[i][3]=i;

    a[i][4]=x[i+1];

    a[i][5]=y[i+1];

    a[i][6]=i+1;

    4) Этап 4: Совместно отсортируем Y-координаты по возрастанию и массив номеров вершин, для того, чтобы можно было определить исходный номер вершины.

    for(i=1;i<=n-1;i++)

    {

    miny=a[i][2];

    for(j=i;j<=n;j++)

    if (miny>=a[j][2]) { miny=a[j][2]; k=j; }

    for(j=1;j<=3;j++)

    {

    t=a[i][j];

    a[i][j]=a[k][j];

    a[k][j]=t;

    }

    5) Этап 5: Определим пределы заполнения по оси Y - Y_мin и Y_max, стартуя с текущим значением Y_tek = Y_min.

    ymin=a[1][2];

    ymax=a[n][2];

    yt=ymin;

    m=0;

    6) Этап 6: Определим число вершин, расположенных на строке Y_tek - текущей строке сканирования. Если вершины есть, то для каждой из вершин дополнить список активных ребер, используя информацию о соседних вершинах.

    punkt4:

    k=0;

    for(i=1;i<=n;i++)

    if (yt==a[i][2]) { k++; b[k]=i;}

    for(i=1;i<=k;i++)

    {

    if (i!=k) if (a[b[i]][2]==a[b[i+1]][2]) { line(400+a[b[i]][1],300-a[b[i]][2],400+a[b[i+1]][1],300-a[b[i+1]][2]); /*printf("line\n"); REMOVED */ }

    for(j=1;j<=n;j++)

    if ((((a[j][3]+1==a[b[i]][3]) || (a[j][3]-1==a[b[i]][3])) && (a[j][2]>a[b[i]][2])) || ((a[b[i]][3]==n) && (a[j][3]==1) && (a[j][2]>a[b[i]][2])))

    {

    m++;

    cap[m][1]=a[j][2];

    z1=a[j][1];

    z2=a[b[i]][1];

    z3=a[j][2];

    z4=a[b[i]][2];

    cap[m][2]=(z1-z2)/(z3-z4);

    cap[m][3]=a[b[i]][1];

    }

    }

    7) Этап 7: Поиск номера текущей вершниы yt

    for(i=1;i<=n;i++)

    if ((a[i][2]==yt) && (a[i][2]!=a[i+1][2])) q=i;

    ys=a[q+1][2];

    8) Этап 8: Отсортиpуем координаты, определим интервалы и выполним закраску.

    9) Этап 9: Выход из графического режима после нажатия на любую клавишу. Завершение работы программы.

    7) Этап 7: Конец. Завершение работы программы.

    Вывод

    В результате проделанной работы мы ознакомились с методами геометрических преобразований графических объектов, изучив методы закраски объектов, научились закрашивать многоугольники.

    12

  • Соседние файлы в папке docx57