mirror of
https://github.com/minetest-mods/MoreMesecons.git
synced 2025-01-08 17:00:23 +01:00
Add MapDataStorage code as replacement for get_data_from_pos etc.
In comparison to using Minetest's hash_node_position, this saves additional information about the z-only and zy-only components, which is what the vector_extras code did. In comparison to the old vector_extras code, this does not create lots of lua tables, which is slow, but instead uses one table as hashmap.
This commit is contained in:
parent
54030d1620
commit
9aa0e6ab3d
@ -52,9 +52,241 @@ function moremesecons.remove_data_from_pos(sto, pos)
|
||||
sto.storage:set_string(sto.name, minetest.serialize(sto.tab))
|
||||
end
|
||||
|
||||
-- Vector helpers
|
||||
-- All the following functions are from the vector_extras mod (https://github.com/HybridDog/vector_extras).
|
||||
-- If you enable that mod, its functions will be used instead of the ones defined below
|
||||
-- Some additional vector helpers
|
||||
|
||||
-- The same as minetest.hash_node_position; I copied it to ensure backwards
|
||||
-- compatibility and used hexadecimal number notation
|
||||
local function node_position_key(pos)
|
||||
return (pos.z + 0x8000) * 0x10000 * 0x10000
|
||||
+ (pos.y + 0x8000) * 0x10000
|
||||
+ pos.x + 0x8000
|
||||
end
|
||||
|
||||
local MapDataStorage = {}
|
||||
setmetatable(MapDataStorage, {__call = function()
|
||||
local obj = {}
|
||||
setmetatable(obj, MapDataStorage)
|
||||
return obj
|
||||
end})
|
||||
MapDataStorage.__index = {
|
||||
getAt = function(self, pos)
|
||||
return self[node_position_key(pos)]
|
||||
end,
|
||||
setAt = function(self, pos, data)
|
||||
-- If x, y or z is omitted, the key corresponds to a position outside
|
||||
-- of the map (hopefully), so it can be used to skip lines and planes
|
||||
local vi_z = (pos.z + 0x8000) * 0x10000 * 0x10000
|
||||
local vi_zy = vi_z + (pos.y + 0x8000) * 0x10000
|
||||
local vi = vi_zy + pos.x + 0x8000
|
||||
local is_new = self[vi] == nil
|
||||
self[vi] = data
|
||||
if is_new then
|
||||
self[vi_z] = (self[vi_z] or 0) + 1
|
||||
self[vi_zy] = (self[vi_zy] or 0) + 1
|
||||
end
|
||||
end,
|
||||
removeAt = function(self, pos)
|
||||
local vi_z = (pos.z + 0x8000) * 0x10000 * 0x10000
|
||||
local vi_zy = vi_z + (pos.y + 0x8000) * 0x10000
|
||||
local vi = vi_zy + pos.x + 0x8000
|
||||
if self[vi] == nil then
|
||||
-- Nothing to remove
|
||||
return
|
||||
end
|
||||
self[vi] = nil
|
||||
-- Update existence information for the xy plane and x line
|
||||
self[vi_z] = self[vi_z] - 1
|
||||
if self[vi_z] == 0 then
|
||||
self[vi_z] = nil
|
||||
self[vi_zy] = nil
|
||||
return
|
||||
end
|
||||
self[vi_zy] = self[vi_zy] - 1
|
||||
if self[vi_zy] == 0 then
|
||||
self[vi_zy] = nil
|
||||
end
|
||||
end,
|
||||
iter = function(self, pos1, pos2)
|
||||
local ystride = 0x10000
|
||||
local zstride = 0x10000 * 0x10000
|
||||
|
||||
-- Skip z values where no data can be found
|
||||
pos1 = vector.new(pos1)
|
||||
local vi_z = (pos1.z + 0x8000) * 0x10000 * 0x10000
|
||||
while not self[vi_z] do
|
||||
pos1.z = pos1.z + 1
|
||||
vi_z = vi_z + zstride
|
||||
if pos1.z > pos2.z then
|
||||
-- There are no values to iterate through
|
||||
return function() return end
|
||||
end
|
||||
end
|
||||
-- Skipping y values is not yet implemented and may require much code
|
||||
|
||||
local xrange = pos2.x - pos1.x + 1
|
||||
local yrange = pos2.y - pos1.y + 1
|
||||
local zrange = pos2.z - pos1.z + 1
|
||||
|
||||
-- x-only and y-only parts of the vector index of pos1
|
||||
local vi_y = (pos1.y + 0x8000) * 0x10000
|
||||
local vi_x = pos1.x + 0x8000
|
||||
|
||||
local y = 0
|
||||
local z = 0
|
||||
|
||||
local vi = node_position_key(pos1)
|
||||
local pos = vector.new(pos1)
|
||||
local nextaction = vi + xrange
|
||||
pos.x = pos.x - 1
|
||||
vi = vi - 1
|
||||
local function iterfunc()
|
||||
-- continue along x until it needs to jump
|
||||
vi = vi + 1
|
||||
pos.x = pos.x + 1
|
||||
if vi ~= nextaction then
|
||||
local v = self[vi]
|
||||
if v == nil then
|
||||
-- No data here
|
||||
return iterfunc()
|
||||
end
|
||||
-- The returned position must not be changed
|
||||
return pos, v
|
||||
end
|
||||
|
||||
-- Reset x position
|
||||
vi = vi - xrange
|
||||
-- Go along y until pos2.y is exceeded
|
||||
while true do
|
||||
y = y + 1
|
||||
pos.y = pos.y + 1
|
||||
-- Set vi to index(pos1.x, pos1.y + y, pos1.z + z)
|
||||
vi = vi + ystride
|
||||
if y == yrange then
|
||||
break
|
||||
end
|
||||
if self[vi - vi_x] then
|
||||
nextaction = vi + xrange
|
||||
|
||||
vi = vi - 1
|
||||
pos.x = pos1.x - 1
|
||||
return iterfunc()
|
||||
end
|
||||
-- Nothing along this x line, so increase y again
|
||||
end
|
||||
|
||||
-- Go back along y
|
||||
vi = vi - yrange * ystride
|
||||
y = 0
|
||||
pos.y = pos1.y
|
||||
-- Go along z until pos2.z is exceeded
|
||||
while true do
|
||||
z = z + 1
|
||||
pos.z = pos.z + 1
|
||||
vi = vi + zstride
|
||||
if z == zrange then
|
||||
-- Cuboid finished, return nil
|
||||
return
|
||||
end
|
||||
if self[vi - vi_x - vi_y] then
|
||||
y = 0
|
||||
nextaction = vi + xrange
|
||||
|
||||
vi = vi - 1
|
||||
pos.x = pos1.x - 1
|
||||
return iterfunc()
|
||||
end
|
||||
-- Nothing in this xy plane, so increase z again
|
||||
end
|
||||
end
|
||||
return iterfunc
|
||||
end,
|
||||
iterAll = function(self)
|
||||
local pairsfunc = pairs(self)
|
||||
local function iterfunc()
|
||||
local vi, v = pairsfunc()
|
||||
if not vi then
|
||||
return
|
||||
end
|
||||
local z = math.floor(vi / (0x10000 * 0x10000))
|
||||
vi = vi - z * 0x10000 * 0x10000
|
||||
local y = math.floor(vi / 0x10000)
|
||||
if y == 0 or z == 0 then
|
||||
-- The index does not refer to a position inside the map
|
||||
return iterfunc()
|
||||
end
|
||||
local x = vi - y * 0x10000 - 0x8000
|
||||
y = y - 0x8000
|
||||
z = z - 0x8000
|
||||
return {x=x, y=y, z=z}, v
|
||||
end
|
||||
end,
|
||||
}
|
||||
moremesecons.MapDataStorage = MapDataStorage
|
||||
|
||||
|
||||
-- This testing code shows an example usage of the MapDataStorage code
|
||||
local function do_test()
|
||||
print("Test if iter returns correct positions when a lot is set")
|
||||
local data = MapDataStorage()
|
||||
local k = 0
|
||||
for x = -5, 3 do
|
||||
for y = -5, 3 do
|
||||
for z = -5, 3 do
|
||||
k = k + 1
|
||||
data:setAt({x=x, y=y, z=z}, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
local expected_positions = {}
|
||||
for z = -4, 2 do
|
||||
for y = -4, 2 do
|
||||
for x = -4, 2 do
|
||||
expected_positions[#expected_positions+1] = {x=x, y=y, z=z}
|
||||
end
|
||||
end
|
||||
end
|
||||
local i = 0
|
||||
for pos, v in data:iter({x=-4, y=-4, z=-4}, {x=2, y=2, z=2}) do
|
||||
i = i + 1
|
||||
assert(vector.equals(pos, expected_positions[i]))
|
||||
end
|
||||
|
||||
print("Test if iter works correctly on a corner")
|
||||
local found = false
|
||||
for pos, v in data:iter({x=-8, y=-7, z=-80}, {x=-5, y=-5, z=-5}) do
|
||||
assert(not found)
|
||||
found = true
|
||||
assert(vector.equals(pos, {x=-5, y=-5, z=-5}))
|
||||
end
|
||||
assert(found)
|
||||
|
||||
print("Test if iter finds all corners")
|
||||
local expected_positions = {}
|
||||
local k = 1
|
||||
for _, z in ipairs({-9, -6}) do
|
||||
for _, y in ipairs({-9, -6}) do
|
||||
for _, x in ipairs({-8, -6}) do
|
||||
local pos = {x=x, y=y, z=z}
|
||||
expected_positions[#expected_positions+1] = pos
|
||||
data:setAt(pos, k)
|
||||
k = k + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
local i = 1
|
||||
for pos, v in data:iter({x=-8, y=-9, z=-9}, {x=-6, y=-6, z=-6}) do
|
||||
assert(v == i)
|
||||
assert(vector.equals(pos, expected_positions[i]))
|
||||
i = i + 1
|
||||
--~ print("found " .. minetest.pos_to_string(pos))
|
||||
end
|
||||
assert(i == 8 + 1, "Not enough or too many corners found")
|
||||
|
||||
--~ data:iterAll()
|
||||
end
|
||||
|
||||
do_test()
|
||||
|
||||
|
||||
if not vector.get_data_from_pos then
|
||||
function vector.get_data_from_pos(tab, z,y,x)
|
||||
|
Loading…
Reference in New Issue
Block a user