ĐỀ THI CÓ 2 CÂU ĐƯỢC CHỌN TRONG 2 PHẦN
MỖI CÂU 5 ĐIỂM
SINH VIÊN KHÔNG ĐƯỢC XEM TÀI LIỆU
PHẦN I
16 CÂU, CHỌN 1 TRONG 16 CÂU, MỖI CÂU 5
ĐIỂM
Câu 1:
Xây dựng và cài đặt thuật toán vẽ đoạn thẳng đi qua hai điểm
),(
11
yx
và
),(
22
yx
.
Quy ước vẽ từ
),(
11
yx
đến
),(
22
yx
Đáp án:
Ta phân biệt các trường hợp:
Đường ngang và
21
xx ≤
hoặc
21
xx >
Đường dọc và
21
yy ≤
hoặc
21
yy >
Trị tuyệt đối hệ số góc
1|| ≤m
và
21
xx ≤
hoặc
21
xx >
Trị tuyệt đối hệ số góc
1|| >m
và
21
yy ≤
hoặc
21
yy >
Ta có phương trình đường thẳng đi qua 2 điểm
),(
11
yx
và
),(
22
yx
là
12
1
12
1
yy
yy
xx
xx
−
−
=
−
−
.
hay
11
12
12
)( yxx
xx
yy
y +−
−
−
=
, đặt
12
12
xx
yy
m
−
−
=
và
11
ymxb +−=
. Vậy ta có phương trình
bmxy +=
.
int Round(double a)
{
return (int)(a+0.5);
}
void Line(HDC hdc, int x1, int y1, int x2, int y2)
{
int x, y;
double m, b;
COLORREF Color = RGB(255,0,0);
// Duong ngang
if (y1 == y2)
if (x1 <= x2)
for (x=x1; x<=x2; x++)
SetPoint(hdc,x,y1,Color);
else
for (x=x1; x>=x2; x )
SetPoint(hdc,x,y1,Color);
// Duong doc
else if (x1 == x2)
if (y1 <= y2)
for (y=y1; y<=y2; y++)
SetPoint(hdc,x1,y,Color);
else
for (y=y1; y>=y2; y )
SetPoint(hdc,x1,y,Color);
else {
m = 1.0*(y2-y1)/(x2-x1);
b = -m*x1 + y1;
if (fabs(m) <= 1)
if (x1 <= x2)
for (x=x1; x<=x2; x++) {
y = Round(m*x+b);
SetPoint(hdc,x,y,Color);
}
else
for (x=x1; x>=x2; x ) {
y = Round(m*x+b);
SetPoint(hdc,x,y,Color);
}
else {
m = 1.0*(x2-x1)/(y2-y1);
b = -m*y1 + x1;
if (y1 <= y2)
for (y=y1; y<=y2; y++) {
x = Round(m*y+b);
SetPoint(hdc,x,y,Color);
}
else
for (y=y1; y>=y2; y ) {
x = Round(m*y+b);
SetPoint(hdc,x,y,Color);
}
}
}
return;
}
Câu 2:
Xây dựng và cài đặt thuật toán vẽ đường tròn có phương trình là
222
ryx =+
ra chính
giữa màn hình.
Đáp án:
Ta có phương trình đường tròn là
222
ryx =+
Do tính đối xứng của đường tròn, nên ta chỉ cần vẽ cung I, các cung còn lại đều suy ra từ
cung I.
Quan sát trên hình vẽ, ta thấy ở cung I, x tăng nhanh hơn y, nên ta dùng phương trình
)(xfy =
, hay
22
xry −=
, tức là khi lập trình ta cho x chạy rồi tính y.
int Round(double a)
{
return (int)(a+0.5);
}
void Circle(HWND hWnd)
{
HDC hdc;
RECT rt;
int xc, yc;
hdc = GetDC(hWnd);
GetClientRect(hWnd,&rt);
xc = rt.right/2;
yc = rt.bottom/2;
int x, y, r = 100;
x = 0;
y = r;
SetPixel(hdc,xc+x,yc-y,RGB(255,0,0));
SetPixel(hdc,xc+y,yc-x,RGB(255,0,0));
SetPixel(hdc,xc+y,yc+x,RGB(255,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-y,yc+x,RGB(255,0,0));
SetPixel(hdc,xc-y,yc-x,RGB(255,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(255,0,0));
while (x < y) {
x++;
y = Round(sqrt(1.0*r*r-x*x));
SetPixel(hdc,xc+x,yc-y,RGB(255,0,0));
SetPixel(hdc,xc+y,yc-x,RGB(255,0,0));
SetPixel(hdc,xc+y,yc+x,RGB(255,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-y,yc+x,RGB(255,0,0));
SetPixel(hdc,xc-y,yc-x,RGB(255,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(255,0,0));
}
ReleaseDC(hWnd,hdc);
return;
}
Câu 3: Xây dựng và cài đặt thuật toán vẽ ellipse có phương trình là
1
2
2
2
2
=+
b
y
a
x
ra
chính giữa màn hình.
Đáp án:
Ta có phương trình của ellipse là
1
2
2
2
2
=+
b
y
a
x
sát trên đồ thị, ta thấy:
Cung I có x tăng nhanh hơn y, nên ta dùng phương trình
22
xa
a
b
y −=
, tức là khi lập
trình ta cho x chạy rồi tính y.
Cung II có y tăng nhanh hơn x, nên ta dùng phương trình
22
yb
b
a
x −=
, tức là khi lập
trình ta cho y chạy rồi tính x.
Để xác định được điểm chuyển tiếp A(
)
,
AA
yx
, ta cho đạo hàm cấp một của phương trình
22
xa
a
b
y −=
bằng -1.
1'
22
−=
−
−=
xaa
bx
y
22
2
ba
a
x
A
+
=
Như vậy ta chỉ cần vẽ cung I và II, các cung còn lại lấy đối xứng là được.
int Round(double a)
{
return (int)(a+0.5);
}
void Ellipse(HWND hWnd)
{
HDC hdc;
RECT rt;
int xc, yc;
hdc = GetDC(hWnd);
int x, y;
GetClientRect(hWnd,&rt);
xc = rt.right/2;
yc = rt.bottom/2;
int a = 300, b = 200;
int xa = Round(a*a/sqrt(1.0*a*a+b*b));
x=0; y=b;
SetPixel(hdc,xc+x,yc-y,RGB(255,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(255,0,0));
while (x<xa) {
x++;
y = Round(1.0*b/a*sqrt(1.0*a*a-x*x));
SetPixel(hdc,xc+x,yc-y,RGB(255,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(255,0,0));
}
while (y>0) {
y ;
x = Round(1.0*a/b*sqrt(1.0*b*b-y*y));
SetPixel(hdc,xc+x,yc-y,RGB(255,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(255,0,0));
}
ReleaseDC(hWnd,hdc);
return;
}
Câu 4: Xây dựng và cài đặt thuật toán vẽ parabola
a
x
y
2
=
có a dương ra chính giữa màn
hình.
Đáp án:
Quan sát trên đồ thị, ta thấy
Cung I có x tăng nhanh hơn y nên ta dùng phương trình
a
x
y
2
=
, tức là khi lập trình ta
cho x chạy rồi tính y.
Cung II có y tăng nhanh hơn x nên ta dùng phương trình
ayx =
, tức là khi lập trình ta
cho y chạy rồi tính x.
Để xác định được điểm chuyển tiếp A(
)
,
AA
yx
, ta cho đạo hàm cấp một của phương trình
a
x
y
2
=
bằng 1.
1
2
' ==
a
x
y
hay
2
a
x
A
=
Như vậy ta chỉ cần vẽ cung I và II, nhánh còn lại của parabola lấy đối xứng là được.
void Parabola(HWND hWnd)
{
HDC hdc;
RECT rt;
int xc, yc;
hdc = GetDC(hWnd);
int x, y;
int a = 100;
int xa = Round(1.0*a/2);
GetClientRect(hWnd,&rt);
xc = rt.right/2;
yc = rt.bottom/2;
x=0;
y=0;
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
while (x < xa) {
x++;
y = Round(1.0*x*x/a);
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(0,0,0));
}
while (y < 250) {
y++;
x = Round(sqrt(1.0*a*y));
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(0,0,0));
}
ReleaseDC(hWnd,hdc);
return;
}
Câu 5: Xây dựng và cài đặt thuật toán vẽ đường
xy sin=
trong một chu kỳ ra chính
giữa màn hình.
Đáp án:
Ta phải chuẩn hóa phương trình
xy sin=
để có thể vẽ ra màn hình, chẳng hạn ta chuẩn
hóa như sau:
N
x
Ay
π
2
sin=
với x = 0 N
Quan sát trên đồ thị, ta nhận thấy:
Cung I có x tăng hơn y nên ta dùng phương trình
N
x
Ay
π
2
sin=
, tức là khi lập trình ta cho
x chạy rồi tính y.
Cung II có y tăng hơn x nên ta dùng phương trình
=
A
yN
x arcsin
2
π
, tức là khi lập trình
ta cho y chạy rồi tính x.
Để xác định được điểm chuyển tiếp A(
)
,
AA
yx
, ta cho đạo hàm cấp một của phương trình
N
x
Ay
π
2
sin=
bằng 1.
1
2
cos
2
' =
=
N
x
N
A
y
ππ
=
A
N
ar
N
x
A
ππ
2
cos
2
Như vậy ta chỉ cần vẽ cung I và II, các cung còn lại lấy đối xứng là được.
int Round(double a)
{
return (int)(a+0.5);
}
void Sin(HWND hWnd)
{
HDC hdc;
RECT rt;
double pi = 4.0*atan(1.0);
int xc, yc;
int x, y;
int A = 200, N = 400;
int xa = Round(N/(2*pi)*acos(N/(2*pi*A)));
hdc = GetDC(hWnd);
GetClientRect(hWnd,&rt);
xc = rt.right/2;
yc = rt.bottom/2;
x = N/4; y = A;
SetPixel(hdc,x,yc-y,RGB(0,0,0));
SetPixel(hdc,N/2-x,yc-y,RGB(0,0,0));
SetPixel(hdc,N/2+x,yc+y,RGB(0,0,0));
SetPixel(hdc,N-x,yc+y,RGB(0,0,0));
while (x>xa) {
x ;
y = Round(A*sin(2*pi*x/N));
SetPixel(hdc,x,yc-y,RGB(0,0,0));
SetPixel(hdc,N/2-x,yc-y,RGB(0,0,0));
SetPixel(hdc,N/2+x,yc+y,RGB(0,0,0));
SetPixel(hdc,N-x,yc+y,RGB(0,0,0));
}
while (y>0) {
y ;
x = Round(N/(2*pi)*asin(1.0*y/A));
SetPixel(hdc,x,yc-y,RGB(0,0,0));
SetPixel(hdc,N/2-x,yc-y,RGB(0,0,0));
SetPixel(hdc,N/2+x,yc+y,RGB(0,0,0));
SetPixel(hdc,N-x,yc+y,RGB(0,0,0));
}
ReleaseDC(hWnd,hdc);
return;
}
Câu 5: Xây dựng và cài đặt thuật toán DDA (Digital Differential Analyzer)
vẽ đoạn thẳng đi qua hai điểm
),(
11
yx
và
),(
22
yx
. Giới hạn chỉ xét hệ số góc
10
≤<
m
và
21
xx <
.
Đáp án:
Ta có phương trình đường thẳng đi qua 2 điểm
),(
11
yx
và
),(
22
yx
là
12
1
12
1
yy
yy
xx
xx
−
−
=
−
−
.
Do giới hạn hệ số góc
10
≤<
m
, tức là x tăng nhanh hơn y nên ta dùng phương trình
)(xfy =
, hay
11
12
12
)( yxx
xx
yy
y +−
−
−
=
, đặt
12
12
xx
yy
m
−
−
=
và
11
ymxb +−=
. Vậy ta có phương trình
bmxy +=
.
Ở bước k ta có
bmxy
kk
+=
Ở bước k+1 ta có
bmxy
kk
+=
++ 11
Do x tăng nhanh hơn y nên
1
1
+=
+ kk
xx
Suy ra
mbmxbxmy
kkk
++=++=
+
)1(
1
Hay
myy
kk
+=
+1
So sánh phương trình
bmxy
kk
+=
với phương trình
myy
kk
+=
+1
, ta thấy bớt đi một
phép nhân.
Đại lượng
kk
kk
xx
yy
−
−
+
+
1
1
được gọi là vi phân số (sai phân), do
myy
xx
yy
kk
kk
kk
=−=
−
−
+
+
+
1
1
1
nên
thuật toán có tên là bộ phân tích sai phân.
int Round(double a)
{
return (int)(a+0.5);
}
void DDALine(HWND hWnd, int x1, int y1, int x2, int y2)
{
HDC hdc;
hdc = GetDC(hWnd);
double x,y,m;
x = x1;
y = y1;
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
if (x1 == x2)
if (y1<y2)
while (y<y2) {
y++;
Sleep(20);
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
}
else
while (y>y2) {
y ;
Sleep(20);
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
}
else if (y1 == y2)
if (x1<x2)
while (x<x2) {
x++;
Sleep(20);
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
}
else
while (x>x2) {
x ;
Sleep(20);
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
}
else {
m = 1.0*(y2-y1)/(x2-x1);
if (fabs(m)<=1)
if (x1<x2)
while (x<x2) {
x++;
y = y+m;
Sleep(20);
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
}
else
while (x>x2) {
x ;
y = y-m;
Sleep(20);
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
}
else
if (y1<y2)
while (y<y2) {
y++;
x = x+1/m;
Sleep(20);
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
}
else
while (y>y2) {
y ;
x = x-1/m;
Sleep(20);
SetPixel(hdc,Round(x),Round(y),RGB(255,0,0));
}
}
ReleaseDC(hWnd,hdc);
return;
}
Câu 6:
Xây dựng và cài đặt thuật toán Bresenham vẽ đoạn thẳng đi qua hai điểm
),(
11
yx
và
),(
22
yx
. Giới hạn chỉ xét hệ số góc
10 ≤< m
và
21
xx <
.
Đáp án:
Ta có phương trình đường thẳng đi qua 2 điểm
),(
11
yx
và
),(
22
yx
là
12
1
12
1
yy
yy
xx
xx
−
−
=
−
−
.
Do giới hạn hệ số góc
10
≤<
m
, tức là x tăng nhanh hơn y nên ta dùng phương trình
)(xfy =
, hay
11
12
12
)( yxx
xx
yy
y +−
−
−
=
, đặt
12
12
xx
yy
m
−
−
=
và
11
ymxb +−=
. Vậy ta có phương trình
bmxy +=
.
Do giới hạn hệ số góc
10
≤<
m
và
21
xx <
, nên ở:
Bước k ta có
),(
kk
yx
Bước
1
+
k
ta có
),1(),(
111 +++
+=
kkkk
yxyx
, trong đó
kk
yy =
+1
hoặc
1
1
+=
+ kk
yy
Mục đích của thuật toán Bresenham là dùng toàn số nguyên để tính toán cho nhanh.
Thuật toán gồm 3 bước:
Bước 1: Tính
1
d
và
2
d
kk
yyd −=
+11
12
1
+
−+=
kk
yyd
nên
1
d
và
2
d
dương.
Gọi
21
ddd −=
Nhận xét: nếu
0<d
thì
kk
yy =
+1
0≥d
thì
1
1
+=
+ kk
yy
Ta có:
122
1
−−=
+ kk
yyd
12))1((2 −−++=
kk
ybxmd
12222 −−++=
kk
ybmmxd
12)(222
11
−−+−++=
kk
yymxmmxd
Ta nhận thấy d có đại lượng
12
12
xx
yy
m
−
−
=
là số thực, nên d là số thực do đó ta phải
chuyển thành số nguyên, nếu không chuyển được thành số nguyên thì ta dùng phương
pháp bình thường để vẽ đường.
Đặt
12
yydy −=
và
12
xxdx −=
.
Nhân 2 vế của d cho dx để tạo ra
k
p
nguyên.
dxdydxydyxdxydyxddxp
kkk
−++−−== 22222
11
Do giới hạn
21
xx <
nên
12
xxdx −=
dương, nên nhận xét về
k
p
cũng như nhận xét về
d, tức là nếu
0<
k
p
thì
kk
yy =
+1
0≥
k
p
thì
1
1
+=
+ kk
yy
Bước 2: Đệ qui
Nhắc lại
dxdydxydyxdxydyxddxp
kkk
−++−−== 22222
11
Suy ra
dxdydyxdyxdxydyxp
kkk
−++−−=
+++
22222
11111
Do
1
1
+=
+ kk
xx
, nên
)(22
11 kkkk
yydxdypp −−=−
++
hay
)(22
11 kkkk
yydxdypp −−+=
++
Nếu
0<
k
p
thì
kk
yy =
+1
, hay
dypp
kk
2
1
+=
+
Nếu
0≥
k
p
thì
1
1
+=
+ kk
yy
, hay
dxdypp
kk
22
1
−+=
+
Bước 3: Tính p ban đầu
Dùng phương trình
dxdydxydyxdxydyxddxp
kkk
−++−−== 22222
11
Tại điểm ban đầu thì
),(),(
11
yxyx
kk
=
nên
dxdydxydyxdxydyxp −++−−= 22222
1111
dxdyp −= 2
void BresenhamLine(HWND hWnd)
{
HDC hdc;
hdc = GetDC(hWnd);
int x1,y1,x2,y2;
x1 = 100; y1 = 100; x2 = 400; y2 = 300;
int dx = x2-x1, dy = y2-y1;
int p = 2*dy - dx;
int x, y;
x = x1;
y = y1;
SetPixel(hdc,x,y,RGB(255,0,0));
while (x<x2) {
if (p<0)
p = p + 2*dy;
else {
p = p + 2*dy - 2*dx;
y++;
}
x++;
Sleep(20);
SetPixel(hdc,x,y,RGB(255,0,0));
}
ReleaseDC(hWnd,hdc);
return;
}
Câu 7: Xây dựng và cài đặt thuật toán vẽ đường tròn có phương trình là
222
ryx =+
ra
chính giữa màn hình bằng thuật toán Bresenham.
Đáp án:
Do tính đối xứng của đường tròn, nên ta chỉ cần vẽ cung
8
1
, các cung còn lại lấy đối
xứng là được. Cung cần vẽ có x tăng nhanh hơn y, nên ta dùng phương trình
)(xfy =
,
hay
22
xry −=
, tức là khi lập trình ta cho x chạy rồi tính y.
Bước k ta có
),(
kk
yx
Bước
1+k
ta có
),1(),(
111 +++
+=
kkkk
yxyx
, trong đó
kk
yy =
+1
hoặc
1
1
−=
+ kk
yy
Mục đích của thuật toán Bresenham là dùng toàn số nguyên để tính toán cho nhanh.
Thuật toán gồm 3 bước:
Bước 1: Tính
1
d
và
2
d
)1(
11
−−=
+ kk
yyd
12 +
−=
kk
yyd
nên
1
d
và
2
d
dương.
Gọi
21
ddd −=
Nhận xét: nếu
0<d
thì
1
1
−=
+ kk
yy
0≥d
thì
kk
yy =
+1
Ta có
122
1
+−=
+ kk
yyd
122
2
1
2
+−−=
+ kk
yxrd
Ta nhận thấy d có đại lượng
2
1
2
+
−
k
xr
là số thực, nên d là số thực do đó ta phải chuyển
thành số nguyên, nếu không chuyển được thành số nguyên thì ta dùng phương pháp bình
thường để vẽ đường tròn.
Đặt
22
11
)1( −−=
+ kk
yyp
2
1
2
2 +
−=
kk
yyp
Gọi
21
ppp
k
−=
Nhận xét: nếu
0<
k
p
thì
1
1
−=
+ kk
yy
0≥
k
p
thì
kk
yy =
+1
Ta thấy
k
p
nguyên.
Ta có
222
1
)1( +−=
+ kk
xry
1222
22
1
−+−=
+ kkkk
yyyp
322242
222
−++−−−= ryyxxp
kkkkk
Bước 2: Đệ qui
Nhắc lại
322242
222
−++−−−= ryyxxp
kkkkk
Suy ra
322242
2
1
2
11
2
11
−++−−−=
+++++
ryyxxp
kkkkk
Ta có
1
1
+=
+ kk
xx
, nên
)(2)(264
1
22
11 kkkkkkk
yyyyxpp −+−−−−=−
+++
Hay
)(2)(264
1
22
11 kkkkkkk
yyyyxpp −+−−−−=
+++
Nếu
0<
k
p
thì
1
1
−=
+ kk
yy
, hay
1044
1
−+−=
+ kkkk
yxpp
Nếu
0≥
k
p
thì
kk
yy =
+1
, hay
64
1
−−=
+ kkk
xpp
Bước 3: Tính p ban đầu
Dùng phương trình
322242
222
−++−−−= ryyxxp
kkkkk
Tại điểm ban đầu thì
),0(),( ryx
kk
=
Nên
32 −= rp
void BresenhamCircle(HWND hWnd)
{
HDC hdc;
RECT rt;
int xc, yc;
hdc = GetDC(hWnd);
int x, y, p;
GetClientRect(hWnd,&rt);
xc = rt.right/2;
yc = rt.bottom/2;
int r = 200;
x=0; y=r;
SetPixel(hdc,xc+x,yc-y,RGB(255,0,0));
SetPixel(hdc,xc+y,yc-x,RGB(255,0,0));
SetPixel(hdc,xc+y,yc+x,RGB(255,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-y,yc+x,RGB(255,0,0));
SetPixel(hdc,xc-y,yc-x,RGB(255,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(255,0,0));
p = 2*r-3;
while (x<y) {
if (p<0) {
p = p - 4*x + 4*y - 10;
y ;
}
else
p = p - 4*x - 6;
x++;
SetPixel(hdc,xc+x,yc-y,RGB(255,0,0));
SetPixel(hdc,xc+y,yc-x,RGB(255,0,0));
SetPixel(hdc,xc+y,yc+x,RGB(255,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(255,0,0));
SetPixel(hdc,xc-y,yc+x,RGB(255,0,0));
SetPixel(hdc,xc-y,yc-x,RGB(255,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(255,0,0));
}
ReleaseDC(hWnd, hdc);
return;
}
Câu 8: Xây dựng và cài đặt thuật toán vẽ ellipse có phương trình là
1
2
2
2
2
=+
b
y
a
x
ra chính
giữa màn hình bằng thuật toán Bresenham.
Đáp án:
Quan sát trên đồ thị, ta thấy:
Cung I có x tăng nhanh hơn y, nên ta dùng phương trình
22
xa
a
b
y −=
, tức là khi lập
trình ta cho x chạy rồi tính y.
Cung II có y tăng nhanh hơn x, nên ta dùng phương trình
22
yb
b
a
x −=
, tức là khi lập
trình ta cho y chạy rồi tính x.
Để xác định được điểm chuyển tiếp A(
)
,
AA
yx
, ta cho đạo hàm cấp một của phương trình
22
xa
a
b
y −=
bằng -1.
1'
22
−=
−
−=
xaa
bx
y
22
2
ba
a
x
A
+
=
Như vậy ta chỉ cần vẽ cung I và II, các cung còn lại lấy đối xứng là được.
Thuật toán Bresenham gồm 3 bước:
Xét cung I:
Bước k ta có
),(
kk
yx
Bước
1
+
k
ta có
),1(),(
111 +++
+=
kkkk
yxyx
, trong đó
kk
yy =
+1
hoặc
1
1
−=
+ kk
yy
Mục đích của thuật toán Bresenham là dùng toàn số nguyên để tính toán cho nhanh.
Bước 1: Tính
1
d
và
2
d
)1(
11
−−=
+ kk
yyd
12 +
−=
kk
yyd
nên
1
d
và
2
d
dương.
Gọi
21
ddd −=
Nhận xét: nếu
0
<
d
thì
1
1
−=
+ kk
yy
0
≥
d
thì
kk
yy =
+1
Ta có
122
1
+−=
+ kk
yyd
12
2
1
2
+−−=
+ kk
yxa
a
b
d
Ta nhận thấy d có đại lượng
2
1
2
+
−
k
xa
a
b
là số thực, nên d là số thực do đó ta phải
chuyển thành số nguyên, nếu không chuyển được thành số nguyên thì ta dùng phương
pháp bình thường để vẽ ellipse.
Đặt
))1((
22
1
2
1
−−=
+ kk
yyap
)(
2
1
22
2 +
−=
kk
yyap
nên
1
p
và
2
p
dương.
Gọi
21
ppp
k
−=
Nhận xét cũng tương tự như trên:
Nếu
0<
k
p
thì
1
1
−=
+ kk
yy
0≥
k
p
thì
kk
yy =
+1
Ta có
1
1
+=
+ kk
xx
22222
1
2
222 ayayayap
kkkk
−+−=
+
222222
2
2
2
22))1((2 ayayaxa
a
b
ap
kkkk
−+−+−=
2222222222
222242 babayayaxbxbp
kkkkk
−−++−−−=
Bước 2: Đệ qui
Nhắc lại
2222222222
222242 babayayaxbxbp
kkkkk
−−++−−−=
Suy ra
2222
1
22
1
2222
1
2222)1(4)1(2 babayayaxbxbp
kkkkk
−−++−+−+−=
+++
2
1
222
1
22
1
6)(2)(24 byyayyaxbpp
kkkkkkk
−−+−−−=
+++
Nếu
0<
k
p
thì
1
1
−=
+ kk
yy
2222
1
6444 bayaxbpp
kkkk
−−+−=
+
Nếu
0≥
k
p
thì
kk
yy =
+1
22
1
64 bxbpp
kkk
−−=
+
Bước 3: Tính p ban đầu
Dùng phương trình
2222222222
222242 babayayaxbxbp
kkkkk
−−++−−−=
Tại điểm ban đầu thì
),0(),( byx
kk
=
Nên
222
22 babap −−=
Thực hiện tương tự cho cung II
void BresenhamEllipse(HWND hWnd)
{
HDC hdc;
RECT rt;
int xc, yc;
hdc = GetDC(hWnd);
int x, y;
GetClientRect(hWnd,&rt);
xc = rt.right/2;
yc = rt.bottom/2;
int a = 200, b = 250;
int xa = Round(a*a/sqrt(1.0*a*a+b*b));
int p = 2*a*a*b - a*a - 2*b*b ;
x=0; y=b;
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(0,0,0));
// Cung I
while (x<xa) {
if (p<0) {
p = p - 4*b*b*x+4*a*a*y-4*a*a-6*b*b;
y ;
}
else
p = p -4*b*b*x - 6*b*b;
x++;
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(0,0,0));
}
// Cung II
p = -2*a*a*y*y-4*a*a*y-2*b*b*x*x+2*b*b*x+2*a*a*b*b-2*a*a-b*b;
while (y>0) {
if (p<0) {
p = p - 4*a*a*y+4*b*b*x-4*b*b-6*a*a;
x++;
}
else
p = p -4*a*a*y - 6*a*a;
y ;
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
SetPixel(hdc,xc+x,yc+y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc+y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(0,0,0));
}
ReleaseDC(hWnd,hdc);
return;
}
Câu 9: Xây dựng và cài đặt thuật toán vẽ parabola
a
x
y
2
=
với a nguyên dương ra chính
giữa màn hình dùng thuật toán Bresenham.
Đáp án:
Quan sát trên đồ thị, ta thấy
Cung I có x tăng nhanh hơn y nên ta dùng phương trình
a
x
y
2
=
, tức là khi lập trình ta
cho x chạy rồi tính y.
Cung II có y tăng nhanh hơn x nên ta dùng phương trình
ayx =
, tức là khi lập trình ta
cho y chạy rồi tính x.
Để xác định được điểm chuyển tiếp A(
)
,
AA
yx
, ta cho đạo hàm cấp một của phương trình
a
x
y
2
=
bằng 1.
1
2
' ==
a
x
y
hay
2
a
x
A
=
Như vậy ta chỉ cần vẽ cung I và II, nhánh còn lại của parabola lấy đối xứng là được.
Mục đích của thuật toán Bresenham là dùng toàn số nguyên để tính toán cho nhanh.
Xét cung I:
Ta có x tăng nhanh hơn y.
Bước k ta có
),(
kk
yx
Bước
1
+
k
ta có
),1(),(
111 +++
+=
kkkk
yxyx
, trong đó
kk
yy =
+1
hoặc
1
1
+=
+ kk
yy
Thuật toán Bresenham gồm 3 bước:
Bước 1: Tính
1
d
và
2
d
kk
yyd −=
+11
12
1
+
−+=
kk
yyd
nên
1
d
và
2
d
dương.
Gọi
21
ddd −=
Nhận xét: nếu
0
<
d
thì
kk
yy =
+1
0
≥
d
thì
1
1
+=
+ kk
yy
Ta có
122
1
−−=
+ kk
yyd
với
a
x
a
x
y
kk
k
22
1
1
)1( +
==
+
+
nên d là số thực do đó ta phải chuyển thành số nguyên, nếu không chuyển được thành số
nguyên thì ta dùng phương pháp bình thường để vẽ.
Nhân 2 vế của d cho a.
Đặt
aayxdap
kkk
−−+== 2)1(2
2
2242
2
+−−+= aayxxp
kkkk
Nhận xét của d và
k
p
giống nhau vì a dương, do a nguyên nên
k
p
nguyên.
Nhận xét: nếu
0<
k
p
thì
kk
yy =
+1
0≥
k
p
thì
1
1
+=
+ kk
yy
Bước 2: Đệ qui
Nhắc lại
2242
2
+−−+= aayxxp
kkkk
Suy ra
2242
11
2
11
+−−+=
++++
aayxxp
kkkk
22)1(4)1(2
1
2
1
+−−+++=
++
aayxxp
kkkk
Suy ra
6)(24
11
+−−+=
++ kkkkk
yyaxpp
Nếu
0<
k
p
thì
kk
yy =
+1
nên
64
1
++=
+ kkk
xpp
Nếu
0≥
k
p
thì
1
1
+=
+ kk
yy
nên
62
1
+−=
+
app
kk
Bước 3: Tính p ban đầu
Dùng phương trình
2242
2
+−−+= aayxxp
kkkk
Tại điểm ban đầu thì
)0,0(),( =
kk
yx
Nên
2+−= ap
Xét cung II:
Ta có y tăng nhanh hơn x.
Bước k ta có
),(
kk
yx
Bước
1
+
k
ta có
)1,(),(
111
+=
+++ kkkk
yxyx
, trong đó
kk
xx =
+1
hoặc
1
1
+=
+ kk
xx
Thuật toán Bresenham gồm 3 bước:
Bước 1: Tính
1
d
và
2
d
11
1
+
−+=
kk
xxd
kk
xxd −=
+12
nên
1
d
và
2
d
dương.
Gọi
21
ddd −=
Nhận xét: nếu
0
<
d
thì
1
1
+=
+ kk
xx
0
≥
d
thì
kk
xx =
+1
Ta có
122
1
+−=
+kk
xxd
với
)1(
11
+==
++ kkk
yaayx
nên d là số thực do đó ta phải chuyển thành số nguyên, nếu không chuyển được thành số
nguyên thì ta dùng phương pháp bình thường để vẽ.
Đặt
2
1
2
1
)1(
+
−+=
kk
xxp
22
12 kk
xxp −=
+
2
1
2
21
2122
+
−++=−=
kkkk
xxxppp
12222
2
+−−+= aayxxp
kkkk
Nhận xét của d và
k
p
cũng giống nhau.
Nếu
0<
k
p
thì
1
1
+=
+ kk
xx
0≥
k
p
thì
kk
xx =
+1
Bước 2: Đệ qui
Nhắc lại
12222
2
+−−+= aayxxp
kkkk
Suy ra
12222
11
2
11
+−−+=
++++
aayxxp
kkkk
với
1
1
+=
+ kk
yy
Suy ra
axxxxpp
kkkkkk
2)(2)(2
1
22
11
−−+−+=
+++
Nếu
0<
k
p
thì
1
1
+=
+ kk
xx
, hay
axpp
kkk
244
1
−++=
+
0≥
k
p
thì
kk
xx =
+1
, hay
app
kk
2
1
−=
+
Bước 3: Tính p ban đầu
Dùng phương trình
12222
2
+−−+= aayxxp
kkkk
void BresenhamParabola(HWND hWnd)
{
HDC hdc;
RECT rt;
int xc, yc;
hdc = GetDC(hWnd);
int x, y;
int a = 200;
int xa = Round(1.0*a/2);
int p = -a+2;
GetClientRect(hWnd,&rt);
xc = rt.right/2;
yc = rt.bottom/2;
x=0;
y=0;
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
while (x < xa) {
if (p<0)
p = p + 4*x+6;
else {
p = p + 4*x+6-2*a;
y++;
}
x++;
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(0,0,0));
}
p = 2*x*x+2*x-2*a*y-2*a+1;
while (y < 250) {
if (p<0) {
p = p + 4*x+4-2*a;
x++;
}
else
p = p - 2*a;
y++;
SetPixel(hdc,xc+x,yc-y,RGB(0,0,0));
SetPixel(hdc,xc-x,yc-y,RGB(0,0,0));
}
ReleaseDC(hWnd,hdc);
return;
}
Câu 10:
Xây dựng và cài đặt thuật toán MidPoint vẽ đoạn thẳng đi qua hai điểm
),(
11
yx
và
),(
22
yx
. Giới hạn chỉ xét hệ số góc
10 ≤< m
và
21
xx <
. Quy ước vẽ từ
),(
11
yx
đến
),(
22
yx
.
Đáp án:
Do giới hạn hệ số góc
10 ≤< m
và
21
xx <
, nên ở:
Bước k ta có
),(
kk
yx
Bước
1+k
ta có
),1(),(
111 +++
+=
kkkk
yxyx
, trong đó
kk
yy =
+1
hoặc
1
1
+=
+ kk
yy
Ta dùng phương trình tổng quát của đường thẳng
0),( =++= CByAxyxF
Ta có phương trình đường thẳng đi qua 2 điểm
),(
11
yx
và
),(
22
yx
là
12
1
12
1
yy
yy
xx
xx
−
−
=
−
−
.
Suy ra
0)()()()(
1211211212
=−+−−−−− xxyyyxyxxxyy
Đặt
12
yyA −=
,
)(
12
xxB −−=
và
)()(
121121
xxyyyxC −+−−=
Ta được phương trình
0=++ CByAx
Ta chọn phương trình đơn giản nhất để dễ nêu nhận xét, chẳng hạn chọn
)0,0(),(
11
=yx
và
)1,3(),(
22
=yx
, khi đó ta được phương trình
03),( =−= yxyxF
.
Nhận xét:
0),( <yxF
thì điểm
),( yx
nằm phía trên đường thẳng.
0),( ≥yxF
thì điểm
),( yx
nằm trùng hay nằm phía dưới đường thẳng.
Thuật toán MidPoint gồm 3 bước:
Bước 1: Tính
)(MF
với
),1(
2
1
++=
kk
yxM
CyBxAMF
kk
++++= )()1()(
2
1
Mục đích của thuật toán MidPoint là dùng số nguyên để tính cho nhanh. Ta thấy
)(MF
có
đại lượng
2
1
nên
)(MF
là số thực. Ta phải chuyển
)(MF
thành số nguyên nhưng nhận
xét cũng giống như nhận xét về
)(MF
.
Đặt
CBAByAxMFp
kkk
2222)(2 ++++==