# # File:: vector.rb # Description:: Vector classes (2, 3 and 4 component). # # Author:: David Muir # Date:: 28 February 2008 # #---------------------------------------------------------------------------- # Uses #---------------------------------------------------------------------------- require 'pipeline/math/angles' require 'pipeline/util/float' require 'mathn' #---------------------------------------------------------------------------- # Implementation #---------------------------------------------------------------------------- module Pipeline module Math # # == Description # 3D vector class. # class Vector3 attr_accessor :x attr_accessor :y attr_accessor :z # Virtual methods for R, G, B access alias r x alias g y alias b z # # == Description # Vector class constructor, taking three optional arguments. All # vector components must be of the same class otherwise an # ArgumentError exception is raised. # def initialize( x = 0.0, y = 0.0, z = 0.0 ) if x.class != y.class or x.class != z.class or y.class != z.class raise ArgumentError.new( 'Not all vector components are same type' ) end @x = x @y = y @z = z end # # Addition with another vector object or scalar. # def +( v ) if ( v.is_a?( Vector3 ) ) Vector3.new( @x + v.x, @y + v.y, @z + v.z ) elsif ( v.is_a?( Numeric ) ) Vector3.new( @x + v, @y + v, @z + v ) else throw ArgumentError.new( "Addition of Vector3 and #{v.class} not supported." ) end end # # Subtraction of another vector object or scalar. # def -( v ) if ( v.is_a?( Vector3 ) ) Vector3.new( @x - v.x, @y - v.y, @z - v.z ) elsif ( v.is_a?( Numeric ) ) Vector3.new( @x - v, @y - v, @z - v ) else throw ArgumentError.new( "Subtraction of #{v.class} from Vector not supported." ) end end # # Multiplication by a scalar. # def *( s ) if ( s.is_a?( Numeric ) ) Vector3.new( @x * s, @y * s, @z * s ) else throw ArgumentError.new( "Multiplication by #{s.class} is not supported." ) end end # # == Description # Return magnitude of vector. # def magnitude ::Math::sqrt( @x*@x + @y*@y + @z*@z ) end # # == Description # Alias for vector magnitude. # alias length magnitude # # == Description # Return the inverse magnitude of this vector. # def inverse_magnitude ::Math::rsqrt( dot( self ) ) end # # == Description # Normalise this vector. # def normalise! scale!( inverse_magnitude ) end # # == Description # Return this vector normalised # def normalise scale( inverse_magnitude ) end alias normalize! normalise! alias normalize normalise # # == Description # Scale this vector. # def scale!( f ) @x *= f @y *= f @z *= f end # # == Description # Return this vector scaled by multiplier f. # def scale( f ) Vector.new( @x * f, @y * f, @z * f ) end # # == Description # Return this vector scaled by inverse of f. # def inverse_scale( f ) inv_f = 1.0 / f scale( inf_f ) end # # == Description # Scale this vector by the inverse of a value. def inverse_scale!( f ) inv_f = 1.0 / f scale!( inv_f ) end # # == Description # Find and return dot product of this vector and v2. # def dot( v2 ) ( (@x * v2.x) + (@y * v2.y) + (@z * v2.z) ) end #--------------------------------------------------------------------- # Operators #--------------------------------------------------------------------- # # == Description # [] operator for readonly access to components # def [](index) case index when 0 return @x when 1 return @y when 2 return @z else raise ArgumentError.new( "0 <= index <= 2 for xyz vector" ) end end #--------------------------------------------------------------------- # Overloaded Methods #--------------------------------------------------------------------- # # Return Vector3 as an Array object. # def to_a( ) [ @x, @y, @z ] end # # String conversion function for vectors. Optional precision flag # can be used if x, y and z components are all Float. # def to_s( prec = nil ) all_float = ( Float == @x.class and Float == @y.class and Float == @z.class ) if ( all_float and !prec.nil? ) return "[ #{@x.to_s(prec)}, #{@y.to_s(prec)}, #{@z.to_s(prec)} ]" else return "[ #{@x}, #{@y}, #{@z} ]" end end #--------------------------------------------------------------------- # Class Methods #--------------------------------------------------------------------- # # Construct a Vector3 object from a XML node. This should be coded # to handle both libXML and REXML XML node types. # def Vector3::from_xml( xml_node ) if ( xml_node.is_a?( LibXML::XML::Node ) ) then if ( xml_node['x'] and xml_node['y'] and xml_node['z'] ) then Vector3.new( xml_node['x'].to_f, xml_node['y'].to_f, xml_node['z'].to_f ) elsif ( xml_node['r'] and xml_node['g'] and xml_node['b'] ) then Vector3.new( xml_node['r'].to_f, xml_node['g'].to_f, xml_node['b'].to_f ) end elsif ( xml_node.is_a?( REXML::Node ) ) then if ( xml_node.attributes['x'] and xml_node.attributes['y'] and xml_node.attributes['z'] ) then Vector3.new( xml_node.attributes['x'].to_f, xml_node.attributes['y'].to_f, xml_node.attributes['z'].to_f ) elsif ( xml_node.attributes['r'] and xml_node.attributes['g'] and xml_node.attributes['b'] ) then Vector3.new( xml_node.attributes['r'].to_f, xml_node.attributes['g'].to_f, xml_node.attributes['b'].to_f ) end end end end end # Math module end # Pipeline module # End of vector.rb