Revolving Earth

In the following program, Earth orbits Sun once per minute while rotating on its own axis once every 4 seconds. As in our solar system, earth’s axis points in a fixed direction at all times.

Revolving earth

We might be tempted to model the two rotations as a two-level scene graph whose parent node rotates the earth around the sun while its child node rotates the earth on its own axis. However, this design fails because the outer (parent) rotation would not hold the earth’s axis fixed. For example, if the north pole were inclined away from earth, it would remain so inclined throughout its orbit and so would not point in a fixed direction (and the southern hemisphere would enjoy perpetual summer).

Instead of nesting rotations, we will translate earth along its orbital path as time passes while keeping its axial direction fixed, while rotating earth independently on its axis. It is this self-rotating earth that gets passed as the first argument to the following function:

function revolveEarth(earth, orbit=10, rps=0.01, tilt=-23.44) {
    let root = new THREE.Object3D();
    root.add(earth);
    // tilt earth
    root.rotation.z = degreesToRadians(tilt);
    // revolve earth
    root.angle = 0;
    root.rps = rps;
    root.update = makeUpdateRevolution(orbit);
    subject.register(root);
    return root;
}

Regarding the remaining parameters: orbit is the orbital radius (distance from sun); rps is the rate of rotation around the sun; and tilt is earth’s inclination in degrees.

The the scene graph root is an Object3D that stores three properties:

  • root.angle is earth’s current angle of rotation around the sun, a value between 0 and 2π.
  • root.rps is earth’s rate of rotation around the sun (rotations per second).
  • root.update, a function that gets called with each frame of the animation, updates earth’s orbital position over time.

The call to makeUpdateRevolution on line 8 of revolveEarth returns a new update function for translating earth along an orbit of given radius. Throughout, this refers to the root returned by the previous function, with angle and rps properties as discussed. Since the update function translates the root node’s position along a circle of given radius in the xz-plane, root‘s child, earth, moves with it as desired.

function makeUpdateRevolution(radius) {
    function updateRevolution(delta) {
        angle = this.angle;
        let deltaRadians = rpsToRadians(this.rps, delta);
        angle = this.angle + deltaRadians;
        angle %= 2 * Math.PI;
        this.angle = angle;
        this.position.z = radius * Math.cos(angle);
        this.position.x = radius * Math.sin(angle);
    }
    return updateRevolution;
}