目录
前言
总体设计
1.思路介绍
2.模块介绍
代码:
<智能指针.hpp>
<文件操作模块.h>
<文件管理模块.cpp>
<鼠标操作模块.h>
<鼠标操作模块.cpp>
<界面逻辑模块.h>
<界面逻辑模块.cpp>
<界面外观模块.h>
<界面外观模块.cpp>
<选色模块.h>
<选色模块.cpp>
如何运行?
2.效果展示
总结
前言
头一回写博客,不知道有啥要求,随便写了。
这是我们大一小学期面向对象程序设计实习作业:简易矢量图生成系统。我和另外两名同学用vs2022和自带的easyx图形工具共同完成了这坨代码(别问为啥不用Qt啥的)
总体设计
1.思路介绍
瞎设计的,总之能跑。也不能说完全没有设计思绪,如图:
这俩图差不多,第二个是我画的,更注重图形管理这块,有一些各类的功能介绍。
所幸有设计思路,我们按照分模块的方法分工协作,将项目分为CShape(图形)模块、智能指针模块、文件管理模块、界面逻辑模块,鼠标操作模块、界面外观模块以及外观模块附带的选色模块。(好吧刚开始压根没想这么多)
2.模块介绍
1. CShape模块,顾名思义,图形基类和各派生类。
2.智能指针模块,顾名思义,管理图形类用的指针。
3.文件管理模块,顾名思义,管理图形和文件操作的模块,包含图形管理类和图形文件流类。
4.界面逻辑模块,顾名思义,管理界面相应操作带来的对应结果用,说白了信息处理。
5.界面外观模块,顾名思义,长相。
6.鼠标操作模块,其实是创建图形时的动态动画。
7.选色模块,顾名思义,取色板,分为填充色和线条色。
二、代码实现
先上类图(不想看就划走吧,我也不想看):
代码:
<CShape.h>
定义了全局颜色变量,各类的声明。
#pragma once
#include <iostream>
#include <graphics.h>
#include <cmath>
#include <assert.h>
using namespace std;
extern COLORREF ANY_FILL_COLOR;//全局填充颜色变量
extern COLORREF ANY_LINE_COLOR;//全局线条颜色变量
extern COLORREF ANY_FONT_COLOR;//全局字体颜色变量
const double PI = 3.1415926;
/*********************************
*CShape基类,满足矢量图共有的特征
* id 为 0
*********************************/
class CRectangle;
class CShape {
protected:
const int m_id;
public:
//构造、析构函数
CShape(int id = 0);
virtual ~CShape() {}//一定是虚析构,这样子类会调用自己的析构函数
virtual bool ifPointIn(POINT& a) const;//判断某点是否在图形内
virtual bool ifShapeIn(POINT& a, POINT& b) const;//图形是否在框选图中
virtual bool ifShapeIn(const CRectangle& rec) const;//图形是否在框选图中
virtual void draw(COLORREF line_color)const;//绘制,无填充,采用线条颜色
virtual void drawColor(COLORREF line_color, COLORREF fill_color)const;//绘制,采用填充颜色和线条颜色
virtual CShape* copy()const;//复制图形
virtual CShape& move(int _x,int _y);//移动
//set函数
virtual void setPoint(int i, const POINT&) {};//设置第i点坐标,i = 1,2,3
virtual void setPoints(POINT* ptr, int num) {}//设置多个点
//get函数
int getId()const;//获取类型
virtual double getArea();//获取面积
virtual int getNum() const;//返回类内点个数
virtual POINT getPoint(int i) const;//获取第i个点
virtual double getLength(int num = 1)const;//获取边长
};
/*********************************
* CStraightline直线类,通过两点确定
* id 为 1
*********************************/
class CStraightline :public CShape{
protected:
POINT m_p1;
POINT m_p2;
private://屏蔽用
void setPoints(POINT* ptr, int num) override {}//设置多个点
public:
//构造,析构
CStraightline();
CStraightline(const POINT& p1, const POINT& p2);
bool ifShapeIn(const CRectangle& rec)const;//图形是否在框选图中
void draw(COLORREF line_color)const;//绘制,无填充
void drawColor(COLORREF line_color, COLORREF fill_color) const;//绘制,有颜色
CStraightline* copy()const;//复制图形
CStraightline& move(int _x, int _y);//移动
void setPoint(int i,const POINT& point);//设置第i点坐标,i = 1,2
POINT getPoint(int i) const;//获取第i个点
double getLength(int num )const;//获取边长
int getNum() const;//返回类内点个数
};
/*********************************
*CTriangle三角形类,通过三点确定
* id 为 2
*********************************/
class CTriangle :public CShape{
protected:
POINT pts[3];//包含三个顶点坐标的数组
public:
//构造析构
CTriangle();
CTriangle(const POINT& p1, const POINT& p2, const POINT& p3);
void draw(COLORREF line_color)const;//绘制,无填充
void drawColor(COLORREF line_color,COLORREF fill_color)const;//绘制,填充颜色
bool ifPointIn(const POINT& pt)const;//判断某点是否在图形内
bool ifShapeIn(const CRectangle& rec)const;//图形是否在框选图中
CTriangle* copy()const;//复制图形
CTriangle& move(int _x, int _y);//移动
//set函数
void setPoint(int i,const POINT& point);//设置第i点坐标,i = 1,2,3
void setPoints(POINT* ptr, int num);//设置多个点
//get函数
POINT getPoint(int i) const;//获取第i个点
double getLength(int num)const;//获取边长,num = 1,2,3;
double getArea()const;//获取面积
int getNum()const;//获取点的个数
};
/*********************************
*CEllipse椭圆类,通过两点确定
* id 为 3
*********************************/
class CEllipse :public CShape{
protected:
POINT m_p1;//外切矩形左上角的坐标
POINT m_p2;//外切矩形右下角的坐标
private://屏蔽用
double getLength(int num)const override { return 0.0; }//获取边长
void setPoints(POINT* ptr, int num) override {}//设置多个点
public:
//构造、析构
CEllipse();
CEllipse(const POINT& p1, const POINT& p2 );
//判断某点是否在图形内
bool ifPointIn(const POINT& pt)const;
bool ifShapeIn(const CRectangle& rec)const;//图形是否在框选图中
void draw(COLORREF line_color)const;//绘制,无填充
void drawColor(COLORREF line_color, COLORREF fill_color)const;//绘制,填充颜色
CEllipse* copy()const;//复制图形
CEllipse& move(int _x, int _y);//移动
void setPoint(int i, const POINT& point);//设置第i点的坐标
double getArea()const;//获取面积
int getNum()const;//获取点的个数
POINT getPoint(int i) const;//获取第i个点,i = 1,2
};
/*********************************
*CRectangle矩形类,通过两点确定
* id 为 4
*********************************/
class CRectangle :public CShape {
protected:
POINT m_p1;//矩形的左上点
POINT m_p2;//矩形的右下点
private://屏蔽用
void setPoints(POINT* ptr, int num) override {}//设置多个点
public:
//构造/析构
CRectangle();
CRectangle(const POINT& ptl,const POINT& pt2);
bool ifPointIn(const POINT& a)const;//判断某点是否在图形内
bool ifShapeIn(const CRectangle& rec)const;//图形是否在框选图中
void draw(COLORREF line_color)const;//绘制,无填充
void drawColor(COLORREF line_color, COLORREF fill_color)const;//绘制,填充颜色
CRectangle* copy()const;//复制图形
CRectangle& move(int _x, int _y);//移动
void setPoint(int i, const POINT& point);
POINT getPoint(int i) const;
double getArea() const;//获取面积
int getNum() const;//返回类内点个数
double getLength(int num)const;//获取边长,num = 1,2;
};
/*********************************
*CCircle圆类,通过圆心半径确定
* id 为 5
*********************************/
class CCircle :public CShape {
protected:
POINT m_ptCen;//圆的圆心
int m_r;//圆的半径
private:
int getNum() const override { return 1; }//返回类内点个数
void setPoints(POINT* ptr, int num) override {}//设置多个点
public:
CCircle();
CCircle(POINT& pt, int r);
bool ifPointIn(const POINT& a)const;//判断某点是否在图形内
bool ifShapeIn(const CRectangle& rec)const;//图形是否在框选图中
void draw(COLORREF line_color)const;//绘制,无填充
void drawColor(COLORREF line_color, COLORREF fill_color)const;//绘制,填充颜色
CCircle* copy()const;//复制图形
CCircle& move(int _x, int _y);//移动
//第一个参数是半径,第二个是圆心
void setPoint(int i, const POINT& tmp);
POINT getPoint(int i) const;//获取圆心
double getArea() const;//获取面积
double getLength(int num)const;//获取半径
};
/*********************************
* CPolygon多边形类,通过n点顺序确定
* id 为 6
*********************************/
class CPolygon :public CShape {
protected:
int m_num;//点的个数
POINT* m_ptr;//点集合指针
private://屏蔽用
double getLength(int num)const override { return 0.0; }//获取边长,num;
private:
POINT getCentrePoint() const;
public:
//构造函数,深拷贝,析构函数
CPolygon();
CPolygon(POINT* ptr, int num);//这里不能是const
CPolygon(const CPolygon& poly);
~CPolygon();
bool ifPointIn(const POINT& a) const;//改为判断某点是否在图形区域附近
bool ifShapeIn(const CRectangle& rec) const;//图形是否在框选图中
void draw(COLORREF line_color)const;//绘制,无填充
void drawColor(COLORREF line_color, COLORREF fill_color)const;//绘制,填充颜色
CPolygon* copy()const;//复制图形
CPolygon& move(int _x, int _y);//移动
void setPoint(int i, const POINT&);//更新第i个点
void setPoints(POINT* ptr,int num);//同构造
POINT getPoint(int i) const;//获取某点
int getNum() const;//返回类内点个数
};
/*********************************
* 曲线类,起始点,终点,两控制点
* 用三次方贝塞尔曲线实现
* id 为 7
*********************************/
class CCurve :public CShape {
protected:
POINT m_pts[4];//起点、控制点1、控制点2、终点
private://屏蔽用
double getLength(int num)const override { return 0.0; }//获取边长
public:
CCurve();
CCurve(POINT pts[4]);
//图形是否在框选图中,判断起点和终点
bool ifShapeIn(const CRectangle& rec) const;
void draw(COLORREF line_color)const;//绘制,无填充
void drawColor(COLORREF line_color, COLORREF fill_color) const;
CCurve* copy()const;//复制图形
CCurve& move(int _x, int _y);//移动
void setPoint(int i,POINT& p);//设置第i点
void setPoints(POINT* ptr, int num);//设置多个点
int getNum() const;//返回类内点个数
POINT getPoint(int i) const;//获取某点
};
<CShape.cpp>
各类的实现。
#include"CShape.h"
//全局颜色变量,默认黑
COLORREF ANY_FILL_COLOR = WHITE;
//线条颜色,默认黑
COLORREF ANY_LINE_COLOR = BLACK;
//字体颜色,默认黑
COLORREF ANY_FONT_COLOR = BLACK;
/*********************************
*CShape基类,满足矢量图共有的特征
* id 为 0
*********************************/
CShape::CShape(int id) :m_id(id) {}
bool CShape::ifPointIn(POINT& a) const { return false; }
bool CShape::ifShapeIn(POINT& p1, POINT& p2) const {
CRectangle tmp(p1, p2);
return ifShapeIn(tmp);
}
bool CShape::ifShapeIn(const CRectangle& rec) const { return false; }
void CShape::draw(COLORREF line_color) const{}
void CShape::drawColor(COLORREF line_color, COLORREF fill_color) const {}
CShape* CShape::copy()const { return new CShape(*this); }
CShape& CShape::move(int _x, int _y) { return *this; }
int CShape::getId() const { return m_id; }
double CShape::getArea() { return 0.0; }
int CShape::getNum() const { return 0; }
POINT CShape::getPoint(int i) const { return { 0,0 }; }
double CShape::getLength(int num)const { return 0.0; };
/*********************************
* CStraightline直线类,通过两点确定
* id 为 1
*********************************/
CStraightline::CStraightline():CShape(1) {}
CStraightline::CStraightline(const POINT& p1, const POINT& p2):CShape(1){
m_p1 = p1; m_p2 = p2;
}
bool CStraightline::ifShapeIn(const CRectangle& rec)const{
return (rec.ifPointIn(m_p1) && rec.ifPointIn(m_p2));
}
void CStraightline::draw(COLORREF line_color)const{
setlinecolor(line_color); //设置成存储中的颜色
line(m_p1.x, m_p1.y, m_p2.x, m_p2.y);
}
void CStraightline::drawColor(COLORREF line_color, COLORREF fill_color) const { draw(line_color); }//绘制,有颜色
CStraightline* CStraightline::copy()const{
return new CStraightline(*this);
}
CStraightline& CStraightline::move(int _x, int _y){
m_p1.x += _x; m_p1.y += _y;
m_p2.x += _x; m_p2.y += _y;
return *this;
}
void CStraightline::setPoint(int i,const POINT& point) {
if (i == 1)
m_p1 = point;
else if (i == 2)
m_p2 = point;
else
assert("直线只能操作首尾两点(i = 1,2)");
}
POINT CStraightline::getPoint(int i) const {
if (i == 1) return m_p1;
else if (i == 2) return m_p2;
else assert("直线只能返回端点(i = 1,2)");
}
double CStraightline::getLength(int num )const {
return sqrt((m_p1.x - m_p2.x) * (m_p1.x - m_p2.x)
+ (m_p1.y - m_p2.y) * (m_p1.y - m_p2.y));
}
int CStraightline::getNum() const {
return 2;
}
/*********************************
*CTriangle三角形类,通过三点确定
* id 为 2
*********************************/
CTriangle::CTriangle():CShape(2) {}
CTriangle::CTriangle(const POINT& p1, const POINT& p2, const POINT& p3):CShape(2) {
pts[0] = p1; pts[1] = p2; pts[2] = p3;
}
void CTriangle::draw(COLORREF line_color)const{
setlinecolor(line_color);
polygon(pts, 3);
}
void CTriangle::drawColor(COLORREF line_color, COLORREF fill_color)const{
setlinecolor(line_color);
setfillcolor(fill_color);
fillpolygon(pts, 3);
}
bool CTriangle::ifPointIn(const POINT& pt)const{
CTriangle a(pts[0], pts[1], pt), b(pts[1], pts[2], pt), c(pts[2], pts[0], pt);
return (fabs(getArea() - a.getArea() - b.getArea() - c.getArea()) < 1E-4);
}
bool CTriangle::ifShapeIn(const CRectangle& rec)const{
return (rec.ifPointIn(pts[0]) && rec.ifPointIn(pts[1]) && rec.ifPointIn(pts[2]));
}
CTriangle* CTriangle::copy() const {
return new CTriangle(*this);
}
CTriangle& CTriangle::move(int _x, int _y) {
for (int i = 0; i < 3; i++){
pts[i].x += _x;
pts[i].y += _y;
}
return *this;
}
void CTriangle::setPoint(int i,const POINT& point) {
if (i > 3 || i < 1)
assert("访问到三角形不存在的点");
else
pts[i - 1] = point;
}
void CTriangle::setPoints(POINT* ptr, int num = 3) {
for (int i = 0; i < 3; ++i) {
pts[i] = ptr[i];
}
}
POINT CTriangle::getPoint(int i) const {
if (i <= 3 && i >= 1) {
return pts[i - 1];
}
else
assert("三角形只有三个点(i = 1,2,3)");
}
double CTriangle::getLength(int num)const {
if (num < 1 || num > 3) {
assert("访问到三角形不存在的边");
return 0;
}
else
return sqrt(
(pts[num - 1].x - pts[num % 3].x) * (pts[num - 1].x - pts[num % 3].x)
+ (pts[num - 1].y - pts[num % 3].y) * (pts[num - 1].y - pts[num % 3].y)
);
}
double CTriangle::getArea()const{
double a = getLength(1), b = getLength(2), c = getLength(3);
double p = (a + b + c) / 2;
return sqrt(p * (p - a) * (p - b) * (p - c));
}
int CTriangle::getNum()const {
return 3;
}
/*********************************
*CEllipse椭圆类,通过两点确定
* id 为 3
*********************************/
CEllipse::CEllipse():CShape(3) {}
CEllipse::CEllipse(const POINT& p1, const POINT& p2) :CShape(3) {
m_p1 = p1; m_p2 = p2;
}
bool CEllipse::ifPointIn(const POINT& pt)const{
int x = (m_p1.x + m_p2.x) / 2;//椭圆中点
int y = (m_p1.y + m_p2.y) / 2;
int a = (m_p2.x - m_p1.x) / 2;
int b = (m_p2.y - m_p1.y) / 2;//半轴长
return (pt.x - x) * (pt.x - x) / (a * a) + (pt.y - y) * (pt.y - y) / (b * b) <= 1;
}
bool CEllipse::ifShapeIn(const CRectangle& rec)const{
return (rec.ifPointIn(m_p1) && rec.ifPointIn(m_p2));
}
void CEllipse::draw(COLORREF line_color)const{
setlinecolor(line_color);
ellipse(m_p1.x, m_p1.y, m_p2.x, m_p2.y);
}
void CEllipse::drawColor(COLORREF line_color, COLORREF fill_color)const{
setlinecolor(line_color);
setfillcolor(fill_color);
fillellipse(m_p1.x, m_p1.y, m_p2.x, m_p2.y);
}
CEllipse* CEllipse::copy()const {
return new CEllipse(*this);
}
CEllipse& CEllipse::move(int _x, int _y) {
m_p1.x += _x; m_p1.y += _y;
m_p2.x += _x; m_p2.y += _y;
return *this;
}
void CEllipse::setPoint(int i, const POINT& point) {
if (i < 1 || i > 2)
assert("椭圆只能操作外接矩形的左上点和右下点(i = 1,2)");
else if (i == 1)
m_p1 = point;
else
m_p2 = point;
}
double CEllipse::getArea()const{
int a = abs(this->m_p2.x - this->m_p1.x) / 2;
int b = abs(this->m_p2.y - this->m_p1.y) / 2;//半轴长
return PI * a * b;
}
int CEllipse::getNum()const {
return 2;
}
POINT CEllipse::getPoint(int i) const {
if (i == 1) return m_p1;
else if (i == 2) return m_p2;
else assert("椭圆只由外接矩形的左上点和右下点决定(i = 1,2)");
}
/*********************************
*CRectangle矩形类,通过两点确定
* id 为 4
*********************************/
CRectangle::CRectangle():CShape(4) {};
CRectangle::CRectangle(const POINT& pt1, const POINT& pt2):CShape(4) {
m_p1 = pt1, m_p2 = pt2;
}
bool CRectangle::ifPointIn(const POINT& a)const {
if (m_p1.x < a.x && a.x < m_p2.x || m_p1.x > a.x && a.x > m_p2.x)
if (m_p1.y < a.y && a.y < m_p2.y || m_p1.y > a.y && a.y > m_p2.y)
return true;
return false;
}
bool CRectangle::ifShapeIn(const CRectangle& rec)const{
return rec.ifPointIn(m_p1) && rec.ifPointIn(m_p2);
}
void CRectangle::draw(COLORREF line_color)const{
setlinecolor(line_color);
rectangle(m_p1.x, m_p1.y, m_p2.x, m_p2.y);
}
void CRectangle::drawColor(COLORREF line_color, COLORREF fill_color)const{
setfillcolor(fill_color);
setlinecolor(line_color);
fillrectangle(m_p1.x, m_p1.y, m_p2.x, m_p2.y);
}
CRectangle* CRectangle::copy()const{
return new CRectangle(*this);
}
CRectangle& CRectangle::move(int _x, int _y){
m_p1.x += _x; m_p2.x += _x;
m_p1.y += _y; m_p2.y += _y;
return *this;
}
void CRectangle::setPoint(int i, const POINT& point) {
if (i == 1)
m_p1 = point;
else if (i == 2)
m_p2 = point;
else assert("矩形只用左上点和右下点决定(i = 1,2)");
}
double CRectangle::getArea() const{
return abs(m_p1.x - m_p2.x) * abs(m_p1.y - m_p2.y);
}
POINT CRectangle::getPoint(int i) const {
if (i < 1 || i > 2) {
assert("矩形只由左上角和右下角决定");
return {0,0};
}
return i == 1 ? m_p1 : m_p2;
}
int CRectangle::getNum() const {
return 2;
}
double CRectangle::getLength(int num)const {
if (num == 1) return fabs(m_p1.x - m_p2.x);
else if (num == 2) return fabs(m_p1.y - m_p2.y);
else assert("矩形只有长边和短边(i = 1,2)");
return 0.0;
}
/*********************************
*CCircle圆类,通过圆心半径确定
* id 为 5
*********************************/
CCircle::CCircle():CShape(5) {}
CCircle::CCircle(POINT& pt, int r):CShape(5) {
m_ptCen = pt; m_r = r;
}//初始化圆
bool CCircle::ifPointIn(const POINT& a)const{
return (a.x - m_ptCen.x)* (a.x - m_ptCen.x) +
(a.y - m_ptCen.y)* (a.y - m_ptCen.y) <= m_r * m_r;
}
bool CCircle::ifShapeIn(const CRectangle& rec)const{
POINT p1 = { m_ptCen.x - m_r, m_ptCen.y - m_r };
POINT p2 = { m_ptCen.x + m_r, m_ptCen.y + m_r };
return rec.ifPointIn(p1) && rec.ifPointIn(p2);
}
void CCircle::draw(COLORREF line_color)const{
setlinecolor(line_color);
circle(m_ptCen.x, m_ptCen.y, m_r);
}
void CCircle::drawColor(COLORREF line_color, COLORREF fill_color)const{
setlinecolor(line_color);
setfillcolor(fill_color);
fillcircle(m_ptCen.x, m_ptCen.y, m_r);
}
CCircle* CCircle::copy()const{
return new CCircle(*this);
}
CCircle& CCircle::move(int _x, int _y){
m_ptCen.x += _x;
m_ptCen.y += _y;
return *this;
}
void CCircle::setPoint(int i, const POINT& tmp){
m_r = i;
m_ptCen = tmp;
}
POINT CCircle::getPoint(int i) const {
return m_ptCen;
}
double CCircle::getArea() const {
return PI * m_r * m_r;
}
double CCircle::getLength(int num)const {
return m_r;
}
/*********************************
* CPolygon多边形类,通过n点顺序确定
* id 为 6
*********************************/
CPolygon::CPolygon():CShape(6) {
m_ptr = nullptr;
m_num = 0;
}
CPolygon::CPolygon(POINT* ptr, int num):CShape(6) {
m_ptr = ptr;
m_num = num;
}
CPolygon::CPolygon(const CPolygon& poly):CShape(6) {//深拷贝构造
if (poly.m_ptr) {
m_num = poly.m_num;
m_ptr = new POINT[m_num];
for (int i = 0; i < m_num; ++i) {
m_ptr[i] = poly.m_ptr[i];
}
}
else {
m_num = 0;
m_ptr = nullptr;
}
}
CPolygon::~CPolygon() {
if (m_ptr) {
delete[] m_ptr;
m_ptr = nullptr;
m_num = 0;
}
}
//获取中心点
POINT CPolygon::getCentrePoint() const {
int _x = 0, _y = 0;
for (int i = 0; i < m_num; ++i) {
_x += m_ptr[i].x;
_y += m_ptr[i].y;
}
_x /= m_num;
_y /= m_num;
return { _x,_y };
}
bool CPolygon::ifPointIn(const POINT& a) const{
if (!m_ptr)
return false;
int _x = 0, _y = 0;
int max_x, max_y, min_x, min_y;
max_x = min_x = m_ptr[0].x;
max_y = min_y = m_ptr[0].y;
for (int i = 0; i < m_num; ++i) {
max_x = max(max_x, m_ptr[i].x);
max_y = max(max_y, m_ptr[i].y);
min_x = min(min_x, m_ptr[i].x);
min_y = min(min_y, m_ptr[i].y);
_x += m_ptr[i].x;
_y += m_ptr[i].y;
}
_x /= m_num;
_y /= m_num;
int tmp_r = (max_x + max_y - min_x - min_y) / 6;
return (a.x - _x) * (a.x - _x) + (a.y - _y) * (a.y - _y) <= tmp_r * tmp_r;
}
bool CPolygon::ifShapeIn(const CRectangle& rec) const{
if (!m_ptr)
return false;
//获取中心点
POINT CPoint = getCentrePoint();
//框选矩形的左上和右下均不在点云圆中且圆心在矩形中
return !ifPointIn(rec.getPoint(1)) && !ifPointIn(rec.getPoint(2)) && rec.ifPointIn(CPoint);
}
void CPolygon::draw(COLORREF line_color) const{
setlinecolor(line_color);
polygon(m_ptr, m_num);
}
void CPolygon::drawColor(COLORREF line_color, COLORREF fill_color)const {
setlinecolor(line_color);
setfillcolor(fill_color);
fillpolygon(m_ptr, m_num);
}
CPolygon* CPolygon::copy()const {
return new CPolygon (*this);
}
CPolygon& CPolygon::move(int _x, int _y) {
for (int i = 0; i < m_num; ++i) {
m_ptr[i].x += _x;
m_ptr[i].y += _y;
}
return *this;
}
void CPolygon::setPoint(int i, const POINT& p) {
if (i <= m_num && i >= 1) {
m_ptr[i - 1] = p;
}
else
assert("你这操作数有问题啊");
}
void CPolygon::setPoints(POINT* ptr, int num) {
if (m_ptr) {
delete[] m_ptr;
m_num = 0;
}
m_ptr = ptr;
m_num = num;
}
POINT CPolygon::getPoint(int i) const {
if (i < 1 || i > m_num) {
assert("越界,此点不存在");
}
return m_ptr[i - 1];
}
int CPolygon::getNum()const {
return m_num;
}
/*********************************
* CCurve类,起始点,终点,两控制点
* 用三次方贝塞尔曲线实现
* id 为 7
*********************************/
CCurve::CCurve():CShape(7) {}
CCurve::CCurve(POINT pts[4]):CShape(7) {
for (int i = 0; i < 4; ++i) {
m_pts[i] = pts[i];
}
}
bool CCurve::ifShapeIn(const CRectangle& rec) const {
return rec.ifPointIn(m_pts[0]) && rec.ifPointIn(m_pts[3]);
}
void CCurve::draw(COLORREF line_color) const {
setlinecolor(line_color);
polybezier(m_pts, 4);
}
void CCurve::drawColor(COLORREF line_color, COLORREF fill_color) const{
draw(line_color);
}
CCurve* CCurve::copy()const {
return new CCurve(*this);
}
CCurve& CCurve::move(int _x, int _y) {
for (int i = 0; i < 4; ++i) {
m_pts[i].x += _x;
m_pts[i].y += _y;
}
return *this;
}
void CCurve::setPoint(int i,POINT& p) {
if (i < 1 || i > 4) {
assert("曲线只由四个点决定");
return;
}
m_pts[i - 1] = p;
}
void CCurve::setPoints(POINT* ptr, int num = 4) {
for (int i = 0; i < 4; ++i) {
m_pts[i] = ptr[i];
}
}
int CCurve::getNum() const {
return 4;
}
POINT CCurve::getPoint(int i) const {
if (i < 1 || i > 4)
assert("越界,该点不存在");
else return m_pts[i - 1];
}
<智能指针.hpp>
智能指针用于管理各矢量图,这里借鉴了别人的写法但有所修改。vector数组中如果用普通指针,那调用erase函数时只会清除指针本身,需要人工释放空间,我懒得淦(其实是这样显得高级,有没有bug我不知道)
其他地方应该也能用。
#pragma once
/*******************************************************
* 智能指针的定义与实现 AutoPtr<T>
* 指向一块由new申请的一块T类型的内存,实现智能识别空指针
* 和自动释放给定大小区域的功能
* 使用方法:AutoPtr<POINT> pPoint = new POINT [20];
* pPoint[10] = {20,15};
* 以下两种方式等价:
* AutoPtr<POINT> p = new POINT({50,20});
* p->x = 10; p->y = 15;
* *p.x = 10; *p.y = 15;
*******************************************************/
template <class T>
class AutoPtr {
//public:
protected:
T* m_ptr; //new申请的内存空间
int* m_num; //空间的用户指针数目
public:
//构造,析构
AutoPtr();
AutoPtr(T* ptr);
AutoPtr(const AutoPtr<T>& tmp);
~AutoPtr();
void del_num();//用户数 -= 1
//重载
AutoPtr<T>& operator=(const AutoPtr<T>& tmp) {
del_num();
m_num = tmp.m_num;
m_ptr = tmp.m_ptr;
*m_num += 1;
return *this;
}
T* operator->() { return m_ptr; }
T& operator*() { return *m_ptr; }
T& operator[](int i) { return *(m_ptr + i); }
const T* operator->() const { return m_ptr; }
const T& operator*() const { return *m_ptr; }
};
template<class T>
AutoPtr<T>::AutoPtr() {
m_ptr = new T;
m_num = new int(1);
}
template<class T>
AutoPtr<T>::AutoPtr(T* ptr) {
if (ptr != nullptr) {
m_ptr = ptr;
m_num = new int(1);
}
else {
m_ptr = nullptr;
m_num = new int(0);
}
}
template<class T>
AutoPtr<T>::AutoPtr(const AutoPtr<T>& tmp) {
m_ptr = tmp.m_ptr;
m_num = tmp.m_num;
*m_num += 1;
}
template<class T>
AutoPtr<T>::~AutoPtr() {
del_num();
}
template<class T>
void AutoPtr<T>::del_num() {
*m_num -= 1;
if (*m_num <= 0) {
//cout << "指针成功析构" << endl;
delete [] m_ptr;
delete [] m_num;
m_ptr = nullptr;
m_num = nullptr;
}
}
<文件操作模块.h>
这里封装了矢量图管理类,里面存放矢量图的智能指针,一些功能和一些信息,这些信息未来可用作拓展功能。同时这里封装了文件流管理类,通过重载的函数实现矢量图管理类和二进制文件的直接交互。
插一句,文件流重载函数原来直接将类存入二进制文件,但读取id时一直是随机数(调试这个操作时vs经常崩溃,不知道是我操作问题还是vs在这块有bug)。后来看了二进制文件后才知道这家伙把虚基列表里那个指针放在第一位(八字节),所以读取id(4字节)时d读的是半个地址,于是乎重写了读取逻辑办成了。
#pragma once
#include<fstream>
#include<Windows.h>
#include<vector>
#include"CShape.h"
#include"智能指针.hpp"
using namespace std;
/*************************************************
* 矢量图管理类:
* 对矢量图进行内部管理
* 类内通过vector<CShape>和vector<COLORRREF>管理
* 分别记录矢量图和颜色(包括线条颜色和填充颜色)
*************************************************/
struct OtherInformation {
COLORREF AllLineColor; //对应每个矢量图线条颜色
COLORREF AllFillColor; //对应每个矢量图填充颜色
bool if_selected; //是否被选中
bool if_fill; //是否填充
};
class GraphicManage {
public:
vector<AutoPtr<CShape>> AllGraphics;//所有矢量图
vector<OtherInformation> Other; //其他信息
AutoPtr<CShape> Clipboard; //剪切板
OtherInformation Clip; //剪切板
public:
int theSelected();//找到被选中元素返回位置
void addGraphic(CShape*, COLORREF, COLORREF ,bool ,bool);//添加元素
void drawAll();//打印所有元素
void dltOne();//删除选中的元素
void dltAll(); //删除所有元素
void ifSelected(CRectangle* p);//判断是否被选中
void chgColor(COLORREF line_color, COLORREF fill_color);
void copy(); //crtl+c实现对选中图形拷贝至剪切板
void paste(); //crtl+v实现将剪切板图形存入管理类中
bool move(int x, int y);//移动元素
void moveAll(int x,int y); //对所有图形进行移动操作,用于拖动
};
//预定义一个全局 矢量图管理类 对象
extern GraphicManage Graphics;
/*******************************
* 新 矢量图文件流 类:
* 对矢量图进行文件管理
* 类内采用fsream进行二进制
* 的文件读写,各类通过id区分
*******************************/
class GraphicStream {
private:
fstream foi; //文件操作流
COLORREF m_color; //矢量图的颜色(线条和填充)
public:
//打开新文件
bool writeopen(const wchar_t* filename, ios_base::openmode mode =
ios_base:: out | ios_base:: binary);
//打开只读文件
bool readopen(const wchar_t* filename, ios_base::openmode mode =
ios_base::in | ios_base::binary);
//关闭
void close();
//友元函数,重载输入输出流简化操作
friend GraphicStream& operator>>(GraphicStream& foi, GraphicManage& Gvect);
friend GraphicStream& operator<<(GraphicStream& Gfout, GraphicManage& Gvect);
private:
//各个类获取文件信息函数,返回指针
CShape* NewCShape(); //CShape类,id = 0
CShape* NewCStraightline(); //CStraight类,id = 1
CShape* NewCTriangle(); //CTriangle类,id = 2
CShape* NewCEllipse(); //CEllipse类,id = 3
CShape* NewCRectangle(); //CRectangle类,id = 4
CShape* NewCCircle(); //CCircle类,id = 5
CShape* NewCPolygon(); //CPolygon类,id = 6
CShape* NewCCurve(); //CCurve类,id = 7
};
//从二进制文件中读入数据,读取到矢量图管理类中
GraphicStream& operator>>(GraphicStream& Gfin, GraphicManage& Gvect);
//从矢量图管理类写入数据到二进制文件中
GraphicStream& operator<<(GraphicStream& Gfout, GraphicManage& Gvect);
<文件管理模块.cpp>
#include"文件管理模块.h"
//预定义一个全局 矢量图管理类 对象
GraphicManage Graphics;
/**************************************
* GraphicManage类内成员函数
**************************************/
//找到被选中元素返回位置
int GraphicManage::theSelected() {
for (int i = 0; i < AllGraphics.size(); ++i) {
if (Other[i].if_selected) {
return i;
}
}
return -1;//未找到
}
//添加元素
void GraphicManage::addGraphic(CShape* ptr, COLORREF c1, COLORREF c2, bool line, bool fill) {
if (ptr == nullptr)
return;
Graphics.AllGraphics.push_back(ptr);
OtherInformation tmp = { c1,c2,line,fill };
Graphics.Other.push_back(tmp);;
}
//打印所有矢量图
void GraphicManage::drawAll() {
for (int i = 0; i < AllGraphics.size(); i++) {
if (Other[i].if_selected) {
setlinestyle(PS_SOLID, 3);
}
if (Other[i].if_fill) {
AllGraphics[i]->drawColor(Other[i].AllLineColor, Other[i].AllFillColor);
}
else {
AllGraphics[i]->draw(Other[i].AllLineColor);
}
setlinestyle(PS_SOLID, 1);
}
}
//删除被选中的矢量图
void GraphicManage::dltOne() {
int i = theSelected();
if (i >= 0) {//存在选中
AllGraphics.erase(AllGraphics.begin() + i);
Other.erase(Other.begin() + i);
}
}
//清空矢量图
void GraphicManage::dltAll() {
AllGraphics.clear();
Other.clear();
}
//框选矢量图
void GraphicManage::ifSelected(CRectangle* p) {
if (p == nullptr)//无效框选
return;
AutoPtr<CRectangle> Aptr = p;
//规定只能选择一个
bool ifselect = false;//是否选中,若未选中取消原有选中
for (int i = 0; i < AllGraphics.size(); ++i) {
if (AllGraphics[i]->ifShapeIn(*p)) {
ifselect = true;
Other[i].if_selected = true;
//将其他置否
for (int k = 0; k < AllGraphics.size(); ++k) {
if (i == k)
continue;
else
Other[k].if_selected = false;
}
break;
}
}
if (ifselect == false) {
for (int i = 0; i < AllGraphics.size(); ++i) {
Other[i].if_selected = false;
}
}
}
//改变填充状态并上色,若非填充则改为填充,多边形填充样式设置
void GraphicManage::chgColor(COLORREF line_color, COLORREF fill_color) {
int i = theSelected();
if (i >= 0) {//若存在选中
Other[i].if_fill = !Other[i].if_fill;
Other[i].AllFillColor = fill_color;
Other[i].AllLineColor = line_color;
}
}
//crtl+c实现对选中图形拷贝至剪切板
void GraphicManage::copy() {
int i = theSelected();
if (i >= 0) {//若存在选中
Clipboard = AllGraphics[i]->copy();
Clip = Other[i];
Clip.if_selected = false;
}
}
//crtl+v实现将剪切板图形存入管理类中
void GraphicManage::paste() {
//使新图形偏移一点以区分原图形
Clipboard->move(50, 50);
AllGraphics.push_back(Clipboard->copy());
Other.push_back(Clip);
}
//移动选中元素
bool GraphicManage::move(int x,int y) {
int i = theSelected();
if (i >= 0) {//若存在选中
AllGraphics[i]->move(x, y);
return true;
}
return false;
}
//移动所有元素
void GraphicManage::moveAll(int x, int y) {
for (int i = 0; i < AllGraphics.size(); ++i) {
AllGraphics[i]->move(x, y);
}
}
/**************************************
* GraphicStream类内成员函数
**************************************/
//关闭文件操作
void GraphicStream::close() {
if(foi.is_open())
foi.close();
}
/**************************************
* 打开文件操作,创建新文件
**************************************/
bool GraphicStream::writeopen(const wchar_t* filename, ios_base::openmode mode) {
foi.open(filename, mode);
if (foi.is_open() == false) {
wstring sentence = L"无法找到文件: ";
sentence += filename;
MessageBox(NULL, sentence.c_str(), _T("文件打开失败"), MB_OK);
return false;
}
foi.seekp(0, ios::beg);
foi.seekg(0, ios::beg);
return true;
}
/**************************************
* 打开文件操作,只读文件
**************************************/
bool GraphicStream::readopen(const wchar_t* filename, ios_base::openmode mode) {
foi.open(filename, mode);
if (foi.is_open() == false) {
wstring sentence = L"无法找到文件: ";
sentence += filename;
MessageBox(NULL, sentence.c_str(), _T("文件打开失败"), MB_OK);
return false;
}
foi.seekp(0, ios::beg);
foi.seekg(0, ios::beg);
return true;
}
/**************************************
* CShape* NewCShape();
**************************************/
CShape* GraphicStream::NewCShape() {
return new CShape();
}
/**************************************
* CShape* NewCSraightline();
**************************************/
CShape* GraphicStream::NewCStraightline() {
POINT p1, p2;
foi.read((char*)(&p1), sizeof(POINT));
foi.read((char*)(&p2), sizeof(POINT));
return new CStraightline(p1,p2);
}
/**************************************
* CShape* NewCTriangle();
**************************************/
CShape* GraphicStream::NewCTriangle() {
POINT pts[3];
foi.read((char*)(pts), 3 * sizeof(POINT));
return new CTriangle(pts[0], pts[1], pts[2]);
}
/**************************************
* CShape* NewCEllipse();
**************************************/
CShape* GraphicStream::NewCEllipse() {
POINT p1, p2;
foi.read((char*)(&p1), sizeof(POINT));
foi.read((char*)(&p2), sizeof(POINT));
return new CEllipse(p1, p2);
}
/**************************************
* CShape* NewCRectangle();
**************************************/
CShape* GraphicStream::NewCRectangle() {
POINT p1, p2;
foi.read((char*)(&p1), sizeof(POINT));
foi.read((char*)(&p2), sizeof(POINT));
return new CRectangle(p1, p2);
}
/**************************************
* CShape* NewCCircle();
**************************************/
CShape* GraphicStream::NewCCircle() {
POINT ptCen;
int r;
foi.read((char*)(&ptCen), sizeof(POINT));
foi.read((char*)(&r), sizeof(int));
return new CCircle(ptCen, r);
}
/**************************************
* CShape* NewCPolygon();
**************************************/
CShape* GraphicStream::NewCPolygon() {
//先获取点的个数m_num
int m_num;
foi.read((char*)(&m_num), sizeof(int));
POINT* pts = new POINT [m_num];
//循环读取点,存入POINT*中,此时m_id为点个数
for (int i = 0; i < m_num; ++i) {
foi.read((char*)(pts+i), sizeof(POINT));
}
return new CPolygon(pts, m_num);
}
/**************************************
* CShape* NewCCurve();
**************************************/
CShape* GraphicStream::NewCCurve() {
POINT pts[4];
for (int i = 0; i < 4; ++i) {
foi.read((char*)(pts+i), sizeof(POINT));
}
return new CCurve(pts);
}
/**************************************
* 输入流重载友元函数
* 从二进制文件中读入数据,
* 读取到矢量图管理类中
**************************************/
GraphicStream& operator>>(GraphicStream& Gfin, GraphicManage& Gvect) {
//循环读入id进行判断
int m_id;
while (Gfin.foi.read((char*)(&m_id), sizeof(int))) {
//读取各类并存入管理类中
switch (m_id) {
case 0://创建CShape类并读取
Gvect.AllGraphics.push_back(Gfin.NewCShape());
break;
case 1://创建CStraight类并获取
Gvect.AllGraphics.push_back(Gfin.NewCStraightline());
break;
case 2://创建CTriangle类并获取
Gvect.AllGraphics.push_back(Gfin.NewCTriangle());
break;
case 3://创建CElipse类并获取
Gvect.AllGraphics.push_back(Gfin.NewCEllipse());
break;
case 4://创建CRectangle类并获取
Gvect.AllGraphics.push_back(Gfin.NewCRectangle());
break;
case 5://创建CCircle类并获取
Gvect.AllGraphics.push_back(Gfin.NewCCircle());
break;
case 6://创建CPolygon类并获取
Gvect.AllGraphics.push_back(Gfin.NewCPolygon());
break;
case 7://创建CCurve类并获取
Gvect.AllGraphics.push_back(Gfin.NewCCurve());
break;
default://无法创建
MessageBox(NULL, L"未识别的格式", L"文件打开失败", MB_OK | MB_SYSTEMMODAL);
return Gfin;
}
//类读取完后,读取其余信息
OtherInformation tmp;
Gfin.foi.read((char*)(&tmp), sizeof(OtherInformation));
Gvect.Other.push_back(tmp);
}
return Gfin;
}
/**************************************
* 输出流重载友元函数
* 将信息从矢量图管理类中
* 以二进制形式写入文件
**************************************/
GraphicStream& operator<<(GraphicStream& Gfout, GraphicManage& Gvect) {
POINT point; //用于读取点
int m_id; 类的id,也存点个数和半径
for (int i = 0; i < Gvect.AllGraphics.size(); ++i) {
AutoPtr<CShape> AC_ptr = Gvect.AllGraphics[i];
m_id = AC_ptr->getId();//先写id
Gfout.foi.write((char*)(&m_id), sizeof(int));
switch (m_id) {
case 0://CShape类,只写入id
break;
case 1://CStraightline类,写入id,两点,
for (int k = 1; k <= 2; ++k) {
point = AC_ptr->getPoint(k);
Gfout.foi.write((char*)(&point), sizeof(POINT));
}
break;
case 2://CTriangle类,写入id,三点
for (int k = 1; k <= 3; ++k) {
point = AC_ptr->getPoint(k);
Gfout.foi.write((char*)(&point), sizeof(POINT));
}
break;
case 3://CEllipse类,写id,两点
for (int k = 1; k <= 2; ++k) {
point = AC_ptr->getPoint(k);
Gfout.foi.write((char*)(&point), sizeof(POINT));
}
break;
case 4://CRectangle类,写id,两点
for (int k = 1; k <= 2; ++k) {
point = AC_ptr->getPoint(k);
Gfout.foi.write((char*)(&point), sizeof(POINT));
}
break;
case 5://CCircle类,写id,圆心,半径
point = AC_ptr->getPoint(1);
Gfout.foi.write((char*)(&point), sizeof(POINT));
m_id = AC_ptr->getLength(1);//暂存半径
Gfout.foi.write((char*)(&m_id), sizeof(int));
break;
case 6: //CPolygon类,写id,点个数,各点坐标
//先读取m_num点的个数
m_id = AC_ptr->getNum();
Gfout.foi.write((char*)(&m_id), sizeof(int));
//再写入各个点
for (int k = 1; k <= m_id; ++k) {
point = AC_ptr->getPoint(k);
Gfout.foi.write((char*)(&point), sizeof(POINT));
}
break;
case 7://CCurve类,写id,四个点
for (int k = 1; k <= 4; ++k) {
point = AC_ptr->getPoint(k);
Gfout.foi.write((char*)(&point), sizeof(POINT));
}
break;
default://不认识,不写
break;
}
//写入OtherInformation
Gfout.foi.write((char*)(&Gvect.Other[i]), sizeof(OtherInformation));
}
return Gfout;
}
<鼠标操作模块.h>
定义了各操作数用于界面逻辑判断。创建动画类实现了创建图形时的实时动画,同时通过判断鼠标操作完成对新矢量图的空间申请并返回。
#pragma once
#include <iostream>
#include <vector>
#include <graphics.h>
#include "CShape.h"
using namespace std;
/***************************
* 选中图形的枚举类
* 通过全局变量OPERATOR
* 进行使用
***************************/
enum MouseOperator {
//创建类
c_Rectangle, c_Triangle, c_Straight,
c_Circle, c_Ellipse, c_Polygon,
c_Curve, c_Select,//框选
//文件操作
c_Openfile, c_Savefile,
//颜色操作
c_Fillcolor, c_Linecolor, c_Color,
//退出程序
c_Exit,
//无操作
c_Menu = 66
};
/***************************
* 创建动画类:
* 每个图形在创建时都有动画
* 封装在创建动画类内
***************************/
typedef bool ifDrawFinish;
class CreateAnimations {
private:
int _x1, _y1, _x2, _y2; //可能用到的坐标
int num; //计数
bool if_click; //鼠标是否点击
CShape* ret; //返回的类指针
ExMessage msg; //存储信息
IMAGE background; //存储背景
IMAGE background_backup;//背景备份,esc退出恢复原状用
vector<POINT> Ptmp; //画多边形用,存储点集信息
public:
CShape* operator()();
private:
inline void update();//更新图像
CShape* select();//选择绘制并返回矢量图
//以下函数返回是否完成绘制,注意完成返回是
ifDrawFinish draw_straight();
ifDrawFinish draw_triangle();
ifDrawFinish draw_rectangle();
ifDrawFinish draw_circle();
ifDrawFinish draw_ellipse();
ifDrawFinish draw_polygon();
ifDrawFinish draw_curve();
//框选,实际上是矩形,但使用时用虚线,用后恢复实线
ifDrawFinish draw_select();
};
extern CreateAnimations createSP; //预定义创建动画类对象
extern MouseOperator OPERATOR; //操作的全局变量
extern ExMessage ANY_MSG; //全局消息结构体变量,用于界面操作
<鼠标操作模块.cpp>
#include"鼠标操作模块.h"
MouseOperator OPERATOR; //运用枚举来对操作编号
CreateAnimations createSP; //预定义创建动画类对象
ExMessage ANY_MSG; //全局消息结构体变量
/***************************
* 每个图形在创建时都有动画
* 封装在创建动画类内
* select函数返回并创建一个
* 选中的类的指针
* 6.25更新日志:改为仿函数
***************************/
CShape* CreateAnimations::operator()() {
return select();
}
CShape* CreateAnimations::select() {
//初始化操作若干
{
Ptmp.clear(); //清空点
num = 0; //计数变量归零
if_click = false; //鼠标记作未点击
ret = nullptr; //返回指针初始化为空
update(); //记录背景
background_backup = background; //背景备份
}
ifDrawFinish flag = false; //当鼠标抬起完成绘制时为true
msg = ANY_MSG; //先传递之前的信息过渡
while (!flag){
switch (OPERATOR) {
case c_Menu://未选中
return ret;
case c_Straight://直线
flag = draw_straight(); break;
case c_Triangle://三角形
flag = draw_triangle(); break;
case c_Rectangle://矩形,实线,返回矩形
flag = draw_rectangle();break;
case c_Circle://圆形
flag = draw_circle(); break;
case c_Ellipse://椭圆
flag = draw_ellipse(); break;
case c_Polygon://多边形
flag = draw_polygon(); break;
case c_Curve://曲线
flag = draw_curve(); break;
case c_Select://框选,虚线,改为返回矩形
setlinecolor(BLACK);
flag = draw_select(); break;
setlinecolor(ANY_LINE_COLOR);
default:
flag = true;
break;
}
msg = getmessage(); //更新现有信息
FlushBatchDraw();
}
return ret;
}
/***************************
* 在类外更新背景时用
* 适用于矢量图复制和删除
* 打印之后使用
* 以便于保存信息
***************************/
//6.24更新日志:好像外部不需要这个函数,暂时保留在public中
//更新:已经在private中了
void CreateAnimations::update(){
getimage(&background, 0, 0, getwidth(), getheight());
}
/*****************************************
* 左键按下选中该点,
* (若左键点下)移动时绘制
* 左键松开结束选点
* 移动过程中按esc退出
*****************************************/
ifDrawFinish CreateAnimations::draw_straight(){
switch (msg.message) {
case WM_LBUTTONDOWN:
if (!if_click) {//记录状态和第一个点坐标
if_click = true;
_x1 = msg.x;
_y1 = msg.y;
}
break;
case WM_LBUTTONUP://抬起后,完成绘制,返回true和ret
if (if_click) {
if_click = false;
ret = new CStraightline({_x1,_y1}, {msg.x,msg.y});
ret->draw(ANY_LINE_COLOR);
return true;
}
break;
case WM_MOUSEMOVE://点击后,抬起前,画跟随鼠标的直线
if (if_click) {
cleardevice();
putimage(0, 0, &background);
line(_x1, _y1, msg.x, msg.y);
}
break;
case WM_KEYDOWN://键盘信息
if (msg.vkcode == VK_ESCAPE) {//esc键退出
ret = nullptr; if_click = false;
cleardevice();
putimage(0, 0, &background_backup);
Sleep(100);//给时间松开左键
return true;
}
break;
}
return false;
}
/*****************************************
* 左键按下选中该点,
* (左键松开)移动时绘制
* 选中三个点结束
* 移动过程中按esc退出
*****************************************/
ifDrawFinish CreateAnimations::draw_triangle() {
bool flag = false;
putimage(0, 0, &background);//因为这有多个点,要实时更新,输出背景
switch (msg.message) {
case WM_LBUTTONDOWN://点击选中该点,三次结束
if (!if_click) {
if_click = true; ++num;
if (num == 1) {//一个点,只记录坐标
_x1 = msg.x; _y1 = msg.y;
}
else if (num == 2) {//两个点,画线并保存截图
_x2 = msg.x; _y2 = msg.y;
line(_x1, _y1, _x2, _y2);
FlushBatchDraw();
update(); //getimage(&background, 0, 0, getwidth(), getheight());
}
else if (num == 3) {//三个点,头尾相连,返回
if_click = false;
line(_x1, _y1, msg.x, msg.y);
line(_x2, _y2, msg.x, msg.y);
FlushBatchDraw();
ret = new CTriangle({ _x1,_y1 }, { _x2,_y2 }, { msg.x,msg.y });
return true;
}
break;
}
case WM_LBUTTONUP://左键抬起,标记恢复
if_click = false; break;
case WM_MOUSEMOVE://移动时,画跟随鼠标的直线
if (num == 0) {
break;
}
else if (num == 1) {
cleardevice();
putimage(0, 0, &background);
line(_x1, _y1, msg.x, msg.y);
}
else if (num == 2) {
cleardevice();
putimage(0, 0, &background);
line(_x2, _y2, msg.x, msg.y);
}
break;
case WM_KEYDOWN://按esc退出
if (msg.vkcode == VK_ESCAPE) {
putimage(0, 0, &background_backup);
ret = nullptr;
Sleep(100);
return true;
}
break;
}
return false;
}
/*****************************************
* 左键按下选中该点,
* (左键按下)移动时绘制
* 左键抬起结束
* 移动过程中按esc退出
*****************************************/
ifDrawFinish CreateAnimations::draw_rectangle() {
switch (msg.message) {
case WM_LBUTTONDOWN:
if (!if_click) {//记录状态和第一个点坐标
if_click = true;
_x1 = msg.x;
_y1 = msg.y;
}
break;
case WM_LBUTTONUP://抬起后,完成绘制,返回true和ret
if (if_click) {
if_click = false;
ret = new CRectangle({ _x1,_y1 }, { msg.x,msg.y });
ret->draw(ANY_LINE_COLOR);
return true;
}
break;
case WM_MOUSEMOVE://点击后,抬起前,画跟随鼠标的直线
if (if_click) {
cleardevice();
putimage(0, 0, &background);
rectangle(_x1, _y1, msg.x, msg.y);
}
break;
case WM_KEYDOWN://键盘信息
if (msg.vkcode == VK_ESCAPE) {//esc键退出
ret = nullptr; if_click = false;
cleardevice();
putimage(0, 0, &background);
Sleep(100);//给时间松开左键
return true;
}
break;
}
return false;
}
/*****************************************
* 左键按下选中该点,(直径d = msg.x - _x1)
* (左键按下)移动时绘制
* 左键抬起结束
* 移动过程中按esc退出
*****************************************/
ifDrawFinish CreateAnimations::draw_circle() {
switch (msg.message) {
case WM_LBUTTONDOWN:
if (!if_click) {//记录状态和第一个点坐标
if_click = true;
_x1 = msg.x;
_y1 = msg.y;
}
break;
case WM_LBUTTONUP://抬起后,完成绘制,返回true和ret
if (if_click) {
if_click = false;
int m = (msg.x - _x1) / 2;
POINT pr = { _x1 + m,_y1 + m };
ret = new CCircle(pr, abs(m));
ret->draw(ANY_LINE_COLOR);
return true;
}
break;
case WM_MOUSEMOVE://点击后,抬起前,画跟随鼠标的圆
if (if_click) {
int m = (msg.x - _x1) / 2;
POINT pr = { _x1 + m,_y1 + m };
cleardevice();
putimage(0, 0, &background);
circle(pr.x, pr.y, abs(m));
}
break;
case WM_KEYDOWN://键盘信息
if (msg.vkcode == VK_ESCAPE) {//esc键退出
ret = nullptr; if_click = false;
cleardevice();
putimage(0, 0, &background_backup);
Sleep(100);//给时间松开左键
return true;
}
break;
}
return false;
}
/*****************************************
* 左键按下选中该点,
* (左键按下)移动时绘制
* 左键抬起结束
* 移动过程中按esc退出
*****************************************/
ifDrawFinish CreateAnimations::draw_ellipse() {
switch (msg.message) {
case WM_LBUTTONDOWN:
if (!if_click) {//记录状态和第一个点坐标
if_click = true;
_x1 = msg.x;
_y1 = msg.y;
}
break;
case WM_LBUTTONUP://抬起后,完成绘制,返回true和ret
if (if_click) {
if_click = false;
ret = new CEllipse({ _x1,_y1 }, { msg.x,msg.y });
ret->draw(ANY_LINE_COLOR);
return true;
}
break;
case WM_MOUSEMOVE://点击后,抬起前,画跟随鼠标的直线
if (if_click) {
cleardevice();
putimage(0, 0, &background);
ellipse(_x1, _y1, msg.x, msg.y);
}
break;
case WM_KEYDOWN://键盘信息
if (msg.vkcode == VK_ESCAPE) {//esc键退出
ret = nullptr; if_click = false;
cleardevice();
putimage(0, 0, &background_backup);
Sleep(100);//给时间松开左键
return true;
}
break;
}
return false;
}
/*****************************************
* 左键按下选中该点,
* (左键松开)移动时绘制
* 选中n个点后按右键结束
* 移动过程中按esc退出
*****************************************/
ifDrawFinish CreateAnimations::draw_polygon() {
switch (msg.message) {
case WM_KEYDOWN:
if (msg.vkcode == VK_ESCAPE) {//按esc退出返回空
putimage(0, 0, &background_backup);
ret = nullptr;
Sleep(100);
return true;
}
break;
case WM_LBUTTONDOWN: //左键按下固定点
if (!if_click) {
if_click = true;
Ptmp.push_back({ msg.x,msg.y }); //记录目前点
if (num != 0) { //此时num为之前记录点的数目,连接上一个点和鼠标所在点
cleardevice();
putimage(0, 0, &background);
line(Ptmp[num - 1].x, Ptmp[num - 1].y, Ptmp[num].x, Ptmp[num].y);
FlushBatchDraw();
update(); //更新背景
}
++num;
}
break;
case WM_LBUTTONUP:
if_click = false;
break;
case WM_RBUTTONDOWN: {
if (num == 0) { //没有点返回空
ret = nullptr;
return true;
}
cleardevice();
putimage(0, 0, &background);
//首尾相连
line(Ptmp[num - 1].x, Ptmp[num - 1].y, Ptmp[0].x, Ptmp[0].y);
POINT* pts = new POINT[num];
for (size_t i = 0; i < num; ++i) {
pts[i] = Ptmp[i];
}
Ptmp.clear();
ret = new CPolygon(pts, num);
return true;
}
case WM_MOUSEMOVE:
if (num == 0)
break;
cleardevice();
putimage(0, 0, &background);
line(Ptmp[num - 1].x, Ptmp[num - 1].y, msg.x, msg.y);
break;
}
return false;
}
/*****************************************
* 左键按下选中该点,
* (左键按下)移动时绘制
* (左键抬起)连接首尾画直线
* (左键再一次点击和移动时)画曲线
* (左键再一次点击和移动时)画二次曲线
* 移动过程中按esc退出
*****************************************/
ifDrawFinish CreateAnimations::draw_curve() {
switch (msg.message) {
case WM_LBUTTONDOWN:
if (num == 4) {
POINT pts[4];
pts[0] = Ptmp[0]; pts[3] = Ptmp[1];
pts[1] = Ptmp[2]; pts[2] = Ptmp[3];
Ptmp.clear();
ret = new CCurve(pts);
return true;
}//改为一般数组
if (!if_click) {//记录状态
if_click = true;
Ptmp.push_back({ msg.x,msg.y });//记录目前点
++num;
}
break;
case WM_LBUTTONUP://抬起后,完成绘制,返回true和ret
if_click = false;
break;
case WM_MOUSEMOVE://点击后,抬起前,画跟随鼠标的直线
if (num == 1){
cleardevice();
putimage(0, 0, &background);
POINT pts_1[] = { Ptmp[0],Ptmp[0],{msg.x,msg.y} ,{msg.x,msg.y} };
polybezier(pts_1, 4);
break;
}
if (num == 2){
cleardevice();
putimage(0, 0, &background);
POINT pts_2[] = { Ptmp[0],{msg.x,msg.y},Ptmp[1],Ptmp[1] };
polybezier(pts_2, 4);
break;
}
if (num == 3){
cleardevice();
putimage(0, 0, &background);
POINT pts_3[] = { Ptmp[0],Ptmp[2],{msg.x,msg.y},Ptmp[1] };
polybezier(pts_3, 4);
break;
}
case WM_KEYDOWN://键盘信息
if (msg.vkcode == VK_ESCAPE) {//esc键退出
putimage(0, 0, &background);
ret = nullptr;
Sleep(100);
return true;
}
break;
case WM_RBUTTONDOWN:
if (num == 1){
cleardevice();
putimage(0, 0, &background);
Ptmp.push_back({ msg.x,msg.y });
POINT pts_3[] = { Ptmp[0],Ptmp[0], Ptmp[1],Ptmp[1] };
polybezier(pts_3, 4);
ret = new CCurve(pts_3);
return true;
}
if (num == 2)
{
cleardevice();
putimage(0, 0, &background);
Ptmp.push_back({ msg.x,msg.y });
POINT pts_4[] = { Ptmp[0],Ptmp[2], Ptmp[1],Ptmp[1] };
polybezier(pts_4, 4);
ret = new CCurve(pts_4);
return true;
}
}
return false;
}
/*****************************************
* 左键按下选中该点,
* (左键按下)移动时绘制虚线
* (左键抬起)结束选取
* 清空虚线 返回矩形
* 移动过程中按esc退出
*****************************************/
ifDrawFinish CreateAnimations::draw_select() {
switch (msg.message) {
case WM_KEYDOWN:
if (msg.vkcode == VK_ESCAPE) {//按esc退出返回空
setlinestyle(PS_SOLID); //恢复实线
putimage(0, 0, &background_backup);
ret = nullptr;
Sleep(100);
return true;
}
break;
case WM_LBUTTONDOWN://左键按下,记录初始位置坐标
if (!if_click) {
_x1 = msg.x;
_y1 = msg.y;
if_click = true;
}
break;
case WM_MOUSEMOVE://如果左键点击后,检测鼠标移动,画框
if (if_click) {
cleardevice();
putimage(0, 0, &background);
setlinestyle(PS_DASH);
rectangle(_x1, _y1, msg.x, msg.y);
}
break;
case WM_LBUTTONUP://检测到鼠标抬起,选中并清除
cleardevice();
putimage(0, 0, &background_backup);
setlinestyle(PS_SOLID); //恢复实线
ret = new CRectangle({ _x1,_y1 }, { msg.x,msg.y });
if_click = false;
return true;
}
return false;
}
<界面逻辑模块.h>
窗口维护,对鼠标进行一些逻辑判断并跳转至对应功能。因为界面模块与管理模块的衔接对不上号,这块改了好久。
#pragma once
#include <iostream>
#include <conio.h>
#include <vector>
#include <Windows.h>
#include "CShape.h"
#include "界面外观模块.h"
#include "智能指针.hpp"
#include "文件管理模块.h"
#include "取色模块.h"
class ManageMent {
private:
IMAGE m_bk;
//菜单按钮容器(装指针)
vector<AutoPtr<PushButton>>menu_btns;
public:
ManageMent();
//启动管理类
void run();
//按钮初始化
void initButton();
//若选中按钮则修改操作数,
//将其他选中状态置否,返回是否选中
bool ButtonClick(MouseOperator& _op);
//打印所有按钮
void drawAllButton();
//打开文件
bool openFILE();
//保存文件
bool saveFILE();
//改变填充颜色
void chgFillColor();
//改变线条颜色
void chgLineColor();
};
<界面逻辑模块.cpp>
ctrl键有bug,读取文件之前可以用ctrl+c等,读取后就不能用了,妈妈生的。索性直接删了ctrl,直接用c、v、x键进行复制,粘贴,剪切。撤销、缩放和旋转没时间做了。
#include "界面逻辑模块.h"
using namespace std;
/*********************************
* 构造函数实现初始化背景加载
*********************************/
ManageMent::ManageMent(){
//主界面初始化
menu_btns.push_back(new PushButton(c_Fillcolor,L"填充颜色"));
menu_btns.push_back(new PushButton(c_Openfile,L"打开"));
menu_btns.push_back(new PushButton(c_Rectangle,L"矩形"));
menu_btns.push_back(new PushButton(c_Triangle,L"三角"));
menu_btns.push_back(new PushButton(c_Straight,L"直线"));
menu_btns.push_back(new PushButton(c_Circle,L"圆形"));
menu_btns.push_back(new PushButton(c_Ellipse,L"椭圆"));
menu_btns.push_back(new PushButton(c_Linecolor,L"线条颜色"));
menu_btns.push_back(new PushButton(c_Savefile,L"保存"));
menu_btns.push_back(new PushButton(c_Polygon,L"多边形"));
menu_btns.push_back(new PushButton(c_Curve,L"曲线"));
menu_btns.push_back(new PushButton(c_Color,L"上色"));
menu_btns.push_back(new PushButton(c_Select,L"框选"));
menu_btns.push_back(new PushButton(c_Exit,L"退出"));
}
/*********************************
* 按钮的界面初始化
*********************************/
void ManageMent::initButton() {
for (int i = 0; i < menu_btns.size(); i++) {
//设置按钮大小
int dx = (getwidth()) / 12;
int dy = (getheight()) / 13;
menu_btns[i]->setFixedSize(dx, dy);
//设置按钮位置
int _x, _y;
if (i <= 6) {
_x = 5 * dx + i * dx;
_y = 0;
}
else if (i <= 13) {
_x = 5 * dx + (i - 7) * dx;
_y = dy;
}
menu_btns[i]->setLocation(_x, _y);
}
}
/*********************************
* 窗口控制台的实现
* 逻辑操作,程序主体框架
*********************************/
void ManageMent::run() {
Window graphics_window(1120, 700,NULL);
setpolyfillmode(WINDING);
setbkcolor(0xFFFED3);
//按钮初始化
initButton();
graphics_window.setWindowTitle(L"简易矢量图生成器");
//三块裁剪区,分别是绘图区,坐标区和按钮区
//graphics_window.setCutWindow();
//graphics_window.setCutWindow();
//graphics_window.setCutWindow();
BeginBatchDraw();
//按钮改变标志
bool ButtonChange;
//移动速度是否重置
int speed = 0;
int MoveSpeedFlag = 0;//等于0时重置速度
//鼠标操作的枚举值默认值
OPERATOR = c_Menu;
while (true) {
ANY_MSG = getmessage();
//设置矢量图移动速度
if (MoveSpeedFlag == 0) {
speed = 0;
}
else {
--MoveSpeedFlag;
}
if (speed >= 30) {
speed = 30;
}
//按钮改变标志
ButtonChange = false;
switch (ANY_MSG.message) {
//键盘操作有:
case WM_KEYDOWN: {
switch (ANY_MSG.vkcode) {
//按ESC退出操作,返回主界面
case VK_ESCAPE: {
int i;//取消选中
if ((i = Graphics.theSelected()) >= 0) {
Graphics.Other[i].if_selected = false;
}
OPERATOR = c_Menu;
break;
}
//back键按下,删除选中矢量图
case VK_BACK: {
Graphics.dltOne();
break;
}
//移动
case VK_UP: {
MoveSpeedFlag = 2;
++speed;
if (!Graphics.move(0, -speed / 3)) {
Graphics.moveAll(0, speed);
}
break;
}
case VK_DOWN: {
MoveSpeedFlag = 2;
++speed;
if (!Graphics.move(0, speed / 3)) {
Graphics.moveAll(0, -speed);
}
break;
}
case VK_LEFT: {
MoveSpeedFlag = 2;
++speed;
if (!Graphics.move(-speed / 3, 0)) {
Graphics.moveAll(speed, 0);
}
break;
}
case VK_RIGHT: {
MoveSpeedFlag = 2;
++speed;
if (!Graphics.move(speed / 3, 0)) {
Graphics.moveAll(-speed, 0);
}
break;
}
//C复制
case 0x43: {
Graphics.copy();
break;
}
//V粘贴
case 0x56: {
Graphics.paste();
break;
}
//X剪切
case 0x58: {
Graphics.copy();
Graphics.dltOne();
break;
}
//L清屏
case 0x4C: {
Graphics.dltAll();
break;
}
}
break;
}
//鼠标操作有
case WM_LBUTTONDOWN:
//鼠标左键按下的几种情况:
//1. 绘图区内,判断选中图形,调用select(),否则
//2. 按钮区内,若选中按钮则更改选中图形/颜色
//绘图区
if (ANY_MSG.x <= getwidth() && ANY_MSG.x >= 0 &&
ANY_MSG.y <= getheight() && ANY_MSG.y >= getheight() / 7)
{
//如果是创建类操作
if (OPERATOR >= c_Rectangle && OPERATOR <= c_Curve) {
Graphics.addGraphic(createSP(), ANY_LINE_COLOR, ANY_FILL_COLOR, false, false);
}
//如果是框选操作
else if (OPERATOR == c_Select) {
Graphics.ifSelected((CRectangle*)createSP());
}
//其余的视为空操作
break;
}
//按钮区
else {
ButtonChange = ButtonClick(OPERATOR);
}
//选中按钮则进入defaul
if (ButtonChange == false) {
break;
}
default:
switch (OPERATOR) {
case c_Openfile: //打开 - 读方式
openFILE();
OPERATOR = c_Menu;//完成后回到等待操作
break;
case c_Savefile: //保存 - 写方式
saveFILE();
OPERATOR = c_Menu;//完成后回到等待操作
break;
case c_Fillcolor: //选填充色
chgFillColor();
OPERATOR = c_Menu;
break;
case c_Linecolor: //选线条色
chgLineColor();
OPERATOR = c_Menu;
break;
case c_Color: //填色
Graphics.chgColor(ANY_LINE_COLOR, ANY_FILL_COLOR);
OPERATOR = c_Select;//完成后回到框选操作
break;
case c_Exit: {//直接退出
int retmsg = MessageBox(NULL, _T("是否保存"), _T("退出程序"), MB_YESNOCANCEL | MB_APPLMODAL | MB_SETFOREGROUND);
if (retmsg == IDYES) {
saveFILE();
exit(0);
}
else if (retmsg == IDNO) {
exit(0);
}
else
OPERATOR = c_Menu;
}
case c_Menu: default:
break;
}
}
cleardevice();
Graphics.drawAll(); //打印所有图像
if(ANY_MSG.message == WM_MOUSEMOVE)
graphics_window.MouseXY(); //打印坐标
drawAllButton(); //打印所有按钮
FlushBatchDraw();
}
EndBatchDraw();
};
//选中按钮则修改操作数,将其他选中状态置否,返回是否修改
bool ManageMent::ButtonClick(MouseOperator& _op) {
bool flag = false; //是否修改
for (size_t i = 0; i < menu_btns.size(); ++i) {
//选中按钮则修改操作数,将其他选中状态置否
if (menu_btns[i]->isin(ANY_MSG)) {
flag = true;
menu_btns[i]->set_selected(true);
_op = menu_btns[i]->op;
for (size_t k = 0; k < menu_btns.size(); ++k) {
if (k == i)
continue;
menu_btns[k]->set_selected(false);
}
break;
}
}
return flag;
}
//打印所有按钮
void ManageMent::drawAllButton() {
for (int i = 0; i < menu_btns.size(); ++i) {
menu_btns[i]->draw(i);
}
}
//打开文件对话框,第三个参数是保存(true)或打开(false)
wstring fileDialogBox(const wstring& location, const wstring& openWith ,bool flag) {
TCHAR szBuffer[MAX_PATH] = { 0 };
OPENFILENAME ofn = { 0 };
ofn.lStructSize = sizeof(ofn);
ofn.hwndOwner = NULL;
ofn.lpstrFilter = openWith.c_str();//要选择的文件后缀
ofn.lpstrInitialDir = location.c_str();//默认的文件路径
ofn.lpstrFile = szBuffer;//存放文件的缓冲区
ofn.nMaxFile = sizeof(szBuffer) / sizeof(*szBuffer);
ofn.nFilterIndex = 0;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_EXPLORER;//标志如果是多选要加上OFN_ALLOWMULTISELECT
if (!flag) {
BOOL bSel = GetOpenFileName(&ofn);
}
else {
BOOL bsel = GetSaveFileName(&ofn);
}
wstring SZ = szBuffer;
return SZ;
}
//打开文件 - 读
bool ManageMent::openFILE() {
wstring szBuffer = fileDialogBox(_T("./存放矢量图二进制文件"), _T("*.bin"), false);
GraphicStream Gss;
if (Gss.readopen(szBuffer.c_str()) == false) {
return false;
}
Graphics.AllGraphics.clear();
Graphics.Other.clear();
Gss >> Graphics;
Gss.close();
return true;
}
//保存文件 - 写
bool ManageMent::saveFILE() {
wstring szBuffer = fileDialogBox(_T("./存放矢量图二进制文件"), _T("*.bin"), true);
GraphicStream Gss;
if (Gss.writeopen(szBuffer.c_str()) == false) {
return false;
}
Gss << Graphics;
Gss.close();
return true;
}
//改变填充颜色
void ManageMent::chgFillColor(){
getColor(true);
}
//改变线条颜色
void ManageMent::chgLineColor(){
getColor(false);
}
<界面外观模块.h>
主要维护窗口和按钮
#pragma once
#include <iostream>
#include <sstream>
#include <graphics.h>
#include <Windows.h>
#include <easyx.h>
#include <conio.h>
#include "CShape.h"
#include "鼠标操作模块.h"
using namespace std;
/******************************************
* 新Window窗口类,初始化绘图界面
* 白色背景板,透明文字背景
* 融合裁剪区类
******************************************/
class Window {
private:
HWND m_handle; //窗口句柄(HWND)
HRGN rgn; //矩形区域(HRGN)
wstring w; //坐标字串
public:
Window(int w, int h, int flag); //形成窗口
~Window(); //析构,主要是释放矩形区域
//设置窗口标题
void setWindowTitle(const wstring& title);
//设置窗口裁剪区,默认下部五分之四区域
void setCutWindow(int x1 = 0, int y1 = getwidth()/5,
int x2 = getwidth(),int y2 = getheight());
//取消设置的裁剪区
void CancelCutWindow();
//获取绘图区长宽
int width() { return getwidth(); }
int height() { return getheight(); }
//判断是否有按键操作或鼠标操作
bool hasMsg() {
return peekmessage(&ANY_MSG, EX_MOUSE | EX_KEY);
}
//返回按键操作或鼠标操作信息
const ExMessage& getMsg() {
peekmessage(&ANY_MSG, EX_MOUSE | EX_KEY);
return ANY_MSG;
}
//获取实时鼠标坐标,并打印在给定位置
void MouseXY(int x1 = 0, int y1 = getwidth() / 8);
};
/***************************************
* 按钮控件基类 BasicWidget
***************************************/
class BasicWidget {
public:
int m_x, m_y; //左上角坐标
int m_width, m_height; //按钮长宽
void setLocation(int x, int y);//设置按钮坐标
void setFixedSize(int w, int h);//设置按钮长宽
virtual void draw(int x) = 0;//打印实现
};
/***************************************
* 按钮控件派生类 PushButton
***************************************/
class PushButton : public BasicWidget {
public:
const MouseOperator op; //操作数常量
private:
wstring m_text; //按钮内文本
bool select = false; //是否选中
COLORREF normal_c = RGB(232, 232, 236);//未被选中
COLORREF hover_c = RGB(216, 216, 219);//已被选中
public:
PushButton(MouseOperator _op,const wstring& text = L"BUTTON", int x = 0, int y = 0, int w = 100, int h = 30);
void draw(int x) override;//override覆盖虚函数表明我们意在覆盖父类的虚函数
bool isin(ExMessage& m_msg);//判断是否鼠标在按钮上
bool if_selected() const;//判断按钮是否被选中
void set_selected(bool m_flag);//设置选中flag
void setNormalColor(COLORREF c);//设置未选中颜色
void setHoverColor(COLORREF c);//设置选中颜色
};
<界面外观模块.cpp>
#include "界面外观模块.h"
/******************************************
* 新Window窗口类,初始化绘图界面
* 白色背景板,透明文字背景
* 初始化绘图区
******************************************/
//窗口构造函数
Window::Window(int w, int h, int flag) {
m_handle = initgraph(w, h, flag);
rgn = NULL;
setbkcolor(WHITE);
cleardevice();
setbkmode(TRANSPARENT);//设置文字背景为透明
}
//析构,主要是释放裁剪区域
Window::~Window() {
// 不再使用 rgn,清理 rgn 占用的系统资源
DeleteObject(rgn);
CancelCutWindow();
closegraph();
}
//设置窗口标题
void Window::setWindowTitle(const wstring& title) {
SetWindowText(m_handle, title.c_str());
}
//设置裁剪区
void Window::setCutWindow(int x1,int y1,int x2, int y2) {
// 创建一个矩形区域
HRGN tmp = CreateRectRgn(x1, y1, x2, y2);
CombineRgn(rgn, tmp, rgn, RGN_OR);
// 将该矩形区域设置为裁剪区
setcliprgn(rgn);
}
// 取消之前设置的裁剪区
void Window::CancelCutWindow(){
setcliprgn(NULL);
}
//获取鼠标坐标并实时打印在屏幕左上角
void Window::MouseXY(int x, int y) {
//填充和文字颜色
setfillcolor(0xFFFED3);
settextcolor(BLACK);
if (ANY_MSG.x <= getwidth() && ANY_MSG.x >= 0
&& ANY_MSG.y <= getheight() && ANY_MSG.y >= 0)
{
//此处进行int转wstring
wostringstream oss;
oss << "[" << ANY_MSG.x << " " << ANY_MSG.y << "]";
w = oss.str();
solidrectangle(0, 0, getwidth() / 8, getheight() / 8);
outtextxy(0, 0, w.c_str());
}
}
/***************************************
* 按钮基类BasicWidget
* 设置 按钮坐标 和 按钮长宽
***************************************/
void BasicWidget::setLocation(int x, int y) {
this->m_x = x;
this->m_y = y;
}
void BasicWidget::setFixedSize(int w, int h) {
this->m_width = w;
this->m_height = h;
}
/***************************************
* 按钮控件派生类 PushButton
***************************************/
//构造函数
PushButton::PushButton(MouseOperator _op,const wstring& text, int x, int y, int w, int h)
:op(_op),m_text(text) {
m_x = x; m_y = y; m_width = w; m_height = h;
}
//打印函数,传入按钮编号
void PushButton::draw(int x) {
//setfillcolor(select ? hover_c : normal_c);
if (select && op == OPERATOR) {
setfillcolor(hover_c);
}
else {
setfillcolor(normal_c);
}
setlinecolor(BLACK);
setlinestyle(PS_SOLID, 1);
fillroundrect(m_x, m_y, m_x + m_width, m_y + m_height, 10, 10);
//一般操作
if (x >= 1 && x <= 6 || x >= 8 && x <= 13) {
//文字居中显示在按钮中间
settextcolor(BLACK);
int tx = m_x + (m_width - textwidth(m_text.c_str())) * 0.5;
int ty = m_y + (m_height - textheight(m_text.c_str())) * 0.5;//text处理基本都用c形式的字符串
outtextxy(tx, ty, m_text.c_str());
}
//圆形调色板
else if (x == 0) { //填充色圆
setlinecolor(BLACK);
setfillcolor(ANY_FILL_COLOR);
int MM_x = m_x + m_width / 2;
int MM_y = m_y + m_height / 2;
solidcircle(MM_x, MM_y, m_height / 3);
}
else if (x == 7) { //线条色圆
setlinecolor(BLACK);
setfillcolor(ANY_LINE_COLOR);
int MM_x = m_x + m_width / 2;
int MM_y = m_y + m_height / 2;
solidcircle(MM_x, MM_y, m_height / 3);
}
//重置
settextcolor(ANY_FONT_COLOR);
setlinecolor(ANY_LINE_COLOR);
setfillcolor(ANY_FILL_COLOR);
}
//点击后,判断鼠标是否在按钮里
bool PushButton::isin(ExMessage& m_msg) {
if (m_msg.x >= m_x && m_msg.x < m_x + m_width && m_msg.y >= m_y && m_msg.y < m_y + m_height) {
return true;
}
return false;
}
//判断按钮是否被选中
bool PushButton::if_selected() const {
return select;
}
//设置选中状态
void PushButton::set_selected(bool m_flag) {
select = m_flag;
}
//设置选中和未选中颜色
void PushButton::setNormalColor(COLORREF c) {
normal_c = c;
}
void PushButton::setHoverColor(COLORREF c) {
hover_c = c;
}
<选色模块.h>
原窗口直接选色(方便省事),采用HSL取色法,三条矩形选色框分表对应色相,亮度和饱和度,成色在圆形框内,如图:
#pragma once
#include <sstream>
#include <easyx.h>
#include <graphics.h>
#include <string>
#include <conio.h>
#include "CShape.h"
#include "鼠标操作模块.h"
using namespace std;
class C_getColor {
private:
ExMessage msg; //消息
COLORREF Hue; //色相
COLORREF color;
//若干对 HSL色参数
float H, S, L; //合成色
float m_H, m_S, m_L;//临时色
private:
//初始化
void hueDiagram();//初始化色相图
void ButtonPush();//设置确定和取消按钮
//画饱和度表
void Saturation(); //饱和度表
//画出亮度表
void Lightness();
//画出合成色
void comColors();
public:
void operator()(bool fill_or_line);//运行函数,传入调用方式
};
extern C_getColor getColor;
<选色模块.cpp>
#include "取色模块.h"
C_getColor getColor;
/*******************************
* 初始化色相图,色相默认红
*******************************/
void C_getColor::hueDiagram() {
//H色相0~360,S饱和度0~1,L亮度0~1
H = 0; S = 1.0f; L = 0.5f;
setlinestyle(PS_SOLID, 2); // 设置线宽为 2
for (int r = 360; r >= 0; r--) {
H++;
setlinecolor(HSLtoRGB(H, S, L));
line(getwidth() / 8 + r, getheight() / 10, getwidth() / 8 + r, 3 * getheight() / 10);
}
Hue = RED; //色相默认为红
}
/*******************************
* 按钮初始化
*******************************/
void C_getColor::ButtonPush() {
setfillcolor(0xDCDCDC);
setlinecolor(BLACK);
settextcolor(BLACK);
//确定按钮
fillroundrect(3 * getwidth() / 4, 3 * getheight() / 5, 7 * getwidth() / 8, 7 * getheight() / 10, 10 , 10);
//确定文本
int bx = 3 * getwidth() / 4 + ( (7 * getwidth() / 8 - 3 * getwidth() / 4 ) - textwidth(L"确定")) / 2;
int by = 3 * getheight() / 5 + ((7 * getheight() / 10 - 3 * getheight() / 5) - textheight(L"确定")) / 2;
outtextxy(bx, by, L"确定");
//取消按钮
fillroundrect(3 * getwidth() / 4, 4 * getheight() / 5, 7 * getwidth() / 8, 9 * getheight() / 10 , 10 , 10);
//取消文本
outtextxy(bx, by + getheight() / 5, L"取消");
}
/*******************************
* 鼠标获取合成色
*******************************/
void C_getColor::operator()(bool fill_or_line) {
cleardevice();
hueDiagram();//初始化色相图
ButtonPush();//初始化按钮
FlushBatchDraw();
bool if_click = false;
while (1) {
msg = getmessage(EX_MOUSE);
switch(msg.message) {
case WM_MOUSEMOVE:
if (!if_click) {
break;
}
case WM_LBUTTONDOWN:
//选择确定按钮
if_click = true;
if (msg.x >= 3 * getwidth() / 4 && msg.x <= 7 * getwidth() / 8
&& msg.y >= 3 * getheight() / 5 && msg.y <= 7 * getheight() / 10)
{
if (fill_or_line)//fill
ANY_FILL_COLOR = HSLtoRGB(H, S, L);
else //line
ANY_LINE_COLOR = HSLtoRGB(H, S, L);
cleardevice();
return;
}
//选择取消按钮
else if (msg.x >= 3 * getwidth() / 4 && msg.x <= 7 * getwidth() / 8
&& msg.y >= 4 * getheight() / 5 && msg.y <= 9 * getheight() / 10)
{
cleardevice();
return;
}
//二者皆不是,判断选择颜色
if (msg.x <= getwidth() / 8 + 360 && msg.x >= getwidth() / 8
&& msg.y <= 3* getheight() / 10 && msg.y >= getheight() / 10)
{//选择色相区1-3
Hue = getpixel(msg.x, msg.y);
RGBtoHSL(Hue, &m_H, &m_S, &m_L);
H = m_H; //确定色相
}
else if (msg.x <= getwidth() / 8 + 360 && msg.x >= getwidth() / 8
&& msg.y <= 6 * getheight() / 10 && msg.y >= 4 * getheight() / 10)
{//选择亮度区4-6
color = getpixel(msg.x, msg.y);
RGBtoHSL(color, &m_H, &m_S, &m_L);
L = m_L;
}
else if (msg.x <= getwidth() / 8 + 360 && msg.x >= getwidth() / 8
&& msg.y <= 9 * getheight() / 10 && msg.y >= 7 * getheight() / 10)
{//选择饱和区7-9
color = getpixel(msg.x, msg.y);
RGBtoHSL(color, &m_H, &m_S, &m_L);
S = m_S;
}
break;
case WM_LBUTTONUP:
if_click = false;
}
Lightness(); //画出亮度表
Saturation(); //画饱和度表
comColors(); //画出合成色
//cleardevice();
FlushBatchDraw();
}
}
/**********************************************************************
* 绘制亮度区
* msg.x <= getwidth() / 8 + 360 && msg.x >= getwidth() / 8
* && msg.y <= 6 * getheight() / 10 && msg.y >= 4 * getheight() / 10
**********************************************************************/
void C_getColor::Lightness() {
m_L = 0.0f; //色相亮度从0到1
m_S = 1.0f; //饱和度拉满
for (int y = 360; y >= 0; y--) {
m_L += 0.0028f;
if (m_L > 1.0) m_L = 0.9999999999f;
setlinecolor(HSLtoRGB(H, m_S, m_L));
line(getwidth() / 8 + y, 4 * getheight() / 10,
getwidth() / 8 + y, 6 * getheight() / 10);
}
}
/**********************************************************************
* 绘制饱和度区
* msg.x <= getwidth() / 8 + 360 && msg.x >= getwidth() / 8
* && msg.y <= 9 * getheight() / 10 && msg.y >= 7 * getheight() / 10
**********************************************************************/
void C_getColor::Saturation() {
m_S = 0.0f;//饱和度从0到1
m_L = 0.5f;//亮度适中
for (int y = 360; y >= 0; y--) {
m_S += 0.0028f;
if (m_S > 1.0) m_S = 1.0;
setlinecolor(HSLtoRGB(H, m_S, m_L));
line(getwidth() / 8 + y, 7 * getheight() / 10,
getwidth() / 8 + y, 9 * getheight() / 10);
}
}
/*******************************
* 打印合成色
*******************************/
void C_getColor::comColors() {
setfillcolor(HSLtoRGB(H, S, L));
solidcircle(13 * getwidth() / 16, 3 * getheight() / 10, getheight() / 5);
settextcolor(BLACK);
wostringstream oss;
wchar_t w[20];
swprintf(w, 20 ,L"%06X", HSLtoRGB(H, S, L));
oss << "RGB: " << w;
wstring wstr = oss.str();
wstr += L" ";
setbkmode(OPAQUE);
outtextxy(12.5 * getwidth() / 16, 11 * getheight() / 20, wstr.c_str());
setbkmode(TRANSPARENT);
}
如何运行?
main函数里面这样写就行:
ManageMent m;
m.run();
2.效果展示
左上角实时坐标,右上是功能按钮。绘图:
框选中后会加粗标记,之后可进行上色,移动。若不选中移动则是视角移动(所有矢量图反向移动),对上色图形再次上色则取消填充。
文件操作
复制粘贴剪切不演示了,保存文件也和上面一样。
总结
没啥总结的,这个系统没做完,以后可能会加缩放(矢量图的灵魂),旋转,显示优先级的变化(现在优先级固定为创建顺序)。可以继续优化一些操作,大但毕竟是练手的项目,可能就这样了,以后会学习Qt,OpenGL等更实用的图形库。