Units of Three.JS, Calculating Rotation & Orbit Speeds

Ben Marshall picture Ben Marshall · Jul 6, 2012 · Viewed 19.7k times · Source

I'm trying to build a to scale model of the solar system. I wanted to see if someone could explain to me how the rotation speed works. Here's the important piece:

objects[index].rotation.y += calculateRotationSpeed(value.radius,value.revolution) * delta;

How does the rotation speed relate to actual time? So if you have a speed of 1, is that a movement of 1 px per millisecond? Or if you have a speed of 0.1, is that less that a px a second?

Basically I'm trying to calculate the correct rotation speed for the planets given their radius and amount of hours in a day. So if you were on earth, it would complete 1 rotation in 24 hours. Here's the function that I wrote that's doing the calculation now:

/* In a day */
function calculateRotationSpeed(radius,hrs,delta) {
    var cir = findCircumference(radius);
    if(delta) {
        var d = delta;
    } else {
        var d = 1;
    }
    var ms = hrs2ms(hrs) * d;
    var pxPerMS = km2px(cir) / ms;
    return pxPerMS;
}

I gave it a try and it still seems to be moving too fast. I also need something similar to calculate orbit speeds.

Answer

Cory Gross picture Cory Gross · Jul 6, 2012

Rotation and Units

Rotation in Three.JS is measured in radians. For those that are completely unfamiliar with radians (a small excerpt from an old paper of mine):

Like the mathematical constant Pi, a radian (roughly 57.3 degrees) is derived from the relationship between a circle's radius (or diameter) and its circumference. One radian is the angle which will always span an arc on the circumference of a circle which is equal in length to the radius of that same circle (true for any circle, regardless of size). Similarly, Pi is the ratio of circumference over diameter, such that the circumference of the unit circle is precisely Pi. Radians and degrees are not actually true units, in fact angles are in general dimensionless (like percentages and fractions, we do not use actual units to describe them).

However, unlike the degree, the radian was not defined arbitrarily, making it the more natural choice in most cases; often times being much easier and much more elegant, clear, and concise than using degrees in mathematical formulae. The Babylonians probably gave us the degree, dividing their circle into 6 equal sections (using the angle of an equilateral triangle). each of these 6 sections were probably further subdivided into 60 equal parts given their sexagesimal (base 60) number system. This would also allow them to use such a system for astronomy because the estimated number of days in a year was much less accurate during their time and was often considered 360.

Basic Rotation in Three.JS

So now, knowing you're working in radians, if you increment using the first of the following statements once in your anim function (callback to requestAnimFrame), you will be incrementing the rotation of mesh on the x-axis by one radian

mesh.rotation.x += 1;                      // Rotates   1 radian  per frame
mesh.rotation.x += Math.PI / 180;          // Rotates   1 degree  per frame
mesh.rotation.x += 45 * Math.PI / 180      // Rotates  45 degrees per frame

As the last two of the above statements show we can use Math.PI / 180 to easily convert a value in degrees into radians before the assignment if we wish to use degrees instead.

Taking Framerate Into Account

In your case, you need to take into consideration how much time passes with each frame. This is your delta. You have to think of it like this: How many FPS are we running at? We'll declare a global clock variable which will store a THREE.Clock object which has an interface to the information we require. We need a global variable we'll call clock (needs to be accessible in other functions, specifically anim):

Within init, create an instance of THREE.Clock; storing it in the variable declared outside init (with a greater scope):

clock = new THREE.Clock();

Then, in your anim function, you'll make two calls that will update two variables associated with clock:

  • time (total elapsed time in milliseconds since the clock was instantiated)
  • delta (time in milliseconds between each frame) in two other global variables:
 time = clock.getElapsedTime();
 delta = clock.getDelta();

Note that delta is meant to return the amount of time between each frame; however, this will be true if and only if clock.getDelta is consistently called within anim/render

  1. Only once each animation/render cycle
  2. In the same place each animation cycle (beginning or end, which one shouldn't matter as far as I know)

The above conditions are a result of the THREE.Clock implementation. getDelta initially returns the amount of time since the clock was instantiated, afterwards the time it returns is simply the time since it was last called). If it somehow gets called mistakenly or inconsistently it's going to screw things up.

Rotating With A Delta Factor

Now if your scene doesn't bog down the processor or GPU, Three.JS and it's included requestAnimationFrame shim will try (working with the available resources) to keep things running at a smooth 60 frames per second. This means ideally we will have approximately 1/60 = .016666 seconds between each frame, this is your delta value which you can read from the clock each frame and use it to normalize your speed based on the framerate by multiplying as shown below. This way you can get a value in terms of seconds regardless of small variations in the framerate which you can multiply each time in order to get a value in terms of seconds.

So, based on what we had at the beginning in your anim function you can use it like so:

mesh.rotation.x += delta * 1;                     // Rotates  1 radian  per second
mesh.rotation.x += delta * Math.PI / 180;         // Rotates  1 degree  per second
mesh.rotation.x += delta * 45 * Math.PI / 180;    // Rotates 45 degrees per second

Rotational Speed and Units

Because our measures of angles, radians and degrees are not actually units then when we look at our units for angular velocity we will see that it is going to function of only time (rather than as a function of distance and time like you have in your code).

Calculating Rotational Speeds Based On Time

As for your specific case, you don't need the radius to calculate the rotational speed (angular velocity), instead you can use the number of hours in a day (the amount of time it takes for a complete revolution, ie. 2 * Math.PI radians of rotation on it's axis). If you have a variable called revolutionTime then you can calculate it like so.

secondsInAnHour = 3600;
rotationalSpeed = (2 * Math.PI) / revolutionTime;

If you assume Earth has 24 hours = 24 * 60 * 60 = 86,400 in a day (it doesn't). Then we will get rotationalSpeed = 2 * PI / 86,400, or roughly 0.0000727 radians per second. You should be able to find textbook values which may be more accurate than this (taking into account a more accurate measurement than our 24 hour flat figure for the amount of time it takes for Earth to complete a revolution.

Considering Your Case Particularly

However, I wouldn't worry about making sure you have all of the angular velocities for the planets exactly correct. Instead, a better idea would be to work out what the ratios between each of the angular velocities (of the planets) are and use that. This will work better for a few reasons: You will likely want a faster rotational speed, this will allow you to use whatever rotational speed works well; the important thing, as with any model (especially when it comes to astronomical models), is that you keep it to scale.