参考《自然代码》一书的前五章来描述牛顿随机行为。
平台:processing
第二章主要是模拟力的运用
这里我们参考《自然代码》实现多个大小不一样的圆围绕一个有引力的球,并同时受到浮力的影响。
先确定类的个数,这里我们要创建三个类,一个是吸引别的物体的“吸引者”,第二个是“被吸引者”,第三个是液体类。
被吸引者类(mover):
两个float类型变量:质量(正比于物体大小)、持续的重力
两个PVector类型变量:位置、拖拽的偏移量
两个boolean类型变量:一个是判断物体是否被拖拽、另一个是判断是判断鼠标是否在物体上。
类的构造函数:
吸引者的初始位置在画布正中央,拖拽偏移量初始为0
Attractor()
{
position = new PVector(width/2,height/2);
mass = 20;
G = 1;
dragOffset = new PVector(0.0,0.0);
}
接下来就是类函数:
PVector类型函数,形参为mover类型的实例,就是对“被吸引者”的吸引向量:
引力向量的大小用到物理公式,两个物体之间的引力等于引力常量物体1的质量物体2的质量再除以两者距离的平方。两个物体间的距离直接用mag函数取到力向量的长度。
PVector attract(Mover m)
{
PVector force = PVector.sub(position,m.position); // 计算力的方向
float d = force.mag(); // 物体之间的距离(计算力的向量的长度)
d = constrain(d,5.0,25.0); // 将距离限定在指定范围内
force.normalize(); // 力的向量的归一化(因为大小无所谓这里只需要方向)
float strength = (G * mass * m.mass) / (d * d); // 计算重力的大小 用的是两个物体之间的引力公式
force.mult(strength); // 获取力的向量=方向*大小
return force;
}
显示函数:将吸引者画在画布上
吸引者的颜色有三种,当鼠标不在吸引者的圆范围内时,颜色较浅,当鼠标在吸引者圆范围内但是没按下的时候颜色交深,当鼠标既在范围内又按下拖拽时为黑色。(判断函数接下来会写)
void display()
{
ellipseMode(CENTER);
strokeWeight(4);
stroke(0);
if (dragging)
fill (50);
else if
(rollover) fill(100);
else fill(175,200);
ellipse(position.x,position.y,mass*2,mass*2);
}
鼠标交互函数:
processing 中的鼠标点击事件是mouseClicked函数
判断当前距离与鼠标距离。
void mouseClicked(int mx, int my)
{
float d = dist(mx,my,position.x,position.y); //计算本位置和目标位置的距离
if (d < mass) //这里的mass等于size
{
dragging = true;
dragOffset.x = position.x-mx;
dragOffset.y = position.y-my;
}
}
判断鼠标是否在吸引者范围内:
void hover(int mx, int my)
{
float d = dist(mx,my,position.x,position.y);
if (d < mass)
{
rollover = true;
}
else
{
rollover = false;
}
}
停止拖拽函数(不能一直在拖动):
void stopDragging()
{
dragging = false;
}
拖拽函数:
void drag()
{
if (dragging)
{
position.x = mouseX + dragOffset.x;
position.y = mouseY + dragOffset.y;
}
}
第二个是液体类:
类属性有五个float类型的变量,前四个控制的是液体的正方形区域,最后一个是物体掉入液体中时的拖拽系数。
// 液体是一个正方形区域
float x, y, w, h;
// 拖拽的系数
float c;
构造函数(一 一对应):
Liquid(float x_, float y_, float w_, float h_, float c_)
{
x = x_;
y = y_;
w = w_;
h = h_;
c = c_;
}
判断物体是否在液体中(因为创建液体时有两个坐标了,这里直接进行比较就可以了):
boolean contains(Mover m)
{
PVector l = m.position;
return l.x > x && l.x < x + w && l.y > y && l.y < y + h;
}
就算拖拽的力(PVector类型的函数,因为是计算对mover的力):
拖拽力的大小正比于物体的大小(这里的质量和大小在数值上是相等的)
浮力=液体密度物体排除体积g(这里水的密度为1 ,重力不计)
这里要注意将物体的速度方向调转(数乘-1)
// 计算拖拽的力
PVector drag(Mover m)
{
//拖拽大小是系数*速度的平方
float speed = m.velocity.mag();
float dragMagnitude = m.mass;
// 方向与速度成反比
PVector dragForce = m.velocity.get();
dragForce.mult(-1);
dragForce.normalize();
dragForce.mult(dragMagnitude);
return dragForce;
}
显示函数:
void display()
{
noStroke();
fill(50);
rect(x, y, w, h);
}
这里的mover参考第一章,基本相同。
主函数:
先创建mover数组、吸引者、液体:
Mover[] movers = new Mover[10];
Attractor a;
Liquid liquid;
在setup函数中进行实例化:
void setup()
{
size(800, 800);
// Create liquid object
liquid = new Liquid(0, height/2, width, height/2, 0.1);
for (int i = 0; i < movers.length; i++)
{
movers[i] = new Mover(random(0.1, 2), random(width), random(height));
}
a = new Attractor();
}
在draw函数中对每一个物体都进行被吸引者吸引的引力计算和是否在液体中并进行浮力计算:
void draw()
{
background(255);
liquid.display();
a.display();
a.drag();
a.hover(mouseX, mouseY);
for (int i = 0; i < movers.length; i++)
{
PVector force = a.attract(movers[i]);
if (liquid.contains(movers[i])) {
// Calculate drag force
PVector dragForce = liquid.drag(movers[i]);
// Apply drag force to Mover
movers[i].applyForce(dragForce);
}
movers[i].applyForce(force);
movers[i].update();
movers[i].display();
}
}
最后就是鼠标的按压和弹起函数:
void mousePressed()
{
a.mouseClicked(mouseX, mouseY);
}
void mouseReleased()
{
a.stopDragging();
}
我们先看看不加上液体是什么效果:
加上液体效果: