This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Xtansia's profile picture

Advanced Vector API V1.1

Started by Xtansia, 01 September 2012 - 08:43 AM
Xtansia #1
Posted 01 September 2012 - 10:43 AM
This a new, improved and advanced alternative to the inbuilt vector api,
With a lot more functions etc
There's not much else to say really,

Heres the documentation(It is also included in the file at the top):
--[[
Advanced Vector API by Tomass1996

Vector 'Object' Functions And Fields:
  vector.x				-- Vectors X component
  vector.y				-- Vectors Y component
  vector.z				-- Vectors Z component
  vector:add(otherVector)		 -- Component-wise addition
  vector:scalarAdd(n)			-- Scalar addition
  vector:subtract(otherVector)		-- Component-wise subtraction
  vector:scalarSubtract(n)		-- Scalar subtraction
  vector:multiply(otherVector)		 -- Component-wise multiplication
  vector:scalarMultiply(n)		-- Scalar multiplication
  vector:divide(otherVector)		-- Component-wise division
  vector:scalarDivide(n)		-- Scalar division
  vector:length()			-- Get the length of the vector
  vector:lengthSq()			-- Get the length ^ 2 of the vector
  vector:distance(otherVector)		-- Get the distance away from a vector
  vector:distanceSq(otherVector)	-- Get the distance away from a vector, squared
  vector:normalize()			-- Get the normalized vector
  vector:dot(otherVector)		-- Get the dot product of vector and otherVector
  vector:cross(otherVector)		-- Get the cross product of vector and otherVector
  vector:containedWithin(minVec, maxVec)-- Check to see if vector is contained within minVec and maxVec
  vector:clampX(min, max)		-- Clamp the X component
  vector:clampY(min, max)		-- Clamp the Y component
  vector:clampZ(min, max)		-- Clamp the Z component
  vector:floor()			-- Rounds all components down
  vector:ceil()				-- Rounds all components up
  vector:round()			-- Rounds all components to the closest integer
  vector:absolute()			-- Vector with absolute values of components
  vector:isCollinearWith(otherVector)	-- Checks to see if vector is collinear with otherVector
  vector:getIntermediateWithX(other, x)	-- New vector with given x value along the line between vector and other, or nil if not possible
  vector:getIntermediateWithY(other, y)	-- New vector with given y value along the line between vector and other, or nil if not possible
  vector:getIntermediateWithZ(other, z)	-- New vector with given z value along the line between vector and other, or nil if not possible
  vector:rotateAroundX(angle)		-- Rotates vector around the x axis by the specified angle(radians)
  vector:rotateAroundY(angle)		-- Rotates vector around the y axis by the specified angle(radians)
  vector:rotateAroundZ(angle)		-- Rotates vector around the z axis by the specified angle(radians)
  vector:clone()			-- Returns a new vector with same component values as vector
  vector:equals(otherVector)		-- Checks to see if vector and otherVector are equal
  vector:tostring()			-- Returns the string representation of vector "(x, y, z)"

Vector 'Object' Metatable Overrides:	  -- [x, y, z] represents a vector object in these examples, not irl
  To String		-- tostring will get the string representation
		   	 ie.	tostring([1, 2, 3])	-->	"(1, 2, 3)"
  Unary Minus		-- Using unary minus on a vector will result in the negative of vector
		   	 ie.	-[1, -2, 3]		-->	[-1, 2, -3]
  Addition		-- Can add two vectors or vector and number with +
		   	 ie.	[1, 2, 3] + [4, 5, 6]	-->	[5, 7, 9]
				[1, 2, 3] + 3		-->	[4, 5, 6]
  Subtraction		-- Can subtract two vectors or vector and number with -
		   	 ie.	[4, 5, 6] - [1, 2, 3]	-->	[3, 3, 3]
				[4, 5, 6] - 3		-->	[1, 2, 3]
  Multiplication	-- Can multiply two vectors or vector and number with *
		   	 ie.	[1, 2, 3] * [4, 5, 6]	-->	[4, 10, 18]
				[1, 2, 3] * 3		-->	[3, 6, 9]
  Division		-- Can divide two vectors or vector and number with /
		   	 ie.	[4, 10, 18] / [1, 2, 3]	-->	[4, 5, 6]
				[3, 6, 9] / 3		-->	[1, 2, 3]
  Equality		-- Can check if two vectors are the same with ==
		   	 ie.	[4, 5, 6] == [4, 5, 6]	-->	true
				[4, 5, 6] == [4, 99, 6]	-->	false

Vector API functions:
  Vector.getMinimum(v1, v2)		-- Gets the minimum components of two vectors
  Vector.getMaximum(v1, v2)		-- Gets the maximum components of two vectors
  Vector.getMidpoint(v1, v2)		-- Gets the midpoint of two vectors
  Vector.isVector(v)			-- Checks whether v is a vector created by this api
  Vector.new(x, y, z)			-- Creates a new vector object with the component values
--]]

Pastebin ID: 4rvCwWAL
Pastebin Link: http://pastebin.com/4rvCwWAL
BitBucket Link: https://bitbucket.or...0e58/Vector.lua

API Code:
Spoiler
--[[
Advanced Vector API by Tomass1996

Vector 'Object' Functions And Fields:
  vector.x                              -- Vectors X component
  vector.y                              -- Vectors Y component
  vector.z                              -- Vectors Z component
  vector:add(otherVector)               -- Component-wise addition
  vector:scalarAdd(n)                   -- Scalar addition
  vector:subtract(otherVector)          -- Component-wise subtraction
  vector:scalarSubtract(n)              -- Scalar subtraction
  vector:multiply(otherVector)          -- Component-wise multiplication
  vector:scalarMultiply(n)              -- Scalar multiplication
  vector:divide(otherVector)            -- Component-wise division
  vector:scalarDivide(n)                -- Scalar division
  vector:length()                       -- Get the length of the vector
  vector:lengthSq()                     -- Get the length ^ 2 of the vector
  vector:distance(otherVector)          -- Get the distance away from a vector
  vector:distanceSq(otherVector)        -- Get the distance away from a vector, squared
  vector:normalize()                    -- Get the normalized vector
  vector:dot(otherVector)               -- Get the dot product of vector and otherVector
  vector:cross(otherVector)             -- Get the cross product of vector and otherVector
  vector:containedWithin(minVec, maxVec)-- Check to see if vector is contained within minVec and maxVec
  vector:clampX(min, max)               -- Clamp the X component
  vector:clampY(min, max)               -- Clamp the Y component
  vector:clampZ(min, max)               -- Clamp the Z component
  vector:floor()                        -- Rounds all components down
  vector:ceil()                         -- Rounds all components up
  vector:round()                        -- Rounds all components to the closest integer
  vector:absolute()                     -- Vector with absolute values of components
  vector:isCollinearWith(otherVector)   -- Checks to see if vector is collinear with otherVector
  vector:getIntermediateWithX(other, x) -- New vector with given x value along the line between vector and other, or nil if not possible
  vector:getIntermediateWithY(other, y) -- New vector with given y value along the line between vector and other, or nil if not possible
  vector:getIntermediateWithZ(other, z) -- New vector with given z value along the line between vector and other, or nil if not possible
  vector:rotateAroundX(angle)           -- Rotates vector around the x axis by the specified angle(radians)
  vector:rotateAroundY(angle)           -- Rotates vector around the y axis by the specified angle(radians)
  vector:rotateAroundZ(angle)           -- Rotates vector around the z axis by the specified angle(radians)
  vector:clone()                        -- Returns a new vector with same component values as vector
  vector:equals(otherVector)            -- Checks to see if vector and otherVector are equal
  vector:tostring()                     -- Returns the string representation of vector "(x, y, z)"

Vector 'Object' Metatable Overrides:    -- [x, y, z] represents a vector object in these examples, not irl
  To String             -- tostring will get the string representation
                            ie. tostring([1, 2, 3])     -->     "(1, 2, 3)"
  Unary Minus           -- Using unary minus on a vector will result in the negative of vector
                            ie. -[1, -2, 3]             -->     [-1, 2, -3]
  Addition              -- Can add two vectors or vector and number with +
                            ie. [1, 2, 3] + [4, 5, 6]   -->     [5, 7, 9]
                                [1, 2, 3] + 3           -->     [4, 5, 6]
  Subtraction           -- Can subtract two vectors or vector and number with -
                            ie. [4, 5, 6] - [1, 2, 3]   -->     [3, 3, 3]
                                [4, 5, 6] - 3           -->     [1, 2, 3]
  Multiplication        -- Can multiply two vectors or vector and number with *
                            ie. [1, 2, 3] * [4, 5, 6]   -->     [4, 10, 18]
                                [1, 2, 3] * 3           -->     [3, 6, 9]
  Division              -- Can divide two vectors or vector and number with /
                            ie. [4, 10, 18] / [1, 2, 3] -->     [4, 5, 6]
                                [3, 6, 9] / 3           -->     [1, 2, 3]
  Equality              -- Can check if two vectors are the same with ==
                            ie. [4, 5, 6] == [4, 5, 6]  -->     true
                                [4, 5, 6] == [4, 99, 6] -->     false

Vector API functions:
  Vector.getMinimum(v1, v2)             -- Gets the minimum components of two vectors
  Vector.getMaximum(v1, v2)             -- Gets the maximum components of two vectors
  Vector.getMidpoint(v1, v2)            -- Gets the midpoint of two vectors
  Vector.isVector(v)                    -- Checks whether v is a vector created by this api
  Vector.new(x, y, z)                   -- Creates a new vector object with the component values
--]]

new = function() end -- Forward declaration
isVector = function() end -- Mo' forward declaration
local getType = function() end -- Mo' forward declaration

local vector = {
  add = function(self, v)
    return new(
      self.x + v.x,
      self.y + v.y,
      self.z + v.z
    )
  end,
  scalarAdd = function(self, n)
    return new(
      self.x + n,
      self.y + n,
      self.z + n
    )
  end,
  subtract = function(self, v)
    return new(
      self.x - v.x,
      self.y - v.y,
      self.z - v.z
    )
  end,
  scalarSubtract = function(self, n)
    return new(
      self.x - n,
      self.y - n,
      self.z - n
    )
  end,
  multiply = function(self, v)
    return new(
      self.x * v.x,
      self.y * v.y,
      self.z * v.z
    )
  end,
  scalarMultiply = function(self, n)
    return new(
      self.x * n,
      self.y * n,
      self.z * n
    )
  end,
  divide = function(self, o)
    return new(
      self.x / o.x,
      self.y / o.y,
      self.z / o.z
    )
  end,
  scalarDivide = function(self, n)
    return new(
      self.x / n,
      self.y / n,
      self.z / n
    )
  end,
  length = function(self)
    return math.sqrt(
      self.x * self.x
      + self.y * self.y
      + self.z * self.z
    )
  end,
  lengthSq = function(self)
    return (
      self.x * self.x
      + self.y * self.y
      + self.z * self.z
    )
  end,
  distance = function(self, o)
    return math.sqrt(
      math.pow(o.x - self.x, 2)
      + math.pow(o.y - self.y, 2)
      + math.pow(o.z - self.z, 2)
    )
  end,
  distanceSq = function(self, o)
    return (
      math.pow(o.x - self.x, 2)
      + math.pow(o.y - self.y, 2)
      + math.pow(o.z - self.z, 2)
    )
  end,
  normalize = function(self)
    return self:scalarDivide(self:length())
  end,
  dot = function(self, o)
    return (
      self.x * o.x
      + self.y * o.y
      + self.z * o.z
    )
  end,
  cross = function(self, o)
    return new(
      self.y * o.z - self.z * o.y,
      self.z * o.x - self.x * o.z,
      self.x * o.y - self.y * o.x
    )
  end,
  containedWithin = function(self, min, max)
    return (
      self.x >= min.x and self.x <= max.x
      and self.y >= min.y and self.y <= max.y
      and self.z >= min.z and self.z <= max.z
    )
  end,
  clampX = function(self, min, max)
    return new(
      math.max(min, math.min(max, self.x)),
      self.y,
      self.z
    )
  end,
  clampY = function(self, min, max)
    return new(
      self.x,
      math.max(min, math.min(max, self.y)),
      self.z
    )
  end,
  clampZ = function(self, min, max)
    return new(
      self.x,
      self.y,
      math.max(min, math.min(max, self.z))
    )
  end,
  floor = function(self)
    return new(
      math.floor(self.x),
      math.floor(self.y),
      math.floor(self.z)
    )
  end,
  ceil = function(self)
    return new(
      math.ceil(self.x),
      math.ceil(self.y),
      math.ceil(self.z)
    )
  end,
  round = function(self)
    return new(
      math.floor(self.x + 0.5),
      math.floor(self.y + 0.5),
      math.floor(self.z + 0.5)
    )
  end,
  absolute = function(self)
    return new(
      math.abs(self.x),
      math.abs(self.y),
      math.abs(self.z)
    )
  end,
  isCollinearWith = function(self, o)
    if self.x == 0 and self.y == 0 and self.z == 0 then
      return true
    end
    local otherX, otherY, otherZ = o.x, o.y, o.z
    if otherX == 0 and otherY == 0 and otherZ == 0 then
      return true
    end
    if (self.x == 0) ~= (otherX == 0) then return false end
    if (self.y == 0) ~= (otherY == 0) then return false end
    if (self.z == 0) ~= (otherZ == 0) then return false end
    local quotientX = otherX / self.x
    if quotientX == quotientX then
      return o:equals(self:scalarMultiply(quotientX))
    end
    local quotientY = otherY / self.y
    if quotientY == quotientY then
      return o:equals(self:scalarMultiply(quotientY))
    end
    local quotientZ = otherZ / self.z
    if quotientZ == quotientZ then
      return o:equals(self:scalarMultiply(quotientZ))
    end
  end,
  getIntermediateWithX = function(self, o, v)
    local vX = o.x - self.x
    local vY = o.y - self.y
    local vZ = o.z - self.z
    if vX * vX < 1.0000000116860974e-7 then
      return nil
    else
      local nMul = (v - self.x) / vX
      return (
        (nMul >= 0 and nMul <= 1) 
        and new(
          self.x + vX * nMul,
          self.y + vY * nMul,
          self.z + vZ * nMul
        )
        or nil
      )
    end
  end,
  getIntermediateWithY = function(self, o, v)
    local vX = o.x - self.x
    local vY = o.y - self.y
    local vZ = o.z - self.z
    if vY * vY < 1.0000000116860974e-7 then
      return nil
    else
      local nMul = (v - self.y) / vY
      return (
        (nMul >= 0 and nMul <= 1) 
        and new(
          self.x + vX * nMul,
          self.y + vY * nMul,
          self.z + vZ * nMul
        )
        or nil
      )
    end
  end,
  getIntermediateWithZ = function(self, o, v)
    local vX = o.x - self.x
    local vY = o.y - self.y
    local vZ = o.z - self.z
    if vZ * vZ < 1.0000000116860974e-7 then
      return nil
    else
      local nMul = (v - self.z) / vZ
      return (
        (nMul >= 0 and nMul <= 1) 
        and new(
          self.x + vX * nMul,
          self.y + vY * nMul,
          self.z + vZ * nMul
        )
        or nil
      )
    end
  end,
  rotateAroundX = function(self, n)
    local c, s = math.cos(n), math.sin(n)
    return new(
      self.x,
      self.y * c + self.z * s,
      self.z * c - self.y * s
    )
  end,
  rotateAroundY = function(self, n)
    local c, s = math.cos(n), math.sin(n)
    return new(
      self.x * c + self.z * s,
      self.y,
      self.z * c - self.x * s
    )
  end,
  rotateAroundZ = function(self, n)
    local c, s = math.cos(n), math.sin(n)
    return new(
      self.x * c + self.y * s,
      self.y * c - self.x * s,
      self.z
    )
  end,
  clone = function(self)
    return new(
      self.x,
      self.y,
      self.z
    )
  end,
  equals = function(self, o)
    if not isVector(self) or not isVector(o) then return false end
    return (
      o.x == self.x
      and o.y == self.y
      and o.z == self.z
    )
  end,
  tostring = function(self)
    return "("..self.x..", "..self.y..", "..self.z..")"
  end
}

local vmetatable = {
  __index = vector,
  __tostring = vector.tostring,
  __unm = function(v) return v:scalarMultiply(-1) end,
  __add = function(a, :)/>/> 
    if type(B)/>/> == "number" and isVector(a) then 
      return a:scalarAdd(:)/>/> 
    elseif type(a) == "number" and isVector(B)/>/> then
      return b:scalarAdd(a)
    elseif isVector(a) and isVector(B)/>/> then 
      return a:add(B)/>/> 
    else
      error("Attempt to perform vector addition on <"..getType(a).."> and <"..getType(B)/>/>..">")
    end 
  end,
  __sub = function(a, B)/>/> 
    if type(B)/>/> == "number" and isVector(a) then 
      return a:scalarSubtract(B)/>/> 
    elseif type(a) == "number" and isVector(B)/>/> then
      return b:scalarSubtract(a)
    elseif isVector(a) and isVector(B)/>/> then 
      return a:subtract(B)/>/> 
    else
      error("Attempt to perform vector subtraction on <"..getType(a).."> and <"..getType(B)/>/>..">")
    end 
  end,
  __mul = function(a, B)/>/> 
    if type(B)/>/> == "number" and isVector(a) then 
      return a:scalarMultiply(B)/>/> 
    elseif type(a) == "number" and isVector(B)/>/> then
      return b:scalarMultiply(a)
    elseif isVector(a) and isVector(B)/>/> then 
      return a:multiply(B)/>/> 
    else
      error("Attempt to perform vector multiplication on <"..getType(a).."> and <"..getType(B)/>/>..">")
    end 
  end,
  __div = function(a, B)/>/> 
    if type(B)/>/> == "number" and isVector(a) then 
      return a:scalarDivide(B)/>/> 
    elseif type(a) == "number" and isVector(B)/>/> then
      return b:scalarDivide(a)
    elseif isVector(a) and isVector(B)/>/> then 
      return a:divide(B)/>/> 
    else
      error("Attempt to perform vector division on <"..getType(a).."> and <"..getType(B)/>/>..">")
    end 
  end,
  __eq = vector.equals
}

function getMinimum(v1, v2)
  return new(
    math.min(v1.x, v2.x),
    math.min(v1.y, v2.y),
    math.min(v1.z, v2.z)
  )
end

function getMaximum(v1, v2)
  return new(
    math.max(v1.x, v2.x),
    math.max(v1.y, v2.y),
    math.max(v1.z, v2.z)
  )
end

function getMidpoint(v1, v2)
  return new(
    (v1.x + v2.x) / 2,
    (v1.y + v2.y) / 2,
    (v1.z + v2.z) / 2
  )
end

getType = function(v)
  if isVector(v) then
    return "vector"
  else
    return type(v)
  end
end

isVector = function(v)
  return getmetatable(v) == vmetatable
end

new = function(x, y, z)
  local v = {
        ['x'] = x or 0,
        ['y'] = y or 0,
        ['z'] = z or 0
  }
  setmetatable(v, vmetatable)
  return v
end
FuzzyPurp #2
Posted 01 September 2012 - 10:52 AM
Wow! Nice work!
Xtansia #3
Posted 01 September 2012 - 11:22 AM
Just realised I had made a mistake in isCollinearWith now fixed,
I had put multiply(quotient) instead of scalarMultiply(quotient)
Xtansia #4
Posted 02 September 2012 - 01:46 AM
Derp, fixed another mistake,
in the normalize function, self:divide should be self:scalarDivide
MysticT #5
Posted 02 September 2012 - 02:14 AM
Nice work! Some really usefull functions.

I think there's another error in the isCollinearWith function:

if quotientX == quotientX then
That will always be true. I think one should be math.floor(quotientX) or something like that. Same for y and z.
Xtansia #6
Posted 02 September 2012 - 02:17 AM
Nice work! Some really usefull functions.

I think there's another error in the isCollinearWith function:

if quotientX == quotientX then
That will always be true. I think one should be math.floor(quotientX) or something like that. Same for y and z.

Thank you, and it's not an error

quotientX == quotientX

Stops a NaN(0/0) value from passing through, As NaN is the only value that will not equal itself.
MysticT #7
Posted 02 September 2012 - 02:25 AM
Oh, didn't think that :)/>/>
Xtansia #8
Posted 10 September 2012 - 12:24 PM
New update, changes:

Added:
Vector Object:
vector:getIntermediateWithX(other, x) – New vector with given x value along the line between vector and other, or nil if not possible
vector:getIntermediateWithY(other, y) – New vector with given y value along the line between vector and other, or nil if not possible
vector:getIntermediateWithZ(other, z) – New vector with given z value along the line between vector and other, or nil if not possible
vector:rotateAroundX(angle) – Rotates vector around the x axis by the specified angle(radians)
vector:rotateAroundY(angle) – Rotates vector around the y axis by the specified angle(radians)
vector:rotateAroundZ(angle) – Rotates vector around the z axis by the specified angle(radians)
vector:clone() – Returns a new vector with same component values as vector

Vector API:
Vector.isVector(v) – Checks whether v is a vector created by this api

Removed:
Vector.clone(v)
Sebra #9
Posted 15 September 2012 - 07:32 AM
How about vector(x,y,z) to be available?
Also probably can substitute standard API.
Xtansia #10
Posted 17 September 2012 - 10:51 AM
How about vector(x,y,z) to be available?
Also probably can substitute standard API.

Could you explain more what you mean by vector(x, y, z)?
And yes it is possible to completely replace the default vector api with this one
MysticT #11
Posted 17 September 2012 - 05:56 PM
How about vector(x,y,z) to be available?
Also probably can substitute standard API.

Could you explain more what you mean by vector(x, y, z)?
And yes it is possible to completely replace the default vector api with this one
I think he means to create new vector "objects" using vector(x, y, z) (calling the table as a function).
Not sure if that's possible, since vector is the api table, so you can't change it's metatable. You could probably do something like vector.vector(x, y, z), but it's easier with vector.new.
Sebra #12
Posted 17 September 2012 - 06:59 PM
metatable(obj).__call called when Lua calls obj()
MysticT is right.
Also seems bios create api's metatable after loading. That's mean in order to implement it bios should be changed. :)/>/>

Would it good if __call would be transfered to metatable of api instead of api.__call ?
May be we should suggest it to Dan200? Only __call probably.
Xtansia #13
Posted 18 September 2012 - 06:00 AM
I was guessing you meant call it like a constructor but just wanted to make sure,
I /could/ make it possible to make the vector api callable but in a round about way, without modfiying the bios.
I might mess around with it a bit and get back to you.
elementalist #14
Posted 07 October 2012 - 05:56 AM
It's a shame it appears no one is using this, it does everything I want to once I found the reason it wasn't loading.

Line 346 is missing the , after end.

Fixed up pasteBin link http://pastebin.com/qpK5AQDq

Your inline version, as well as pastebin and bitbucket versions all have this bug.

Thanks for the API :D/>/>
Xtansia #15
Posted 07 October 2012 - 07:22 AM
It's a shame it appears no one is using this, it does everything I want to once I found the reason it wasn't loading.

Line 346 is missing the , after end.

Fixed up pasteBin link http://pastebin.com/qpK5AQDq

Your inline version, as well as pastebin and bitbucket versions all have this bug.

Thanks for the API ;)/>/>

Thank you for pointing that out, :D/>/>
Fixed it now.