Spring Forces

To start out, we'll script a spring. Specifically, we'll apply a spring force to every object towards the left side of the screen using this function:

fn apply_spring_forces(ids) {
    let spring_factor = 0.01;
    let damping_factor = 0.999;

    for body in ids {
        draw_spring(vec(0.0, body.get_pos().y), body.get_pos(), body.get_radius() * 2.0, body.get_radius() / 2.0, 10);
        let spring_length = (body.get_pos().x + body.get_vel().x);
        body.add_force(vec(spring_factor * -spring_length, 0.0));
        body.set_vel(body.get_vel() * damping_factor);
    }
}

fn draw_spring(start, end, width, thickness, coils) {
    let r = end - start;
    let incr = r / coils;
    let offs = incr.rotate(3.1415 / 2.0).normalized * width;

    let pos = start;
    for i in 0..coils {
        let p1 = pos + (incr / 3.0) + (offs / 2.0);
        let p2 = pos + (incr * 2.0/3.0) - (offs / 2.0);
        let p3 = pos + incr;
        draw_line(pos, p1, thickness);
        draw_line(p1, p2, thickness);
        draw_line(p2, p3, thickness);
        pos = p3;
    }
}

The damping_factor simulates energy lost by the spring. It is common in computational integration to prevent integration error for growing; it is better for the total energy of the system to increase than decrease.

We can use this function in the update function below:

set_g(0.0);

let update = |ids, bodies| {
    apply_spring_forces(ids);
};

fn apply_spring_forces(ids) {
    ...
}

Run this script and try instantiating some objects. What happens?

Momentum Update

Currently, we're using the engine's built in integration. We want to replicate this behavior with our own integration code. Similarly to the last two labs, we'll use the momentum update equations you learned in class:

p_f = p_0 + f_0 * dt
v_f = p_f / m
x_f = x_0 + v_f * dt

One difference is that the engine doesn't keep track of momentum, so you have to calculate the starting momentum yourself.

Fill in this script:

set_g(0.0);
set_integration(false);

let update = |ids, bodies| {
    apply_spring_forces(ids);

    for id in ids {
        let x_0 = id.get_pos();
        let v_0 = id.get_vel();
        let dt = DT();

        let m = id.get_mass();
        let p_o = ??;

        let p_f = ??;
        let v_f = ??;
        let x_f = ??;

        id.set_pos(x_f);
        id.set_vel(v_f);
        id.set_accel(vec(0.0, 0.0));
    }
};

fn apply_spring_forces(ids) {
    ...
}

After you've completed this script, the simulation's behavior should be almost the same as in the first example. There will be slight differences due to integration error.