main.cpp
#include<stdio.h>
#include"Shape.h"
#include"Button.h"
/*
* 项目内容:绘图工具
* 开发环境:vs2019 + easyx
* 主讲老师:顽石老师
*/
//顺序表来存储所有的形状 Line Rect ...
struct ShapeSet
{
void* shapes[256]; //万能指针数组,所有类型的指针都可以自动转换为void*
int size;
};
#define ShapeSet_Init(set) memset(set.shapes,0,sizeof(set.shapes));set.size = 0
#define ShapeSet_Add(set,pshape) set.shapes[set.size++] = pshape
#define ShapeSet_Pop(set) if(set.size>0){free(set.shapes[set.size-1]);set.size--;}
//快捷遍历顺序表
#define ShapeSet_Foreach(pval,set) \
for(int i = 0,cnt = 0;i < set.size;++i,cnt = 0) \
for(pval = set.shapes[i];cnt < 1;++cnt)
ShapeSet g_shapes;
POINT begPos = { 0,0 }; //鼠标点击的位置
bool isPress = false; //鼠标左键是否按下
int curShapeType = ST_Rect; //当前绘图的形状
COLORREF curLineColor = RED; //当前的线条颜色
Graffiti g_graf; //用于当前操作的涂鸦
void onMouseLButtonDown(ExMessage* msg)
{
//记录开始点击的坐标
begPos = { msg->x,msg->y };
//左键按下了
isPress = true;
if (curShapeType == ST_Graffiti)
{
graf_init(&g_graf, curLineColor);
graf_addPoint(&g_graf, msg->x, msg->y);
}
}
void onMouseLButtonUp(ExMessage* msg)
{
//放开的时候保存线
isPress = false;
//动态申请一个内存,存放形状
if (curShapeType == ST_Line)
{
Line* le = (Line*)calloc(1, sizeof(Line));
line_init(le, begPos.x, begPos.y, msg->x, msg->y, curLineColor);
ShapeSet_Add(g_shapes, le);
}
else if (curShapeType == ST_Rect)
{
Rect* rect = (Rect*)calloc(1, sizeof(Rect));
rect_init(rect, begPos.x, begPos.y, msg->x, msg->y, curLineColor);
ShapeSet_Add(g_shapes, rect);
}
else if (curShapeType == ST_Graffiti)
{
Graffiti* g = (Graffiti*)calloc(1, sizeof(Graffiti));
*g = g_graf; //把刚才绘制的涂鸦,交给申请的内存
ShapeSet_Add(g_shapes, g);
}
}
void onMouseMove(ExMessage* msg)
{
if (isPress)
{
cleardevice();
setlinecolor(curLineColor);
switch (curShapeType)
{
case ST_Line:
line(begPos.x, begPos.y, msg->x, msg->y);
break;
case ST_Rect:
rectangle(begPos.x, begPos.y, msg->x, msg->y);
break;
case ST_Graffiti:
graf_addPoint(&g_graf, msg->x, msg->y);
graf_draw(&g_graf);
break;
}
}
}
void onMouseRButtonDown(ExMessage*msg)
{
printf("onMouseRButtonDown %d\n",g_shapes.size);
ShapeSet_Pop(g_shapes);
cleardevice(); //撤销一个清屏一下
}
int main()
{
initgraph(640, 480,EW_SHOWCONSOLE);
//设置窗口背景颜色
setbkcolor(WHITE);
cleardevice();
ShapeSet_Init(g_shapes);
Button* btn = createButton(0, 0, 150, 30, BT_Rect);
btn_setText(btn, "Maye");
//通过鼠标绘制,获取鼠标的消息
while (true)
{
//双缓冲
BeginBatchDraw();
//绘制所有形状
ShapeSet_Foreach(void* shape, g_shapes)
{
int type = ((Shape*)shape)->type;
switch (type)
{
case ST_Line:
line_draw((Line*)shape);
break;
case ST_Rect:
rect_draw((Rect*)shape);
break;
case ST_Graffiti:
graf_draw((Graffiti*)shape);
break;
}
}
//绘制控件
btn_draw(btn);
EndBatchDraw(); //结束双缓冲
//定义一个消息的结构体
ExMessage msg;
//不断地获取消息
while (peekmessage(&msg, EM_MOUSE))
{
if (btn_isClicked(btn, &msg))
{
curLineColor = RGB(187, 136, 243);
printf("btn_isClicked\n");
}
btn_msgHanding(btn, &msg);
switch (msg.message)
{
case WM_LBUTTONDOWN: //左键按下
onMouseLButtonDown(&msg);
break;
case WM_MOUSEMOVE: //鼠标移动
onMouseMove(&msg);
break;
case WM_LBUTTONUP: //左键释放
onMouseLButtonUp(&msg);
break;
case WM_RBUTTONDOWN:
onMouseRButtonDown(&msg);
break;
case WM_MOUSEWHEEL:
curShapeType = (curShapeType + 1) % ST_Max;
break;
default:
break;
}
}
}
return 0;
}
/*
* 1,选择形状和颜色的菜单(封装菜单)
* 2,保存图形和加载图形(文件操作)
* 3,按钮的处理之回调写法
*/
Shape.h
#ifndef _SHAPE_H_
#define _SHAPE_H_
#include<easyx.h>
//绘制的图形有哪些,枚举
enum ShapeType
{
ST_Line, //线
ST_Rect, //矩形
ST_Ellipse, //圆
ST_Graffiti, //涂鸦
ST_FiveStar, //五角星
ST_Max
};
/* @形状*/
struct Shape
{
int type; //形状类型
COLORREF lineColor; //线条颜色
};
//初始化形状的成员
void shape_init(Shape* shape, int type, COLORREF c);
/* @直线*/
struct Line
{
Shape super; //组合一个形状结构(相当父类)
int x1;
int y1;
int x2;
int y2;
};
void line_init(Line* pline, int x1, int y1, int x2, int y2,COLORREF c);
void line_draw(Line* pline);
/* @矩形*/
struct Rect
{
Line super;
};
void rect_init(Rect* rect, int x1, int y1, int x2, int y2, COLORREF c);
void rect_draw(Rect* rect);
/* @涂鸦*/
struct Graffiti
{
Shape super; //必须是第一个成员
POINT* points; //数组指针
int size; //当前点数
int capacity; //容量
};
void graf_init(Graffiti* graf, COLORREF c);
void graf_draw(Graffiti* graf);
void graf_addPoint(Graffiti* graf, int x, int y);
#endif // ! _SHAPE_H_
Button.h
#pragma once
#include<easyx.h>
enum ButtonType
{
BT_Rect,
BT_Circle
};
struct Button
{
int type; //按钮形状
int x;
int y;
int w;
int h;
COLORREF bkColor; //背景颜色
COLORREF borderColor; //边框颜色
char text[50]; //按钮文本
COLORREF hoverColor; //鼠标悬停颜色
COLORREF curColor; //当前背景颜色
};
//在堆区创建一个按钮
Button* createButton(int x, int y, int w, int h, int type);
//绘制按钮
void btn_draw(Button* btn);
//鼠标是否在按钮上
bool btn_isIn(Button* btn, ExMessage* msg);
//鼠标左键是否按下
bool btn_isClicked(Button* btn, ExMessage* msg);
//设置文本
void btn_setText(Button* btn, const char* text);
//按钮的消息处理函数
void btn_msgHanding(Button*bnt,ExMessage* msg);
Shape.cpp
#include"Shape.h"
#include<stdio.h>
void shape_init(Shape* shape, int type, COLORREF c)
{
shape->type = type;
shape->lineColor = c;
}
void line_init(Line* pline, int x1, int y1, int x2, int y2, COLORREF c)
{
shape_init(&pline->super, ST_Line, c); //初始化父类
pline->x1 = x1;
pline->y1 = y1;
pline->x2 = x2;
pline->y2 = y2;
}
void line_draw(Line* pline)
{
setlinecolor(pline->super.lineColor);
line(pline->x1, pline->y1, pline->x2, pline->y2);
}
void rect_init(Rect* rect, int x1, int y1, int x2, int y2, COLORREF c)
{
line_init(&rect->super, x1, y1, x2, y2, c);
rect->super.super.type = ST_Rect;
}
void rect_draw(Rect* rect)
{
setlinecolor(rect->super.super.lineColor);
rectangle(rect->super.x1, rect->super.y1, rect->super.x2, rect->super.y2);
}
void graf_init(Graffiti* graf, COLORREF c)
{
shape_init(&graf->super, ST_Graffiti, c);
graf->capacity = 500; //开始容量500
graf->size = 0;
graf->points = (POINT*)calloc(graf->capacity, sizeof(POINT));
if (graf->points == NULL)
{
printf("[error %d]memory allocated failed\n", __LINE__);
}
}
void graf_draw(Graffiti* graf)
{
if (graf->points)
{
setlinecolor(graf->super.lineColor);
polyline(graf->points, graf->size);
}
}
void graf_addPoint(Graffiti* graf, int x, int y)
{
//是否能插
if (graf->size < graf->capacity)
{
graf->points[graf->size++] = { x,y };
}
//满了,需要扩容
else if(graf->size == graf->capacity)
{
graf->capacity *= 2; //每次扩容为本身的两倍
POINT* newPoints = (POINT*)realloc(graf->points, graf->capacity);
if (newPoints == NULL)
{
printf("[error %d]扩容失败\n", __LINE__);
}
else
{
graf->points = newPoints;
}
}
}
Button.cpp
#include "Button.h"
Button* createButton(int x, int y, int w, int h, int type)
{
Button* btn = (Button*)calloc(1, sizeof(Button));
if (!btn)
return NULL;
btn->x = x;
btn-> y = y;
btn->w = w;
btn->h = h;
btn->type = type;
btn->bkColor = RGB(230, 231, 232);
btn->borderColor = RGB(50, 124, 46);
memset(btn->text, 0, sizeof(btn->text));
setbkmode(TRANSPARENT);
btn->hoverColor = RGB(85, 85, 85);
btn->curColor = btn->bkColor;
return btn;
}
void btn_draw(Button* btn)
{
setlinecolor(btn->borderColor);
setfillcolor(btn->curColor);
if (btn->type == BT_Circle)
{
fillellipse(btn->x, btn->y, btn->x + btn->w, btn->y + btn->h);
}
else if (btn->type == BT_Rect)
{
fillroundrect(btn->x, btn->y, btn->x + btn->w, btn->y + btn->h,5,5);
}
//绘制文本
if (btn->text[0] != '\0')
{
settextcolor(RED);
int hspace = (btn->w - textwidth(btn->text)) / 2;
int vspace = (btn->h - textheight(btn->text)) / 2;
outtextxy(btn->x + hspace, btn->y+vspace, btn->text);
}
}
bool btn_isIn(Button* btn, ExMessage* msg)
{
if (msg->x > btn->x && msg->x<btn->x + btn->w && msg->y>btn->y && msg->y < btn->y + btn->h)
{
return true;
}
return false;
}
bool btn_isClicked(Button* btn, ExMessage* msg)
{
if (btn_isIn(btn, msg) && msg->message == WM_LBUTTONDOWN)
{
return true;
}
return false;
}
void btn_setText(Button* btn, const char* text)
{
strcpy(btn->text, text);
}
void btn_msgHanding(Button* btn, ExMessage*msg)
{
if (btn_isIn(btn, msg))
{
btn->curColor = btn->hoverColor;
}
else
{
btn->curColor = btn->bkColor;
}
}