question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Unexpected loss of energy in frictionless perfectly elastic setting

See original GitHub issue

I’m using a modified version of the beachBalls example, with no friction, no air friction, and restitution of 1. When I let it run in the demo window, the balls eventually stop bouncing and come to a stop. I would expect them to keep bouncing indefinitely.

(function() {

    var World = Matter.World,
        Bodies = Matter.Bodies,
        Composites = Matter.Composites;

    Example.frictionlessBeachBalls = function(demo) {

        var engine = demo.engine,
            world = engine.world;

        // need random initialization
        var stack = Composites.stack(0, 100, 3, 1, 20, 0, function(x, y) {
            return Bodies.circle(x, y, 75, { restitution: 1, friction: 0, frictionAir: 0, frictionStatic: 0 });
        });
        World.add(world, stack);
    };

})();

I also tried setting the restitution of the world boundaries to 1 (the code below is modified from the Demo.reset, and that did not help.

        World.add(world, [
            Bodies.rectangle(400, -offset, 800.5 + 2 * offset, 50.5, { isStatic: true, restitution: 1 }),
            Bodies.rectangle(400, 600 + offset, 800.5 + 2 * offset, 50.5, { isStatic: true, restitution: 1 }),
            Bodies.rectangle(800 + offset, 300, 50.5, 600.5 + 2 * offset, { isStatic: true, restitution: 1 }),
            Bodies.rectangle(-offset, 300, 50.5, 600.5 + 2 * offset, { isStatic: true, restitution: 1 })
        ]);

I thought this might be an approximation issue when computing the effects of collisions and set high numbers for positionIterations, velocityIterations, and constraintIterations.

        demo.engine.positionIterations = 1000
        demo.engine.velocityIterations = 1000
        demo.engine.constraintIterations = 1000

This did not help either.

Is this loss of energy due to numerical approximation errors, or can this energy loss be explicitly controlled? At the moment beyond adjusting restitution and friction I am unable to find ways to make energy conservation perfect.

Issue Analytics

  • State:open
  • Created 7 years ago
  • Reactions:5
  • Comments:24 (8 by maintainers)

github_iconTop GitHub Comments

3reactions
liabrucommented, May 24, 2021

@karimshalapy thanks for sharing!

This is still on my list of improvements I’d like to make (quite a long list though!)

For now my suggestions are the following as discussed earlier in the thread:

  • prevent friction body.friction = 0 and body.frictionAir = 0
  • prevent rotation body.inertia = Infinity
  • increase or decrease body.slop
  • use a small fixed timestep Engine.update(engine, 1) and call multiple updates per frame
  • use more iterations engine.positionIterations = 20 and engine.velocityIterations = 20
  • manually set a constant velocity on every frame
    Body.setVelocity(body, Vector.mult(Vector.normalise(body.velocity), speed))
2reactions
karimshalapycommented, May 4, 2021

I wanted to do a collision simulation, and a Brownian motion simulation but the energy loss in a fully elastic world is recognizable and the system becomes idle fairly quickly especially when a ball hits a stationary wall. In order to get the desired outcome, I had to implement the physics logic in the “collisionStart” event on the engine.

And here’s what I did to achieve the Brownian motion…

Events.on(engine, "collisionStart", e => {
        e.pairs.forEach(pair => {
            const { bodyA, bodyB } = pair
            if (bodyA.label === "wallH") Body.setVelocity(bodyB, { x: bodyB.velocity.x, y: -bodyB.velocity.y }) //Horizontal wall collision
            else if (bodyB.label === "wallH") Body.setVelocity(bodyA, { x: bodyA.velocity.x, y: -bodyA.velocity.y }) //Horizontal wall collision
            else if (bodyA.label === "wallV") Body.setVelocity(bodyB, { x: -bodyB.velocity.x, y: bodyB.velocity.y }) //Vertical wall collision
            else if (bodyB.label === "wallV") Body.setVelocity(bodyA, { x: -bodyA.velocity.x, y: bodyA.velocity.y }) //Vertical wall collision
            else { //Two balls collision
                const vAXBefore = bodyA.velocity.x;
                const vBXBefore = bodyB.velocity.x;
                const vAYBefore = bodyA.velocity.y;
                const vBYBefore = bodyB.velocity.y;
                const mA = bodyA.mass
                const mB = bodyB.mass
                const { vAFinal: vAXFinal, vBFinal: vBXFinal } = calcElasticCollision(mA, mB, vAXBefore, vBXBefore)
                const { vAFinal: vAYFinal, vBFinal: vBYFinal } = calcElasticCollision(mA, mB, vAYBefore, vBYBefore)
                if (bodyA.label !== "wall") Body.setVelocity(bodyA, { x: vAXFinal, y: vBXFinal })
                if (bodyB.label !== "wall") Body.setVelocity(bodyB, { x: vAYFinal, y: vBYFinal })
            }
        })
    })

Add some walls

Composite.add(world, [
        Bodies.rectangle(-50, canvasHeight / 2, 50, canvasHeight + 200, {
            label: "wallV",
            isStatic: true,
            friction: 0,
            frictionAir: 0,
            frictionStatic: 0,
            restitution: 1,
            render: { visible: false }
        }),
        Bodies.rectangle(canvasWidth + 50, canvasHeight / 2, 50, canvasHeight + 200, {
            label: "wallV",
            isStatic: true,
            friction: 0,
            frictionAir: 0,
            frictionStatic: 0,
            restitution: 1,
            render: { visible: false }
        }),
        Bodies.rectangle(canvasWidth / 2, -50, canvasWidth + 200, 50, {
            label: "wallH",
            isStatic: true,
            friction: 0,
            frictionAir: 0,
            frictionStatic: 0,
            restitution: 1,
            render: { visible: false }
        }),
        Bodies.rectangle(canvasWidth / 2, canvasHeight + 50, canvasWidth + 200, 50, {
            label: "wallH",
            isStatic: true,
            friction: 0,
            frictionAir: 0,
            frictionStatic: 0,
            restitution: 1,
            render: { visible: false }
        }),
    ])

Then I add balls at random locations and set a random initial velocity to each one.

const balls = Array.from({ length: 100 }, () => Bodies.circle(getRandomNumber(5, canvasWidth - 5), getRandomNumber(5, canvasHeight - 5), 5, {
        render: { fillStyle: "blue" },
        frictionAir: 0,
        friction: 0,
        frictionStatic: 0,
        inertia: Infinity,
        restitution: 1,
    }))
 Composite.add(world, balls)
 balls.forEach(ball => Body.setVelocity(ball, { x: getRandomNumber(-3, 3), y: getRandomNumber(-3, 3) }))

And here are the Helper functions:

const getRandomNumber = (min, max) => Math.random() * (max - min) + min
const calcElasticCollision = (mA, mB, vAInitial, vBInitial) => ({
    vAFinal: (((mA - mB) / (mA + mB)) * vAInitial) + (((2 * mB) / (mA + mB)) * vBInitial),
    vBFinal: (((2 * mA) / (mA + mB)) * vAInitial) + (((mB - mA) / (mA + mB)) * vBInitial)
})

The function calcElasticCollision calculates the final velocity after an elastic collision according to the equations: One-dimensional Newtonian For more information search Elastic collisions or check the equations for a brief explanation on the wiki here

Doing this will provide you with a fully elastic collision system that never loses energy, this would work with one-dimensional collision too, but too complicated stuff I have no guarantees it’ll work. Anyway, this is my workaround and I hope this helps someone who needed the same thing I did, and I hope this issue is solved and closed soon enough.

Regards,

Read more comments on GitHub >

github_iconTop Results From Across the Web

8.3 Elastic and Inelastic Collisions - Physics | OpenStax
In an elastic collision, the objects separate after impact and don't lose any of their kinetic energy. Kinetic energy is the energy of...
Read more >
What are elastic and inelastic collisions? - Khan Academy
Both momentum and kinetic energy are conserved quantities in elastic collisions. ... This collision is perfectly elastic because no energy has been lost....
Read more >
Why do colliding blocks compute pi? - 3Blue1Brown
The setup involves two sliding blocks in a perfectly idealized ... and all collisions are perfectly elastic, meaning no energy is lost.
Read more >
Inelastic Collisions
energy does not. • Calculate the percentage of KE which will be lost (converted to other forms of energy) in a perfectly inelastic...
Read more >
4.5: Collisions - Physics LibreTexts
Of the two types of inelastic collisions, the totally inelastic case is ... This shows that the same amount of energy is lost...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found