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

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