Given two 2D points (p1 and p2), I need find a point (p3) that (from the perspective of p1) is on the other side of p2 and at the same time it needs to have a given distance to p2.
It could look like this with a bigger given distance:
^
|
| p1
| p2
|
| p3
<-|---------------------------->
v
Or like this with a smaller given distance:
^
|
|
| p3
| p2
|p1
<-|---------------------------->
v
How can I calculate this point (p3)?
My language of choice is JavaScript, but I'm not strictly asking for a JavaScript answer. If you can explain it in a way that is translatable into code or if you write pseudo code that would be just fine.
This was my last attempt (obviously this would not work):
calculate_point_on_other_side_of_p2(p1, p2, distance_p2_to_p3) {
deltaX = p1.x-p2.x
deltaY = p1.y-p2.y
distance_p1_to_p2 = sqrt(deltaX*deltaX + deltaY*deltaY)
if (deltaX < 0)
p3.x = p2.x+distance_p2_to_p3
else
p3.x = p1.x-distance_p2_to_p3
if (deltaY < 0)
p3.y = p2.y+distance_p2_to_p3
else
p3.y = p1.y-distance_p2_to_p3
return p3
}
This is an easy problem if you understand how 2D vectors work.
Calculate the unit vector from p1 to p2:
(nx, ny) = ((p2x - p1x)*i + (p2y-p1y)*j)/sqrt((p2x-p1x)^2 + (p2y-p1y)^2)
Where i and j are unit vectors in the x and y directions, respectively.
Now you can calculate (p3x, p3y) at any distance d from p1:
(p3x, p3y) = (p1x, p1y) + (d*nx, d*ny)
Note minus sign because deltas are components of vector from p2 to p1 but p2p3 is anticollinear to p2p1
calculate_point_on_other_side_of_p2(p1, p2, distance_p2_to_p3) {
deltaX = p1.x-p2.x
deltaY = p1.y-p2.y
distance_p1_to_p2 = sqrt(deltaX*deltaX + deltaY*deltaY)
scale = distance_p2_to_p3 / distance_p1_to_p2
p3.x = p2.x - deltaX * scale
p3.y = p2.y - deltaY * scale
return p3
}
One version of the correct algorithm would be as follows (this isn't even pseudocode, but should explain what to do):
work out the distance between p1 and p2 (use pythag)
divide the given distance (distance_p2_to_p3) by that
for each of the x and y co-ordinates, add on deltaX (resp deltaY) multiplied by that ratio
Let's assume all three lines are in a line. Then the slope is deltaY/deltaX. If p3 is x away from p2 horizontally, then it is deltaY/deltaX * x away from p2 vertically. distance_p2_to_p3^2 = x^2 + (deltaY/deltaX * x)^2, solve for x. Then add/subtract x from p2.x and add.subtract deltaY/deltaX * x from p2.y.
Related
I have three 3D points defining a plane, P1(x1,y1,z1) P2(x2,y2,z3) and P3(x3,y3,z3). I need to find another point A on that plane which is L1 length from P1 and L2 length from P2 and closest to P3 (out of the 2 possible positions). I have found a solution for this in 2D (https://www.hindawi.com/journals/jr/2010/984823) but I am having difficulty extending it to 3D. I will be implementing this in JS. Any help would be appreciated!
function calculatePointFromLengths(p1,p2,l1,l2) {
let L = sqrt((p2.x-p1.x)*(p2.x-p1.x)+(p2.y-p1.y)*(p2.y-p1.y));
let angleP1P2 = Math.atan((p2.y-p1.y)/(p2.x-p1.x));
let theta1 = Math.acos(l1*l1+l2*l2-l2*l2)/(2*l1*L) + angleP1P2;
// now calculate the point using the angle
return( {
x: p1.x + l1 * Math.cos(theta1);
y: p1.y + l1 * Math.sin(theta1);
});
}
Because you already have angle between P1A and P1P2 vectors
theta = Math.acos(l1*l1+l2*l2-l2*l2)/(2*l1*L)
You can take vector with direction of P1P2 and length l1
V = P1P2 * l1 / L (do the same for every component)
and rotate it by angle theta around axis defined by vector product of P1P2 and P2P3
axis = P1P2 x P2P3
and apply Rodrigues' rotation formula, then get A point as P1 + V_rotated
Alternative way: write A in parametric coordinates
A = P1 + P1P2*u + P1P3*v
Substitute these coordinates into distance equations
l1^2 = (P1P2*u + P1P3*v)^2
l2^2 = (-P2+ P1 + P1P2*u + P1P3*v)^2
and solve this equation system for unknows u,v, choosing solution with 0<u,v<1, u+v<1 (to provide A inside triangle)
I have used an answer from stack exchange in the mathematics group (https://math.stackexchange.com/questions/4548418/finding-point-in-a-plane-at-fixed-distances-from-2-other-points-in-the-plane) and coded it here with a demonstration:
https://playground.babylonjs.com/#UEQUG8#46
I'm trying to make some simple pool game in java script. I have made it but I do not love way of checking if two balls will collide in next frame. I would like to have more easier way to calculate coordinates of balls when collision occurs. I found lot of answers base on collision kinematics, how to handle velocities and directions after collision, but no calculating a position when collision occurs.
As you can see in sample diagram, gold ball is moving slower than a blue ball, and with distance that each ball will have to move on next frame will not be considered as collision. But, as you can see, they should collide (dashed lines).
In that cause I have divided each movement into sectors and calculating if distance between the points is equal or smaller than ball diameter, which is slowing down process when many balls (like in snooker) have to be calculated in each frame, plus that way is not always 100% accurate and balls can go in inaccurate angles after hit (not a big difference, but important in snooker).
Is there any easier way to calculate those (XAC,YAC) and (XBC,YBC) values with knowing start positions and velocities of each ball without dividing ball paths into sectors and calculating many times to find a proper distance?
It is worth to precalculate collision event only once (this approach works well with reliable number of balls, because we have to treat all ~n^2 pairs of balls).
The first ball position is A0, velocity vector is VA.
The second ball position is B0, velocity vector is VB.
To simplify calculations, we can use Halileo principle - use moving coordinate system connected with the first ball. In that system position and velocity of the first ball are always zero. The second ball position against time is :
B'(t) = (B0 - A0) + (VB - VA) * t = B0' + V'*t
and we just need to find solution of quadratic equation for collision distance=2R:
(B0'.X + V'.X*t)^2 + (B0'.X + V'.Y*t)^2 = 4*R^2
Solving this equation for unknown time t, we might get cases: no solutions (no collision), single solution (only touch event), two solutions - in this case smaller t value corresponds to the physical moment of collision.
Example (sorry, in Python, ** is power operator):
def collision(ax, ay, bx, by, vax, vay, vbx, vby, r):
dx = bx - ax
dy = by - ay
vx = vbx - vax
vy = vby - vay
#(dx + vx*t)**2 + (dy + vy*t)**2 == 4*r*r solve this equation
#coefficients
a = vx**2 + vy**2
b = 2*(vx*dx + vy*dy)
c = dx**2+dy**2 - 4*r**2
dis = b*b - 4*a*c
if dis<0:
return None
else:
t = 0.5*(-b - dis**0.5)/a ##includes case of touch when dis=0
return [(ax + t * vax, ay + t * vay), (bx + t * vbx, by + t * vby)]
print(collision(0,0,100,0,50,50,-50,50,10)) #collision
print(collision(0,0,100,0,50,50,-50,80,10)) #miss
print(collision(0,0,100,0,100,0,99,0,10)) #long lasting chase along OX axis
[(40.0, 40.0), (60.0, 40.0)]
None
[(8000.0, 0.0), (8020.0, 0.0)]
Regarding to MBo's solution, here is a function in java script that will calculate coordinates of balls on collision and time in which collision will happen:
calcCollisionBallCoordinates(ball1_x, ball1_y, ball2_x, ball2_y, ball1_vx, ball1_vy, ball2_vx, ball2_vy, r) {
let dx = ball2_x - ball1_x,
dy = ball2_y - ball1_y,
vx = ball2_vx - ball1_vx,
vy = ball2_vy - ball1_vy,
a = Math.pow(vx, 2) + Math.pow(vy, 2),
b = 2 * (vx * dx + vy * dy),
c = Math.pow(dx, 2) + Math.pow(dy, 2) - 4 * Math.pow(r, 2),
dis = Math.pow(b, 2) - 4 * a * c;
if (dis < 0) {
//no collision
return false;
} else {
let t1 = 0.5 * (-b - Math.sqrt(dis)) / a,
t2 = 0.5 * (-b + Math.sqrt(dis)) / a,
t = Math.min(t1, t2);
if (t < 0) {
//time cannot be smaller than zero
return false;
}
return {
ball1: {x: ball1_x + t * ball1_vx, y: ball1_y + t * ball1_vy},
ball2: {x: ball2_x + t * ball2_vx, y: ball2_y + t * ball2_vy},
time: t
};
}
}
I'm trying to find a second intersection point of two circles. One of the points that I already know was used to calculate a distance and then used as the circle radius (exemple). The problem is that im not getting the know point, im getting two new coordinates, even thou they are similar. The problem is probably related to the earth curvature but I have searched for some solution and found nothing.
The circles radius are calculated with the earth curvature. And this is the code I have:
function GET_coordinates_of_circles(position1,r1, position2,r2) {
var deg2rad = function (deg) { return deg * (Math.PI / 180); };
x1=position1.lng;
y1=position1.lat;
x2=position2.lng;
y2=position2.lat;
var centerdx = deg2rad(x1 - x2);
var centerdy = deg2rad(y1 - y2);
var R = Math.sqrt(centerdx * centerdx + centerdy * centerdy);
if (!(Math.abs(r1 - r2) <= R && R <= r1 + r2)) { // no intersection
console.log("nope");
return []; // empty list of results
}
// intersection(s) should exist
var R2 = R*R;
var R4 = R2*R2;
var a = (r1*r1 - r2*r2) / (2 * R2);
var r2r2 = (r1*r1 - r2*r2);
var c = Math.sqrt(2 * (r1*r1 + r2*r2) / R2 - (r2r2 * r2r2) / R4 - 1);
var fx = (x1+x2) / 2 + a * (x2 - x1);
var gx = c * (y2 - y1) / 2;
var ix1 = fx + gx;
var ix2 = fx - gx;
var fy = (y1+y2) / 2 + a * (y2 - y1);
var gy = c * (x1 - x2) / 2;
var iy1 = fy + gy;
var iy2 = fy - gy;
// note if gy == 0 and gx == 0 then the circles are tangent and there is only one solution
// but that one solution will just be duplicated as the code is currently written
return [[iy1, ix1], [iy2, ix2]];
}
The deg2rad variable it is suppose to adjust the other calculations with the earth curvature.
Thank you for any help.
Your calculations for R and so on are wrong because plane Pythagorean formula does not work for spherical trigonometry (for example - we can have triangle with all three right angles on the sphere!). Instead we should use special formulas. Some of them are taken from this page.
At first find big circle arcs in radians for both radii using R = Earth radius = 6,371km
a1 = r1 / R
a2 = r2 / R
And distance (again arc in radians) between circle center using haversine formula
var R = 6371e3; // metres
var φ1 = lat1.toRadians();
var φ2 = lat2.toRadians();
var Δφ = (lat2-lat1).toRadians();
var Δλ = (lon2-lon1).toRadians();
var a = Math.sin(Δφ/2) * Math.sin(Δφ/2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ/2) * Math.sin(Δλ/2);
var ad = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
And bearing from position1 to position 2:
//where φ1,λ1 is the start point, φ2,λ2 the end point
//(Δλ is the difference in longitude)
var y = Math.sin(λ2-λ1) * Math.cos(φ2);
var x = Math.cos(φ1)*Math.sin(φ2) -
Math.sin(φ1)*Math.cos(φ2)*Math.cos(λ2-λ1);
var brng = Math.atan2(y, x);
Now look at the picture from my answer considering equal radii case.
(Here circle radii might be distinct and we should use another approach to find needed arcs)
We have spherical right-angle triangles ACB and FCB (similar to plane case BD is perpendicular to AF in point C and BCA angle is right).
Spherical Pythagorean theorem (from the book on sph. trig) says that
cos(AB) = cos(BC) * cos(AC)
cos(FB) = cos(BC) * cos(FC)
or (using x for AC, y for BC and (ad-x) for FC)
cos(a1) = cos(y) * cos(x)
cos(a2) = cos(y) * cos(ad-x)
divide equations to eliminate cos(y)
cos(a1)*cos(ad-x) = cos(a2) * cos(x)
cos(a1)*(cos(ad)*cos(x) + sin(ad)*sin(x)) = cos(a2) * cos(x)
cos(ad)*cos(x) + sin(ad)*sin(x) = cos(a2) * cos(x) / cos(a1)
sin(ad)*sin(x) = cos(a2) * cos(x) / cos(a1) - cos(ad)*cos(x)
sin(ad)*sin(x) = cos(x) * (cos(a2) / cos(a1) - cos(ad))
TAC = tg(x) = (cos(a2) / cos(a1) - cos(ad)) / sin(ad)
Having hypotenuse and cathetus of ACB triangle we can find angle between AC and AB directions (Napier's rules for right spherical triangles) - note we already know TAC = tg(AC) and a1 = AB
cos(CAB)= tg(AC) * ctg(AB)
CAB = Math.acos(TAC * ctg(a1))
Now we can calculate intersection points - they lie at arc distance a1 from position1 along bearings brng-CAB and brng+CAB
B_bearing = brng - CAB
D_bearing = brng + CAB
Intersection points' coordinates:
var latB = Math.asin( Math.sin(lat1)*Math.cos(a1) +
Math.cos(lat1)*Math.sin(a1)*Math.cos(B_bearing) );
var lonB = lon1.toRad() + Math.atan2(Math.sin(B_bearing)*Math.sin(a1)*Math.cos(lat1),
Math.cos(a1)-Math.sin(lat1)*Math.sin(lat2));
and the same for D_bearing
latB, lonB are in radians
I had a similar need ( Intersection coordinates (lat/lon) of two circles (given the coordinates of the center and the radius) on earth ) and hereby I share the solution in python in case it might help someone:
'''
FINDING THE INTERSECTION COORDINATES (LAT/LON) OF TWO CIRCLES (GIVEN THE COORDINATES OF THE CENTER AND THE RADII)
Many thanks to Ture Pålsson who directed me to the right source, the code below is based on whuber's brilliant work here:
https://gis.stackexchange.com/questions/48937/calculating-intersection-of-two-circles
The idea is that;
1. The points in question are the mutual intersections of three spheres: a sphere centered beneath location x1 (on the
earth's surface) of a given radius, a sphere centered beneath location x2 (on the earth's surface) of a given radius, and
the earth itself, which is a sphere centered at O = (0,0,0) of a given radius.
2. The intersection of each of the first two spheres with the earth's surface is a circle, which defines two planes.
The mutual intersections of all three spheres therefore lies on the intersection of those two planes: a line.
Consequently, the problem is reduced to intersecting a line with a sphere.
Note that "Decimal" is used to have higher precision which is important if the distance between two points are a few
meters.
'''
from decimal import Decimal
from math import cos, sin, sqrt
import math
import numpy as np
def intersection(p1, r1_meter, p2, r2_meter):
# p1 = Coordinates of Point 1: latitude, longitude. This serves as the center of circle 1. Ex: (36.110174, -90.953524)
# r1_meter = Radius of circle 1 in meters
# p2 = Coordinates of Point 2: latitude, longitude. This serves as the center of circle 1. Ex: (36.110174, -90.953524)
# r2_meter = Radius of circle 2 in meters
'''
1. Convert (lat, lon) to (x,y,z) geocentric coordinates.
As usual, because we may choose units of measurement in which the earth has a unit radius
'''
x_p1 = Decimal(cos(math.radians(p1[1]))*cos(math.radians(p1[0]))) # x = cos(lon)*cos(lat)
y_p1 = Decimal(sin(math.radians(p1[1]))*cos(math.radians(p1[0]))) # y = sin(lon)*cos(lat)
z_p1 = Decimal(sin(math.radians(p1[0]))) # z = sin(lat)
x1 = (x_p1, y_p1, z_p1)
x_p2 = Decimal(cos(math.radians(p2[1]))*cos(math.radians(p2[0]))) # x = cos(lon)*cos(lat)
y_p2 = Decimal(sin(math.radians(p2[1]))*cos(math.radians(p2[0]))) # y = sin(lon)*cos(lat)
z_p2 = Decimal(sin(math.radians(p2[0]))) # z = sin(lat)
x2 = (x_p2, y_p2, z_p2)
'''
2. Convert the radii r1 and r2 (which are measured along the sphere) to angles along the sphere.
By definition, one nautical mile (NM) is 1/60 degree of arc (which is pi/180 * 1/60 = 0.0002908888 radians).
'''
r1 = Decimal(math.radians((r1_meter/1852) / 60)) # r1_meter/1852 converts meter to Nautical mile.
r2 = Decimal(math.radians((r2_meter/1852) / 60))
'''
3. The geodesic circle of radius r1 around x1 is the intersection of the earth's surface with an Euclidean sphere
of radius sin(r1) centered at cos(r1)*x1.
4. The plane determined by the intersection of the sphere of radius sin(r1) around cos(r1)*x1 and the earth's surface
is perpendicular to x1 and passes through the point cos(r1)x1, whence its equation is x.x1 = cos(r1)
(the "." represents the usual dot product); likewise for the other plane. There will be a unique point x0 on the
intersection of those two planes that is a linear combination of x1 and x2. Writing x0 = ax1 + b*x2 the two planar
equations are;
cos(r1) = x.x1 = (a*x1 + b*x2).x1 = a + b*(x2.x1)
cos(r2) = x.x2 = (a*x1 + b*x2).x2 = a*(x1.x2) + b
Using the fact that x2.x1 = x1.x2, which I shall write as q, the solution (if it exists) is given by
a = (cos(r1) - cos(r2)*q) / (1 - q^2),
b = (cos(r2) - cos(r1)*q) / (1 - q^2).
'''
q = Decimal(np.dot(x1, x2))
if q**2 != 1 :
a = (Decimal(cos(r1)) - Decimal(cos(r2))*q) / (1 - q**2)
b = (Decimal(cos(r2)) - Decimal(cos(r1))*q) / (1 - q**2)
'''
5. Now all other points on the line of intersection of the two planes differ from x0 by some multiple of a vector
n which is mutually perpendicular to both planes. The cross product n = x1~Cross~x2 does the job provided n is
nonzero: once again, this means that x1 and x2 are neither coincident nor diametrically opposite. (We need to
take care to compute the cross product with high precision, because it involves subtractions with a lot of
cancellation when x1 and x2 are close to each other.)
'''
n = np.cross(x1, x2)
'''
6. Therefore, we seek up to two points of the form x0 + t*n which lie on the earth's surface: that is, their length
equals 1. Equivalently, their squared length is 1:
1 = squared length = (x0 + t*n).(x0 + t*n) = x0.x0 + 2t*x0.n + t^2*n.n = x0.x0 + t^2*n.n
'''
x0_1 = [a*f for f in x1]
x0_2 = [b*f for f in x2]
x0 = [sum(f) for f in zip(x0_1, x0_2)]
'''
The term with x0.n disappears because x0 (being a linear combination of x1 and x2) is perpendicular to n.
The two solutions easily are t = sqrt((1 - x0.x0)/n.n) and its negative. Once again high precision
is called for, because when x1 and x2 are close, x0.x0 is very close to 1, leading to some loss of
floating point precision.
'''
if (np.dot(x0, x0) <= 1) & (np.dot(n,n) != 0): # This is to secure that (1 - np.dot(x0, x0)) / np.dot(n,n) > 0
t = Decimal(sqrt((1 - np.dot(x0, x0)) / np.dot(n,n)))
t1 = t
t2 = -t
i1 = x0 + t1*n
i2 = x0 + t2*n
'''
7. Finally, we may convert these solutions back to (lat, lon) by converting geocentric (x,y,z) to geographic
coordinates. For the longitude, use the generalized arctangent returning values in the range -180 to 180
degrees (in computing applications, this function takes both x and y as arguments rather than just the
ratio y/x; it is sometimes called "ATan2").
'''
i1_lat = math.degrees( math.asin(i1[2]))
i1_lon = math.degrees( math.atan2(i1[1], i1[0] ) )
ip1 = (i1_lat, i1_lon)
i2_lat = math.degrees( math.asin(i2[2]))
i2_lon = math.degrees( math.atan2(i2[1], i2[0] ) )
ip2 = (i2_lat, i2_lon)
return [ip1, ip2]
elif (np.dot(n,n) == 0):
return("The centers of the circles can be neither the same point nor antipodal points.")
else:
return("The circles do not intersect")
else:
return("The centers of the circles can be neither the same point nor antipodal points.")
'''
Example: The output of below is [(36.989311051533505, -88.15142628069133), (38.2383796094578, -92.39048549120287)]
intersection_points = intersection((37.673442, -90.234036), 107.5*1852, (36.109997, -90.953669), 145*1852)
print(intersection_points)
'''
Any feedback is appreciated.
All my searching comes up with more general arc/sin/cos usage or shooting to the mouse position.
I am looking to aim and fire a projectile with the keyboard and have done a lot of it from scratch, as a noob in a web class doing a project, but I am stuck on this. My current math got me to this mess in firing the shot in the direction the line is currently pointing... (code names cleaned for readability):
this.x = x + len * Math.cos(angle);
this.y = y + len * Math.sin(angle);
this.xmov = -((x + len * Math.cos(angle)) - x) / ((y + len * Math.sin(angle)) - y);
this.ymov = ((y + len * Math.sin(angle)) - y) / ((x + len * Math.cos(angle)) - x);
if (Math.abs(this.xmov) > Math.abs(this.ymov)) {
this.xmove = (this.xmov * Math.abs(this.ymov));
} else {
this.xmove = this.xmov;
}
if (Math.abs(this.ymov) > Math.abs(this.xmov)) {
this.ymove = (this.xmov * this.ymov);
} else {
this.ymove = this.ymov;
}
(And here is the full thing http://jsbin.com/ximatoq/edit. A and D to turn, S to fire (on release). Can also hold S while turning.)
... but, you'll see that it only works for 3/8's of it. What is the math to make this fire from a complete circle?
Use this as shoot function:
this.shoot = function() {
if (this.fire > 0) {
this.x = P1gun.x2;
this.y = P1gun.y2;
this.xmove = (P1gun.x2 - P1gun.x)/100;
this.ymove = (P1gun.y2 - P1gun.y)/100;
this.fire = 0;
this.firetravel = 1;
}
}
The /100 can be removed, but you have to reduce the projectile speed.
If you want to shoot gun2 change the P1gun to P2gun.
Normalising a vector.
To control the speed of something using a vector, first make the length of the vector 1 unit long (one pixel). This is commonly called normalising the vector, and sometimes it's called the unit vector. Then you can multiply that vector by any number to get the desired speed.
To normalise a vector first calculate its length, then divide it by that value.
function normalizeVector(v){
var len = Math.sqrt(v.x * v.x + v.y * v.y);
v.x /= len;
v.y /= len;
return v;
}
Trig
When you use trig to create a vector it is also a unit vector and does not need to be normalised.
function directioToUnitVector(angle){ // angle in radians
return {
x : cos(angle),
y : sin(angle)
}
Why normalise
Many many reasons, you build almost everything from unit vectors.
One example, if you have two points and want to move from one to the next at a speed of 10 pixels per second with a frame rate of 60frame per second.
var p1 = {};
var p2 = {};
p1.x = ? // the two points
p1.y = ?
p2.x = ?
p2.y = ?
// create a vector from p1 to p2
var v = {}
v.x = p2.x -p1.x;
v.y = p2.y -p1.y;
// Normalize the vector
normalizeVector(v);
var frameRate = 1/60; // 60 frames per second
var speed = 10; // ten pixels per second
function update(){
// scale vec to the speed you want. keeping the vec as a unit vec mean
// you can also change the speed, or use the time for even more precise
// speed control.
p1.x += v.x * (speed * frameRate);
p1.y += v.y * (speed * frameRate);
// draw the moving object at p1
requestAnimationFrame(update)
}
NOTE when normalizing you may get a vector that has no length. If your code is likely to create such a vector you need to check for the zero length and take appropriate action. Javascript does not throw an error when you divide by zero, but will return Infinity, with very strange results to your animations.
We have two circles, Circle1 & Circle2, Circle2 is static and Circle1 is moving.
Circle1 = C1, Circle2 = C2.
C1 has a velocity and direction which will cause it to touch C2 at one point.
v is a vector describing C1's velocity.
d is the distance from the center point of C1 to the center point of C2.
We also know the radius of both circles.
Frame 1:
C1 has not yet collided with C2, but as we can see it will in the next frame do so.
Frame 2:
C1 is now intersecting with C2.
Frame 2 (after calculation):
C1 has now been positioned at the point where it first touched C2.
So the question is, how can we calculate (preferably in js) the point along v where C1 should stop?
Current code:
// x^2 + B * x + C = 0
// x= v'
// B = 2 * (d.x * v.x + d.y*v.y)/Math.sqrt(v.x*v.x + v.y*v.y)
// C = (d.length()^2 - rs^2)
// get distance from ri to ri2 as a vector.
var d = new Vector(ri2.x - ri.x, ri2.y - ri.y);
// get sum of radiuses.
var rs = ri.r + ri2.r;
var A = 1;
var B = 2 * (d.x * v.x + d.y*v.y)/Math.sqrt(v.x*v.x + v.y*v.y);
var C = (d.length()^2 - rs^2);
var x1 = (-B + Math.sqrt(Math.pow(B, 2) - 4 * A * C)) / 2 * A;
var x1 = (-B - Math.sqrt(Math.pow(B, 2) - 4 * A * C)) / 2 * A;
// and then we get the lowest positive of x1 & x2.
Center-center distance in the touch moment is
R12 = R1 + R2
So using cosine theorem:
R122 = v'2 + d2 - 2*d*v'*Cos(dv)
Solve this quadratic equation against v' and get smaller positive solution value (if 2 cases exist).
You can find Cos(dv) through scalar product of vectors d and v
Cos(dv) = d * v / (|d||v|)
So final quadratic equation is
v'2 - v' * 2 * (d * v) / |v| + (d2 - R122) = 0
for standart form
x^2 + B * x + C = 0
x= v'
B = -2 * (d.x * v.x + d.y*v.y)/Sqrt(v.x*v.x + v.y*v.y)
C = (d^2 - R12^2)
Check for simple case: circle radius 2 centered at (0,0), moving right (v = (10,0)); static circle radius 3 centered (6,3). Result should be v'=2
B = -2*(6*10+3*0)/10= -12
C=45-(2+3)^2=20
Determinant = B*B - 4*C = 144-80 = 64
v'= (12 +- 8)/2
smaller value v'=2
You need to solve a few steps
Set up equation of motion and impose criteria that circles touch
| P1 + v * t - P2 | = R1 + R1
where P1 and P2 are the initial positions of the circle 1 and 2, respectively, and t is time.
Square the equation and solve for the intersection time using the quadratic equation
Use intersection time to solve for the intersection location of circle 1
Once you have where circle 1 is at the moment of intersection, and since circle 2 is static, solve circle-circle intersection problem
for each x,y on your V line
calculate the distance between the centers of C1 and C2.
it the distance equals to r1+r2 then the circles touch eternally.
P2(x2,y2) is center of C2
P1(x1,y1) is calculated center for C1 moving on V
P1P2 distance is:
SQuare Root of: (x2-x1)^2+(y2-y1)^2
once this equals r1+r2 you should stop
Distnace function for javascript is:
var d = Math.sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
or if you wish:
var dist = Math.sqrt( Math.pow((x1-x2), 2) + Math.pow((y1-y2), 2) );