话不多说,先上效果
拖动下方的滚动条可以实现立方体沿XYZ轴的旋转
C的图形化库中,easyx.h是比较适合上手的一款。本次教程涉及的内容:
- 简易3D对象的数据结构
- 使用线性代数旋转3D对象
- 3D对象的投影法(不包括近大远小的效果)
- 控件:按钮,滚动条,提示栏的实现
(先叠一个甲)楼主只是一只菜狗大学生,在暑假闲来无事做的小项目。发到网上只是为了方便更多的初学者少走弯路。可能有更好的实现效果,希望可以指出,大家理性讨论😶🌫️
一些建议:
- 注意!请提前配置完成easyx.h的环境。
- 推荐使用VS环境进行编程
- 遇到读不懂的函数,下载easyx官网上的说明文档
- 不懂绕XYZ轴旋转的数学原理?推荐[这篇知乎文章,写的挺好的]
(https://zhuanlan.zhihu.com/p/183973440)
下面是代码以及注释
#include <graphics.h>
#include <math.h>
#include <easyx.h>
#include <stdbool.h>
#include <stdio.h>
#define SIZE 100 // 立方
int SCRW = 800;
int SCRH = 600;
//操控区
struct Bar {
//操控杆。横放
int x;
int y;
int w;
int h;
COLORREF nowcolor;
COLORREF outcolor;
COLORREF incolor;
bool keydown;
};
struct button {//最上方的还原按钮
int x;
int y;
int w;
int h;
COLORREF nowcolor;
COLORREF outcolor;
COLORREF incolor;
};
struct button* createbutton(int x, int y, int w, int h,COLORREF out,COLORREF in) {
struct button* pB = (struct button*)malloc(sizeof(struct button));
pB->x = x;
pB->y = y;
pB->w = w;
pB->h = h;
pB->outcolor = out;
pB->incolor = in;
pB->nowcolor = out;
return pB;
}
bool isInbutton(struct button* pB, ExMessage m) {
if (m.x > pB->x && m.y > pB->y &&
m.x < pB->x + pB->w && m.y < pB->y + pB->h) {
pB->nowcolor = pB->incolor;
return true;
}
pB->nowcolor = pB->outcolor;
return false;
}
void drawbutton(struct button* pB) {
int right = pB->w + pB->x;
int buttom = pB->y + pB->h;
setfillcolor(pB->nowcolor);
solidrectangle(pB->x, pB->y, right, buttom);
settextstyle(20, 0, _T("Arial")); // 设置文本样式
// 设置文本背景色为透明色
setbkmode(TRANSPARENT);
settextcolor(BLACK);
outtextxy(15,15, "RE");
}
struct Bar* createBar(int x, int y, int w, int h, COLORREF outcolor, COLORREF incolor) {
struct Bar* pbar = (struct Bar*)malloc(sizeof(struct Bar));
pbar->x = x;
pbar->y = y;
pbar->w = w;
pbar->h = h;
pbar->outcolor = outcolor;
pbar->incolor = incolor;
pbar->nowcolor = outcolor;
pbar->keydown = false;
return pbar;
}
bool isInBar(struct Bar* pB, ExMessage m) {
if (m.x > pB->x && m.y > pB->y &&
m.x < pB->x + pB->w && m.y < pB->y + pB->h) {
pB->nowcolor = pB->incolor;
return true;
}
pB->nowcolor = pB->outcolor;
return false;
}
void movebar(struct Bar* pB, ExMessage m) {
static int temp = 0;
if (m.message == WM_LBUTTONDOWN) {
pB->keydown = true;
temp = m.x;
}
if (m.message == WM_LBUTTONUP) {
pB->keydown = false;
}
if (pB->keydown && m.message == WM_MOUSEMOVE) {
int deltaX = m.x - temp;
pB->x += deltaX;
temp = m.x; // 更新temp
}
if (pB->x <= 0)
pB->x = 0;
if (pB->x >= 700)
pB->x = 700;
}
void drawBar(struct Bar* pxbar) {
setlinestyle(1, 5);
setlinecolor(BLACK);
line(0, pxbar->y + pxbar->h / 2, SCRW, pxbar->y + pxbar->h / 2);
setfillcolor(pxbar->nowcolor);
solidrectangle(pxbar->x, pxbar->y, pxbar->x + pxbar->w, pxbar->y + pxbar->h);
}
//底层区
double Xangle = 0;
double Yangle = 0;
double Zangle = 0;
double PI = 3.1415926;
double cube[8][3] = {
{-1, -1, -1}, {1, -1, -1}, {1, 1, -1}, {-1, 1, -1},
{-1, -1, 1}, {1, -1, 1}, {1, 1, 1}, {-1, 1, 1}
};
struct Link {
int str;
int end;//一个LINK,表示cube中下标元素的连接
};
struct Link Lincube[12]{
{0,1},{0,3},{1,2},{2,3},
{4,5},{5,6},{6,7},{7,4},
{1,5},{2,6},{3,7},{0,4}
//这里更改数据结构之后,还可以旋转不同的形状
};
void project(double x, double y, int* sx, int* sy) {
*sx = (int)(x * SIZE + SCRW / 2-50);
*sy = (int)(y * SIZE + SCRH / 2 -80);
}
void drawCube(double cube[8][3]) {
int sx, sy, ex, ey;
for (int i = 0; i < 12; i++) {
project(cube[Lincube[i].str][0], cube[Lincube[i].str][1], &sx, &sy);
project(cube[Lincube[i].end][0], cube[Lincube[i].end][1], &ex, &ey);
setlinecolor(BLACK);
line(sx, sy, ex, ey);
}
}
//旋转
//绕X轴旋转
void XrotateCube(double Angle) {
int angle = Angle - Xangle;//这里是变化的角度
double sinA = sin(angle * PI / 180);
double cosA = cos(angle * PI / 180);
for (int i = 0; i < 8; i++) {
double x = cube[i][0], y = cube[i][1], z = cube[i][2];
cube[i][0] = x;
cube[i][1] = y * cosA - z * sinA;
cube[i][2] = sinA * y + cosA * z;
}
}
//绕Y轴旋转
void YrotateCube(double Angle) {
int angle = Angle - Yangle;
double sinA = sin(angle * PI / 180);
double cosA = cos(angle * PI / 180);
for (int i = 0; i < 8; i++) {
double x = cube[i][0], y = cube[i][1], z = cube[i][2];
cube[i][0] = cosA * x - sinA * z;
cube[i][1] = y;
cube[i][2] = sinA * x + cosA * z;
}
}
//绕Z轴旋转
void ZrotateCube(double Angle) {
int angle = Angle - Zangle;
double sinA = sin(angle * PI / 180);
double cosA = cos(angle * PI / 180);
for (int i = 0; i < 8; i++) {
double x = cube[i][0], y = cube[i][1], z = cube[i][2];
cube[i][0] = cosA * x - sinA * y;
cube[i][1] = sinA * x + cosA * y;
cube[i][2] = z;
}
}
void outtextxy_int(int x, int y, double data, char name) {
char text[64] = { 0 };
sprintf_s(text, "Radian parameter of %c axis rotation:%.3f PI", name, data);
outtextxy(x, y, text);
}
void drawParameterTable(struct Bar* pxbar, struct Bar* pybar, struct Bar* pzbar) {
settextcolor(BLACK);
setlinecolor(BLACK);
setlinestyle(3);
rectangle(450, 20, 790, 95);
outtextxy_int(460, 25, (pxbar->x-350)*0.005714, 'x');
outtextxy_int(460, 45, (pybar->x - 350) * 0.005714, 'y');
outtextxy_int(460, 65, (pzbar->x - 350) * 0.005714, 'z');
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
initgraph(SCRW, SCRH);
setbkcolor(WHITE);
cleardevice();
struct Bar* pxbar = createBar(350, 500, 100, 30, RGB(204, 213, 240), RGB(236, 244, 255));
struct Bar* pybar = createBar(350, 535, 100, 30, RGB(204, 213, 240), RGB(236, 244, 255));
struct Bar* pzbar = createBar(350, 570, 100, 30, RGB(204, 213, 240), RGB(236, 244, 255));
struct button* pb = createbutton(0, 0, 50, 50, RGB(204, 213, 240), RGB(236, 244, 255));
ExMessage m;
BeginBatchDraw();
while (1) {
cleardevice();
peekmessage(&m, EM_MOUSE);
drawBar(pxbar);
drawBar(pybar);
drawBar(pzbar);
drawbutton(pb);
drawParameterTable(pxbar, pybar, pzbar);
if (isInBar(pxbar, m)) {
movebar(pxbar, m);
if (pxbar->x != Xangle) {
XrotateCube(pxbar->x);
Xangle = pxbar->x;
}
}
if (isInBar(pybar, m)) {
movebar(pybar, m);
if (pybar->x != Yangle) {
YrotateCube(pybar->x);
Yangle = pybar->x;
}
}
if (isInBar(pzbar, m)) {
movebar(pzbar, m);
if (pzbar->x != Zangle) {
ZrotateCube(pzbar->x);
Zangle = pzbar->x;
}
}
if (isInbutton(pb, m)&& m.message == WM_LBUTTONDOWN) {
pxbar->x = 350;
pybar->x = 350;
pzbar->x = 350;
Xangle = 0;
Yangle = 0;
Zangle = 0;
cube[0][0] = -1;cube[0][1] = -1; cube[0][2] = -1;
cube[1][0] = 1;cube[1][1] = -1; cube[1][2] = -1;
cube[2][0] = 1; cube[2][1] = 1; cube[2][2] = -1;
cube[3][0] = -1; cube[3][1] = 1; cube[3][2] = -1;
cube[4][0] = -1; cube[4][1] = -1; cube[4][2] = 1;
cube[5][0] = 1; cube[5][1] = -1; cube[5][2] = 1;
cube[6][0] = 1; cube[6][1] = 1; cube[6][2] = 1;
cube[7][0] = -1; cube[7][1] = 1; cube[7][2] = 1;
//回正
}
setlinestyle(PS_SOLID,2);
drawCube(cube);
FlushBatchDraw();
}
EndBatchDraw();
closegraph();
return 0;
}