您可以使用线性代数或不同的三角函数来解决这个问题。
给定一个向量A(xi,yj,zk),其中i,j,k 是该向量的unit axis vectors,x,y,z 是该向量的magnitudes,您可以通过选择存在于@987654331 上的两个单独的points 来构造一个line @ 中的vector 使得点P1(xi1,yj1,zk1) 和P2(xi2,yj2,zk2) 存在于由向量A 生成的线L 上。我们知道L = P2 - P1 的线段存在或平行于A,因此给你一个dot product of 1,如果dot product is 0 它们是Orthogonal 或Perpendicular。然后,您可以从这两点中找到该线的 3D 斜率,使用该帖子StackExchange:Mathematics 的已接受答案中的数学计算,并且一旦您获得了该线的斜率和基础数学; orthogonal 到另一个他们的产品将 = -1。在二维欧几里得几何中,如果斜率为 1/2,则其垂线的斜率为 -2;因此,一旦您找到由原始 3D 矢量生成的那条线的斜率,您需要做的就是找到它的负倒数,但这仅适用于 2D,并且使用该值,您可以使用该值以及原始 @987654344 上的任意点@ 生成一个有效的任意垂直向量。
如果您知道三角函数及其性质;特定函数将具有值 1、-1、0、1/2 以及其他特定值作为输出,并具有相应的特定值作为输入,例如:
sin A { 0PI, PI, 2PI, 0deg, 180deg, 360deg } = 0
sin A { PI/2, 90deg } = 1
sin A { 3PI/2, 270deg } = -1
cos A { 0PI, 2PI, 0deg, 360deg } = 1
cos A { PI/2, 3PI/2, 90deg, 270deg } = 0
cos A { PI, 180deg } = -1
tan A { 0PI, PI, 2PI, 0deg, 180deg, 360deg } = 0
tan A { PI/4, 45deg } = 1
tan A { PI/2, 3PI/2, 90deg, 270deg } = undefined.
有了这些函数,知道它们之间的关系,知道两个斜率的乘积 = -1。我们可以看到,这些函数中最容易使用的是cos A,其中A = PI or 180 degrees 并知道三角函数的恒等函数,例如倒数、商、毕达哥拉斯、协函数、(偶/奇)恒等式以及各种公式例如 Sum & Difference、Double Angle、Power Reducing、Sum to Product 和 Product to Sum 公式以及三角函数的弧函数和反函数,您应该能够轻松找到与余弦版本的垂直线通过Law of Cosines 进行点积和叉积。这里有更多关于这个的数学:dot:cross 和这里wiki
现在我们知道了一些涉及的数学及其属性,并且我们有了一个向量 A(x,y,z),我们可以使用以下内容:
// Values of Cosine At Specific Angles
cos (PI) = -1, cos (PI/2) = 0, cos (PI) = 1
// Orthogonal Vectors
A dot B = 0
// Parallel Vectors - Multiples Of Each Other
A(1,2)
B(2,4)
C(4,8)
etc.
// Angle Between To Vectors where neither vector is a 0 vector
cos (theta) = (A dot B) / (magnitude(A) * magnitude(B))
这些为我们构建称为单位向量的基向量奠定了基础
或原始向量的归一化向量,并且从该归一化向量中,知道上面列出的几个属性,很容易从中计算出正交向量。要对向量进行归一化以获得其单位向量,可以使用以下公式或函数:
C++ 版本
// -----------------------------------------------------------------------
// normalize()
// Make The Length Of This Vector Equal To One
inline void Vector3::normalize() {
float magnitude;
magnitude = sqrt( x*x + y*y + z*z );
if ( magnitude <= Math::ZERO ) { // Math::ZERO = (float)1e-7;
x = 0.0f;
x = 0.0f;
x = 0.0f;
return;
}
magnitude = 1 / magnitude;
x *= magnitude;
y *= magnitude;
z *= magnitude;
} // Normalize
// -----------------------------------------------------------------------
// getCosAngle()
// Returns The cos(Angle) Value Between This Vector And Vector V. This
// Is Less Expensive Than Using GetAngle
inline float Vector3::getCosAngle( const Vector3 &v3, const bool normalized ) {
// a . b = |a||b|cos(angle)
// -> cos-1((a.b)/(|a||b|))
// Make Sure We Do Not Divide By Zero
float magnitudeA = length();
if ( magnitudeA <= Math::ZERO ) {
// This (A) Is An Invalid Vector
return 0;
}
float value = 0;
if ( normalized ) {
// v3 Is Already Normalized
value = dot(v3)/magnitudeA;
} else {
float magnitudeB = v3.length();
if ( magnitudeB <= Math::ZERO) {
// B Is An Invalid Vector
return 0;
}
value = dot(v3)/(magnitudeA * magnitudeB);
}
// Correct Value Due To Rounding Problem
Math::constrain( -1.0f, 1.0f, value );
return value;
} // getCosAngle
// -----------------------------------------------------------------------
// length()
// Return The Length Of This Vector
inline float Vector3::length() const {
return sqrtf( x*x + y*y + z*z );
} // length
// -----------------------------------------------------------------------
// Constrain()
// Prevent Value From Going Outside The Min, Max Range.
template<class T>
inline void Math::constrain( T min, T max, T &value ) {
if ( value < min ) {
value = min;
return;
}
if ( value > max ) {
value = max;
}
} // constrain
一旦你有一个向量的总长度 = 1 的归一化向量。针对这个向量计算叉积和点积是非常便宜的,并且找到一个与之正交的向量很容易并且计算如下
p>
Vector3 original( 2,3,5 );
Vector3 arbitrary( 7,8,6 ); // Any vector that intersects original.
Vector3 findOrthogonalVector( const Vector3& original, const Vector3& arbitrary ) {
// Test Both Vectors To See If Either Are the 0 Vector If So Just Return the Zero Vector And Be Done
if ( original.isZero() || arbitrary.isZero() ) {
return Vector3( 0.0f, 0.0f, 0.0f );
}
Vector3 orthogonal(); // default constructor set to (0,0,0);
original.normalize(); // Total Length or Magnitude of 1 but Gives Direction
arbitrary.normalize();
// This will not give you values in degrees; it will give you the value of the cosine at a given angle but not the angle itself for example cos(90) = -1
// This will give a value of -1 and not 90.
float cosAngle = original.getCosAngle( arbitrary, true ); // True since arbitrary is normalized
// Calculate Actual Angle From Trig Functions
float angle = someTrigFuncToGiveAngle( cosAngle );
// Clamp Angle between [0,180]
angle = clampAngle( angle, 0.0f, 180.0f );
float desiredAngle = 0;
// If a dot b = 0; where dot product = (ax*bx + ay*by + az*bz) then
if ( angle == Math::ZERO || angle == 180.0f ) {
// original & arbitrary are parallel (oops)
// Quick Fix or Quick Hack - Change X component by 1 and recursively call this function only in this case
arbitrary.x += 1;
return findOrthogonalVector( original, arbitrary );
} else if ( angle == 90.0f ) { // (if in degrees otherwise in radians)
// Already Orthogonal
return arbitrary;
} else if ( angle < 90.0f ) {
desiredAngle = 90.0f - angle;
} else if ( angle > 90.0f && angle < 180.0f ) {
desiredAngle = angle - 90.0f;
}
// With this new angle we should be able to find the orthogonal vector
// Rotate The Arbitrary Vector by desiredAngle
return orthogonal = arbitrary.rotate( desiredAngle );
}
上述算法中的以下方法未显示:
- someTrigFuncToGiveAngle( ... );
- clampAngle( ... );
- vector.rotate( ... );
这些可以留给 OP 做为作业。
所以基本算法如下:
- 取原始向量和任意向量都不能是零向量
- 如果其中一个是 0 个向量,则只需返回一个零向量并使用函数完成
- 标准化两个向量
- 计算两个向量之间的余弦角
- 从角度的余弦获取实际角度
- 夹角在 [0,180] 之间
- 检查角度是否 = 0;如果为真 - 并行将 x 分量更改一并再次调用,传入更新后的任意向量
- 检查角度是否 = 90;如果为真 - 已经正交返回任意
- 如果角度
- 如果角度 > 90;通过从中减去 90 来计算所需的角度,然后通过它旋转任意角度并返回正交或所需的向量