Calculate the position of an orbiting object - javascript

I'm creating a solar system animation using canvas and have a problem calculating an object's position (x,y) values.
Earth is orbiting around the Sun, and I need to calculate and update the Earth's position at each frame. And using Earth's position, I'll orbit the Moon around the Earth.
The related function is this:
orbitAround : function (master) {
master.makeOrbitCenter();
this.increaseOrbitAngle();
context.rotate(this.orbit.angle * Math.PI/180);
context.translate(this.orbit.distance, 0);
// calculate and update the X and Y position of the earth
// so that luna can orbit around earth
// these are the initial values of earth
this.position.x = master.position.x + this.orbit.distance;
this.position.y = master.position.y;
},
Fiddle demo
To make it simpler, I've drawn this picture
Let's say the blue disc is the Earth, and is located at (300,200). The Sun is at (200,200) and the distance between them is 100. After the Earth makes a 45 degree rotation around the Sun, what would its position be? How can I calculate it using the given values?

It looks like you've got something working there, but here's a breakdown:
Math.cos and Math.sin return a theoretical x and y position along an axis. They need to be given the angle in radians.
So, in order to get the position of a 45 degree angle, you essentially need to calculate the radian equivalent of degrees first.
var degrees = 45;
var radians = degrees * (Math.PI / 180);
The result here is 0.785398163. You can then get the x and y coordinate of the orbiting object using this:
var x = Math.cos(0.785398163) * distance;
var y = Math.sin(0.785398163) * distance;
The whole process for a single frame would look like this:
var distance = 100; // from the centre of orbit
var degrees = 45; // around a 360 degree orbit
var radians = degrees * (Math.PI / 180);
var x = Math.cos(radians) * distance;
var y = Math.sin(radians) * distance;
Here's a very basic fiddle with square planets and everything.

I would do something like this:
distance = 100; //distance between earth and sun.
getXPosition = function(angle){
return distance*(Math.cos(Math.PI * (angle/180)));
};
getYPosition = function(angle){
return distance*(Math.sin(Math.PI * (angle/180)));
};
var x = getXPosition(45); //70.71 from the sun
var y = getYPosition(45); //70.71 from the sun
And to get the final position just do this:
x += 200; //200 = sun position
y -= 200; //200 = sun position

Related

Calculate distance on the basis of Angle and Maximum Range

I am working on javascript code where I need to find out distance on the basis of Angle, Initial Height, Velocity, Maximum Range.
Example: If an object shoots from the ground (where height = 0) at the angle 45 degree and with the velocity of 3000. The Object drops at distance 1500 meter far from the point where it was thrown.
What will be the distance from the shooting point to dropping point on the ground, if the object shoots from same height and velocity but at the angle of 60 degree.
Initial Height => h = 0
Angle => a = 45 degree
Velocity => v = 3000
Max Range => m = 1500 meter
var h = 0;
var a = 45;
var v = 3000;
var m = 1500;
var d = null; //need to calculate this
// Range calculation formula is: d = V₀² * sin(2 * α) / g
d = v * v * Math.sin(2 * a) / 9.8;
I am getting Range from above formula but that's not on the basis of given maximum range.
The function Math.sin expects that the angle is given in radian. Given an angle α in degree, you can compute the angle in radian by α * (π/180). Thus, your computation needs to be performed as follows.
d = v * v * Math.sin(2 * a * Math.PI / 180) / 9.8;
Note, your maximum range is actually ≈920000 m. Your initial velocity is 10800 km/h (or 6710 mph), which is 10 times as fast as a commercial air plane.

Javascript | SVG object jumps after rotation

I'm trying to rotate an SVG object (a rectangle, which is also draggable using x and y attributes) via rotate(angle x y), x and y is the midpoint of the object which I calculate with this function:
getMidPoint(x, y, width, height, angle, oldMidPointX, oldMidPointY, drawPoint) {
let angleRad = angle * Math.PI / 180;
let cosa = Math.cos(angleRad);
let sina = Math.sin(angleRad);
// Find new x/y values after rotation
let tempX = x - oldMidPointX;
let tempY = y - oldMidPointY;
let xAfterRotation = (tempX * cosa) - (tempY * sina);
let yAfterRotation = (tempX * sina) + (tempY * cosa);
drawPoint(x, y, "aqua");
// translate back
x = xAfterRotation + oldMidPointX;
y = yAfterRotation + oldMidPointY;
drawPoint(x, y, "green");
let wp = width / 2;
let hp = height / 2;
let px = x + (wp * cosa) - (hp * sina);
let py = y + (wp * sina) + (hp * cosa);
drawPoint(x, y, "red");
return {
px: px,
py: py };
}
Consecutive rotations are no problem. The jumping occurs if the object gets rotated, resized (for example increasing the width of the object) and then rotated again. While the function above gives the correct midpoint, the object first jumps and rotates around the the calculated point.
I don't know what I'm missing here, maybe someone can spot the mistake or give me some ideas to try out.
EDIT
Here is an image to showcase my problem.
1: Starting position; red dot marks the midpoint of the object and green dot is the position used for calculating the midpoint.
2: Rotating the object with no problem.
3: Resizing the object.
4: Calculating new midpoint on start; blue dot marks x and y coordinates of the object.
5: Rotating the second time, "jumping" occurs.
6-8: The object rotates (the same rotation started at previous step) around the midpoint but with an offset.
9: 360° rotation puts the top left corner of the object to it's x/y position
Maybe someone can spot the mistake now.

Problems With Vector Reflection with Particle Collisions

I was wondering whether I made a math mistake in my particle collision simulation found here.
The particles don't seem to separate properly during collision resolution. Here is a code snippet from the function which separates particles and changes their velocities:
//particle 1
var pi = particles[i];
//particle 2
var pj = particles[j];
//particle 1 to particle 2
var pimpj = pi.mesh.position.clone().sub(pj.mesh.position);
//particle 2 to particle 1
var pjmpi = pj.mesh.position.clone().sub(pi.mesh.position);
//if colliding (radius is 20)
if(pimpj.length() < 20 && pimpj.length() != 0)
{
//reflect velocity off of 1->2
pi.velocity = pi.velocity.reflect(pimpj.clone().normalize()).multiplyScalar(restitution);
//reflect velocity off of 2->1
pj.velocity = pj.velocity.reflect(pjmpi.clone().normalize()).multiplyScalar(restitution);
//move particle 1 to appropiate location based off of distance in between
var pip = pi.velocity.clone().normalize().multiplyScalar(20-pimpj.length());
//move particle 2
var pjp = pj.velocity.clone().normalize().multiplyScalar(20-pimpj.length());
pi.mesh.position.add(pip);
pj.mesh.position.add(pjp);
}
I have tried reversing pimpj with pjmpi while changing pi.velocity, but to no effect.
note: I am using three.js
Firstly, the particle collisions you seem to be looking for are Elastic collisions, for which there is maths covering the calculation of the velocities after a collision.
The collision is simplest in the centre of momentum frame, so if you first calculate that frame V = (m1v1 + m2v2)/(m1+m2), then you can subtract it from both particles, do a simple symmetric collision and add the frame velocity back on afterwards.
From there, calculate the velocities using the formulae in the 2 & 3d section of that page.
Specific points on your code:
pimpj = -pjmpi, so you don't need both
A collision occurs when the paths between the last frame and this frame got too close; if you only check the distance at each frame you will have problems where particles fly through each other at high speed, and that you have to keep shifting their positions because they are already overlapping when you detect the collision.
Ideally calculate the positions on impact and use those to redirect them.
For speed, only calculate pimpj.clone().normalize() once, and store it - you're not changing this direction unit vector later, so you don't need to keep recalculating it, or calculating pjmpi-derived equivalents (see #1)
I think part of the problem is that your model of collision is overly simplistic. The basic rules for collision are conservation of momentum and conservation of energy. Looking at the 1D case. If both particles have the same mass m and u1 and u2 are you velocities beforehand and v1, v2 are the velocities after then
m u1 + m2 u2 = m v1 + m v2
conservation of energy for a perfect collision gives
1/2 m u1.u1 + 1/2 m u2.u2 = 1/2 m v1.v1 + 1/2 m v2.v2.
These two equations have the solution v1 = u2, v2 = u1. That is the velocities switch. In particular if one velocity is zero before collision then after collision the other velocity becomes zero after the collision. You can see this happen when using a newton's cradle.
In 2D we can resolve in a coordinate system with 1 direction along to the plane of contact and one direction perpendicular to it. The force only occurs in the perpendicular direction, this means the velocities along the pane don't change but the perpendicular velocities switch.
var u = pjmpi.clone().normalize();
var v = new THREE.Vector3(u.y,-u.x,0);
// resolve in two directions
var piu = pi.velocity.dot(u);
var piv = pi.velocity.dot(v);
pi.velocity = new THREE.Vector3(
pju * u.x + piv * v.x,
pju * u.y + piv * v.y,
0);
pj.velocity = new THREE.Vector3(
piu * u.x + pjv * v.x,
piu * u.y + pjv * v.y,
0);
That works for a perfectly elastic collision. See Wikipedia elastic collision which has an nice illustration. The formula at the end simplifies a bit if you take the masses equal.
For an partially inelastic collision with restitution R we can look at the end of http://www.plasmaphysics.org.uk/collision2d.htm. Now take the velocity of the center of mass w. This will not change after the collision because the total momentum is conserved. So w=(u1+u2)/2 = (v1+v2)/2. Take the velocities relative to this center of mass
v1' = v1-w, v2' = v2-w, apply the restitution v1'' = R (v1'-w), v2'' = R(v2'-w) and add the velocity of the center of mass.
v1''' = R(v1-v2)/2 + (v1+v2)/2
v2''' = R(v1-v2)/2 + (v1+v2)/2
Also see wikipedia Inelastic collision which has the same formula in 1D.
This translate to code as
var u = pjmpi.clone().normalize();
var v = new THREE.Vector3(u.y,-u.x,0);
// resolve in two directions
var piu = pi.velocity.dot(u);
var piv = pi.velocity.dot(v);
var pju = pj.velocity.dot(u);
var pjv = pj.velocity.dot(v);
// velocities after collision
var v1x = pju * u.x + piv * v.x;
var v1y = pju * u.y + piv * v.y;
var v2x = piu * u.x + pjv * v.x;
var v2y = piu * u.y + pjv * v.y;
// vel center of mass
var wx = (v1x+v2x)/2;
var wy = (v1y+v2y)/2;
// difference
var dx = (v1x-v2x)/2;
var dy = (v1y-v2y)/2;
// final velocities
pi.velocity = new THREE.Vector3(
wx + restitution * dx,
wy + restitution * dy,
0);
pj.velocity = new THREE.Vector3(
wx - restitution * dx,
wy - restitution * dy,
0);
// We can print the KE and momentum before and after to check
console.log("KE before ",
pi.velocity.lengthSq()+pj.velocity.lengthSq());
console.log("M before ",
pi.velocity.x+pj.velocity.x ,
pi.velocity.y+pj.velocity.y);
console.log("KE after",v1x*v1x+v1y*v1y + v2x*v2x + v2y*v2y);
console.log("M after ", v1x+v2x, v1y+v2y);
console.log("KE rest",
pi.velocity.lengthSq()+pj.velocity.lengthSq());
console.log("M rest ",
pi.velocity.x+pj.velocity.x ,
pi.velocity.y+pj.velocity.y);
This can simplify nicely. Start by taking the mean and half the difference of the two particles. Reflect the difference and apply the restitution
var len = pjmpi.length();
// unit vector normal to plane of collision
var nx = pjmpi.x / len;
var ny = pjmpi.y / len;
// unit vector tangent to plane of collision
var tx = -ny;
var ty = nx;
// center of mass
var wx = (pi.velocity.x+pj.velocity.x)/2;
var wy = (pi.velocity.y+pj.velocity.y)/2;
// half difference
var dx = (pi.velocity.x-pj.velocity.x)/2;
var dy = (pi.velocity.y-pj.velocity.y)/2;
// resolve in two directions
var a = dx * nx + dy * ny;
var b = dx * tx + dy * ty;
// reflect difference in normal
var cx = -a * nx + b * tx;
var cy = -a * ny + b * ty;
// apply restitution and add back center of mass
pi.velocity.set(
wx + restitution * cx,
wy + restitution * cy,
0);
pj.velocity.set(
wx - restitution * cx,
wy - restitution * cy,
0);
I've used THREE as little as possible to avoid creating too many objects.
I've saved this as a fiddle at http://jsfiddle.net/SalixAlba/8axnL59k/. I've reduced number of points, and removed gravity to make things a bit simpler to see.
Besides the very good points in the other answers, you only want to do the velocity changes if the particles move towards each other. This is the case of the derivative of the distance is negative, which can be transformed to the scalar product of pi-pj and vi-vj being negative.
Without that you may enter an infinite loop where the velocities get reflected back and forth while the distance stays below the critical radius.

How to fix errant orientation and points in an elliptical particle cloud?

I'm creating an elliptical cloud of points in Three.js and have two problems that seem straight forward, but I've been stuck on now for a couple days:
The cloud should be elliptically shaped with the longest axis on the x axis - currently it's oriented with the shortest axis on the x axis
While my fiddle for the cloud looks perfectly elliptical, the working code in my file has a few points at y = 0 on the x axis that appear to be running to the beat of their own drummer - they are lined up like soldiers on the y = 0 on either side of the ellipse to the measure of length (see screenshot below). Both files contain the same math for the ellipse - what's going on here?
Thanks!
The code for the elliptical cloud of points:
particleCount = 300;
for (var p = 0; p < particleCount; p++) {
// ELLIPTICAL
var length = 200; // x (major axis)
var width = 75; // y (minor axis)
var pX = (Math.random() * length);
// solve for length of vertical chord at pX:
var chord;
var rightBisector = Math.abs((length/2) - pX);
chord = (2 * length) * Math.sqrt(1 - Math.exp(2 * Math.log(rightBisector/width)));
var pY = (Math.random() * chord);
pX -= length/2; // center the cloud horizontally
pY -= chord/2; // center the cloud vertically
// END ELLIPTICAL
// Make image particle at pX, pY and add to system
It turns out all the errors lay in my misunderstanding of the ellipse equation someone kindly aided me on here. I've corrected the code below now realizing that I used the dimensions of the axes in places where I should have used the two different radii. I've edited some of the variable names to make things clearer.
// ELLIPTICAL particle cloud
var majorAxis = 200; // x (major axis)
var minorAxis = 75; // y (minor axis)
var majorRadius = majorAxis / 2;
var minorRadius = minorAxis / 2;
var pX = ( Math.random() * majorAxis );
// solve for length of vertical chord at pX:
var rightBisector = Math.abs( majorRadius - pX );
var chord = (2 * minorRadius) * Math.sqrt( 1 - Math.exp( 2 * Math.log( rightBisector / majorRadius ) ) );
var pY = ( Math.random() * chord );
pX -= majorRadius; // center the cloud horizontally
pY -= chord / 2; // center the cloud vertically
// END ELLIPTICAL
// Make image particle at pX, pY and add to system
Now all is as it should be:

Custom Rotation Algorithm Needs Expandability

I'm working on a library for canvas, and I can't figure out how I should be doing rotations around the center of an object. Below is the function I'm using to render them currently and an example object I'm giving it.
I feel there is a better way to do the 4 if statements, but I can't figure out the math for it. Currently I am taking each "pixel" of the object and rotating it around the center, but I can't see room for expansion. What am I doing wrong?
//Rendering function
Display.prototype.renderObject = function(object, direction) {
if (typeof object.rotation !== 'number') object.rotation = 0;
for (x=0;x<object.bounds.x;x++) {
for (y=0;y<object.bounds.y;y++) {
rotation = 45;
if (x==0 && y==0) rotation += 0;
if (x==0 && y==1) rotation += 90;
if (x==1 && y==0) rotation += 270;
if (x==1 && y==1) rotation += 180;
display.drawRect(object.color[x][y],
(display.width/2) - (players[playerIndex].position.x * 16) + (object.position.x * 16) - (object.bounds.x * object.scale)/4 - (object.bounds.x/3 * object.scale * Math.cos((object.rotation+rotation)*(Math.PI/180))),
(display.height/2) + (players[playerIndex].position.y * 16) - (object.position.y * 16) - (object.bounds.y * object.scale)/4 - (object.bounds.y/3 * object.scale * Math.sin((object.rotation+rotation)*(Math.PI/180))),
object.scale, object.scale, object.rotation * (direction || 1));
}
}
};
// Example object
block = {
"color": [
["#FFF","#CCC"], // Some colors to make
["#999","#666"] // a shaded illusion
],
"position": {
"x": 0,
"y": 0
},
"bounds": {
"x": 2, // color[0].length
"y": 2 // color.length
},
"rotation": 0, // 0 to 360
"scale": 4 // real pixels per object pixel
}
// Example usage
Display.renderObject(block);
- edit -
Maybe I need to have it calculate where each pixel's center coordinates would be, then get the distance from that to the origin of the object and the rotation each pixel would be offset at.
If x = 0 and y = 0, then it's +45 degrees with sin and -45 degrees with cos+45 degrees with sin. If (object.bounds.x-1)/2 gives us the center coords for dealing with x, then Math.sqrt(Math.pow(x,2)+Math.pow(y,2)) gives us the radius for the color block from the center of the object. I'm not sure where to go from there.
I am not 100% sure of what you're asking, but if it is how to rotate points around the points' center you have to translate all your points so that you object to rotate is centered around origin or point [0, 0] if you like.
A rotation matrix always rotates around origin so there is no way around (no pun intended).
So before you rotate your points calculate a delta value that you apply before the rotation, rotate and then reverse the delta and apply again to get the object back with its offset relative to origin.
So first step is to calculate the delta to find out how much you need to translate the points to get them into center.
For a rectangle it is easy: it is its x and y position plus half of its width and height:
deltaX = x + width * 0.5;
deltaY = y + height * 0.5;
This value you then need to subtract to the points. How you apply this delta is up to you: you can do it in advance with each point or inject it directly into the formula.
Then rotate and when done you simply apply add the delta value to each point again.
Here is a shorter version of a rotation matrix based on my article here:
var a = rotation * Math.PI / 180; /// calculate angle once
x -= deltaX; /// apply pre-translation
y -= deltaY;
/// rotate and apply post-translation
nx = x * Math.cos(a) + y * Math.sin(a) + deltaX;
ny = y * -Math.sin(a) + x * Math.cos(a) + deltaY;
The ifs you're using seem to come from code that use this for optimizations of the angles; when you use 0, 90 etc. as cos and sin would produce 1 and 0 respectively. You are however not utilizing this optimization as you could simply swap x for y etc. when these cases occurs. However, you bind then to x and y position so I am not sure what you try to achieve here..
I would suggest you just remove them as you calculate and applies sin/cos anyways - you could instead cache the sin and cos and use those for all the points:
var angle = rotation * Math.PI / 180;
sin = Math.sin(angle),
cos = Math.cos(angle),
...calc deltas here...
...enter loop here, then for each point:...
x -= deltaX;
y -= deltaY;
nx = x * cos + y * sin + deltaX;
ny = y * -sin + x * cos + deltaY;
Now the cost is lower for each point and your calculations will perform faster.
Hope this helps.

Categories