以编程方式在向量相等性中查找标量
Finding scalar in a vector equality programatically
我有两个等式向量 V1 和 V2
x*V1 = V2
当我需要确定 x 时,V1 和 V2 都是已知的。我也很确定有一个 x 可以求解方程。
显然我可以手动建立一个方程组来得到 x。事实上,我不需要做任何高斯消除,因为它是如此简单的设置。这导致我尝试 x = V2.x / V1.x。然而,这可能导致被零除。
如果这很重要,我会用 C++ 编写。
谢谢!
只需循环遍历 V1 的坐标,找到第一个非零值,然后除法。
作为对我自己问题的回答
x = (V2.x + V2.y + V2.z) / (V1.x + V1.y + V1.z)
如果 V1 全部为零,则不会起作用,但在我的情况下不会发生这种情况。
怎么样:
double getX(const std::vector<double>& V1, const std::vector<double>& V2) {
if (V1.size() != V2.size()) return 0;
for( int i=0; i<V1.size(); i++ ){
if(V1[i] != 0) {
return v2[i] / v1[i];
}
}
return 0;
}
我有几个 classes 来自我的一个数学库的旧版本。我不再特别使用它们,因为我最近一直在使用 GLM 数学库,但为了这个答案的目的,它们将演示需要什么。
GeneralMath.h - 执行基本数学运算需要此通用数学 Class。
#ifndef GENERALMATH_H
#define GENERALMATH_H
#include "stdafx.h"
class Math {
public:
static const float PI;
static const float PI_HALVES;
static const float PI_THIRDS;
static const float PI_FOURTHS;
static const float PI_SIXTHS;
static const float PI_2;
static const float PI_INVx180;
static const float PI_DIV180;
static const float PI_INV;
static const float ZERO;
Math();
inline static bool isZero( float fValue );
inline static float sign( float fValue );
inline static int randomRange( int iMin, int iMax );
inline static float randomRange( float fMin, float fMax );
inline static float degree2Radian( float fDegrees );
inline static float radian2Degree( float fRadians );
inline static float correctAngle( float fAngle, bool bDegrees, float fAngleStart = 0.0f );
inline static float mapValue( float fMinY, float fMaxY, float fMinX, float fMaxX, float fValueX );
template<class T>
inline static void constrain( T min, T max, T &value );
template<class T>
inline static void swap( T &value1, T &value2 );
}; // Math
// -----------------------------------------------------------------------
// degree2Radian()
// Convert Angle In Degrees To Radians
inline float Math::degree2Radian( float fDegrees ) {
return fDegrees * PI_DIV180;
} // degree2Radian
// -----------------------------------------------------------------------
// radian2Degree()
// Convert Angle In Radians To Degrees
inline float Math::radian2Degree( float fRadians ) {
return fRadians * PI_INVx180;
} // radian2Degree
// -----------------------------------------------------------------------
// correctAngle()
// Returns An Angle Value That Is Alway Between fAngleStart And fAngleStart + 360
// If Radians Are Used, Then Range Is fAngleStart To fAngleStart + 2PI
inline float Math::correctAngle( float fAngle, bool bDegrees, float fAngleStart ) {
if ( bDegrees ) {
// Using Degrees
if ( fAngle < fAngleStart ) {
while ( fAngle < fAngleStart ) {
fAngle += 360.0f;
}
}
else if ( fAngle >= (fAngleStart + 360.0f) ) {
while ( fAngle >= (fAngleStart + 360.0f) ) {
fAngle -= 360.0f;
}
}
return fAngle;
}
else {
// Using Radians
if ( fAngle < fAngleStart ) {
while ( fAngle < fAngleStart ) {
fAngle += Math::PI_2;
}
}
else if ( fAngle >= (fAngleStart + Math::PI_2) ) {
while ( fAngle >= (fAngleStart + Math::PI_2) ) {
fAngle -= Math::PI_2;
}
}
return fAngle;
}
} // correctAngle
// -----------------------------------------------------------------------
// isZero()
// Tests If Input Value Is Close To Zero
inline bool Math::isZero( float fValue ) {
if ( (fValue > -ZERO) && (fValue < ZERO) ) {
return true;
}
return false;
} // isZero
// -----------------------------------------------------------------------
// sign()
// Returns 1 If Value Is Positive, -1 If Value Is Negative Or 0 Otherwise
inline float Math::sign( float fValue ) {
if ( fValue > 0 ) {
return 1.0f;
}
else if ( fValue < 0 ) {
return -1.0f;
}
return 0;
} // sign
// -----------------------------------------------------------------------
// randomRange()
// Return A Random Number Between iMin And iMax Where iMin < iMax
// NOTE: This function is quite old; haven't had time to update the random number generator system
inline int Math::randomRange( int iMin, int iMax ) {
if ( iMax < iMin ) {
swap( iMax, iMin );
}
return (iMin + ((iMax - iMin +1) * rand()) / (RAND_MAX+1) );
} // randomRange
// -----------------------------------------------------------------------
// randomRange()
// Return A Random Number Between fMin And fMax Where fMin < fMax
// NOTE: This function is quite old; haven't had time to update the random number generator system
inline float Math::randomRange( float fMin, float fMax ) {
if ( fMax < fMin ) {
swap( fMax, fMin );
}
return (fMin + (rand()/(float)RAND_MAX)*(fMax-fMin));
} // randomRange
// -----------------------------------------------------------------------
// mapValue()
// Returns The fValueY That Corresponds To A Point On The Line Going From Min To Max
inline float Math::mapValue( float fMinY, float fMaxY, float fMinX, float fMaxX, float fValueX ) {
if ( fValueX >= fMaxX ) {
return fMaxY;
}
else if ( fValueX <= fMinX ) {
return fMinY;
}
else {
float fM = (fMaxY - fMinY) / (fMaxX - fMinX);
float fB = fMaxY - fM * fMaxX;
return (fM*fValueX + fB);
}
} // mapValue
// -----------------------------------------------------------------------
// 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
// -----------------------------------------------------------------------
// swap()
template<class T>
inline void Math::swap( T &value1, T &value2 ) {
T temp;
temp = value1;
value1 = value2;
value2 = temp;
} // swap
#endif // GENERALMATH_H
GeneralMath.cpp
#include "stdafx.h"
#include "GeneralMath.h"
const float Math::PI = 4.0f * atan(1.0f); // tan(pi/4) = 1
const float Math::PI_HALVES = 0.50f * Math::PI;
const float Math::PI_THIRDS = Math::PI * 0.3333333333333f;
const float Math::PI_FOURTHS = 0.25f * Math::PI;
const float Math::PI_SIXTHS = Math::PI * 0.6666666666667f;
const float Math::PI_2 = 2.00f * Math::PI;
const float Math::PI_DIV180 = Math::PI / 180.0f;
const float Math::PI_INVx180 = 180.0f / Math::PI;
const float Math::PI_INV = 1.0f / Math::PI;
const float Math::ZERO = (float)1e-7;
// -----------------------------------------------------------------------
// Math()
// Default Constructor
Math::Math() {
} // Math
Vector3.h
#ifndef VECTOR3_H
#define VECTOR3_H
#include "stdafx.h"
#include "GeneralMath.h"
class Vector3 {
public:
union {
float m_f3[3];
struct {
float m_fx;
float m_fy;
float m_fz;
};
};
inline Vector3();
inline Vector3( float x, float y, float z );
inline Vector3( float *pfv );
~Vector3();
// Operators
inline Vector3 operator+( const Vector3 &v3 ) const;
inline Vector3 operator+() const;
inline Vector3& operator+=( const Vector3 &v3 );
inline Vector3 operator-( const Vector3 &v3 ) const;
inline Vector3 operator-() const;
inline Vector3& operator-=( const Vector3 &v3 );
inline Vector3 operator*( const float &fValue ) const;
inline Vector3& operator*=( const float &fValue );
inline Vector3 operator/( const float &fValue ) const;
inline Vector3& operator/=( const float &fValue );
// -------------------------------------------------------------------
// operator*()
// Pre Multiple Vector By A Scalar
inline friend Vector3 Vector3::operator*( const float &fValue, const Vector3 v3 ) {
return Vector3( fValue*v3.m_fx, fValue*v3.m_fy, fValue*v3.m_fz );
} // operator*
// -------------------------------------------------------------------
// operator/()
// Pre Divide Vector By A Scalar Value
inline friend Vector3 Vector3::operator/( const float &fValue, const Vector3 v3 ) {
Vector3 vec3;
if ( Math::isZero( v3.m_fx ) ) {
vec3.m_fx = 0.0f;
} else {
vec3.m_fx = fValue / v3.m_fx;
}
if ( Math::isZero( v3.m_fy ) ) {
vec3.m_fy = 0.0f;
} else {
vec3.m_fy = fValue / v3.m_fy;
}
if ( Math::isZero( v3.m_fz ) ) {
vec3.m_fz = 0.0f;
} else {
vec3.m_fz = fValue / v3.m_fz;
}
return vec3;
} // operator/
// -----------------------------------------------------------------
// operator/()
// Divide Vector by Vector component wise returns scalar value.
inline friend float Vector3::operator/( const Vector3& v1, const Vector3& v2 ) {
if ( Math::isZero( v2.m_fx ) ||
Math::isZero( v2.m_fy ) ||
Math::izZero( v2.m_fz ) ) {
throw ( std::string( "Divide by Zero" ) );
}
float val = v1.m_fx / v2.m_fx;
if ( (val == (v1.m_fy / v2.m_fy) ) &&
(val == (v1.m_fz / v2.m_fz) ) ) {
return val;
}
} // operator/
// Functions
inline float divideByVector( const Vector3& v );
inline Vector3 rotateX( float fRadians );
inline Vector3 rotateY( float fRadians );
inline Vector3 rotateZ( float fRadians );
inline void setPerpendicularXZ( Vector3 v3 );
inline void setPerpendicularXY( Vector3 v3 );
inline void setPerpendicularYZ( Vector3 v3 );
inline Vector3 cross( const Vector3 v3 ) const;
inline float dot( const Vector3 v3 ) const;
inline float getAngle( const Vector3 &v3, const bool bNormalized = false, bool bRadians = true );
inline float getCosAngle( const Vector3 &v3, const bool bNormalized = false );
inline float length() const;
inline float length2() const;
inline void normalize();
inline void zero();
inline bool isZero() const;
}; // Vector3
// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3() :
m_fx( 0.0f ),
m_fy( 0.0f ),
m_fz( 0.0f ) {
} // Vector3
// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3( float x, float y, float z ) :
m_fx( x ),
m_fy( y ),
m_fz( z ) {
} // Vector3
// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3( float *pfv ) {
m_fx = pfv[0];
m_fy = pfv[1];
m_fz = pfv[2];
} // Vector3
// -----------------------------------------------------------------------
// operator+()
// Unary - Operator:
inline Vector3 Vector3::operator+() const {
return *this;
} // operator+
// -----------------------------------------------------------------------
// operator+()
// Binary - Add Two Vectors Together
inline Vector3 Vector3::operator+( const Vector3 &v3 ) const {
return Vector3( m_fx + v3.m_fx,
m_fy + v3.m_fy,
m_fz + v3.m_fz );
} // operator+
// -----------------------------------------------------------------------
// operator+=()
// Add Two Vectors Together
inline Vector3 &Vector3::operator+=( const Vector3 &v3 ) {
m_fx += v3.m_fx;
m_fy += v3.m_fy;
m_fz += v3.m_fz;
return *this;
} // operator+=
// -----------------------------------------------------------------------
// operator-()
// Unary - Operator: Negate Each Value
inline Vector3 Vector3::operator-() const {
return Vector3( -m_fx, -m_fy, -m_fz );
} // operator-
// -----------------------------------------------------------------------
// operator-()
// Binary - Take This Vector And Subtract Another Vector From It
inline Vector3 Vector3::operator-( const Vector3 &v3 ) const {
return Vector3( m_fx - v3.m_fx,
m_fy - v3.m_fy,
m_fz - v3.m_fz );
} // operator-
// -----------------------------------------------------------------------
// operator-=()
// Subtract Two Vectors From Each Other
inline Vector3 &Vector3::operator-=( const Vector3 &v3 ) {
m_fx -= v3.m_fx;
m_fy -= v3.m_fy;
m_fz -= v3.m_fz;
return *this;
} // operator-=
// -----------------------------------------------------------------------
// operator*()
// Post Multiple Vector By A Scalar
inline Vector3 Vector3::operator*( const float &fValue ) const {
return Vector3( m_fx*fValue, m_fy*fValue, m_fz*fValue );
} // operator*
// -----------------------------------------------------------------------
// operator*=()
// Multiply This Vector By A Scalar
inline Vector3& Vector3::operator*=( const float &fValue ) {
m_fx *= fValue;
m_fy *= fValue;
m_fz *= fValue;
return *this;
} // operator*=
// -----------------------------------------------------------------------
// operator/()
// Post Divide Vector By A Scalar
inline Vector3 Vector3::operator/( const float &fValue ) const {
Vector3 v3;
if ( Math::isZero( fValue ) ) {
v3.m_fx = 0.0f;
v3.m_fy = 0.0f;
v3.m_fz = 0.0f;
} else {
float fValue_Inv = 1/fValue;
v3.m_fx = v3.m_fx * fValue_Inv;
v3.m_fy = v3.m_fy * fValue_Inv;
v3.m_fz = v3.m_fz * fValue_Inv;
}
return v3;
} // operator/
// -----------------------------------------------------------------------
// operator/=()
// Divide This Vector By A Scalar
inline Vector3& Vector3::operator/=( const float &fValue ) {
if ( Math::isZero( fValue ) ) {
m_fx = 0.0f;
m_fy = 0.0f;
m_fz = 0.0f;
} else {
float fValue_Inv = 1/fValue;
m_fx *= fValue_Inv;
m_fy *= fValue_Inv;
m_fy *= fValue_Inv;
}
return *this;
} // operator/=
// -----------------------------------------------------------------------
// divideByVector()
// Division Is Performed By A Vector Component Basis And Returns A Scalar
// With (x*V1) = V2 since (x*V1) = (x*V1i, x*V1j, x*V1k) = V2(i,j,k)
// Then As long as the this vector does not have a 0 component
// We only need to perform the division on the first element of this vector
// where x = V2i / V1i. No need to perform division on j or k.
inline float Vector3::divideByVector( const Vector3& v ) {
if ( Math::isZero( v.m_fx ) ||
Math::isZero( v.m_fy ) ||
Math::izZero( v.m_fz ) ) {
throw( std::string( "Divide By 0" ) );
}
float val = m_fx / v.m_fx;
if ( (val == (m_fy / v.m_fy)) &&
(val == (m_fz / v.m_fz)) ) {
return val;
}
} // divideByVector
// -----------------------------------------------------------------------
// rotateX()
// Rotate This Vector About The X Axis
inline Vector3 Vector3::rotateX( float fRadians ) {
Vector3 v3;
v3.m_fx = m_fx;
v3.m_fy = m_fy*cos( fRadians ) - m_fz*sin( fRadians );
v3.m_fz = m_fy*sin( fRadians ) + m_fz*cos( fRadians );
return v3;
} // rotateX
// -----------------------------------------------------------------------
// rotateY()
// Rotate This Vector About The Y Axis
inline Vector3 Vector3::rotateY( float fRadians ) {
Vector3 v3;
v3.m_fx = m_fx*cos( fRadians ) + m_fz*sin( fRadians );
v3.m_fy = m_fy;
v3.m_fz = -m_fx*sin( fRadians ) + m_fz*cos( fRadians );
return v3;
} // rotateY
// -----------------------------------------------------------------------
// rotateZ()
// Rotate This Vector About The Z Axis
inline Vector3 Vector3::rotateZ( float fRadians ) {
Vector3 v3;
v3.m_fx = m_fx*cos( fRadians ) - m_fy*sin( fRadians );
v3.m_fy = m_fx*sin( fRadians ) + m_fy*cos( fRadians );
v3.m_fz = m_fz;
return v3;
} // rotateZ
// -----------------------------------------------------------------------
// setPerpendicularXY()
// Make This Vector Perp To Vector3
inline void Vector3::setPerpendicularXY( Vector3 v3 ) {
m_fx = -v3.m_fy;
m_fy = v3.m_fx;
m_fz = v3.m_fz;
} // setPerpendicularXY
// -----------------------------------------------------------------------
// setPerpendicularXZ()
// Make This Vector Perp To Vector3
inline void Vector3::setPerpendicularXZ( Vector3 v3 ) {
m_fx = -v3.m_fz;
m_fy = v3.m_fy;
m_fz = v3.m_fx;
} // setPerpendicularXZ
// -----------------------------------------------------------------------
// setPerpendicularYX()
// Make This Vector Perp To Vector3
inline void Vector3::setPerpendicularYZ( Vector3 v3 ) {
m_fx = v3.m_fx;
m_fy = -v3.m_fz;
m_fz = v3.m_fy;
} // setPerpendicularYZ
// -----------------------------------------------------------------------
// cross()
// Get The Cross Product Of Two Vectors
inline Vector3 Vector3::cross( const Vector3 v3 ) const {
return Vector3( m_fy*v3.m_fz - m_fz*v3.m_fy,
v3.m_fx*m_fz - m_fx*v3.m_fz,
m_fx*v3.m_fy - m_fy*v3.m_fx );
} // cross
// -----------------------------------------------------------------------
// dot()
// Return The Dot Product Between This Vector And Another One
inline float Vector3::dot( const Vector3 v3 ) const {
return ( m_fx * v3.m_fx +
m_fy * v3.m_fy +
m_fz * v3.m_fz );
} // dot
// -----------------------------------------------------------------------
// normalize()
// Make The Length Of This Vector Equal To One
inline void Vector3::normalize() {
float fMag;
fMag = sqrt( m_fx*m_fx + m_fy*m_fy + m_fz*m_fz );
if ( fMag <= Math::ZERO ) {
m_fx = 0.0f;
m_fy = 0.0f;
m_fz = 0.0f;
return;
}
fMag = 1/fMag;
m_fx *= fMag;
m_fy *= fMag;
m_fz *= fMag;
} // normalize
// -----------------------------------------------------------------------
// isZero()
// Return True if Vector Is (0,0,0)
inline bool Vector3::isZero() const {
if ( Math::isZero( m_fx ) && Math::isZero( m_fy) && Math::isZero( m_fz ) ) {
return true;
} else {
return false;
}
} // isZero
// -----------------------------------------------------------------------
// zero()
// Set The Value To (0,0,0)
inline void Vector3::zero() {
m_fx = 0.0f;
m_fy = 0.0f;
m_fz = 0.0f;
} // Zero
// -----------------------------------------------------------------------
// 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 bNormalized ) {
// a . b = |a||b|cos(angle)
// -> cos-1((a.b)/(|a||b|))
// Make Sure We Do Not Divide By Zero
float fMagA = length();
if ( fMagA <= Math::ZERO ) {
// This (A) Is An Invalid Vector
return 0;
}
float fValue = 0;
if ( bNormalized ) {
// v3 Is Already Normalized
fValue = dot(v3)/fMagA;
}
else {
float fMagB = v3.length();
if ( fMagB <= Math::ZERO) {
// B Is An Invalid Vector
return 0;
}
fValue = dot(v3)/(fMagA*fMagB);
}
// Correct Value Due To Rounding Problem
Math::constrain( -1.0f, 1.0f, fValue );
return fValue;
} // getCosAngle
// -----------------------------------------------------------------------
// getAngle()
// Returns The Angle Between This Vector And Vector V in Radians.
// This Is More Expensive Than Using GetCosAngle
inline float Vector3::getAngle( const Vector3 &v3, const bool bNormalized, bool bRadians ) {
// a . b = |a||b|cos(angle)
// -> cos-1((a.b)/(|a||b|))
if ( bRadians ) {
return acos( this->getCosAngle( v3 ) );
}
else {
// Convert To Degrees
return Math::radian2Degree( acos( getCosAngle( v3, bNormalized ) ) );
}
} // GetAngle
// -----------------------------------------------------------------------
// length()
// Return The Length Of This Vector
inline float Vector3::length() const {
return sqrtf( m_fx * m_fx +
m_fy * m_fy +
m_fz * m_fz );
} // length
// -----------------------------------------------------------------------
// length2()
// Return The Length Of This Vector
inline float Vector3::length2() const {
return ( m_fx * m_fx +
m_fy * m_fy +
m_fz * m_fz );
} // length2
#endif // VECTOR3_H
Vector3.cpp
#include "stdafx.h"
#include "Vector3.h"
// -----------------------------------------------------------------------
// ~Vector3()
// Destructor
Vector3::~Vector3() {
} // ~Vector3
如果您在我的 Vector3 中注意到 class,我有一堆运算符和通常定义的函数,可以应用于 3 个分量的向量。现在,除法运算符 returns 一个 vector3 并将每个分量除以一个标量。因此,我还包含了友元运算符/ 和函数调用 divideByVector。他们会做同样的操作。在代码中使用它们非常简单。
#include "stdafx.h" // string isostream etc.
#include "Vector3.h"
int main() {
Vector3 v1 ( 4.0f, 4.0f, 4.0f );
Vector3 v2 ( 2.0f, 2.0f, 2.0f );
float value = v1.divideByVector( v2 );
std::cout << value << std::endl;
value = v2.divideByVector( v1 );
std::cout << value << std::endl;
std::cout << v1 / v2 << std::endl;
std::cout << v2 / v1 << std::endl;
v1.m_fx = 0;
v2.m_fx = 0;
std::cout << v1.divideByVector( v2 ) << std::endl;
std::cout << v2.divideByVector( v1 ) << std::endl;
std::cout << v1 / v2 << std::endl;
std::cout << v2 / v1 << std::endl;
std::cout << "\nPress any key to quit.\n" << std::endl;
_getch();
return 0;
} // main
您也可以尝试向量,如果一个向量的每个分量不是另一个分量的倍数,例如:v1( 15.0f, 25.0f, 30.0f ) & v2( 3.0f, 5.0f, 7.0f)
如果 v2's k component was 6.0f
,这将因 v2's k component
now 而失败那么返回的结果值应该是5
,如果你反转除法它应该是0.2
。现在使用我的编译器 (VS2015),如果这些值不是公倍数,则打印到屏幕上的消息是:
-nan(ind)
如果您将 0 作为值传递并尝试除以它,则调试器将抛出未处理的异常。通常这将作为消息抛出并捕获在 try { ... } catch( ... ) { }
块中。但是,我的 Logger 和 ExceptionHandler class 都无法用于此演示。另请注意,我不再使用 rand
或 srand
,因为我现在使用 <random>
库。因此,我在这个旧库中的一些函数已被弃用或修改。
提问者自己的解决方案是有缺陷的,因为如果 (V1.x + V1.y + V1.z) 加起来为零,它将失败:
x = (V2.x + V2.y + V2.z) / (V1.x + V1.y + V1.z)
下面是这个问题的正确解决方案:
x = (V2.x * V1.x + V2.y * V1.y + V2.z * V1.z) /
(V1.x * V1.x + V1.y * V1.y + V1.z * V1.z);
我有两个等式向量 V1 和 V2
x*V1 = V2
当我需要确定 x 时,V1 和 V2 都是已知的。我也很确定有一个 x 可以求解方程。
显然我可以手动建立一个方程组来得到 x。事实上,我不需要做任何高斯消除,因为它是如此简单的设置。这导致我尝试 x = V2.x / V1.x。然而,这可能导致被零除。
如果这很重要,我会用 C++ 编写。
谢谢!
只需循环遍历 V1 的坐标,找到第一个非零值,然后除法。
作为对我自己问题的回答
x = (V2.x + V2.y + V2.z) / (V1.x + V1.y + V1.z)
如果 V1 全部为零,则不会起作用,但在我的情况下不会发生这种情况。
怎么样:
double getX(const std::vector<double>& V1, const std::vector<double>& V2) {
if (V1.size() != V2.size()) return 0;
for( int i=0; i<V1.size(); i++ ){
if(V1[i] != 0) {
return v2[i] / v1[i];
}
}
return 0;
}
我有几个 classes 来自我的一个数学库的旧版本。我不再特别使用它们,因为我最近一直在使用 GLM 数学库,但为了这个答案的目的,它们将演示需要什么。
GeneralMath.h - 执行基本数学运算需要此通用数学 Class。
#ifndef GENERALMATH_H
#define GENERALMATH_H
#include "stdafx.h"
class Math {
public:
static const float PI;
static const float PI_HALVES;
static const float PI_THIRDS;
static const float PI_FOURTHS;
static const float PI_SIXTHS;
static const float PI_2;
static const float PI_INVx180;
static const float PI_DIV180;
static const float PI_INV;
static const float ZERO;
Math();
inline static bool isZero( float fValue );
inline static float sign( float fValue );
inline static int randomRange( int iMin, int iMax );
inline static float randomRange( float fMin, float fMax );
inline static float degree2Radian( float fDegrees );
inline static float radian2Degree( float fRadians );
inline static float correctAngle( float fAngle, bool bDegrees, float fAngleStart = 0.0f );
inline static float mapValue( float fMinY, float fMaxY, float fMinX, float fMaxX, float fValueX );
template<class T>
inline static void constrain( T min, T max, T &value );
template<class T>
inline static void swap( T &value1, T &value2 );
}; // Math
// -----------------------------------------------------------------------
// degree2Radian()
// Convert Angle In Degrees To Radians
inline float Math::degree2Radian( float fDegrees ) {
return fDegrees * PI_DIV180;
} // degree2Radian
// -----------------------------------------------------------------------
// radian2Degree()
// Convert Angle In Radians To Degrees
inline float Math::radian2Degree( float fRadians ) {
return fRadians * PI_INVx180;
} // radian2Degree
// -----------------------------------------------------------------------
// correctAngle()
// Returns An Angle Value That Is Alway Between fAngleStart And fAngleStart + 360
// If Radians Are Used, Then Range Is fAngleStart To fAngleStart + 2PI
inline float Math::correctAngle( float fAngle, bool bDegrees, float fAngleStart ) {
if ( bDegrees ) {
// Using Degrees
if ( fAngle < fAngleStart ) {
while ( fAngle < fAngleStart ) {
fAngle += 360.0f;
}
}
else if ( fAngle >= (fAngleStart + 360.0f) ) {
while ( fAngle >= (fAngleStart + 360.0f) ) {
fAngle -= 360.0f;
}
}
return fAngle;
}
else {
// Using Radians
if ( fAngle < fAngleStart ) {
while ( fAngle < fAngleStart ) {
fAngle += Math::PI_2;
}
}
else if ( fAngle >= (fAngleStart + Math::PI_2) ) {
while ( fAngle >= (fAngleStart + Math::PI_2) ) {
fAngle -= Math::PI_2;
}
}
return fAngle;
}
} // correctAngle
// -----------------------------------------------------------------------
// isZero()
// Tests If Input Value Is Close To Zero
inline bool Math::isZero( float fValue ) {
if ( (fValue > -ZERO) && (fValue < ZERO) ) {
return true;
}
return false;
} // isZero
// -----------------------------------------------------------------------
// sign()
// Returns 1 If Value Is Positive, -1 If Value Is Negative Or 0 Otherwise
inline float Math::sign( float fValue ) {
if ( fValue > 0 ) {
return 1.0f;
}
else if ( fValue < 0 ) {
return -1.0f;
}
return 0;
} // sign
// -----------------------------------------------------------------------
// randomRange()
// Return A Random Number Between iMin And iMax Where iMin < iMax
// NOTE: This function is quite old; haven't had time to update the random number generator system
inline int Math::randomRange( int iMin, int iMax ) {
if ( iMax < iMin ) {
swap( iMax, iMin );
}
return (iMin + ((iMax - iMin +1) * rand()) / (RAND_MAX+1) );
} // randomRange
// -----------------------------------------------------------------------
// randomRange()
// Return A Random Number Between fMin And fMax Where fMin < fMax
// NOTE: This function is quite old; haven't had time to update the random number generator system
inline float Math::randomRange( float fMin, float fMax ) {
if ( fMax < fMin ) {
swap( fMax, fMin );
}
return (fMin + (rand()/(float)RAND_MAX)*(fMax-fMin));
} // randomRange
// -----------------------------------------------------------------------
// mapValue()
// Returns The fValueY That Corresponds To A Point On The Line Going From Min To Max
inline float Math::mapValue( float fMinY, float fMaxY, float fMinX, float fMaxX, float fValueX ) {
if ( fValueX >= fMaxX ) {
return fMaxY;
}
else if ( fValueX <= fMinX ) {
return fMinY;
}
else {
float fM = (fMaxY - fMinY) / (fMaxX - fMinX);
float fB = fMaxY - fM * fMaxX;
return (fM*fValueX + fB);
}
} // mapValue
// -----------------------------------------------------------------------
// 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
// -----------------------------------------------------------------------
// swap()
template<class T>
inline void Math::swap( T &value1, T &value2 ) {
T temp;
temp = value1;
value1 = value2;
value2 = temp;
} // swap
#endif // GENERALMATH_H
GeneralMath.cpp
#include "stdafx.h"
#include "GeneralMath.h"
const float Math::PI = 4.0f * atan(1.0f); // tan(pi/4) = 1
const float Math::PI_HALVES = 0.50f * Math::PI;
const float Math::PI_THIRDS = Math::PI * 0.3333333333333f;
const float Math::PI_FOURTHS = 0.25f * Math::PI;
const float Math::PI_SIXTHS = Math::PI * 0.6666666666667f;
const float Math::PI_2 = 2.00f * Math::PI;
const float Math::PI_DIV180 = Math::PI / 180.0f;
const float Math::PI_INVx180 = 180.0f / Math::PI;
const float Math::PI_INV = 1.0f / Math::PI;
const float Math::ZERO = (float)1e-7;
// -----------------------------------------------------------------------
// Math()
// Default Constructor
Math::Math() {
} // Math
Vector3.h
#ifndef VECTOR3_H
#define VECTOR3_H
#include "stdafx.h"
#include "GeneralMath.h"
class Vector3 {
public:
union {
float m_f3[3];
struct {
float m_fx;
float m_fy;
float m_fz;
};
};
inline Vector3();
inline Vector3( float x, float y, float z );
inline Vector3( float *pfv );
~Vector3();
// Operators
inline Vector3 operator+( const Vector3 &v3 ) const;
inline Vector3 operator+() const;
inline Vector3& operator+=( const Vector3 &v3 );
inline Vector3 operator-( const Vector3 &v3 ) const;
inline Vector3 operator-() const;
inline Vector3& operator-=( const Vector3 &v3 );
inline Vector3 operator*( const float &fValue ) const;
inline Vector3& operator*=( const float &fValue );
inline Vector3 operator/( const float &fValue ) const;
inline Vector3& operator/=( const float &fValue );
// -------------------------------------------------------------------
// operator*()
// Pre Multiple Vector By A Scalar
inline friend Vector3 Vector3::operator*( const float &fValue, const Vector3 v3 ) {
return Vector3( fValue*v3.m_fx, fValue*v3.m_fy, fValue*v3.m_fz );
} // operator*
// -------------------------------------------------------------------
// operator/()
// Pre Divide Vector By A Scalar Value
inline friend Vector3 Vector3::operator/( const float &fValue, const Vector3 v3 ) {
Vector3 vec3;
if ( Math::isZero( v3.m_fx ) ) {
vec3.m_fx = 0.0f;
} else {
vec3.m_fx = fValue / v3.m_fx;
}
if ( Math::isZero( v3.m_fy ) ) {
vec3.m_fy = 0.0f;
} else {
vec3.m_fy = fValue / v3.m_fy;
}
if ( Math::isZero( v3.m_fz ) ) {
vec3.m_fz = 0.0f;
} else {
vec3.m_fz = fValue / v3.m_fz;
}
return vec3;
} // operator/
// -----------------------------------------------------------------
// operator/()
// Divide Vector by Vector component wise returns scalar value.
inline friend float Vector3::operator/( const Vector3& v1, const Vector3& v2 ) {
if ( Math::isZero( v2.m_fx ) ||
Math::isZero( v2.m_fy ) ||
Math::izZero( v2.m_fz ) ) {
throw ( std::string( "Divide by Zero" ) );
}
float val = v1.m_fx / v2.m_fx;
if ( (val == (v1.m_fy / v2.m_fy) ) &&
(val == (v1.m_fz / v2.m_fz) ) ) {
return val;
}
} // operator/
// Functions
inline float divideByVector( const Vector3& v );
inline Vector3 rotateX( float fRadians );
inline Vector3 rotateY( float fRadians );
inline Vector3 rotateZ( float fRadians );
inline void setPerpendicularXZ( Vector3 v3 );
inline void setPerpendicularXY( Vector3 v3 );
inline void setPerpendicularYZ( Vector3 v3 );
inline Vector3 cross( const Vector3 v3 ) const;
inline float dot( const Vector3 v3 ) const;
inline float getAngle( const Vector3 &v3, const bool bNormalized = false, bool bRadians = true );
inline float getCosAngle( const Vector3 &v3, const bool bNormalized = false );
inline float length() const;
inline float length2() const;
inline void normalize();
inline void zero();
inline bool isZero() const;
}; // Vector3
// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3() :
m_fx( 0.0f ),
m_fy( 0.0f ),
m_fz( 0.0f ) {
} // Vector3
// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3( float x, float y, float z ) :
m_fx( x ),
m_fy( y ),
m_fz( z ) {
} // Vector3
// -----------------------------------------------------------------------
// Vector3()
// Constructor
inline Vector3::Vector3( float *pfv ) {
m_fx = pfv[0];
m_fy = pfv[1];
m_fz = pfv[2];
} // Vector3
// -----------------------------------------------------------------------
// operator+()
// Unary - Operator:
inline Vector3 Vector3::operator+() const {
return *this;
} // operator+
// -----------------------------------------------------------------------
// operator+()
// Binary - Add Two Vectors Together
inline Vector3 Vector3::operator+( const Vector3 &v3 ) const {
return Vector3( m_fx + v3.m_fx,
m_fy + v3.m_fy,
m_fz + v3.m_fz );
} // operator+
// -----------------------------------------------------------------------
// operator+=()
// Add Two Vectors Together
inline Vector3 &Vector3::operator+=( const Vector3 &v3 ) {
m_fx += v3.m_fx;
m_fy += v3.m_fy;
m_fz += v3.m_fz;
return *this;
} // operator+=
// -----------------------------------------------------------------------
// operator-()
// Unary - Operator: Negate Each Value
inline Vector3 Vector3::operator-() const {
return Vector3( -m_fx, -m_fy, -m_fz );
} // operator-
// -----------------------------------------------------------------------
// operator-()
// Binary - Take This Vector And Subtract Another Vector From It
inline Vector3 Vector3::operator-( const Vector3 &v3 ) const {
return Vector3( m_fx - v3.m_fx,
m_fy - v3.m_fy,
m_fz - v3.m_fz );
} // operator-
// -----------------------------------------------------------------------
// operator-=()
// Subtract Two Vectors From Each Other
inline Vector3 &Vector3::operator-=( const Vector3 &v3 ) {
m_fx -= v3.m_fx;
m_fy -= v3.m_fy;
m_fz -= v3.m_fz;
return *this;
} // operator-=
// -----------------------------------------------------------------------
// operator*()
// Post Multiple Vector By A Scalar
inline Vector3 Vector3::operator*( const float &fValue ) const {
return Vector3( m_fx*fValue, m_fy*fValue, m_fz*fValue );
} // operator*
// -----------------------------------------------------------------------
// operator*=()
// Multiply This Vector By A Scalar
inline Vector3& Vector3::operator*=( const float &fValue ) {
m_fx *= fValue;
m_fy *= fValue;
m_fz *= fValue;
return *this;
} // operator*=
// -----------------------------------------------------------------------
// operator/()
// Post Divide Vector By A Scalar
inline Vector3 Vector3::operator/( const float &fValue ) const {
Vector3 v3;
if ( Math::isZero( fValue ) ) {
v3.m_fx = 0.0f;
v3.m_fy = 0.0f;
v3.m_fz = 0.0f;
} else {
float fValue_Inv = 1/fValue;
v3.m_fx = v3.m_fx * fValue_Inv;
v3.m_fy = v3.m_fy * fValue_Inv;
v3.m_fz = v3.m_fz * fValue_Inv;
}
return v3;
} // operator/
// -----------------------------------------------------------------------
// operator/=()
// Divide This Vector By A Scalar
inline Vector3& Vector3::operator/=( const float &fValue ) {
if ( Math::isZero( fValue ) ) {
m_fx = 0.0f;
m_fy = 0.0f;
m_fz = 0.0f;
} else {
float fValue_Inv = 1/fValue;
m_fx *= fValue_Inv;
m_fy *= fValue_Inv;
m_fy *= fValue_Inv;
}
return *this;
} // operator/=
// -----------------------------------------------------------------------
// divideByVector()
// Division Is Performed By A Vector Component Basis And Returns A Scalar
// With (x*V1) = V2 since (x*V1) = (x*V1i, x*V1j, x*V1k) = V2(i,j,k)
// Then As long as the this vector does not have a 0 component
// We only need to perform the division on the first element of this vector
// where x = V2i / V1i. No need to perform division on j or k.
inline float Vector3::divideByVector( const Vector3& v ) {
if ( Math::isZero( v.m_fx ) ||
Math::isZero( v.m_fy ) ||
Math::izZero( v.m_fz ) ) {
throw( std::string( "Divide By 0" ) );
}
float val = m_fx / v.m_fx;
if ( (val == (m_fy / v.m_fy)) &&
(val == (m_fz / v.m_fz)) ) {
return val;
}
} // divideByVector
// -----------------------------------------------------------------------
// rotateX()
// Rotate This Vector About The X Axis
inline Vector3 Vector3::rotateX( float fRadians ) {
Vector3 v3;
v3.m_fx = m_fx;
v3.m_fy = m_fy*cos( fRadians ) - m_fz*sin( fRadians );
v3.m_fz = m_fy*sin( fRadians ) + m_fz*cos( fRadians );
return v3;
} // rotateX
// -----------------------------------------------------------------------
// rotateY()
// Rotate This Vector About The Y Axis
inline Vector3 Vector3::rotateY( float fRadians ) {
Vector3 v3;
v3.m_fx = m_fx*cos( fRadians ) + m_fz*sin( fRadians );
v3.m_fy = m_fy;
v3.m_fz = -m_fx*sin( fRadians ) + m_fz*cos( fRadians );
return v3;
} // rotateY
// -----------------------------------------------------------------------
// rotateZ()
// Rotate This Vector About The Z Axis
inline Vector3 Vector3::rotateZ( float fRadians ) {
Vector3 v3;
v3.m_fx = m_fx*cos( fRadians ) - m_fy*sin( fRadians );
v3.m_fy = m_fx*sin( fRadians ) + m_fy*cos( fRadians );
v3.m_fz = m_fz;
return v3;
} // rotateZ
// -----------------------------------------------------------------------
// setPerpendicularXY()
// Make This Vector Perp To Vector3
inline void Vector3::setPerpendicularXY( Vector3 v3 ) {
m_fx = -v3.m_fy;
m_fy = v3.m_fx;
m_fz = v3.m_fz;
} // setPerpendicularXY
// -----------------------------------------------------------------------
// setPerpendicularXZ()
// Make This Vector Perp To Vector3
inline void Vector3::setPerpendicularXZ( Vector3 v3 ) {
m_fx = -v3.m_fz;
m_fy = v3.m_fy;
m_fz = v3.m_fx;
} // setPerpendicularXZ
// -----------------------------------------------------------------------
// setPerpendicularYX()
// Make This Vector Perp To Vector3
inline void Vector3::setPerpendicularYZ( Vector3 v3 ) {
m_fx = v3.m_fx;
m_fy = -v3.m_fz;
m_fz = v3.m_fy;
} // setPerpendicularYZ
// -----------------------------------------------------------------------
// cross()
// Get The Cross Product Of Two Vectors
inline Vector3 Vector3::cross( const Vector3 v3 ) const {
return Vector3( m_fy*v3.m_fz - m_fz*v3.m_fy,
v3.m_fx*m_fz - m_fx*v3.m_fz,
m_fx*v3.m_fy - m_fy*v3.m_fx );
} // cross
// -----------------------------------------------------------------------
// dot()
// Return The Dot Product Between This Vector And Another One
inline float Vector3::dot( const Vector3 v3 ) const {
return ( m_fx * v3.m_fx +
m_fy * v3.m_fy +
m_fz * v3.m_fz );
} // dot
// -----------------------------------------------------------------------
// normalize()
// Make The Length Of This Vector Equal To One
inline void Vector3::normalize() {
float fMag;
fMag = sqrt( m_fx*m_fx + m_fy*m_fy + m_fz*m_fz );
if ( fMag <= Math::ZERO ) {
m_fx = 0.0f;
m_fy = 0.0f;
m_fz = 0.0f;
return;
}
fMag = 1/fMag;
m_fx *= fMag;
m_fy *= fMag;
m_fz *= fMag;
} // normalize
// -----------------------------------------------------------------------
// isZero()
// Return True if Vector Is (0,0,0)
inline bool Vector3::isZero() const {
if ( Math::isZero( m_fx ) && Math::isZero( m_fy) && Math::isZero( m_fz ) ) {
return true;
} else {
return false;
}
} // isZero
// -----------------------------------------------------------------------
// zero()
// Set The Value To (0,0,0)
inline void Vector3::zero() {
m_fx = 0.0f;
m_fy = 0.0f;
m_fz = 0.0f;
} // Zero
// -----------------------------------------------------------------------
// 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 bNormalized ) {
// a . b = |a||b|cos(angle)
// -> cos-1((a.b)/(|a||b|))
// Make Sure We Do Not Divide By Zero
float fMagA = length();
if ( fMagA <= Math::ZERO ) {
// This (A) Is An Invalid Vector
return 0;
}
float fValue = 0;
if ( bNormalized ) {
// v3 Is Already Normalized
fValue = dot(v3)/fMagA;
}
else {
float fMagB = v3.length();
if ( fMagB <= Math::ZERO) {
// B Is An Invalid Vector
return 0;
}
fValue = dot(v3)/(fMagA*fMagB);
}
// Correct Value Due To Rounding Problem
Math::constrain( -1.0f, 1.0f, fValue );
return fValue;
} // getCosAngle
// -----------------------------------------------------------------------
// getAngle()
// Returns The Angle Between This Vector And Vector V in Radians.
// This Is More Expensive Than Using GetCosAngle
inline float Vector3::getAngle( const Vector3 &v3, const bool bNormalized, bool bRadians ) {
// a . b = |a||b|cos(angle)
// -> cos-1((a.b)/(|a||b|))
if ( bRadians ) {
return acos( this->getCosAngle( v3 ) );
}
else {
// Convert To Degrees
return Math::radian2Degree( acos( getCosAngle( v3, bNormalized ) ) );
}
} // GetAngle
// -----------------------------------------------------------------------
// length()
// Return The Length Of This Vector
inline float Vector3::length() const {
return sqrtf( m_fx * m_fx +
m_fy * m_fy +
m_fz * m_fz );
} // length
// -----------------------------------------------------------------------
// length2()
// Return The Length Of This Vector
inline float Vector3::length2() const {
return ( m_fx * m_fx +
m_fy * m_fy +
m_fz * m_fz );
} // length2
#endif // VECTOR3_H
Vector3.cpp
#include "stdafx.h"
#include "Vector3.h"
// -----------------------------------------------------------------------
// ~Vector3()
// Destructor
Vector3::~Vector3() {
} // ~Vector3
如果您在我的 Vector3 中注意到 class,我有一堆运算符和通常定义的函数,可以应用于 3 个分量的向量。现在,除法运算符 returns 一个 vector3 并将每个分量除以一个标量。因此,我还包含了友元运算符/ 和函数调用 divideByVector。他们会做同样的操作。在代码中使用它们非常简单。
#include "stdafx.h" // string isostream etc.
#include "Vector3.h"
int main() {
Vector3 v1 ( 4.0f, 4.0f, 4.0f );
Vector3 v2 ( 2.0f, 2.0f, 2.0f );
float value = v1.divideByVector( v2 );
std::cout << value << std::endl;
value = v2.divideByVector( v1 );
std::cout << value << std::endl;
std::cout << v1 / v2 << std::endl;
std::cout << v2 / v1 << std::endl;
v1.m_fx = 0;
v2.m_fx = 0;
std::cout << v1.divideByVector( v2 ) << std::endl;
std::cout << v2.divideByVector( v1 ) << std::endl;
std::cout << v1 / v2 << std::endl;
std::cout << v2 / v1 << std::endl;
std::cout << "\nPress any key to quit.\n" << std::endl;
_getch();
return 0;
} // main
您也可以尝试向量,如果一个向量的每个分量不是另一个分量的倍数,例如:v1( 15.0f, 25.0f, 30.0f ) & v2( 3.0f, 5.0f, 7.0f)
如果 v2's k component was 6.0f
,这将因 v2's k component
now 而失败那么返回的结果值应该是5
,如果你反转除法它应该是0.2
。现在使用我的编译器 (VS2015),如果这些值不是公倍数,则打印到屏幕上的消息是:
-nan(ind)
如果您将 0 作为值传递并尝试除以它,则调试器将抛出未处理的异常。通常这将作为消息抛出并捕获在 try { ... } catch( ... ) { }
块中。但是,我的 Logger 和 ExceptionHandler class 都无法用于此演示。另请注意,我不再使用 rand
或 srand
,因为我现在使用 <random>
库。因此,我在这个旧库中的一些函数已被弃用或修改。
提问者自己的解决方案是有缺陷的,因为如果 (V1.x + V1.y + V1.z) 加起来为零,它将失败:
x = (V2.x + V2.y + V2.z) / (V1.x + V1.y + V1.z)
下面是这个问题的正确解决方案:
x = (V2.x * V1.x + V2.y * V1.y + V2.z * V1.z) /
(V1.x * V1.x + V1.y * V1.y + V1.z * V1.z);