program tip

두 벡터 사이의 시계 방향 각도를 직접 계산하는 방법

radiobox 2020. 11. 23. 07:58
반응형

두 벡터 사이의 시계 방향 각도를 직접 계산하는 방법


두 벡터 (2D, 3D) 사이의 시계 방향 각도를 알고 싶습니다.

내적의 기본적인 방법은 내적 각도 (0-180도)를 제공하며 결과가 필요한 각도인지 또는 그 보수인지 결정하기 위해 if 문을 사용해야합니다.

시계 방향 각도를 직접 계산하는 방법을 알고 있습니까?


2D 케이스

내적 이 각도의 코사인에 비례하는 것처럼 행렬식 은 사인에 비례합니다. 따라서 다음과 같이 각도를 계산할 수 있습니다.

dot = x1*x2 + y1*y2      # dot product between [x1, y1] and [x2, y2]
det = x1*y2 - y1*x2      # determinant
angle = atan2(det, dot)  # atan2(y, x) or atan2(sin, cos)

이 각도의 방향은 좌표계의 방향과 일치합니다. A의 좌표 왼손잡이 시스템 , 즉 x는 오른쪽을 가리키는 및 Y를 컴퓨터 그래픽을위한 공통으로 아래로, 이것은 당신이 시계 방향으로 각도에 대한 긍정적 인 신호를 얻을 의미합니다. 좌표계의 방향이 y를 위로 하여 수학적이면 수학 의 관례대로 시계 반대 방향의 각도를 얻습니다. 입력 순서를 변경하면 기호가 변경되므로 기호가 마음에 들지 않으면 입력을 바꾸십시오.

3D 케이스

3D에서 임의로 배치 된 두 벡터는 둘 다에 수직 인 자체 회전 축을 정의합니다. 이 회전축은 고정 된 방향으로 제공되지 않으므로 회전 각도의 방향을 고유하게 고정 할 수 없습니다. 한 가지 일반적인 규칙은 각도를 항상 양수로 만들고 양의 각도에 맞도록 축 방향을 지정하는 것입니다. 이 경우 정규화 된 벡터의 내적은 각도를 계산하기에 충분합니다.

dot = x1*x2 + y1*y2 + z1*z2    #between [x1, y1, z1] and [x2, y2, z2]
lenSq1 = x1*x1 + y1*y1 + z1*z1
lenSq2 = x2*x2 + y2*y2 + z2*z2
angle = acos(dot/sqrt(lenSq1 * lenSq2))

3D에 포함 된 평면

한 가지 특별한 경우는 벡터가 임의로 배치되지 않고 알려진 법선 벡터 n이 있는 평면 내에있는 경우 입니다. 그러면 회전 축도 n 방향이되고 n 방향은 해당 축의 방향을 고정합니다. 이 경우 n행렬식포함하여 위의 2D 계산 을 적용하여 크기를 3 × 3으로 만들 수 있습니다.

dot = x1*x2 + y1*y2 + z1*z2
det = x1*y2*zn + x2*yn*z1 + xn*y1*z2 - z1*y2*xn - z2*yn*x1 - zn*y1*x2
angle = atan2(det, dot)

이것이 작동하는 한 가지 조건은 정규 벡터 n 이 단위 길이를 갖는다는 것입니다. 그렇지 않은 경우 정규화해야합니다.

트리플 제품으로

이 결정자 는 제안 된 편집에서 @Excrubulent가 지적한 것처럼 트리플 제품 으로 표현 될 수도 있습니다 .

det = n · (v1 × v2)

이것은 일부 API에서 구현하기가 더 쉬울 수 있으며 여기서 진행되는 작업에 대한 다른 관점을 제공합니다. 외적은 각도의 사인에 비례하고 평면에 수직이므로 n 의 배수가 됩니다. 따라서 내적은 기본적으로 해당 벡터의 길이를 측정하지만 올바른 기호가 첨부되어 있습니다.


각도를 계산하려면 atan2(v1.s_cross(v2), v1.dot(v2))2D 케이스 를 호출하면 됩니다. s_cross교차 생산의 스칼라 아날로그는 어디에 있습니까 (평행 사변형의 부호있는 영역). 웨지 생산이 될 2D 케이스의 경우. 3D의 경우 시계 방향으로 회전을 정의해야합니다. 평면의 한 쪽에서 시계 방향은 한 방향이고 다른 쪽에서는 다른 방향입니다 =)

편집 : 이것은 시계 반대 방향 각도이고 시계 방향 각도는 반대입니다.


이 답변은 MvG와 동일하지만 다르게 설명합니다 (MvG의 솔루션이 작동하는 이유를 이해하려는 노력의 결과입니다). 다른 사람들이 유용하다고 생각할 기회에 게시하고 있습니다.

반 시계 방향으로 각 thetaxy통상 그들의 소정의 관점에 대해는 n( ||n|| = 1)로 주어진다

atan2 (dot (n, cross (x, y)), dot (x, y))

(1) = atan2 (|| x || || y || sin (theta), || x || || y || cos (theta))

(2) = atan2 (sin (theta), cos (theta))

(3) = x 축과 벡터 사이의 반 시계 방향 각도 (cos (theta), sin (theta))

(4) = 세타

여기서는 ||x||의 크기를 나타냅니다 x.

단계 (1)은

십자가 (x, y) = || x || || y || sin (theta) n,

그래서

dot (n, cross (x, y))

= dot (n, || x || || y || sin (theta) n)

= || x || || y || sin (theta) dot (n, n)

이것은

|| x || || y || sin (세타)

경우 ||n|| = 1.

단계 (2)의 정의로부터 다음 atan2것을주의 atan2(cy, cx) = atan2(y,x)여기서, c는 스칼라이다. 단계 (3)은 atan2. 단계 (4)는 cos의 기하학적 정의를 따릅니다 sin.


두 벡터의 스칼라 (점) 곱을 사용하면 두 벡터 사이의 각도에 대한 cosinus를 얻을 수 있습니다. 각도의 '방향'을 얻으려면 외적도 계산해야합니다. z 좌표를 통해 각도가 시계 방향인지 아닌지 (즉, 360도에서 추출해야하는지 여부)를 확인할 수 있습니다.


2D 방법의 경우 코사인 법칙과 "방향"방법을 사용할 수 있습니다.

세그먼트 P3 : P2에서 시계 방향으로 스위핑하는 세그먼트 P3 : P1의 각도를 계산합니다.

 
    P1 P2

        P3
    double d = direction(x3, y3, x2, y2, x1, y1);

    // c
    int d1d3 = distanceSqEucl(x1, y1, x3, y3);

    // b
    int d2d3 = distanceSqEucl(x2, y2, x3, y3);

    // a
    int d1d2 = distanceSqEucl(x1, y1, x2, y2);

    //cosine A = (b^2 + c^2 - a^2)/2bc
    double cosA = (d1d3 + d2d3 - d1d2)
        / (2 * Math.sqrt(d1d3 * d2d3));

    double angleA = Math.acos(cosA);

    if (d > 0) {
        angleA = 2.*Math.PI - angleA;
    }

This has the same number of transcendental

operations as suggestions above and only one more or so floating point operation.

the methods it uses are:

 public int distanceSqEucl(int x1, int y1, 
    int x2, int y2) {

    int diffX = x1 - x2;
    int diffY = y1 - y2;
    return (diffX * diffX + diffY * diffY);
}

public int direction(int x1, int y1, int x2, int y2, 
    int x3, int y3) {

    int d = ((x2 - x1)*(y3 - y1)) - ((y2 - y1)*(x3 - x1));

    return d;
}

If by "direct way" you mean avoiding the if statement, then I don't think there is a really general solution.

However, if your specific problem would allow loosing some precision in angle discretization and you are ok with loosing some time in type conversions, you can map the [-pi,pi) allowed range of phi angle onto the allowed range of some signed integer type. Then you would get the complementarity for free. However, I didn't really use this trick in practice. Most likely, the expense of float-to-integer and integer-to-float conversions would outweigh any benefit of the directness. It's better to set your priorities on writing autovectorizable or parallelizable code when this angle computation is done a lot.

Also, if your problem details are such that there is a definite more likely outcome for the angle direction, then you can use compilers' builtin functions to supply this information to the compiler, so it can optimize the branching more efficiently. E.g., in case of gcc, that's __builtin_expect function. It's somewhat more handy to use when you wrap it into such likely and unlikely macros (like in linux kernel):

#define likely(x)      __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)

A formula for clockwise angle,2D case, between 2 vectors, xa,ya and xb,yb.

Angle(vec.a-vec,b)=pi()/2*((1+sign(ya))* (1-sign(xa^2))-(1+sign(yb))* (1-sign(xb^2)))

                        +pi()/4*((2+sign(ya))*sign(xa)-(2+sign(yb))*sign(xb))

                        +sign(xa*ya)*atan((abs(ya)-abs(xa))/(abs(ya)+abs(xa)))

                        -sign(xb*yb)*atan((abs(yb)-abs(xb))/(abs(yb)+abs(xb)))

just copy & paste this.

angle = (acos((v1.x * v2.x + v1.y * v2.y)/((sqrt(v1.x*v1.x + v1.y*v1.y) * sqrt(v2.x*v2.x + v2.y*v2.y))))/pi*180);

you're welcome ;-)

참고URL : https://stackoverflow.com/questions/14066933/direct-way-of-computing-clockwise-angle-between-2-vectors

반응형