rotationthree.jsorbit

Units of Three.JS, Calculating Rotation & Orbit Speeds


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.


Solution

  • 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 the rotation of an object by 1 you will be incrementing the rotation of the object by one radian. For example, consider making the following calls in the callback to requestAnimationFrame:

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

    As the above examples show, we can easily convert a value in degrees into a value in radians by multiplying by a constant factor of Math.PI / 180.

    Taking Framerate Into Account

    In your case, you will also need to take into consideration how much time passes with each frame. This is your delta. Think about it like this: What framerate 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:

    clock = new THREE.Clock();
    

    Then, in the callback to requestAnimationFrame, we can use clock to get two values that will be useful for our animation logic:

    time = clock.getElapsedTime(); // seconds since clock was instantiated 
    delta = clock.getDelta();      // seconds since getDelta was last called
    

    The delta value is meant to represent the time between each frame. However, note that this is only true when clock.getDelta is called consistently, exactly once in the same place somewhere within the callback to requestAnimationFrame. If clock.getDelta somehow gets called more than once or is called inconsistently it's going to screw things up.

    Rotating With A Delta Factor

    Now, if your scene doesn't bog down the processor or the GPU, then Three.JS and it's included requestAnimationFrame will try to keep things running at a smooth 60 frames per second. This means that ideally we will have approximately 1/60 = .016666 seconds between each frame. This is your delta value which can be obtained by calling clock.getDelta each frame.

    We can use the delta value to decouple the rate at which we animate objects by multiplying as shown below. In this case, multiplying by delta will allow us to update our rotation at a rate defined in terms of seconds (as opposed to updating the rotation per frame as we did before). Multiplying by the delta value will also allow us to smoothly animate objects at a constant velocity without being effected by any small variations in the framerate from frame to frame and will even maintain that velocity even in the case that the framerate drops below the target 60fps (for example 30FPS, 45FPS, etc).

    So, the examples we considered previously now become:

    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 radians and degrees are not actually units defined in terms of distance/size), then when we we calculate our rotational speed (angular velocity) you'll see that it is going to be a function only of time and is not dependent on the radius as in your code.

    Calculating Rotational Speeds Based On Time

    For example, you don't need the radius of a planet to calculate its angular velocity, instead you can calculate it using only the number of hours in a day or the amount of time it takes for the planet to complete a single revolution (ie. the duration it takes for the planet to rotate 2 * PI radians on it's axis).

    If we assume that the Earth has exactly 24 hours = 24 * 60 * 60 = 86,400 seconds in a day (it doesn't). Then, given that there are 2 * PI radians in a complete revolution (360 deg), we can calculate the Earth's constant angular velocity in radians as:

    radsPerRevolution = 2 * Math.PI;
    secsPerRevolution = 24 * 60 * 60;
    angularVelocity = radsPerRevolution / secsPerRevolution ;  // 0.0000727 rad/sec
    

    The above only needs to be calculated once, outside of the callback to requestAnimationFrame, as the value never changes. You could probably find textbook values that will 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).

    At this point, rotating our mesh with the same angular velocity as Earth would be as simple as updating its rotation every frame by incrementing its value by delta multiplied by the constant angularVelocity. If angularVelocity is defined as above, this can be done by calling the following in the callback to requestAnimationFrame:

    mesh.rotation.x += delta * angularVelocity;
    

    In Conclusion

    I wouldn't worry about making sure you have all of the angular velocities for the planets exactly correct. Instead, a better idea might be to work out what the ratios between each of the angular velocities (of the planets) are and use those. This might work better since it will allow you to speed up or slow down the animation as desired, and as when working with any model (particularly astronomical models) the most important thing is that you keep it to scale, not necessarily, the scale doesn't necessarily have to be 1:1.