1 Bresenham直线
1.1 起始条件
-
终点坐标
-
起始点坐标
通过终点坐标和起始点坐标可以得到直线方程:
KaTeX parse error: Undefined control sequence: \label at position 8: y=kx+b\̲l̲a̲b̲e̲l̲{1}\tag{1}
然后选定主位移方向:
{ X 轴 ∣ k ∣ < 1 Y 轴 ∣ k ∣ > 1 (2) \begin{cases} X\text{轴}& \left|k\right|<1\\\tag{2} Y\text{轴}& \left|k\right|>1\\ \end{cases} {
X轴Y轴∣k∣<1∣k∣>1(2)
1.2 构造中点误差项
从 P i ( x i , y i ) P_i(\text{x}_i,\text{y}_i) Pi(xi,yi)点走第一步后为了选取下一个像素点,需将 P u P_u Pu和 P d P_d Pd的中点 M ( x i + 1 , y i + 0.5 ) M(\text{x}_i+1,\text{y}_i+0.5) M(xi+1,yi+0.5)带入直线的隐函数方程,构造中点误差项 d i d_i di
d i = F ( x M , y M ) = F ( x i + 1 , y i + 0.5 ) = y i + 0.5 − k ( x i + 1 ) − b (3) d_i=F(x_{_M},y_{_M})=F(x_i+1,y_i+0.5)=y_i+0.5-k(x_i+1)-b\tag{3} di=F(xM,yM)=F(xi+1,yi+0.5)=yi+0.5−k(xi+1)−b(3)
根据 d i d_i di判断选择下一个点为 P u P_u Pu还是 P d P_d Pd
y i + 1 = { y i + 1 , d i < 0 y i , d i ⩾ 0 (4) y_{i+1}= \begin{cases} y_{i}+1,& d_i<0\\\tag{4} y_i,& d_i\geqslant0 \end{cases} yi+1={
yi+1,yi,di<0di⩾0(4)
1.3 递推公式☆
1.3.1 中点误差项的递推公式
- 当 d i < 0 d_i<0 di<0时,下一步进行判断的中点坐标为 M ( x i + 2 , y i + 1.5 ) M(x_i+2,y_i+1.5) M(xi+2,yi+1.5)。所以下一步中点误差项为:
d i + 1 = F ( x i + 2 , y i + 1.5 ) = y i + 1.5 − k ( x i + 2 ) − b = y i + 0.5 − k ( x i + 1 ) − b + 1 − k = d i + 1 − k (5) \begin{aligned} d_{i+1}&=F(x_i+2,y_i+1.5)=y_i+1.5-k(x_i+2)-b\\ &=y_i+0.5-k(x_i+1)-b+1-k\\ &=d_i+1-k \end{aligned}\tag{5} di+1=F(xi+2,yi+1.5)=yi+1.5−k(xi+2)−b=yi+0.5−k(xi+1)−b+1−k=di+1−k(5)
- 当 d i ⩾ 0 d_i\geqslant0 di⩾0时,下一步的中点坐标为 M ( x i + 2 , y i + 0.5 ) M(x_i+2,y_i+0.5) M(xi+2,yi+0.5)。所以下一步中点误差项为:
d i + 1 = F ( x i + 2 , y i + 0.5 ) = y i + 0.5 − k ( x i + 2 ) − b = y i + 0.5 − k ( x i + 1 ) − b − k = d i − k (6) \begin{aligned} d_{i+1}&=F(x_i+2,y_i+0.5)=y_i+0.5-k(x_i+2)-b\\ &=y_i+0.5-k(x_i+1)-b-k\\ &=d_i-k \end{aligned}\tag{6} di+1=F(xi+2,yi+0.5)=yi+0.5−k(xi+2)−b=yi+0.5−k(xi+1)−b−k=di−k(6)
1.3.2 中点误差项的初始值
假设以 x x x为主位移方向。因此,第一个中点坐标是 ( x 0 + 1 , y 0 + 0.5 ) (x_0+1,y_0+0.5) (x0+1,y0+0.5),相应的 d i d_i di的初始值为:
d 0 = F ( x 0 + 1 , y 0 + 0.5 ) = y 0 + 0.5 − k ( x 0 + 1 ) − b = y 0 − k x 0 − b − k + 0.5 (7) \begin{aligned} d_0&=F(x_0+1,y_0+0.5)=y_0+0.5-k(x_0+1)-b\\ &=y_0-kx_0-b-k+0.5 \end{aligned}\tag{7} d0=F(x0+1,y0+0.5)=y0+0.5−k(x0+1)−b=y0−kx0−b−k+0.5(7)
其中,因此 ( x 0 , y 0 ) (x0,y_0) (x0,y0)在直线上,所以 y 0 − k x 0 − b = 0 y_0-kx_0-b=0 y0−kx0−b=0,则
d i = 0.5 − k (8) d_i=0.5-k\tag{8} di=0.5−k(8)
1.4 算法流程图
1.5 代码
void CMFCApplication1View::DrawLine(CDC* pDC, int x1, int y1, int x2, int y2)
{
// TODO: 在此处添加实现代码.
int dx = x2 - x1;
int dy = y2 - y1;
int deltaY = 0;
int middle = dx;
int y = y1;
for (int i = x1;i < x2;i++) {
pDC->SetPixel(i, y, RGB(30, 30, 30));
deltaY += 2 * dy;
if (deltaY >= middle) {
y += 1;
middle += 2 * dx;
}
}
pDC->SetPixel(x2, y2, RGB(30, 30, 30));
}
1.6
void CMFCApplication1View::DrawLine(int x1, int y1, int x2, int y2, CDC* pDC){
// TODO: 在此处添加实现代码.
int dx = x2 - x1;
int dy = y2 - y1;
int di = 2*dx - 2*dy;
int y = y1;
for (int i = x1; i < x2; i++) {
pDC->SetPixel(i, y, RGB(30, 30, 30));
if (di < 0){
di = di + 2 * (dx - dy);
y++;
}
else{
di = di - 2 * dy;
}
}
}
2 Bresenham圆
圆只需要画 1 8 \frac{1}{8} 81的圆弧就行了,剩下的圆弧可以通过对称得到。
2.1 起始条件
- 半径R
- 起始点(x,y)
主位移方向为X轴方向
2.2 构造中点误差项
从 P i ( x i , y i ) P_i(\text{x}_i,\text{y}_i) Pi(xi,yi)点走第一步后为了选取下一个像素点,需将 P u P_u Pu和 P d P_d Pd的中点 M ( x i + 1 , y i + 0.5 ) M(\text{x}_i+1,\text{y}_i+0.5) M(xi+1,yi+0.5)带入圆的隐函数方程,构造中点误差项 d i d_i di
d i = F ( x M , y M ) = F ( x i + 1 , y i − 0.5 ) = ( x i + 1 ) 2 + ( y i − 0.5 ) 2 − R 2 (9) d_i=F(x_{_M},y_{_M})=F(x_i+1,y_i-0.5)=(x_i+1)^2+(y_i-0.5)^2-R^2\tag{9} di=F(xM,yM)=F(xi+1,yi−0.5)=(xi+1)2+(yi−0.5)2−R2(9)
根据 d i d_i di判断选择下一个点为 P u P_u Pu还是 P d P_d Pd
y i + 1 = { y i , d i < 0 y i − 1 , d i ⩾ 0 (10) y_{i+1}= \begin{cases} y_{i},& d_i<0\\\tag{10} y_i-1,& d_i\geqslant0 \end{cases} yi+1={
yi,yi−1,di<0di⩾0(10)
2.3 递推公式☆
2.3.1 中点误差项的递推公式
- 当 d i < 0 d_i<0 di<0时,下一步进行判断的中点坐标为 M ( x i + 2 , y i − 0.5 ) M(x_i+2,y_i-0.5) M(xi+2,yi−0.5)。所以下一步中点误差项为:
d i + 1 = F ( x i + 2 , y i − 0.5 ) = ( x i + 2 ) 2 + ( y i − 0.5 ) 2 − R 2 = ( x i + 1 ) 2 + ( y i − 0.5 ) 2 − R 2 + 2 x i + 3 = d i + 2 x i + 3 (11) \begin{aligned} d_{i+1}&=F(x_i+2,y_i-0.5)=(x_i+2)^2+(y_i-0.5)^2-R^2\\ &=(x_i+1)^2+(y_i-0.5)^2-R^2+2x_i+3\\ &=d_i+2x_i+3 \end{aligned}\tag{11} di+1=F(xi+2,yi−0.5)=(xi+2)2+(yi−0.5)2−R2=(xi+1)2+(yi−0.5)2−R2+2xi+3=di+2xi+3(11)
-
当 d i ⩾ 0 d_i\geqslant0 di⩾0时,下一步的中点坐标为 M ( x i + 2 , y i − 1.5 ) M(x_i+2,y_i-1.5) M(xi+2,yi−1.5)。所以下一步中点误差项为:
d i + 1 = F ( x i + 2 , y i − 1.5 ) = ( x i + 2 ) 2 + ( y i − 1.5 ) 2 − R 2 = ( x i + 1 ) 2 + ( y i − 0.5 ) 2 − R 2 + 2 x i + 3 + ( − 2 y i + 2 ) = d i + 2 ( x i − y i ) + 5 (12) \begin{aligned} d_{i+1}&=F(x_i+2,y_i-1.5)=(x_i+2)^2+(y_i-1.5)^2-R^2\\ &=(x_i+1)^2+(y_i-0.5)^2-R^2+2x_i+3+(-2y_i+2)\\ &=d_i+2(x_i-y_i)+5 \end{aligned}\tag{12} di+1=F(xi+2,yi−1.5)=(xi+2)2+(yi−1.5)2−R2=(xi+1)2+(yi−0.5)2−R2+2xi+3+(−2yi+2)=di+2(xi−yi)+5(12)
2.3.2 中点误差项的初始值
以 x x x为主位移方向。因此,第一个中点坐标是 ( 1 , R − 0.5 ) (1,R-0.5) (1,R−0.5),相应的 d i d_i di的初始值为:
d 0 = F ( 1 , R − 0.5 ) = 1 + ( R − 0.5 ) 2 − R 2 = 1.25 − R (13) \begin{aligned} d_0&=F(1,R-0.5)=1+(R-0.5)^2-R^2=1.25-R \end{aligned}\tag{13} d0=F(1,R−0.5)=1+(R−0.5)2−R2=1.25−R(13)
2.4 算法流程图
2.5 代码
void CMFCApplication1View::BreCircle(int x0, int y0, int R, CDC* pDC)
{
// TODO: 在此处添加实现代码.
int x = x0;
int y = R;
float di = 1.25 - R;
while (x <= y) {
CirclePoint(pDC, x, y);
pDC->SetPixel(x, y, RGB(30, 30, 30));
if (di <= 0) {
di = di + 2 * x + 3;
x += 1;
}
else {
di = di + 2 * (x - y) + 5;
x += 1;
y -= 1;
}
}
}
3 Bresenham椭圆
3.1 起始条件
- 长半轴a
- 短半轴b
椭圆画出 1 4 \frac{1}{4} 41的圆弧的就行了,其他三个圆弧可以通过对称得到。但是第一个圆弧的主位移方向会发生变化,需要分情况讨论。我们通过法矢量的两个分量进行讨论。
椭圆上任意一点的处的法矢量:
KaTeX parse error: Undefined control sequence: \symbfit at position 80: …tial{y}}j=2b^2x\̲s̲y̲m̲b̲f̲i̲t̲{i}+2a^2y\symbf…
确定主位移方向:
{ X 轴 , 2 b 2 x ≥ 2 a 2 y Y 轴 , 2 b 2 x < 2 a 2 y (15) \begin{cases} X轴,& 2b^2x≥2a^2y\\ Y轴,& 2b^2x<2a^2y\tag{15} \end{cases} {
X轴,Y轴,2b2x≥2a2y2b2x<2a2y(15)
3.2 构造中点误差项及递推式☆
3.2.1 上半部分的中点误差项初始值
上半部分椭圆弧的起点坐标为 A ( 0 , b ) A(0,b) A(0,b),因此,第一个中点坐标为 ( 1 , b − 0.5 ) (1,b-0.5) (1,b−0.5),对应的 d 1 i d_1i d1i的初始值为:
d 1 i = F ( 1 , b − 0.5 ) = b 2 + a 2 ( − b + 0.25 ) (16) d_1i=F(1,b-0.5)=b^2+a^2(-b+0.25)\tag{16} d1i=F(1,b−0.5)=b2+a2(−b+0.25)(16)
3.2.2 上半部分中点误差项递推式
KaTeX parse error: Undefined control sequence: \textless at position 100: …(2x_i+3)&d_{1i}\̲t̲e̲x̲t̲l̲e̲s̲s̲0\tag{17} \end{…
y i + 1 = { y i , d i < 0 y i − 1 , d i ⩾ 0 (18) y_{i+1}= \begin{cases} y_{i},& d_i<0\\\tag{18} y_i-1,& d_i\geqslant0 \end{cases} yi+1={ yi,yi−1,di<0di⩾0(18)
3.2.1 下半部分的中点误差项初始值
d 2 i = F ( x M , y M ) = b 2 ( x i + 0.5 ) 2 + a 2 ( y i − 1 ) 2 − a 2 b 2 (19) d_{2i}=F(x_{_M},y_{_M})=b^2(x_i+0.5)^2+a^2(y_i-1)^2-a^2b^2\tag{19} d2i=F(xM,yM)=b2(xi+0.5)2+a2(yi−1)2−a2b2(19)
3.2.2 下半部分中点误差项递推式
KaTeX parse error: Undefined control sequence: \textless at position 101: …-2y_i+3)&d_{2i}\̲t̲e̲x̲t̲l̲e̲s̲s̲0\tag{20} \end{…
y i + 1 = { y i , d i < 0 y i − 1 , d i ⩾ 0 (21) y_{i+1}= \begin{cases} y_{i},& d_i<0\\\tag{21} y_i-1,& d_i\geqslant0 \end{cases} yi+1={ yi,yi−1,di<0di⩾0(21)
3.3 算法流程图
3.4 代码
void CMFCApplication1View::BreEllipse(CDC* pDC,int a,int b) {
int x0 = 0;
int y0 = b;
float di1 = b * b + a * a * (-b + 0.25);
int x = x0;
int y = y0;
int dx = 2 * b * b * x;
int dy = 2 * a * a * y;
while (dy>=dx)
{
if (di1 < 0) {
di1 = di1 + b * b * (2 * x + 3);
x = x + 1;
y = y;
}
else {
di1 = di1 + b * b * (2 * x + 3) + a * a * (2 - 2 * y);
x = x + 1;
y = y - 1;
}
dx = 2 * b * b * x;
dy = 2 * a * a * y;
EllipsePoint(pDC, x, y);
}
float di2 = b * b * (x + 0.5) * (x + 0.5) + a * a * (y - 1) * (y - 1) - a * a * b * b;
while (y>=0) {
if (di2 < 0) {
di2 = di2 + b * b * (2 * x + 2) + a * a * (3 - 2 * y);
x = x + 1;
y = y - 1;
}
else
{
di2 = di2 + a * a * (3 - 2 * y);
x = x;
y = y - 1;
}
EllipsePoint(pDC, x, y);
}
}
4 Wu反走样算法
4.1 原理
对上下的两个点同时绘制,根据距离设置不同亮度
P u 亮 度 P d 亮 度 = D i s t a n c e P u F i D i s t a n c e P d F i \frac{P_u亮度}{P_d亮度}=\frac{Distance_{_{P_uF_i}}}{Distance_{_{P_dF_i}}} Pd亮度Pu亮度=DistancePdFiDistancePuFi
4.2 算法流程图
4.3 代码
void CMFCApplication1View::WuLine(CDC *pDC,int x1,int y1,int x2,int y2) {
float k = (y2 - y1) * 1.0 / (x2 - x1);
float deltae = k;
int y = y1;
pDC->SetPixel(x1, y1, RGB(0, 0, 0));
for (int i = x1+1;i <= x2-1;i++) {
if (deltae <= 0.5) {
y = y;
pDC->SetPixel(i, y, RGB(deltae * 255, deltae * 255, deltae * 255));
pDC->SetPixel(i, y+1, RGB((1-deltae) * 255, (1 - deltae) * 255, (1 - deltae) * 255));
}
else {
y = y + 1;
pDC->SetPixel(i, y, RGB((1 - deltae) * 255, (1 - deltae) * 255, (1 - deltae) * 255));
pDC->SetPixel(i, y - 1, RGB(deltae * 255, deltae * 255, deltae * 255));
}
deltae = deltae + k;
if (deltae > 1) {
deltae -= 1;
}
}
pDC->SetPixel(x2, y2, RGB(0, 0, 0));
}
5 重建坐标系
//将坐标系移到中点
CRect rect;
GetClientRect(rect);
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(rect.Width(), rect.Height());
pDC->SetViewportExt(rect.Width(), -rect.Height());
pDC->OffsetViewportOrg(rect.Width() / 2, rect.Height() / 2);
GetClientRect(rect);//获取指定窗口的客户区域大小
pDC->SetMapMode(MM_ANISOTROPIC);//设备视图和逻辑视图可以任意改变
pDC->SetWindowExt(rect.Width(), rect.Height());//逻辑范围
pDC->SetViewportExt(rect.Width(), -rect.Height());//设备范围,以及Y轴方向
pDC->OffsetViewportOrg(rect.Width() / 2, rect.Height() / 2);//视图中心偏移
//以物理设置坐标原点为基础(显示作用),以逻辑为单位(逻辑用于按比例设置单位)
5.1 参考资料
SetWindowOrg,SetViewportOrg,SetWindowExt,SetViewportExt
SetWindowExt() 与SetViewportExt()
mfc编程中SetViewportOrg与SetWindowOrg的理解
逻辑坐标与设备坐标——全窗口坐标、屏幕坐标、客户区坐标的总结
6 如何在MFC中添加自定义函数
-
方法一
在类视图中,选择view,然后添加函数
-
方法二
在view.h的头文件添加声明
在view.cpp文件中添加函数体定义和调用
的几个函数详解](https://blog.csdn.net/zhandeen/article/details/8223747)
SetWindowExt() 与SetViewportExt()
mfc编程中SetViewportOrg与SetWindowOrg的理解
逻辑坐标与设备坐标——全窗口坐标、屏幕坐标、客户区坐标的总结
6 如何在MFC中添加自定义函数
-
方法一
在类视图中,选择view,然后添加函数
-
方法二
在view.h的头文件添加声明
在view.cpp文件中添加函数体定义和调用