算 法 描 述 及 实 验 步 骤 | 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<x1,重复步骤(4),否则结束。 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);
}
|