struct Bivector3
{
float b01 = 0;
float b02 = 0;
float b12 = 0;
Bivector3( float b01, float b02, float b12 );
};
Bivector3::Bivector3( float b01, float b02, float b12 )
: b01(b01), b02(b02), b12(b12) {}
// Wedge product
inline Bivector3 Wedge( const Vector3& u, const Vector3& v )
{
Bivector3 ret(
u[0]*v[1] - u[1]*v[0], // XY
u[0]*v[2] - u[2]*v[0], // XZ
u[1]*v[2] - u[2]*v[1] // YZ
);
return ret;
}
// ---------------------------------------------------------------------
struct Rotor3
{
// scalar part
float a = 1;
// bivector part
float b01 = 0;
float b02 = 0;
float b12 = 0;
// default ctor
Rotor3() {}
Rotor3( float a, float b01, float b02, float b12 );
Rotor3( float a, const Bivector3& bv );
// construct the rotor that rotates one vector to another
Rotor3( const Vector3& vFrom, const Vector3& vTo );
// angle+axis, or rather angle+plane
Rotor3( const Bivector3& bvPlane, float angleRadian );
// rotate a vector by the rotor
Vector3 rotate( const Vector3& v ) const;
// multiply
Rotor3 operator*( const Rotor3& r ) const;
Rotor3 operator*=( const Rotor3& r );
Rotor3 rotate( const Rotor3& r ) const;
// length utility
Rotor3 reverse() const; // equivalent to conjugate
float lengthsqrd() const;
float length() const;
void normalize();
Rotor3 normal() const;
// convert to matrix
Matrix3 toMatrix3() const;
};
// default ctor
Rotor3::Rotor3( float a, float b01, float b02, float b12 )
: a(a), b01(b01), b02(b02), b12(b12) {}
Rotor3::Rotor3( float a, const Bivector3& bv )
: a(a), b01(bv.b01), b02(bv.b02), b12(bv.b12) {}
// construct the rotor that rotates one unit vector to another
// uses the usual trick to get the half angle
Rotor3::Rotor3( const Vector3& vFrom, const Vector3& vTo )
{
a = 1 + Dot( vTo, vFrom );
// the left side of the products have b a, not a b, so flip
Bivector3 minusb = Wedge( vTo, vFrom );
b01 = minusb.b01;
b02 = minusb.b02;
b12 = minusb.b12;
normalize();
}
// angle+plane, plane must be normalized
Rotor3::Rotor3( const Bivector3& bvPlane, float angleRadian )
{
float sina = sin(angleRadian / 2.f);
a = cos(angleRadian / 2.f);
// the left side of the products have b a, not a b
b01 = -sina * bvPlane.b01;
b02 = -sina * bvPlane.b02;
b12 = -sina * bvPlane.b12;
}
// Rotor3-Rotor3 product
// non-optimized
inline Rotor3 Rotor3::operator*( const Rotor3& q ) const
{
const Rotor3& p = *this;
Rotor3 r;
// here we just expanded the geometric product rules
r.a = p.a * q.a
- p.b01 * q.b01 - p.b02 * q.b02 - p.b12 * q.b12;
r.b01 = p.b01 * q.a + p.a * q.b01
+ p.b12 * q.b02 - p.b02 * q.b12;
r.b02 = p.b02 * q.a + p.a * q.b02
- p.b12 * q.b01 + p.b01 * q.b12;
r.b12 = p.b12 * q.a + p.a * q.b12
+ p.b02 * q.b01 - p.b01 * q.b02;
return r;
}
/// R x R*
// non-optimized
Vector3 Rotor3::rotate( const Vector3& x ) const
{
const Rotor3& p = *this;
// q = P x
Vector3 q;
q[0] = p.a * x[0] + x[1] * p.b01 + x[2] * p.b02;
q[1] = p.a * x[1] - x[0] * p.b01 + x[2] * p.b12;
q[2] = p.a * x[2] - x[0] * p.b02 - x[1] * p.b12;
float q012 = x[0] * p.b12 - x[1] * p.b02 + x[2] * p.b01; // trivector
// r = q P*
Vector3 r;
r[0] = p.a * q[0] + q[1] * p.b01 + q[2] * p.b02 + q012 * p.b12;
r[1] = p.a * q[1] - q[0] * p.b01 - q012 * p.b02 + q[2] * p.b12;
r[2] = p.a * q[2] + q012 * p.b01 - q[0] * p.b02 - q[1] * p.b12;
// trivector part of the result is always zero!
return r;
}
// Rotor3-Rotor3 product
inline Rotor3 Rotor3::operator*=( const Rotor3& r )
{
(*this) = (*this) * r;
return *this;
}
// rotate a rotor by another
inline Rotor3 Rotor3::rotate( const Rotor3& r ) const
{
// should unwrap this for efficiency
return (*this) * r * (*this).reverse();
}
// Equivalent to conjugate
inline Rotor3 Rotor3::reverse() const
{
return Rotor3( a, -b01, -b02, -b12 );
}
// Length Squared
inline float Rotor3::lengthsqrd() const
{
return Sqr(a) + Sqr(b01) + Sqr(b02) + Sqr(b12);
}
// Length
inline float Rotor3::length() const
{
return sqrt( lengthsqrd() );
}
// Normalize
inline void Rotor3::normalize()
{
float l = length();
a /= l; b01 /= l; b02 /= l; b12 /= l;
}
// Normalized rotor
inline Rotor3 Rotor3::normal() const
{
Rotor3 r = *this;
r.normalize();
return r;
}
// convert to matrix
// non-optimized
inline Matrix3 Rotor3::toMatrix3() const
{
Vector3 v0 = rotate( Vector3(1,0,0) );
Vector3 v1 = rotate( Vector3(0,1,0) );
Vector3 v2 = rotate( Vector3(0,0,1) );
return Matrix3( v0, v1, v2 );
}
// geometric product (for reference), produces twice the angle, negative direction
inline Rotor3 Geo( const Vector3 & a, const Vector3 & b )
{
return Rotor3( Dot(a,b), Wedge(a,b) );
}
// Cross product
inline Vector3 Cross( const Vector3& u, const Vector3& v )
{
Vector3 ret(
u[1]*v[2] - u[2]*v[1],
u[2]*v[0] - u[0]*v[2],
u[0]*v[1] - u[1]*v[0]
);
return ret;
}
// ---------------------------------------------------------------------
struct Quaternion
{
// real part
float a = 1;
// imaginary part
float v0 = 0;
float v1 = 0;
float v2 = 0;
// default ctor
Quaternion() {}
Quaternion( float a, float v0, float v1, float v2 );
// construct the quaternion that rotates one vector to another
Quaternion( const Vector3& vFrom, const Vector3& vTo );
// angle+axis, axis must be normalized
Quaternion( float angleRadian, const Vector3& axis );
// rotate a vector by the quaternion
Vector3 rotate( const Vector3& v ) const;
// multiply
Quaternion operator*( const Quaternion& q ) const;
Quaternion operator*=( const Quaternion& q );
Quaternion rotate( const Quaternion& q ) const;
// length utility
Quaternion conjugate() const;
float lengthsqrd() const;
float length() const;
void normalize();
Quaternion normal() const;
// convert to matrix
Matrix3 toMatrix3() const;
};
// default ctor
Quaternion::Quaternion( float a, float v0, float v1, float v2 )
: a(a), v0(v0), v1(v1), v2(v2) {}
// construct the quaternion that rotates one unit vector to another
// uses the usual trick to get the half angle
Quaternion::Quaternion( const Vector3& vFrom, const Vector3& vTo )
{
a = 1 + Dot( vTo, vFrom );
Vector3 vec = Cross( vFrom, vTo );
v0 = vec[0];
v1 = vec[1];
v2 = vec[2];
normalize();
}
// angle+axis, axis must be normalized
Quaternion::Quaternion( float angleRadian, const Vector3& axis )
{
float sina = sin(angleRadian / 2.f);
a = cos(angleRadian / 2.f);
v0 = sina * axis[0];
v1 = sina * axis[1];
v2 = sina * axis[2];
}
// Quaternion-Quaternion product
// non-optimized
inline Quaternion Quaternion::operator*( const Quaternion& q ) const
{
const Quaternion& p = *this;
Quaternion r;
// Quaternion formulas:
// r.a = p.a * q.a - Dot(p.v,q.v);
// r.v = p.a * q.v + q.a * p.v + Cross(p.v,q.v);
r.a = p.a * q.a
- p.v0 * q.v0 - p.v1 * q.v1 - p.v2 * q.v2;
r.v0 = p.a * q.v0 + q.a * p.v0
+ p.v1 * q.v2 - p.v2 * q.v1;
r.v1 = p.a * q.v1 + q.a * p.v1
+ p.v2 * q.v0 - p.v0 * q.v2;
r.v2 = p.a * q.v2 + q.a * p.v2
+ p.v0 * q.v1 - p.v1 * q.v0;
return r;
}
/// Q (x,0) Q*
// non-optimized
Vector3 Quaternion::rotate( const Vector3& x ) const
{
const Quaternion& p = *this;
// q = P (x,0)
Quaternion q;
q.v0 = p.a * x[0] + p.v1 * x[2] - p.v2 * x[1];
q.v1 = p.a * x[1] + p.v2 * x[0] - p.v0 * x[2];
q.v2 = p.a * x[2] + p.v0 * x[1] - p.v1 * x[0];
q.a = - p.v0 * x[0] - p.v1 * x[1] - p.v2 * x[2];
// r = q P*
Vector3 r;
r[0] = q.a * -p.v0 + p.a * q.v0 - q.v1 * p.v2 + q.v2 * p.v1;
r[1] = q.a * -p.v1 + p.a * q.v1 - q.v2 * p.v0 + q.v0 * p.v2;
r[2] = q.a * -p.v2 + p.a * q.v2 - q.v0 * p.v1 + q.v1 * p.v0;
return r;
}
// Quaternion-Quaternion product
inline Quaternion Quaternion::operator*=( const Quaternion& r )
{
(*this) = (*this) * r;
return *this;
}
// rotate a rotor by another
inline Quaternion Quaternion::rotate( const Quaternion& r ) const
{
// should unwrap this for efficiency
return (*this) * r * (*this).conjugate();
}
// C
inline Quaternion Quaternion::conjugate() const
{
return Quaternion( a, -v0, -v1, -v2 );
}
// Length Squared
inline float Quaternion::lengthsqrd() const
{
return Sqr(a) + Sqr(v0) + Sqr(v1) + Sqr(v2);
}
// Length
inline float Quaternion::length() const
{
return sqrt( lengthsqrd() );
}
// Normalize
inline void Quaternion::normalize()
{
float l = length();
a /= l; v0 /= l; v1 /= l; v2 /= l;
}
// Normalized rotor
inline Quaternion Quaternion::normal() const
{
Quaternion r = *this;
r.normalize();
return r;
}
// convert to matrix
// non-optimized
inline Matrix3 Quaternion::toMatrix3() const
{
Vector3 v0 = rotate( Vector3(1,0,0) );
Vector3 v1 = rotate( Vector3(0,1,0) );
Vector3 v2 = rotate( Vector3(0,0,1) );
return Matrix3( v0, v1, v2 );
}