Kü thuËt §å ho¹ m¸y tÝnh 28 intr($10,R); End; Procedure write_color_reg(index: Integer;color: RGB_COLOR_TYP); Begin port[COLOR_REG_WR]:=index; port[COLOR_DATA]:=color.red; port[COLOR_DATA]:=color.green; port[COLOR_DATA]:=color.blue; End; Var VIDEO_BUFFER : Word; gm,index,n: integer; color: RGB_COLOR_TYP; Begin clrscr; VIDEO_BUFFER:=$A000; gm:=$13; set_mode(gm); index:=0; n:=2; Repeat n:=n+b; color.red:=n; color.green:=0; color.blue:=n; write_color_reg(index,color); Delay(150); If n>60 Then b:=-1; If n<4 Then b:=1; Until KeyPressed; gm:=3; (* Text Mode *) set_mode(gm); End. $3. C¸c thuËt to¸n vÏ ®o¹n th¼ng, ®−êng trßn I. VÏ ®o¹n th¼ng : Kỹ thuật Đồ hoạ máy tính 29 Giả sử ta cần vẽ đoạn thẳng đi qua 2 điểm (x1, y1) và (x2, y2), thì hệ số góc của nó là m=dy/dx, trong đó dy=y2-y1; dx=x2-x1 và phơng trình đờng thẳng sẽ là y=m.x+b với b=y1-m.x1; 1. Vẽ đoạn thẳng bằng cách làm tròn số : a. Trờng hợp x1=x2 : trong trờng hợp này đoạn thẳng là đờng song song với trục tung b. Trờng hợp y1=y2 : trong trờng hợp này đoạn thẳng là đờng song song với trục hoành c. Trong trờng hợp 0<|m|<=1 : Trờng hợp này là trờng hợp giá trị tuyệt đối của hệ số góc nhỏ hơn 1 thì ta vẽ đoạn thẳng bằng cách cho x chạy từ x1 đến x2 tính y=Round(m.x+b) và vẽ điểm (x,y) d. Trong trờng hợp |m|>1 : Trờng hợp này là trờng hợp giá trị tuyệt đối của hệ số góc lớn hơn 1 ta viết lại phơng trình đờng để cho giá trị tuyệt đối của hệ số góc nhỏ hơn 1 : x=(1/m)y- b/m thì ta vẽ đoạn thẳng bằng cách cho y chạy từ y1 đến y2 tính x=Round((1/m)y-b/m) và vẽ điểm (x,y) Chơng trình trên chạy chậm vì phải tính toán với các số thực. Bài tập : Lập chơng trình vẽ đoạn thẳng theo thuật toán trên 2. Thuật toán vẽ đoạn thẳng của Bresanham Ta bắt đầu bằng cách xét đờng thẳng có hệ số góc nằm trong khoảng (0,1] vì các điểm trên màn hình đợc biểu diễn bởi các toạ độ nguyên nên ta cho x tăng theo bớc nhảy đơn vị từ x1 đến x2. Giả sử trên hình vẽ điểm (x[i],y[i]) là điểm nằm trên đoạn thẳng đã đợc vẽ và cho x[i] tăng thêm 1 để vẽ điểm tiếp theo và giá trị của y ứng với x[i]+1 ta nên chọn là y[i] hay y[i]+1, giá trị đúng của y=m(x[i]+1)+b nh vậy ta sẽ tuỳ xem y[i] hay y[i]+1 gần y(=m(x[i]+1)+b) hơn thì ta sẽ chọn giá trị đó. Ta xét các khoảng cách : Khoảng cách giữa y và y[i] : d1=y-y[i]=m(x[i]+1)+b-y[i] Kỹ thuật Đồ hoạ máy tính 30 Khoảng cách giữa y[i]+1 và y : d2=y[i]+1-y=y[i]+1-m(x[i]+1)-b Hiệu giữa hai khoảng cách này là : d1-d2=2m(x[i]+1)-2y[i]+2b-1 thay m=dy/dx ta có d1-d2=2(dy/dx)(x[i]+1)-2y[i]+2b-1 nên p[i]=dx(d1-d2)=2dy.x[i]+2dy-2dx.y[i]+2dx.b-dx =2dy.x[i]-2dx.y[i]+2dy+dx(2b-1) Hay p[i]=2dy.x[i]-2dx.y[i]+c với c=2dy+dx(2b-1) (1) thay i=1 ta có p[1]=2dy.x1-2dy.y1+c=2dy-dx Nếu p[i]<0 nghĩa là y[i] gần đờng thẳng hơn y[i]+1 và ta chọn y[i+1]=y[i] ngợc lại ta chọn y[i+1]=y[i]+1 Công thức (1) có thể đợc giản lợc bằng cách liên hệ với điểm (x[i+1],y[i+1]) tiếp theo, khi đó p[i+1]=2dy.x[i+1]-2dx.y[i+1]+c (2) Trừ (2) cho (1) ta có : p[i+1]-p[i]=2dy(x[i+1]-x[i])-2dx(y[i+1]-y[i]) mà x[i+1]=x[i]+1 nên p[i+1]=p[i]+2dy-2dx(y[i+1]-y[i]) Mà nếu p[i]<0 thì y[i+1] tiếp theo sẽ đợc chọn là y[i], nghĩa là y[i+1]=y[i] nên p[i+1]=p[i]+2dy=p[i]+const1 với const1=2dy ngợc lại chọn y[i+1]=y[i]+1 nên p[i+1]=p[i]+2(dy-dx)=p[i]+const2 với const2=2(dy-dx) Và thuật toán vẽ đờng thẳng của Presenham trong trờng hợp hệ số góc nằm trong khoảng (0,1] nh sau : 1. Nếu x1>x2 Thì ta hoán vị 2 điểm (x1,y1) và (x2,y2) 2. Đặt dx:=Abs(x2-x1); dy:=Abs(y2-y1); p:=2*dy-dx; const1:=2*dy; const2:=2*(dy-dx); 3. Đặt x:=x1; y:=y1; 4. vẽ điểm(x,y); 5. Tăng x lên 1 đơn vị 6. Nếu p<0 Thì p:=p+const1 và giữ nguyên y ngợc lại p:=p+const2 va tăng y lên 1 đơn vị; 7. Kiểm tra xem x còn <= x2 không nếu còn quay lên bớc 4 ngợc lại kết thúc 8. Kết thúc Và thủ tục vẽ đờng thẳng là : Procedure Line_(x1,y1,x2,y2 : Integer); Var x,y,tg,dx,dy,const1,const2,p : Integer; mau : Byte; Begin If x1>x2 Then Begin tg:=x1; x1:=x2; x2:=tg; tg:=y1; y1:=y2; y2:=tg; End; dx:=Abs(x2-x1); dy:=Abs(y2-y1); p:=2*dy-dx; const1:=2*dy; const2:=2*(dy-dx); mau:=GetColor; Kỹ thuật Đồ hoạ máy tính 31 x:=x1; y:=y1; While x<=x2 Do Begin Putpixel(x,y,mau); Inc(x); If p<0 Then p:=p+const1 Else Begin Inc(y); p:=p+const2; End; End; End; Bài tập : 1. Vẽ sơ đồ khối minh hoạ thuật toán vẽ đoạn thẳng của Bresenham 2. Hoàn thiện chơng trình vẽ đoạn thẳng theo thuật toán của Bresenham trong tất cả các trờng hợp trờng hợp II. Vẽ đờng tròn : Để vẽ đờng tròn chúng ta cũng phải tiến hành chọn những điểm có toạ độ nguyên để vẽ mà điểm đó nằm gần đờng tròn thực nhất Giả sử đờng tròn cần phải vẽ có phơng trình là x 2 +y 2 =R 2 , ở đây để đơn giản ta xét tâm của đờng tròn là tại gốc toạ độ sau đó để vẽ đờng tròn ta chi cần tịnh tiến theo vector (x 0 ,y 0 ) là tâm Có thể tham số hoá chơng trình trên thành x=R*cos(T); y=R*sin(T) với 0<=T<=2*pi Nh vậy ta có thể vẽ đờng tròn bằng cách cho góc T chạy từ 0 cho đến 2*pi với bớc tăng là 1/R để lấy x:=Round(R*cos(T)) y:=Round(R*sin(T)) và vẽ điểm (x,y) nhng làm nh vậy lại phải tính sin và cos và do đó chơng trình sẽ không chạy nhanh nên ta sẽ cải tiến bằng các phơng pháp sau : 1. Vẽ đờng tròn bằng phơng pháp xấp xỉ : nh ta biết x=R*cos(T); y=R*sin(T), lấy đạo hàm theo T ta có : x'=-R*sin(T)=-y; y'=R*cos(T)=x (1) cho T chạy từ 0 đến 2*pi với bớc là h=1/R thì điểm thứ i sẽ có toạ độ là x[ih]=R*cos(ih); y[ih]=R*sin(ih) Theo 1 ta có : -y[ih]=x'[ih] bằng đạo hàm trái tại điểm T=ih xấp xỉ (x[(i+1)h]-x[ih])/h (h càng nhỏ thì độ chính xác càng lớn), nên -h.y[ih]=x[(i+1)h]-x[ih] => x[(i+1)h]=x[ih]-h.y[ih] (2) Và x[(i+1)h]=y'[(i+1)h] bằng đạo hàm phải tại điểm T=(i+1)h xấp xỉ (y[ih]- y[(i+1)h])/-h (h càng nhỏ thì độ chính xác càng lớn), nên -h.x[(i+1)h]=y[ih]-y[(i+1)h] => y[(i+1)h]=h.x[(i+1)h]+y[ih] (3) Từ (2) và (3) ta có x[(i+1)h]=x[ih]-h.y[ih] y[(i+1)h]=h.x[(i+1)h]+y[ih] Với giá trị ban đầu khi T=0 là x 0 =R.cos(0)=R, y 0 :=R.sin(0)=0; Ta có thể viết gọn lại với bớc h=1/R : Kỹ thuật Đồ hoạ máy tính 32 x[i+1]=x[i]-hy.[i] y[i+1]=h.x[i+1]+y[i] Với giá trị ban đầu là x 0 =R, y 0 =0 Và thủ tục để vẽ đờng tròn theo phơng pháp này là : Procedure Circle_(x1,y1,r : Integer); Var mau : Byte; d: LongInt; x,y,h: Real; Begin mau:=GetColor; h:=1/r; d:=0; x:=r; y:=0; Repeat Putpixel(x1+Round(x),y1+Round(y),mau); x:=x-h*y; y:=h*x+y; Inc(d); Until d*h>2*pi; End; 2. Thuật toán vẽ đờng tròn của Bresenham Thuật toán trên vẽ đờng tròn vẫn còn chậm vì phải tính toán liên quan với các số thực. Tơng tự nh với đờng thẳng, thuật toán vẽ đờng tròn của Bresanham chỉ thực hiện trên các số nguyên nên chạy nhanh hơn rất nhiều : Kỹ thuật Đồ hoạ máy tính 33 Cũng nh trên ta xét tâm ở gốc toạ độ, với chú ý rằng cứ một điểm thuộc đờng tròn thì bằng cách đối xứng qua các trục toạ độ, qua các đờng chéo ta có thể xác định đợc 8 điểm thuộc đờng tròn (nh hình vẽ). Do đó ta chỉ cần xét 1/8 cung tròn bắt đầu từ x=0,y=R và kết thúc khi x=y. Ta viết lại phơng trình đờng tròn thành y 2 =R 2 -x 2 Giả sử điểm (x[i], y[i]) là điểm thuộc đờng tròn, vị trí của điểm kế tiếp cần vẽ sẽ là (x[i]+1,y[i]) hoặc (x[i]+1,y[i]-1) và giá trị đúng của y sẽ đợc xét bằng phơng trình y 2 =R 2 -(x[i+1]) 2 Ta xét 2 độ dài : d1=(y[i]) 2 -y 2 =(y[i]) 2 -R 2 +(x[i]+1) 2 Và d2=y 2 -(y[i]-1) 2 =R 2 -(x[i]+1) 2 -(y[i]-1) 2 Do đó hiệu giữa d1 và d2 là : p[i]=d1-d2=2(x[i]+1) 2 +(y[i]) 2 +(y[i]-1) 2 -2R 2 Cũng giống nh đoạn thẳng nếu p[i] âm ta chọn y[i], ngợc lại chọn y[i]-1 Tăng i lên 1 đơn vị ta có p[i+1]=d1-d2=2(x[i+1]+1) 2 +(y[i+1]) 2 +(y[i+1]-1) 2 -2R 2 Nên p[i+1]=p[i]+4x[i]+6+2((y[i+1]) 2 -(y[i]) 2 )-2(y[i+1]-y[i]) mà y[i+1] hoặc bằng y[i] hoặc bằng y[i]-1 Nếu y[i+1]=y[i] thì p[i+1]=p[i]+4x[i]+6 Nếu y[i+1]=y[i]-1 thì p[i+1]=p[i]+4(x[i]-y[i])+10 Có thể tóm tắt các bớc nh sau : 1. Đặt x:=0; y:=r; p:=3-2*r; 2. Nếu x>y kết thúc nếu không thực hiện bớc 3 3. Vẽ 8 điểm (x, y); (-x, y); (x, -y); (-x, -y); (y, x); (-y, x); (y, -x); (-y, -x); 4. Nếu p<0 thì p:=p+4*x+6 Ngơc lại p:=p+4*(x-y)+10; và giảm y 1 đơn vị 5. Tăng x lên 1 đơn vị Kỹ thuật Đồ hoạ máy tính 34 6. Quay lại bớc 2 Và thủ tục để vẽ đờng tròn theo thuật toán Bresenham là : Procedure Circle_(x1,y1,r : Integer); Var mau : Byte; x,y,p: Integer; Procedure Put_Pixels(x,y: Integer); Begin Putpixel(x1+x,y1+y,mau); Putpixel(x1-x,y1+y,mau); Putpixel(x1+x,y1-y,mau); Putpixel(x1-x,y1-y,mau); Putpixel(x1+y,y1+x,mau); Putpixel(x1-y,y1+x,mau); Putpixel(x1+y,y1-x,mau); Putpixel(x1-y,y1-x,mau); End; Begin mau:=GetColor; x:=0; y:=r; p:=3-2*r; While x<=y Do Begin Put_pixels(x,y); If p<0 Then p:=p+4*x+6 Else Begin p:=p+4*(x-y)+10; Dec(y); End; Inc(x); End; End; Bài tập : 1. Lập chơng trình vẽ đờng tròn theo phơng pháp xấp xỉ 2. Lập thiện chơng trình vẽ đờng tròn theo thuật toán của Bresenham 3. Lập chơng trình vẽ đờng tròn theo tham số 4. Lập chơng trình vẽ Elip theo tham số 5. Tơng tự nh vẽ đờng tròn lập chơng trình vẽ Elip theo thuật toán của Bresenham . Putpixel(x1+x,y1+y,mau); Putpixel(x1-x,y1+y,mau); Putpixel(x1+x,y1-y,mau); Putpixel(x1-x,y1-y,mau); Putpixel(x1+y,y1+x,mau); Putpixel(x1-y,y1+x,mau); Putpixel(x1+y,y1-x,mau); Putpixel(x1-y,y1-x,mau);. lại chọn y[i ] -1 Tăng i lên 1 đơn vị ta có p[i +1] =d1-d2=2(x[i +1] +1) 2 +(y[i +1] ) 2 +(y[i +1 ] -1 ) 2 -2 R 2 Nên p[i +1] =p[i]+4x[i]+6+2((y[i +1] ) 2 -( y[i]) 2 )-2 (y[i +1] -y[i]) mà y[i +1] hoặc bằng y[i]. (x[i +1] ,y[i +1] ) tiếp theo, khi đó p[i +1] =2dy.x[i +1 ]-2 dx.y[i +1] +c (2) Trừ (2) cho (1) ta có : p[i +1] -p[i]=2dy(x[i +1] -x[i] )-2 dx(y[i +1] -y[i]) mà x[i +1] =x[i] +1 nên p[i +1] =p[i]+2dy-2dx(y[i +1] -y[i])