Ne开发者_如何学Ced help with a math issue: i need to get the true angle from 0 degrees using x and y cordinates im using this at the moment:
Math.atan((x2-x1)/(y1-y2))/(Math.PI/180)
but /(Math.PI/180)
limits results from -90 to 90
i need 0-360
note: I'm using the angle to indicate direction:
- 0=up
- 90=right
- 135=45 degree right+down
- 180=down
- 270=left
- etc
The atan function only gives half the unit circle between -pi/2 and +pi/2 (0 on x axis), there is another library function that can give the whole unit circle between -pi and + pi, atan2
I would think you are better of using atan2 to get the right quadrant rather than branching yourself, then just scale as you have been, something like
Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI + 180
The multiply by 180 over pi is just the scale from radians to degrees as in the question (but with the division by a division simplified), the +180 makes sure its always positive i.e. 0-360 deg rather than -180 to 180 deg
Math.atan limits you to the two rightmost quadrants on the unit circle. To get the full 0-360 degrees:
if x < 0 add 180 to the angle
else if y < 0 add 360 to the angle.
Your coordinate system is rotated and inverted compared to mine (and compared to convention). Positive x is to the right, positive y is up. 0 degrees is to the right (x>0, y=0, 90 degrees is up (x=0,y>0) 135 degrees is up and to the left (y>0, x=-y), etc. Where are your x- and y-axes pointing?
The answers of @jilles de wit and @jk. led me on the right path but for some reason did not provide the right solution for my problem that i think is very similar to the original question.
I wanted to get up = 0°, right = 90°, down = 180°, left = 270° as in aeronautical navigation systems.
Presuming the question was referring to canvas drawing i reached this solution:
I first translated the canvas origin using ctx.translate(ctx.canvas.width / 2, ctx.canvas.height / 2)
. I also halved e.offsetX and e.offsedY i got from a mouse event on the canvas to get x and y with the same coordinate system as the canvas.
let radianAngle = Math.atan2(y, x); // x has the range [-canvas.width/2 ... +canvas.width/2], y is similar
let northUpAngle = radianAngle * 180 / PI + 90; // convert to degrees and add 90 to shift the angle counterclockwise from it's default "left" = 0°
if (x < 0 && y < 0) { // check for the top left quadrant
northUpAngle += 360; // add 360 to convert the range of the quadrant from [-90...0] to [270...360] (actual ranges may vary due to the way atan2 handles quadrant boundaries)
}
northUpAngle.toFixed(2) // to avoid getting 360° near the "up" position
There might be a more concise solution using the modulo operation but i could't find it.
Also note:
if (y1==y2) {
if (x1>x2)
angle = 90;
else if (x1<x2)
angle = 270;
else
angle = 0;
}
This should do the trick:
- If y2
- If < 0, add 360.
Examples:
(x1,y1) = 0
(x2,y2) = (-1,1), atan() = -45, [add 360], 270
(x2,y2) = (1,1), atan() = 45
(x2,y2) = (1,-1), atan() = -45, [add 180], 135
(x2 ,y2) = (-1,-1), atan() = 45, [add 180], 225
angle = Math.atan(this.k) * 180 / Math.PI;
angle = 180 - (angle < 0 ? 180 + angle : angle);
angle = p2.Y > p1.Y || (p2.Y == p1.Y && p2.X > p1.X) ? 180 + angle : angle;
Here's two solutions, one with Math.atan (which takes a FRACTION opposite/adjacent) and one with Math.atan2 (which takes TWO ARGUMENTS)
solutions are written in ES6 (ES2015+) syntax, ironically, because the question predates this javascript.
note that 'to the right' is 0° (=0 Radians); up is 90° (= PI/2); left is 180° (PI), and down is 270° (PI*1.5)
angleGivenCoords(coord1,coord2) {
// given two coords {x,y}, calculate the angle in radians with
// right being 0° (0 radians)
if (coord1.x === coord2.x) return (coord1.y > coord2.y ? Math.PI * 0.5 : Math.PI * 1.5)
if (coord1.y === coord2.y) return (coord1.x > coord2.x ? Math.PI : 0 )
let opposite = coord2.x - coord1.x
let adjacent = coord1.y - coord2.y
let adjustor = ((coord2.x < coord1.x && coord2.y < coord1.y) || (coord2.x < coord1.x && coord2.y > coord1.y)) ? Math.PI : 0
let res = Math.atan(opposite/adjacent) + adjustor
if (res < 0) { res = res + Math.PI*2 }
return res ;
}
now with Math.atan2. notice that with this solution the guard clauses at the top (coord1.x === coord2.x, (coord1.y === coord2.y)) are unneeded
angleGivenCoords(coord1,coord2) {
// given two coords {x,y}, calculate the angle in radians with
// left being 0° (0 radians)
let opposite = coord2.x - coord1.x
let adjacent = coord1.y - coord2.y
let res = Math.atan2(adjacent, opposite)
if (res < 0) { res = res + Math.PI*2 }
return res ;
}
(I tried to keep the 'opposite' and 'adjacent' variable names in deference to the Trigonometry)
please note that here is my test suite written in Jest. Also note my function above returns radians and my code (not shown here) has a simple Trig.degreesToRadians() as you would expect
it('for right 0°', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 600, y: 500}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(0))
})
it('for up-right 45°', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 600, y: 400}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(45))
})
it('for 90° up', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 500, y: 400}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(90))
})
it('for 135° up to left', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 400, y: 400}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(135))
})
it('for 180° to left', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 400, y: 500}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(180))
})
it('for 225° to to bottom left', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 400, y: 600}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(225))
})
it('for 270° to the bottom', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 500, y: 600}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(270))
})
it('for 315° to the bottom', () => {
let coord1 = {x: 500, y: 500},
coord2 = {x: 600, y: 600}
expect(Trig.angleGivenCoords(coord1,coord2)).toEqual(Trig.degreesToRadians(315))
})
For 0=up,90=right,180=down,270=left etc (x=x2-x1,y=y2-y1)
you can use the formula:
f(x,y)=180-90*(1+sign(y))* (1-sign(x^2))-45*(2+sign(y))*sign(x)
-(180/pi())*sign(x*y)*atan((abs(y)-abs(x))/(abs(y)+abs(x)))
so with a single decesion (i hope this ugly basiclike notation explains it):
IF x = 0 THEN
360degree = 270 - (SIGN(x) + 1) * 90
ELSE
360degree = MOD(180 + (SIGN(y) + 1) * 90 + ATAN(x/y) , 360)
ENDIF
to draw a full circle from north 0degree to 360degree clockwise:
x=SIN(0to360) y=COS(0to360)
cheers, Lev
function angle(x1,y1,x2,y2)
{
eangle = Math.atan((x2-x1)/(y1-y2))/(Math.PI/180)
if ( angle > 0 )
{
if (y1 < y2)
return angle;
else
return 180 + angle;
} else {
if (x1 < x2)
return 180 + angle;
else
return 360 + angle;
}
}
精彩评论