Tabs -> spaces

Add box allocation
This commit is contained in:
Ekdohibs 2017-01-26 16:12:57 +01:00
parent 396886ad28
commit c4c203384c

View File

@ -171,7 +171,7 @@ function boxes.load(minp, compressed)
-- Read cid mapping -- Read cid mapping
local cid_mapping = {} local cid_mapping = {}
cid_index = read_u16() local cid_index = read_u16()
for i = 0, cid_index - 1 do for i = 0, cid_index - 1 do
cid_mapping[i] = minetest.get_content_id(read_string()) cid_mapping[i] = minetest.get_content_id(read_string())
end end
@ -220,6 +220,156 @@ function boxes.load(minp, compressed)
end end
--[[
Now for box allocation.
To keep things simple, we will always allocate boxes with same width and
depth, and with a sizee that is a multiple of boxes_resolution. Height is
assumed to be unlimited above box_alloc_miny.
In order to do that, we keep a quadtree of allocatable positions; an allocated
box will always be a leaf of that tree. We also keep for each subtree the max
size that can be allocated in it.
Thus, this is https://en.wikipedia.org/wiki/Buddy_memory_allocation in 2D.
]]
local boxes_resolution = 64
local box_alloc_miny = 50
local boxes_tree = {
minp = {x = -16384, z = -16384},
edge_size = 32768,
max_alloc_size = 32768,
}
local function split_leaf(node)
assert (node.edge_size >= 2 * boxes_resolution)
assert (node.children == nil)
local new_size = node.edge_size / 2
local x = node.minp.x
local z = node.minp.z
node.children = {
{
minp = {x = x, z = z},
edge_size = new_size,
max_alloc_size = new_size,
parent = node
},
{
minp = {x = x + new_size, z = z},
edge_size = new_size,
max_alloc_size = new_size,
parent = node,
},
{
minp = {x = x, z = z + new_size},
edge_size = new_size,
max_alloc_size = new_size,
parent = node,
},
{
minp = {x = x + new_size, z = z + new_size},
edge_size = new_size,
max_alloc_size = new_size,
parent = node,
},
}
end
-- Update `max_alloc_size` for node and all its parents.
-- Also, merge subtrees if possible.
local function update_alloc_sizes(node)
while node ~= nil do
local max_size = 0
local el = node.edge_size / 2
local can_merge = true
for _, child in ipairs(node.children) do
max_size = math.max(max_size, child.max_alloc_size)
if child.max_alloc_size < el then
can_merge = false
end
end
if can_merge then
node.children = nil
node.max_alloc_size = node.edge_size
else
node.max_alloc_size = max_size
end
node = node.parent
end
end
-- Function to know how close to zero a node is.
-- Used to keep allocations close to (0, box_alloc_miny, 0).
local function zero_close(node)
local x = node.minp.x
local z = node.minp.z
local size = node.edge_size
if x < 0 then x = x + size end
if z < 0 then z = z + size end
return math.abs(x) + math.abs(z)
end
-- Allocated a box of size `size` horizontally, and unbounded vertically
-- Returns box minimum position. The minimum position must be given to
-- boxes.vfree to free the corresponding area.
function boxes.valloc(size)
assert (size > 0)
if boxes_tree.max_alloc_size < size then
return nil
end
-- Find leaf with enough room, splitting it if big enough
local node = boxes_tree
while node.edge_size / 2 >= size and node.edge_size / 2 >= boxes_resolution do
if node.children == nil then
split_leaf(node)
end
local best = nil
local best_distance = 1e10 -- infinity
for _, child in ipairs(node.children) do
if child.max_alloc_size >= size and zero_close(child) < best_distance then
best_distance = zero_close(child)
best = child
end
end
assert (best ~= nil)
node = best
end
local result = {x = node.minp.x, y = box_alloc_miny, z = node.minp.z}
node.max_alloc_size = 0
update_alloc_sizes(node.parent)
return result
end
function boxes.vfree(minp)
assert (minp.y == box_alloc_miny)
assert (boxes_tree.minp.x <= minp.x)
assert (minp.x < boxes_tree.minp.x + boxes_tree.edge_size)
assert (boxes_tree.minp.z <= minp.z)
assert (minp.z < boxes_tree.minp.z + boxes_tree.edge_size)
-- Find leaf containing minp
local node = boxes_tree
while node.children ~= nil do
local cld = nil
local el = node.edge_size / 2
for _, child in ipairs(node.children) do
if child.minp.x <= minp.x and minp.x < child.minp.x + el
and child.minp.z <= minp.z and minp.z < child.minp.z + el then
cld = child
break
end
end
assert (cld ~= nil)
node = cld
end
assert (node.max_alloc_size == 0)
node.max_alloc_size = node.edge_size
update_alloc_sizes(node.parent)
end
-- test code -- test code
--[[ --[[
@ -228,5 +378,13 @@ minetest.after(5, function()
{x = 1, y = -2, z = 1}) {x = 1, y = -2, z = 1})
print(string.len(data)) print(string.len(data))
boxes.load({x = -10, y = 0, z = 5}, data) boxes.load({x = -10, y = 0, z = 5}, data)
print(dump(boxes_tree))
local minp = boxes.valloc(237)
print(dump(minp))
print(dump(boxes_tree))
boxes.vfree(minp)
print(dump(boxes_tree))
end) end)
]] ]]