查找0-360度角度

15

需要解决一个数学问题: 我需要使用 x 和 y 坐标来获取相对于 0 度的真实角度。 目前我正在使用以下代码:

Math.atan((x2-x1)/(y1-y2))/(Math.PI/180)

但是/(Math.PI/180)会将结果限制在-90到90之间,我需要0-360。

注意:我使用角度来表示方向:

  • 0=向上
  • 90=向右
  • 135=45度向右下
  • 180=向下
  • 270=向左
  • 等等
10个回答

39

atan函数只能在-x轴到+x轴之间的半个单位圆内(0在x轴上)提供结果,而在-pi和+pi之间的整个单位圆需要使用另一个库函数来获取,即atan2。

我认为最好使用atan2来获取正确的象限,而不是自己分支,然后按照您一直在做的方式进行缩放,例如:

Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI + 180

将180除以pi得出的数值只是将弧度转换为角度的比例尺,这与问题中的内容相同(但进行了简化)。加上+180确保始终为正,即0-360度,而不是-180到180度。


你能解释一下这个吗?atan2是什么意思?为什么要乘以180再除以Math.PI + 180? - Muhammad Umer
atan2可以给出正确的象限。http://en.wikipedia.org/wiki/Atan2#Motivation - jk.
不起作用,原因在这里:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 他们颠倒了x和y的字母顺序。愚蠢但却是事实。 - candied_orange
@CandiedOrange TA - 已修复 - jk.
这很好,但是把0放在正西方。OP(和我)需要0在正北方。有什么建议吗? - Drew Baker

16

Math.atan将您限制在单位圆上的最右两个象限。要获取完整的0-360度:

if x < 0 add 180 to the angle
else if y < 0 add 360 to the angle. 

你的坐标系相对于我的(以及相对于常规)旋转并翻转了。 正向x朝右,正向y朝上。 0度朝右(x> 0,y = 0),90度朝上(x = 0,y> 0),135度朝左上方(y> 0,x = -y),等等。 你的x轴和y轴指向哪里?


var add=0 var x=x2-x1 var y=y2-y1 if(x<0) add=180 else if(y<0) add=360 var u=parseInt((Math.atan((x2-x1)/(y1-y2))+add)/(Math.PI/3.1428)),v=u变量add=0 变量x=x2-x1 变量y=y2-y1 如果(x<0) add=180 否则如果(y<0) add=360 变量u=parseInt((Math.atan((x2-x1)/(y1-y2))+add)/(Math.PI/3.1428)),v=u - Johnny Darvall
var add = 0 var x = x2 - x1 var y = y2 - y1 if (x<0)add = 180 else if (y<0)add = 360 var u = parseInt((Math.atan((x2-x1)/(y1-y2))+add)/(Math.PI/180)),v=u - Johnny Darvall
不,你需要在除以(pi/180)之后添加数字,否则应该使用math.PI而不是180,使用2*Math.PI而不是360。 - jilles de wit

4
@jillesde wit和@jk的答案指引我走上了正确的道路,但由于某些原因并没有为我的问题提供正确的解决方案,我认为这与原始问题非常相似。我想要在航空导航系统中获得起点= 0°,右侧= 90°,下方= 180°,左侧= 270°。假设该问题是关于canvas绘图的,我得出了以下解决方案:首先使用ctx.translate(ctx.canvas.width / 2,ctx.canvas.height / 2)来翻译画布原点。我还将从画布上的鼠标事件获得的e.offsetX和e.offsedY减半,以便使用与画布相同的坐标系获取x和y。
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

可能有一种更简洁的方法使用模数运算,但我找不到。


2
不要加90,改为加450,然后使用模运算:(Math.atan2(y, x) * 180 / Math.PI + 450) % 360 - John S
为什么up=0°?难道不应该是右边为0°吗?我在一个基于向量的坐标系中工作。 - Jason FB
@JasonFB 我正在创建一个交互式的标题仪表,所以我想显示角度值。为了实现这一点,我旋转了坐标系。 - Vlad Macovei
没错,我只是想问一下是否有不同的领域/部门会旋转坐标系?(我之前认为0度=“向右”是标准的,直到遇到其他人将其实现为0度=“向上”) - Jason FB

2

同时需要注意:

if (y1==y2) {
    if (x1>x2)
        angle = 90;
    else if (x1<x2)
        angle = 270;
    else
        angle = 0;
}

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

1

这应该可以解决问题:

  1. 如果 y2
  2. 如果 < 0,则加上 360。

示例:

(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

1

因此,通过单个决策(我希望这种丑陋的类Basic符号可以解释清楚):

如果x = 0,则

360度 = 270 -(SIGN(x)+1)* 90

否则

360度= MOD(180 +(SIGN(y)+1)* 90 + ATAN(x / y),360)

ENDIF

要从北部0度顺时针绘制一个完整的圆:

x = SIN(0to360)y = COS(0to360)

干杯,Lev


1

对于0=上,90=右,180=下,270=左等(x=x2-x1,y=y2-y1)

你可以使用以下公式:

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

1
这里有两个解决方案,一个使用Math.atan(它接受一个对边/邻边的分数),另一个使用Math.atan2(它接受两个参数)。
这些解决方案采用ES6(ES2015+)语法编写,具有讽刺意味的是,这个问题早于这个javascript。
请注意,“向右”为0°(= 0弧度);向上为90°(= PI/2);向左为180°(PI),向下为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 ;
  }

现在有了Math.atan2方法。请注意,使用此解决方案时,顶部的保护条款(coord1.x === coord2.x,(coord1.y === coord2.y))是不必要的。
  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 ;
  }

我尝试保留“相反”和“邻边”的变量名称,以表示对三角学的尊重。

请注意,这是我用Jest编写的测试套件。另请注意,我上面的函数返回弧度,我的代码(此处未显示)有一个简单的Trig.degreesToRadians(),就像您预期的那样。

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

我认为正确的术语可能是“轨迹”而不是“角度”。 - Jason FB

-1
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;
}
}

尽管这个解决方案被踩了,但它确实解决了象限问题。 - Jason FB

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接