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.
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; }