B.圆、椭圆生成算法 一、实验目的
编写圆和椭圆的扫描转换算法程序,验证算法的正确性。
二、实验任务(2学时)
1. 编写中点画圆法的扫描转换程序,考虑原点在(x0,y0)处程序的改动; 2. 添加鼠标程序,实现交互式画圆; 3. 编写中点画椭圆法的扫描转换程序; 4. 添加鼠标程序,实现交互式画椭圆;
三、实验内容
圆的Bresenham算法
设圆的半径为r。先考虑圆心在(0, 0),并从x=0、y=r,开始的顺时针方向的1/8圆周的生成过程。在这种情况下,x每步增加1,从x=0开始,到x=y结束。即有 xi+1 = xi + 1
相应的yi+1则在两种可能中选择: yi+1 = yi或者yi+1 = yi-1 选择的原则是考察精确值y是靠近yi还是 靠近yi-1(如右图), 计算式为 y2 = r2-(xi+1)2
d1 = yi2-y2 = yi2-r2+(xi+1)2
d2 = y2-(yi-1)2 = r2-(xi+1)2-(yi-1)2 令pi=d1-d2,并代入d1、d2,则有 pi = 2(xi+1)2 + yi2 + (yi-1)2-2r2
pi称为误差。如果pi<0则yi+1=yi,否则yi+1=yi-1。 pi的递归式为
pi+1 = pi + 4xi +6+2(yi+12- yi2) -2(yi+1-yi) pi的初值由上式代入xi=0,yi=r而得 p1 = 3-2r
根据上面的推导,圆周生成算法思想如下: ⒈ 求误差初值,p1=3-2r,i=1,画点(0, r);
⒉ 求下一个光栅位置,其中xi+1=xi+1,如果pi<0则yi+1=yi,否则yi+1=yi-1; ⒊ 画点(xi+1, yi+1);
⒋ 计算下一个误差,如果pi<0则pi+1=pi+4xi+6,否则pi+1=pi+4(xi-yi)+10; ⒌ i=i+1,如果x=y则结束,否则返回步骤2。
-- 37
任务一:中点画圆法的扫描转换算法
编写中点画圆法的扫描转换程序,考虑原点在(x0,y0)处程序的改动;
分析:考虑圆心不在原点,设圆心坐标为(x0,y0)。通过平移坐标原点到圆心,则第二个8分圆上一点p(x,y),其原始坐标为 x’=x+x0 y’=y+y0
即p’1(x0 +x, y+y0)
其它7个对称点分别是:p’2(x0+y,y0+x), p’3 (x0+y,y0-x),p’4 (x0+x,y0-y),p’5 (x0-x,y0-y),p’6 (x0-y,y0-x),p’7 (x0-y,y0+x),p’8 (x0-x,y0+y)
p’1(x0 +x, y0+y) Y’ Y 算法程序如下:
p(x,y)
MidpointCircle(int x0,int y0,int r, int color) p’8 (x0-x,y0+y) R {
p’2(x0+y,y0+x) int x,y; p’7 (x0-y,y0+x) float d;
(x0,y0) X’ x=0; y=r ; d=1.25-r ;
p’6 (x0-y,y0-x) CirPot(x0,y0,x,y,color); p’3 (x0+y,y0-x) while (x<=y)
p’4 (x0+x,y0-y)) { p’5 (x0-x,y0-y) if(d<0)
O(0,0) X {
d+=2*x+3; x++; }
(x0+R,y0)
else {
d+=2*(x-y)+5; x++; y--; }
CirPot(x0,y0,x,y,color); } /* while*/
} /* MidpointCiecle */
int CirPot(int x0,int y0,int x,int y,int color) {
Setpixel((x0+x),(y0+y)); Setpixel((x0+y),(y0+x)); Setpixel((x0+y),(y0-x)); Setpixel((x0+x),(y0-y)); Setpixel((x0-x),(y0-y)); Setpixel((x0-y),(y0-x)); Setpixel((x0-y),(y0+x));
-- 38
Setpixel((x0-x),(y0+y)); }
程序实现步骤:
(1) 建立MidPointCircle工程文件;
(2) 右击CMidPointCircleView类,建立成员函数
void MidpointCircle(CDC *pDC,int x0, int y0, int r, COLORREF color) int CirPot(CDC *pDC,int x0, int y0, int x, int y, COLORREF color) (3) 编写成员函数代码,程序如下:
void CMidPointCircleView::MidpointCircle(CDC *pDC,int x0, int y0, int r, COLORREF color) {
int x,y; float d;
x=0;y=r;d=1.25-r;
CirPot(pDC,x0,y0,x,y,color); while (x<=y) {
if(d<0) {
d+=2*x+3; x++; } else {
d+=2*(x-y)+5; x++; y--; }
CirPot(pDC,x0,y0,x,y,color); } /* while*/ }
int CMidPointCircleView::CirPot(CDC *pDC,int x0, int y0, int x, int y, COLORREF color) {
pDC->SetPixel((x0+x),(y0+y),color); pDC->SetPixel((x0+y),(y0+x),color); pDC->SetPixel((x0+y),(y0-x),color); pDC->SetPixel((x0+x),(y0-y),color); pDC->SetPixel((x0-x),(y0-y),color); pDC->SetPixel((x0-y),(y0-x),color); pDC->SetPixel((x0-y),(y0+x),color); pDC->SetPixel((x0-x),(y0+y),color); return 0; }
(4)编写OnDraw(CDC* pDC)函数,程序如下: void CMidPointCircleView::OnDraw(CDC* pDC) {
-- 39
CMidPointCircleDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here MidpointCircle(pDC,100, 100, 10, RGB(255,0,0)); MidpointCircle(pDC,500, 300, 60, RGB(255,255,0)); }
(6) 编译、运行程序,查看结果。
任务二:添加鼠标程序,实现交互式画圆
在任务1的基础上,完成下列步骤:
(1)向视图类中添加自定义的成员变量
用鼠标右键单击视图类,选择“Add Member Variable…”,添加下面三个成员变量。 proctected :
int m_r; // 半径
CPoint m_bO; // 圆心 CPoint m_bR; //圆上的点
int m_ist; //圆心与圆周上点的区别,m_ist=0,表示鼠标左击点为圆心, //m_ist=1,表示鼠标左击点为圆周上的点
(2)在视图类CPP文件的构造函数中初始化成员变量 CMidPointCircleMouseView::CMidPointCircleMouseView() { // TODO: add construction code here m_bO.x=0; m_bO.y=0; //圆心 m_bR.x=0; m_bR.y=0; //圆上的点 m_ist=0; //圆心与圆上的点区别 m_r=0; //圆的半径 }
(3)向视图类中添加自定义的成员函数原型:
-- 40
public:
int ComputeRadius(CPoint cenp,CPoint ardp);
添加成员函数的程序代码:
int CMouseSpringView::ComputeRadius(CPoint cenp, CPoint ardp) {
int dx=cenp.x-ardp.x; int dy=cenp.y-ardp.y;
//sqrt()函数的调用,在头文件中加入#include \ return (int)sqrt(dx*dx+dy*dy); }
(4)向视图类中添加两个鼠标消息响应函数,并输入鼠标处理程序代码。
具体操作方法与鼠标示例1方法相同。一个是OnLButtonDown()函数,另一个是OnMouseMove()函数。程序如下:
void CMidPointCircleMouseView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC *pDC=GetDC();
pDC->SelectStockObject(NULL_BRUSH); if (!m_ist) //绘制圆 { m_bO=m_bR=point; //纪录第一次单击鼠标位置,定圆心 m_ist++; } else { m_bR=point; //记录第二次单击鼠标的位置,定圆周上的点 m_ist--; // 为新绘图作准备 m_r=ComputeRadius(m_bO,m_bR); MidpointCircle(pDC,m_bO.x,m_bO.y,m_r,RGB(255,0,0)); } ReleaseDC(pDC); //释放设备环境 CView::OnLButtonDown(nFlags, point); }
void CMidPointCircleMouseView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CDC *pDC=GetDC();
int nDrawmode=pDC->SetROP2(R2_NOT); //设置异或绘图模式,并保存原来绘图模式 pDC->SelectStockObject(NULL_BRUSH); if(m_ist==1)
-- 41