두 벡터 (2D, 3D) 사이의 시계 방향 각도를 알고 싶습니다.
내적의 기본적인 방법은 내적 각도 (0-180도)를 제공하며 결과가 필요한 각도인지 또는 그 보수인지 결정하기 위해 if 문을 사용해야합니다.
시계 방향 각도를 직접 계산하는 방법을 알고 있습니까?
두 벡터 (2D, 3D) 사이의 시계 방향 각도를 알고 싶습니다.
내적의 기본적인 방법은 내적 각도 (0-180도)를 제공하며 결과가 필요한 각도인지 또는 그 보수인지 결정하기 위해 if 문을 사용해야합니다.
시계 방향 각도를 직접 계산하는 방법을 알고 있습니까?
답변:
내적 이 각도의 코사인에 비례하는 것처럼 행렬식 은 사인에 비례합니다. 따라서 다음과 같이 각도를 계산할 수 있습니다.
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에서 임의로 배치 된 두 벡터는 둘 다에 수직 인 자체 회전 축을 정의합니다. 이 회전축은 고정 된 방향으로 제공되지 않으므로 회전 각도의 방향을 고유하게 고정 할 수 없습니다. 한 가지 일반적인 규칙은 각도를 항상 양수로 만들고 양의 각도에 맞도록 축 방향을 지정하는 것입니다. 이 경우 정규화 된 벡터의 내적은 각도를 계산하기에 충분합니다.
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))
한 가지 특별한 경우는 벡터가 임의로 배치되지 않고 알려진 법선 벡터 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 의 배수가 됩니다. 따라서 내적은 기본적으로 해당 벡터의 길이를 측정하지만 올바른 기호가 부착되어 있습니다.
qAtan2(y, x)
(Qt 프레임 워크에서) 구현 인지 모르겠지만 누군가 나와 같은 문제가 있다면 도움이 될 수 있습니다.
atan2
일반적으로 [-180 °, 180 °] 범위에 있습니다. 얻으려면 [0 °, 360 °] 경우의 구분없이, 하나는 교체 할 수 있습니다 atan2(y,x)
로 atan2(-y,-x) + 180°
.
각도를 계산하려면 atan2(v1.s_cross(v2), v1.dot(v2))
2D 케이스 를 호출하면 됩니다. s_cross
교차 생산의 스칼라 아날로그는 어디에 있습니까 (평행 사변형의 부호있는 영역). 웨지 생산이 될 2D 케이스의 경우. 3D의 경우 시계 방향으로 회전을 정의해야합니다. 평면의 한 쪽에서 시계 방향은 한 방향이고 다른 쪽에서는 다른 방향이므로 =)
편집 : 이것은 시계 반대 방향 각도이고 시계 방향 각도는 반대입니다.
atan2f
첫 번째 인수로 y 좌표가 있으므로 angle = atan2f(v2.y, v2.x) - atan2f(v1.y, v1.x)
.
cross
및 dot
2 차원 경우 명시 적 공식에 의해,에 대한 모든 의심을 제거 할 것이라고 cross
3 차원 벡터를 반환 (하지만 당신이 무시할 수는 권장 사항입니다). -그렇지 않으면 하나의 atan2f
함수 호출 만 필요하기 때문에이 솔루션을 좋아합니다 .
이 답변은 MvG와 동일하지만 다르게 설명합니다 (MvG의 솔루션이 작동하는 이유를 이해하려는 노력의 결과입니다). 다른 사람들이 유용하다고 생각할 기회가있을 때 게시하고 있습니다.
주어진 법선 ( ) 의 관점에 대한 theta
에서에서 x
까지의 시계 반대 방향 각도 는 다음과 같이 지정됩니다.y
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
.
두 벡터의 스칼라 (점) 곱을 사용하면 두 벡터 사이 각도의 코사인을 얻을 수 있습니다. 각도의 '방향'을 얻으려면 외적도 계산해야합니다. 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
위의 제안과 같은 연산과 하나 이상의 부동 소수점 연산 만 있습니다.
사용하는 방법은 다음과 같습니다.
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
진술을 , 정말 일반적인 해결책은 없다고 생각합니다.
그러나 특정 문제가 각도 이산화에서 정밀도를 잃을 수 있고 유형 변환에서 시간을 잃어도 괜찮다면 [-pi, pi) 허용되는 파이 각도 범위를 일부 부호있는 정수 유형의 허용 범위에 매핑 할 수 있습니다. . 그러면 무료로 상보성을 얻을 수 있습니다. 그러나 나는 실제로이 트릭을 실제로 사용하지 않았습니다. 대부분의 경우 float-to-integer 및 integer-to-float 변환의 비용이 직접성의 이점을 능가합니다. 이 각도 계산이 많이 수행되면 자동 벡터화 또는 병렬화 가능한 코드 작성에 대한 우선 순위를 설정하는 것이 좋습니다.
또한 문제 세부 정보가 각도 방향에 대해 더 확실한 결과가있을 수있는 경우 컴파일러의 내장 함수를 사용하여이 정보를 컴파일러에 제공 할 수 있으므로 분기를보다 효율적으로 최적화 할 수 있습니다. 예를 들어 gcc의 경우 그 __builtin_expect
기능입니다. (리눅스 커널에서와 같이) 이러한 매크로 likely
와 unlikely
매크로 로 래핑 할 때 사용하는 것이 다소 더 편리합니다 .
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
xa, ya와 xb, yb의 두 벡터 사이의 시계 방향 각도, 2D 케이스에 대한 공식.
각도 (vec.a-vec, b) = pi () / 2 * ((1+ 기호 (ya)) * (1- 기호 (xa ^ 2))-(1+ 기호 (yb)) * (1- 부호 (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)))
복사하여 붙여 넣기 만하면됩니다.
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);
천만에요 ;-)
std::atan2()
않습니까?