In short, I have a spiral plot and dates are mapped on to this. This works fine but I now want to map date ranges on this same spiral. This spiral is produced using d3.radialLine. I can get the angle of each data point from using math.atan2(x,y), but how can I get the radius of each data point?
Math.hypot calculates the distance between two points:
const distance = Math.hypot(ax - bx, ay - by);
Similar to:
const dx = ax - bx;
const dy = ay - by;
const distance = Math.sqrt(dx * dx + dy * dy);
This works: radius = Math.sqrt(((x-0)*(x-0))+((y-0)*(y-0))).
However, if there is a more elegant way to write this let me know
Related
Hi I want to calculate the perpendicular distance between two rotated elements, when rotation is 0 I use el1.getBoundingClientRect().x - el2.getBoundingClientRect().x and it gives me right distance, but for rotated elements, it does not work as it gives the perpendicular distance between the vertexes (the distance of bounding rectangles, how can I get x? thanks!
https://codesandbox.io/s/epic-thunder-mxqbc?file=/src/index.js
If differences of vertex coordinates of two rectangles before rotation were (dx, dy), then after rotattion by angle fi new differences are:
nx = dx * cos(fi) - dy * sin(fi)
ny = dx * sin(fi) + dy * cos(fi)
If we multiply the first equation by cos(fi) and the second one by sin(fi), then add them, we can find needed value
dx = nx * cos(fi) + ny * sin(fi)
(assuming you know vertex difference in rotated state)
For example below: dx was 25, cos(fi)=4/5, sin(fi)=3/5
After rotation: nx = 5, ny = 35, we can find dx = 5*4/5 + 25*3/5 = 4+21 = 25
I need to place vector b(p2, p3) on vector a(p1, p2)
Could you advise me some formula?
If you need angle to rotate, calculate it as
dAx = P1.x - P2.x
dAy = P1.y - P2.y
dBx = P3.x - P2.x
dBy = P2.y - P2y
an = atan2(dAx * dBy - dAy * dBx, dAx * dBx + dAy * dBy)
but your picture perhaps shows that you need to just make vector with length of P2P3 but collinear with P2P1. In this case:
-calculate length of both vectors
-find normalized direction vector for P2P1 - get it's component nad divide by vector length
-multiply components of normalized vector by length of P2P3
self.hitBall = function(ball, x, y) {
var angle = Math.atan2((x - ball.centerX), (y - ball.centerY));
ball.velocityY = (Math.sin(angle) * 10);
ball.velocityX = (Math.cos(angle) * 10);
};
So the function takes in the ball, which has a centerX variable and a centerY variabe. The x and y passed into the function is the x and y is the point the ball was hit. I want to make the ball travel in the direction it was hit from.
Not really sure why my code isn't working.. it's behaving very strangely and I'm not that good with trigonometry so I'm not really quite sure why it isn't working.
Two problems with your code:
Math.atan2() takes the arguments in (y, x) order. Most languages (Java, JavaScript, C, etc.) do this (except Microsoft Excel and some others, which use (x, y) order).
When you say "[make] the ball travel in the angle it was hit from", you want to subtract the hit point from the ball point. In other words, the vector is (ball.centerX - hitX, ball.centerY - hitY).
Thus, the solutions:
Solution 1:
var angle = Math.atan2((ball.centerY - y), (ball.centerX - x));
Solution 2 - do vector math without angles (equivalent calculation):
var dx = ball.centerX - x;
var dy = ball.centerY - y;
var norm = Math.sqrt(dx * dx + dy * dy);
ball.velocityX = (dx / norm) * 10;
ball.velocityY = (dy / norm) * 10;
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.
I'm trying to calculate an angle based on triangle sides, preferably with sin.
The first 2 are helper functions getDistance and getPointsDifference
I have these functions:
var getDistance = function(p1, p2){
var dx = p1.x - p2.x, dy = p1.y - p2.y;
return Math.sqrt(dx*dx + dy*dy);
}
var getPointsDifference = function(p1, p2){
return {
x: -1 * (p1.x - p2.x),
y: (p1.y - p2.y)
}
}
and finaly:
var getMenuChoice = function(cx,cy, x, y){
var distance = getDistance({x:cx,y:cy}, {x:x,y:y});
if (distance <= 100) {
console.log(1)
} else {
console.log(2)
}
var diff = getPointsDifference({x:cx,y:cy}, {x:x,y:y});
var a = Math.sin(diff.y/distance)
console.log("asdf:", a)
}
Could someone please show me what am I doing wrong? I would like to calculate the result in degrees.
update
I detect a lick on the screen which gives me a x,y, and then I subtract those x,y from cx and cy which are the center of the screen
This is called the angle (or direction) of the vector from (or to, depends on what you need) point of click to the center of the screen. There is no need in calculation of the distance and arcsin of the angle (instead of yours sin) - you can just use Math.atan2(dy, dx);.
dy is change in y (y2 - y1) and dx is change in x (x2 - x1) between those two points. You can use a regular Math.atan(dy / dx), but then you must be sure that you are not dividing by zero and have to take into account the signs of dy and dx to have answer in the correct quadrant. Math.atan2 will do it all for you. And the picture below is just a reminder.
And yes, the answer will be in radians, as it was mentioned in comments. Conversion is simple degrees = radians * (180 / Math.PI);