Last active
August 29, 2015 14:14
-
-
Save nadar71/a7e83298425630199c29 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-------------------------------------------------------------------------------- | |
--[[ | |
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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment