Find intersection coordinates of two circles on earth? - javascript

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.

Related

For the point inside circle, find in which quarter it is?

I researched google but couldn't find the keywords for search. So I ask here if my algorithm and code is efficient?
http://sketchtoy.com/66429941 (algorithm)
The algoritm is: I have four points which are: north, east, south and west of circle. I check 4 distances (distanceToNorth, distanceToEast, distanceToSouth, distanceToWest). And I find minimum of them so that is the quarter.
Here is the code but it does not seem efficient for me.
(firstQuarter is North, secondQuarter is East and so on..
note: assume that mousemove is inside the circle.
var firstQuarterX = centerX;
var firstQuarterY = centerY - radius;
var secondQuarterX = centerX + radius;
var secondQuarterY = centerY;
var thirdQuarterX = centerX;
var thirdQuarterY = centerY + radius;
var fourthQuarterX = centerX - radius;
var fourthQuarterY = centerY;
var distanceToFirst = Math.sqrt(Math.pow(x-firstQuarterX, 2) + Math.pow(y-firstQuarterY, 2));
var distanceToSecond = Math.sqrt(Math.pow(x-secondQuarterX, 2) + Math.pow(y-secondQuarterY, 2));
var distanceToThird = Math.sqrt(Math.pow(x-thirdQuarterX, 2) + Math.pow(y-thirdQuarterY, 2));
var distanceToFourth = Math.sqrt(Math.pow(x-fourthQuarterX, 2) + Math.pow(y-fourthQuarterY, 2));
var min = Math.min(distanceToFirst, distanceToSecond, distanceToThird, distanceToFourth);
var numbers = [distanceToFirst, distanceToSecond, distanceToThird, distanceToFourth];
var index = numbers.indexOf(min); // it will give 0 or 1 or 2 or 3
var quarter = index + 1;
Observe that the boundaries between your quarters lie along the lines with equations y = x and y = -x, relative to an origin at the center of the circle. You can use those to evaluate which quarter each point falls in.
If your point is (x, y), then its coordinates relative to the center of the circle are xRelative = x - centerX and yRelative = y - centerY. Then
your point is in the first (south in your code) quarter if yRelative < 0 and Math.abs(xRelative) < -yRelative
your point is in the second (east) quarter if xRelative > 0 and Math.abs(yRelative) < xRelative
your point is in the third (north) quarter if yRelative > 0 and Math.abs(xRelative) < yRelative
your point is in the fourth (west) quarter if xRelative < 0 and Math.abs(yRelative) < -xRelative
I leave it to you to determine to which quarter to assign points that fall exactly on a boundary. Also, you can implement a little decision tree based on those criteria if you prefer; that should be a little more efficient then testing each criterion in turn.
Not so sure but I think this might work. Math.atan2(CenterY - y, CenterX - x) * 180 / Math.PI gives the apparent angle between the points. Do the remaining math to figure out the quarter.
What about something like:
return x>centerX?(y>centerY?"Quad 2":"Quad 1"):(y>centerY?"Quad 3":"Quad 4");
Less graceful, more slim.
For more efficient algorithm, you can compute the quadrant just by analyzing the signs of dx + dy and dx - dy quantities (dx, dy being x, y minus centerX, centerY respectively) (I presume that as your animation shows, your quadrants are rotated by 45 degrees against 'standard' quadrants.

Calculating the point where two circles touch based on circle1's velocity

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) );

Which one of these azimuth values is the right one? And why?

First of all, let's make clear that I want the Azimuth on the surface of the Earth, i.e. the angle between two locations, for example New York and Moscow.
I am testing some azimuth calculations with my JS functions (shown below). For the points A(-170, -89) to B(10, 89), I get ~90º.
JS function for Azimuth on sphere (from Wikipedia)
var dLon = lon2 - lon1;
var y = Math.sin(dLon) * Math.cos(lat2);
var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
var angle = Math.atan2(y, x) * 180 / Math.PI;
JS function for Azimuth on oblate spheroid (from Wikipedia)
var dLon = lon2 - lon1;
var f = 1 / 298.257223563; /* Flattening for WGS 84 */
var b = (1 - f) * (1 - f);
var tanLat2 = Math.tan(lat2);
var y = Math.sin(dLon);
var x;
if (lat1 === 0) {
var x = b * tanLat2;
} else {
var a = f * (2 - f);
var tanLat1 = Math.tan(lat1);
var c = 1 + b * tanLat2 * tanLat2;
var d = 1 + b * tanLat1 * tanLat1;
var t = b * Math.tan(lat2) / Math.tan(lat1) + a * Math.sqrt(c / d);
var x = (t - Math.cos(dLon)) * Math.sin(lat1);
}
var angle = Math.atan2(y, x) * 180 / Math.PI;
In Calculator 2, I get 90º.
In PostGIS, I get 270º
In Calculator 1, I get 180º.
I know the Azimuth gets more and more distorted near the Poles, but that's exactly why I am testing at these spots. This variety of different solutions are confusing me. Could you please help me getting the right answer for this?
It depends on the reference used for azimuth, e.g. map-types use 0° for North and positive is clockwise, while math-types uses 0° for East and positive is anticlockwise.
The pair of coordinates A(-170, -89) and B(10, 89) are antipodes which are a special case for finding minimum distances and azimuths. Your question can be answered with a thought exercise.
First note that the half-circumferences of the earth are:
Equatorial: 20037.5085 km
Meridional (north-to-south): 20003.93 km
For a pair of antipodes on the north and south poles, there are an infinite number of azimuths, since the distance is the same along each longitude. (What direction would you go from the south pole to the north pole?)
For a pair of antipodes on the equator, the shortest distances are north or south, since it is slightly shorter along the meridional direction.
For any other pair of antipodes, it is the same answer as from the equator: north or south.
Update
To investigate the problem a bit more with the PostGIS SQL query:
SELECT ST_Distance(A, B), degrees(ST_Azimuth(A, B))
FROM (
SELECT 'POINT(-170 -89)'::geography A, 'POINT(10 89)'::geography B
) f;
With PostGIS 2.0 and 2.1, the incorrect results are:
st_distance | degrees
-----------------+------------------
20003900.583699 | 270.005278779849
But with PostGIS 2.2 (and PROJ 4.9.1), the correct results are now:
st_distance | degrees
------------------+---------
20003931.4586255 | 180

rotating a sphere a to b point on itself

Im trying to figure out how to rotate a sphere from point A on itself to point b on itself. I found some Unity3d code:
Quaternion rot = Quaternion.FromToRotation (pointA, pointB);
sphere.transform.rotation *= rot; //Multiply rotations to get the resultant rotation
via http://answers.unity3d.com/questions/21921/rotate-point-a-on-sphere-to-point-b-on-sphere.html but I can't figure out how to implement it in Three.js.
Here's my code:
var s = sphere mesh
var va = 1st start vector
var vb = 2nd end vector;
var qa = new THREE.Quaternion(va.x, va.y, va.z, 1);
var qb = new THREE.Quaternion(vb.x, vb.y, vb.z, 1);
var qc = new THREE.Quaternion();
THREE.Quaternion.slerp(qa, qb, qc, 1);
s.useQuaternion = true;
s.quaternion = qc;
Thanks!
Assume the sphere is centered at the origin and A and B are normalized (i.e. unit length). Then compute the cross product C = A×B. This is your vector representing your rotation axis. The angle for your rotation is given by θ = cos-1(A∙B) where A∙B is the dot product of the unit vectors A and B. Note that the angle in this case is typically in radians, not degrees.
If the sphere is centered at some point P not at the origin or its radius is not unit length, you will have to translate and scale A and B before derive the rotation. This looks like:
A' ← (A-P)/|A-P| // Normalized version of A
B' ← (B-P)/|B-P| // Normalized version of B
V ← A'×B' // Axis about which to rotate the system
θ ← cos-1(A'∙B') // Angle by which to rotate
You can then use V and θ as your arguments for constructing the quaternion you will use for defining your rotation. This rotation will be centered at the origin, so before applying it, translate by -P, then apply the rotation, and translate back by P.
One note: There may be a sign error in here. If it doesn't work, it's because the sign of the cross product doesn't match with the sign of the dot product. Just reverse the order of the arguments in the cross product to fix it if this is a problem.
var c = group.rotation.y;
var d = -b * (Math.PI / 180)%(2 * Math.PI);
var e = Math.PI / 2 * -1;
group.rotation.y = c % (2 * Math.PI);
group.rotation.x = a * (Math.PI / 180) % Math.PI;
group.rotation.y= d+e;
where a= latitude, b= longitude,group=Object3D(or sphere)

Find column, row on 2D isometric grid from x,y screen space coords (Convert equation to function)

I'm trying to find the row, column in a 2d isometric grid of a screen space point (x, y)
Now I pretty much know what I need to do which is find the length of the vectors in red in the pictures above and then compare it to the length of the vector that represent the bounds of the grid (which is represented by the black vectors)
Now I asked for help over at mathematics stack exchange to get the equation for figuring out what the parallel vectors are of a point x,y compared to the black boundary vectors. Link here Length of Perpendicular/Parallel Vectors
but im having trouble converting this to a function
Ideally i need enough of a function to get the length of both red vectors from three sets of points, the x,y of the end of the 2 black vectors and the point at the end of the red vectors.
Any language is fine but ideally javascript
What you need is a base transformation:
Suppose the coordinates of the first black vector are (x1, x2) and the coordinates of the second vector are (y1, y2).
Therefore, finding the red vectors that get at a point (z1, z2) is equivalent to solving the following linear system:
x1*r1 + y1*r2 = z1
x2*r1 + y2*r2 = z2
or in matrix form:
A x = b
/x1 y1\ |r1| = |z1|
\x2 y2/ |r2| |z2|
x = inverse(A)*b
For example, lets have the black vector be (2, 1) and (2, -1). The corresponding matrix A will be
2 2
1 -1
and its inverse will be
1/4 1/2
1/4 -1/2
So a point (x, y) in the original coordinates will be able to be represened in the alternate base, bia the following formula:
(x, y) = (1/4 * x + 1/2 * y)*(2,1) + (1/4 * x -1/2 * y)*(2, -1)
What exactly is the point of doing it like this? Any isometric grid you display usually contains cells of equal size, so you can skip all the vector math and simply do something like:
var xStep = 50,
yStep = 30, // roughly matches your image
pointX = 2*xStep,
pointY = 0;
Basically the points on any isometric grid fall onto the intersections of a non-isometric grid. Isometric grid controller:
screenPositionToIsoXY : function(o, w, h){
var sX = ((((o.x - this.canvas.xPosition) - this.screenOffsetX) / this.unitWidth ) * 2) >> 0,
sY = ((((o.y - this.canvas.yPosition) - this.screenOffsetY) / this.unitHeight) * 2) >> 0,
isoX = ((sX + sY - this.cols) / 2) >> 0,
isoY = (((-1 + this.cols) - (sX - sY)) / 2) >> 0;
// isoX = ((sX + sY) / isoGrid.width) - 1
// isoY = ((-2 + isoGrid.width) - sX - sY) / 2
return $.extend(o, {
isoX : Math.constrain(isoX, 0, this.cols - (w||0)),
isoY : Math.constrain(isoY, 0, this.rows - (h||0))
});
},
// ...
isoToUnitGrid : function(isoX, isoY){
var offset = this.grid.offset(),
isoX = $.uD(isoX) ? this.isoX : isoX,
isoY = $.uD(isoY) ? this.isoY : isoY;
return {
x : (offset.x + (this.grid.unitWidth / 2) * (this.grid.rows - this.isoWidth + isoX - isoY)) >> 0,
y : (offset.y + (this.grid.unitHeight / 2) * (isoX + isoY)) >> 0
};
},
Okay so with the help of other answers (sorry guys neither quite provided the answer i was after)
I present my function for finding the grid position on an iso 2d grid using a world x,y coordinate where the world x,y is an offset screen space coord.
WorldPosToGridPos: function(iPosX, iPosY){
var d = (this.mcBoundaryVectors.upper.x * this.mcBoundaryVectors.lower.y) - (this.mcBoundaryVectors.upper.y * this.mcBoundaryVectors.lower.x);
var a = ((iPosX * this.mcBoundaryVectors.lower.y) - (this.mcBoundaryVectors.lower.x * iPosY)) / d;
var b = ((this.mcBoundaryVectors.upper.x * iPosY) - (iPosX * this.mcBoundaryVectors.upper.y)) / d;
var cParaUpperVec = new Vector2(a * this.mcBoundaryVectors.upper.x, a * this.mcBoundaryVectors.upper.y);
var cParaLowerVec = new Vector2(b * this.mcBoundaryVectors.lower.x, b * this.mcBoundaryVectors.lower.y);
var iGridWidth = 40;
var iGridHeight = 40;
var iGridX = Math.floor((cParaLowerVec.length() / this.mcBoundaryVectors.lower.length()) * iGridWidth);
var iGridY = Math.floor((cParaUpperVec.length() / this.mcBoundaryVectors.upper.length()) * iGridHeight);
return {gridX: iGridX, gridY: iGridY};
},
The first line is best done once in an init function or similar to save doing the same calculation over and over, I just included it for completeness.
The mcBoundaryVectors are two vectors defining the outer limits of the x and y axis of the isometric grid (The black vectors shown in the picture above).
Hope this helps anyone else in the future

Categories