Bài 7: Thuật toán vẽ đường Ellipse Midpoint
Trong đồ họa máy tính, khi muốn vẽ đường Elip , ngoài việc áp dụng thuật toán Bresenham (như bài 6), chúng ta còn có thể áp dụng một thuật toán nữa – đó chính là thuật toán Midpoint. Và thuật toán Midpoint , chúng ta cũng đã tìm hiểu khi dùng để vẽ đường thẳng ( tại bài 5) . Nếu bạn nào chưa đọc thì hãy vào xem phần này nha !
Đặt vấn đề
Vẽ elip bằng thuật toán Midpoint.
Giải thuật
Để đơn giản, chúng ta sẽ chọn vẽ hình Elip có tâm ở gốc tọa độ.
Phương trình của đường Elip:
f(x,y) = b2x2 + a2y2 – a2b2
- < 0 nếu (x,y) nằm bên trong elip.
- = 0 nếu (x,y) nằm trên elip.
- > 0 nếu (x,y) nằm bên ngoài elip.
*Ý tưởng và phân tích giải thuật:
- Chia Elip làm 2 phần tại điểm Q nơi có hệ số góc của tiếp tuyến với Elip bằng -1 (véc tơ gradient bằng 1) ( lý do tại sao lại phải chia làm 2 phần thì bài 6 mình đã giải thích rồi nhé). Tại vùng thứ nhất, x biến thiên nhanh hơn y và tại vùng thứ hai , y biến thiên nhanh hơn x. Nhớ lại công thức hệ số góc của đường cong :
dx/dy = fx/fy = (2b2x) /( 2a2y)
trong đó: fx và fy là đạo hàm riêng phần của f(x,y) theo x, theo y.
- Trong phần thứ nhất:
Giả sử đã vẽ được điểm (xi,yi) , điểm tiếp theo trên elip được chọn trong bước nhảy i+1 là T hoặc S. Trung điểm I của TS sẽ quyết định điểm nào được chọn. Giá trị của f(x,y) tại điểm I:
di = f(xi + 1, yi – ½) = b2(xi + 1)2 + a2(yi – ½)2 – a2b2
+Nếu di ≥ 0 thì trung điểm I nằm ngoài đường elip => điểm được chọn là S.
+Nếu di < 0 thì trung điểm I nằm trong đường elip => điểm được chọn là T.
Ta lại có:
di+1 = f(xi+1 + 1, yi+1 – ½) = b2(xi+1 +1)2 + a2(yi+1 – ½)2 – a2b2
Suy ra:
di+1 – di = b2[(xi+1 +1)2 – (xi +1)2] +a2[(yi+1 – ½)2 – (yi – ½)2]
Vì xi+1 = xi+1 nên
di+1 – di = 2b2xi+1 + b2 + a2[(yi+1 – ½)2 – (yi – ½)2]
+ Nếu điểm được chọn là T (di < 0) thì yi+1 = yi .
Ta có :
di+1 = di + 2b2xi+1 + b2 (= di + fx + b2)
= di + 2b2 (xi + 1) + b2
= di + b2 (2xi + 3)
+ Nếu điểm được chọn là S (di ≥ 0) thì yi+1 = yi – 1
Ta có:
di+1 = di + 2b2xi+1 + b2 – 2a2yi+1 (= di + fx + b2 – fy)
= di + 2b2 (xi + 1) + b2 – 2a2 (yi – 1)
= di + b2 (2xi + 3) + a2 (-2yi + 2)
Với điểm đầu tiên (0,b) ta có:
d1 = f(0,b) = b2 + a2(b – ½)2 – a2b2 = b2 ─ a2b + a2/4
- Trong phần thứ hai:
Chúng ta tính toán như phần 1. Giả sử ta phải xác định điểm (xj+1,yj+1) tiếp theo trên elip trong bước j+1. Điểm được chọn là U hoặc V. Trung điểm K của UV sẽ quyết định việc chọn điểm U hay điểm V. Giá trị của f(x,y) tại điểm K:
ej = f(xj + ½, yj – 1) = b2(xj + ½)2 + a2(yj – 1)2 – a2b2
+ Nếu ej ≥ 0 điểm được chọn là U.
+ Nếu ej < 0 điểm được chọn là V.
Ta lại có:
ej+1 = f(xj+1 + ½, yj+1 – 1) = b2(xj+1 + ½)2 + a2(yj+1 – 1)2 – a2b2
Suy ra:
ej+1 – ej = b2[(xj+1 + ½)2 – (xj + ½)2] + a2[(yj+1 – 1)2 – (yj – 1)2]
= b2[(xj+1 + ½)2 – (xj + ½)2] – 2a2yj+1 + a2
(Chú ý : yj+1 = yi – 1)
+ Nếu điểm được chọn là U (tức ej ≥ 0) thì xj+1 = xj
Ta có:
ej+1 = ej – 2a2yj+1 + a2 (= ej – fy + a2)
= ej – 2a2(yj -1) + a2
= ej – a2(3 – 2yj)
+ Nếu điểm được chọn là V (tức ej < 0 )thì xj+1 = xj+1
Ta có:
ej+1 = ej + 2b2xj+1 – 2a2yj+1 + a2 (= ej + fx – fy + a2)
= ej + 2b2(xj + 1)– 2a2(yj -1) + a2
= ej + b2(2xj + 2)+ a2(3 -2yj )
Giá trị khởi tạo ban đầu trong phần 2 phụ thuộc vào vị trí cuối cùng của phần 1 , giả sử là (xk,yk). Khi đó:
e1 = f(xk + ½, yk – 1) = b2(xk + ½) + a2(yk – 1)2 – a2b2
Lưu đồ thuật toán
*Q: Tại sao lại so sánh 2A2y < 0 ???
=> A: Do ở phần ý tưởng thuật toán, chúng ta chia elip làm 2 phần để vẽ và lấy điểm Q có hệ số góc của tiếp tuyến với elip bằng -1 làm giao điểm. Theo công thức hệ số đã nêu ở trên: dx/dy = fx/fy = (2b2x) /( 2a2y). Tại điểm đầu tiên có tọa độ (0,b), chúng ta thay x=0 và y=b vào công thức trên được hệ số góc= 0/( 2a2b) lớn hơn -1 . Nên ta chọn vẽ theo nhánh 1 . Như vậy, việc so sánh 2A2y < 0 là để chọn nhánh vẽ elip thôi.
Code minh họa
#include<graphics.h> #include<conio.h>//de dung man hinh dô hoa #include<iostream> #define ROUND(a) (int)(a+0.5) using namespace std; void plot(int xc, int yc, int x, int y, int color) { putpixel(xc+x, yc+y, color); putpixel(xc-x, yc+y, color); putpixel(xc+x, yc-y, color); putpixel(xc-x, yc-y, color); } void elipMidpoint(int xc,int yc, int a, int b, int color) { int x, y, fx, fy, a2, b2, p; x = 0; y = b; a2 = a*a; b2 = b*b; fx = 0; fy = 2 * a2 * y; plot(xc, yc, x, y, color); p = ROUND(b2 -(a2*b) + (0.25*a2));//p=b2 - a2*b +a2/4 while(fx<fy) { x++; fx += 2*b2; delay(50); if(p<0) { p += b2*(2*x + 3);//p=p + b2*(2x +3) } else { y--; p += b2*(2*x +3) + a2*(2- 2*y);//p=p +b2(2x +3) +a2(2-2y) fy -= 2*a2; } plot(xc, yc, x, y, color); } p = ROUND(b2*(x +0.5)*(x +0.5) + a2*(y-1)*(y-1) - a2*b2); // while(y>0) { y--; fy -= 2*a2; delay(50); if(p >=0) { p += a2*(3-2*y); //p=p +a2(3-2y) } else { x++; fx += 2*b2; p += b2*(2*x +2) +a2*(3- 2*y);//p=p+ b2(2x +2) + a2(3-2y) } plot(xc, yc, x, y, color); } } int main() { int gd=DETECT, gm; initgraph(&gd,&gm,""); elipMidpoint(getmaxx()/2, getmaxy()/2,150,80,15); getch(); closegraph(); return 0; }
Hình minh họa
Cảm ơn các bạn đã đọc bài viết . Mong bài viết giúp ích cho việc học tập của các bạn !