力学:鼠标用弹簧挂着一串小方块
2012-4-27 ~ 2021-9-1
(0)
这个程序模拟了几个小方块用弹簧连接到鼠标上的效果。当鼠标在绘图窗口上移动时,可以看到小方块连续受力的效果。
动态程序就不再抓图了。源代码如下:
///////////////////////////////////////////////////
// 程序名称:力学:鼠标用弹簧挂着一串小方块
// 编译环境:Visual C++ 6.0 / 2010,EasyX_20210730
// 移植作者:yangw80 <yw80@qq.com>
// 发布日期:2012-4-27
//
#include <graphics.h>
#include <math.h>
#include <time.h>
const int WIDTH = 640; // 窗口宽
const int HEIGHT = 480; // 窗口高
const int BOXCOUNT = 10; // 方块的数量
const int BOXSIZE = 10; // 方块的边长
const double DELTAT = 0.01; // 时间粒度
const double SEGLEN = 10; // 一根弹簧的静止长度
const double SPRINGK = 10; // 弹簧的弹力系数
const double MASS = 1; // 质量
const double GRAVITY = 50; // 重力加速度
const double RESISTANCE = 10; // 空气的阻力系数(阻力=速度*阻力系数)
const double STOPVEL = 0.1; // 速度的临界值(低于此值将忽略)
const double STOPACC = 0.1; // 重力加速度的临界值(低于此值将忽略)
const double BOUNCE = 0.75; // 边框的弹力(反弹速度 = 原速度 * BOUNCE)
//////////////////////////////
// 四舍五入
//
int Round(double x)
{
return (int)(x < 0 ? x - 0.5 : x + 0.5);
}
//////////////////////////////
// 浮点坐标
//
struct FPOINT
{
double X, Y;
};
//////////////////////////////
// 方块对象
//
class BOX
{
private:
double oldx, oldy;
public:
double X, Y;
double dx, dy;
// 构造函数
BOX()
{
oldx = X = dx = 0;
oldy = Y = dy = 0;
}
// 设置方块是否可以见
void setvisible(bool visible)
{
if (visible)
rectangle(Round(X), Round(Y), Round(X) + BOXSIZE - 1, Round(Y) + BOXSIZE - 1);
}
// 绘制方块
void draw()
{
rectangle(Round(oldx), Round(oldy), Round(oldx) + BOXSIZE - 1, Round(oldy) + BOXSIZE - 1);
rectangle(Round(X), Round(Y), Round(X) + BOXSIZE - 1, Round(Y) + BOXSIZE - 1);
oldx = X;
oldy = Y;
}
};
//////////////////////////////
// 方块数组
//
BOX g_boxes[BOXCOUNT];
//////////////////////////////
// 计算 g_boxes[i] 和 g_boxes[j] 之间的拉力
//
void springForce(int i, int j, FPOINT* spring)
{
double dx = (g_boxes[i].X - g_boxes[j].X);
double dy = (g_boxes[i].Y - g_boxes[j].Y);
double len = sqrt(dx * dx + dy * dy);
if (len > SEGLEN)
{
double springF = SPRINGK * (len - SEGLEN);
spring->X += (dx / len) * springF;
spring->Y += (dy / len) * springF;
}
}
//////////////////////////////
// 动画过程
//
void Animate()
{
// g_boxes[0] 表示鼠标位置,不绘制方块
for (int i = 1; i < BOXCOUNT; i++)
{
FPOINT spring = { 0, 0 };
// 计算每个方块受前后方块的拉力
springForce(i - 1, i, &spring);
if (i < (BOXCOUNT - 1))
springForce(i + 1, i, &spring);
// 空气阻力
FPOINT resist = { -g_boxes[i].dx * RESISTANCE, -g_boxes[i].dy * RESISTANCE };
// 计算新的加速度
FPOINT accel = { (spring.X + resist.X) / MASS, (spring.Y + resist.Y) / MASS + GRAVITY };
// 计算新的速度
g_boxes[i].dx += (DELTAT * accel.X);
g_boxes[i].dy += (DELTAT * accel.Y);
// 接近静止时使其不再运动
if (fabs(g_boxes[i].dx) < STOPVEL && fabs(g_boxes[i].dy) < STOPVEL &&
fabs(accel.X) < STOPACC && fabs(accel.Y) < STOPACC)
{
g_boxes[i].dx = 0;
g_boxes[i].dy = 0;
}
// 计算移动到的新位置
g_boxes[i].X += g_boxes[i].dx;
g_boxes[i].Y += g_boxes[i].dy;
// 墙壁的反弹(天花板不反弹)
if (g_boxes[i].X > WIDTH - BOXSIZE)
{
g_boxes[i].X = WIDTH - BOXSIZE; if (g_boxes[i].dx > 0) g_boxes[i].dx = BOUNCE * -g_boxes[i].dx;
}
if (g_boxes[i].Y > HEIGHT - BOXSIZE)
{
g_boxes[i].Y = HEIGHT - BOXSIZE; if (g_boxes[i].dy > 0) g_boxes[i].dy = BOUNCE * -g_boxes[i].dy;
}
if (g_boxes[i].X < 0)
{
g_boxes[i].X = 0; if (g_boxes[i].dx < 0) g_boxes[i].dx = BOUNCE * -g_boxes[i].dx;
}
// 在新位置画出方块
g_boxes[i].draw();
}
}
//////////////////////////////
// 精确延时函数(可以精确到 1ms,精度 ±1ms)
// by yangw80<yw80@qq.com>, 2011-5-4
void HpSleep(int ms)
{
static clock_t oldclock = clock(); // 静态变量,记录上一次 tick
oldclock += ms * CLOCKS_PER_SEC / 1000; // 更新 tick
if (clock() > oldclock) // 如果已经超时,无需延时
oldclock = clock();
else
while (clock() < oldclock) // 延时
Sleep(1); // 释放 CPU 控制权,降低 CPU 占用率
}
//////////////////////////////
// 主函数
//
int main()
{
// 初始化绘图环境
initgraph(WIDTH, HEIGHT); // 设置绘图窗口宽高
BeginBatchDraw(); // 开始批量绘图模式
setlinecolor(GREEN); // 设置绘图颜色
setwritemode(R2_XORPEN); // 设置异或绘图模式
// 初始化所有方块
for (int i = 0; i < BOXCOUNT; i++)
{
g_boxes[i].setvisible(i != 0); // 除了第 0 个方块,都设置为可见
g_boxes[i].draw();
}
// 进入主循环
ExMessage m; // 保存鼠标消息的变量
while (true)
{
// 处理鼠标消息
while (peekmessage(&m, EM_MOUSE)) // 获取一条鼠标消息
{
switch (m.message)
{
case WM_MOUSEMOVE:
g_boxes[0].X = m.x; // g_boxes[0] 表示鼠标位置(不绘制)
g_boxes[0].Y = m.y;
break;
case WM_RBUTTONUP: // 按鼠标右键退出程序
return 0;
}
}
// 绘制一帧动画并显示
Animate();
FlushBatchDraw();
// 延时 20 毫秒
HpSleep(20);
}
}
添加评论
取消回复