185 lines
4.8 KiB
Ruby
Executable File
185 lines
4.8 KiB
Ruby
Executable File
#
|
|
# File:: quat.rb
|
|
# Description:: Quaternion class
|
|
#
|
|
# Author:: David Muir <david.muir@rockstarnorth.com>
|
|
# Date:: 28 February 2008
|
|
#
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Uses
|
|
#-----------------------------------------------------------------------------
|
|
require 'pipeline/math/vector3'
|
|
require 'pipeline/util/float'
|
|
require 'mathn'
|
|
|
|
#-----------------------------------------------------------------------------
|
|
# Implementation
|
|
#-----------------------------------------------------------------------------
|
|
module Pipeline
|
|
module Math
|
|
|
|
#
|
|
# == Description
|
|
# Quaternion class, representing a 3D rotation.
|
|
#
|
|
class Quat
|
|
|
|
#---------------------------------------------------------------------
|
|
# Class Members
|
|
#---------------------------------------------------------------------
|
|
|
|
# Create and return a Quat object from a rotation axis and angle.
|
|
# +:axis+ is a normalised Vector.
|
|
# +:angle+ is in radians.
|
|
def Quat.from_rotation( axis, angle )
|
|
raise ArgumentError.new( "axis must be a Vector object" ) \
|
|
unless Vector == axis.class
|
|
raise ArgumentError.new( "axis must be normalised" ) \
|
|
unless 1.0 == axis.magnitude
|
|
|
|
local_sin = ::Math.sin( 0.5 * angle )
|
|
local_cos = ::Math.cos( 0.5 * angle )
|
|
Quat.new( axis.x * local_sin, axis.y * local_sin, axis.z * local_sin, local_cos )
|
|
end
|
|
|
|
# Return identify Quat.
|
|
def Quat::identity( )
|
|
Quat.new( 0.0, 0.0, 0.0, 1.0 )
|
|
end
|
|
|
|
#---------------------------------------------------------------------
|
|
# Instance Members
|
|
#---------------------------------------------------------------------
|
|
|
|
attr_reader :x
|
|
attr_reader :y
|
|
attr_reader :z
|
|
attr_reader :w
|
|
|
|
def initialize( x = 0.0, y = 0.0, z = 0.0, w = 1.0 )
|
|
@x = x
|
|
@y = y
|
|
@z = z
|
|
@w = w
|
|
end
|
|
|
|
# Return the magnitude of this quaternion.
|
|
def magnitude
|
|
::Math::sqrt( @x*@x + @y*@y + @z*@z + @w*@w )
|
|
end
|
|
|
|
alias length magnitude
|
|
|
|
# Return the inverse magnitude of this quaternion.
|
|
def inverse_magnitude
|
|
::Math::rsqrt( @x*@x + @y*@y + @z*@z + @w*@w )
|
|
end
|
|
|
|
# Normalise this quaternion.
|
|
def normalise!
|
|
scale!( inverse_magnitude! )
|
|
end
|
|
|
|
# Return this quaternion normalised.
|
|
def normalise
|
|
scale( inverse_magnitude )
|
|
end
|
|
|
|
alias normalize! normalise!
|
|
alias normalize normalise
|
|
|
|
# Scale this quaternion
|
|
def scale!( f )
|
|
@x *= f
|
|
@y *= f
|
|
@z *= f
|
|
@w *= f
|
|
end
|
|
|
|
# Return this quaternion scaled.
|
|
def scale( f )
|
|
Quat.new( @x * f, @y * f, @z * f, @w * f )
|
|
end
|
|
|
|
#
|
|
# Return Quat as friendly String.
|
|
#
|
|
def to_s( )
|
|
"[ #{@x}, #{@y}, #{@z}, #{@w} ]"
|
|
end
|
|
|
|
#--------------------------------------------------------------------
|
|
# Class Methods
|
|
#--------------------------------------------------------------------
|
|
|
|
#
|
|
# Construct a Quat representing the rotation defined in a Matrix34
|
|
# object.
|
|
#
|
|
# Ref: X:\jimmy\src\dev\rage\base\src\vector\quaternion_default.h
|
|
#
|
|
def Quat::from_matrix34( m )
|
|
temp = m.a.x + m.b.y + m.c.z
|
|
data = [ temp, m.a.x, m.b.y, m.c.z ]
|
|
largest_value = data.max()
|
|
largest_index = data.index( largest_value )
|
|
|
|
x = y = z = w = 0.0
|
|
case ( largest_index )
|
|
when 0:
|
|
w = 0.5 * ::Math::sqrt( temp + 1.0 )
|
|
temp = 0.25 / w
|
|
x = ( m.b.z - m.c.y ) * temp
|
|
y = ( m.c.x - m.a.z ) * temp
|
|
z = ( m.a.y - m.b.x ) * temp;
|
|
when 1:
|
|
x= 0.5 * ::Math::sqrt( ( 2.0 * m.a.x ) - temp + 1.0 );
|
|
temp = 0.25 / x;
|
|
w = ( m.b.z - m.c.y ) * temp;
|
|
y = ( m.a.y + m.b.x ) * temp;
|
|
z = ( m.c.x + m.a.z ) * temp;
|
|
when 2:
|
|
y = 0.5 * ::Math::sqrt( ( 2.0 * m.b.y )- temp + 1.0 );
|
|
temp = 0.25 / y;
|
|
w = ( m.c.x - m.a.z ) * temp;
|
|
x = ( m.a.y + m.b.x ) * temp;
|
|
z = ( m.b.z + m.c.y ) * temp;
|
|
when 3:
|
|
z = 0.5 * ::Math::sqrt( ( 2.0 * m.c.z ) - temp + 1.0 );
|
|
temp = 0.25 / z;
|
|
w = ( m.a.y - m.b.x ) * temp;
|
|
x = ( m.c.x + m.a.z ) * temp;
|
|
y = ( m.b.z + m.c.y ) * temp;
|
|
else
|
|
throw RuntimeError.new( "Invalid largest index." )
|
|
end
|
|
|
|
Quat.new( x, y, z, w )
|
|
end
|
|
|
|
#
|
|
# Construct a Quat object from a XML node. This should be coded
|
|
# to handle both libXML and REXML XML node types.
|
|
#
|
|
def Quat::from_xml( xml_node )
|
|
if ( xml_node.is_a?( LibXML::XML::Node ) ) then
|
|
|
|
Quat.new( xml_node['x'].to_f, xml_node['y'].to_f,
|
|
xml_node['z'].to_f, xml_node['w'].to_f )
|
|
|
|
elsif ( xml_node.is_a?( REXML::Node ) ) then
|
|
|
|
Quat.new( xml_node.attributes['x'].to_f,
|
|
xml_node.attributes['y'].to_f,
|
|
xml_node.attributes['z'].to_f,
|
|
xml_node.attributes['w'].to_f )
|
|
end
|
|
end
|
|
end
|
|
|
|
end # Math module
|
|
end # Pipeline module
|
|
|
|
# End of quat.rb
|