세 점에서 각도를 계산하는 방법은 무엇입니까? [닫은]


120

이것을 가지고 있다고 가정 해 봅시다.

P1 = (x=2, y=50)
P2 = (x=9, y=40)
P3 = (x=5, y=20)

P1이것이 원의 중심점 이라고 가정합니다 . 항상 동일합니다. 나는 P2and 로 이루어진 각도 P3, 즉 옆에 있는 각도를 원합니다 P1. 정확한 내부 각도입니다. 항상 예각이므로 -90도 미만입니다.

나는 생각했다 : 남자, 그것은 단순한 기하학 수학이다. 하지만 지금까지 약 6 시간 동안 공식을 찾았고, arccos 및 벡터 스칼라 제품과 같은 복잡한 NASA에 대해 이야기하는 사람들 만 있습니다. 머리가 냉장고에있는 것 같은 느낌이 들어요.

이것이 간단한 문제라고 생각하는 수학 전문가? 나는 여기서 프로그래밍 언어가 중요하다고 생각하지 않지만 그렇게 생각하는 사람들에게는 java와 objective-c가 중요하다. 둘 다 필요하지만 태그를 지정하지 않았습니다.

답변:


87

P1이 꼭지점 인 각도를 의미하는 경우 코사인법칙 을 사용하면 다음과 같이 작동합니다.

Arccos((P 12 2 + P 13 2 -P 23 2 ) / (2 * P 12 * P 13 ))

여기서 P 12 는 P1에서 P2까지의 세그먼트 길이이며 다음과 같이 계산됩니다.

sqrt ((P1 x -P2 x ) 2 + (P1 y -P2 y ) 2 )



@Rafa Firenze cos ^ -1은 acos의 일반적인 표기법이지만 acos는 덜 모호합니다. en.wikipedia.org/wiki/Inverse_trigonometric_functions
geon

아무것도 아프지 않기 때문에 편집을 떠날 것입니다. 그러나 수학 / CS / EE 학위를 가지고있는 cos ^ -1은 확실히 가장 일반적인 표기법입니다.
Lance Roberts

1
소수의 언어에서만 '힘'에 캐럿을 사용하므로 arcos라고 부르지 않으려면 cos⁻¹를 입력하세요. (지수를 입력하기 어려운 상용 운영 체제를 사용하는 경우 구입 가능한 키캡 응용 프로그램이나 설치할 수있는 브라우저 플러그인이있을 것으로 예상합니다. 또는 웹 검색 및 복사 및 붙여 넣기를 사용할 수 있습니다.)
Michael Scheper 2015 년

1
@MichaelScheper, html이 제한된 주석에서만 캐럿을 사용했습니다. 나는 실제 답변에서 아래 / 위 첨자 표기법을 확실히 사용할 것입니다.
Lance Roberts

47

점 P1에서 P2로, 하나는 P1에서 P3까지 두 개의 벡터로 생각하면 매우 간단 해집니다.

그래서 :
a = (p1.x-p2.x, p1.y-p2.y)
b = (p1.x-p3.x, p1.y-p3.y)

그런 다음 내적 공식을 반전
내적
하여 각도를 구할 수 있습니다.
두 벡터 사이의 각도

기억 내적단지 수단 : A1 *, A2 * B1 + B2를 (여기서는 단지 2 차원 ...)


1
벡터의 아 크기
다니엘 리틀

atan2 솔루션을 확인하십시오.
Luc Boissaye

25

각도 계산을 처리하는 가장 좋은 방법 atan2(y, x)은 주어진 점이 원점을 기준으로 x, y해당 점과 X+축에서 각도를 반환하는 것을 사용 하는 것입니다.

계산이

double result = atan2(P3.y - P1.y, P3.x - P1.x) -
                atan2(P2.y - P1.y, P2.x - P1.x);

즉, 기본적으로 두 점을 -P1(즉 P1, 원점에서 끝나 도록 모든 것을 번역) 변환 한 다음의 P3및의 절대 각도의 차이를 고려합니다 P2.

의 장점은 atan2완전한 원이 표시된다는 것입니다 (-π와 π 사이의 숫자를 얻을 수 있음). 대신 acos올바른 결과를 계산하기 위해 부호에 따라 여러 경우를 처리해야합니다.

에 대한 유일한 특이점은 atan2입니다 (0, 0)모두 의미 ... P2와는 P3달라야합니다 P1각도에 대한 이야기로 이해가되지 않는 경우 등.


답변 해 주셔서 감사합니다. 그것이 바로 제가 찾던 것입니다. 간단한 해결책이며 값이 음수 일 때 2pi를 더하면 시계 반대 방향 각도를 쉽게 얻을 수 있습니다.
Mario

@marcpt : atan2정확히이 문제에 필요한 것이지만, 대부분의 사람들이이 질문에 대해 이해하지 못하거나 왜 acos기반 솔루션이 나쁜지 이해할 수없는 것처럼 보입니다 . 운 좋게도 저는 "누군가가 인터넷에서 잘못되었습니다"( xkcd.com/386 ) 단계를 몇 년 전에 떠났고 명백한 방어를위한 싸움을 시작하지 않을 것입니다 :-)
6502

지적 해주셔서 감사합니다. 3D를 이렇게 처리 할 수 ​​있습니까?
nicoco

1
@nicoco : 3 차원에서 각도를 어떻게 정의합니까? 더 구체적으로 각도가 음수이거나 파이 (180도)보다 클 수 있습니까? 3d에서 두 개의 평행하지 않은 벡터는 평면을 정의하지만 평면은 두 측면에서 "보일"수 있습니다. 한쪽에서 보면 A는 B의 "왼쪽"으로, 다른 쪽에서는 "오른쪽"으로 나타납니다. .
6502

@ 6505 답변 주셔서 감사합니다. 문제를 생각하기 전에 게시했습니다. 그래도 지금 알아 냈어.
nicoco

19

JavaScript로 예를 들어 보겠습니다. 저는 그와 많이 싸웠습니다.

/**
 * Calculates the angle (in radians) between two vectors pointing outward from one center
 *
 * @param p0 first point
 * @param p1 second point
 * @param c center point
 */
function find_angle(p0,p1,c) {
    var p0c = Math.sqrt(Math.pow(c.x-p0.x,2)+
                        Math.pow(c.y-p0.y,2)); // p0->c (b)   
    var p1c = Math.sqrt(Math.pow(c.x-p1.x,2)+
                        Math.pow(c.y-p1.y,2)); // p1->c (a)
    var p0p1 = Math.sqrt(Math.pow(p1.x-p0.x,2)+
                         Math.pow(p1.y-p0.y,2)); // p0->p1 (c)
    return Math.acos((p1c*p1c+p0c*p0c-p0p1*p0p1)/(2*p1c*p0c));
}

보너스 : HTML5 캔버스의 예


5
더 적은 작업을 수행 sqrt하고 제곱 하여이를 더 효율적으로 만들 수 있습니다 . 여기 (Ruby로 작성된) 또는이 업데이트 된 데모 (JavaScript) 에서 내 대답을 참조하십시오 .
Phrogz 2014

더 간단한 솔루션을 위해 atan2를 사용할 수 있습니다.
Luc Boissaye

15

기본적으로 두 개의 벡터가 있습니다. 하나는 P1에서 P2로, 다른 하나는 P1에서 P3까지입니다. 따라서 두 벡터 사이의 각도를 계산하는 공식 만 있으면됩니다.

봐 가지고 여기 좋은 설명과 수식을.

대체 텍스트


12

P1을 원의 중심으로 생각한다면 너무 복잡하다고 생각하는 것입니다. 간단한 삼각형이 있으므로 코사인 법칙으로 문제를 해결할 수 있습니다 . 극좌표 변형 등이 필요하지 않습니다. 거리가 P1-P2 = A, P2-P3 = B 및 P3-P1 = C라고 가정합니다.

각도 = arccos ((B ^ 2-A ^ 2-C ^ 2) / 2AC)

당신이해야 할 일은 거리 A, B, C의 길이를 계산하는 것뿐입니다. 그것들은 당신의 포인트와 피타고라스의 정리 의 x, y 좌표에서 쉽게 구할 수 있습니다.

길이 = sqrt ((X2-X1) ^ 2 + (Y2-Y1) ^ 2)


P1 등을 (x, y)가 아닌 개별 값으로 취급하므로 실제로 구현하는 방법에 대해 약간 혼란 스럽습니다.
Dominic

@Dominic Tobias : 표기법 P1-P2 = A은 "A를 계산하려면 P1에서 P2를 뺍니다"로 읽어서는 안되며 "나는 P1에서 P2까지의 거리로 A를 정의합니다"로 읽어야합니다. 그러면 두 번째 방정식을 사용하여 계산할 수 있습니다. 나는 방정식을 더 읽기 쉽게 만들기 위해 거리에 대한 속기를 정의하고 싶었습니다.
Treb 2017-04-20

8

최근에 비슷한 문제가 발생했지만 양의 각도와 음의 각도를 구분해야했습니다. 이것이 누구에게나 유용한 경우, Android 용 터치 이벤트에 대한 회전 감지에 대해이 메일 링 목록 에서 가져온 코드 스 니펫을 권장합니다 .

 @Override
 public boolean onTouchEvent(MotionEvent e) {
    float x = e.getX();
    float y = e.getY();
    switch (e.getAction()) {
    case MotionEvent.ACTION_MOVE:
       //find an approximate angle between them.

       float dx = x-cx;
       float dy = y-cy;
       double a=Math.atan2(dy,dx);

       float dpx= mPreviousX-cx;
       float dpy= mPreviousY-cy;
       double b=Math.atan2(dpy, dpx);

       double diff  = a-b;
       this.bearing -= Math.toDegrees(diff);
       this.invalidate();
    }
    mPreviousX = x;
    mPreviousY = y;
    return true;
 }

7

설명이있는 매우 간단한 기하학적 솔루션

며칠 전, 같은 문제에 빠졌고 수학 책과 함께 앉아야했습니다. 몇 가지 기본 공식을 결합하고 단순화하여 문제를 해결했습니다.


이 그림을 고려해 봅시다.

각도

ϴ 를 알고 싶기 때문에 먼저 αβ 를 알아 내야 합니다. 이제 모든 직선에 대해

y = m * x + c

Let- A = (ax, ay) , B = (bx, by) , O = (ox, oy) . 따라서 OA 라인의 경우 -

oy = m1 * ox + c   ⇒ c = oy - m1 * ox   ...(eqn-1)

ay = m1 * ax + c   ⇒ ay = m1 * ax + oy - m1 * ox   [from eqn-1]
                   ⇒ ay = m1 * ax + oy - m1 * ox
                   ⇒ m1 = (ay - oy) / (ax - ox)
                   ⇒ tan α = (ay - oy) / (ax - ox)   [m = slope = tan ϴ]   ...(eqn-2)

같은 방식으로 라인 OB의 경우 -

tan β = (by - oy) / (bx - ox)   ...(eqn-3)

이제 ϴ = β - α. 삼각법에서는 공식이 있습니다.

tan (β-α) = (tan β + tan α) / (1 - tan β * tan α)   ...(eqn-4)

tan αeqn-4에서 (from eqn-2) 및 tan b(from eqn-3) 의 값을 바꾸고 단순화를 적용하면

tan (β-α) = ( (ax-ox)*(by-oy)+(ay-oy)*(bx-ox) ) / ( (ax-ox)*(bx-ox)-(ay-oy)*(by-oy) )

그래서,

ϴ = β-α = tan^(-1) ( ((ax-ox)*(by-oy)+(ay-oy)*(bx-ox)) / ((ax-ox)*(bx-ox)-(ay-oy)*(by-oy)) )

그게 다야!


이제 다음 그림을보십시오.

각도

이 C # 또는 Java 방법은 각도 ( ϴ )를 계산합니다.

    private double calculateAngle(double P1X, double P1Y, double P2X, double P2Y,
            double P3X, double P3Y){

        double numerator = P2Y*(P1X-P3X) + P1Y*(P3X-P2X) + P3Y*(P2X-P1X);
        double denominator = (P2X-P1X)*(P1X-P3X) + (P2Y-P1Y)*(P1Y-P3Y);
        double ratio = numerator/denominator;

        double angleRad = Math.Atan(ratio);
        double angleDeg = (angleRad*180)/Math.PI;

        if(angleDeg<0){
            angleDeg = 180+angleDeg;
        }

        return angleDeg;
    }

이 방법을 정삼각형에 어떻게 사용할 수 있습니까?
Vikrant

1
글쎄, 당신의 대답은 이제 잘 작동합니다. 이전 코드 주에서 논리 문제였습니다.
Vikrant

6

Objective-C에서는 다음과 같이 할 수 있습니다.

float xpoint = (((atan2((newPoint.x - oldPoint.x) , (newPoint.y - oldPoint.y)))*180)/M_PI);

또는 여기에서 더 많은 것을 읽으 십시오


7
어, 아니. 세 개의 점이 있으며 중심은 (0,0)에 있지 않으며 정점 각도가 아닌 직각 삼각형의 각도를 제공합니다. 각도의 "xpoint"는 어떤 이름입니까?
Jim Balter 2015

4

부호있는 각도 (-90)를 언급하셨습니다. 많은 응용 프로그램에서 각도에 부호가있을 수 있습니다 (양수 및 음수, http://en.wikipedia.org/wiki/Angle 참조 ). 점이 (예를 들어) P2 (1,0), P1 (0,0), P3 (0,1)이면 각도 P3-P1-P2는 일반적으로 양수 (PI / 2)이고 각도 P2-P1- P3은 음수입니다. 변의 길이를 사용해도 +와-를 구분하지 못하므로, 이것이 중요하다면 벡터 나 Math.atan2 (a, b)와 같은 함수를 사용해야합니다.

각도는 또한 2 * PI 이상으로 확장 될 수 있으며 이것은 현재 질문과 관련이 없지만 내 자신의 각도 클래스를 작성하는 것이 충분히 중요했습니다 (도 및 라디안이 혼합되지 않았는지 확인하기 위해). angle1이 angle2보다 작은 지 여부에 대한 질문은 각도 정의 방법에 따라 결정적으로 달라집니다. 줄 (-1,0) (0,0) (1,0)이 Math.PI 또는 -Math.PI로 표시되는지 여부를 결정하는 것도 중요 할 수 있습니다.


4

내 각도 데모 프로그램

최근 저도 같은 문제가 있습니다. Delphi에서는 Objective-C와 매우 유사합니다.

procedure TForm1.FormPaint(Sender: TObject);
var ARect: TRect;
    AWidth, AHeight: Integer;
    ABasePoint: TPoint;
    AAngle: Extended;
begin
  FCenter := Point(Width div 2, Height div 2);
  AWidth := Width div 4;
  AHeight := Height div 4;
  ABasePoint := Point(FCenter.X+AWidth, FCenter.Y);
  ARect := Rect(Point(FCenter.X - AWidth, FCenter.Y - AHeight),
    Point(FCenter.X + AWidth, FCenter.Y + AHeight));
  AAngle := ArcTan2(ClickPoint.Y-Center.Y, ClickPoint.X-Center.X) * 180 / pi;
  AngleLabel.Caption := Format('Angle is %5.2f', [AAngle]);
  Canvas.Ellipse(ARect);
  Canvas.MoveTo(FCenter.X, FCenter.Y);
  Canvas.LineTo(FClickPoint.X, FClickPoint.Y);
  Canvas.MoveTo(FCenter.X, FCenter.Y);
  Canvas.LineTo(ABasePoint.X, ABasePoint.Y);
end;

2

다음은 원의 한 점에 대해 수평에서 시계 반대 방향으로 각도 (0-360)를 반환하는 C # 메서드입니다.

    public static double GetAngle(Point centre, Point point1)
    {
        // Thanks to Dave Hill
        // Turn into a vector (from the origin)
        double x = point1.X - centre.X;
        double y = point1.Y - centre.Y;
        // Dot product u dot v = mag u * mag v * cos theta
        // Therefore theta = cos -1 ((u dot v) / (mag u * mag v))
        // Horizontal v = (1, 0)
        // therefore theta = cos -1 (u.x / mag u)
        // nb, there are 2 possible angles and if u.y is positive then angle is in first quadrant, negative then second quadrant
        double magnitude = Math.Sqrt(x * x + y * y);
        double angle = 0;
        if(magnitude > 0)
            angle = Math.Acos(x / magnitude);

        angle = angle * 180 / Math.PI;
        if (y < 0)
            angle = 360 - angle;

        return angle;
    }

건배, 폴


2

function p(x, y) {return {x,y}}

function normaliseToInteriorAngle(angle) {
	if (angle < 0) {
		angle += (2*Math.PI)
	}
	if (angle > Math.PI) {
		angle = 2*Math.PI - angle
	}
	return angle
}

function angle(p1, center, p2) {
	const transformedP1 = p(p1.x - center.x, p1.y - center.y)
	const transformedP2 = p(p2.x - center.x, p2.y - center.y)

	const angleToP1 = Math.atan2(transformedP1.y, transformedP1.x)
	const angleToP2 = Math.atan2(transformedP2.y, transformedP2.x)

	return normaliseToInteriorAngle(angleToP2 - angleToP1)
}

function toDegrees(radians) {
	return 360 * radians / (2 * Math.PI)
}

console.log(toDegrees(angle(p(-10, 0), p(0, 0), p(0, -10))))


0

고등학교 수학을 이용한 간단한 답이 있습니다.

당신이 3 점을 가지고 있다고합시다

지점 A에서 B까지 각도를 얻으려면

angle = atan2(A.x - B.x, B.y - A.y)

지점 B에서 C까지의 각도를 얻으려면

angle2 = atan2(B.x - C.x, C.y - B.y)

Answer = 180 + angle2 - angle
If (answer < 0){
    return answer + 360
}else{
    return answer
}

방금 만든 최근 프로젝트에서이 코드를 사용했습니다. B를 P1로 변경합니다. 원하는 경우 "180 +"를 제거하는 것이 좋습니다.


-1

글쎄, 다른 답변은 필요한 모든 것을 다루는 것처럼 보이므로 JMonkeyEngine을 사용하는 경우 이것을 추가하고 싶습니다.

Vector3f.angleBetween(otherVector)

그것이 내가 여기에 온 것이므로 :)


-2
      Atan2        output in degrees
       PI/2              +90
         |                | 
         |                |    
   PI ---.--- 0   +180 ---.--- 0       
         |                |
         |                |
       -PI/2             +270

public static double CalculateAngleFromHorizontal(double startX, double startY, double endX, double endY)
{
    var atan = Math.Atan2(endY - startY, endX - startX); // Angle in radians
    var angleDegrees = atan * (180 / Math.PI);  // Angle in degrees (can be +/-)
    if (angleDegrees < 0.0)
    {
        angleDegrees = 360.0 + angleDegrees;
    }
    return angleDegrees;
}

// Angle from point2 to point 3 counter clockwise
public static double CalculateAngle0To360(double centerX, double centerY, double x2, double y2, double x3, double y3)
{
    var angle2 = CalculateAngleFromHorizontal(centerX, centerY, x2, y2);
    var angle3 = CalculateAngleFromHorizontal(centerX, centerY, x3, y3);
    return (360.0 + angle3 - angle2)%360;
}

// Smaller angle from point2 to point 3
public static double CalculateAngle0To180(double centerX, double centerY, double x2, double y2, double x3, double y3)
{
    var angle = CalculateAngle0To360(centerX, centerY, x2, y2, x3, y3);
    if (angle > 180.0)
    {
        angle = 360 - angle;
    }
    return angle;
}

}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.