2019-10-31 14:01:53 -07:00
local max_linear = 10 -- Maximum number of colors for using linear search
-- Finds the closest color using a binary search like thing - point cloud is split into two (ideally equally sized) clouds multiple times which gives a searchable tree
-- Builds a k-d tree for 3d points
function kd_tree_builder ( dim )
return function ( points , axis )
if # points == 1 then
return points [ 1 ]
end
axis = ( axis or 0 ) + 1
table.sort ( points , function ( a , b ) return a [ axis ] > b [ axis ] end )
local median = math.floor ( # points / 2 )
local next_axis = ( axis + 1 ) % dim
return {
axis = axis ,
pivot = points [ median ] ,
left = build_kd_tree ( { unpack ( points , 1 , median ) } , next_axis ) ,
right = build_kd_tree ( { unpack ( points , median + 1 ) } , next_axis )
}
end
end
build_kd_tree = kd_tree_builder ( 3 )
distance = function ( c1 , c2 ) return math.sqrt ( math.pow ( ( c1 [ 1 ] - c2 [ 1 ] ) , 2 ) + math.pow ( ( c1 [ 2 ] - c2 [ 2 ] ) , 2 ) + math.pow ( ( c1 [ 3 ] - c2 [ 3 ] ) , 2 ) ) end
-- Returns a function which gives you the closest color, based on k-d trees
function kd_closest_color_finder ( colors )
local tree = build_kd_tree ( colors )
return function ( color )
local min_distance = math.huge
local closest_color
f = function ( tree )
local axis = tree.axis
if # tree > 0 then -- Subtree is leaf
local distance = distance ( tree , color )
if distance < min_distance then
min_distance = distance
closest_color = tree
end
return
else
local new_tree , other_tree = tree.right , tree.left
if color [ axis ] < tree.pivot [ axis ] then
new_tree , other_tree = tree.left , tree.right
end
f ( other_tree )
if tree.pivot then
local dist = math.abs ( tree.pivot [ axis ] - color [ axis ] )
if dist <= min_distance then
f ( new_tree )
end
end
end
end
f ( tree )
return closest_color , min_distance
end
end
-- returns a function which returns closest color, based on linear search
function linear_closest_color_finder ( colors )
return function ( c1 )
local min = math.huge
local clos
for _ , c2 in pairs ( colors ) do
local dis = distance ( c1 , c2 )
if dis < min then
min = dis
clos = c2
end
end
return clos , min
end
end
-- returns a function for finding the closest color, based on length of colors uses either k-d tree or linear search
function closest_color_finder ( colors )
if # colors <= max_linear then
return linear_closest_color_finder ( colors )
end
return kd_closest_color_finder ( colors )
end