-- voronoi diagram in regular grid
-- MEDIUM!
-- 1. create a grid of some seed_window_y and seed_window_x size
-- 2. randomize x,y position inside grid cell and get r,g,b of image
-- 3. calculate voronoi diagram and draw resultant image 
-- author: Siniša Petrić http://www.sigmapi-design.com, 2008-2014


-- init images/process objects 
imgA = TLuaImageWrap(inputImage)
imgB = TLuaImageWrap(outputImage)
proC = TLuaProcessWrap(procObj)


r_seed = {}
g_seed = {}              
b_seed = {}

x_seed =  {}
y_seed = {}

-- input variables
-- seed_dither values must be less or equal respective seed_window
-- seed shift x must be less then seed_window_x
-- example 1: square grid, no shift
seed_window_y, seed_window_x = 20,20
seed_dither_y, seed_dither_x = 20,20
seed_shift_x = 0  
seed_fall = 0      -- 0 no falloff
--[[
--example 2: rectangular "brick-style" layout   
seed_window_y, seed_window_x = 80, 30
seed_dither_y, seed_dither_x = 80, 30         
seed_shift_x = 15  
seed_fall = 1      -- falloff
--]]
-- generate random points inside image and get pixels rgb
function generate_seeds()
   local x, y, ii, jj, iWidthM1, iHeightM1
   iWidthM1, iHeightM1 = imgA:GetImageSize()
   iWidthM1 = iWidthM1 - 1
   iHeightM1 = iHeightM1 - 1
                           
   math.randomseed( os.time() )
   ii = 0
   for y = 0, iHeightM1, seed_window_y  do
       ii = ii + 1
       x_seed[ii] = {}      
       y_seed[ii] = {}        
       r_seed[ii] = {}      
       g_seed[ii] = {}        
       b_seed[ii] = {}      
       jj = 0           
       for x = 0, iWidthM1, seed_window_x do
           jj = jj + 1 
           y_seed[ii][jj] = y + seed_window_y/2 + (seed_dither_y/4 - math.random(seed_dither_y/2))
           x_seed[ii][jj] = x + seed_window_x/2 + (seed_dither_x/4 - math.random(seed_dither_x/2))           
           if y_seed[ii][jj] > iHeightM1 then
              y_seed[ii][jj] = iHeightM1
           end
           if x_seed[ii][jj] > iWidthM1 then
              x_seed[ii][jj] = iWidthM1
           end
           b_seed[ii][jj], g_seed[ii][jj], r_seed[ii][jj] = imgA:GetImageBGR(x_seed[ii][jj], y_seed[ii][jj])    
       end 
   end 
   return ii, jj
end

-- find closest seed
function find_closest(x, y)
    local dist, min_dist, iis, jjs
    local row, col
    local dX, dY
    local r_x, shift_x = 0, 0    
    min_dist = 999999.0
    row = math.floor(y / seed_window_y) + 1
    col = math.floor(x / seed_window_x) + 1
    iis = row
    jjs = col
    for ii = row-range_y, row+range_y do
        if (ii > 0 and ii <= max_i) then
            if (ii%2 == 0) then 
              shift_x = seed_shift_x
              r_x = 1
            else
              shift_x = 0
              r_x = 0
            end        
            for jj = col-range_x-r_x, col+range_x do
               if (jj > 0 and jj <= max_j) then
                   --dist = math.sqrt((x - (shift_x + x_seed[ii][jj]))^2+(y-y_seed[ii][jj])^2)
                   dX = x - shift_x - x_seed[ii][jj]
                   dY = y - y_seed[ii][jj]  
                   dist = dX*dX + dY*dY                   
                   if dist < min_dist then
                      min_dist = dist
                      iis = ii
                      jjs = jj 
                   end
               end
            end
        end  
    end       
    return min_dist, r_seed[iis][jjs], g_seed[iis][jjs], b_seed[iis][jjs]
end

-- and now the main routine
local dist = 0       
local eos = false
local x, y = 0, 0            
local iWidth, iHeight = imgA:GetImageSize()
local total = iHeight   
local done = 0
local seed_max_d, seed_max_d_quad    
-- check input parameters range
if (seed_dither_y > seed_window_y) then
   seed_dither_y = seed_widnow_y
end            
if (seed_dither_x > seed_window_x) then
   seed_dither_x = seed_widnow_x
end                               
if (seed_shift_x >= seed_window_x) then
   seed_shift_x = seed_window_x/2
end     
if (seed_fall == 0) then
   seed_max_d = 0
else
   seed_max_d = math.sqrt((seed_window_x/seed_fall)^2 +( seed_window_y/seed_fall)^2)    
end
seed_max_d_quad = seed_max_d * seed_max_d
range_x = math.floor(seed_window_y/(seed_window_x*2))
range_y = math.floor(seed_window_x/(seed_window_y*2))
if (range_x == 0) then
   range_x = 1
end
if (range_y == 0) then
   range_y = 1
end        
max_i, max_j = generate_seeds()                      
while (not eos) do       
   dist, r,g,b = find_closest(x,y)
   if seed_max_d > 0 then       
      dist = 1 - seed_fall * math.sqrt(dist)/seed_max_d      --linear
      --dist = 1 - seed_fall * dist/seed_max_d_quad          --quad 
      --dist = 1 - dist --negative
      if dist < 0 then
         dist = 0
      elseif dist > 1 then
         dist = 1
      end
      r = r*dist
      g = g*dist
      b = b*dist          
   end 
   imgB:SetScanBGR(b, g, r)
   eos, x, y = imgB:ScanNext()
   if (y > done) then
      proC:ShowProgress(y, total)
      done = y       
   end           
end 

               

