From 155ab6deb7fe77687f29ea9f976b1e4154301da3 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Sun, 7 Feb 2021 00:40:25 +0100 Subject: [PATCH] Add simple k-d-tree --- init.lua | 1 + kdtree.lua | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 kdtree.lua diff --git a/init.lua b/init.lua index 2e162d2..480ae8a 100644 --- a/init.lua +++ b/init.lua @@ -78,6 +78,7 @@ for _, component in ipairs{ "quaternion", "minetest", "trie", + "kdtree", "heap", "ranked_set", "b3d" diff --git a/kdtree.lua b/kdtree.lua new file mode 100644 index 0000000..8ea8e1c --- /dev/null +++ b/kdtree.lua @@ -0,0 +1,54 @@ +local metatable = {__index = getfenv(1)} + +distance = modlib.vector.distance + +--: vectors first vector is used to infer the dimension +--: distance (vector, other_vector) -> number, default: modlib.vector.distance +function new(vectors, distance) + assert(#vectors > 0, "vector list must not be empty") + local dimension = #vectors[1] + local function builder(vectors, axis) + if #vectors == 1 then return { value = vectors[1] } end + table.sort(vectors, function(a, b) return a[axis] > b[axis] end) + local median = math.floor(#vectors / 2) + local next_axis = ((axis + 1) % dimension) + 1 + return setmetatable({ + axis = axis, + pivot = vectors[median], + left = builder({ unpack(vectors, 1, median) }, next_axis), + right = builder({ unpack(vectors, median + 1) }, next_axis) + }, metatable) + end + local self = builder(vectors, 1) + self.distance = distance + return self +end + +function get_nearest_neighbor(self, vector) + local min_distance = math.huge + local nearest_neighbor + local distance_func = self.distance + local axis = tree.axis + local function visit(tree) + if tree.value ~= nil then + local distance = distance_func(tree.value, vector) + if distance < min_distance then + min_distance = distance + nearest_neighbor = tree.value + end + return + else + local this_side, other_side = tree.left, tree.right + if vector[axis] < tree.pivot[axis] then this_side, other_side = other_side, this_side end + visit(this_side) + if tree.pivot then + local dist = math.abs(tree.pivot[axis] - color[axis]) + if dist <= min_distance then visit(other_side) end + end + end + end + visit(self) + return nearest_neighbor, min_distance +end + +-- TODO insertion & deletion + rebalancing \ No newline at end of file