Lazythunk

Generating Simple Voronoi Diagrams with Lua and Love2d

-- http://en.wikipedia.org/wiki/Manhattan_distance
local manhattan = function(a, b)
  return math.abs(a.x - b.x) + math.abs(a.y - b.y)
end

local voronoi = function(width, height, dst)
  local random_points = {}

  -- ideally a point per 25x25 pixels
  local count  = (width * height) / (25 * 25)

  -- generate random points
  for i=1,count do
    table.insert(random_points, {
      x = tonumber(math.random() * width),
      y = tonumber(math.random() * height),
      pixels = {}
    })
  end

  -- for every pixel available
  for x=1,width do
    for y=1,height do
      -- find the closest random point to this pixel
      local point = {x = x, y = y}
      local closest_point = random_points[1]

      for i=2,#random_points do
        if dst(point, closest_point) > dst(point, random_points[i]) then
          closest_point = random_points[i]
        end
      end

      table.insert(closest_point.pixels, point)
    end
  end

  return {
    width = width,
    height = height,
    points = random_points
  }
end

-- return a new rgb value with each call
local nextColor = (function()
  local i, colors = 0, {}
  for r=0,255,32 do
    for g=0,255,32 do table.insert(colors, {r, g, 82}) end
    for b=0,255,32 do table.insert(colors, {r, 82, b}) end
  end

  return function()
    i = i + 1
    if i > #colors then i = 1 end
    return unpack(colors[i])
  end
end)()

-- draw voronoi on a love2d canvas
local createCanvas = function(vrn)
  local canvas = love.graphics.newCanvas(vrn.width, vrn.height)
  love.graphics.setCanvas(canvas)
  canvas:clear(255, 255, 255, 255)

  for i,point in ipairs(vrn.points) do
    -- with a new drawing color
    love.graphics.setColor(nextColor())

    -- draw every nearby pixel
    -- love2d centers pixels, so offset them by 0.5 for crispness
    for i,pixel in ipairs(point.pixels) do
      love.graphics.point(pixel.x - 0.5, pixel.y - 0.5)
    end

    -- draw the center dot
    love.graphics.setColor(0, 0, 0)
    love.graphics.point(point.x - 0.5, point.y - 0.5)
  end

  love.graphics.setCanvas()
  love.graphics.setColor(255, 255, 255)

  return canvas
end

local canvas;

function love.load()
  -- reseed RNG
  math.randomseed(os.time())

  -- set a smaller window; drawing pixels 1-by-1 can be slow
  local width, height = 400, 400
  love.window.setMode(width, height)
  love.window.setTitle("Voronoi")

  vrn    = voronoi(width, height, manhattan)
  canvas = createCanvas(vrn)
end

function love.draw()
  love.graphics.draw(canvas, 0, 0)
end