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;
Related
I am trying to calculate the angle for an arrow on a ball, based on the position where it is going to.
The arrow moves, but in a total unexplainable direction, can anybody give some pointers?
Codepen available: Codepen
I added the full code on here (EDITED based on input):
I added a step to make the difference bigger for the angle calculation, not sure if that is the right way to go, but it seems a bit more functional. Plus added the +/- 90 in the angle method, but that doesnt seem to fix it. It is still feeling odd.
class Throwable {
constructor(){
this.throwObject = null;
this.canDrag = null;
this.initialDiffX = 0;
this.initialDiffY = 0;
this.previousX = 0;
this.previousY = 0;
this.intervalCounter = 0;
}
set x(input) {
this.throwObject.style.left = input + 'px';
}
set y(input) {
this.throwObject.style.top = input + 'px';
}
set rotation(input) {
this.throwObject.style.transform = `rotate(${input}deg)`;
}
init(){
this.throwObject = document.querySelector('.throwable');
this.throwObject.addEventListener('mousedown', this.activateDrag.bind(this));
this.throwObject.addEventListener('mouseup', this.deactivateDrag.bind(this));
document.addEventListener('mousemove', this.drag.bind(this));
}
activateDrag(event) {
this.canDrag = true;
this.initialDiffX = event.clientX - this.throwObject.offsetLeft;
this.initialDiffY = event.clientY - this.throwObject.offsetTop;
}
deactivateDrag() {
this.canDrag = false;
}
drag(event) {
if(this.canDrag === true) {
if(this.intervalCounter >= 30) {
this.intervalCounter = 0;
}
if(this.intervalCounter === 0) {
this.previousX = event.clientX;
this.previousY = event.clientY;
}
this.intervalCounter++;
this.y = event.clientY- this.initialDiffY;
this.x = event.clientX - this.initialDiffX;
this.rotation = this.angle(event.clientX, event.clientY, this.previousX, this.previousY);
}
}
angle(ex, ey, cx, cy) {
var dy = ey - cy;
var dx = ex - cx;
return Math.atan2(dy, dx) * 180 / Math.PI + 90;
}
// Untility
log(logObject) {
let logStr = '';
for(let key in logObject) {
logStr += `${key}: ${logObject[key]}<br>`;
}
document.getElementById('log').innerHTML = logStr;
}
}
let throwable = new Throwable();
throwable.init();
I made a mistake in comparing two different values, I fixed that, it is working way better, still have some odd behavior sometimes, seems like it doesnt know where to go in some points. But working better than before.
Maybe you have some mistakes in your angle function. This works for me:
angle(cx, cy, ex, ey) {
var dy = ey - cy ;
var dx = cx - ex ;
return Math.atan2(dx, dy) * 180 / Math.PI;
}
When you call this.angle() you give it twice this.throwObject.offset..., once directly and once via px and py:
let px = this.throwObject.offsetLeft;
let py = this.throwObject.offsetTop;
this.rotation = this.angle(this.throwObject.offsetLeft, this.throwObject.offsetTop, px, py)
That will result in dx and dy to be 0 in angle() making the result of Math.atan2() unpredictable.
I'm not sure about the rest of your code, but maybe you meant to call angle() like this:
this.rotation = this.angle(this.x, this.y, px, py);
There are a couple small issues that I can see.
First, the angle method is calculating radians in range of -180 to 180 and you want it to be 0 to 360. So after angle calculation you'll want to convert something like this:
angle(ex, ey, cx, cy) {
var dy = ey - cy;
var dx = ex - cx;
var theta = Math.atan2(dy, dx) * 180 / Math.PI;
if (theta < 0) theta += 360; // convert to [0, 360]
return theta;
}
Second, the starting angle of your element at 0 degrees is not the actual 0 degrees calculated by this method due to how js coordinates work. A quick fix is to add 90 degrees to make it match:
set rotation(input) {
this.throwObject.style.transform = `rotate(${input + 90}deg)`;
}
It's still a little janky after these conversion but I think it's a start on the right calculations. My guess is part of the issue is having such close points for calculation.
This happens because there's a difference how angles are measured between Math.atan2() and the CSS rotate transformation.
For us humans it's natural that the 12 o' clock position on an analog clock refers to the angle 0 - same for CSS rotate.
Math.atan2() however measures the angle starting from the horizontal x axis. So depending on your input coordinates it would be the 3 or 9 o' clock position.
There's an easy fix however.
After calculating the angle
Math.atan2(dy, dx) * 180 / Math.PI
just subtract 90 degrees like
Math.atan2(dy, dx) * 180 / Math.PI - 90
What happens when intervalCounter become 0? The previus point moved to the event point, so dy, dx becomes 0 and you have a jitter: -180 + 90, +180 + 90, 0 + 90 as defined in Math.atan2. After that, the previus point is fixed until intervalCounter < 30 and you have some inceasing distance between the previus and event points, so the angle is close to the expected one.
Anyway, this is a bad coordinate filter. You can improve it by implementing simple exponential filtering or by using fixed size (30 in your case) queue for event point.
So I'm stumped. I didn't know trigonometry before this, and I've been learning but nothing seems to be working.
So a few things to note: In html, cartesian origin(0,0) is the top left corner of the screen. DIVS natural rotation is 0deg or ---->this way.
I need to find the x,y point noted by the ? mark in the problem.
$('#wrapper').on('click', function(e){
mouseX = e.pageX;
mouseY= e.pageY;
var angle = getAngle(mouseX,Rocket.centerX,mouseY,Rocket.centerY);
var angleDistance = Math.sqrt((Math.pow((mouseX - (Rocket.left+Rocket.halfX)),2)) + (Math.pow((mouseY-(Rocket.top+Rocket.halfY)),2)));
var cp2Angle = -90 +(angle*2);
var invCP2Angle = 90+ angle;
var cp2Distance = angleDistance*.5;
//Red Line
$(this).append('<div class="line" style="transform-origin:left center;width:'+(Math.round(angleDistance))+'px;top:'+(Rocket.top+Rocket.halfY)+'px;left:'+(Rocket.left+Rocket.halfX)+'px;transform:rotate('+(Math.round(angle))+'deg);"></div>');
//Blue Line
$(this).append('<div class="line" style="background:#0000FF;transform-origin:left center;width:'+Math.round(cp2Distance)+'px;top:'+(mouseY)+'px;left:'+(mouseX)+'px;transform:rotate('+(Math.round(cp2Angle))+'deg);"></div>');
}
function getAngle(x2,x1,y2,y1){
var angle = Math.degrees(Math.atan2(y2-y1,x2-x1));
return angle;
}
Math.degrees = function(radians) {
return (radians * 180) / Math.PI;
};
So this might be confusing. Basically when I click on the page, i calculate the angle between my custom origin and the mouse points using Math.atan2(); I also calculate the distance using Math.sqrt((Math.pow((x2 - x1),2)) + (Math.pow((y2-y1),2)));
The blue line length is half the length of the red line, but the angle changes, based on the angle of the red line.
When the red line angle = 0deg(a flat line), the blue line angle will be -90(or straight up, at red line -45 deg, the blue line will be -180(or flat), and at Red Line -90, the blue line will be -270 deg(or straight down). The formula is -90 +(angle*2)
I need to know the other end point of the blue line. The lines only exist to debug, but the point is needed because I have an animation where I animate a rocket on a bezier curve, and I need to change the control point based on the angle of the mouse click, if there's abetter way to calculate that without trigonometry, then let me know.
I read that the angle is the same as the slope of the line and to find it by using Math.tan(angle in radians). Sometimes the triangle will be a right triangle for instance if the first angle is 0 deg, sometimes it won't be a triangle at all, but a straight line down, for instance if they click -90.
I've also tried polar coordinates thought I wasn't sure which angle to use:
var polarX = mouseX-(cp2Distance * Math.cos(Math.radians(invCP2Angle)));
var polarY = mouseY- (cp2Distance * Math.sin(Math.radians(invCP2Angle)));
I do not know javascript well, so instead of giving you code, I'll just give you the formulae. On the figure below, I give you the conventions used.
x3 = x2 + cos(brownAngle + greenAngle) * d2
y3 = y2 + sin(brownAngle + greenAngle) * d2
If I understand you correctly, you have already d2 = 0.5 * d1, d1, (x2, y2) as well as the angles. This should then just be a matter of plugging these values into the above formulae.
Let A, B and C be the three points.
AB = ( cos(angle1), sin(angle1) ) * length1
B = A + B
BC = ( cos(angle1+angle2), sin(angle1+angle2) ) * length2
C = B + BC
In your case,
A = ( 0, 0 )
angle1 = 31°
length1 = 655
angle2 = 152°
length2 = 328
Then,
C = ( Math.cos(31*Math.PI/180), Math.sin(31*Math.PI/180) ) * 655 +
( Math.cos(152*Math.PI/180), Math.sin(152*Math.PI/180) ) * 328
= ( Math.cos(31*Math.PI/180) * 655 + Math.cos(183*Math.PI/180) * 328,
Math.sin(31*Math.PI/180) * 655 + Math.sin(183*Math.PI/180) * 328 )
= ( 233.8940945603834, 320.1837454184)
I'm kinda confused with this one.
I have an object and I know it's velocities on axis x and y. My problem is how to determine the angle at which it's moving.
function Object(){
this.velocity = {x: 5, y: 1};
}
Basically I Know that a vector's direction is x_projectioncos(deg) + y_projectionsin(deg), but I don't know how to get those projections since I only have the velocity, as I said I'm really confused.
#EDIT:
in addition to the accepted answer, here's what I did to get a full 360 degree spectrum
var addDeg = 0;
if(obj.velocity.x<0)
addDeg = obj.velocity.y>=0 ? 180 : 270;
else if(obj.velocity.y<=0) addDeg = 360;
deg = Math.abs(Math.abs(Math.atan(obj.velocity.y/obj.velocity.x)*180/Math.PI)-addDeg)
I don't know how to get those projections since I only have the
velocity
Actually, what you seem to be missing is that you already have the projections. That's what x and y are.
x is speed * cos(angle)
y is speed * sin(angle)
So y/x = sin(angle)/cos(angle) which is tan(angle) so angle=arctan(y/x).
That's the angle rotating anti-clockwise starting from the x axis (with x pointing right and y pointing up).
Find the angle between that vector and (1,0) (Right horizontal positive direction).
The math is:
A = (5,1)
B = (1,0)
A.B = |A||B|cos(angle) -> angle = arccos((|A||B|)/(A.B))
Dot product, check geometric definition
Edit:
Another option is to use the cross product formula:
|AxB| = |A||B|sin(angle) -> angle = arcsin((|A||B|)/(|AxB|))
It will give you the angle you need.
There is an easier way to get full 360 degrees. What you're looking for, is Math.atan2:
deg = Math.atan2(obj.velocity.y,obj.velocity.x)*180/Math.PI;
I'm using the following functions to track mouse movement and rotate an object:
function getAngle(dx, dy) {
var angle
if (dx != 0) {
var radians = Math.atan(dy / dx) + (dx < 0 ? Math.PI : 0)
angle = radiansToDegrees(radians);
if (angle < 0) angle += 360;
} else {
angle = dy > 0 ? 90 : 270;
}
return angle;
}
function getAngleBetweenPoints(p1, p2) {
var dx = p1.x - p2.x
var dy = p1.y - p2.y
return getAngle(dx, dy)
}
$(document).mousemove(function (e) {
if (selectionBounds) {
var midpoint = new pe.Classes.Point(selectionBounds.Left + (selectionBounds.Width / 2), selectionBounds.Top + (selectionBounds.Height / 2));
var mousepoint = new pe.Classes.Point(e.pageX, e.pageY);
var angle = getAngleBetweenPoints(midpoint, mousepoint);
if (lastAngle) {
var diff = angle - lastAngle;
rotate(degreesToRadians(diff));
}
lastAngle = angle;
}
});
This works well, as long as I move the mouse slowly, and as long as the mouse doesn't get too close to the origin (midpoint). Moving too quickly causes additional spin rotations, and coming close to the origin causes unexpected changes of direction.
How can I fix this code? I really just need to know which direction the mouse is moving in (clockwise or anti-clockwise), as I can get an idea of the speed just from the change in mousepoint and then update the rotation based on that.
There are literally dozens of SO threads on topics related to this (How to get the direction (angle) of rectangle after rotating it from a pivot point, How to get cardinal mouse direction from mouse coordinates, Moving a rotated element in the direction of the rotation in JavaScript) - but I haven't been able to find anything that can answer this question, except one comment referring to this requiring the cross product, which I didn't fully understand.
http://jsfiddle.net/wRexz/3/ (click and drag to rotate the rectangle)
var angle = 0, sp = startpoint, mp = midpoint;
var p = {x:e.offsetX, y:e.offsetY};
var sAngle = Math.atan2((sp.y-mp.y),(sp.x - mp.x));
var pAngle = Math.atan2((p.y-mp.y),(p.x - mp.x));
angle = (pAngle - sAngle) * 180/Math.PI;
$("#display").text(angle);
$('#rotateme').css({ rotate: '+=' + angle });
startpoint = {x:p.x, y:p.y};
The concept here is basic trig. You find the angle from 0 of the "start point" and do the same for the "end point" or "current point". Subtract the first from the second, and that is your "delta angle".
You will still get erratic behavior around the midpoint, due to the nature of how rapidly the angles can change. One solution to this is stopping rotation when within a certain distance of the midpoint.
I'm sorry to say that Math really isn't my strong suit. Normally I can get by, but this has got me totally stumped.
I'm trying to code up a quiz results screen in HTML/CSS/Javascript.
On my interface, I have a semicircle (the right hemisphere of a target).
I have a range of 'scores' (integers out of 100 - so 50, 80, 90 etc.).
I need to plot these points on the semicircle to be n% away from the centre, where n is the value of each score - the higher the score, the closer to the centre of the target the point will appear.
I know how wide my semicircle is, and have already handled the conversion of the % values so that the higher ones appear closer to the centre while the lower ones appear further out.
What I can't wrap my head around is plotting these points on a line that travels out from the centre point (x = 0, y = target height/2) of the target at a random angle (so the points don't overlap).
Any suggestions are gratefully received!
Do you have an example of what you want this to look like? It sounds like you want to divide up the circle into N slices where N is the number of points you need to display, then plot the points along each of those radii. So you might have something like:
Edit: code was rotating about the origin, not the circle specified
var scores = [];
//...
//assume scores is an array of distances from the center of the circle
var points = [];
var interval = 2 * Math.PI / N;
var angle;
for (var i = 0; i < N; i++) {
angle = interval * i;
//assume (cx, cy) are the coordinates of the center of your circle
points.push({
x: scores[i] * Math.cos(angle) + cx,
y: scores[i] * Math.sin(angle) + cy
});
}
Then you can plot points however you see fit.
After much headscratching, I managed to arrive at this solution (with the help of a colleague who's much, much better at this kind of thing than me):
(arr_result is an array containing IDs and scores - scores are percentages of 100)
for (var i = 0; i < arr_result.length; i++){
var angle = angleArray[i]; // this is an array of angles (randomised) - points around the edge of the semicircle
var radius = 150; // width of the semicircle
var deadZone = 25 // to make matters complicated, the circle has a 'dead zone' in the centre which we want to discount
var maxScore = 100
var score = parseInt(arr_result[i]['score'], 10)
var alpha = angle * Math.PI
var distance = (maxScore-score)/maxScore*(radius-deadZone) + deadZone
var x = distance * Math.sin(alpha)
var y = radius + distance * Math.cos(alpha)
$('#marker_' + arr_result[i]['id'], templateCode).css({ // target a specific marker and move it using jQuery
'left' : pointX,
'top': pointY
});
}
I've omitted the code for generating the array of angles and randomising that array - that's only needed for presentational purposes so the markers don't overlap.
I also do some weird things with the co-ordinates before I move the markers (again, this has been omitted) as I want the point to be at the bottom-centre of the marker rather than the top-left.