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.
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.
Related
In a Cartesian coordinate system / Euclidian plane, a vehicle travels clockwise around the origin with radius 1 and currently at angle θ = PI/4 (45°), so its heading is 7/4*PI (315°).
To reach its new destination (the origin) it should change course (instantly) to -5/4*PI (-135°).
This angle can be obtained with e.g. atan2 on the components of the vector from position to target (with the target at origin, it's the the inverted position vector)b:
Finding Signed Angle Between Vectors
Using atan2 to find angle between two vectors
Algorithm Improvement: Calculate signed change in heading
However, due to inertia, the vehicle cannot change its course instantly and crosses the X-axis (x=1, y=0, θ=0), where a new calculation results in a course of PI (180°).
The vehicle tries to achieve this new (positive angle) course by a positive turn (counterclockwise, CCW), which takes it back across the X-axis, and so on (it "wiggles" away from the target along the x-axis).
It breaks also if negative angles are "wrapped" into the absolute positive range 0...2PI - example data of the southbound vehicle passing west of the target:
course -0.00477
adjust 6.27840
course 0.00034
adjust 0.00034
The proportional control then basically causes a negative (clockwise) turn, which is the wrong direction.
Do I eliminate this "overflow" somehow (not limit to 0...2PI but map absolute angles to the vehicle's) or should I use a different strategy altogether (which)? 😕
Not limiting the vehicle angle to 0...2PI, i.e. allowing negative and large angles seems to be a promising approach.
E.g. going from 10° to 350°=-10° means a -20° turn.
This determines the heading with the smallest turn angle:
delta = course - heading%fullcircle
if (delta > halfcircle)
delta = delta - fullcircle
if (delta < -halfcircle)
delta = delta + fullcircle
newheading = heading + delta
halfcircle and fullcircle correspond to 180°/PI and 360°/2*PI respectively.
The vehicles heading will "wind up" if it's going circles, but if need be, this could be restored by an occasional modulo operation.
Currently I'm working on a orbit system for a game. I've got it so an object will move along a circle based on a progress value that'll be between 0.0 and 1.0 (0.5 being half way around the circle). I calculate this like this:
float angle = Mathf.Deg2Rad * 360 * t;
float xPos = Mathf.Sin(angle) * xAxis;
float yPos = Mathf.Cos(angle) * yAxis;
return new Vector3(xPos, yPos, 0.0f);
With t simply being deltatime and the xAxis/yAxis variables being the radius of the circle.
What I'm a little stuck on currently though is how I could possibly get the progress around the circle based on a poisition. So if I have an object that hits the bottom of the circle, how do I calculate that to be a progress of 0.5?
First step: Find out the angle of your given position with the y-axis.
Second step: Calculate the fraction of a full circle (360 degs) that your angle has.
First step involves a bit of trigonometry, and there you have to make sure to get the right type of angle based on what quadrant you're in. Second step should be trivial then.
You can check out the atan2 function that's available in many programming languages: https://en.wikipedia.org/wiki/Atan2
It gives the angle between a point (x, y) and the positive x-axis. So then in your case, depending on where your circle starts, you'd then shift that by 90 degrees to get the angle with the positive y-axis. Other than that it should work fine though.
I am writing a simulation in which an object moves in a 2D world towards a target position, stored as vector target(x,y). The object position is stored as position vector pos(x,y) as well. The object contains two more vectors, the desired movement velocity dv(x,y), as well as the current movement velocity cv(x,y). At the start of the simulation both these velocity vectors are initial, i.e. set to (0,0).
When the object should move towards the target position, I calcuate the desired velocity vector dv, normalize it, and scale it by a movement speed value:
dv.set(target).sub(pos).normalize()
dv.scale(speed)
I want to make the movement look more realistic, that's why I use two velocity vectors. dv tells the full speed I want to move the object, and cv holds the real speed the object currently moves at.
Then at each frame (update step) the current velocity cv is set based on the desired velocity dv and an acceleration value acc. This is done by simply calculating the difference between cv and dv and clamping this difference to acc. That way the object starts to move slowly and accelerates gradually to eventually reach full speed.
So far this is working fine. Now I want to make use of acc for deceleration as well. When the distance between pos and target is at a certain value, the desired velocity dv should be set to (0,0), so that the object gradually decelerates until it comes to a full stop at the target position.
My question is: How can I calculate at which distance I need to set dv to (0,0) (i.e. tell the system to stop movement), so that the object decelerates correctly to stop exactly at the target position?
Use the kinematic equations:
vf2 = vi2 + 2 * a * d
vf is your final velocity, or 0 (the speed you want to be going)
vi is your initial velocity, given (the speed your object is currently moving).
a is acceleration
d is distance.
Solve for d:
2*a*d = vf2 - vi2
2*a*d = 0 - vi2
assume acceleration is negative, so multiply both sides by -1
2*|a|*d = vi2
|a| is the absolute value of your acceleration (deceleration in your case)
d = vi2 / (2*|a|)
You're doing a discrete time simulation of motion. One way to keep things simple is to perform the calculations in a way that makes acceleration and deceleration symmetrical. In other words, the distance traveled while accelerating should be the same as the distance traveled while decelerating. As an example, assume
acceleration is 5
top speed is 13
the object begins decelerating as soon as it reaches top speed
Here's how the discrete time simulation would progress
first tick
old speed = 0
new speed = 5
distance = 5
second tick
old speed = 5
new speed = 10
distance = 15
third tick
old speed = 10
new speed = 13
distance = 28 <-- total distance while accelerating
fourth tick
old speed = 13
distance = 41
new speed = 10 <-- not 8!!!
fifth tick
old speed = 10
distance = 51
new speed = 5
sixth tick
old speed = 5
distance = 56 <-- Yay, twice the distance, we have symmetry
new speed = 0
There are two key points here
While accelerating the speed is updated first and then the distance is updated. While decelerating the order is reversed, the distance is updated first, and then the speed.
When decelerating, it's important to keep the adjusted speed as a multiple of the acceleration
In the C programming language, the following code could be used to update the speed during deceleration
if ( old_speed % acceleration != 0 ) // if speed is not a multiple of acceleration
new_speed = old_speed - old_speed % acceleration; // reduce speed to a multiple of acceleration
else // otherwise
new_speed = old_speed - acceleration; // reduce speed by acceleration
If acceleration and deceleration are symmetrical, then computing the deceleration distance is the same as computing the acceleration distance.
distance = acceleration * (1+2+3+ ... +N) + fudge_factor
where
N is top_speed / acceleration truncated to an integer, e.g. 13/5 ==> 2
fudge_factor is 0 if top speed is a multiple of acceleration, or
top_speed otherwise
The computation can be simplified by noting that
1+2+3+ ... +N = N * (N+1) / 2
In C, the total distance travelled while decelerating could be computed as follows
int top_speed = 13;
int acceleration = 5;
int N = top_speed / acceleration; // Note: in C, integer division truncates
int fudge = 0;
if ( top_speed % acceleration != 0 )
fudge = top_speed;
int distance = acceleration * (N * (N+1)) / 2 + fudge;
I'm looking at this example in particular:
http://www.airtightinteractive.com/demos/processing_js/noisefield08.html
And here's the code for it:
http://www.airtightinteractive.com/demos/processing_js/noisefield08.pjs
I guess I need explaining for what these lines do in the particle class:
d=(noise(id,x/mouseY,y/mouseY)-0.5)*mouseX;
x+=cos(radians(d))*s;
y+=sin(radians(d))*s;
I understand that noise calculates a value based on the coordinates given, but I don't get the logic in dividing the particles' x pos by the mouseY, or the y pos by the mouseY. I also don't understand what 'id', which seems to be a counter stands for, or what the next two lines accomplish.
Thanks
Move mouse to change particle motion.
d seems to be the direction of motion. By putting mouseY and mouseX into the calculation of d it allows the underlying field to depend on the mouse position. Without a better understanding of the function itself I can't tell you exactly what affect mouseY and mouseX have on the field.
By running cos(radians(d)) and sin(radians(d)) the code turns an angle (d) into a unit vector. For example, if d was 1 radian then cos(radians(d)) would be -1 and sin(radians(d)) would be 0 so it turns the angle 1 radians into the unit vector (-1,0).
So it appears that there is some underlying motion field which determines the direction the particles move. The motion field is represented by the noise function and takes in the current position of the particle, the particle id (perhaps to give each particle independent motion or perhaps to remember a history of the particle's motion and base the future motion on that history) and the current position of the mouse.
The actual distance the particle moves is s which is determined randomly to be between 2 and 7 pixels.
By running cos(radians(d)) and sin(radians(d)) the code turns an angle (d) into a unit vector. For example, if d was 1 radian then cos(radians(d)) would be -1 and sin(radians(d)) would be 0 so it turns the angle 1 radians into the unit vector (-1,0).
Slight correction: that is a rotation of pi radians (180 degrees), not 1 radian (around 57 degrees).
I have a point that moves (in one dimension), and I need it to move smoothly. So I think that it's velocity has to be a continuous function and I need to control the acceleration and then calculate it's velocity and position.
The algorithm doesn't seem something obvious to me, but I guess this must be a common problem, I just can't find the solution.
Notes:
The final destination of the object may change while it's moving and the movement needs to be smooth anyway.
I guess that a naive implementation would produce bouncing, and I need to avoid that.
This is a perfect candidate for using a "critically damped spring".
Conceptually you attach the point to the target point with a spring, or piece of elastic. The spring is damped so that you get no 'bouncing'. You can control how fast the system reacts by changing a constant called the "SpringConstant". This is essentially how strong the piece of elastic is.
Basically you apply two forces to the position, then integrate this over time. The first force is that applied by the spring, Fs = SpringConstant * DistanceToTarget. The second is the damping force, Fd = -CurrentVelocity * 2 * sqrt( SpringConstant ).
The CurrentVelocity forms part of the state of the system, and can be initialised to zero.
In each step, you multiply the sum of these two forces by the time step. This gives you the change of the value of the CurrentVelocity. Multiply this by the time step again and it will give you the displacement.
We add this to the actual position of the point.
In C++ code:
float CriticallyDampedSpring( float a_Target,
float a_Current,
float & a_Velocity,
float a_TimeStep )
{
float currentToTarget = a_Target - a_Current;
float springForce = currentToTarget * SPRING_CONSTANT;
float dampingForce = -a_Velocity * 2 * sqrt( SPRING_CONSTANT );
float force = springForce + dampingForce;
a_Velocity += force * a_TimeStep;
float displacement = a_Velocity * a_TimeStep;
return a_Current + displacement;
}
In systems I was working with a value of around 5 was a good point to start experimenting with the value of the spring constant. Set it too high will result in too fast a reaction, and too low the point will react too slowly.
Note, you might be best to make a class that keeps the velocity state rather than have to pass it into the function over and over.
I hope this is helpful, good luck :)
EDIT: In case it's useful for others, it's easy to apply this to 2 or 3 dimensions. In this case you can just apply the CriticallyDampedSpring independently once for each dimension. Depending on the motion you want you might find it better to work in polar coordinates (for 2D), or spherical coordinates (for 3D).
I'd do something like Alex Deem's answer for trajectory planning, but with limits on force and velocity:
In pseudocode:
xtarget: target position
vtarget: target velocity*
x: object position
v: object velocity
dt: timestep
F = Ki * (xtarget-x) + Kp * (vtarget-v);
F = clipMagnitude(F, Fmax);
v = v + F * dt;
v = clipMagnitude(v, vmax);
x = x + v * dt;
clipMagnitude(y, ymax):
r = magnitude(y) / ymax
if (r <= 1)
return y;
else
return y * (1/r);
where Ki and Kp are tuning constants, Fmax and vmax are maximum force and velocity. This should work for 1-D, 2-D, or 3-D situations (magnitude(y) = abs(y) in 1-D, otherwise use vector magnitude).
It's not quite clear exactly what you're after, but I'm going to assume the following:
There is some maximum acceleration;
You want the object to have stopped moving when it reaches the destination;
Unlike velocity, you do not require acceleration to be continuous.
Let A be the maximum acceleration (by which I mean the acceleration is always between -A and A).
The equation you want is
v_f^2 = v_i^2 + 2 a d
where v_f = 0 is the final velocity, v_i is the initial (current) velocity, and d is the distance to the destination (when you switch from acceleration A to acceleration -A -- that is, from speeding up to slowing down; here I'm assuming d is positive).
Solving:
d = v_i^2 / (2A)
is the distance. (The negatives cancel).
If the current distance remaining is greater than d, speed up as quickly as possible. Otherwise, begin slowing down.
Let's say you update the object's position every t_step seconds. Then:
new_position = old_position + old_velocity * t_step + (1/2)a(t_step)^2
new_velocity = old_velocity + a * t_step.
If the destination is between new_position and old_position (i.e., the object reached its destination in between updates), simply set new_position = destination.
You need an easing formula, which you would call at a set interval, passing in the time elapsed, start point, end point and duration you want the animation to be.
Doing time-based calculations will account for slow clients and other random hiccups. Since it calculates on time elapsed vs. the time in which it has to compkete, it will account for slow intervals between calls when returning how far along your point should be in the animation.
The jquery.easing plugin has a ton of easing functions you can look at:
http://gsgd.co.uk/sandbox/jquery/easing/
I've found it best to pass in 0 and 1 as my start and end point, since it will return a floating point between the two, you can easily apply it to the real value you are modifying using multiplication.