Calculating the Right Ascension and Declination of the Sun - javascript

I've been following this guide, porting it to javascript:
http://www.saao.ac.za/public-info/sun-moon-stars/sun-index/how-to-calculate-altaz/
Everything was going swimmingly up until 9.(right ascension) and 10.(declination). I can't recreate the answers they give for these.
(9) find alpha the right ascension of the sun:
(a) for cape town:
lambda = 326.186
epsilon = 23.4396
alpha = arctan (tan(lambda) x cos(epsilon)) // in same quadrant as lambda
// THEIR RESULT
alpha = 328.428
// MY RESULT
var DEGREES = function (val) {
return val / (Math.PI / 180);
};
var alpha = Math.atan(Math.tan(lambda) * Math.sin(epsilon));
alpha = 0.495;
alpha = DEGREES(0.495) = 28.39;
I also tried:
var alpha = Math.atan2(Math.tan(lambda) * Math.sin(epsilon), lambda);
alpha = DEGREES(result) = 1.321;
Not even close!
And 10(a), the declination
delta = arcsin (sin(lambda) x sin(epsilon))
// THEIR RESULT
(a) delta = -12.789
// MY RESULT
var result = Math.asin(Math.sin(eclipticLong) * Math.sin(obliq));
result = DEGREES(result);
result = -10.966;
As you can see I'm clutching at straws as I don't really have a clue about this. Any help would be much appreciated.

Well, the biggest issue I see is here:
alpha = arctan (tan(lambda) x cos(epsilon)) // in same quadrant as lambda
...
var alpha = Math.atan(Math.tan(lambda) * Math.sin(epsilon));
You went from using cosine to sine in the second expression.
Now, this on the face of it doesn't lead to the same result, so lets dig in a bit deeper. For clarity, I'm going to use these functions and constants:
var lambda = 326.186;
var epsilon = 23.4396;
function rad (v) { return v * Math.PI / 180 }
function deg (v) { return v * 180 / Math.PI }
Javascript math functions take a radial coordinate, so lets give this a try:
var result = deg(Math.atan(Math.tan(rad(lambda)) * Math.cos(rad(epsilon))));
console.log(result); // -.31.5717
Thanks to the magic of how degrees work, this is the same answer as 360 + -31.5717 = 328.428.

Related

are Triangles Similar?

You have two triangles a1 b1 c1 and a2 b2 c3 on a plane. Your task is to determine whether they are, i.e. if their corresponding angles have the same measurements.
coordinates is an
array []
let coord = [0, 0, 0, 1, 1, 0, 0, 0, 0, -3, -3, 0];
where a1 is (coord[0],coord[1]), b1 (coord[2],coord[3]) ...
let s = [0, 0, 0, 1, 1, 0, 0, 0, 0, -3, -3, 0]
function areTrianglesSimilar(c) {
let result = null
let line1 = (Math.abs(c[2]) - Math.abs(c[0])) + (Math.abs(c[3]) - Math.abs(c[1]))
let line2 = (Math.abs(c[4]) - Math.abs(c[0])) + (Math.abs(c[5]) - Math.abs(c[1]))
let line3 = Math.abs(Math.sqrt( Math.pow(line1, 2)+ Math.pow(line2, 2)))
console.log(line1, line2, line3)
let angle1 = Math.atan2(line1, line2) * 180 / Math.PI
let angle2 = Math.atan2(line1, line3) * 180 / Math.PI
let angle3 = 180 - (angle1 + angle2)
console.log(angle1, angle2, angle3)
let arr1 = []
arr1.push(angle1, angle2, angle3)
let line4 = (Math.abs(c[8]) - Math.abs(c[6])) + (Math.abs(c[9]) - Math.abs(c[7]))
let line5 = (Math.abs(c[10]) - Math.abs(c[0])) + (Math.abs(c[11]) - Math.abs(c[1]))
let line6 = Math.abs(Math.sqrt( Math.pow(line4, 2)+ Math.pow(line5, 2)))
console.log(line4, line5, line6)
let angle4 = Math.atan2(line4, line5) * 180 / Math.PI
let angle5 = Math.atan2(line4, line6) * 180 / Math.PI
let angle6 = 180 - (angle4 + angle5)
console.log(angle6, angle5, angle4)
if (arr1.includes(angle4) && arr1.includes(angle5) && arr1.includes(angle6)){
return result = true
} else return result = false
}
console.log(areTrianglesSimilar(s))
this was my try but did not pass all tests, any better idea?
Thanks to Mbo
function areTrianglesSimilar(c) {
let dx1 = c[2] - c[0];
let dy1 = c[3] - c[1];
let dx2 = c[4] - c[0];
let dy2 = c[5] - c[1];
let dx3 = c[4] - c[2];
let dy3 = c[5] - c[3];
let l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
let l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
let l3 = Math.sqrt(dx3 * dx3 + dy3 * dy3);
console.log(l1,l2,l3);
let angle12 = Math.acos((dx1 * dx2 + dy1 * dy2) / (l1 * l2));
let angle13 = Math.acos((dx1 * dx3 + dy1 * dy3) / (l1 * l3));
let angle23 = Math.acos((dx3 * dx2 + dy3 * dy2) / (l3 * l2));
console.log(angle12, angle13, angle23);
let dx4 = c[8] - c[6];
let dy4 = c[9] - c[7];
let dx5 = c[10] - c[6];
let dy5 = c[11] - c[7];
let dx6 = c[10] - c[8];
let dy6 = c[11] - c[9];
let l4 = Math.sqrt(dx4 * dx4 + dy4 * dy4);
let l5 = Math.sqrt(dx5 * dx5 + dy5 * dy5);
let l6 = Math.sqrt(dx6 * dx6 + dy6 * dy6);
console.log(l4,l5,l6);
let angle45 = Math.acos((dx4 * dx5 + dy4 * dy5) / (l4 * l5));
let angle46 = Math.acos((dx4 * dx6 + dy4 * dy6) / (l4 * l6));
let angle56 = Math.acos((dx6 * dx5 + dy6 * dy5) / (l6 * l5));
console.log(angle45, angle46, angle56);
if (angle12 == angle45 && angle13 == angle46){
console.log('result'+':'+ true);
} else console.log("result" + ":" + false);
}
let coordinates = [3, 4, 4, 7, 6, 1, -2, -1, 0, 5, 4, -7];
console.log(areTrianglesSimilar(coordinates))
Your calculation is completely wrong. Dot product approach:
dx1 = c[2] - c[0]
dy1 = c[3] - c[1]
dx2 = c[4] - c[0]
dy2 = c[5] - c[1]
dx3 = c[4] - c[2]
dy3 = c[5] - c[3]
l1 = Math.sqrt(dx1*dx1+dy1*dy1)
l2 = Math.sqrt(dx2*dx2+dy2*dy2)
l3 = Math.sqrt(dx3*dx3+dy3*dy3)
angle12 = Math.acos((dx1*dx2+dy1*dy2)/(l1*l2)
and similar for angle13, and later you need to compare only two angles for equality
if angle12 == angle45 and angle13 == angle46 ...
or use some epsylon value to avoid floating calculation errors
if abs(angle12 -angle45) < 0.0000001 ...
Moreover, you can avoid angles and compare side length ratios
if l1/l4==l2/l5 and l1/l4==l3/l6...
There are quite a number of errors here. Aside from these, you should probably consider refactoring the code into separate functions which encapsulate commonly performed calculations. This will cut down on needless repetition and make copy-paste typos less possible. It will also make the code a little more self-documenting, which allows human beings to understand what you're doing better.
Assuming you want to determine the angles of the triangles and compare them (but you could also use side length ratios as #MBo pointed out), the general approach I would follow is this:
Write a function to convert the coordinates array into a pair of Triangle objects, where a Triangle is a three-tuple of Point objects, defined like this:
type Triangle = [Point, Point, Point];
interface Point { x: number, y: number };
function toTrianges(coords: number[]): [Triangle, Triangle] {
// implement this
}
Write a function that takes three Points, A, B, and C, and returns the (absolute value of the) measure of angle ∡ABC (with B as the vertex) in, say, degrees:
function measureAngleABC(a: Point, b: Point, c: Point): number {
// implement this
}
In order to do that, you might want to write functions that turn two Points A and B and produces the Vector from A to B, and that manipulate vectors:
type Vector = Point;
function vector(a: Point, b: Point): Vector { /* impl */ }
function vectorLength(v: Vector): number { /* impl */ }
function dotProduct(v1: Vector, v2: Vector): number { /* impl */ }
Note that the (unsigned) angle between two vectors can be determined by examining their lengths and their dot product.
Once you have these, you should be able to turn a Triangle into a (sorted) triplet of its (unsigned) angles:
type TriangleAngles = [number, number, number];
function angles(triangle: Triangle): TriangleAngles { /* impl * }
And finally, write a function that compares two TriangleAngles for near-equality. Not actual equality using ===, which is fraught with troubles. Since floating-point numbers do not have infinite precision, two different calculations that should yield the same quantity might actually produce two different floating-point results. The famous example is that 0.1 + 0.2 === 0.3 is false. When you compare two TriangleAngles, you need to decide how close is "close enough" to call two triangles similar:
function areNearlyEqual(ta1: TriangleAngles, ta2: TriangleAngles): boolean {
// impl here
}
I'm not going to write out how to implement these, since this looks like an exercise that benefits you most if you actually do it, not if someone does it for you.
In any case, here are the errors I see in your code:
The line (Math.abs(c[10]) - Math.abs(c[0])) + (Math.abs(c[11]) - Math.abs(c[1])) looks like a typo with indices, as you are seemingly comparing a point from one triangle with a point on a different triangle. This sort of typo would be much less likely if you refactor so as to move from an array of numbers to something like a pair of Triangles.
All code of the form Math.abs(c[k]) for some index k is highly suspect. This treats c[k] === 100 identically to c[k] === -100. If you take a triangle and flip the sign of the x or y coordinate of one of its vertices, you are almost certainly going to change the shape of the triangle by reflecting that vertex across the x or y axis:
If your code can't tell the difference between those two triangles, it's not going to be able to accurately determine if two triangles are similar or not.
The line let line1 = (Math.abs(c[2]) - Math.abs(c[0])) + (Math.abs(c[3]) - Math.abs(c[1])) and its brethren seem to looking at one of the sides of one of the triangles and adding the x component of its length to the y component of its length to get a single number. This doesn't represent much of anything that I can think of. The vector of x-component-of-length and y-component-of-length are important, but when you just add the components together you are throwing away information you need. You can verify this for yourself by coming up with a triangle where swapping c[2] and c[3] will change its shape, but the above code will not see a difference.
The line let line3 = Math.abs(Math.sqrt( Math.pow(line1, 2)+ Math.pow(line2, 2))) seems to assume that line1 and line2 represent the lengths of two sides of a right triangle and line3 is the length of the hypotenuse. But unless your two sides are really perpendicular to each other, this will not be true.
The line let angle2 = Math.atan2(line1, line3) * 180 / Math.PI is calculating an angle, but what angle? You can only use the arctangent to get an angle from the opposite and adjacent sides of a right triangle. But there might be no right triangles here, and since line3 was earlier assumed to be the hypotenuse of a right triangle where one of the sides was line1, there's no way line3 is now one of the perpendicular legs.
Um, I think I have to stop here. Suffice it to say that I would be very surprised if you could get this algorithm working by tweaking it. I'd strongly recommend starting over with reusable functions that perform well-defined calculations.
Good luck.

Collision detection: Separating Axis Theorem - Circle versus Polygon

I've been trying to implement collision detection between circles and polygons based on Randy Gaul's C++ Impulse Engine, following the code pretty closely, but the algorithm never returns true.
Here's the JSFiddle. (the bodies are rendered using the HTML5 Canvas API for convenience)
A snippet of the code (just collision detection):
const circPoly = (a, b) => {
let data = {},
center = a.pos;
data.contacts = [];
center = b.mat.clone().trans().mult(center.clone().sub(b.pos));
let sep = -Number.MAX_VALUE,
faceNorm = 0;
for (let i = 0; i < b.verts2.length; ++i) {
let sep2 = b.norms[i].dot(center.clone().sub(b.verts2[i]));
if (sep2 > a.radius) return data;
if (sep2 > sep) { sep = sep2; faceNorm = i; }
}
let v1 = b.verts2[faceNorm],
v2 = b.verts2[faceNorm + 1 < b.verts2.length ? faceNorm + 1 : 0];
if (sep < 0.0001) {
data.depth = a.radius;
data.norm = b.mat.clone().mult(b.norms[faceNorm]).neg();
data.contacts[0] = data.norm.clone().vmult(a.pos.clone().sadd(a.radius));
return data;
}
let dot1 = center.clone().sub(v1).dot(v2.clone().sub(v1)),
dot2 = center.clone().sub(v2).dot(v1.clone().sub(v2));
data.depth = a.radius - sep;
if (dot1 <= 0) {
if (center.dist2(v1) > a.radius * a.radius) return data;
let norm = v1.clone().sub(center);
norm = b.mat.clone().mult(norm);
norm.norm();
data.norm = norm;
v1 = b.mat.clone().mult(v1.clone().add(b.pos));
data.contacts[0] = v1;
} else if (dot2 <= 0) {
if (center.dist2(v2) > a.radius * a.radius) return data;
let norm = v2.clone().sub(center);
norm = b.mat.clone().mult(norm);
norm.norm();
data.norm = norm;
v2 = b.mat.clone().mult(v2.clone().add(b.pos));
data.contacts[0] = v2;
} else {
let norm = b.norms[faceNorm];
if (center.clone().sub(v1).dot(norm) > a.radius) return data;
norm = b.mat.clone().mult(norm);
data.norm = norm.clone().neg();
data.contacts[0] = data.norm.clone().vmult(a.pos.clone().sadd(a.radius));
}
return data;
};
Note that b.verts2 refers to the polygon's vertices in real world coordinates.
I know for a fact that there are no problems with the Vector class but as I don't exactly have very much experience with transformation matrices, that class could be the root of these errors, although the code for it is pretty much entirely derived from the Impulse Engine as well, so it should work. As mentioned before, the algorithm always returns false, even when a collision really has occurred. What am I doing wrong here? I tried taking out the early returns, but that just returns weird results like contact points with negative coordinates which obviously is not quite correct.
EDIT: Modified my vector class's perpendicular function to work the same way as the Impulse Engine's (both ways are right, but I think one is clockwise and the other one counterclockwise -- I also modified my vertices to reflect the counterclockwise-ness). Unfortunately, it still fails the test.
https://jsfiddle.net/khanfused/tv359kgL/4/
Well the are many problems and I really dont understand what you are trying to do as it seem overly complex. Eg why does matrix have trans??? and why are you using the Y up screen as the coordinate system for the transform??? (rhetorical)
In the first loop.
The first is that you are testing the distance of the normal vectors
of each vert, should be testing the vert position.
Also you are finding the distance using the vec.dot function that
returns the square of the distance. But you test for the radius, you
should be testing for if(sep2 < radius * radius)
And you have the comparison the wrong way around you should be
testing if less than radius squared (not greater than)
Then when you do detect a vert within the radius you return the data
object but forget to put the vert that was found inside the circle on
the data.contacts array.
I am not sure what the intention of keeping the index of the most
distant vect is but then the rest of the function make zero sense to
me???? :( and I have tried to understand it.
All you need to do is
A check if any verts on the poly are closer than radius, if so then you have a intercept (or is completely inside)
Then you need to check the distance of each line segment
Can be done for each line segment with the following if you dont need the intercepts (or below that if you need intercepts) only use one or the other.
// circle is a point {x:?,y:?}
// radius = is the you know what
// p1,p2 are the start and end points of a line
checkLineCircle = function(circle,radius,p1,p2){
var v1 = {};
var v2 = {};
var v3 = {};
var u;
// get dist to end of line
v2.x = circle.x - p1.x;
v2.y = circle.y - p1.y;
// check if end points are inside the circle
if( Math.min(
Math.hypot(p2.x - circle.x, p2.y - circle.y),
Math.hypot(v2.x, v2.y)
) <= radius){
return true;
}
// get the line as a vector
v1.x = p2.x - p1.x;
v1.y = p2.y - p1.y;
// get the unit distance of the closest point on the line
u = (v2.x * v1.x + v2.y * v1.y)/(v1.y * v1.y + v1.x * v1.x);
// is this on the line segment
if(u >= 0 && u <= 1){
v3.x = v1.x * u; // get the point on the line segment
v3.y = v1.y * u;
// get the distance to that point and return true or false depending on the
// it being inside the circle
return (Math.hypot(v3.y - v2.y, v3.x - v2.x) <= radius);
}
return false; // no intercept
}
Do that for each line.To save time transform the circle center to the polygon local, rather than transform each point on the poly.
If you need the points of intercept then use the following function
// p1,p2 are the start and end points of a line
// returns an array empty if no points found or one or two points depending on the number of intercepts found
// If two points found the first point in the array is the point closest to the line start (p1)
function circleLineIntercept(circle,radius,p1,p2){
var v1 = {};
var v2 = {};
var ret = [];
var u1,u2,b,c,d;
// line as vector
v1.x = p2.x - p1.x;
v1.y = p2.y - p1.y;
// vector to circle center
v2.x = p1.x - circle.x;
v2.y = p1.y - circle.y;
// dot of line and circle
b = (v1.x * v2.x + v1.y * v2.y) * -2;
// length of line squared * 2
c = 2 * (v1.x * v1.x + v1.y * v1.y);
// some math to solve the two triangles made by the intercept points, the circle center and the perpendicular line to the line.
d = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - radius * radius));
// will give a NaN if no solution
if(isNaN(d)){ // no intercept
return ret;
}
// get the unit distance of each intercept to the line
u1 = (b - d) / c;
u2 = (b + d) / c;
// check the intercept is on the line segment
if(u1 <= 1 && u1 >= 0){
ret.push({x:line.p1.x + v1.x * u1, y : line.p1.y + v1.y * u1 });
}
// check the intercept is on the line segment
if(u2 <= 1 && u2 >= 0){
ret.push({x:line.p1.x + v1.x * u2, y : line.p1.y + v1.y * u2});
}
return ret;
}
I will leave it up to you to do the polygon iteration.

Calculate current position between two points based on distance left

Ok, I have two positions in 3d space:
var fromX = 1,
fromY = 2,
fromZ = 3,
toX = 15,
toY = 16,
toZ = 17;
Then I need to calculate the current position, when someone/something is moving in a straight line from the from-coordinates, to the to-coordinates. I know the distance left is 2, what would be the formula for calculating the current position?
I guess this is more of a math question than a javascript question, but it is for a javascript application, so I'm hoping that is not a problem.
Given two points, fromPt and toPt, the distance between two points can easily be calculated:
distanceX = Math.pow(fromPt.x - toPt.x, 2)
distanceY = Math.pow(fromPt.y - toPt.y, 2)
distanceZ = Math.pow(fromPt.z - toPt.z, 2)
total_distance = Math.sqrt(distanceX + distanceY + distanceZ)
and now finding the correct point along the line is just a case of correct interpolation :)
newPt = {}
newPt.x = fromPt.x + ((toPt.x - fromPt.x) * (wantedDistance / total_distance))
newPt.y = fromPt.y + ((toPt.y - fromPt.y) * (wantedDistance / total_distance))
newPt.z = fromPt.z + ((toPt.z - fromPt.z) * (wantedDistance / total_distance))
There are already 2 answers with the correct algorithm, this one's no different, just a bit neater.
// Distance between two points is the square root of the sum
// of the squares of the differences
function get3dDistance(startCoords, endCoords) {
var dx = Math.pow((startCoords[0] - endCoords[0]), 2);
var dy = Math.pow((startCoords[1] - endCoords[1]), 2);
var dz = Math.pow((startCoords[2] - endCoords[2]), 2);
return Math.sqrt(dx + dy + dz);
}
// The coordinates of a point some distance from the end is
// proportional to the distance left and total distance.
function getCoordsFromDistanceLeft(startCoords, endCoords, distanceLeft) {
var distance = get3dDistance(startCoords, endCoords);
var f = (distance - distanceLeft)/distance;
return [startCoords[0] + f*(endCoords[0] - startCoords[0]),
startCoords[1] + f*(endCoords[1] - startCoords[1]),
startCoords[2] + f*(endCoords[2] - startCoords[2])];
}
// Test case
var start = [1,2,3];
var end = [15,16,17];
var distanceLeft = 2;
// Distance between the two points
var dist = get3dDistance(start, end)
document.write('distance: ' + dist + '<br>');
// distance: 24.24871130596428
// Get the coords
var x = getCoordsFromDistanceLeft(start, end, distanceLeft);
document.write('x: ' + x + ' is ' + distanceLeft + ' to end<br>');
// x: 13.845299461620748,14.845299461620748,15.845299461620748 is 2 to end
document.write('From x to end: ' + get3dDistance(x, end) + '<br>');
// From x to end: 2.0000000000000013
Salix alba has introduced Math.hypot, which is interesting but since it's a new feature in ECMAScript 2015 it would be wise to include a polyfill.
You need to use 3D Pythagoras to find the distance between two points. If x1,y1,z1 and x2,y2,z2 are your points then the distance is sqrt((x1-x2)^2+(y1-y2)^2+(z1-z2)^2). There are several ways of finding the desired point. We can find the distance from the starting point to the ending point and then calculate the proportion of that distance which will give 2 as a result using linear interpolation.
var fromX = 1,
fromY = 2,
fromZ = 3,
toX = 15,
toY = 16,
toZ = 17;
// find the difference
var dx = toX-fromX, dy = toY-fromY, dz=toZ-fromZ;
// find the total length
var dist = Math.hypot(dx,dy,dz);
// find the proportion of this length
var lambda = (dist-2.0) / dist;
// do the linear interpolation
var x = fromX + lambda * dx,
y = fromY + lambda * dy,
z = fromZ + lambda * dz;
console.log(x,y,z);
// Just to check
var dx2 = toX-x, dy2 = toY-y, dz2=toZ-z;
var dist2 = Math.hypot(dx2,dy2,dz2);
console.log(dist2);
We get the result 13.845299461620748 14.845299461620748 15.845299461620748 and the final distance is 2.0000000000000013.
Note I've uses Math.hypot this is a new feature which works in Chrome/firefox/opera but not in IE. There is a work-around to enable it in other browsers if needed. You just use Math.sqrt(dx*dx+dy*dy+dz*dz) instead.

Rotating a clock hand in javascript

I am learning to make a clock using raphael js,
I am using this tutorial to get me started http://www.tuttoaster.com/creating-a-clock-animation-without-css3/
When this is diplayed the second hand doesnt move one second per second. I know one second is 6 degrees, it moves around 45 degrees though!
If someone could please explain what he has done wrong and how to make the hands rotate at appropriate angles that would be great. I am a beginner so plain english please :)
The code is as follows.
window.onload = function(){
var canvas = Raphael("pane",0,0,500,500);
canvas.circle(200,150,100).attr("stroke-width",2);
canvas.circle(200,150,3).attr("fill","#000");
var angleplus = 360,rad = Math.PI / 180,
cx = 200,cy =150 ,r = 90,
startangle = -90,angle=30,x,y, endangle;
for(i=1;i<13;i++)
{
endangle = startangle + angle ;
x = cx + r * Math.cos(endangle * rad);
y = cy + r * Math.sin(endangle * rad);
canvas.text(x,y,i+"");
startangle = endangle;
}
var hand = canvas.path("M200 70L200 150").attr("stroke-width",1);
var minute_hand = canvas.path("M200 100L200 150").attr("stroke-width",2);
var hour_hand = canvas.path("M200 110L200 150").attr("stroke-width",3);
var time = new Date();
angle = time.getSeconds() * 6;
minute_hand.rotate(6 * time.getMinutes(),200,150);
var hr = time.getHours();
if(hr>12)
hr = hr -11;
hour_hand.rotate(30 * hr,200,150);
var minute_angle= 6 + time.getMinutes()*6,hour_angle=0.5+
time.getMinutes()*30;
setInterval(function(){
angle = angle + 6;
if(angle>=360)
{
angle=0;
minute_hand.rotate(minute_angle,200,150);
minute_angle = minute_angle + 6;
hour_hand.rotate(hour_angle,200,150);
hour_angle = hour_angle + 0.5;
}
if(minute_angle>=360)
{
minute_angle=0;
}
hand.rotate(angle,200,150);
},1000);
hand.rotate(6,200,150);
Bernard, you don't need to rotate by the variable angle since you're simply rotating by 6 degrees every second regardless of how many seconds have elapsed.
http://jsbin.com/domoqojipe/1/
So you want to speed up the clock speed by twenty?
It's a long shot, but try changing the 1000 at the bottom to 50. Because 1000 divided by 20 equals 50.
Try that and see if it works...

Efficiently calculate angle (and direction) between 3 points

Using this thread (Calculate the shortest way to rotate, right or left?) I created a function that returns a positive or negative rotation angle. Problem is, it seems really inefficient. Can anyone help? (for clarity purposes, i've spelled out the var names and created helper functions.)
function getRotation(center, target, heading) {
var north = new point(center.x(), center.y() + 10);
/* I tried to calculate these seperately so I dont have to calc the distance so many tiems but couldnt come up with anything...
var distCenterToHeading = getDist(center, heading);
var distCenterToTarget = getDist(center, target);
var distTargetToHeading = getDist(target, heading);
var distCenterToNorth = 10;
var distNorthToHeading = getDist(north, heading);
var distNorthToTarget = getDist(north, target);
*/
var angHeadingToTarget = getAngle(center, heading, target);
var angNorthToHeading = getAngle(center, north, heading);
var angNorthToTarget = getAngle(center, north, target);
if (((angNorthToHeading - angNorthToTarget) + 360) % 360 > 180) {
return angHeadingToTarget;
} else {
return -angHeadingToTarget;
}
}
function getAngle(Center, heading, target) {
var p12 = getDist(Center, heading);
var p13 = getDist(Center, target);
var p23 = getDist(heading, target);
return Math.acos((sq(p12) + sq(p13) - sq(p23)) / (2 * p12 * p13)) * 180 / Math.PI;
}
function point(iX, iY) {
var _X=iX;
var _Y=iY;
this.x = function () { return _X; };
this.y = function () { return _Y; };
this.setX = function (iX) {
_X = iX;
}
this.setY = function (iY) {
_Y = iY;
}
}
function getDist(p1, p2) { return Math.sqrt(Math.pow(p1.x() - p2.x(), 2) + Math.pow(p1.y() - p2.y(), 2)); }
function sq(num) { return Math.pow(num, 2); }
I will potentially need to get the rotation many times, and i've heard that the sqrt function is expensive... In the above example, it's being hit 9 times, where I can see that at least three of them are redundant (center to heading, center to target and center to north).
Is there a better way of doing this all-together?
Thanks in advance,
Dave
EDIT: #Mike Dunlavey
I've been messing around with atan2 but I cant seem to get anything that works. Maybe i'm just missing something simple, but could you explain? Thanks.
1) In your getDist function, it might be faster to just square the deltas, rather than calling Math.pow.
It's possible Math.pow treats integer powers as a special case, but in general it has to get wrapped up in log and exp, which you don't need.
2) To get the direction from one point to another, you can just use the Math.atan2 function.
No need to deal with distances.

Categories