We work with two-dimensional graphics in JavaScript

Good day, friends!

Creating realistic animations of physical processes may seem like a daunting task, but it is not. The algorithms used for this can be very simple and at the same time accurately reproduce such physical phenomena as motion, acceleration and gravity (attraction).

Want to know how these algorithms are implemented in JS?



Examples can be found here .

The source code is here .

Uniform and accelerated motion


Let's start with the movement.

For uniform movement, we can use the following code:

function move(dt) {
    x += vx * dt
    y += vy * dt
}

Here x and y are the coordinates of the object, vx and vy are the speed of the object along the horizontal and vertical axes, respectively, dt (time delta - time delta) is the time between two timer marks, which in JS is equal to two calls to requestAnimationFrame.

For example, if we want to move an object located at a point with coordinates 150, 50 to the southwest, we can do the following (one timer mark or one step):

x = 150 += -1 * 0.1 - > 149.9
y = 50 += 1 * 0.1 - > 50.1

Even movement is boring, so let's give our object an acceleration:

function move(dt) {
    vx += ax * dt
    vy += ay * dt
    x += vx * dt
    y += vy * dt
}

Here ax and ay are the acceleration along the x and y axes, respectively. We use acceleration to change the speed (vx / vy). Now, if we take the previous example and add acceleration along the x axis (to the west), we get the following:

vx = -1 += -1 * 0.1 - > -1.1 // vx += ax * dt
vy = 1 += 0 * 0.1 - > 1 // vy += ay * dt
x = 150 += -1.1 * 0.1 - > 149.89 // x += vx * dt;     -0.01
y = 50 += 1 * 0.1 - > 50.1 // y += vy * dt

Gravity


We learned how to move individual objects. How about learning how to move them relative to each other? This is called gravity or gravity. What do we need to do for this?

Here's what we want to get:



First, let's recall a few equations from high school.

The force applied to the body is calculated by the following formula:
F = m * a ... the force is equal to the mass times acceleration
a = F / m ... from this we can conclude that the force acts on the object with acceleration

If we apply this to two interacting objects, we get the following:



It looks complicated (at least for me), so let's get it right. In this equation, | F | - this is the magnitude of the force, which is the same for both objects, but directed in opposite directions. Objects are represented by masses m_1 and m_2. k is the gravitational constant and r is the distance between the centers of mass of the objects. Still not clear? Here is an illustration:



If we want to do something interesting, we need more than two objects.



In this image, we see two orange objects attracting black with the forces F_1 and F_2, however, we are interested in the resultant force F, which we can calculate as follows:

  • first we calculate the forces F_1 and F_2 using the previous formula:
  • then translate everything into vectors:

Great, we have all the necessary calculations. How do we translate this into code? I will not bore you with the intermediate steps and I will immediately give the finished code with comments. If you need more information, you can write to me, I will answer all your questions.

function moveWithGravity(dt, o) { // o -  ,    
    for (let o1 of o) { //   ()   
        o1.fx = 0
        o1.fy = 0
    }

    for (let [i, o1] of o.entries()) { //    
        for (let [j, o2] of o.entries()) {
            if (i < j) { //            
                let dx = o2.x - o1.x //     
                let dy = o2.y - o1.y
                let r = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
                if (r < 1) { //     0
                    r = 1
                }

                //     ; k = 1000
                let f = (1000 * o1.m * o2.m) / Math.pow(r, 2)
                let fx = f * dx / r
                let fy = f * dy / r
                o1.fx += fx //   
                o1.fy += fy
                o2.fx -= fx //      
                o2.fy -= fy
            }
        }
    }

    for (let o1 of o) { //    ...
        let ax = o1.fx / o1.m // 
        let ay = o1.fy / o1.m

        o1.vx += ax * dt // 
        o1.vy += ay * dt

        o1.x += o1.vx * dt // 
        o1.y += o1.vy * dt
    }
}

Clash


Moving bodies sometimes collide. From a collision, either objects are pushed out by others, or some objects bounce off from others. First



, let's talk about pushing: First of all, we need to determine that there was a collision:

class Collision {
    constructor(o1, o2, dx, dy, d) {
        this.o1 = o1
        this.o2 = o2

        this.dx = dx
        this.dy = dy
        this.d = d
    }
}

function checkCollision(o1, o2) {
    let dx = o2.x - o1.x
    let dy = o2.y - o1.y
    let d = Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2))
    if(d < o1.r + o2.r){
        return {
            collisionInfo: new Collision(o1, o2, dx, dy, d),
            collided: true
        }
    }
    return {
        collisionInfo: null,
        collided: false
    }
}

We declare a Collision class that represents two colliding objects. In the checkCollision function, we first calculate the difference between the x and y coordinates of the objects, then we calculate their actual distance d. If the sum of the radii of the objects is less than the distance between them, then there was a collision of these objects - return the Collision object.



Next, we need to determine the direction of the displacement and its magnitude (magnitude):
n_x = d_x / d ... this is the vector
n_y = d_y / d

s = r_1 + r_2 - d ... this is the “magnitude” of the collision (see picture below)



In JS, it may look So:

function resolveCollision(info){ // "info" -   Collision   
    let nx = info.dx / info.d //  
    let ny = info.dy / info.d
    let s = info.o1.r + info.o2.r - info.d //   
    info.o1.x -= nx * s/2 //       
    info.o1.y -= ny * s/2
    info.o2.x += nx * s/2 //      
    info.o2.y += ny * s/2
}

Bounce


The final part of the puzzle is the implementation of the bounce of one object from another in a collision. I will not give all the mathematical calculations, since this will make the article very long and boring, I will limit myself to the fact that I mention the law of conservation of momentum and the law of conservation of energy, which help to arrive at the following magic formula:

k = -2 * ((o2.vx - o1.vx) * nx + (o2.vy - o1.vy) * ny) / (1 / o1.m + 1 / o2.m) ... * Magic *

How can we use magic k? We know in which direction the objects will move, but we do not know how far. This is k. This is how vector (z) is calculated, showing where the objects should move:





The code looks like this:

function resolveCollisionWithBounce(info){
    let nx = info.dx / info.dy
    let ny = info.dy / info.d
    let s = info.o1.r + info.o2.r - info.d
    info.o1.x -= nx * s/2
    info.o1.y -= ny * s/2
    info.o2.x += nx * s/2
    info.o2.y += ny * s/2

    // ...
    let k = -2 ((info.o2.vx - info.o1.vx) * nx + (info.o2.vy - info.o1.vy) * ny) / (1/info.o1.m + 1/info.o2.m)
    info.o1.vx -= k * nx / info.o1.m //   ,   "k"   "s/2"  "m"
    info.o1.vy -= k * ny / info.o1.m
    info.o2.vx += k * nx / info.o2.m
    info.o2.vy += k * ny / info.o2.m
}

Conclusion


The article has many equations, but most of them are very simple. I hope this article has helped you a little to understand how physical phenomena and processes are implemented in JS.

All Articles