선과 가로 축 사이의 각도를 계산하는 방법은 무엇입니까?


248

프로그래밍 언어 (Python, C # 등)에서 선과 가로 축 사이의 각도를 계산하는 방법을 결정해야합니까?

이미지가 내가 원하는 것을 가장 잘 묘사한다고 생각합니다.

어떤 단어도 이것을 설명 할 수 없습니다

주어진 (P1 x , P1 y ) 및 (P2 x , P2 y )이 각도를 계산하는 가장 좋은 방법은 무엇입니까? 원점은 왼쪽 상단에 있으며 양의 사분면 만 사용됩니다.


답변:


388

먼저 시작점과 끝점 사이의 차이점을 찾으십시오 (여기서, 이것은 선이 무한대로 확장되고 특정 지점에서 시작하지 않기 때문에 "선"이 아닌 직접 선 세그먼트에 가깝습니다).

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

그런 다음 각도를 계산하십시오 (양의 X 축에서 P1양의 Y 축으로 이동 P1).

angleInDegrees = arctan(deltaY / deltaX) * 180 / PI

그러나이 arctan방법으로 차이를 나누면 각도가 어느 사분면인지 구분하는 데 필요한 구별이 지워지기 때문에 이상적이지 않을 수 있습니다 (아래 참조). 언어에 atan2함수가 포함 된 경우 대신 다음을 사용하십시오 .

angleInDegrees = atan2(deltaY, deltaX) * 180 / PI

EDIT (2017년 2월 22일) : 일반적으로, 그러나, 호출 atan2(deltaY,deltaX)에 대한 적절한 각도를 얻기 위해 단지 cossin우아 할 수있다. 이 경우 종종 다음을 수행 할 수 있습니다.

  1. (deltaX, deltaY)벡터로 취급하십시오 .
  2. 해당 벡터를 단위 벡터로 정규화합니다. 그래서 나누기를 수행 deltaX하고 deltaY벡터의 길이 (기준 sqrt(deltaX*deltaX+deltaY*deltaY)길이가 0이 아닌).
  3. 그 후 deltaX이제 벡터와 가로 축 사이의 각도의 코사인이됩니다 (에서 양의 X에서 양의 Y 축 방향 P1).
  4. 그리고 deltaY이제 그 각도의 사인이됩니다.
  5. 벡터의 길이가 0이면 벡터와 가로 축 사이에 각도가 없으므로 의미있는 사인과 코사인이 없습니다.

편집 (2017 년 2 월 28 일) : 표준화하지 않아도 (deltaX, deltaY):

  • 의 부호는 deltaX3 단계에서 설명한 코사인이 양수인지 음수인지 나타냅니다.
  • 의 부호는 deltaY4 단계에서 설명한 사인이 양수인지 음수인지 나타냅니다.
  • deltaX및 의 부호 deltaY는 다음에서 양의 X 축과 관련하여 각도가 어느 사분면에 있는지 알려줍니다 P1.
    • +deltaX, +deltaY: 0 ~ 90도.
    • -deltaX, +deltaY: 90 ~ 180도.
    • -deltaX, -deltaY : 180 ~ 270도 (-180 ~ -90도).
    • +deltaX, -deltaY: 270 ~ 360도 (-90 ~ 0도).

라디안을 사용한 Python 구현 (2015 년 7 월 19 일 Eric Leschinski 제공, 내 답변 편집) :

from math import *
def angle_trunc(a):
    while a < 0.0:
        a += pi * 2
    return a

def getAngleBetweenPoints(x_orig, y_orig, x_landmark, y_landmark):
    deltaY = y_landmark - y_orig
    deltaX = x_landmark - x_orig
    return angle_trunc(atan2(deltaY, deltaX))

angle = getAngleBetweenPoints(5, 2, 1,4)
assert angle >= 0, "angle must be >= 0"
angle = getAngleBetweenPoints(1, 1, 2, 1)
assert angle == 0, "expecting angle to be 0"
angle = getAngleBetweenPoints(2, 1, 1, 1)
assert abs(pi - angle) <= 0.01, "expecting angle to be pi, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 3)
assert abs(angle - pi/2) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 0)
assert abs(angle - (pi+pi/2)) <= 0.01, "expecting angle to be pi+pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(1, 1, 2, 2)
assert abs(angle - (pi/4)) <= 0.01, "expecting angle to be pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -2, -2)
assert abs(angle - (pi+pi/4)) <= 0.01, "expecting angle to be pi+pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -1, 2)
assert abs(angle - (pi/2)) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)

모든 테스트는 통과합니다. https://en.wikipedia.org/wiki/Unit_circle을 참조 하십시오.


35
이것을 발견하고 JAVASCRiPT를 사용하는 경우 Math.sin 및 Math.cos는 라디안을 사용하므로 결과를도 단위로 변환 할 필요가 없습니다. 따라서 * 180 / PI 비트는 무시하십시오. 그걸 알아내는 데 4 시간 걸렸습니다. :)
sidonaldson

수직축을 따라 각도를 계산하기 위해 무엇을 사용해야합니까?
ZeMoon

3
@akashg : 90 - angleInDegrees ?
jbaums 2016 년

왜 90-angleInDegrees를해야합니까? 그 이유가 있습니까? 동일하게 설명하십시오.
Praveen Matanam

2
@sidonaldson 그것은 단지 자바 스크립트 이상의 것이지, C, C #, C ++, Java 등입니다. 사실 나는 대부분의 언어가 수학 라이브러리가 주로 라디안으로 작동한다고 감히 말합니다. 기본적으로 학위 만 지원하는 언어는 아직 보지 못했습니다.
Pharap

50

미안하지만 피터의 대답이 틀렸다고 확신합니다. y 축은 페이지 아래로 내려갑니다 (그래픽에서 일반적 임). 따라서 deltaY 계산이 취소되어야합니다. 그렇지 않으면 오답이 나타납니다.

치다:

System.out.println (Math.toDegrees(Math.atan2(1,1)));
System.out.println (Math.toDegrees(Math.atan2(-1,1)));
System.out.println (Math.toDegrees(Math.atan2(1,-1)));
System.out.println (Math.toDegrees(Math.atan2(-1,-1)));

준다

45.0
-45.0
135.0
-135.0

따라서 위의 예에서 P1이 (1,1)이고 P2가 (2,2) 인 경우 (Y가 페이지를 증가시키기 때문에) 위의 코드는 표시된 예에 대해 45.0도를 제공합니다. deltaY 계산 순서를 변경하면 제대로 작동합니다.


3
나는 당신이 제안한대로 그것을 뒤집었고 내 회전은 거꾸로되었습니다.
악마의 옹호자

1
내 코드에서 나는 이것을 다음과 같이 수정한다. double arc = Math.atan2(mouse.y - obj.getPy(), mouse.x - obj.getPx()); degrees = Math.toDegrees(arc); if (degrees < 0) degrees += 360; else if (degrees > 360) degrees -= 360;
Marcus Becker

각도가있는 원의 4 분의 1에 따라 달라집니다. 1 분기 (최대 90도) 인 경우 deltaX 및 deltaY (Math.abs)에 양수 값을 사용하고 두 번째 (90-180) 사용 deltaX의 추상 가치를 부정하고, 세 번째 (180-270)에서 deltaX와 deltaY를 모두 부정하고 int 네 번째 (270-360)는 deltaY 만 부정합니다-아래 내 답변 참조
mamashare

1

파이썬에서 잘 작동하는 솔루션을 찾았습니다!

from math import atan2,degrees

def GetAngleOfLineBetweenTwoPoints(p1, p2):
    return degrees(atan2(p2 - p1, 1))

print GetAngleOfLineBetweenTwoPoints(1,3)

1

정확한 질문을 고려하여 양의 축이 화면 또는 인터페이스보기와 같이 아래로 이동하는 것을 의미하는 "특수"좌표 시스템에 넣으려면이 기능을 다음과 같이 조정하고 Y 좌표를 음수로 조정해야합니다.

Swift 2.0의 예

func angle_between_two_points(pa:CGPoint,pb:CGPoint)->Double{
    let deltaY:Double = (Double(-pb.y) - Double(-pa.y))
    let deltaX:Double = (Double(pb.x) - Double(pa.x))
    var a = atan2(deltaY,deltaX)
    while a < 0.0 {
        a = a + M_PI*2
    }
    return a
}

이 기능은 질문에 대한 정답을 제공합니다. 답은 라디안 단위이므로 각도를도 단위로 보는 사용법은 다음과 같습니다.

let p1 = CGPoint(x: 1.5, y: 2) //estimated coords of p1 in question
let p2 = CGPoint(x: 2, y : 3) //estimated coords of p2 in question

print(angle_between_two_points(p1, pb: p2) / (M_PI/180))
//returns 296.56

0

"Peter O"참조. 자바 버전은 다음과 같습니다.

private static final float angleBetweenPoints(PointF a, PointF b) {
float deltaY = b.y - a.y;
float deltaX = b.x - a.x;
return (float) (Math.atan2(deltaY, deltaX)); }

0

MATLAB 기능 :

function [lineAngle] = getLineAngle(x1, y1, x2, y2) 
    deltaY = y2 - y1;
    deltaX = x2 - x1;

    lineAngle = rad2deg(atan2(deltaY, deltaX));

    if deltaY < 0
        lineAngle = lineAngle + 360;
    end
end

0

0에서 2pi까지의 각도에 대한 공식.

x = x2-x1 및 y = y2-y1이 있습니다.

x와 y의 모든 값 x = y = 0의 경우 결과가 정의되지 않습니다.

f (x, y) = pi ()-pi () / 2 * (1 + sign (x)) * (1-sign (y ^ 2))

     -pi()/4*(2+sign(x))*sign(y)

     -sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))

0
deltaY = Math.Abs(P2.y - P1.y);
deltaX = Math.Abs(P2.x - P1.x);

angleInDegrees = Math.atan2(deltaY, deltaX) * 180 / PI

if(p2.y > p1.y) // Second point is lower than first, angle goes down (180-360)
{
  if(p2.x < p1.x)//Second point is to the left of first (180-270)
    angleInDegrees += 180;
  else //(270-360)
    angleInDegrees += 270;
}
else if (p2.x < p1.x) //Second point is top left of first (90-180)
  angleInDegrees += 90;

코드가 의미가 없습니다. else (270-360) .. 무엇?
WDUK

0
import math
from collections import namedtuple


Point = namedtuple("Point", ["x", "y"])


def get_angle(p1: Point, p2: Point) -> float:
    """Get the angle of this line with the horizontal axis."""
    dx = p2.x - p1.x
    dy = p2.y - p1.y
    theta = math.atan2(dy, dx)
    angle = math.degrees(theta)  # angle is in (-180, 180]
    if angle < 0:
        angle = 360 + angle
    return angle

테스팅

테스트를 위해 가설이 테스트 사례를 생성 하도록했습니다 .

여기에 이미지 설명을 입력하십시오

import hypothesis.strategies as s
from hypothesis import given


@given(s.floats(min_value=0.0, max_value=360.0))
def test_angle(angle: float):
    epsilon = 0.0001
    x = math.cos(math.radians(angle))
    y = math.sin(math.radians(angle))
    p1 = Point(0, 0)
    p2 = Point(x, y)
    assert abs(get_angle(p1, p2) - angle) < epsilon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.