- Industrial design and construction

Newton's law of motion

Simulation of particles based on action and reaction

2022-11-08. The simulation was programmed by Ladislav Ides, B.Sc., M.Sc., part of the script by Josh Bradley  https://joshbradley.me/object-collisions-with-canvas

 

Simulation of particles according to the law of momentum P


m= mass
v= speed
u= speed after colision

P= m * v
m1 * v1 +  m2 * v= m1 * u1 +  m2 * u2

 

Learn more about particle acceleration here: https://www.xtream.sk/zakony-vesmiru

 

If there are multiple particles of various sizes in space, their momenta will equalize. The temperature within the substance will equalize. Therefore, larger particles will move slower, and smaller particles will move faster.

The demonstration is set up so that, at the beginning, the larger particles have the highest speed. However, very quickly after collisions, it is evident that the smaller particles accelerate from the larger ones and take part of their momentum. As a result, the speeds of the particles will arrange according to their sizes. Thus, larger particles will, on average, move slower than smaller particles. There are not as many particles as in reality, so there is a significant random factor, meaning that sometimes a particle may move outside the average speed for its size. However, the more particles added to the simulation, the more stable the results would be.

Refresh the page to better observe the acceleration of smaller particles at the beginning.

Aside from the acceleration of particles, it is also possible to observe the emergence of gravity in the simulation, which is caused by the shielding effect of the larger particles. The principle behind the emergence of gravity can be found here. The emergence of gravity is not as visible because there are few particles and the simulation is only in two dimensions, so there is a higher likelihood of particle collisions than in a 3D space. Nonetheless, you can still notice that the larger particles collide with each other and try to stay as close to each other as possible. Of course, random factors sometimes push them apart, but there is a greater likelihood that the larger particles will move toward each other. In reality, gravity is created by much smaller particles, which are far more numerous and move at higher speeds, so turbulence does not occur, and larger particles are more stable. This results in a certain frequency of their contact. The closer we push larger particles together, the more strongly they will also rebound from each other. So everything depends on a certain frequency and impact force. Meanwhile, the walls act as a screen, so larger particles often tend to collide with the walls as well.

The simulation may be inaccurate, and over time the particles may speed up or slow down. This is due to the fact that everything is based on calculations that are not perfectly accurate.

Gravity can be simulated in a similar way, but many more particles need to be simulated to reduce the random impact of larger particles.

The force of gravity, that is, the mutual attraction of particles caused by the differing sizes of the particles, always occurs during collisions between all types of particles. However, its effect depends on the density and the size differences between the various types of particles. It is possible to simulate this and thus create a miniature model of the universe without having to program anything more into the model than the action and reaction of particles. The only problem is that it is mathematically impossible to calculate particle collisions with perfect precision, as the calculations involve rounding values.

When observing the movement of particles, it looks like cellular activity. This suggests that with such a number of combinations over an infinite time, the emergence of cells and therefore life is inevitable.

 

HTML code for display:

<script type="text/javascript">sem vložíte zdrojový kód</script>
  <body onload="draw();">
  <canvas id="canvas" width="600" height="600"></canvas>
  </body>


 

Source code of the simulation:

//pohyb castic simulacia graviacie 600x600
class State {
  constructor(display, actors) {
    this.display = display;
    this.actors = actors;
  }

  update(time) {

    /**
     * provide an update ID to let actors update other actors only once
     * used with collision detection
     */
    const updateId = Math.floor(Math.random() * 1000000);
    const actors = this.actors.map(actor => {
      return actor.update(this, time, updateId);
    });
    return new State(this.display, actors);
  }
}


class Vector {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  add(vector) {
    return new Vector(this.x + vector.x, this.y + vector.y);
  }

  subtract(vector) {
    return new Vector(this.x - vector.x, this.y - vector.y);
  }

  multiply(scalar) {
    return new Vector(this.x * scalar, this.y * scalar);
  }

  dotProduct(vector) {
    return this.x * vector.x + this.y * vector.y;
  }

  get magnitude() {
    return Math.sqrt(this.x ** 2 + this.y ** 2);
  }

  get direction() {
    return Math.atan2(this.x, this.y);
  }
}

 //ctx = document.getElementById('canvas').getContext('2d');

class Canvas {
  constructor(parent = document.body, width = 10, height = 10) {
    this.canvas = document.createElement('canvas');
    this.canvas.width = width;
    this.canvas.height = height;
    parent.appendChild(this.canvas);
    this.ctx =document.getElementById('canvas').getContext('2d');
  }

  sync(state) {
    this.clearDisplay();
    this.drawActors(state.actors);
  }

  clearDisplay() {

    // opacity controls the trail effect set to 1 to remove
    this.ctx.fillStyle = 'rgba(255, 255, 255, .4)';
    this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
    this.ctx.strokeStyle = 'black';
    this.ctx.strokeRect(0, 0, this.canvas.width, this.canvas.height);
  }

  drawActors(actors) {
    for (let actor of actors) {
      if (actor.type === 'circle') {
        this.drawCircle(actor);
      }
    }
  }

  drawCircle(actor) {
    this.ctx.beginPath();
    this.ctx.arc(actor.position.x, actor.position.y, actor.radius, 0, Math.PI * 2);
    this.ctx.closePath();
    this.ctx.fillStyle = actor.color;
    this.ctx.fill();
  }
}

class Ball {
  constructor(config) {
    Object.assign(this,
      {
        id: Math.floor(Math.random() * 1000000),
        type: 'circle',
        position: new Vector(100, 100),
        velocity: new Vector(3, 3),
        radius: 25,
        color: 'blue',
        collisions: [],
      },
      config
    );
  }

  update(state, time, updateId) {

    /**
     * if slice occurs on too many elements, it starts to lag
     * collisions is an array to allow multiple collisions at once
     */
    if (this.collisions.length > 10) {
      this.collisions = this.collisions.slice(this.collisions.length - 3);
    }

    /**
     * this is the most stable solution to avoid overlap
     * but it is slightly inaccurate
     */
    for (let actor of state.actors) {
      if (this === actor || this.collisions.includes(actor.id + updateId)) {
        continue;
      }

      /**
       * check if actors collide in the next frame and update now if they do
       * innaccurate, but it is the easiest solution to the sticky collision bug
       */
      const distance = this.position.add(this.velocity).subtract(actor.position.add(actor.velocity)).magnitude;

      if (distance <= this.radius + actor.radius) {
        const v1 = collisionVector(this, actor);
        const v2 = collisionVector(actor, this);
        this.velocity = v1;
        actor.velocity = v2;
        this.collisions.push(actor.id + updateId);
        actor.collisions.push(this.id + updateId);
      }
    }

    // setting bounds on the canvas prevents balls from overlapping on update
    const upperLimit = new Vector(state.display.canvas.width - this.radius, state.display.canvas.height - this.radius);
    const lowerLimit = new Vector(0 + this.radius, 0 + this.radius);

    //check if hitting left or right of container
    if (this.position.x >= upperLimit.x || this.position.x <= lowerLimit.x) {
     this.velocity = new Vector(-this.velocity.x, this.velocity.y);
    }

    //if (this.position.x >= upperLimit.x) {//moje
    // this.position.x=lowerLimit.x+1;
    //}

    //if (this.position.x <= lowerLimit.x) {//moje
    // this.position.x=upperLimit.x;
    //}

    // check if hitting top or bottom of container
    if (this.position.y >= upperLimit.y || this.position.y <= lowerLimit.y) {
      this.velocity = new Vector(this.velocity.x, -this.velocity.y);
    }


    //if (this.position.y >= upperLimit.y) {//moje
    // this.position.y=lowerLimit.y+1;
    //}

    //if (this.position.y <= lowerLimit.y) {//moje
    // this.position.y=upperLimit.y;
    //}



 

const newX = Math.max(Math.min(this.position.x + this.velocity.x, upperLimit.x), lowerLimit.x);
const newY = Math.max(Math.min(this.position.y + this.velocity.y, upperLimit.y), lowerLimit.y);

return new Ball({
...this,
position: new Vector(newX, newY),
});
}

get area() {
return Math.PI * this.radius ** 2;
}

get sphereArea() {
return 4 * Math.PI * this.radius ** 2;
}
}

// see elastic collision: https://en.wikipedia.org/wiki/Elastic_collision
const collisionVector = (particle1, particle2) => {
return particle1.velocity
.subtract(particle1.position
.subtract(particle2.position)
.multiply(particle1.velocity
.subtract(particle2.velocity)
.dotProduct(particle1.position.subtract(particle2.position))
/ particle1.position.subtract(particle2.position).magnitude ** 2/1.002 // lomeno 1.002 je spomalenie na vyrovnanie chyby výpočtu
)

// add mass to the system
.multiply((2 * particle2.sphereArea) / (particle1.sphereArea + particle2.sphereArea))
);
};

const isMovingTowards = (particle1, particle2) => {
return particle2.position.subtract(particle1.position).dotProduct(particle1.velocity) > 0;
};

const runAnimation = animation => {
let lastTime = null;
const frame = time => {
if (lastTime !== null) {
const timeStep = Math.min(100, time - lastTime) / 1000;

// return false from animation to stop
if (animation(timeStep) === false) {
return;
}
}
lastTime = time;
requestAnimationFrame(frame);
};
requestAnimationFrame(frame);
};

const random = (max = 9, min = 0) => {
return Math.floor(Math.random() * (max - min + 1) + min);
};

const colors = ['red', 'green', 'blue', 'purple', 'orange'];

const collidingBalls = ({ width = 600, height = 600, parent = document.body, count = 10 } = {}) => {
const display = new Canvas(parent, width, height);
const balls = [];
for (let i = 0; i < 10; i++) {
balls.push(new Ball({
radius: 7,
color: colors[1],
position: new Vector(random(width - 10, 10), random(height - 10, 10)),
velocity: new Vector(random(3, -3), random(3, -3)),
}));
}

//velke
for (let i = 0; i < 2; i++) {
balls.push(new Ball({
radius: 30,
color: colors[2],
position: new Vector(random(width - 10, 10), random(height - 10, 10)),
velocity: new Vector(5, 0),
}));
}

for (let i = 0; i < 2; i++) {
balls.push(new Ball({
radius: 30,
color: colors[2],
position: new Vector(random(width - 10, 10), random(height - 10, 10)),
velocity: new Vector(0,5),
}));
}
for (let i = 0; i < 2; i++) {
balls.push(new Ball({
radius: 30,
color: colors[2],
position: new Vector(random(width - 10, 10), random(height - 10, 10)),
velocity: new Vector(0, -5),
}));
}
for (let i = 0; i < 2; i++) {
balls.push(new Ball({
radius: 30,
color: colors[2],
position: new Vector(random(width - 10, 10), random(height - 10, 10)),
velocity: new Vector(-5, 0),
}));
}
//kioniec velke
for (let i = 0; i < 500; i++) {
balls.push(new Ball({
radius: 3,
color: colors[0],
position: new Vector(random(width - 10, 10), random(height - 10, 10)),
velocity: new Vector(random(3, -3), random(3, -3)),
}));
}

let state = new State(display, balls);
runAnimation(time => {
state = state.update(time);
display.sync(state);
});
};

function draw() {
collidingBalls();
}


    


All rights reserved. Copying text and images is only allowed with XTREAM a.s. and link to the source www.xtream.sk