lib/cyberarm_engine/vector.rb in cyberarm_engine-0.19.0 vs lib/cyberarm_engine/vector.rb in cyberarm_engine-0.19.1

- old
+ new

@@ -1,293 +1,293 @@ -module CyberarmEngine - class Vector - ## - # Creates a up vector - # - # Vector.new(0, 1, 0) - # - # @return [CyberarmEngine::Vector] - def self.up - Vector.new(0, 1, 0) - end - - ## - # Creates a down vector - # - # Vector.new(0, -1, 0) - # - # @return [CyberarmEngine::Vector] - def self.down - Vector.new(0, -1, 0) - end - - ## - # Creates a left vector - # - # Vector.new(-1, 0, 0) - # - # @return [CyberarmEngine::Vector] - def self.left - Vector.new(-1, 0, 0) - end - - ## - # Creates a right vector - # - # Vector.new(1, 0, 0) - # - # @return [CyberarmEngine::Vector] - def self.right - Vector.new(1, 0, 0) - end - - ## - # Creates a forward vector - # - # Vector.new(0, 0, 1) - # - # @return [CyberarmEngine::Vector] - def self.forward - Vector.new(0, 0, 1) - end - - ## - # Creates a backward vector - # - # Vector.new(0, 0, -1) - # - # @return [CyberarmEngine::Vector] - def self.backward - Vector.new(0, 0, -1) - end - - attr_accessor :x, :y, :z, :weight - - def initialize(x = 0, y = 0, z = 0, weight = 0) - @x = x - @y = y - @z = z - @weight = weight - end - - alias w weight - alias w= weight= - - # @return [Boolean] - def ==(other) - if other.is_a?(Numeric) - @x == other && - @y == other && - @z == other && - @weight == other - elsif other.is_a?(Vector) - @x == other.x && - @y == other.y && - @z == other.z && - @weight == other.weight - else - other == self - end - end - - # Create a new vector using {x} and {y} values - # @return [CyberarmEngine::Vector] - def xy - Vector.new(@x, @y) - end - - # Performs math operation, excluding {weight} - private def operator(function, other) - if other.is_a?(Numeric) - Vector.new( - @x.send(:"#{function}", other), - @y.send(:"#{function}", other), - @z.send(:"#{function}", other) - ) - else - Vector.new( - @x.send(:"#{function}", other.x), - @y.send(:"#{function}", other.y), - @z.send(:"#{function}", other.z) - ) - end - end - - # Adds Vector and Numeric or Vector and Vector, excluding {weight} - # @return [CyberarmEngine::Vector] - def +(other) - operator("+", other) - end - - # Subtracts Vector and Numeric or Vector and Vector, excluding {weight} - # @return [CyberarmEngine::Vector] - def -(other) - operator("-", other) - end - - # Multiplies Vector and Numeric or Vector and Vector, excluding {weight} - # @return [CyberarmEngine::Vector] - def *(other) - operator("*", other) - end - - def multiply_transform(transform) - e = transform.elements - - x = @x * e[0] + @y * e[1] + @z * e[2] + 1 * e[3] - y = @x * e[4] + @y * e[5] + @z * e[6] + 1 * e[7] - z = @x * e[8] + @y * e[9] + @z * e[10] + 1 * e[11] - w = @x * e[12] + @y * e[13] + @z * e[14] + 1 * e[15] - - Vector.new(x / 1, y / 1, z / 1, w / 1) - end - - # Divides Vector and Numeric or Vector and Vector, excluding {weight} - # @return [CyberarmEngine::Vector] - def /(other) - # Duplicated to protect from DivideByZero - if other.is_a?(Numeric) - Vector.new( - (@x == 0 ? 0 : @x / other), - (@y == 0 ? 0 : @y / other), - (@z == 0 ? 0 : @z / other) - ) - else - Vector.new( - (@x == 0 ? 0 : @x / other.x), - (@y == 0 ? 0 : @y / other.y), - (@z == 0 ? 0 : @z / other.z) - ) - end - end - - # dot product of {Vector} - # @return [Integer|Float] - def dot(other) - product = 0 - - a = to_a - b = other.to_a - - 3.times do |i| - product += (a[i] * b[i]) - end - - product - end - - # cross product of {Vector} - # @return [CyberarmEngine::Vector] - def cross(other) - a = to_a - b = other.to_a - - Vector.new( - b[2] * a[1] - b[1] * a[2], - b[0] * a[2] - b[2] * a[0], - b[1] * a[0] - b[0] * a[1] - ) - end - - # returns degrees - # @return [Float] - def angle(other) - Math.acos(normalized.dot(other.normalized)) * 180 / Math::PI - end - - # returns magnitude of Vector, ignoring #weight - # @return [Float] - def magnitude - Math.sqrt((@x * @x) + (@y * @y) + (@z * @z)) - end - - ## - # returns normalized {Vector} - # - # @example - # CyberarmEngine::Vector.new(50, 21.2, 45).normalized - # # => <CyberarmEngine::Vector:0x001 @x=0.7089... @y=0.3005... @z=0.6380... @weight=0> - # - # @return [CyberarmEngine::Vector] - def normalized - mag = magnitude - self / Vector.new(mag, mag, mag) - end - - # returns a direction {Vector} - # - # z is pitch - # - # y is yaw - # - # x is roll - # @return [CyberarmEngine::Vector] - def direction - _x = -Math.sin(@y.degrees_to_radians) * Math.cos(@z.degrees_to_radians) - _y = Math.sin(@z.degrees_to_radians) - _z = Math.cos(@y.degrees_to_radians) * Math.cos(@z.degrees_to_radians) - - Vector.new(_x, _y, _z) - end - - # returns an inverse {Vector} - # @return [CyberarmEngine::Vector] - def inverse - Vector.new(1.0 / @x, 1.0 / @y, 1.0 / @z) - end - - # Adds up values of {x}, {y}, and {z} - # @return [Integer|Float] - def sum - @x + @y + @z - end - - ## - # Linear interpolation: smoothly transition between two {Vector} - # - # CyberarmEngine::Vector.new(100, 100, 100).lerp( CyberarmEngine::Vector.new(0, 0, 0), 0.75 ) - # # => <CyberarmEngine::Vector:0x0001 @x=75.0, @y=75.0, @z=75.0, @weight=0> - # - # @param other [CyberarmEngine::Vector | Integer | Float] value to subtract from - # @param factor [Float] how complete transition to _other_ is, in range [0.0..1.0] - # @return [CyberarmEngine::Vector] - def lerp(other, factor) - (self - other) * factor.clamp(0.0, 1.0) - end - - # 2D distance using X and Y - # @return [Float] - def distance(other) - Math.sqrt((@x - other.x)**2 + (@y - other.y)**2) - end - - # 2D distance using X and Z - # @return [Float] - def gl_distance2d(other) - Math.sqrt((@x - other.x)**2 + (@z - other.z)**2) - end - - # 3D distance using X, Y, and Z - # @return [Float] - def distance3d(other) - Math.sqrt((@x - other.x)**2 + (@y - other.y)**2 + (@z - other.z)**2) - end - - # Converts {Vector} to Array - # @return [Array] - def to_a - [@x, @y, @z, @weight] - end - - # Converts {Vector} to String - # @return [String] - def to_s - "X: #{@x}, Y: #{@y}, Z: #{@z}, Weight: #{@weight}" - end - - # Converts {Vector} to Hash - # @return [Hash] - def to_h - { x: @x, y: @y, z: @z, weight: @weight } - end - end -end +module CyberarmEngine + class Vector + ## + # Creates a up vector + # + # Vector.new(0, 1, 0) + # + # @return [CyberarmEngine::Vector] + def self.up + Vector.new(0, 1, 0) + end + + ## + # Creates a down vector + # + # Vector.new(0, -1, 0) + # + # @return [CyberarmEngine::Vector] + def self.down + Vector.new(0, -1, 0) + end + + ## + # Creates a left vector + # + # Vector.new(-1, 0, 0) + # + # @return [CyberarmEngine::Vector] + def self.left + Vector.new(-1, 0, 0) + end + + ## + # Creates a right vector + # + # Vector.new(1, 0, 0) + # + # @return [CyberarmEngine::Vector] + def self.right + Vector.new(1, 0, 0) + end + + ## + # Creates a forward vector + # + # Vector.new(0, 0, 1) + # + # @return [CyberarmEngine::Vector] + def self.forward + Vector.new(0, 0, 1) + end + + ## + # Creates a backward vector + # + # Vector.new(0, 0, -1) + # + # @return [CyberarmEngine::Vector] + def self.backward + Vector.new(0, 0, -1) + end + + attr_accessor :x, :y, :z, :weight + + def initialize(x = 0, y = 0, z = 0, weight = 0) + @x = x + @y = y + @z = z + @weight = weight + end + + alias w weight + alias w= weight= + + # @return [Boolean] + def ==(other) + if other.is_a?(Numeric) + @x == other && + @y == other && + @z == other && + @weight == other + elsif other.is_a?(Vector) + @x == other.x && + @y == other.y && + @z == other.z && + @weight == other.weight + else + other == self + end + end + + # Create a new vector using {x} and {y} values + # @return [CyberarmEngine::Vector] + def xy + Vector.new(@x, @y) + end + + # Performs math operation, excluding {weight} + private def operator(function, other) + if other.is_a?(Numeric) + Vector.new( + @x.send(:"#{function}", other), + @y.send(:"#{function}", other), + @z.send(:"#{function}", other) + ) + else + Vector.new( + @x.send(:"#{function}", other.x), + @y.send(:"#{function}", other.y), + @z.send(:"#{function}", other.z) + ) + end + end + + # Adds Vector and Numeric or Vector and Vector, excluding {weight} + # @return [CyberarmEngine::Vector] + def +(other) + operator("+", other) + end + + # Subtracts Vector and Numeric or Vector and Vector, excluding {weight} + # @return [CyberarmEngine::Vector] + def -(other) + operator("-", other) + end + + # Multiplies Vector and Numeric or Vector and Vector, excluding {weight} + # @return [CyberarmEngine::Vector] + def *(other) + operator("*", other) + end + + def multiply_transform(transform) + e = transform.elements + + x = @x * e[0] + @y * e[1] + @z * e[2] + 1 * e[3] + y = @x * e[4] + @y * e[5] + @z * e[6] + 1 * e[7] + z = @x * e[8] + @y * e[9] + @z * e[10] + 1 * e[11] + w = @x * e[12] + @y * e[13] + @z * e[14] + 1 * e[15] + + Vector.new(x / 1, y / 1, z / 1, w / 1) + end + + # Divides Vector and Numeric or Vector and Vector, excluding {weight} + # @return [CyberarmEngine::Vector] + def /(other) + # Duplicated to protect from DivideByZero + if other.is_a?(Numeric) + Vector.new( + (@x == 0 ? 0 : @x / other), + (@y == 0 ? 0 : @y / other), + (@z == 0 ? 0 : @z / other) + ) + else + Vector.new( + (@x == 0 ? 0 : @x / other.x), + (@y == 0 ? 0 : @y / other.y), + (@z == 0 ? 0 : @z / other.z) + ) + end + end + + # dot product of {Vector} + # @return [Integer|Float] + def dot(other) + product = 0 + + a = to_a + b = other.to_a + + 3.times do |i| + product += (a[i] * b[i]) + end + + product + end + + # cross product of {Vector} + # @return [CyberarmEngine::Vector] + def cross(other) + a = to_a + b = other.to_a + + Vector.new( + b[2] * a[1] - b[1] * a[2], + b[0] * a[2] - b[2] * a[0], + b[1] * a[0] - b[0] * a[1] + ) + end + + # returns degrees + # @return [Float] + def angle(other) + Math.acos(normalized.dot(other.normalized)) * 180 / Math::PI + end + + # returns magnitude of Vector, ignoring #weight + # @return [Float] + def magnitude + Math.sqrt((@x * @x) + (@y * @y) + (@z * @z)) + end + + ## + # returns normalized {Vector} + # + # @example + # CyberarmEngine::Vector.new(50, 21.2, 45).normalized + # # => <CyberarmEngine::Vector:0x001 @x=0.7089... @y=0.3005... @z=0.6380... @weight=0> + # + # @return [CyberarmEngine::Vector] + def normalized + mag = magnitude + self / Vector.new(mag, mag, mag) + end + + # returns a direction {Vector} + # + # z is pitch + # + # y is yaw + # + # x is roll + # @return [CyberarmEngine::Vector] + def direction + _x = -Math.sin(@y.degrees_to_radians) * Math.cos(@z.degrees_to_radians) + _y = Math.sin(@z.degrees_to_radians) + _z = Math.cos(@y.degrees_to_radians) * Math.cos(@z.degrees_to_radians) + + Vector.new(_x, _y, _z) + end + + # returns an inverse {Vector} + # @return [CyberarmEngine::Vector] + def inverse + Vector.new(1.0 / @x, 1.0 / @y, 1.0 / @z) + end + + # Adds up values of {x}, {y}, and {z} + # @return [Integer|Float] + def sum + @x + @y + @z + end + + ## + # Linear interpolation: smoothly transition between two {Vector} + # + # CyberarmEngine::Vector.new(100, 100, 100).lerp( CyberarmEngine::Vector.new(0, 0, 0), 0.75 ) + # # => <CyberarmEngine::Vector:0x0001 @x=75.0, @y=75.0, @z=75.0, @weight=0> + # + # @param other [CyberarmEngine::Vector | Integer | Float] value to subtract from + # @param factor [Float] how complete transition to _other_ is, in range [0.0..1.0] + # @return [CyberarmEngine::Vector] + def lerp(other, factor) + (self - other) * factor.clamp(0.0, 1.0) + end + + # 2D distance using X and Y + # @return [Float] + def distance(other) + Math.sqrt((@x - other.x)**2 + (@y - other.y)**2) + end + + # 2D distance using X and Z + # @return [Float] + def gl_distance2d(other) + Math.sqrt((@x - other.x)**2 + (@z - other.z)**2) + end + + # 3D distance using X, Y, and Z + # @return [Float] + def distance3d(other) + Math.sqrt((@x - other.x)**2 + (@y - other.y)**2 + (@z - other.z)**2) + end + + # Converts {Vector} to Array + # @return [Array] + def to_a + [@x, @y, @z, @weight] + end + + # Converts {Vector} to String + # @return [String] + def to_s + "X: #{@x}, Y: #{@y}, Z: #{@z}, Weight: #{@weight}" + end + + # Converts {Vector} to Hash + # @return [Hash] + def to_h + { x: @x, y: @y, z: @z, weight: @weight } + end + end +end