Files
2025-09-29 00:52:08 +02:00

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