I wish to create the compass / arrow exactly like the one we see in AroundMe Mobile App that point exactly to a pin to the map accordingly with my mobile position and update the arrow when I move the phone.
I'm getting crazy to understand exactly how to do that and I can not find any guide or tutorial that explain a bit it.
What I found online is a bearing function and I created a directive around it:
app.directive('arrow', function () {
function bearing(lat1, lng1, lat2, lng2) {
var dLon = (lng2 - lng1);
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 rad = Math.atan2(y, x);
var brng = toDeg(rad);
return (brng + 360) % 360;
}
function toRad(deg) {
return deg * Math.PI / 180;
}
function toDeg(rad) {
return rad * 180 / Math.PI;
}
return {
restrict: 'E',
link: function (scope, element, attrs) {
var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng);
element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
}
};
});
It seems to update the arrow in a direction but unfortunately it is not the right direction because it is not calculated using also the mobile magneticHeading position.
So I added the ngCordova plugin for Device Orientation to get the magneticHeading and now I don't know exactly how to use it and where in the bearing function.
$cordovaDeviceOrientation.getCurrentHeading().then(function(result) {
var magneticHeading = result.magneticHeading;
var arrowAngle = bearing(scope.user.position.lat, scope.user.position.lng, attrs.lat, attrs.lng, magneticHeading);
element.parent().css('transform', 'rotate(' + arrowAngle + 'deg)');
});
I tried to add it in the return statement:
return (brng - heading) % 360;
or:
return (heading - ((brng + 360) % 360));
Implementing this code with a watcher I see the arrow moving but not in the exact position... For example from my position and the pin the arrow should point to N and it is pointing to E.
Always looking online I can not find any tutorial / question to find the bearing between a lat/lng point and a magnetingHeading.
Maybe I'm close to the solution but I can not go ahead alone.
I also tried to search for a mathematical formulas but even there is a huge pain to understand and implement it.
I hope you can help.
It's hard to give a plain answer to this question because a lot depends on the actual graphical representation. For instance, in what direction do you point where rotate(0deg).
I can explain the formula you've found, which might help you to clear the issue yourself. The hard part is the following:
var dLon = (lng2 - lng1);
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 rad = Math.atan2(y, x);
What you see here is Haversines formula (https://en.wikipedia.org/wiki/Haversine_formula). Normally one could suffice with two sets of coordinates and calculate the angle between them. When working with latitude and longitude this will not work because the earth is not a flat surface. The first three lines are Haversine and the result (x and y) are coordinates on the unit circle (https://en.wikipedia.org/wiki/Unit_circle)
The next step is to calculate the angle from the point on this unit circle to the center. We can use the arctangant for this. Javascript has a helper function atan2 which simplifies this process. The result is simple the angle of your point towards the center of the circle. In other words, your position towards your point of interest. The result is in radians and needs to be converted to degrees. (https://en.wikipedia.org/wiki/Atan2)
A simplified version with a flat coordinate system would look like this:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));
bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
Where poi is the Point of Interest and you is your position.
To compensate for your own rotation, you need to substract your own rotation. In the above sample the result would become:
var deltaX = poi.x - you.x;
var deltaY = you.y - poi.y;
var rotation = toDeg(Math.atan2(deltaX, deltaY));
rotation -= you.rotation;
bearingElement.css('transform', 'rotate(' + rotation + 'deg)');
I've made a Plunckr in which you can see a simple flat coordinate system. You can move and rotate you and the point of interest. The arrow inside 'you' will always point towards the poi, even if you rotate 'you'. This is because we compensate for our own rotation.
https://plnkr.co/edit/OJBGWsdcWp3nAkPk4lpC?p=preview
Note in the Plunckr that the 'zero'-position is always to the north. Check your app to see your 'zero'-position. Adjust it accordingly and your script will work.
Hope this helps :-)
Related
I have a grid for a flat, 2D map which is a bit different from usual. The top left point is 0,0 and bottom right is 1000,1000. I have 2 points on this map, an origin/anchor point and a destination.
I am looking to figure out the degrees (in javascript) from the origin to the destination. I have looked through many answers and they all don't produce the correct result.
function getAngle(origin_x, origin_y, destination_x, destination_y) {
var newx = destination_x - origin_x;
var newy = destination_y - origin_y;
var theta = Math.atan2(-newy, newx);
if (theta < 0) {
theta += 2 * Math.PI;
}
theta *= 180 / Math.PI;
return theta;
}
This is what I have so far but it doesn't produce the right angle.
Thankyou very much in advance!
image from mdn Math.atan2 doc
It will give the angle relative to the x-axis, not the y-axis. To convert all you would do is
var newAngle = 90 - theta;
In order to better understand how trigonometry works in game development, I've been creating little javascript snippets on CodePen.
I managed to create an example that uses Math.atan2() to point a pixel-art shotgun at the mouse cursor.
Now, I am trying to accomplish the same exact thing using the Math.atan() function but it isn't functioning properly.
Here is the logic code I am using:
canvas.onmousemove = function(event) {
Mouse = {
x: event.pageX,
y: event.pageY
}
// These length variables use the distance formula
var opposite_length = Math.sqrt((Mouse.x - Mouse.x) * (Mouse.x - Mouse.x) + (Mouse.y - y) * (Mouse.y - y));
var adj_length = Math.sqrt((Mouse.x - x) * (Mouse.x - x) + (y - y) * (y - y));
var angle_in_radians = Math.atan(opposite_length / adj_length);
//var angle_in_radians = Math.atan2(Mouse.y - y, Mouse.x - x);
angle = angle_in_radians * (180 / Math.PI);
}
The in my draw() function, I rotate the gun to the angle var using:
cxt.rotate(angle*(Math.PI/180));
If you uncomment the line that starts as // var angle_in_radians, everything will suddenly work.
So, atan2 is working, but atan is producing the result I want.
I know that opposite_length and adj_length are accurate, because when i console.log() them, they are the correct values.
You can check out the code being used on CodePen for a live example.
There's a lot of initialization stuff but you only really need to focus on the canvas.onmousemove = function(event) section, starting on line 50. You can also check out my draw function on line 68.
Note that your atan computation is equivalent to
atan2( abs(mouse.y-y), abs(mouse.x-x) )
The screen coordinates have the opposite orientation to the cartesian coordinates. To get a cartesian angle from screen coordinates, use
atan2( y-mouse.y, mouse.x-x )
As part of a word cloud rendering algorithm (inspired by this question), I created a Javascript / Processing.js function that moves a rectangle of a word along an ever increasing spiral, until there is no collision anymore with previously placed words. It works, yet I'm uncomfortable with the code quality.
So my question is: How can I restructure this code to be:
readable + understandable
fast (not doing useless calculations)
elegant (using few lines of code)
I would also appreciate any hints to best practices for programming with a lot of calculations.
Rectangle moveWordRect(wordRect){
// Perform a spiral movement from center
// using the archimedean spiral and polar coordinates
// equation: r = a + b * phi
// Calculate mid of rect
var midX = wordRect.x1 + (wordRect.x2 - wordRect.x1)/2.0;
var midY = wordRect.y1 + (wordRect.y2 - wordRect.y1)/2.0;
// Calculate radius from center
var r = sqrt(sq(midX - width/2.0) + sq(midY - height/2.0));
// Set a fixed spiral width: Distance between successive turns
var b = 15;
// Determine current angle on spiral
var phi = r / b * 2.0 * PI;
// Increase that angle and calculate new radius
phi += 0.2;
r = (b * phi) / (2.0 * PI);
// Convert back to cartesian coordinates
var newMidX = r * cos(phi);
var newMidY = r * sin(phi);
// Shift back respective to mid
newMidX += width/2;
newMidY += height/2;
// Calculate movement
var moveX = newMidX - midX;
var moveY = newMidY - midY;
// Apply movement
wordRect.x1 += moveX;
wordRect.x2 += moveX;
wordRect.y1 += moveY;
wordRect.y2 += moveY;
return wordRect;
}
The quality of the underlying geometric algorithm is outside my area of expertise. However, on the quality of the code, I would say you could extract a lot of functions from it. Many of the lines that you have commented could be turned into separate functions, for example:
Calculate Midpoint of Rectangle
Calculate Radius
Determine Current Angle
Convert Polar to Cartesian Coodinates
You could consider using more descriptive variable names too. 'b' and 'r' require looking back up the code to see what they are for, but 'spiralWidth' and 'radius' do not.
In addition to Stephen's answer,
simplify these two lines:
var midX = wordRect.x1 + (wordRect.x2 - wordRect.x1)/2.0;
var midY = wordRect.y1 + (wordRect.y2 - wordRect.y1)/2.0;
The better statements:
var midX = (wordRect.x1 + wordRect.x2)/2.0;
var midY = (wordRect.y1 + wordRect.y2)/2.0;
I'm working on some coordinates function to my canvas in HTML5, and I want to make a function which can move an object by degrees.
My dream is to make a function which works like this:
box.x=10;
box.y=10;
// now the box has the coordinates (10,10)
moveTheBoxWithThisAmountOfDistance=10;
degreesToMoveTheBox=90;
box.moveByDegrees(moveTheBoxWithThisAmountOfDistance,degreesToMoveTheBox);
// now the box.x would be 20 and box.y wouldn't be changed because
// we only move it to the right (directional 90 degrees)
I hope this makes any sense!
So my question is:
How does the mathematical expression look like when I have to turn a degree into to coordinates?
You use sin and cos to convert an angle and a distance into coordinates:
function moveByDegrees(distance, angle) {
var rad = angle * Math.pi / 180;
this.x += Math.cos(rad) * distance;
this.y += Math.sin(rad) * distance;
}
That's it: http://en.wikipedia.org/wiki/Rotation_matrix , all the math is described :)
Also beware that if you need multiple sequential rotations (i.e. you do continuous animation), it's better to recompute x' and y' from initial ones and not just previous. Not doing so will result in rounding errors accumulation, and after some thousand rotations the result will become too rough.
I got a object in a WebGL context that is rotated by a quaternion. Now I would like to rotate it around the x axis according to the current mouse position. How can I do this? I'm using the glMatrix library.
glMatrix doesn't (currently) include much in the way of quaternion transforms. Sorry. Probably not a bad idea to add, however.
You can poke around online to find some references for these things. For example: this page has some good examples for basic quaternion math. In the meantime, if all your looking for is rotation around X I think this will do the trick: (Note! Untested code ahead! This is just a quick optimization of the algorithms on the page I linked)
/**
* Rotates a quaternion by the given angle around the X axis
*
* #param {quat4} quat quat4 to rotate
* #param {number} angle Angle (in radians) to rotate
* #param {quat4} [dest] quat4 receiving operation result. If not specified result is written to quat
*
* #returns {quat4} dest if specified, quat otherwise
*/
quat4.rotateX = function (quat, angle, dest) {
if (!dest) { dest = quat; }
// NOTE: I really have no idea what this is for, the guy on the linked page seemed to think it was necessary, though.
angle *= 0.5;
var qax = quat[0], qay = quat[1], qaz = quat[2], qaw = quat[3],
qbx = Math.sin(angle), qbw = Math.cos(angle);
dest[0] = qax * qbw + qaw * qbx;
dest[1] = qay * qbw + qaz * qbx;
dest[2] = qaz * qbw - qay * qbx;
dest[3] = qaw * qbw - qax * qbx;
};
In the meantime, feel free to add an issue to the gl-matrix github page to request any operations you think would be helpful.
This should do the trick:
quat4.rotateX = function (quat, angle, dest) {
if (!dest) dest = quat;
quat4.multiply(quat, [Math.sin(angle/2), 0, 0, Math.cos(angle/2)]);
}
which you can use with, e.g.,
quat4.rotateX(myQuaternian, Math.PI/4);
(Rotation around Y and Z axes can be done by moving that sin term to the 2nd or 3rd slot.)