2. CÁC THUẬT TỐN VẼ ĐƯỜNG
2.1. Thuật tốn Cohen-Sutherland
Đây là một trong những thuật tốn ra đời sớm nhất và thơng dụng nhất.
Bằng cách kéo dài các biên của cửa sổ, người ta chia mặt phẳng thành chín vùng gồm cửa sổ và tám vùng xung quanh nĩ.
Khái niệm mã vùng (area code)
Một con số 4 bit nhị phân gọi là mã vùng sẽ được gán cho mỗi vùng để mơ tả vị trí tương đối của vùng đĩ so với cửa sổ. Bằng cách đánh số từ 1 đến 4 theo thứ tự từ phải qua trái, các bit của mã vùng được dùng theo quy ước sau để chỉ một trong bốn vị trí tương đối của vùng so với cửa sổ bao gồm : trái, phải, trên, dưới.
Bit 1 : trái (LEFT) Bit 2 : phải (RIGHT) Bit 3 : trên (TOP) Bit 4 : dưới (BOTTOM)
Giá trị 1 tương ứng với vị trí bit nào trong mã vùng sẽ chỉ ra rằng điểm đĩ ở vị trí tương ứng, ngược lại bit đĩ sẽ được đặt bằng 0. Ví dụ một vùng cĩ mã là 1001, thì nĩ sẽ nằm phía dưới (bit 4 bằng 1), bên trái (bit 1 bằng 1) so với cửa sổ, vùng cĩ mã là 0000 chính là cửa sổ.
Hình 4.7 – Mã vùng quy định vị trí tương đối của vùng so với cửa sổ
Các giá trị bit trong mã vùng được tính bằng cách xác định tọa độ của điểm thuộc vùng đĩ với các biên của cửa sổ. Bit 1 được đặt là 1 nếu , các bit khác được tính tương tự.
Thuật tốn
Gán mã vùng tương ứng cho các điểm đầu cuối của đoạn thẳng cần xén lần lượt là . Ta cĩ nhận xét :
•Các đoạn thẳng nằm hồn tồn bên trong cửa sổ sẽ cĩ , ứng với các đoạn này, kết quả sau khi xén là chính nĩ.
•Nếu tồn tại , sao cho với bit thứ k của đều cĩ giá trị 1, lúc này đoạn thẳng sẽ nằm về cùng phía ứng với bit k so với cửa sổ, do đĩ nằm hồn tồn ngồi cửa sổ. Đoạn này sẽ bị loại bỏ sau khi xén. Ví dụ, nếu , rõ ràng bit 1 của chúng đều bằng 1 (ứng với biên trái), do đĩ đoạn thẳng nằm hồn tồn về biên trái của cửa sổ. Để xác định tính chất này, đơn giản chỉ cần thực hiện phép tốn logic AND trên . Nếu kết quả khác 0000, đoạn thẳng sẽ nằm hồn tồn ngồi cửa sổ.
•Nếu khơng thuộc về hai trường hợp trên, đoạn thẳng cĩ thể hoặc khơng cắt ngang cửa sổ (ví dụ đoạn trong hình 4.6) chắc chắn sẽ tồn tại một điểm nằm ngồi cửa sổ, khơng mất tính tổng quát giả sử điểm đĩ là . Bằng cách xét mã vùng của là ta cĩ thể xác định được các biên mà đoạn thẳng cĩ thể cắt để từ đĩ chọn một biên và tiến hành tìm giao điểm của đoạn thẳng với biên đĩ. Lúc này, đoạn thẳng ban đầu được xén thành . Tới đây chúng ta lại lặp lại thao tác đã xét cho đoạn thẳng mới cho tới khi xác định được phần nằm trong hoặc loại bỏ tồn bộ đoạn thẳng.
Chúng ta minh họa thuật tốn bằng hình vẽ 4.8. Với đoạn thẳng , ta sẽ kiểm tra lần lượt với các biên trái, phải, dưới, trên và tìm ra điểm này nằm dưới cửa sổ, sau đĩ chúng ta tìm giao điểm của đoạn thẳng với biên dưới. Lúc này đoạn thẳng ban đầu được xén ngắn lại thành . Vì nằm ngồi cửa sổ nên bằng cách xét tương tự, chúng ta sẽ tiến hành tìm giao điểm của với biên trên và lúc này đoạn , chính là phần nằm hồn tồn trong cửa sổ.
Trong trường hợp đoạn , nằm bên trái cửa sổ nên chúng ta cĩ thể xác định điểm giao , và từ đĩ loại bỏ đoạn thẳng . Bằng cách kiểm tra mã vùng, chúng ta dễ dàng xác định được đoạn thẳng nằm hồn tồn bên dưới cửa sổ nên cĩ thể bỏ đi tồn bộ.
Hình 4.8 – Minh họa thuật tốn Cohen - Sutherland
Các điểm giao với các biên cửa sổ của đoạn thẳng cĩ thể được tính từ phương trình tham số như đã đề cập ở phần trên. Tung độ y của điểm giao đoạn thẳng với biên đứng của cửa sổ cĩ thể tính từ cơng thức , trong đĩ x cĩ thể là hay . Tương tự, hồnh độ x của điểm giao đoạn thẳng với biên ngang của cửa sổ cĩ thể tính từ cơng thức :
, trong đĩ y cĩ thể là hay .
Lưu đồ thuật tốn Cohen-Sutherland dùng để xén một đoạn thẳng qua hai điểm (x1,y1) và (x2,y2) vào cửa sổ hình chữ nhật cho trước
Cài đặt minh họa thuật tốn Cohen - Sutherland
#define TRUE 1 #define FALSE 0 #define LEFT 1 #define RIGHT 2 #define TOP 4 #define BOTTOM 8 typedef struct { int x, y; }POINT;
typedef struct {
int Left, Top, Right, Bottom; }RECT;
typedef int CODE;
#define Accept(a,b) (!(a|b)) #define Reject(a,b) (a&b) // Tra ve ma vung cua p la c
void EnCode(POINT p, CODE &c, RECT rWin) { c = 0; if(p.x < rWin.Left) c |= LEFT; if(p.x > rWin.Right) c |= RIGHT; if(p.y > rWin.Top) c |= TOP; if(p.y < rWin.Bottom) c |= BOTTOM; }
// Hoan vi hai diem p1 va p2 sao cho p1 luon nam ngoai cua so
void SwapPoint(POINT& p1, POINT &p2, CODE &c1, CODE &c2) {
if(!c1) // Neu p1 nam hoan toan trong cua so {
POINT p; p = p1; p1 = p2; p2 = p; CODE c; c = c1; c1 = c2; c2 = c; } }
// Tra ve TRUE neu co cat cua so. Nguoc lai tra ve FALSE
int CohenSutherlandClipping(POINT P1, POINT P2, POINT &Q1, POINT &Q2, RECT
rWin)
{
int fStop = FALSE, fResult = FALSE;
CODE c1, c2;
while(!fStop) {
EnCode(P1, c1, rWin);
EnCode(P2, c2, rWin);
// Neu duong thang nam hoan toan ben trong cua so
if(Accept(c1, c2)) {
fResult = TRUE;
} // Accept else
{
// Neu duong thang nam hoan toan ben ngoai cua so
if(Reject(c1,c2)) {
fStop = TRUE; // break
} // Reject
else // Xet truong hop duong thang co the cat cua so {
SwapPoint(P1, P2, c1, c2); float m;
if(P2.x!=P1.x)
m = float(P2.y-P1.y)/(P2.x-P1.x);
if(c1 & LEFT) { P1.y += (rWin.Left-P1.x)*m; P1.x = rWin.Left; } // Left else {
if(c1 & RIGHT) {
P1.y += (rWin.Right-P1.x)*m; P1.x = rWin.Right;
} // Right
else {
if(c1 & TOP) { if(P2.x!=P1.x) P1.x += (rWin.Top - P1.y)/m; P1.y = rWin.Top; } // Top else // Bottom { if(P2.x!=P1.x) P1.x += (rWin.Bottom - P1.y)/m; P1.y = rWin.Bottom; } // Bottom } }
} // Xet truong hop duong thang co the cat cua so }
} //while
Q1 = P1;
return (fResult);
} //CohenSutherlandClipping