liabru / matter-js

a 2D rigid body physics engine for the web ▲● ■
MIT License
16.81k stars 1.96k forks source link

Energy loss, despite air friction 0 #1250

Open miwick opened 12 months ago

miwick commented 12 months ago

First of all, this project is great. Liam, Thank you very much for it!

I would like to use matter.js to teach physics. One of the first examples is a simple pendulum. I have set air friction to 0, but energy is still lost and the pendulum slows down. Is this simply a consequence of the numerical implementation or am I doing something wrong?

Here is an example:

let engine = Matter.Engine.create();
world = engine.world;
let render = Matter.Render.create({element: document.body,engine: engine,options: {width: 600,height: 800,wireframes: false,background: '#f8f9fa'}});
Matter.Render.run(render);
let runner = Matter.Runner.create();
Matter.Runner.run(runner, engine);
var ball = Matter.Bodies.circle(100, 400, 50, {frictionAir: 0});
Matter.Composite.add(world, ball);
Matter.Composite.add(world, Matter.Constraint.create({pointA: { x: 300, y: 100 },bodyB: ball}));
wilcooo commented 11 months ago

Yes, that is indeed a consequence of this implementation of Verlet integration.

One way would be to keep track of energy yourself, and after each engine update slightly modify the velocity of the pendulum to conserve energy.

const g = engine.gravity.y / 3.625;   // empirically determined
const energy = ball.speed**2 / 2      // kinetic
             + g * -ball.position.y;  // + potential

Matter.Events.on(engine, 'afterUpdate', _event => {
  let speed = Math.sqrt( 2 * (energy - g * -ball.position.y) );
  if (speed > 0.01) Matter.Body.setSpeed(ball, speed);
  // (the conditional prevents getting 'stuck' at extremes)
});

To make this work for more complex systems like Newton's cradle you would probably have to determine which bodies you want to adjust to conserve the speed (probably the one that's moving the fastest).