- •Київ кнутд 2007
- •1.2 Сфери використання
- •1.3 Головнi поняття I операцiї
- •1.4 Типи графiчних приладiв
- •2 Алгоритми растрової графiки
- •2.1 Растрова I векторна графiка
- •2.1.1 Цифровий диференцiйний аналiзатор
- •2.1.2 Алгоритм Брезенхема побудови вiдрiзкiв
- •2.1.3 Алгоритм Брезенхема креслення кола
- •2.1.4 Алгоритм Брезенхема побудови елiпса
- •2.2 Растрова розгортка багатокутникiв.
- •2.2.1 Алгоритм заповнення з упорядкованими списками ребер
- •2.2.2 Алгоритм заповнення за ребрами
- •2.2.3 Алгоритм заповнення iз списком ребер I прапорцем
- •2.2.4 Простий алгоритм заповнення iз зачiпкою
- •2.2.5 Алгоритм заповнення по рядкам iз зачiпкою
- •2.3 Ефект драбини
- •3. Лабораторнi роботи
- •3.1 Лабораторна робота BresLine на тему "Виведення вiдрiзкiв прямих за алгоритмом Брезенхема"
- •3.2 Лабораторна робота BresCirc на тему "Виведення дуг кола за алгоритмом Брезенхема".
- •3.3 Лабораторна робота BresEllips на тему "Виведення елiпсiв за алгоритмом Брезенхема".
- •3.4 Лабораторна робота FloodLine на тему "Заповнення полiгональних фiгур".
- •3.5 Лабораторна робота FloodCirc на тему "Заповнення довiльних областей".
- •4. Література
- •1 Вступ 5
- •2 Алгоритми растрової графiки 8
- •4. Література 54
2.2.4 Простий алгоритм заповнення iз зачiпкою
У попереднiх алгоритмах заповнення звичайно виконується у порядку сканування. Iнший пiдхiд запропоновано у алгоритмах заповнення iз зачiпкою (рос. "заполнение с затравкой"). У цих алгоритмах дiє припущення, що є вiдомим один пiксел, що належить до середини багатокутника. Алгоритм намагається знайти i замалювати всi iншi пiкселi, якi належать до середини багатокутника. Областi площини, якi вiдповiдають серединi багатокутника можуть бути або внутрiшнє- або гранично-визначеними.
Для внутрiшнє-визначеної областi пiкселi, що належать до середини областi, мають один колiр (iнтенсивнiсть), а всi пiкселi, що є зовнiшнiми до цiєї областi, мають iнший колiр (iнтенсивнiсть).
Приклад 1 G1={(x, y)|2 ≤ x ≤ 8,2 ≤ y ≤ 6} -- внутрiшнє-визначена область.
Приклад 2 Область G2, яка має граничнi пiкселi
Г2= {(1,3),(2,3),(1,4),(2,4); (3,1),(3,2),(4,1),(4,2)}
є гранично-визначеною областю.
Для гранично-визначеної областi пiкселi на границi областi мають визначений колiр, а всi пiкселi, що належать до середини областi, не можуть мати цього кольору (але пiкселi, що є зовнiшнiми до цiєї границi, вже можуть мати цей колiр).
Приклад 3 Область G1 з прикладу 1 визначена границею
Г1= {(1,2),(1,3),(1,4),(1,5);\;(2,6),(3,6),(4,6),(5,6),(6,6),(7,6);
(8,2),(8,3),(8,4),(8,5);(2,1),(3,1),(4,1),(5,1),(6,1),(7,1)}
як гранично-визначена область.
Приклад 4 Область G3, яка визначена границею
Г3={(2,6),(3,6);\;(4,5),(4,4),(5,4);\;(3,6),(2,6);
(5,1),(4,1_;\;(3,2),(3,3),(2,3);\;(1,4),(1,5)\},
є гранично-визначеною областю.
Внутрiшнє- або гранично-визначенi областi можуть бути 4- або 8-зв'язними.
Область є 4-зв'язною, якщо вiд будь-якого пiкселя цiєї областi можна перейти до будь-якого iншого пiкселя цiєї областi за допомогою комбiнацiї перемiщень на один крок у 4-x напрямках: праворуч, лiворуч, догори i униз.
Для 8-зв'язної областi до цих перемiщень ще додаються 4 перемiщення у 4-x дiагональних напрямках. Будь-який алгоритм з зачiпкою для 8-зв'язної областi можна використовувати для заповнення 4-зв'язної областi, зворотне вже може бути не вiрним.
Внутрiшнє-визначенi областi можуть бути як 4- так i 8-звязними. Область G1$ з прикладу 1 є як 4- так i 8-зв'язною. Область
G4= {(1,3),(2,3);\;(1,4),(2,4);\;(3,1),(3,2);\;(4,1),(4,2)}
є 8-зв'язною.
Гранично-визначенi областi також можуть бути як 4- так i 8-звязними. Наприклад, гранично-визначена область з прикладу 2 є як 4- так i 8- зв'язною. Але гранично-визначена область з прикладу 4 є 8-звязною.
Далi 8-зв'язнi областi, якi є також 4-зв'язними, будемо вважати тiльки 4-зв'язними.
Легко перевiрити, що областi, границi яких породжуються вiдрiзками прямих i дугами кола за алгоритмами Брезенхема, є 4-зв'язнi областi.
Далi будемо розглядати лише алгоритми заповнення 4-звязних областей, але їх легко переробити на 8-зв'язнi областi, якщо заповнення проводити не у 4-х, а у 8-и напрямках.
Наведемо простий алгоритм заповнення гранично-визначеної областi з використанням стека.
Стек - це масив або iнша структура даних, у яку можна послiдовно розмiщати значення, i з якої їх можна послiдовно вибирати. Коли новi значення додаються до або розмiщуються у стеку, всi iншi значення перемiщуються вниз на один рiвень. Коли значення видаляються або вибираються з стеку, iншi значення випливають або пiдiймаються угору на один рiвень. Такий стек має назву стека прямої дiї або стеком з дисциплiною обслуговування "першим прийшов, останнiм вийшов" (FILO - First In Last Out). Простий алгоритм заповнення з зачiпкою можна подати у наступному виглядi.
Простий алгоритм заповнення iз зачiпкою i стеком.
1. Покласти пiксел зачiпки у стек.
2. Доки стек не є порожнiм:
а) Вибрати пiксел iз стека. Розглядати його як поточний пiксел.
в) Надати пiкселу потрiбне значення.
с) Для кожного з сусiднiх до поточного 4-зв'язних (8- зв'язних) пiкселiв перевiрити: цей пiксел є граничний або пiксел вже має потрiбне значення. Якщо так, тодi не розглядати далi цей пiксел. Iнакше покласти сусiднiй пiксел у стек.
Програмна реалiзацiя алгоритму заповнення з зачiпкою.
unit SimpleSeedUnit1;
interface
uses Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
const _N = 100; {максимальне число граничних пiкселiв}
_Xmax = 10; {розмiр вiкна по вiсi абсцис}
_Ymax = 8; {розмiр вiкна по вiсi ординат}
_Nstack = 100;{максимальна величина стека}
Type TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
Private { Private declarations }
public { Public declarations }
end;
Point = array[1..2] of integer; {точка}
Border = array[1.._N] of Point; {границя}
Pi = array[0.._Ymax,0.._Xmax] of boolean;{пiкселi}
tStack = array[1.._Nstack] of Point; {стек пiкселiв}
var Form1: TForm1;
Color, {колiр}
NP, {число граничних пiкселiв}
NS, {число пiкселiв у стекi}
Xmax,Ymax, {макс.число пiкселiв уздовж осей}
X,Y, {координати поточного пiкселя}
i {робоча змiнна}
: integer;
Ptemp, {поточний пiксел}
Zatr {пiксел зачiпки}
: Point;
P: Border; {список граничних пiкселiв}
Stack: tStack; {стек для вiдмiчених пiкселiв}
M: Pi; {массив пiкселiв вiкна}
fin: text; {файл з вхiдними даними}
implementation {$R *.DFM}
// встановлення списку граничних пiкселiв
procedure SetP(var P:Border;var NP:integer;var Zatr:Point);
var i, xs,ys,xf,yf,dx,dy:integer;
begin {SetP}
assign(fin,'Zatr1gr.dat');reset(fin);MP:=0;
readln(fin,Zatr[1],Zatr[2]);
while (not Eof(fin)) do
begin {while not Eof(fin)}
readln(fin,xs,ys,xf,yf);
if xs<xf then dx:=1
else if xs=xf then dx:=0 else dx:=-1;
if ys<yf then dy:=1
else if ys=yf then dy:=0 else dy:=-1;
if (NP<1)or((P[NP,1]<>xs)or(P[NP,2]<>ys))
then begin
NP:=NP+1;
P[NP,1]:=xs;P[NP,2]:=ys;
end;
while ((P[NP,1]<>xf)or(P[NP,2]<>yf)) do
begin
NP:=NP+1;P[NP,1]:=P[NP-1,1]+dx;
P[NP,2]:=P[NP-1,2]+dy;
end;
end; {while not Eof(fin)}
close(fin);
// виведення на полотно контуру багатокутника (червоним)}
for i:=1 to NP do Form1.Canvas.Pixels[P[i,1],P[i,2]]:=clRed;
end; {SetP}
// гасiння пiкселiв буферу
procedure SetStartM(var M:Pi;Xmax,Ymax:integer;
P:Border;NP:integer);
var i,j:integer;
begin {SetStartM}
for j:=0 to Xmax do
for i:=0 to Ymax do M[i,j]:=false;
for i:=1 to NP do
begin
M[round(P[i,1]),round(P[i,2])]:=true;
end;
end; {SetStartM}
// iнвертування пiксела
procedure Inv(var M:Pi;i,j:integer);
begin
if M[i,j]=true then M[i,j]:=false
else M[i,j]:=true;
end;
// розмiщення пiксела у стеку
procedure PushPixel(p:point;var s:tStack;var Ns:integer);
begin
Ns:=Ns+1; s[Ns,1]:=p[1];s[Ns,2]:=p[2];
end;
// виймання пiксела з стека
procedure PopPixel(s:tStack;var Ns:integer;var p:point);
begin
p[1]:=s[Ns,1];p[2]:=s[Ns,2];Ns:=Ns-1;
end;
{виведення поточного стану растра у вiкно}
procedure TypeRastr(M:Pi;Xmax,Ymax:integer;Color:integer);
var i,j:integer;
begin {TypeRastr}
for i:=0 to Ymax do
for j:=0 to Xmax do
if M[i,j]=true then Form1.Canvas.Pixels[i,j]:=Color;
end; {TypeRastr}
function YesMarked(Ptemp:point;M:Pi):boolean;
var i,j:integer;
begin {YesMarked}
i:=round(Ptemp[1]); j:=round(Ptemp[2]);
if M[i,j]=true then YesMarked:=true
else YesMarked:=false;
end;{YesMarked}
procedure Mark(Ptemp:point;var M:Pi);
var i,j:integer;
begin
i:=round(Ptemp[1]);
j:=round(Ptemp[2]);M[i,j]:=true;
end;
function YesBody(Pnext:Point;P:Border;NP:integer):boolean;
var i:integer;
begin {YesBody}
YesBody:=false;
for i:=1 to NP do if (Pnext[1]=P[i,1])and(Pnext[2]=P[i,2])
then YesBody:=true;
end; {YesBody}
procedure Zapoln(var M:Pi;Xmax,Ymax:integer;
P:Border;NP:integer;Zatr:point);
var i,j:integer; Pnext:Point;
begin {Zapoln}
Ptemp[1]:=Zatr[1]; Ptemp[2]:=Zatr[2];
PushPixel(Ptemp,Stack,NS);{занесення пiксела зачiпки у стек}
while NS<>0 do {доки стек не буде порожнiм}
begin {while NS<>0}
PopPixel(Stack,NS,Ptemp);{вiдбiр пикселю з стеку}
if not YesMarked(Ptemp,M) then Mark(Ptemp,M);
for i:=-1 to 1 do
if i<>0 then begin
Pnext[1]:=Ptemp[1]+i;Pnext[2]:=Ptemp[2];
if (not YesMarked(Pnext,M))and(not YesBody(Pnext,P,NP))
then PushPixel(Pnext,Stack,NS);
end;
for j:=-1 to 1 do
if j<>0 then begin
Pnext[1]:=Ptemp[1];Pnext[2]:=Ptemp[2]+j;
if (not YesMarked(Pnext,M))and(not YesBody(Pnext,P,NP))
then PushPixel(Pnext,Stack,NS);
end;
end;{while NS<>0}
end; {Zapoln}
procedure TForm1.Button1Click(Sender: TObject);
begin {TForm1.Button1Click}
SetP(P,NP,Zatr);//встановлення списку граничних пiкселiв
NS:=0; {"очищення стеку"}
//стартове встановлення пiкселiв i вст.граничних пiкселiв
SetStartM(M,Xmax,Ymax,P,NP);
Zapoln(M,Xmax,Ymax,P,NP,Zatr);
TypeRastr(M,Xmax,Ymax,clYellow);
end; {TForm1.Button1Click}
initialization
Xmax:=_Xmax;
Ymax:=_Ymax;
end.
Лiтература.[ 5, стор. 111-114].
Зауваження 10 У [ 24, стр.650-651] наведено рекурсивний алгоритм заповнення 4-зв'язної областi ( flood-fill algorithm) у виглядi процедури на мовi C. Вiн змiнює колiр кожного пiксела, що знаходиться у серединi областi i має колiр intColor на новий колiр newColor. Алгоритм послiдовно заповнює область кольором newColor "створюючи власний шлях" newColor вiд "початкового" пiкселя з координатами (x, y) у пошуках пикселiв кольору intColor, змiнюючи колiр кожного зі знайдених пiкселiв на newColor}.
Головна iдея цього алгоритму будується на наступних мiркуваннях. Якщо пiксел з координатами (x ,y) знаходиться у серединi областi (тодi його колiр intColor), то змiнюємо його колiр на newColor i рекурсивно використовуємо цей процес до кожного з чотирьох сусiднiх пiкселiв. У протилежному випадку нiчого не робимо.
Наступна реалiзацiя у середовищі програмування DELPHI здiйснена студентом Анiшевим.
Процедура рекурсивного заповнення 4-зв'язної областi.
procedure FloodFill(canvas:TCanvas;x,y:Integer;
IntColor,NewColor:TColor);
begin
If canvas.Pixels[x,y]=IntColor
then begin
canvas.Pixels[x,y]:=NewColor;
FloodFill(canvas,x-1,y,IntColor,NewColor);
FloodFill(canvas,x+1,y,IntColor,NewColor);
FloodFill(canvas,x,y+1,IntColor,NewColor);
FloodFill(canvas,x,y-1,IntColor,NewColor);
end;
procedure TForm1.PaintBox1MouseUp(Sender: TObject;
Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
FloodFill(PaintBox1.Canvas,x,y,
PaintBox1.Canvas.Pixels[x,y],clRed);
end;
procedure TForm1.PaintBox1Paint(Sender: TObject);
begin
PaintBox1.Canvas.Brush.Color:=clWhite;
PaintBox1.Canvas.Brush.Style:=bsSolid;
PaintBox1.Canvas.Pen.Color:=clWhite;
PaintBox1.Canvas.Rectangle(PaintBox1.ClientRect);
end;
