253 lines
6.2 KiB
Ruby
Executable File
253 lines
6.2 KiB
Ruby
Executable File
#
|
|
# File:: vector.rb
|
|
# Description:: Vector classes (2, 3 and 4 component).
|
|
#
|
|
# Author:: David Muir <david.muir@rockstarnorth.com>
|
|
# 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
|