计算机图形学实验二 《绘制任意斜率的直线》

计算机图形学实验二 《绘制任意斜率的直线》

视频讲解地址

一.Bresenham算法

用视频讲会好点我之前也录过相关视频可以先凑合看

二.设计CLine类

之前也说了C++一个类是由源文件和头文件组成的,所以分别对头文件和源文件进行设计

头文件:

头文件写的一般都是类的声明(包括类成员和方法的声明),函数原型,#define(宏定义)等,不涉及实现。
或者说头文件就是去描述这个类的实列长什么样有什么特征,他会干什么,比如我们去设计一个猫类,那首先想去想猫有什么特征或者说属性,比如有四肢,有尾巴,有花纹等等,然后去想他有什么动作或者说行为,比如进食,移动,睡觉,那类的头文件就是去对这些做一个描述,做一个声明。
比如上面的猫类我们头文件可以这么设计,对于类的概念可以类比我们之前学过的java

class Cat{
public:
	//访问权限为公开
	Cat(void);//无参构造,因为为了保证对象在创建的时候
	//就被构造函数初始化所以构造函数应该和类名一致
	virtual ~Cat(void);//析构函数
	/*c++不同于java有垃圾回收,为了在在跳出程序
	(比如关闭文件、释放内存等)前释放资源,
	所以人们设计出析构函数,它会在每次删除所创建的对象时执行*/
	void Move(int x0,int y0);//猫会移动比如移动到(x0,y0)
	void Eat();//猫会吃
	void Sleep(int times);//猫会睡觉
	
private:
	//访问权限为私有一般类的属性都做为私有
	Hand lefthand;
	 /*猫的左手 Hand是我写的另一个类
	这里不做介绍你就当成有个左手就行主要是基础
	数据类型找不到一个好表示左手的*/
	Hand righthand;//同上猫的右手
	//还有什么脚啊尾巴啊就不说了有这么概念就行主要是说一下C++的类该咋写
};

那这样你可能对类有一个初步的认识,我们来想一下该区如何设计一个CLine直线类
首先我们去想一下该怎么画直线画直线需要啥,我们画直线肯定需要两个点,因为两点确定一条直线,所以CLine类就应该两个成员变量表示起点和终点,同样的我们要画肯定需要把笔移动到起点所以有一个MoveTo函数表示移动画笔到起点,同样有个函数LineTo表示用笔去画一条直线,那可能有人会说不应该还要有画笔吗,之前我提到过CDC类设备环境就相当于一个工具箱里面啥都有什么画布,画笔,笔刷等等,我们所有的绘画都依靠他来进行,所以不比去在类里面声明一个成员CDC,只需在画线的时候将他作为参数带入即可,不理解的看代码就好了。
首先创建头文件Line.h

#pragma once
#include "pch.h"
#include "afxwin.h"
#include "atltypes.h"
#include "dlgs.h"
//声明要用到的头文件其中"pch.h"必须在最上面
class CLine {
public:
	CLine(void);//无参构造函数
	virtual ~CLine(void);//析构函数
	void MoveTo(CDC* pDC,CPoint p0);
	//将画笔移动到起点,参数需要起点和CDC设备环境
	void LineTo(CDC* pDC, CPoint p1);
	//画一条直线到终点,参数需要终点和CDC
private:
	CPoint P0;//起点,CPoint是MFC封装好的类之前提到过
	CPoint P1;//终点

};

源文件:

既然头文件已经实现好了,那接下来就需要把头文件声明的操作函数都在源文件去具体实现
首先创建源文件Line.cpp

#include "pch.h"
#include "Line.h"
#include "math.h"


CLine::CLine()//无参构造函数
{

}

CLine::~CLine()//析构函数
{

}
void CLine::MoveTo(CDC* pDC,CPoint p0) {//移动起点到p0
	P0 = p0;//讲指定点p0复制给成员起点P0
}
void CLine::LineTo(CDC* pDC, CPoint p1) {//Bresenham算法实现
	P1 = p1;//设置终点
	int x=P0.x, y=P0.y,f;//x,y为要画的点,f是用来判断候选点的关系式
	int directx=-1,directy=-1;
	/*x,y方向增量就咋说比如终点x坐标在起点x坐标左边的话,
	那x就每次加一反之减一,这里先初始化为-1后面再判断一下*/
	int dx = abs(P1.x - P0.x);//算出起点x坐标和终点x坐标差值
	int dy = abs(P1.y - P0.y);//算出y坐标差值
	if (dx >= dy) {//谁大谁就是主增量方向在视频有详细解释
	//这里x大x就是主增量方向
		if (P1.x > P0.x)directx = 1;//判断终点x坐标和起点坐标关系
		//终点在起点右边的话就每次加一反之减一
		if (P1.y > P0.y)directy = 1;//同上
		f = 2 * dy - dx;//设置判断候选点关系式初值
		if (directx > 0) {//分类讨论如果x是每次加一的话原本是可以根据
		//directx来一个巧妙地设计但是我发现当值为0是是个Bug优化太麻烦了
		//就这么分类讨论吧
			for (x = P0.x; x <= P1.x; x++) {//从起点遍历到终点算出对应y坐标
			//然后绘制对应像素点
				pDC->SetPixelV(x, y, RGB(255, 0, 255));
				if (f < 0) {
					f += 2 * (dy - dx);
				}
				else {
					y += directy;
					f += 2 * dy;
				}
			}
		}
		else {//如果x是每次减一的话
			for (x = P0.x; x >= P1.x; x--) {
				pDC->SetPixelV(x, y, RGB(255, 0, 255));
				if (f < 0) {
					f += 2 * (dy - dx);
				}
				else {
					y += directy;
					f += 2 * dy;
				}
			}
		}

	}
	else {
	//同上就是这里是y是主要增量方向就x和y颠倒一下位置就行
		if (P1.x > P0.x)directx = 1;
		if (P1.y > P0.y)directy = 1;
		f = 2 * dx - dy;
		if (directy > 0) {
			for (y = P0.y; y <= P1.y; y ++) {
				pDC->SetPixelV(x, y, RGB(255, 0, 255));
				if (f < 0) {
					f += 2 * (dx - dy);
				}
				else {
					x += directx;
					f += 2 * dx;
				}
			}
		}
		else {
			for (y = P0.y; y >= P1.y ; y--) {
				pDC->SetPixelV(x, y, RGB(255, 0, 255));
				if (f < 0) {
					f += 2 * (dx - dy);
				}
				else {
					x += directx;
					f += 2 * dx;
				}
			}
		}
	}
}

到这里CLine类就已经设计完毕,接下来实际运用一下

绘制任意斜率直线:

进入你的TestView.cpp文件,这里的Test取决于你的项目名称,然后找到OnDraw函数在这里实现绘制代码

void CTest2View::OnDraw(CDC* pDC)
{
	CTest2Doc* pDoc = GetDocument();//之前介绍过了
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	//套我们之前的自定义二次元坐标系模板
	CRect rect;//创建矩形对象
	GetClientRect(rect);//初始化矩形为客户区
	pDC->SetMapMode(MM_ANISOTROPIC);//设置映射模式为各向异性的
	pDC->SetWindowExt(rect.Width(), rect.Height());//设置窗口的宽度和高度
	pDC->SetViewportExt(rect.Width(), -rect.Height());//设置视区的宽度和高度,同时设置坐标系正方向
	pDC->SetViewportOrg(rect.Width() / 2, rect.Height() / 2);//设置原点坐标于中心
	rect.OffsetRect(-rect.Width() / 2, -rect.Height() / 2);

	// TODO: 在此处为本机数据添加绘制代码
	CLine* line1=new CLine;
	/*
	创建一个CLine对象,这里我好好说一下有助于你们对于面向对象编程的理解
	首先把他拆成两部分,Cline* line1和 new Cline,左边是创建了一个Cline
	类型的指针,右边是创建了一个CLine对象在堆中(应该是在队中java就是没查
	过c++是不是),然后将指针指向这个对象,通过指针去调用对象的操作和访问其
	属性。
	那为啥要这么做呢,因为对象通常都很大,就像这个直线CLine他有很多成员变量
	和操作函数,在堆栈中存放太占空间,所以把它放在堆中,堆的空间大,举个例子
	你有一台电视机,电视机很大,你看电视不可能把电视抱着,走到哪抱到哪,假如
	放置到一个地方,那你想去挑音量换台的时候总不可能每次都去到电视机跟前去按
	他的按钮,这样很麻烦,所以我们选择的是,将他放在一个大空间放着,然后用遥
	控器去操作他,遥控器很小可以随时拿着,放在身上要去操作电视机也很便捷,那
	这个遥控器就是这个指针在java里面就是引用,电视机就是那个对象
	*/
	CPoint p0(-200, 100);//设置起点
	CPoint p1(0, -200);//设置终点
	line1->MoveTo(pDC,p0);//调用Cline类的MoveTo函数去移动到起点
	line1->LineTo(pDC, p1);//调用画直线函数去画
	delete line1;//释放内存

}

你可能感兴趣的:(mfc,c++,windows,算法)