diff --git a/matrix.lua b/matrix.lua index e9945c07fba9b0157e457581c2e3a2a2b10c4da1..e2f0577092cdcb05bec675867dccbd6359198ebd 100644 --- a/matrix.lua +++ b/matrix.lua @@ -1,8 +1,12 @@ require("kara3d.vector") +-- ********** DEFINITION AND GENERIC FUNCTIONS ********** + matrix = {class = "matrix"} matrix.__index = matrix +-- Creates a new matrix with r rows and c columns, +-- initialized with the table vals function matrix.new(r, c, vals) local m = {rows = r or 1, cols = c or 1, values = {}} vals = vals or {} @@ -13,6 +17,12 @@ function matrix.new(r, c, vals) return m end +-- Clones this matrix +function matrix:clone() + return matrix.new(self.rows, self.cols, self.values) +end + +-- Returns a string representing the matrix function matrix:tostring() local str = self.rows .. " x " .. self.cols .. "\n" for i = 1, self.rows do @@ -23,16 +33,67 @@ function matrix:tostring() return str end +-- ********** STATIC FUNCTIONS ********** + +-- Returns a matrix with r rows and c columns, +-- with 1 on the diagonal and 0 in the other cells function matrix.identity(r, c) local m = matrix.new(r, c) for i = 1, math.min(r, c) do m:set(i, i, 1) end return m end -function matrix:clone() - return matrix.new(self.rows, self.cols, self.values) +-- Returns the product of two matrices, or a scalar and a matrix +-- Only works if m1 or m2 is a scalar, +-- or if m1 has a number of columns equal to m2's number of rows +function matrix.mul_matrix(m1, m2) + if (type(m1) == "number") then return matrix.mul_scalar(m2, m1) end + if (type(m2) == "number") then return matrix.mul_scalar(m1, m2) end + if (m1.cols ~= m2.rows) then return m1 end + + local m = matrix.new(m1.rows, m2.cols) + for i = 1, m1.rows do + for j = 1, m2.cols do + local sum = 0 + for k = 1, m1.cols do + sum = sum + m1:get(i, k) * m2:get(k, j) + end + m:set(i, j, sum) + end + end + + return m end +-- Returns the product of a matrix and a vector +-- Only works if m has a number of rows equal to the size of v +function matrix.mul_vector(m, v) + if (m.cols ~= v.size) then return v end + + local vals = {} + for i = 1, m.rows do + local sum = 0 + for j = 1, m.cols do + sum = sum + m:get(i, j) * v:get(j) + end + vals[i] = sum + end + + return vector.new(m.rows, vals) +end + +-- Returns the product of a scalar and a matrix +function matrix.mul_scalar(m, s) + local r = matrix.new(m.rows, m.cols) + for i = 1, #(r.values) do + r.values[i] = s * m.values[i] + end + return r +end + +-- ********** INSTANCE FUNCTIONS ********** + +-- Returns the value on the (i, j) cell column of this matrix function matrix:get(i, j) if (i <= 0 or i > self.rows) then return 0 end if (j <= 0 or j > self.cols) then return 0 end @@ -40,6 +101,13 @@ function matrix:get(i, j) return n > #self.values and 0 or self.values[n] end +-- Changes the (i, j) cell to be value +function matrix:set(i, j, value) + self.values[(i-1) * self.cols + j] = value + return self +end + +-- Returns a vector corresponding to the ith column of this matrix function matrix:column(i) if (i <= 0 or i > self.cols) then return vector.zero(self.rows) end local vals = {} @@ -47,11 +115,11 @@ function matrix:column(i) return vector.new(self.rows, vals) end -function matrix:set(i, j, value) - self.values[(i-1) * self.cols + j] = value - return self -end +-- ********** OPERATOR OVERLOADS ********** +-- + binary operator overload +-- Only works on matrices with the same dimensions +-- Returns a matrix m' such that for all (i, j), m'(i, j) = m1(i, j) + m2(i, j) function matrix.__add(m1, m2) if (m1.rows ~= m2.rows or m1.cols ~= m2.cols) then return m1 end @@ -63,6 +131,8 @@ function matrix.__add(m1, m2) return m end +-- - unary operator overload +-- Returns a matrix m' such that for all (i, j), m'(i, j) = -m(i, j) function matrix.__unm(m) local n = matrix.new(m.rows, m.cols) for i = 1, #(n.values) do @@ -71,6 +141,8 @@ function matrix.__unm(m) return n end +-- - binary operator overload +-- Returns a matrix m' such that for all (i, j), m'(i, j) = m1(i, j) - m2(i, j) function matrix.__sub(m1, m2) if (m1.rows ~= m2.rows or m1.cols ~= m2.cols) then return m1 end @@ -82,48 +154,9 @@ function matrix.__sub(m1, m2) return m end -function matrix.mul_matrix(m1, m2) - if (type(m1) == "number") then return matrix.mul_scalar(m2, m1) end - if (type(m2) == "number") then return matrix.mul_scalar(m1, m2) end - if (m1.cols ~= m2.rows) then return m1 end - - local m = matrix.new(m1.rows, m2.cols) - for i = 1, m1.rows do - for j = 1, m2.cols do - local sum = 0 - for k = 1, m1.cols do - sum = sum + m1:get(i, k) * m2:get(k, j) - end - m:set(i, j, sum) - end - end - - return m -end - -function matrix.mul_vector(m, v) - if (m.cols ~= v.size) then return v end - - local vals = {} - for i = 1, m.rows do - local sum = 0 - for j = 1, m.cols do - sum = sum + m:get(i, j) * v:get(j) - end - vals[i] = sum - end - - return vector.new(m.rows, vals) -end - -function matrix.mul_scalar(m, s) - local r = matrix.new(m.rows, m.cols) - for i = 1, #(r.values) do - r.values[i] = s * m.values[i] - end - return r -end - +-- * binary operator overload +-- Returns the product of a matrix or scalar (left) +-- and a matrix, vector or scalar (right) function matrix.__mul(m, o) if (getmetatable(o) == matrix) then return matrix.mul_matrix(m, o) elseif (getmetatable(o) == vector) then return matrix.mul_vector(m, o) @@ -131,6 +164,8 @@ function matrix.__mul(m, o) return m end +-- == operator overload +-- Returns "for all (i, j), m1(i, j) == m2(i, j)" function matrix.__eq(m1, m2) if (m1.rows ~= m2.rows or m1.cols ~= m2.cols) then return false end diff --git a/shape.lua b/shape.lua index 98d795443e193632ca1a8624f17ec9e071dec8c4..37b6d37ce45918e29b81bfe2b9488ecfe5546cd4 100644 --- a/shape.lua +++ b/shape.lua @@ -5,44 +5,55 @@ require("kara3d.transform") require("utils") re = require("re") +-- ********** DEFINITION AND GENERIC FUNCTIONS ********** + shape = {class = "shape"} shape.__index = shape face = {class = "face"} face.__index = face -function face.new(v, n, t) - local f = {vertices = v or {}, normal = n or -vector.cardinal(3, 3), tags = t} - setmetatable(f, face) - return f -end - +-- Creates a new shape with transform t and faces f function shape.new(t, f) local s = {transform = t, faces = f or {}} setmetatable(s, shape) return s end +-- Creates a new face with vertices c, normal n and tags t +function face.new(v, n, t) + local f = {vertices = v or {}, normal = n or -vector.cardinal(3, 3), tags = t} + setmetatable(f, face) + return f +end + +-- Clones this shape function shape:clone() return shape.new(self.transform, table.copy(self.faces)) end +-- ********** INSTANCE FUNCTIONS ********** + +-- Generates a line for each face of this shape, with the indicated tags +-- The boolean cull determines if the faces facing away from the screen are +-- drawn or not function shape:draw(subs, line, tags, cull) local l = table.copy(line) local p = self.transform:position(true) local r = self.transform:rotation(true) local m = self.transform:matrix(true) local c = vector.cardinal(4, 4) - local return_str = nil + local layer = line.layer for i = 1, #self.faces do local face = self.faces[i] local n = quaternion.mul_vector(r, face.normal) - if not (cull and n:z() <= 0) then + if not (cull and n:z() > 0) then local vs = face.vertices local v = matrix.mul_vector(m, vs[#vs] + c) - p + local sum = vector.zero(4) local str = (tags and "{" .. tags .. "}" or "") .. (face.tags and "{" .. face.tags .. "}" or "") .. "{\\an7\\pos(" .. p:x() .. ", " .. p:y() .. ")\\p1}" @@ -50,10 +61,13 @@ function shape:draw(subs, line, tags, cull) for i = 1, #vs do v = matrix.mul_vector(m, vs[i] + c) - p + sum = sum + v str = str .. "l " .. v:x() .. " " .. v:y() .. " " end l.text = str + -- layers must be between 0 and 9999999999 + l.layer = 500000000 + math.floor(-(p + sum):z() / #vs) l.effect = "fx" subs.append(l) end @@ -62,14 +76,16 @@ function shape:draw(subs, line, tags, cull) return "" end ---[[ - 1________5 - /| /| - 2/__|____/6 | Y - | 3|_ _|_ _|7 |__ X - | / | / / -4|/______|/8 Z -]]-- +-- ********** BASIC SHAPE FUNCTIONS ********** + +-- Returns a cube shape of side length size, that follows the transform t +-- Schematic of the vertices: +-- 1________5 +-- /| /| +-- 2/__|____/6 | +-- | 3|_ _|_ _|7 __ X +-- | / | / /| +-- 4|/______|/8 Z Y function shape.cube(t, size) local hs = size / 2 local vs = {vector.new(3, {-hs, -hs, -hs}), vector.new(3, {-hs, -hs, hs}),