class Object # This is a shorthand to define vector object coordinates. # # Creates a vector object at coordinates x,y,z def v(x, y, z) Ogre::Vector3.new(x, y, z) end end class SymbolToVectorError < StandardError #:nodoc: end class Symbol #:nodoc: # Vectors in symbol form can be any of the following: # :x => v(1,0,0) # :y => v(0,1,0) # :z => v(0,0,1), # :up => v(0,1,0) # :down => v(0,-1,0) # :left => v(-1,0,0) # :right => v(1,0,0) # :forward => v(0,0,1) # :backward => v(0,0,-1) # :zero => v(0,0,0) def to_v @definitions ||= {:x => v(1,0,0),:y => v(0,1,0),:z => v(0,0,1), :up => v(0,1,0),:down => v(0,-1,0), :left => v(-1,0,0),:right => v(1,0,0), :forward => v(0,0,1),:backward => v(0,0,-1), :zero => v(0,0,0)} return @definitions[self] unless @definitions[self].nil? raise SymbolToVectorError, "Undefined vector for symbol #{self}" end # Multiplication defined for vectors carries over to their symbolic equivalence. def *(number) return to_v * number end # Addition for vectors def +(number) return to_v + number end # Subtraction for numbers def -(number) return to_v - number end end class Array #:nodoc: # Vector extensions for array. Allows for coersion of a 3 element Array into a vector. # Randomize the order of an array # # [1, 2, 3].shuffle #=> [2, 3, 1] # def shuffle sort_by {rand} end # Create a vector from an array. # # [1, 2, 3].to_v #=> same as Vector.new(1, 2, 3) # def to_v raise StandardError, "vector #{self.inspect} does not have 3 elements" if self.length < 3 Ogre::Vector3.new self[0], self[1] ,self[2] end end module Ogre # Vector is a three dimensional array, that allows for various operations on itself. # To create a vector, use the shorthand: # v(0,0,0) # Creates a zero vector # # # *NOTE:* The recomended way to create Vector objects is to use the v(1,2,3) shorthand. class Vector3 # Iterate through x, y and z with a block. The passed value is the value of each component of # the vector. def each(&block) self.to_a.each do |component| yield component end end # Returns self def to_v self end # Returns an array with x, y and z dumped into its elements. # v(1, 2, 3).to_a #=> [1.0, 2.0, 3.0] def to_a [x, y, z] end # Add 2 Vectors together. # v(1,1,1) + v(1,2,3) #=> v(2,3,4) alias_method :ogre_plus, :+ def +(*args) v = convert_args_to_vector args ogre_plus v end # Subtract one Vector from another. # v(1,2,3) - v(1,1,1) #=> v(0,1,2) alias_method :ogre_minus, :- def -(*args) v = convert_args_to_vector args ogre_minus v end # Multiply all components of a vector by a scalar amount. # v(1,2,3) * 3 #=> v(3,6,9) alias_method :ogre_multiply, :* def *(*args) #Handle numbers and vectors unless args[0].is_a? Symbol return ogre_multiply(args[0]) end #Symbol to vector conversion v = convert_args_to_vector args ogre_multiply v end # Returns this Vector but normalized to a length of 1. # v(9, 0, 0).normalize #=> v(1,0,0) alias_method :normalize, :normalised_copy alias_method :normalise, :normalised_copy # Same as #normalize but modifies the receiver in place. alias_method :normalize!, :normalise! # Return the value specified by bracket notation. Integers, Symbols or Strings # are accepted as keys. # # vector = v(1,2,3) # vector[:x] #=> 1 # vector['y'] #=> 2 # vector[2] #=> 3 def [](index) case when index == 0 || index == :x || index == 'x' x when index == 1 || index == :y || index == 'y' y when index == 2 || index == :z || index == 'z' z end end # Set the value specified by bracket notation. Accepts the same keys as the #[] # method. def []=(index, value) case when index == 0 || index == :x || index == 'x' self.x = value.to_f when index == 1 || index == :y || index == 'y' self.y = value.to_f when index == 2 || index == :z || index == 'z' self.z = value.to_f end end # Converts the vector into an easily identifiable form. Mostly used for debugging # and console output. # v(1,2,3).to_s #=> "#" def to_s "#" end # Equality test. This method will return true if all components of both vectors are # indentical. def ==(vector) vector = vector.to_v if vector.is_a?(Symbol) vector.kind_of?(Ogre::Vector3) && x == vector.x && y == vector.y && z == vector.z end # Create a unique identifier based on x, y and z. def hash return self.to_a.hash end # Equality test for hash indexes. def eql?(other) return self.to_a.eql?(other.to_a) end private # Convert passed in arguments to vector form. # Supports Symbol, Array, and vectors def convert_args_to_vector(*args) args.flatten! if(args.first.is_a?(Ogre::Vector3) || args.first.is_a?(Symbol)) args.first.to_v else args.to_v end end end end