C++使用graphics.h绘制二维坐标


前言

这是一个学习笔记,仅供参考。
运行环境window10-VS2019-Dedug-x86。
安装了额外的库<graphics.h>

一、了解graphics.h的基本用法

用graphics.h绘制出下面的图片

在这里插入图片描述

1.创建一个画板

#include <graphics.h>		// 引用图形库头文件
#include <conio.h>			// _getch()头文件
void TODO();
int main()
{
	initgraph(640, 480);	// 创建绘图窗口,大小为 640x480 像素
	TODO();					// 创建完画板后做的事
	_getch();				// 按任意键继续
	closegraph();			// 关闭绘图窗口
	return 0;
}
void TODO()
{
	//TODO
}

2.设置白色背景

void TODO()
{
	setbkcolor(WHITE);		// 设置背景色为白色
	cleardevice();			// 用背景色清空屏幕
}

3.绘制一个点

void TODO()
{
	setlinecolor(RED);			// 设置边框颜色为红色
	setfillcolor(RED);			// 设置填充颜色为红色
	setfillstyle(BS_SOLID);		// 设置填充样式为固实填充
	fillcircle(100, 100, 5);	// 绘制Point(100,100)半径为5px的点
}

4.绘制一条直线

void TODO()
{
	setlinecolor(GREEN);		// 设置线的颜色为绿色
	setlinestyle(PS_SOLID, 3);	// 设置线的样式为实线3px
	line(100,300,500,300);		// 绘制Point(100,300)到Point(500,300)的直线
}

5.绘制一段文字

void TODO()
{
	RECT r = { 0, 0, 639, 479 };		// 矩形Point(0,0)到Point(639,479)
	settextcolor(BLUE);					// 设置文本颜色为蓝色
	settextstyle(20, 0, _T("宋体"));	// 设置字体高度为20px,宽自适应,字体为宋体
	drawtext(_T("123"), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);	// 绘制"123"文本到矩形中,水平居中|垂直居中|限定一行
}

二、封装为graph2d类

基本了解graphics.h,接下来封装一个graph2d类,方便后续的一些操作。
在graph2d.h中包含的库、结构体、宏定义。如下:

#include <iostream>
#include <graphics.h>
#include <conio.h>
#include <vector>
#define f(_lambda) ([](double x) {return (_lambda); })
typedef struct mypoint {
	double x;
	double y;
}Point;

1.构造和析构

创建一个画板及结束后关闭画板

graph2d::graph2d(double _width, double _height)
{
	width = _width;
	height = _height;
	initgraph(int(width), int(height), SHOWCONSOLE);
}
graph2d::~graph2d()
{
	closegraph();
}

2.显示画板

#include <iostream>
#include "graph2d.h"
using namespace std;
int main()
{
	graph2d g2d;
	return 0;
}

执行后发现画板一闪而过,原因是没有等待,所以写一个等待函数。

void graph2d::waitKey(int _delay)
{
	_getch();
}

加上等待函数画板就能一直显示了。

#include <iostream>
#include "graph2d.h"
using namespace std;
int main()
{
	graph2d g2d;
	g2d.waitKey();
	return 0;
}

设置一下画板背景和一个矩形就能做到下图所示:

在这里插入图片描述

3.绘制坐标轴

具体代码省略,大致内容就是在画板上画画。如下:

在这里插入图片描述

void graph2d::initAxis()
{
	setBackgroundColor();		// 设置灰色背景颜色
	setAxisColor();				// 设置白色坐标轴
	drawGrid();					// 绘制网格线
	drawAxisX();				// 绘制x轴的坐标
	drawAxisY();				// 绘制y轴的坐标
}

4.plot函数

plot函数是本次设计的主要目标,设想了3个重载,分别绘制点,线,方程。因为画板的坐标和绘制方程的坐标不一样,所以需要设计一个坐标转换的函数。

/*绘制一个点,参数为点的位置(无默认值),颜色(默认=RED),大小(默认=3),样式(默认=BS_SOLID)*/
void graph2d::plot(Point _point, COLORREF _color, int _size, int _type)
{
	if (isBorder(_point)) {
		setlinecolor(_color);
		setfillcolor(_color);
		setfillstyle(_type);
		fillcircle(numConversion(fucCSDataToAbsCSData(_point).x, 'x'), numConversion(fucCSDataToAbsCSData(_point).y, 'y'), _size);
	}
	else {
		showError("point");
	}
}
/*绘制一连串的线,参数为一连串的点(无默认值),颜色(默认=BLACK),粗细(默认=3),样式(默认=PS_SOLID)*/
void graph2d::plot(std::vector<Point> _point, COLORREF _color, int _thickness, int _type)
{
	setlinecolor(_color);
	setlinestyle(_type, _thickness);
	for (int i = 1; i < _point.size(); i++) {
		if (isBorder(_point[i - 1]) && isBorder(_point[i])) {
			line(numConversion(fucCSDataToAbsCSData(_point[i - 1]).x, 'x'), numConversion(fucCSDataToAbsCSData(_point[i - 1]).y, 'y'), numConversion(fucCSDataToAbsCSData(_point[i]).x, 'x'), numConversion(fucCSDataToAbsCSData(_point[i]).y, 'y'));
		}
		else {
			showError("line");
		}
	}
}
/*绘制一个方程,参数为开始值(无默认值),结束值(无默认值),方程(无默认值),间隔(默认=0.1),颜色(默认=BLACK),粗细(默认=3),样式(默认=PS_SOLID)*/
void graph2d::plot(double _start, double _end, double (*_f)(double), double _step, COLORREF _color, int _thickness, int _type)
{
	std::vector<Point> _point;
	for (double i = _start; i <= _end; i += _step) {
		_point.push_back({ i, _f(i) });
	}
	plot(_point, _color, _thickness, _type);
}

plot中用到的其他函数

/*判断点是否在坐标轴内*/
bool graph2d::isBorder(Point _point)
{
	return (pointlb.x <= _point.x && _point.x <= pointrt.x && pointlb.y <= _point.y && _point.y <= pointrt.y);
}
/*y轴上下颠倒,并转换为整形*/
int graph2d::numConversion(double _num, char _axis)
{
	if (_axis == 'x' || _axis == 'X') {
		return int(_num);
	}
	if (_axis == 'y' || _axis == 'Y') {
		return int(height - _num);
	}
	return 0;
}
/*方程坐标转换到画板的坐标*/
Point graph2d::fucCSDataToAbsCSData(Point _point)
{
	return { ((_point.x - pointlb.x) / (pointrt.x - pointlb.x) * 0.78 * width + 0.12 * width),((_point.y - pointlb.y) / (pointrt.y - pointlb.y) * 0.78 * height + 0.1 * height) };
}
/*如果有一个点/线没有显示出来,则打印该函数*/
void graph2d::showError(std::string _err)
{
	std::cout << "Maybe a " << _err << " is not in the coordinates" << std::endl;
}

5.其他函数

在设计时还设想了以下函数,代码略。

void title(std::string _str);							// 标题
void xlabel(std::string _str);							// x轴文本注释
void ylabel(std::string _str);							// y轴文本注释,可以用空格换行
void text(std::string _str);							// 在坐标中添加注释
void legend(std::string _str, COLORREF _color, int _num);// 标签

三、使用graph2d.h

1.使用示例

写好了graph2d.h和graph2d.cpp,测试一下代码。

1.1画一堆离散的点

代码:

#include <iostream>
#include <math.h>
#include <time.h>
#include "graph2d.h"
using namespace std;
double generateGaussianNoise(double mu, double sigma);              // 产生高斯分布的值
double mrand(double a, double b);                                   // 产生a-b的平均分布的随机数
int main()
{
    srand(time(NULL));
    graph2d g2d(700, 590, { -2,-2 }, { 2,2 });
    g2d.xlabel("x轴");
    g2d.ylabel("y 轴");
    g2d.title("离散的点");
    for (int i = 0; i < 1000; i++) {                                // 产生1000个点
        double r = min(abs(generateGaussianNoise(0, 1)), 2);        // 产生r的半径,服从高斯分布
        double x = mrand(-sqrt(r), sqrt(r));
        double y = mrand(-sqrt(r - x * x), sqrt(r - x * x));
        g2d.plot({ x,y });                                          // 在r半径的圆内,随机产生一点
    }
    g2d.waitKey();
    return 0;
}
double mrand(double a, double b)
{
    return (rand() / double(RAND_MAX)) * (max(a, b) - min(a, b)) + min(a, b);
}
double generateGaussianNoise(double mu, double sigma)
{
    const double epsilon = 1e-8;
    const double two_pi = 2.0 * 3.14159265358979323846;
    static double z0, z1;
    static bool generate;
    generate = !generate;
    if (!generate)
        return z1 * sigma + mu;
    double u1, u2;
    do
    {
        u1 = rand() * (1.0 / RAND_MAX);
        u2 = rand() * (1.0 / RAND_MAX);
    } while (u1 <= epsilon);
    z0 = sqrt(-2.0 * log(u1)) * cos(two_pi * u2);
    z1 = sqrt(-2.0 * log(u1)) * sin(two_pi * u2);
    return z0 * sigma + mu;
}

截图:

在这里插入图片描述

1.2画一个五角星

代码:

#include <iostream>
#include "graph2d.h"
using namespace std;
int main()
{
	graph2d g2d(700, 590, { 0,0 }, { 10,10 });
	g2d.xlabel("x轴");
	g2d.ylabel("y 轴");
	g2d.title("五角星");
	g2d.plot({ {2,0},{4.5,10},{7,0},{0,6},{9,6},{2,0} }, BLUE);
	g2d.waitKey();
	return 0;
}

截图:

在这里插入图片描述

1.3画一个方程

代码:

#include <iostream>
#include "graph2d.h"
using namespace std;
int main()
{
	graph2d g2d(700, 590, { -10,-1 }, { 10,1 });
	g2d.xlabel("x轴");
	g2d.ylabel("y 轴");
	g2d.title("sin(x)");
	g2d.plot(-10, 10, f(sin(x)), 0.1, GREEN);
	g2d.waitKey();
	return 0;
}

截图:

在这里插入图片描述

2.具体代码

2.1graph2d.h

#pragma once
#include <iostream>
#include <graphics.h>
#include <conio.h>
#include <vector>
#include <math.h>
#define f(_lambda) ([](double x) {return (_lambda); })
typedef struct mypoint {
	double x;
	double y;
}Point;
class graph2d
{
private:
	double height;											// 画板高度
	double width;											// 画板宽度
	Point pointlb;											// 坐标轴左下角的点
	Point pointrt;											// 坐标轴右上角的点
	int x_len;												// x轴字的宽度
	int y_len;												// y轴字的宽度
public:
	/*********************************
	 *
	 *	初始化、坐标转换、绘制坐标等等
	 *
	 *********************************/
	graph2d();												// 初始化为默认值
	graph2d(double _width, double _height);					// 初始化画板宽高
	graph2d(double _width, double _height, Point _pointlb, Point _pointrt);		// 初始化画板宽高及网格
	~graph2d();												// 析构函数
	void waitKey(int _delay = 0);							// 等待关闭
private:
	/*********************************
	 *
	 *	坐标转换、绘制坐标等等
	 *
	 *********************************/
	wchar_t* ctow(const char* str);							// char* to wchar_t*
	void setlen(int _len = 3);								// 设置坐标文本长度
	std::string dtos(double _num, char _axis);				// string to double
	void setGrid(Point _pointlb, Point _pointrt);			// 设置网格
	void drawGrid();										// 绘制网格
	void drawAxisX();										// 绘制X轴
	void drawAxisY();										// 绘制Y轴
	bool isBorder(Point _point);							// 是否在边界内
	int numConversion(double _num, char _axis);				// 将y轴颠倒
	Point fucCSDataToAbsCSData(Point _point);				// 方程的点转换到画板的点
	Point absCSDataToFucCSData(Point _point);				// 画板的点转换到方程的点
	void showError(std::string _err);						// 显示错误
	void drawRectangle(Point _pointlb, Point _pointrt, COLORREF _colorl, COLORREF _colorf, int _style = BS_SOLID);	//画正方形
	void setBackgroundColor(COLORREF _color = 0xEAEAEA);	// 设置画板背景颜色
	void setAxisColor();									// 设置坐标系背景颜色
	void initAxis();										// 初始化坐标轴内的信息
	void mouseClick();										// 鼠标点击来获取该点函数坐标
public:
	/*********************************
	 *
	 *	绘制坐标方程函数
	 *
	 *********************************/
	void plot(Point _point, COLORREF _color = RED, int _size = 3, int _type = BS_SOLID);		// 绘制点
	void plot(std::vector<Point> _point, COLORREF _color = BLACK, int _thickness = 3, int _type = PS_SOLID);		// 绘制一连串的线
	void plot(double _start, double _end, double (*_f)(double), double _step = 0.1, COLORREF _color = BLACK, int _thickness = 3, int _type = PS_SOLID);		// 绘制f(x)方程
	void title(std::string _str);							// 标题
	void xlabel(std::string _str);							// x轴文本注释
	void ylabel(std::string _str);							// y轴文本注释,可以用空格换行
	void text(std::string _str);							// 在坐标中添加注释
	void legend(std::string _str, COLORREF _color, int _num);// 标签
};

2.2graph2d.cpp

#include "graph2d.h"
graph2d::graph2d()
{
	width = 700;
	height = 590;
	initgraph(int(width), int(height), SHOWCONSOLE);
	setGrid({ 0,0 }, { 10,10 });
	setlen();
	initAxis();
}
graph2d::graph2d(double _width, double _height)
{
	width = _width;
	height = _height;
	initgraph(int(width), int(height), SHOWCONSOLE);
	setGrid({ 0,0 }, { 10,10 });
	setlen();
	initAxis();
}
graph2d::graph2d(double _width, double _height, Point _pointlb, Point _pointrt)
{
	width = _width;
	height = _height;
	initgraph(int(width), int(height), SHOWCONSOLE);
	setGrid(_pointlb, _pointrt);
	setlen();
	initAxis();
}
graph2d::~graph2d()
{
	closegraph();
}
wchar_t* graph2d::ctow(const char* str)
{
	int length = strlen(str) + 1;
	wchar_t* t = (wchar_t*)malloc(sizeof(wchar_t) * length);
	memset(t, 0, length * sizeof(wchar_t));
	MultiByteToWideChar(CP_ACP, 0, str, strlen(str), t, length);
	return t;
}
void graph2d::setlen(int _len)
{
	x_len = max(int(1 + log10(max(pointlb.x, pointrt.x))), _len);
	y_len = max(int(1 + log10(max(pointlb.y, pointrt.y))), _len);
}
std::string graph2d::dtos(double _num, char _axis)
{
	int _len = 3;
	std::string _str = "";	// 储存结果
	int _lg = 0;			// 10^__lg
	if (_axis == 'x' || _axis == 'X')
		_len = x_len;
	if (_axis == 'y' || _axis == 'Y')
		_len = y_len;
	if (int(_num * pow(10.0, _len - 1)) == 0) {
		for (int i = 0; i < _len - 1; i++) {
			_str += '0';
		}
		return "0." + _str;
	}
	if (_num < 0) { _num = -_num; _str += '-'; }
	else { _str += ' '; }
	while (int(_num) > 1) { _num /= 10; _lg++; }
	while (int(_num) < 1 && _lg > 0) { _num *= 10; _lg--; }
	for (int i = 0; i < _len; i++) {
		_str += ('0' + int(_num) % 10);
		if (i == _lg && i + 1 != _len) {
			_str += '.';
		}
		_num *= 10;
	}
	return _str;
}
void graph2d::waitKey(int _delay)
{
	_getch();
}
void graph2d::setGrid(Point _pointlb, Point _pointrt)
{
	pointlb = { min(_pointlb.x,_pointrt.x), min(_pointlb.y,_pointrt.y) };
	pointrt = { max(_pointlb.x,_pointrt.x), max(_pointlb.y,_pointrt.y) };
}
void graph2d::drawGrid()
{
	setlinecolor(0xE0E0E0);
	setlinestyle(PS_SOLID, 1);
	for (int i = 1; i < 10; i++) {
		line(numConversion(0.078 * width * i + 0.12 * width, 'x'), numConversion(0.1 * height, 'y'), numConversion(0.078 * width * i + 0.12 * width, 'x'), numConversion(0.88 * height, 'y'));
		line(numConversion(0.12 * width, 'x'), numConversion(0.078 * height * i + 0.1 * height, 'y'), numConversion(0.9 * width, 'x'), numConversion(0.078 * height * i + 0.1 * height, 'y'));
	}
}
void graph2d::drawAxisX()
{
	std::string _str = "     ";
	for (int i = 0; i <= 10; i++) {
		_str += dtos((pointrt.x - pointlb.x) / 10 * i + pointlb.x, 'x') + "  ";
	}
	RECT r = { numConversion(0,'x'), numConversion(0.04 * height,'y'), numConversion(width,'x'), numConversion(0.11 * height,'y') };
	settextcolor(BLACK);
	settextstyle(int((24 - 2 * x_len) * (width / 700) * 0.9), 0, _T("宋体"));
	drawtext(ctow(_str.c_str()), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
void graph2d::drawAxisY()
{
	std::string _str = "\n\n\n\n\n";
	for (int i = 10; i >= 0; i--) {
		_str += dtos((pointrt.y - pointlb.y) / 10 * i + pointlb.y, 'y') + "\n\n\n";
	}
	RECT r = { numConversion(0,'x'), numConversion(height,'y'), numConversion(0.11 * width,'x'), numConversion(0,'y') };
	settextcolor(BLACK);
	settextstyle(int(15 * height / 590), 0, _T("宋体"));
	drawtext(ctow(_str.c_str()), &r, DT_NOCLIP | DT_RIGHT);
}
bool graph2d::isBorder(Point _point)
{
	return (pointlb.x <= _point.x && _point.x <= pointrt.x && pointlb.y <= _point.y && _point.y <= pointrt.y);
}
int graph2d::numConversion(double _num, char _axis)
{
	if (_axis == 'x' || _axis == 'X') {
		return int(_num);
	}
	if (_axis == 'y' || _axis == 'Y') {
		return int(height - _num);
	}
	return 0;
}
Point graph2d::fucCSDataToAbsCSData(Point _point)
{
	return { ((_point.x - pointlb.x) / (pointrt.x - pointlb.x) * 0.78 * width + 0.12 * width),((_point.y - pointlb.y) / (pointrt.y - pointlb.y) * 0.78 * height + 0.1 * height) };
}
Point graph2d::absCSDataToFucCSData(Point _point)
{
	return { ((_point.x - 0.12 * width) * (pointrt.x - pointlb.x) / 0.78 / width + pointlb.x),((_point.y - 0.1 * height) * (pointrt.y - pointlb.y) / 0.78 / height + pointlb.y) };
}
void graph2d::showError(std::string _err)
{
	std::cout << "Maybe a " << _err << " is not in the coordinates" << std::endl;
}
void graph2d::setBackgroundColor(COLORREF _color)
{
	setbkcolor(_color);
	cleardevice();
}
void graph2d::setAxisColor()
{
	drawRectangle({ 0.12 * width ,0.1 * height }, { 0.9 * width ,0.88 * height }, BLACK, WHITE);
}
void graph2d::drawRectangle(Point _pointlb, Point _pointrt, COLORREF _colorl, COLORREF _colorf, int _style)
{
	setlinecolor(_colorl);
	setfillcolor(_colorf);
	setfillstyle(_style);
	fillrectangle(numConversion(_pointlb.x, 'x'), numConversion(_pointrt.y, 'y'), numConversion(_pointrt.x, 'x'), numConversion(_pointlb.y, 'y'));
}
void graph2d::initAxis()
{
	setBackgroundColor();
	setAxisColor();
	drawGrid();
	drawAxisX();
	drawAxisY();
}
void graph2d::plot(Point _point, COLORREF _color, int _size, int _type)
{
	if (isBorder(_point)) {
		setlinecolor(_color);
		setfillcolor(_color);
		setfillstyle(_type);
		fillcircle(numConversion(fucCSDataToAbsCSData(_point).x, 'x'), numConversion(fucCSDataToAbsCSData(_point).y, 'y'), _size);
	}
	else {
		showError("point");
	}
}
void graph2d::plot(std::vector<Point> _point, COLORREF _color, int _thickness, int _type)
{
	setlinecolor(_color);
	setlinestyle(_type, _thickness);
	for (int i = 1; i < _point.size(); i++) {
		if (isBorder(_point[i - 1]) && isBorder(_point[i])) {
			line(numConversion(fucCSDataToAbsCSData(_point[i - 1]).x, 'x'), numConversion(fucCSDataToAbsCSData(_point[i - 1]).y, 'y'), numConversion(fucCSDataToAbsCSData(_point[i]).x, 'x'), numConversion(fucCSDataToAbsCSData(_point[i]).y, 'y'));
		}
		else {
			showError("line");
		}
	}
}
void graph2d::plot(double _start, double _end, double (*_f)(double), double _step, COLORREF _color, int _thickness, int _type)
{
	std::vector<Point> _point;
	for (double i = _start; i <= _end; i += _step) {
		_point.push_back({ i, _f(i) });
	}
	plot(_point, _color, _thickness, _type);
}
void graph2d::title(std::string _str)
{
	RECT r = { numConversion(0,'x'), numConversion(0.88 * height,'y'), numConversion(width,'x'), numConversion(height,'y') };
	settextcolor(BLACK);
	settextstyle(int(20 * height / 590), 0, _T("宋体"));
	drawtext(ctow(_str.c_str()), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
void graph2d::xlabel(std::string _str)
{
	RECT r = { numConversion(0,'x'), numConversion(0,'y'), numConversion(width,'x'), numConversion(0.05 * height,'y') };
	settextcolor(BLACK);
	settextstyle(int(20 * height / 590), 0, _T("宋体"));
	drawtext(ctow(_str.c_str()), &r, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
}
void graph2d::ylabel(std::string _str)
{
	RECT r = { numConversion(0.01 * width,'x'), numConversion(0.6 * height,'y'), numConversion(0.05 * width,'x'), numConversion(0,'y') };
	settextcolor(BLACK);
	settextstyle(int(20 * width / 700), 0, _T("宋体"));
	drawtext(ctow(_str.c_str()), &r, DT_NOCLIP | DT_LEFT | DT_WORDBREAK);
}
void graph2d::text(std::string _str)
{
	//TODO
}
void graph2d::legend(std::string _str, COLORREF _color, int _num)
{
	//TODO
}
void graph2d::mouseClick()
{
	//TODO
}

总结

在查阅资料时发现c++好像可以调用matlab画图,但具体不太会操作o(TヘTo)。
如有错误欢迎指出。
参考:
【EasyX使用教程】https://docs.easyx.cn/zh-cn/tutorials.
【char转换成wchar_t】https://blog.csdn.net/zhxuan30/article/details/22692389.
【高斯分布随机数】https://www.cnblogs.com/tsingke/p/6194737.html.

  • 33
    点赞
  • 181
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值