[Schooll_ĐHMT] Thuật toán Breshenham vẽ đoạn thẳng
Nội dung
1. Xây dựng thuật toán
2. Lưu đồ thuật toán
3. Code minh họa
4. Code cho mọi trường hợp
Đọc thêm
1. Nguyên lý chung vẽ đoạn thẳng
2. Thuật toán DDA vẽ đoạn thẳng
3. Thuật toán Midpoint vẽ đoạn thẳng
1. Xây dựng thuật toán Breshenham
Cho 2 điểm đầu mút M1 (x1, y1), M2(x2, y2) và màu vẽ C.
Trong bài nguyên lý chung vẽ đoạn thẳng chúng ta đã xây dựng phương trình đường thẳng có dạng:
Để đơn giản hóa giải thuật ta xét đường thẳng với
Tại mỗi bước ta cho x tăng lên 1 đơn vị tức là . Bước tiếp theo là ta tính nhưng ta sẽ không làm giống thuật toán DDA vì nó phải xử lỹ dữ liệu trên số thực và làm tròn số dẫn tới mất thời gian. Ta sẽ tìm các tính theo các số nguyên.
Giả sử tại bước thứ k ta có được hình vẽ như sau:
Gọi y là tung độ chính xác của . Ta sẽ lấy giá trị y là giá trị gần nhất với hay . Để làm việc này ta xét d1 và d2.
Nếu d1 > d2 khi đó
Nếu d1 < d2 khi đó
Do đó nếu ta xác định được dấu của d1 – d2 thì ta sẽ biết được giá trị của . Ta xét hằng số (Ta nhân với (x1-x2) để triệt tiêu được mẫu khi thay m vào khi tính toán).
Do Dx > 0 nên chỉ phụ thuộc vào d1 -d2. Ta có:
$latex
m = \frac{y2-y1}{x2-x1} \\
\Rightarrow P_{k} = (x2-x1)(d1 – d2) \\
\indent \indent= (x2-x1).\frac{2(y2-y1)(x_{k}+1) +(x2-x1)(-2y_{k} + 2b – 1)}{x2-x1} \\
\indent \indent = 2(y2-y1)x_{k} – 2(x2-x1)y_{k} + 2(y2-y1) + (x2-x1)(2b-1) \\
\indent \indent = 2x_{k}Dy – 2y_{k}.Dx + c
\\
\Rightarrow P_{k+1} = 2(x_{k}+1)Dy – 2y_{k+1}Dx + c, voi : \left (c= 2Dy + (2b-1)Dx \right ) \\
\\
TH1: P_{k} \geq 0 \Rightarrow d1 \geq d2 \\
\indent \indent \Rightarrow y_{k+1} = y_{k} + 1 \\
\indent \indent \Rightarrow P_{k+1} = 2(x_{k}+1)Dy – 2(y_{k} + 1)Dx + c = P_{k} + 2(Dy – Dx) \\
\\
TH2: P_{k} < 0 \Rightarrow d1 < d2 \\
\indent \indent \Rightarrow y_{k+1} = y_{k} \\
\indent \indent \Rightarrow P_{k+1} = 2(x_{k}+1)Dy - 2y_{k}Dx + c = P_{k} + 2Dy \\
$
Tính P1, ta có:
$latex
y1 = m.x1 + b = \frac{Dy}{Dx}x1 + b\\
P_{1} = 2x1.Dy - 2y1.Dx + c
= 2x1.Dy - 2\left ( \frac{Dy}{Dx}x1 + b \right )Dx + 2Dy + (2b - 1)Dx\\
= 2Dy - Dx
$
2. Lưu đồ thuật toán
Thuật toán Bresenham chỉ thao tác trên số nguyên và chỉ tính toán trên phép cộng và phép nhân 2 (phép dịch bit). Điều này là một cải tiến làm tăng tốc độ đáng kể so với thuật toán DDA. Ý tưởng chính của thuật toán này là ở chỗ xét dấu P i để quyết định điểm kế tiếp.
3. Code minh họa
/* Program create in Linux OS * nguyenvanquan7826 */ #include <graphics.h> #define DELAY 10 int color = RED; void Bresenham(int x1, int y1, int x2, int y2){ int x, y, Dx, Dy, p, c1, c2; Dx = abs(x2 - x1); Dy = abs(y2 - y1); p = 2*Dy - Dx; c1 = 2*Dy; c2 = 2*(Dy-Dx); x = x1; y = y1; int x_unit = 1, y_unit = 1; putpixel(x,y,color); while(x != x2){ delay(DELAY); if (p<0) p += c1; else{ p += c2; y += y_unit; } x += x_unit; putpixel(x, y, color); } } int main(){ int gd,gm=VGAMAX; gd=DETECT; initgraph(&gd,&gm,NULL); setbkcolor(WHITE); Bresenham(50,150, 300, 200); // ve duong thang getchar(); return 0; }
4. Code cho mọi trường hợp
Code sau sẽ vẽ 8 đoạn thẳng theo 8 hướng, điểm bắt đầu từ tâm.
/* Program create in Linux OS * nguyenvanquan7826 */ #include <graphics.h> #define DELAY 10 int colorRedBlue = BLUE; struct point { int x, y; }; void lineBresenham(int x1, int y1, int x2, int y2){ int x, y, Dx, Dy, p, c1, c2; Dx = abs(x2 - x1); Dy = abs(y2 - y1); p = 2*Dy - Dx; c1 = 2*Dy; c2 = 2*(Dy-Dx); x = x1; y = y1; int x_unit = 1, y_unit = 1; if (x2 - x1 < 0) x_unit = -x_unit; if (y2 - y1 < 0) y_unit = -y_unit; if (x1 == x2) // duong thang dung { while (y != y2+1) { delay(DELAY); y += y_unit; putpixel(x, y, colorRedBlue); } } else if (y1 == y2) // duong ngang { while (x != x2+1) { delay(DELAY); x += x_unit; putpixel(x, y, colorRedBlue); } } else{ putpixel(x, y, colorRedBlue); while(x != x2){ delay(DELAY); if (p<0) p += c1; else{ p += c2; y += y_unit; } x += x_unit; putpixel(x, y, colorRedBlue); } } } int main(){ int gd,gm=VGAMAX; gd=DETECT; initgraph(&gd,&gm,NULL); setbkcolor(WHITE); int x = 100, y = 200; point A[9] = {{getmaxx()/2, getmaxy()/2}, {A[0].x-x, A[0].y-y}, {A[0].x-x, A[0].y+y}, {A[0].x+x, A[0].y+y}, {A[0].x+x, A[0].y-y}, {A[0].x, A[0].y-y}, {A[0].x-x, A[0].y}, {A[0].x, A[0].y+y}, {A[0].x+x, A[0].y}, }; for (int i=1; i<9; i++) { lineBresenham(A[0].x, A[0].y, A[i].x, A[i].y); } getchar(); return 0; }
Tuy nhiên do lý do làm tròn liên tục y và giá trị làm tròn đó lại được lưu lại để tính toán tiếp nên đường thẳng vẽ bằng thuật toán này không chính xác. Ta có thể quan sát sự khác biệt giữa thuật toán này và DDA với cùng 1 hệ các đường thẳng.
A[] = { {getmaxx()/2, getmaxy()/2}, {A[0].x-x, A[0].y-y}, {A[0].x-x, A[0].y+y}, {A[0].x+x, A[0].y+y}, {A[0].x+x, A[0].y-y}, {A[0].x, A[0].y-y}, {A[0].x-x, A[0].y}, {A[0].x, A[0].y+y}, {A[0].x+x, A[0].y} }
Các đường thẳng màu xanh là của Breshenham, màu hồng là của DDA. (DDA chính xác hơn). Các đường ngang và dọc thì chúng khớp hoàn toàn với nhau. Và dưới đây là đoạn code cho thuật toán Breshenham cải tiến.
// Ve duong thang Bresenham Cai tien void lineBresenham_1(int x1, int y1, int x2, int y2) { int c2, c, Dx, Dy, x, y; Dx = abs(x2 - x1); Dy = abs(y2 - y1); c = Dx - Dy; c2 = 2*c; x = x1; y = y1; int x_unit = 1, y_unit = 1; if (x2 - x1 < 0) x_unit = -x_unit; if (y2 - y1 < 0) y_unit = -y_unit; putpixel(x, y, colorGreen); if (x1 == x2) // duong thang dung { while (y != y2) { delay(DELAY); y += y_unit; putpixel(x, y, colorGreen); } } else if (y1 == y2) // duong ngang { while (x != x2) { delay(DELAY); x += x_unit; putpixel(x, y, colorGreen); } } else if (x1 != x2 && y1 != y2) // duong xien { while(x != x2+1) { delay(DELAY); c2 =2*c; if (c2 > -Dy) { c = c - Dy; x = x + x_unit; } if (c2 < Dx) { c = c + Dx; y = y + y_unit; } putpixel(x, y, colorGreen); } } }
Khi chạy bằng đoạn code này so với DDA thì chúng hoàn toàn trùng khớp
Thầy cho e hỏi là với m>1 thì thuật toán bresenham sẽ được mô tả như thế nào ạ
Bạn xem phần code cho mọi trường hợp nhé.
Đúng chỗ mình đang tìm. Hướng dẫn chi tiết và dễ hiểu quá. Cảm ơn nhiều nhé
Thuật toán này có áp dụng được cho việc điều khiển 2 động cơ bước di chuyển đầu công tác theo 1 đường thẳng đúng không bạn. Nhưng mình vẫn chưa hiểu cách xuất xung như thế nào theo thuật toán này ? Hy vọng bạn giúp mình trong trường hợp này !
Cái thuật toán này là vẽ đường thẳng. Bạn nói đến xuất xung thì mình chịu, mù tịt không giúp được bạn rồi.
a cho e hỏi tại sao lại đặt c = Dx – Dy; và thuật toán sau được hiểu như thế nào
else if (x1 != x2 && y1 != y2) // duong xien
{
while(x != x2+1)
{
delay(DELAY);
c2 =2*c;
if (c2 > -Dy)
{
c = c – Dy;
x = x + x_unit;
}
if (c2 < Dx)
{
c = c + Dx;
y = y + y_unit;
}
putpixel(x, y, colorGreen);
}
}
Đây là code đã được chuyển sang từ thuật toán đã xây dựng. Bạn xem lại thuật toán nhé.
thuật toán cải tiến tư tưởng của nó như thế nào?
Tư tưởng của nó là ko làm tròn các số nữa mà dựa vào tỷ lệ đường chéo để đi cho đúng hướng.
#include
#include
#include
#define c CYAN
int breseham (int x1, int y1, int x2, int y2){
int dx = abs (x2 – x1);
int dy = abs (y2 – y1);
int x_unit = 1, y_unit =1;
if (x2-x1 <0)
x_unit = -x_unit;
if (y2-y1 <0)
y_unit = -y_unit;
if (dx==0 && dy!=0){
for (int i=0; i<=dy; i++){
putpixel (x1,y1,c);
y1+= y_unit;
}
}
else if (dy==0 && dx!=0){
for (int j=0; j=dy){
int p= 2*dy-dx;
int c1 = 2*dy, c2 = 2*dy – 2*dx;
while (x1!=x2){
if (p<0)
p+=c1;
else{
y1+= y_unit;
p+=c2;
}
putpixel (x1,y1,c);
x1 +=x_unit;
}
}
else {
int p=2*dx-dy;
int c1 = 2*dx, c2=2*dx – 2*dy;
while (x1!=x2){
if (p<0)
p+=c1;
else {
x1+=x_unit;
p+=c2;
}
putpixel (x1,y1,c);
y1+=y_unit;
}
}
}
}
int main (){
int x1, y1,x2,y2;
printf ("nhap toa do diem A(x1,y1), B(x2,y2)");
printf ("\n x1= "); scanf ("%d",&x1);
printf ("\n y1= "); scanf ("%d",&y1);
printf ("\n x2= "); scanf ("%d",&x2);
printf ("\n y2= "); scanf ("%d",&y2);
initwindow (400,400);
breseham(x1,y1,x2,y2);
getch();
}
anh cho em hỏi khai báo vậy đúng chưa
dòng code này int gd,gm=VGAMAX; gd=DETECT; dùng để làm gì vậy anh?
Khởi tạo các giá trị ban đầu cho chế độ đồ họa nhé.
thuật toán cải tiến có vấn đề nha anh vì:
x1=x2+1 thì code sẽ vẽ sai
dx=1,2,3.. tức dx càng nhỏ thì code cũng vẽ sai nha anh.
Cảm ơn bạn, mình sẽ xem lại ^^
Có câu lệnh nào mà khởi tạo màn hình đồ họa với kích thước tùy theo kích thước của màn hình mà ta không cần phải đặt giá trị cho nó không nhỉ