# # File:: quat.rb # Description:: Quaternion class # # Author:: David Muir # 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