Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Pixels: Corona SDK Pixel-Based Drawing POC
--------------------------------------------------------------------------------
--[[
Pixels
A simple POC of "pixel-based" drawing in Corona SDK. Implements a simple queue-based flood fill algorithm.
--]]
--------------------------------------------------------------------------------
display.setStatusBar(display.HiddenStatusBar)
--------------------------------------------------------------------------------
-- Localize
--------------------------------------------------------------------------------
local display_newRect = display.newRect
local math_ceil = math.ceil
local table_insert = table.insert
local table_remove = table.remove
--------------------------------------------------------------------------------
-- Variables
--------------------------------------------------------------------------------
local canvas = display.newGroup()
local pixels = {}
local pixelSize = 4
local halfPixelSize = pixelSize * 0.5
local penRadius = 3
local penPixelRadius = penRadius * pixelSize
local penColor = {r = 1, g = 0, b = 0}
local floodFillColor = {r = 0, g = 1, b = 1}
local prevEvent -- So that we can track the previous location of touch
--------------------------------------------------------------------------------
-- Helper Functions
--------------------------------------------------------------------------------
local distanceBetween = function(obj1, obj2) return ((obj2.x - obj1.x) ^ 2 + (obj2.y - obj1.y) ^ 2) ^ 0.5 end
local equalColors = function(t1, t2) return (t2.r == t1.r) and (t2.g == t1.g) and (t2.b == t1.b) end
local getGridXY = function(x, y) return math_ceil(x / pixelSize), math_ceil(y / pixelSize) end
--------------------------------------------------------------------------------
-- Make a Pixel
--------------------------------------------------------------------------------
local function makePixel(x, y)
local pixel = display_newRect(x * pixelSize - halfPixelSize, y * pixelSize - halfPixelSize, pixelSize, pixelSize)
pixel.pixelColor = {r = 1, g = 1, b = 1, a = 1}
pixel.gridX, pixel.gridY = x, y
------------------------------------------------------------------------------
-- Set Pixel Color
------------------------------------------------------------------------------
function pixel:setPixelColor(r, g, b, a)
pixel.pixelColor.r, pixel.pixelColor.g, pixel.pixelColor.b, pixel.pixelColor.a = r, g, b, a or 1
pixel:setFillColor(pixel.pixelColor.r, pixel.pixelColor.g, pixel.pixelColor.b, pixel.pixelColor.a)
end
pixel:setFillColor(pixel.pixelColor.r, pixel.pixelColor.g, pixel.pixelColor.b, pixel.pixelColor.a)
return pixel
end
--------------------------------------------------------------------------------
-- Get Pixels in Range
--------------------------------------------------------------------------------
local function getPixelsInRange(x, y, radius)
local p = {}
for xPos = x - radius, x + radius do
for yPos = y - radius, y + radius do
if pixels[xPos] and pixels[xPos][yPos] then
table_insert(p, pixels[xPos][yPos])
end
end
end
return p
end
--------------------------------------------------------------------------------
-- Color Dot
--------------------------------------------------------------------------------
local function colorDot(dotX, dotY)
local x, y = getGridXY(dotX, dotY)
local pixelsInRange = getPixelsInRange(x, y, penRadius)
for i = 1, #pixelsInRange do
local pixel = pixelsInRange[i]
local dist = distanceBetween(pixel, {x = dotX, y = dotY})
if dist <= penPixelRadius then
pixel:setPixelColor(penColor.r, penColor.g, penColor.b)
end
end
end
--------------------------------------------------------------------------------
-- Basic Flood Fill
--------------------------------------------------------------------------------
local function floodFill(x, y, targetColor, replacementColor)
if equalColors(targetColor, replacementColor) then return end
if pixels[x] and pixels[x][y] then
local queue = {}
local processed = {}
table_insert(queue, pixels[x][y])
while #queue > 0 do
local topNode = table_remove(queue, #queue)
if equalColors(topNode.pixelColor, targetColor) then
topNode:setPixelColor(replacementColor.r, replacementColor.g, replacementColor.b)
local gridX, gridY = topNode.gridX, topNode.gridY
processed[gridX] = processed[gridX] or {}
processed[gridX][gridY] = true
if pixels[gridX - 1] and pixels[gridX - 1][gridY] and (not processed[gridX - 1] or not processed[gridX - 1][gridY]) then
table_insert(queue, pixels[gridX - 1][gridY])
end
if pixels[gridX] and pixels[gridX][gridY - 1] and (not processed[gridX] or not processed[gridX][gridY - 1]) then
table_insert(queue, pixels[gridX][gridY - 1])
end
if pixels[gridX + 1] and pixels[gridX + 1][gridY] and (not processed[gridX + 1] or not processed[gridX + 1][gridY]) then
table_insert(queue, pixels[gridX + 1][gridY])
end
if pixels[gridX] and pixels[gridX][gridY + 1] and (not processed[gridX] or not processed[gridX][gridY + 1]) then
table_insert(queue, pixels[gridX][gridY + 1])
end
end
end
end
end
--------------------------------------------------------------------------------
-- Touch Listener
--------------------------------------------------------------------------------
local function onTouch(event)
if "began" == event.phase then
display.getCurrentStage():setFocus(canvas)
canvas.isFocus = true
prevEvent = event
penColor = {r = math.random(), g = math.random(), b = math.random()}
elseif canvas.isFocus then
if "moved" == event.phase then
local dist = distanceBetween(event, prevEvent) / pixelSize
local xAdd = (prevEvent.x - event.x) / dist
local yAdd = (prevEvent.y - event.y) / dist
for i = 1, dist do
colorDot(event.x + xAdd * i, event.y + yAdd * i)
end
prevEvent = event
elseif "ended" == event.phase or "cancelled" == event.phase then
end
end
end
--------------------------------------------------------------------------------
-- Tap Listener
--------------------------------------------------------------------------------
local function onTap(event)
if event.numTaps == 2 then
local x, y = getGridXY(event.x, event.y)
if pixels[x] and pixels[x][y] then
floodFillColor = {r = math.random(), g = math.random(), b = math.random()}
local pxColor = pixels[x][y].pixelColor
floodFill(x, y, {r = pxColor.r, g = pxColor.g, b = pxColor.b}, floodFillColor)
end
end
end
--------------------------------------------------------------------------------
-- Draw Pixels
--------------------------------------------------------------------------------
for x = 1, display.contentWidth / pixelSize do
pixels[x] = {}
for y = 1, display.contentHeight / pixelSize do
pixels[x][y] = makePixel(x, y)
canvas:insert(pixels[x][y])
end
end
Runtime:addEventListener("touch", onTouch)
Runtime:addEventListener("tap", onTap)
@mulawa1

This comment has been minimized.

Copy link

mulawa1 commented Nov 2, 2014

Great work - thank you!

@samwit

This comment has been minimized.

Copy link

samwit commented Nov 17, 2014

Very cool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.