振荡
1.概述
本章涉及到角、角速度以及角加速度,正弦函数和余弦函数,它们可以用来制作平滑的波形曲线。有了这些知识,我们就能计算更复杂的力,而这些力往往都涉及角度,比如钟摆的摆动和盒子从斜坡滑下时所受的力。
为了模拟现实世界的各种运动,我们必须要掌握振荡、振幅和频率/周期这些概念。
振荡的公式:float x = 振幅 \* cos(2π * 帧数 / 周期)
2.应用与拓展
(1)弹簧Spring的实现
弹簧的弹力与弹簧的伸长量成正比,弹力是一个向量,除了大小,还有方向。
控制弹簧的最大长度和最小长度。
胡克定律指出了弹力的大小:F=-kx
k:弹性系数,影响弹力的大小。
x:弹簧发生的形变
已知弹性系数,则计算弹力的关键在于求弹簧的形变。
class Spring {
// 位置
PVector anchor;
// 静止长度和弹性系数
float len;
float k = 0.2;
Spring(float x, float y, int l) {
anchor = new PVector(x, y);
len = l;
}
// 计算弹簧弹力
void connect(Bob b) {
// 从弹簧指向bob位置的向量、距离
PVector force = PVector.sub(b.position, anchor);
float d = force.mag();
// 形变
float stretch = d - len;
// 根据虎克定律计算力F = k * stretch
force.normalize();
force.mult(-1 * k * stretch);
b.applyForce(force);
}
// 距离限制
void constrainLength(Bob b, float minlen, float maxlen) {
PVector dir = PVector.sub(b.position, anchor);
float d = dir.mag();
// 距离短
if (d < minlen) {
dir.normalize();
dir.mult(minlen);
//重置位置并停止移动
b.position = PVector.add(anchor, dir);
b.velocity.mult(0);
}
// 距离长
else if (d > maxlen) {
dir.normalize();
dir.mult(maxlen);
// 重置位置并停止移动
b.position = PVector.add(anchor, dir);
b.velocity.mult(0);
}
}
//显示锚点
void display() {
stroke(0);
fill(175);
strokeWeight(2);
rectMode(CENTER);
rect(anchor.x, anchor.y, 10, 10);
}
//显示BoB锚点之间的弹簧
void displayLine(Bob b) {
strokeWeight(2);
stroke(0);
line(b.position.x, b.position.y, anchor.x, anchor.y);
}
}
(2)Bob与鼠标交互
获取鼠标点击位置与Bob的位置差值,拖动鼠标根据此位置偏移量可以得出Bob被拖去的位置。实现用鼠标拉动钟摆,从而触发它的运动,触发弹力变化。
//弹簧连接的小球
class Bob {
PVector position;
PVector velocity;
PVector acceleration;
float mass = 24;
// 模拟摩擦/阻力的任意阻尼
float damping = 0.98;
// 鼠标交互参数
PVector dragOffset;
boolean dragging = false;
Bob(float x, float y) {
position = new PVector(x,y);
velocity = new PVector();
acceleration = new PVector();
dragOffset = new PVector();
}
// 标准欧拉积分
void update() {
velocity.add(acceleration);
velocity.mult(damping);
position.add(velocity);
acceleration.mult(0);
}
//牛顿第二定律计算加速度
void applyForce(PVector force) {
PVector f = force.get();
f.div(mass);
acceleration.add(f);
}
// 显示
void display() {
stroke(0);
strokeWeight(2);
fill(75,80,255);
if (dragging) {
fill(0);
}
ellipse(position.x,position.y,mass*2,mass*2);
}
//鼠标交互
void clicked(int mx, int my) {
float d = dist(mx,my,position.x,position.y);
if (d < mass) {
dragging = true;
dragOffset.x = position.x-mx;
dragOffset.y = position.y-my;
}
}
void stopDragging() {
dragging = false;
}
void drag(int mx, int my) {
if (dragging) {
position.x = mx + dragOffset.x;
position.y = my + dragOffset.y;
}
}
}
(3)摆锤转动
添加摆锤,摆锤每次转动的角度由Bob的速度控制,可以通过摆锤更价值观的看出Bob的速度变化。
rotate()函数实现摆锤的旋转。
//旋转轴,Bob速度影响轴的转动速度
class AngularMotion{
float angle = 0;
float aVelocity = 0;
//调整选装角度
void calangle(PVector velocity)
{
angle += velocity.mag()/10;
}
//显示
void display()
{
pushMatrix();
fill(127);
stroke(0);
translate(width/5, height/4);
rectMode(CENTER);
rotate(angle);
stroke(0);
strokeWeight(2);
fill(0,0,127);
line(-60, 0, 60, 0);
ellipse(60, 0, 16, 16);
ellipse(-60, 0, 16, 16);
popMatrix();
display2();
}
void display2()
{
pushMatrix();
fill(127);
stroke(0);
translate((width/5)*4, height/4);
rectMode(CENTER);
rotate(-angle);
stroke(0);
strokeWeight(2);
fill(127,0,0);
line(-60, 0, 60, 0);
ellipse(60, 0, 16, 16);
ellipse(-60, 0, 16, 16);
popMatrix();
}
}
(4)控制Bob受到的重力及弹力,连接弹簧与Bob,并控制鼠标交互。
Bob bob;
AngularMotion am;
Spring spring;
void setup() {
size(640,360);
spring = new Spring(width/2,20,100);
bob = new Bob(width/2,100);
am=new AngularMotion ();
}
void draw() {
background(255);
// Bob施加重力
PVector gravity = new PVector(0,2);
bob.applyForce(gravity);
// 连接Bob和弹簧并计算弹力
spring.connect(bob);
// 限制距离
spring.constrainLength(bob,30,200);
bob.update();
bob.drag(mouseX,mouseY);
// 锚点bob之间的直线表示弹簧
spring.displayLine(bob);
bob.display();
spring.display();
fill(0);
text("click on bob to drag",10,height-5);
am.calangle(bob.velocity);
am.display();
}
void mousePressed() {
bob.clicked(mouseX,mouseY);
}
void mouseReleased() {
bob.stopDragging();
}
3.效果
左右两个摆锤的旋转速度受Bob的速度影响,正比关系,通过两个摆锤的转动速度可以更加直观地观察Bob在受到弹力过程中的速度变化。