I'm a total beginoob in Lua. The aim is to display a 3D Torus in dots (see Figure1), or at least compute the positions, privileging clarity over performance. Pseudocode link.
Don't hesitate to be picky, I'd like to learn proper Lua idioms. For instance, I was pleased to find operator overloading, but couldn't find a neat way to bind it to my "matrix type" by default.
-- Bake dot-donut. Based on pseudo-code here:
-- https://64nops.wordpress.com/2021/01/21/rosetta-sugar-japprends-a-coder-avec-mon-cpc/
-- Main parameters to play with.
r, r2 = 100, 20 -- Major and minor radius
dist = 200 -- Distance from observator. Should be greater than major radius
zoom = 300
dots_per_circle = 100
nbcircles = 200
-- pi is wrong!
local tau = 2*math.pi
local cos = math.cos
local sin = math.sin
-- For operator overloading
metamatrix = {}
-- Rotation matrice around x axis
function rotx(a)
local c, s = cos(a), sin(a)
res = {{1, 0, 0},
{0, c, -s},
{0, s, c}}
setmetatable(res, metamatrix)
return res
end
-- Rotation matrice around y axis
function roty(a)
local c, s = cos(a), sin(a)
res = {{c, 0, -s},
{0, 1, 0},
{s, 0, c}}
setmetatable(res, metamatrix)
return res
end
-- Rotation matrice around z axis
function rotz(a)
local c, s = cos(a), sin(a)
res = {{c, -s, 0},
{s, c, 0},
{0, 0, 1}}
setmetatable(res, metamatrix)
return res
end
-- Multiplication of matrices (m rows n cols) * (n rows p cols)
function metamatrix.__mul(A, B)
local res = {}
for i = 1, #A do
res[i] = {}
for j = 1, #B[1] do -- TODO? Handle empty matrix.
local s = 0
for k = 1, #B do
s = s + A[i][k] * B[k][j]
end
res[i][j] = s
end
end
setmetatable(res, metamatrix)
return res
end
-- Abstract the encoding of position (x y z).
-- Here, we choose column vector convention,
-- to be compatible with left multiplication by rotation matrices.
function dot(x, y, z)
return {{x},
{y},
{z}}
end
function undot(dot)
return dot[1][1],
dot[2][1],
dot[3][1]
end
-- 3d to 2d
function proj(x, y, z)
local z2 = z + dist
return x/z2 * zoom, y/z2 * zoom
end
-- List of regularly spaced dots from a circle of radius r' at distance x = r in the plane XOY.
circle = {}
for i = 1, dots_per_circle do
local a = (i-1) * tau / dots_per_circle
circle[i] = dot(cos(a)*r2+r, sin(a)*r2, 0)
end
-- Now the torus is a surface of revolution.
torus = {}
for i = 1, nbcircles do
local a = (i-1) * tau / nbcircles
for _, dot in pairs(circle) do
table.insert(torus, roty(a) * dot)
end
end
-- Let's tilt it.
tilt = rotx(0.5) * rotz(0.3)
torus2 = {}
for _, dot in pairs(torus) do
table.insert(torus2, tilt * dot)
end
-- Project to 2D and serialize.
for _, dot in pairs(torus2) do
local x, y, z = undot(dot)
xp, yp = proj(x, y, z)
print(xp, yp)
end