|

用代码描述自然系统-构造恒星系统

前面的文章中模拟了万有引力,通过万有引力进一步模拟了自由落体,但自由落体并不是万有引力最好的表现,本篇博文通过万有引力模拟一个恒星系统和一个行星系统,这个系统将完全遵守万有引力法则。

构建恒星系统

由于显示范围有限,需要对几个常数进行修改,修改如下:

// 万有引力常数
const G = 6.67259E-9;
// 地球质量
const M = 5.965E5;
// 太阳质量
const M_SUN = 2E10;

然后定义一个 Planet 类,用来实例化行星。

class Planet {
  constructor(name = '', position, mass = 1, diameter = 20) {
    this.name = name;
    this.position = position;
    this.velocity = createVector(0, 0);
    this.mass = mass;
    this.diameter = diameter;
    this.filledColor = color(random(10, 100));
    this.beforeDisplay = null;
  }

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

  setColor(color) {
    this.filledColor = color;
  }

  setVelocity(vel) {
    this.velocity = vel;
  }

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

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

  setBeforeDisplay(fun) {
    this.beforeDisplay = fun;
  }

  display() {
    this.beforeDisplay && this.beforeDisplay(this);
    fill(this.filledColor);
    noStroke();
    ellipse(this.position.x, this.position.y, this.diameter);
    text(this.name, this.position.x + this.diameter / 2 + 6, this.position.y + 6);
  }
}

接下来为系统中加入恒星,作为太阳,太阳的质量在这个有限系统中最大,将其绘制在系统的中心。

function setup() {
  // ...
  // sun
  const sun = new Planet('Sun', createVector(PWidth / 2, PHeight / 2), M_SUN, 30);
  sun.setColor(color('#ffc107'));
  sun.setBeforeDisplay((sun) => {
    fill(color(250, 250, 0, 100));
    ellipse(sun.position.x, sun.position.y, sun.diameter + 10);
  });
  // ...
}

定义一个简单的行星模拟水星,让水星围绕太阳运转,并给予水星初始速度。

function setup() {
  // ...
  // mercury
  const mercury = new Planet('Mercury', createVector(sun.position.x + 35, PHeight / 2), 1E2, 6);
  mercury.setColor(color('#abb8c3'));
  mercury.setVelocity(createVector(0, 2));
  // ...
}

再分别加入金星和地球:

function setup() {
  // ...
  // Venus
  const venus = new Planet('Venus', createVector(sun.position.x + 80, sun.position.y), 100, 7);
  venus.setColor(color('#f3f4f7'));
  venus.setVelocity(createVector(0, -1.2));
  // earth
  const earth = new Planet('Earth', createVector(sun.position.x + 150, PHeight / 2), M, 10);
  earth.setColor(color('#4395ff'));
  earth.setVelocity(createVector(0, 1));
  // ...
}

将三个行星加入到 planets 数组中统一管理:

function setup() {
  // ...
  planets.push(sun);
  planets.push(mercury);
  planets.push(earth);
  planets.push(venus);
  // ...
}

最后,为整个系统增加万有引力,并计算每个星体的受力,将受力作用在星体上,实现 draw 函数如下:

function draw() {
  background(10);
  planets.forEach(mover => {
    planets.forEach(op => {
      if (op === mover) return;
      const forceDirection = p5.Vector.sub(op.position, mover.position).normalize();
      const dist = p5.Vector.dist(mover.position, op.position);
      const f = op.mass * mover.mass * G / (dist * dist);
      mover.addForce(forceDirection.copy().mult(f));
    });
    mover.update();
    mover.display();
  });
}

最终的显示效果:

加入小行星

为系统随机增加一些小行星,定义 createMiniPlanets 方法:

function createMiniPlanets(count = 5) {
  const list = [];
  for (let i = 0; i < count; i++) {
    const position = createVector(PWidth / 2 + 200 + random(-100, 100), PHeight / 2 + random(-200, 200));
    const asteroid = new Planet(`Asteroid_${i + 1}`, position, 1, 4);
    asteroid.setColor(color(random(50, 255), random(50, 255), random(50, 255)));
    asteroid.setVelocity(createVector(0, 0.8));
    list.push(asteroid);
  }
  return list;
}

将小行星加载到系统中:

function setup() {
  createCanvas(PWidth, PHeight);
  planets = createMiniPlanets(5);
  // ...
}

其他代码无需修改,这是加入小行星后的演示效果:

后续文章简单介绍下粒子系统。

类似文章