From 1fd93b966f238721549f0d1c0c1d86df377619e5 Mon Sep 17 00:00:00 2001
From: ultrakatiz <ultrakatiz@gmail.com>
Date: Thu, 19 May 2022 02:52:35 +0200
Subject: [PATCH] cleaned quaternion.lua, transform.lua and vector.lua

---
 quaternion.lua |  73 ++++++++++------
 transform.lua  |  12 +++
 vector.lua     | 226 ++++++++++++++++++++++++++++++-------------------
 3 files changed, 198 insertions(+), 113 deletions(-)

diff --git a/quaternion.lua b/quaternion.lua
index 98ee5fd..4aed970 100644
--- a/quaternion.lua
+++ b/quaternion.lua
@@ -1,47 +1,45 @@
 require("kara3d.vector")
 require("kara3d.matrix")
 
+-- ********** DEFINITION AND GENERIC FUNCTIONS **********
+
 quaternion = {class = "quaternion"}
 quaternion.__index = quaternion
 
+-- Creates the quaternion ai + bj + dk + d
 function quaternion.new(a, b, c, d)
   local q = {x = a or 0, y = b or 0, z = c or 0, w = d or 1}
   setmetatable(q, quaternion)
   return q
 end
 
-function quaternion:inv()
-  return quaternion.new(-self.x, -self.y, -self.z, -self.w)
+-- Clones this quaternion
+function quaternion:clone()
+  return quaternion.new(self.x, self.y, self.z, self.w)
 end
 
+-- Returns this quaternion's coordinates, separated by commas
 function quaternion:tostring()
   return self.x .. ", " .. self.y .. ", " .. self.z .. ", " .. self.w
 end
 
-function quaternion:set(x, y, z, w)
-  if (type(x) == "table" and x.class == "quaternion") then
-    self.x = x.x
-    self.y = x.y
-    self.z = x.z
-    self.w = x.w
-  else
-    self.x = x or 0
-    self.y = y or 0
-    self.z = z or 0
-    self.w = w or 1
-  end
-  return self
-end
+-- ********** STATIC FUNCTIONS **********
+
+-- Returns the quaternion 0i + 0j + 0k + 1, which is no rotation
+function quaternion.identity() return quaternion.new() end
 
+-- Returns the product of two (normalized) quaternions
 function quaternion.mul_quaternion(q1, q2)
-  local b, c, d, a = q1:unpack()
-  local f, g, h, e = q2:unpack()
+  local b, c, d, a = q1:normalized():unpack()
+  local f, g, h, e = q2:normalized():unpack()
   return quaternion.new(a*e - b*f - c*g - d*h,
                         a*f + b*e + c*h - d*g,
                         a*g + c*e + d*f - b*h,
                         a*h + d*e + b*g - c*f)
 end
 
+-- Returns a vector equal to the vector v (size >= 3)
+-- rotated by the quaternion q
 function quaternion.mul_vector(q, v)
   local v3 = vector.new(3, v.values)
   local u1 = vector.new(3, {q.x, q.y, q.z})
@@ -51,6 +49,7 @@ function quaternion.mul_vector(q, v)
   return 2*d * u1 + (q.w*q.w - u1:length2()) * v3 + 2*q.w * c
 end
 
+-- Returns a quaternion representing the rotation from -> to (size = 3)
 function quaternion.from_to_rotation(from, to)
   local v0 = from:normalized()
 	local v1 = to:normalized()
@@ -61,6 +60,8 @@ function quaternion.from_to_rotation(from, to)
   return quaternion.axis_angle(vector.cross(v0, v1):normalize(), math.acos(d))
 end
 
+-- Returns a quaternion representing the rotation of angle around axis
+-- If angle is nil, then the norm of axis will be used as the angle
 function quaternion.axis_angle(axis, angle)
   angle = angle or axis:length()
 
@@ -71,10 +72,36 @@ function quaternion.axis_angle(axis, angle)
   return quaternion.new(naxis:x() * hsin, naxis:y() * hsin, naxis:z() * hsin, hcos)
 end
 
+-- ********** INSTANCE FUNCTIONS **********
+
+-- Sets this quaternion's coordinates to these values
+-- Arguments may be 4 numbers or 1 table with indexes x, y, z and w
+function quaternion:set(x, y, z, w)
+  if (type(x) == "table" and x.class == "quaternion") then
+    self.x = x.x
+    self.y = x.y
+    self.z = x.z
+    self.w = x.w
+  else
+    self.x = x or 0
+    self.y = y or 0
+    self.z = z or 0
+    self.w = w or 1
+  end
+  return self
+end
+
+-- Returns the inverse of this quaternion
+function quaternion:inv()
+  return quaternion.new(-self.x, -self.y, -self.z, -self.w)
+end
+
+-- Returns the 4 coordinates of this quaternion
 function quaternion:unpack()
   return self.x, self.y, self.z, self.w
 end
 
+-- Normalizes this quaternion, so that its norm is 1
 function quaternion:normalize()
   local len = self:length()
   if len ~= 0 and len ~= 1 then
@@ -86,26 +113,24 @@ function quaternion:normalize()
   return self
 end
 
+-- Returns a quaternion which is equal to this quaternion, normalized
 function quaternion:normalized()
   local q = self:clone()
   q:normalize()
   return q
 end
 
-function quaternion:clone()
-  return quaternion.new(self.x, self.y, self.z, self.w)
-end
-
+-- Returns the square of the norm of this quaternion
 function quaternion:length2()
   return self.x * self.x + self.y * self.y + self.z * self.z + self.w * self.w
 end
 
+-- Returns the norm of this quaternion
 function quaternion:length()
   return math.sqrt(self:length2())
 end
 
-function quaternion.identity() return quaternion.new() end
-
+-- Returns the rotation matrix (4x4) corresponding to this quaternion
 function quaternion:to_matrix()
   local x, y, z, w = self:unpack()
   local m = matrix.new(4, 4,
diff --git a/transform.lua b/transform.lua
index 4abe684..e755162 100644
--- a/transform.lua
+++ b/transform.lua
@@ -2,9 +2,13 @@ require("kara3d.vector")
 require("kara3d.matrix")
 require("kara3d.quaternion")
 
+-- ********** DEFINITION AND GENERIC FUNCTIONS **********
+
 transform = {class = "transform"}
 transform.__index = transform
 
+-- Creates a new transform with position p, rotation r, scale s and parent par
+-- p and s should be of size 3, r is a quaternion, and par may be nil
 function transform.new(p, r, s, par)
   p = p or vector.zero(3)
   r = r or quaternion.identity()
@@ -14,6 +18,7 @@ function transform.new(p, r, s, par)
   return t
 end
 
+-- Returns a string that represents this transform
 function transform:tostring()
   return "(pos = (" .. self.pos:tostring() .. "), "
     .. "rot = (" .. self.rot:tostring() .. "), "
@@ -21,11 +26,15 @@ function transform:tostring()
     .. (self.parent and "has parent" or "no parent") .. ")"
 end
 
+-- ********** INSTANCE FUNCTIONS **********
+
+-- Rotates this transform by angle around axis
 function transform:rotate(axis, angle)
   self.rot = quaternion.axis_angle(axis, angle) * self.rot
   return self
 end
 
+-- Returns the local or global position of this transform
 function transform:position(global)
   local p = self.pos:clone()
   if (p.size > 4) then p:set_size(4) end
@@ -36,6 +45,7 @@ function transform:position(global)
   return p:set_size(3)
 end
 
+-- Returns the local or global rotation of this transform
 function transform:rotation(global)
   local q = self.rot:normalized()
   if (global and self.parent) then
@@ -44,6 +54,7 @@ function transform:rotation(global)
   return q
 end
 
+-- Returns the local or global scale of this transform
 function transform:scale(global)
   local s = self.scl:clone()
   if (global and self.parent) then
@@ -55,6 +66,7 @@ function transform:scale(global)
   return s
 end
 
+-- Returns the transformation matrix corresponding to this transform
 function transform:matrix(global)
   local translation = matrix.new(4, 4,
     {1, 0, 0, self.pos:get(1),
diff --git a/vector.lua b/vector.lua
index e77d9e4..60a4e67 100644
--- a/vector.lua
+++ b/vector.lua
@@ -1,6 +1,10 @@
+-- ********** DEFINITION AND GENERIC FUNCTIONS **********
+
 vector = {class = "vector"}
 vector.__index = vector
 
+-- Creates a vector of size n, initialized with values vals
+-- Uninitialized values are set to 0
 function vector.new(n, vals)
   if (type(n) == "table") then vals = n n = #n end
   vals = vals or {}
@@ -12,53 +16,10 @@ function vector.new(n, vals)
   return v
 end
 
--- operator overloading
-function vector.__add(a, b)
-  local small = a.size < b.size and a or b
-  local big = a.size < b.size and b or a
-  local r = big:clone()
-  for i = 1, small.size do r:set(i, r:get(i) + small:get(i)) end
-  return r
-end
-
-function vector.__unm(a)
-  local r = vector.new(a.size)
-  for i = 1, r.size do r:set(i, -(a:get(i))) end
-  return r
-end
-
-function vector.__sub(a, b)
-  local oppb = -b
-  return a + oppb
-end
-
-function vector.__mul(a, b)
-  if type(a) == "number" and type(b) ~= "number" then
-    local r = vector.new(b.size)
-    for i = 1, r.size do r:set(i, a * b:get(i)) end
-    return r
-  elseif type(a) ~= "number" and type(b) == "number" then
-    local r = vector.new(a.size)
-    for i = 1, r.size do r:set(i, a:get(i) * b) end
-    return r
-  elseif type(a) ~= "number" and type(b) ~= "number" then
-    local small = a.size < b.size and a or b
-    local big = a.size < b.size and b or a
-    local r = vector.new(big.size)
-    for i = 1, small.size do r:set(i, small:get(i) * big:get(i)) end
-    return r
-  else
-    return a * b
-  end
-end
-
-function vector.__eq(a, b)
-  if (a.size ~= b.size) then return false end
-  for i = 1, a.size do if (a:get(i) ~= b:get(i)) then return false end end
-  return true
-end
+-- Clones this vector
+function vector:clone() return vector.new(self.size, self.values) end
 
--- actual functions
+-- Returns this vector's coordinates, separated by commas
 function vector:tostring()
   local str = ""
   for i = 1, self.size - 1 do
@@ -67,30 +28,26 @@ function vector:tostring()
   return str .. self:get(self.size)
 end
 
-function vector:clone() return vector.new(self.size, self.values) end
-
-function vector:length2()
-  local sum = 0
-  for i = 1, self.size do sum = sum + self:get(i) * self:get(i) end
-  return sum
-end
-
-function vector:length() return math.sqrt(self:length2()) end
+-- ********** STATIC FUNCTIONS **********
 
-function vector:is_unit() return self:length2() == 1 end
+-- Returns a vector full of 0s, with size size
+function vector.zero(size) return vector.new(size) end
 
-function vector:normalize()
-  local len = self:length()
-  if len == 0 or len == 1 then return self end
-  for i = 1, self.size do self:set(i, self:get(i) / len) end
-  return self
+-- Returns a vector full of 1s, with size size
+function vector.one(size)
+  local vals = {}
+  for i = 1, size do vals[i] = 1 end
+  return vector.new(size, vals)
 end
 
-function vector:normalized() return self:clone():normalize() end
-
-function vector.project_vector(from, to) return (vector.dot(from, to) / to:length2()) * to end
-function vector.project_plane(from, normal) return from - vector.project_vector(from, normal) end
+-- Returns a vector of size size, filled with 0s and a 1 at index
+function vector.cardinal(size, index)
+  local vals = {}
+  for i = 1, size do vals[i] = i == index and 1 or 0 end
+  return vector.new(size, vals)
+end
 
+-- Returns the dot product of two vectors
 function vector.dot(a, b)
   local min_size = math.min(a.size, b.size)
   local sum = 0
@@ -98,20 +55,41 @@ function vector.dot(a, b)
   return sum
 end
 
+-- Returns the cross product of two vectors of size 3
+function vector.cross(a, b)
+  return vector.new(3, {a.values[2] * b.values[3] - a.values[3] * b.values[2],
+                        a.values[3] * b.values[1] - a.values[1] * b.values[3],
+                        a.values[1] * b.values[2] - a.values[2] * b.values[1]})
+end
+
+-- Returns a vector which is the projection of the vector from to the vector to
+function vector.project_vector(from, to) return (vector.dot(from, to) / to:length2()) * to end
+
+-- Returns a vector which is the projection of the vector from to the plane of normal normal
+function vector.project_plane(from, normal) return from - vector.project_vector(from, normal) end
+
+-- Returns the square of the distance between two vectors
 function vector.distance2(a, b) return (a - b):length2() end
+
+-- Returns the distance between two vectors
 function vector.distance(a, b) return (a - b):length() end
 
+-- Returns the angle between two vectors
 function vector.angle(from, to) return math.acos(vector.dot(from, to) / (from:length() * to:length())) end
+
+-- Returns a vector lerped between from and to, with parameter t
 function vector.lerp(from, to, t) return t * from + (1 - t) * to end
 
-function vector:set_values(vals)
-  vals = vals or {}
-  for i = 1, #vals do self.values[i] = vals[i] end
-  for i = #vals + 1, self.size do self.values[i] = nil end
-  self.size = #vals
-  return self
-end
+-- ********** INSTANCE FUNCTIONS **********
 
+-- Sets the ith cell of this vector to value
+function vector:set(i, value) self.values[i] = value return self end
+
+-- Returns the ith value of this vector
+function vector:get(i) return self.values[i] end
+
+-- Sets the size of this vector to n
+-- Uninitialized values are set to 0, and overflowing values are set to nil
 function vector:set_size(n)
   if (self.size == n) then return self end
   for i = n + 1, self.size do self:set(i, nil) end
@@ -120,31 +98,101 @@ function vector:set_size(n)
   return self
 end
 
-function vector.cross(a, b)
-  return vector.new(3, {a.values[2] * b.values[3] - a.values[3] * b.values[2],
-                        a.values[3] * b.values[1] - a.values[1] * b.values[3],
-                        a.values[1] * b.values[2] - a.values[2] * b.values[1]})
+-- Sets the values of this vector to vals
+-- Uninitialized values are set to 0
+function vector:set_values(vals)
+  vals = vals or {}
+  for i = 1, #vals do self:set(i, vals[i]) end
+  for i = #vals + 1, self.size do self:set(i, 0) end
+  self.size = #vals
+  return self
 end
 
-function vector.zero(size) return vector.new(size) end
-function vector.one(size)
-  local vals = {}
-  for i = 1, size do vals[i] = 1 end
-  return vector.new(size, vals)
-end
-function vector.cardinal(size, index)
-  local vals = {}
-  for i = 1, size do vals[i] = i == index and 1 or 0 end
-  return vector.new(size, vals)
+-- Returns the square of the norm of this vector
+function vector:length2()
+  local sum = 0
+  for i = 1, self.size do sum = sum + self:get(i) * self:get(i) end
+  return sum
 end
 
-function vector:get(index) return self.values[index] end
-function vector:set(index, value)
-  self.values[index] = value
+-- Returns the norm of this vector
+function vector:length() return math.sqrt(self:length2()) end
+
+-- Returns ||v|| == 1, v being this vector
+function vector:is_unit() return self:length2() == 1 end
+
+-- Sets the coordinates of this vector so that it has the same direction,
+-- but has a norm equal to 1
+function vector:normalize()
+  local len = self:length()
+  if len == 0 or len == 1 then return self end
+  for i = 1, self.size do self:set(i, self:get(i) / len) end
   return self
 end
 
+-- Returns a vector with the same direction as this vector, but with norm 1
+function vector:normalized() return self:clone():normalize() end
+
+-- Convenience functions that return the first four coordinates
 function vector:x() return self.values[1] end
 function vector:y() return self.values[2] end
 function vector:z() return self.values[3] end
 function vector:w() return self.values[4] end
+
+-- ********** OPERATOR OVERLOADING **********
+
+-- + binary operator overload
+-- Returns a vector c such that for all i, c(i) = a(i) + b(i)
+function vector.__add(a, b)
+  local small = a.size < b.size and a or b
+  local big = a.size < b.size and b or a
+  local r = big:clone()
+  for i = 1, small.size do r:set(i, r:get(i) + small:get(i)) end
+  return r
+end
+
+-- - unary operator overload
+-- Returns a vector c such that for all i, c(i) = -a(i)
+function vector.__unm(a)
+  local r = vector.new(a.size)
+  for i = 1, r.size do r:set(i, -(a:get(i))) end
+  return r
+end
+
+-- - binary operator overload
+-- Returns a vector c such that for all i, c(i) = a(i) - b(i)
+function vector.__sub(a, b)
+  local oppb = -b
+  return a + oppb
+end
+
+-- * binary operator overload
+-- If one of the arguments is a scalar, scales the other argument by it
+-- Else, returns a vector c such that for all i, c(i) = a(i) * b(i)
+function vector.__mul(a, b)
+  if type(a) == "number" and type(b) ~= "number" then
+    local r = vector.new(b.size)
+    for i = 1, r.size do r:set(i, a * b:get(i)) end
+    return r
+  elseif type(a) ~= "number" and type(b) == "number" then
+    local r = vector.new(a.size)
+    for i = 1, r.size do r:set(i, a:get(i) * b) end
+    return r
+  elseif type(a) ~= "number" and type(b) ~= "number" then
+    local small = a.size < b.size and a or b
+    local big = a.size < b.size and b or a
+    local r = vector.new(big.size)
+    for i = 1, small.size do r:set(i, small:get(i) * big:get(i)) end
+    return r
+  else
+    return a * b
+  end
+end
+
+-- == binary operator overload
+-- Returns "for all i, a(i) == b(i)""
+function vector.__eq(a, b)
+  if (a.size ~= b.size) then return false end
+  for i = 1, a.size do if (a:get(i) ~= b:get(i)) then return false end end
+  return true
+end
-- 
GitLab