Я полный новичок в Lua. Цель состоит в том, чтобы отобразить трехмерный тор в точках (см. Рисунок 1) или, по крайней мере, вычислить позиции, отдавая предпочтение ясности перед производительностью. Псевдокодовая ссылка.
Не стесняйтесь быть разборчивым, я бы хотел выучить правильные идиомы Lua. Например, мне было приятно обнаружить перегрузку оператора, но я не смог найти удобный способ привязать его к моему «типу матрицы» по умолчанию.
-- 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