|

用代码描述自然系统-向量与受力

向量

物理系统中,力与加速度都是矢量,是具有大小和方向的量,这些量在数学中有一个名字:向量

在 p5.js 中,原生支持了向量,通过 createVectorp5.Vector 即可使用向量模块,常用的向量方法有:

  • add(): 计算两个向量的和向量;
  • sub(): 向量相减;
  • mult(): 向量与标量相乘;
  • div(): 向量与标量相减;
  • mag(): 计算向量的长度;
  • setMag(): 设置向量的长度;
  • normalize(): 将向量单位化,单位化后的向量长度为1;
  • limit(): 限制向量的长度;
  • dist(): 计算两个向量的几何距离;
  • angleBetween(): 计算两个向量的夹角;
  • dot(): 向量点乘;
  • random2D() : 生成一个随机二维向量;

向量的详细使用可以参考 p5.js reference 的文档。

力与加速度

根据物理规律可知,一个质点的受力等于其加速度与质量的乘积;反之,一个质点的加速度等于受力与其质量之比。

用公式表示为:
F = ma
也就是说,加速度与受力在效果上是等效的。

我们构造一个单位质量的物体,模拟该物体受到重力,今儿得到加速度后的自由落体效果。由于重力实际上就是万有引力,模拟万有引力有些不方便,所以直接模拟重力加速度。

构造

定义一个 Mover 类:

class Mover {
  constructor(position, mass = 1) {
    this.position = position;
    this.velocity = createVector(0, 0);
    this.mass = mass;
    this.filledColor = color(random(50, 200), random(50, 200), random(50, 200));
  }
}

质点具备一些属性:

  • position,质点在平面的位置,也可理解为相对于原点的位移;
  • velocity,质点的速度,矢量;

当给质点施加受力时,受力转换为加速度,被 velocity 进行累加,代码表现上为:

class Mover {
  // ...
  addForce(force) {
    const a = force.div(this.mass);
    this.velocity.add(force);
  }

  update() {
    this.position.add(this.velocity);
  }
  // ...
}

其中 addForce 方法用于对物体受力,物体将受力转换为加速度,update 方法用于将受力结果最终转换为位移。

绘制

为 Mover 定义 display 方法,用于在二维平面上进行显示:

class Mover {
  // ...
  display() {
    fill(this.filledColor);
    noStroke();
    ellipse(this.position.x, this.position.y, 20);
  }
  // ...
}

上面的代码中,使用 ellipse 方法绘制一个圆,填充随机颜色,圆直径为 20 像素。

完整的 Mover 类代码:

class Mover {
  constructor(position, mass = 1) {
    this.position = position;
    this.velocity = createVector(0, 0);
    this.mass = mass;
    this.diameter = diameter;
    this.filledColor = color(random(50, 200), random(50, 200), random(50, 200));
  }

  addForce(force) {
    const a = force.div(this.mass);
    this.velocity.add(force);
  }

  reverseVelocity(attenuation = 0.65) {
    this.velocity.mult(-1 * attenuation);
  }

  update() {
    this.position.add(this.velocity);
  }

  display() {
    fill(this.filledColor);
    noStroke();
    ellipse(this.position.x, this.position.y, this.diameter);
  }
}

自由落体

构造一个二维平面,在平面上施加一个垂直向下的力,构造出重力加速度,并将 Mover 实例加入到系统中进行受力计算。借助 p5 的能力,实现二维平面内的自由落体如下:

const G = 0.01;
const C = 60;
const PWidth = 500, PHeight = 200;

class Mover {
  constructor(position, mass = 1) {
    this.position = position;
    this.velocity = createVector(0, 0);
    this.mass = mass;
    this.diameter = 20;
    this.filledColor = color(random(50, 200), random(50, 200), random(50, 200));
  }

  setColor(filledColor) {
    this.filledColor = filledColor || color(random(100));
  }

  addForce(force) {
    const a = force.div(this.mass);
    this.velocity.add(force);
  }

  update() {
    this.position.add(this.velocity);
    if (this.position.y >= PHeight - this.diameter / 2) {
      this.position.y = PHeight - this.diameter / 2;
    }
  }

  display() {
    fill(this.filledColor);
    noStroke();
    ellipse(this.position.x, this.position.y, this.diameter);
  }
}

function createMovers(count = 5) {
  const list = [];
  for (let i = 0; i < count; i++) {
    const position = createVector(random(PWidth), random(PHeight));
    list.push(new Mover(position));
  }
  return list;
}

var movers = [];
function setup() {
  createCanvas(PWidth, PHeight);
  frameRate(60);
  movers = createMovers(5);
  setInterval(() => {
    movers = [];
    movers = createMovers(5);
  }, 5000);
}

function draw() {
  background(255);
  movers.forEach(mover => {
    mover.addForce(createVector(0, 0.4));
    mover.update();
    mover.display();
  });
}

自由落体效果如下:

反弹效果

假设物体具有一定的弹性,在接触地面碰撞以后,可以被重新弹起。效果如下:

这看起来更符合常识。从物理规律上看,物体反弹后将获得相反的速度,所以为 Mover 增加 reverseVelocity 方法,并设置衰减因子:

class Mover {
  // ...
  reverseVelocity(attenuation = 0.65) {
    this.velocity.mult(-1 * attenuation);
  }
  // ...
}

当更新物体位置时发现物体与地面发生了碰撞,那么就让该物体的速度翻转,并适量衰减,这会更符合现实效果。增加代码如下:

class Mover {
  // ...
    update() {
    this.position.add(this.velocity);
    if (this.position.y >= PHeight - this.diameter / 2) {
      this.position.y = PHeight - this.diameter / 2;
      // 与地面碰撞时反弹,速度衰减到原速度的 80%
      this.reverseVelocity(0.8);
    }
  }
  // ...
}

类似文章