Міністерство освіти та науки України
Національний університет «Львівська політехніка»
Кафедра автоматизованих систем управління
Лабораторна робота №5
з дисципліни
«Комп’ютерна графіка»
на тему:
“Метод цифрового диференціального аналізатора”
Мета: Освоїти розкладання відрізка в растр методом цифрового диференціального аналізатора.
ТЕОРЕТИЧНІ ОСНОВИ
Один з методів розкладання відрізка в растр полягає в розв’язуванні диференціального рівняння, що описує цей процес. Для прямої лінії маємо
dy/dx=const чи Dy/Dx=(y2-y1)/(x2-x1)
Розв’язок представляється у вигляді
yi+1=yi+Dy
yi+1=yi+Dx(y2-y1)/(x2-x1) [1]
де x1, y1 і x2, y2 - кінці відрізка, що розкладається, і yi - початкове значення для чергового кроку вздовж відрізка. Фактично рівняння [1] являє собою рекурентне співвідношення для послідовних значень y вздовж потрібного відрізка. Цей метод, використовуваний для розкладання в растр відрізків, називається цифровим диференціальним аналізатором (ЦДА). У простому ЦДА або Dx, або Dy (більше із збільшень) вибирається як одиниця растра. Нижче наводиться простий алгоритм, що працює у всіх квадрантах:
Процедура розкладання в растр відрізка по методу цифрового диференціального аналізатора (ЦДА)
передбачається, що кінці відрізка (x1, y1) і (x2, y2) не збігаються
Integer - функція перетворення дійсного числа в ціле.
Sign - функція, що повертає -1, 0, 1 для від’ємного, нульового і додатнього аргументу відповідно
апроксимуємо довжину відрізка
if abs(x2-x1)>=abs(y2-y1) then
Довжина=abs(x2-x1)
else
Довжина=abs(y2-y1)
end if
назначаємо більше із збільшень Dx чи Dy рівним одиниці растра
Dx=(x2-x1)/Довжина
Dy=(y2-y1)/Довжина
округляємо величини, а не відкидаємо дробову частину
використання знакової функції робить алгоритм придатним для всіх квадрантів
x=x1+0.5*Sign(Dx)
y=y1+0.5*Sign(Dy)
початок основного циклу
i=1
while (i<=Довжина)
Plot (Integer(x), Integer(y))
x=x+Dx
y=y+Dy
i=i+1
end while
finish
Наведемо приклад ілюструючий роботу алгоритму:
Приклад 2.1. Простий ЦДА в першому квадранті
Розглянемо відрізок із точки (0,0) у точку (5,5). Використовуємо ЦДА для розкладання цього відрізка в растр. Результати роботи алгоритму такі:
початкові установки
x1=0
y1=0
x2=5
y2=5
Довжина=5
Dx=1
Dy=1
x=0,5
y=0,5
результати роботи покрокового циклу
i
Plot
x
y
0,5
0,5
1
(0,0)
1,5
1,5
2
(1,1)
2,5
2,5
3
(2,2)
3,5
3,5
4
(3,3)
4,5
4,5
5
(4,4)
5,5
5,5
Отримане растрове представлення відрізка зображене на рис.2.1. Зауважимо, що кінцеві точки визначені точно, обрані піксели рівномірно розподілені вздовж відрізка. Однак якщо початковим значенням змінної і взяти нуль замість одиниці, то виявиться активованим піксел з координатами (5,5), що небажано. Якщо адреса піксела задана цілими координатами лівого нижнього кута, то активація цього піксела дасть явно невірну кінцеву точку відрізка (рис.2.1). До того ж при кресленні серії послідовних відрізків піксел (5,5) буде активований двічі: наприкінці даного відрізка і на початку наступного. Такий піксел може виглядати як більш яскравий чи мати інший колір. Наведемо приклад ілюструючий роботу алгоритму в третьому квадранті.
Рис.2.1. Результати роботи простого ЦДА в першому квадранті.
Розглянемо відрізок із точки (0,0) у точку (-8,-4) в третьому квадранті. Використовуємо ЦДА для розкладання цього відрізка в растр. Результати роботи алгоритму такі:
початкові установки
x1=0
y1=0
x2=-8
y2=-4
Довжина=8
Dx=-1
Dy=-0,5
x=-0,5
y=-0,5
результати роботи покрокового циклу
Plot
x
y
-0,5
-0,5
1
(-1,-1)
-1,5
-1,0
2
(-2,-1)
-2,5
-1,5
3
(-3,-2)
-3,5
-2,0
4
(-4,-2)
-4,5
-2,5
5
(-5,-3)
-5,5
-3,0
6
(-6,-3)
-6,5
-3,5
7
(-7,-4)
-7,5
-4,0
8
(-8,-4)
-8,5
-4,5
Незважаючи на те що результати, представлені на рис.2.2, виглядають цілком прийнятними, аналіз відрізків, проведених із точки (0,0) у точку (-8,4) і (8,-4), показує, що розкладений у растр відрізок лежить по одну сторону від реального і що на одному з кінців відрізка з'являється зайва точка, тобто результат роботи алгоритму залежить від орієнтації. Отже точність у кінцевих точках зменшується.
Рис.2.2. Результати роботи простого ЦДА в третьому квадранті.
Текст програми
unit Unit1;
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Spin, ExtCtrls;
type
TForm1 = class(TForm)
Image1: TImage;
SpinEdit1: TSpinEdit;
Label1: TLabel;
Button1: TButton;
Button2: TButton;
SpinEdit2: TSpinEdit;
SpinEdit3: TSpinEdit;
SpinEdit4: TSpinEdit;
SpinEdit5: TSpinEdit;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Button3: TButton;
Button4: TButton;
Button5: TButton;
Button6: TButton;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure Button4Click(Sender: TObject);
procedure Button5Click(Sender: TObject);
procedure Button6Click(Sender: TObject);
//procedure clear_field(where: TImage);
//procedure Button6Click(Sender:TObject);
//procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
w,h,zn1,zn2:integer;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var k,m,i:integer;
begin
if SpinEdit1.Value<=0 then SpinEdit1.Value:=5;
i:=SpinEdit1.Value;
k:=0; m:=0; h:=0; w:=0;
Image1.Canvas.Brush.Color:=clWhite;
Image1.Canvas.Brush.Style:=bsSolid;
Image1.Canvas.Rectangle(0,0,Image1.Width,Image1.Height);
Image1.Canvas.Pen.Color:=clLime;
Image1.Canvas.Pen.Style:=psSolid;
while k< Image1.Width do
begin
k:=k+i;
Image1.Canvas.MoveTo(k,0);
Image1.Canvas.LineTo(k,Image1.Height);
k:=k+i;
Image1.Canvas.MoveTo(k,Image1.Height);
Image1.Canvas.LineTo(k,0);
w:=w+2;
end;
while m< Image1.Height do
begin
m:=m+i;
Image1.Canvas.MoveTo(0,m);
Image1.Canvas.LineTo(Image1.Width,m);
m:=m+i;
Image1.Canvas.MoveTo(Image1.Width,m);
Image1.Canvas.LineTo(0,m);
h:=h+2;
end;
Image1.Canvas.Pen.Color:=clBlack;
Button5.Click;
end;
{procedure TForm1.Button2Click(Sender: TObject);
var x1,y1,x2,y2,i:integer;
begin
SpinEdit2.Visible:=True;
SpinEdit3.Visible:=True;
SpinEdit4.Visible:=True;
SpinEdit5.Visible:=True;
Button3.Visible:=True;
Button4.Visible:=True;
Label2.Visible:=True;
Label3.Visible:=True;
Label4.Visible:=True;
Label5.Visible:=True;
SpinEdit2.Value:=0;
SpinEdit3.Value:=0;
SpinEdit4.Value:=1;
SpinEdit5.Value:=1;
end; }
procedure TForm1.Button3Click(Sender: TObject);
var x1,y1,x2,y2,i:integer;
begin
i:=SpinEdit1.Value;
x1:=SpinEdit2.Value;
y1:=SpinEdit3.Value;
x2:=SpinEdit4.Value;
y2:=SpinEdit5.Value;
if y1<0 then zn1:=-1 else zn1:=1;
if y2<0 then zn2:=-1 else zn2:=1;
Image1.Canvas.Brush.Color:=clFuchsia;
Image1.Canvas.Brush.Style:= bsSolid;
Image1.Canvas.Pen.Color:=clLime;
Button5.Click;
Image1.Canvas.FloodFill(trunc((w-1)/2)*i+i*x1-1,Image1.Height+1-i*y1-trunc((h-zn1)/2)*i,clLime, fsBorder);
Image1.Canvas.FloodFill(trunc((w-1)/2)*i+i*x2-1,Image1.Height+1-i*y2-trunc((h-zn2)/2)*i,clLime, fsBorder);
Image1.Canvas.Pen.Color:=clBlack;
Button5.Click;
end;
procedure TForm1.Button4Click(Sender: TObject);
var i,x1,y1,x2,y2,xm,ym,xr,yr:integer; dx,dy,xk,yk,znx,zny:real;
begin
i:=SpinEdit1.Value;
x1:=SpinEdit2.Value;
y1:=SpinEdit3.Value;
x2:=SpinEdit4.Value;
y2:=SpinEdit5.Value;
Image1.Canvas.Pen.Color:=clLime;
Button5.Click;
znx:=1; zny:=1;
if y2<y1 then zny:=-1;
if x2<x1 then znx:=-1;
xk:=x1; yk:=y1;
xr:=abs(x2-x1);
yr:=abs(y2-y1);
if xr>yr then
begin
dy:= yr/xr;
while abs(xk-x2)>1 do
begin
xk:=xk+znx;
yk:=yk+dy*zny;
Image1.Canvas.Brush.Color:=clFuchsia ;
Image1.Canvas.Brush.Style:= bsSolid;
Image1.Canvas.FloodFill(round(trunc((w-1)/2)*i+i*xk-1),round(Image1.Height+1-i*yk-trunc((h-zn1)/2)*i),clLime, fsBorder);
end;
end
else
begin
dx:= xr/yr;
while abs(yk-y2)>1 do
begin
yk:=yk+zny;
xk:=xk+dx*znx;
Image1.Canvas.Brush.Color:=clFuchsia ;
Image1.Canvas.Brush.Style:= bsSolid;
Image1.Canvas.FloodFill(round(trunc((w-1)/2)*i+i*xk-1),round(Image1.Height+1-i*yk-trunc((h-zn2)/2)*i),clLime, fsBorder);
end;
end;
Image1.Canvas.Pen.Color:=clBlack;
Button5.Click;
end;
procedure TForm1.Button5Click(Sender: TObject);
var i:integer;
begin
i:=SpinEdit1.Value;
Image1.Canvas.Pen.Style:=psSolid;
Image1.Canvas.MoveTo(0,trunc(h/2)*i);
Image1.Canvas.LineTo(Image1.Width,trunc(h/2)*i);
Image1.Canvas.MoveTo(trunc((w-1)/2)*i,0);
Image1.Canvas.LineTo(trunc((w-1)/2)*i,Image1.Height);
end;
procedure clear_field(where: TImage);
begin
where.canvas.Pen.color:=clWhite;
where.canvas.Brush.Color := clWhite;
where.canvas.Rectangle(0, 0, where.width, where.Height);
where.canvas.Pen.color:=clBlack;
end;
procedure TForm1.Button6Click(Sender: TObject);
var k,m,i:integer;
begin
clear_field(image1);
if SpinEdit1.Value<=0 then SpinEdit1.Value:=5;
i:=SpinEdit1.Value;
k:=0; m:=0; h:=0; w:=0;
Image1.Canvas.Brush.Color:=clWhite;
Image1.Canvas.Brush.Style:=bsSolid;
Image1.Canvas.Rectangle(0,0,Image1.Width,Image1.Height);
Image1.Canvas.Pen.Color:=clBlack;
Image1.Canvas.Pen.Style:=psSolid;
while k< Image1.Width do
begin
k:=k+i;
Image1.Canvas.MoveTo(k,0);
Image1.Canvas.LineTo(k,Image1.Height);
k:=k+i;
Image1.Canvas.MoveTo(k,Image1.Height);
Image1.Canvas.LineTo(k,0);
w:=w+2;
end;
while m< Image1.Height do
begin
m:=m+i;
Image1.Canvas.MoveTo(0,m);
Image1.Canvas.LineTo(Image1.Width,m);
m:=m+i;
Image1.Canvas.MoveTo(Image1.Width,m);
Image1.Canvas.LineTo(0,m);
h:=h+2;
end;
Image1.Canvas.Pen.Color:=clRed;
Button5.Click;
end;
end.
Висновок: в даній лабораторній роботі я ознайомився з основами комп’ютерної графіки – алгоритмом побудови відрізків методом цифрового диференціального аналізатора.