课程名称 |
计算机图形学 |
班级 |
|
实验日期 |
|
||||
姓名 |
|
学号 |
|
实验成绩 |
|
||||
实验名称 |
直线中点Bresenham算法 |
||||||||
实 验 目 的 及 要 求 |
斜率0≤k≤1直线的中点Bresenham算法。 任意斜率直线段绘制算法。 颜色类的定义与调用方法。 直线类的定义与调用方法。 鼠标按键消息映射方法。 |
||||||||
实 验 内 容 |
1、案例描述 在屏幕客户区内按下鼠标左键赞扬直线的起点,移动鼠标指针到直线终点上,弹起鼠标左键绘制任意斜率的直线段。 2、功能说明 (1)设计CRGB类其成员变量为double型的红绿蓝分量red,green和blue,将red,green和blue分量分别规范到[0,1]区间。 (2)设计Cline直线类,其成员变量为直线段的起点坐标P0和终点坐标P1,成员函数为MoveTo()和LineTo()函数。 (3)Cline类的LineTo()函数使用中点Bresenham算法绘制任意斜率的直线段,包括k=±∞,k>1,0≤�≤1, -1≤�<0和k<-1这5种情况。 (4)自定义屏幕二维坐标系,原点位于客户区中心,x轴水平向右为正,y轴垂直向上为正。直线段的起点坐标和终点坐标相对于屏幕客户区中心定义。 |
||||||||
算 法 描 述
及 实 验 步 骤
|
1、案例分析 MFC提供的CDC类的成员函数MoveTo()和LineTo()函数用于绘制傻任意斜率的直线段,直线段的颜色由所选用的画笔指定。MoveTo()函数移动当前点到参数(x,y)所指定的点,不画线;LineTo()函数从当前点画一直线段到参数(x,y)所指定的点,但不包括(x,y)。 本案例通过定义Cline类来模拟CDC类绘制任意斜的直线段,采用直线中点Bresenham算法。 2、算法设计 对于0≤�≤1的直线段,中点Bresenham算法如下: (1)使用鼠标选择起点坐标p0(x0,y0)和终点坐标p1(x1,y1)。要求起点的的坐标小于等于终点的x坐标。 (2)定义直线段当前点坐标x,y,定义中点误差项d,定义直线斜k,定义像素点颜色clr。 (3)x=x0,y=y0,计算d=0.5-k,k=(y1-y0)/(x1-x0), clr=CRGB(0,0,1) (4)绘制点(x,y),判断d的符号。若d<0,则(x,y)更新为(x+1,y+1),d更新为d+1-k;否则(x,y)更新为(x+1,y),d更新为d-k。 (5)如果当前点x 3、设计CRGB类 为了规范颜色的处事,定义了CRGB类,重载了“+”,“-”、“*”、“\”、“+=”、“-=”、“*=”、“/=”运算符。成员函数Normalize()将颜色分量red,green,blue规范到[0,1]闭区间内。 RGB.h #pragma once class CRGB { public: CRGB(); CRGB(double, double, double); ~CRGB();
friend CRGB operator + (const CRGB&, const CRGB&); friend CRGB operator - (const CRGB&, const CRGB&); friend CRGB operator * (const CRGB&, const CRGB&); friend CRGB operator * (const CRGB&, double); friend CRGB operator * (double, const CRGB&); friend CRGB operator / (const CRGB&, double); friend CRGB operator += (const CRGB&, const CRGB&); friend CRGB operator -= (const CRGB&, const CRGB&); friend CRGB operator *= (const CRGB&, const CRGB&); friend CRGB operator /= (const CRGB&, double); void Normalize();
public: double red; double green; double blue; }; RGB.cpp #include "stdafx.h" #include "RGB.h"
CRGB::CRGB() { red = 1.0; green = 1.0; blue = 1.0; }
CRGB::~CRGB() {}
CRGB::CRGB(double r, double g, double b) { red = r; green = g; blue = b; }
CRGB operator +(const CRGB &c1, const CRGB &c2) { CRGB c; c.red = c1.red + c2.red; c.green = c1.green + c2.green; c.blue = c1.blue + c2.blue; return c; }
CRGB operator - (const CRGB &c1, const CRGB &c2) { CRGB c; c.red = c1.red - c2.red; c.green = c1.green - c2.green; c.blue = c1.blue - c2.blue; return c; }
CRGB operator * (const CRGB&c1, const CRGB&c2) { CRGB c; c.red = c1.red * c2.red; c.green = c1.green * c2.green; c.blue = c1.blue * c2.blue; return c; }
CRGB operator * (const CRGB&c1, double k) { CRGB c; c.red = c1.red*k; c.green = c1.green*k; c.blue = c1.blue*k; return c; }
CRGB operator * (double k, const CRGB&c1) { CRGB c; c.red = c1.red*k; c.green = c1.green*k; c.blue = c1.blue*k; return c; }
CRGB operator / (double k, const CRGB&c1) { CRGB c; c.red = c1.red / k; c.green = c1.green / k; c.blue = c1.blue / k; return c; }
CRGB operator +=(const CRGB &c1, const CRGB &c2) { CRGB c; c.red = c1.red + c2.red; c.green = c1.green + c2.green; c.blue = c1.blue + c2.blue; return c; }
CRGB operator -= (const CRGB &c1, const CRGB &c2) { CRGB c; c.red = c1.red - c2.red; c.green = c1.green - c2.green; c.blue = c1.blue - c2.blue; return c; }
CRGB operator *= (const CRGB&c1, const CRGB&c2) { CRGB c; c.red = c1.red * c2.red; c.green = c1.green * c2.green; c.blue = c1.blue * c2.blue; return c; }
CRGB operator /= (const CRGB&c1, double k) { CRGB c; c.red = c1.red / k; c.green = c1.green / k; c.blue = c1.blue / k; return c; }
void CRGB::Normalize() { red = (red<0.0) ? 0.0 : ((red>1.0) ? 1.0 : red); green = (green<0.0) ? 0.0 : ((green>1.0) ? 1.0 : green); blue = (blue<0.0) ? 0.0 : ((blue>1.0) ? 1.0 : blue); } 4、设计Cline直线类 定义直线绘制任意斜率的直线,其成员函数为MoveTo()和LineTo()。 Line.h #pragma once #include "P2.h" #include "RGB.h"
class CLine { public: CLine(); virtual ~CLine(); void MoveTo(CDC *, CP2);//移动到指定位置 void MoveTo(CDC *, double, double); void LineTo(CDC *, CP2);//绘制直线,不含终点 void LineTo(CDC *, double, double); public: CP2 P0;//起点 CP2 P1;//终点 };
Line.cpp #include "stdafx.h" #include "Line.h" #include "math.h" #define Round(d) int(floor(d+0.5))//四舍五入宏定义 #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[] = __FILE__; #define new DEBUG_NEW #endif
CLine::CLine() {}
CLine::~CLine() {}
void CLine::MoveTo(CDC *pDC, CP2 p0)//绘制直线起点函数 { P0 = p0; }
void CLine::MoveTo(CDC *pDC, double x0, double y0)//重载函数 { P0 = CP2(x0, y0); }
void CLine::LineTo(CDC *pDC, CP2 p1) { P1 = p1; CP2 p, t; CRGB clr = CRGB(0.0, 0.0, 0.0);//黑色像素点 if (fabs(P0.x - P1.x)<1e-6)//绘制垂线 { if (P0.y>P1.y)//交换顶点,使得起始点低于终点 { t = P0; P0 = P1; P1 = t; } for (p = P0; p.y<P1.y; p.y++) { pDC->SetPixelV(Round(p.x), Round(p.y), RGB(clr.red * 255, clr.green * 255, clr.blue * 255)); } } else { double k, d; k = (P1.y - P0.y) / (P1.x - P0.x); if (k>1.0)//绘制k>1 { if (P0.y > P1.y) { t = P0; P0 = P1; P1 = t; } d = 1 - 0.5*k; for (p = P0; p.y < P1.y; p.y++) { pDC->SetPixelV(Round(p.x), Round(p.y), RGB(clr.red * 255, clr.green * 255, clr.blue * 255)); if (d >= 0) { p.x++; d += 1 - k; } else d += 1; } } if (0.0 <= k && k <= 1.0)//绘制0<=k<=1 { if (P0.x > P1.x) { t = P0; P0 = P1; P1 = t; } d = 0.5 - k; for (p = P0; p.x < P1.x; p.x++) { pDC->SetPixelV(Round(p.x), Round(p.y), RGB(clr.red * 255, clr.green * 255, clr.blue * 255)); if (d < 0) { p.y++; d += 1 - k; } else d -= k; } } if (k >= -1.0 && k<0.0)//绘制-1<=k<0 { if (P0.x>P1.x) { t = P0; P0 = P1; P1 = t; } d = -0.5 - k; for (p = P0; p.x<P1.x; p.x++) { pDC->SetPixelV(Round(p.x), Round(p.y), RGB(clr.red * 255, clr.green * 255, clr.blue * 255)); if (d>0) { p.y--; d -= 1 + k; } else d -= k; } } if (k < -1.0)//绘制k<-1 { if (P0.y<P1.y) { t = P0; P0 = P1; P1 = t; } d = -1 - 0.5*k; for (p = P0; p.y>P1.y; p.y--) { pDC->SetPixelV(Round(p.x), Round(p.y), RGB(clr.red * 255, clr.green * 255, clr.blue * 255)); if (d < 0) { p.x++; d -= 1 + k; } else d -= 1; } } } P0 = p1; } void CLine::LineTo(CDC *pDC, double x1, double y1)//重载函数 { LineTo(pDC, CP2(x1, y1)); } 5、添加鼠标消息映射 添加WM_LBUTTONDOWN消息映射函数 void CTestView::OnLButtonDown(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值
p0.x = point.x; p0.y = point.y; p0.x = p0.x - rect.Width() / 2; //设备坐标系向自定义坐标系转换 p0.y = rect.Height() / 2 - p0.y;
CView::OnLButtonDown(nFlags, point); } 添加WM_LBUTTONUP消息映射函数 void CTestView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: 在此添加消息处理程序代码和/或调用默认值
p1.x=point.x; p1.y=point.y; CLine *line=new CLine; CDC *pDC=GetDC(); //定义设备上下文指针
//与案例比新加的语句 GetClientRect(rect);
pDC->SetMapMode(MM_ANISOTROPIC); //自定义坐标系 pDC->SetWindowExt(rect.Width(),rect.Height()); //设置窗口比例 pDC->SetViewportExt(rect.Width(),-rect.Height()); //设置视区比例,且x轴水平向右,y轴垂直向上 pDC->SetViewportOrg(rect.Width()/2,rect.Height()/2);//设置客户区中心为坐标系原点 rect.OffsetRect(-rect.Width()/2,-rect.Height()/2); //矩形与客户区重合
/*pDC->MoveTo(0, 0); pDC->LineTo(100, 200);*/
p1.x=p1.x-rect.Width()/2; p1.y=rect.Height()/2-p1.y; line->MoveTo(pDC,p0); line->LineTo(pDC,p1); delete line; ReleaseDC(pDC);
CView::OnLButtonUp(nFlags, point); } |
||||||||
调 试 过 程 及 实 验 结 果 |
|
||||||||
总
结
|
本案例实现的Cline类的成员函数类似于CDC类的MoveTo()函数和LineTo()函数,用于绘制任意斜的直线段。MSDN指出CDC类的LineTo()函数画一段直线到终点坐标位置,但不包括终点坐标。CLine类的LineTo()函数实现了该功能。本案例映射了WM_LBUTTONDOWN消息来确定直线段的起点坐标,映射了WM_LBUTTONDOWN消息来确定直线段的终点坐标并绘制直线段。本次实验帮助我更好的了解了图形学的理论知识,锻炼了我的动手能力。 |
||||||||
实验地点 |
|
指导教师 |