在Lua中的三维表格

我需要最佳的方法来存储三维像素表。我需要做的是有多个x,y表(基本上是三维的),以栅格化多个带透明度的二维像素图。你看,我可以轻松地创建两个维度,例如:

pixels = {{},{}}
pixels[1][5] = "green" --这只是一个例子
print(pixels[1][5])

然而,我不能像在Java中那样做...

pixels = {{}, {}, {}}
pixels[1][4][3] = "red" -- [x][y][z]
print(pixels[1][4][3])

这就是我想要的功能,但我已经通过以下方式恶心地绕过了这个问题...

pixels = {}
pixels["x23,y02,z05"] = "green"
print(pixels["x23,y02,z05"]")

我只是使用string.sub和string.concat来读取和设置表格...我真的希望例2的功能可以工作,但我知道可能需要以不同的方式实现。

点赞
用户1009479
用户1009479

在你的第一个代码中:

pixels = {{},{}}

等价于:

pixels = {}
pixels[1] = {}
pixels[2] = {}

这里,pixels[1]已经是一个表,这就是为什么你可以给pixels[1][5]赋值。

但是在你的第二个代码中:

pixels = {{}, {}, {}}

在这里,pixels仍然是一个二维数组(有3个元素)。它等价于:

pixels = {}
pixels[1] = {}
pixels[2] = {}
pixels[3] = {}

pixels[1]是一个表,但是pixels[1][4]不是。你需要做的是给pixels[1][4]一个表构造器,像这样:

pixels = {{}, {}, {}}
pixels[1][4] = {}  --将其初始化为空表
pixels[1][4][3] = "red"
print(pixels[1][4][3])
2014-01-20 08:34:53
用户3125367
用户3125367

除了经典的“数组中的数组中的数组”方案外,您还可以使用 Lua 表内部的优势。怎么做呢?Lua 表只是从键到值的映射,当您将其用作数组时,可以跳过一些键,这几乎不会花费任何代价。

t = { }
t[1] = "Hello"
t[500000] = "World" -- 不会分配额外的 499999 个元素

因此,如果您的数据是稀疏的(超过 50% 的三维点没有值),您可能会从中受益:

local n_x, n_y, n_z = 1920, 1080, 1000
local n_xy = n_x * n_y

function setValue(t, x, y, z, value)
    assert(x > 0 and x < n_x)
    assert(y > 0 and y < n_y)
    assert(z > 0 and z < n_z)

    t[((z-1) * n_xy) + ((y-1) * n_z) + x] = value
end

function getValue(t, x, y, z)
    assert(x > 0 and x < n_x)
    assert(y > 0 and y < n_y)
    assert(z > 0 and z < n_z)

    return t[((z-1) * n_xy) + ((y-1) * n_z) + x]
end

t = { }
setValue(t, 1, 1, 1, "red")
setValue(t, 1, 1, 2, "green")
2014-01-20 11:49:24
用户1244588
用户1244588

有两种基本方法可以处理这个问题:

自动创建表格

自动创建表通过使用元表透明地生成子表,创建完后你应该能够忘记它们存在。

function newAutotable(dim)
    local MT = {};
    for i=1, dim do
        MT[i] = {__index = function(t, k)
            if i < dim then
                t[k] = setmetatable({}, MT[i+1])
                return t[k];
            end
        end}
    end

    return setmetatable({}, MT[1]);
end

-- 用法
local at = newAutotable(3);
print(at[0]) -- 返回表
print(at[0][1]) -- 返回表
print(at[0][1][2]) -- 返回 nil
at[0][1][2] = 2;
print(at[0][1][2]) -- 返回值
print(at[0][1][3][3]) -- 出错,因为只设置了3个维度

它们的不足之处在于它们生成了_大量_的表格 -- 显然的。这会有一些内存开销,而且每个深度级别增加了执行时间。

它们的优点在于它们可以完全动态改变大小。你甚至可以使它们无限深。虽然在你的使用情况下,这很可能是不必要的,甚至是个坏主意。

这种结构非常适合非整数索引,你甚至可以使深度依赖于 "模板结构",从而实现一个透明的动态配置表,但我走了神.....

扁平化的数组

另一种变体是扁平化的数组。user3125367 已经谈到了它们,但我想扩展一下,因为它可以更方便地完成并解释一些东西。

通常在 CG 中将多维数组扁平化是一个好主意,因为这样你可以很容易地进行许多矩阵操作。计算修改后的索引在处理时间方面也是比较廉价的。但需要指出的是,虽然有些显然,但这种方法只适用于数值键和矩阵的预定义大小。

function newMdArray(X, Y, Z)
    local MT = { __call = function(t, x, y, z, v)
        if x>X or y>Y or z>Z or x<1 or y<1 or z<1 then return; end
        local k = x + X*(y-1) + X*Y*(z-1);
        if v ~= nil then t[k] = v; end
        return t[k];
    end };
    return setmetatable({}, MT);
end

-- 用法
local mdt = newMdArray(100, 100, 100);
local v = mdt(1, 2, 3);
mdt(1, 2, 3, v*.1);

这段代码摘自我另一个答案:dynamic tables or arrays

它可能可以进行一些优化(例如在闭包中计算 X*Y),但我想粘贴原始代码。无论如何,用这种方法可以通过使用正常的数组索引轻松地处理扁平化的结构:

for i=1, #mdt
    mdt[i] = (mdt[i] or 0)*.5
end

也可以直接访问3D索引:

mdt(12, 13, 14, 0)

你还可以很容易地修改函数,使它在缺少关键字时返回默认值,只需在元表中添加 __index 字段或以及将表保存为矩阵维度。

2014-01-22 15:51:27