Made this simple painting program for my 4-year old boy. He put aside Asphalt 8, and started playing with colors. Well, at least for an hour or so he did
Change the brush color by pressing and holding a colored paint button - it will gradually mix it with the current foreground color. The rectangular button on the right flips the foreground and background colors. The “Clear” button fills the canvas with current background color.
Nothing particularly special about this program, but i felt the need to share it because it is less than 250 lines of code. The power of expression and compactness of code in Codea is most impressive.
Gist
https://gist.github.com/anonymous/9d8ac43e687f8d162891
Or copy from here:
--# Button
Button = class()
function Button:init(label, x, y, w, h, fillColor)
self.label = label
self.x, self.y = x, y
self.w, self.h = w, h
self.fillColor = fillColor or color(149, 145, 156, 255)
local c = color(0, 0, 0, 255)
self.strokeColor = self.fillColor:mix(c, 0.3)
self._strokeColor = self.strokeColor
self._fillColor = self.fillColor
self._white = color(200, 200, 200, 255)
self._black = color(0, 0, 0, 255)
end
function Button:draw()
strokeWidth(1.5)
fill(self.fillColor)
stroke(self.strokeColor)
rectMode(CORNER)
rect(self.x, self.y, self.w, self.h)
textMode(CORNER)
font(self.font or "GillSans")
fill(self.pressed and self._white or self._black)
fontSize(30)
text(self.label, self.x+10, self.y+10)
end
function Button:touched(touch)
local x, y, w, h = self.x, self.y, self.w, self.h
local clicked = false
if (x <= touch.x and touch.x <= x+w) then
if (y <= touch.y and touch.y <= y+h) then
if touch.state == BEGAN then
self.pressed = true
local black = color(0, 0, 0, 255)
self.fillColor, self.strokeColor = self.strokeColor, self.fillColor
sound(DATA, "ZgJANwAiQHM6QEBAAAAAADQfND7Cvvs+fwBAf0BAQEA8QEBA")
elseif touch.state == ENDED and self.pressed then
clicked = true
end
end
end
if self.pressed and touch.state == ENDED then
-- reset state
self.pressed = false
self.strokeColor = self._strokeColor
self.fillColor = self._fillColor
end
if clicked then
self:clicked()
end
end
function Button:clicked()
-- redefine this in subclasses
end
--# Main
-- Painter
supportedOrientations(CurrentOrientation)
function setup()
displayMode(FULLSCREEN)
local cw, ch = WIDTH, HEIGHT*0.85
parameter.integer("brush_radius", 2, 50, 20)
canvas_bc = color(197, 197, 197, 255)
canvas_fc = color(50, 50, 50, 255)
colors = {}
colors.red = color(255, 0, 0, 255)
colors.green = color(2, 255, 0, 255)
colors.blue = color(9, 0, 255, 255)
colors.yellow = color(251, 255, 0, 255)
colors.white = color(255, 255, 255, 255)
colors.black = color(0, 0, 0, 255)
colors.orange = color(255, 151, 0, 255)
colors.violet = color(204, 0, 255, 255)
paints = {
{clr = colors.red},
{clr = colors.orange},
{clr = colors.yellow},
{clr = colors.green},
{clr = colors.blue},
{clr = colors.violet},
{clr = colors.black},
{clr = colors.white},
}
canvas = image(cw, ch)
setContext(canvas)
background(canvas_bc)
setContext()
lastX, lastY = nil, nil
clearButton = Button("Clear", WIDTH-100, HEIGHT-ch-65, 100, 60)
clearButton.clicked = function(self)
clearImage(canvas)
end
invertButton = Button("Invert", WIDTH-205, HEIGHT-ch-65, 100, 60)
invertButton.clicked = function(self)
canvas_bc, canvas_fc = canvas_fc, canvas_bc
end
invertButton.draw = function(self)
local x, y, w, h = self.x, self.y, self.w, self.h
pushStyle()
strokeWidth(3)
stroke(canvas_fc)
fill(canvas_bc)
smooth()
rect(x, y, w, h)
fill(canvas_fc)
noSmooth()
rect(x+14, y+14, w-28, h-28)
popStyle()
end
end
function drawPaint(b, x, y, w, h)
pushStyle()
strokeWidth(3)
stroke(117, 117, 117, 255)
fill(76, 76, 76, 255)
smooth()
rect(x, y, w, h)
fill(b.clr)
noSmooth()
noStroke()
local s = b.touched and 3 or 14
rect(x+s, y+s, w-s*2, h-s*2)
popStyle()
end
function touchPaint(p, t, touch)
if touch.state == BEGAN then
if inside(touch, t.x, t.y, t.w, t.h) then
sound(SOUND_HIT, 8783)
p.touched = true
end
elseif touch.state == ENDED then
p.touched = nil
end
end
function clearImage(img, c)
c = c or canvas_bc
setContext(img)
background(c)
setContext()
end
function draw()
background(0, 0, 0, 255)
blendMode(NORMAL)
local cw, ch = spriteSize(canvas)
sprite(canvas, cw/2, HEIGHT-ch/2)
invertButton:draw()
clearButton:draw()
local py, pw, ph = HEIGHT-ch-65, 60, 60
for i,p in ipairs(paints) do
local x = pw*(i-1)
drawPaint(p, x, py, pw, ph)
-- mix color
if p.touched then
canvas_fc = canvas_fc:mix(p.clr, 0.99)
end
end
end
function touched(touch)
local cw, ch = spriteSize(canvas)
-- first, handle button events
invertButton:touched(touch)
clearButton:touched(touch)
-- paints
local py, pw, ph = HEIGHT-ch-65, 60, 60
for i,p in ipairs(paints) do
local t = {x=pw*(i-1), y=py, w=pw, h=ph}
touchPaint(p, t, touch)
end
-- canvas drawing
canvasTouched(touch)
end
function canvasTouched(touch)
local cw, ch = spriteSize(canvas)
if inside(touch, 0, HEIGHT-ch, cw, ch) then
local cx, cy = touch.x, touch.y
if touch.state == MOVING then
moving = true
if lastX and lastY then
pushStyle()
setContext(canvas)
strokeWidth(brush_radius)
stroke(canvas_fc)
smooth()
noFill()
lineCapMode(ROUND)
line(cx, cy - HEIGHT + ch, lastX, lastY - HEIGHT + ch)
setContext()
popStyle()
end
setContext()
lastX, lastY = cx, cy
elseif touch.state == ENDED then
local wasMoving
wasMoving, moving = moving, nil
if not wasMoving then
setContext(canvas)
stroke(canvas_fc)
fill(canvas_fc)
ellipse(cx, cy - HEIGHT + ch, brush_radius)
setContext()
end
lastX, lastY = nil, nil
elseif touch.state == BEGAN then
lastX, lastY = cx, cy
end
end
end
function inside(touch, x, y, w, h)
return x <= touch.x and touch.x <= x + w
and y <= touch.y and touch.y <= y + h
end