Code Doodles!

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Tst_
Prole
Posts: 7
Joined: Tue Dec 23, 2014 10:11 pm

Re: Code Doodles!

creeper9207 wrote:
Sun Mar 26, 2017 3:18 pm
I sadly don't have the code for this, as it was made by mistake when trying to draw a circle.

-snip-
I managed to do something like that once, I was making a radial menu and I found that if you drew polygons as lines instead of filled they do funky stuff.

Never really finished this project but I might do something out of the code I wrote for it some day.

grump
Party member
Posts: 614
Joined: Sat Jul 22, 2017 7:43 pm

Re: Code Doodles!

A simple, quick, and dirty implementation of a depth-search "genetic" algorithm that tries to recreate a reference image using random primitives (noise, lines, circles) and fitness testing.

The reference image I used for testing:

A series of progress images, taken every 250 generations:

3,000 generations took about 15 minutes on my laptop (i7-7700HQ, 2.8 GHz, GTX 1050, using a single core). The last image is made of 158 primitives.

Code: Select all

local rnd = love.math.random

local function randColor()
return {
math.floor(rnd(0, 256)),
math.floor(rnd(0, 256)),
math.floor(rnd(0, 256)),
math.floor(rnd(0, 256)),
}
end

local Line = function(w, h)
return {
x1 = rnd(0, w),
y1 = rnd(0, h),
x2 = rnd(0, w),
y2 = rnd(0, h),
color = randColor(),
w = rnd(1, 30),

draw = function(self)
love.graphics.setColor(self.color)
love.graphics.setLineWidth(self.w)
love.graphics.line(self.x1, self.y1, self.x2, self.y2)
end
}
end

local Circle = function(w, h)
return {
x = rnd(0, w),
y = rnd(0, h),
w = rnd(1, 30),
color = randColor(),
radius = rnd(0, .5 * math.min(w, h)),
filled = rnd() < .5,

draw = function(self)
love.graphics.setColor(self.color)
if not self.filled then
love.graphics.setLineWidth(self.w)
end
love.graphics.circle(self.filled and "fill" or "line", self.x, self.y, self.radius)
end
}
end

local noise = nil

local Noise = function(w, h)
if not noise then
local img = love.image.newImageData(w, h)
img:mapPixel(function(x, y, r, g, b, a)
local n = love.math.noise(x + rnd(), y + rnd())
return 255, 255, 255, 255 * n
end)
noise = love.graphics.newImage(img)
end

return {
x = rnd(0, w),
y = rnd(0, h),
angle = rnd(0, math.pi * 2),
sx = rnd(0.1, 10),
sy = rnd(0.1, 10),
color = randColor(),

draw = function(self)
love.graphics.setColor(self.color)
love.graphics.draw(noise, self.x, self.y, self.angle, self.sx, self.sy)
end
}
end

local shapes = { Noise, Line, Circle, }

local rimg = love.image.newImageData("reference.jpg")
love.window.setMode(rimg:getWidth(), rimg:getHeight() * 3)

local reference = love.graphics.newImage(rimg)
local state = { }
local canvas = love.graphics.newCanvas(reference:getWidth(), reference:getHeight())
local diff = love.graphics.newCanvas(reference:getWidth(), reference:getHeight())
local best = canvas:newImageData()
local bestImage = love.graphics.newImage(best)
local bestFitness = 1e9
local generation = 1
local nochange = 0

extern Image reference;

vec4 effect(vec4 color, Image texture, vec2 uv, vec2 fc) {
vec3 c = abs(Texel(texture, uv).rgb - Texel(reference, uv).rgb);
return vec4(c.rgb, 1.0);
}
]])

function fitness()
local img = diff:newImageData()
local result = 0
img:mapPixel(function(x, y, r, g, b, a)
result = result + r + g + b
return r, g, b, a
end)
return result, img
end

function mutateState()
local index = rnd(1, #state)
local rand = rnd()
if #state == 0 or nochange > 1000 or rand < 1 / #state then
nochange = 0
table.insert(state, rnd(1, #state), shapes[rnd(1, #shapes)](reference:getWidth(), reference:getHeight()))
elseif rand < .25 then
table.remove(state, index)
elseif rand < .5 then
local index2 = rnd(1, #state)
local temp = state[index]
state[index] = state[index2]
state[index2] = temp
else
state[index] = shapes[rnd(1, #shapes)](reference:getWidth(), reference:getHeight())
end
end

function copyState()
local r = {}
for i = 1, #state do
r[i] = state[i]
end
return r
end

function love.draw()
for loop = 1, 256 do
local old = copyState()
mutateState()

canvas:renderTo(function()
love.graphics.clear(0, 0, 0, 255)
for i = 1, #state do
state[i]:draw()
end
end)

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

diff:renderTo(function()
love.graphics.clear(0, 0, 0, 255)
love.graphics.draw(canvas)
end)

local fit, diffImage = fitness()
if fit < bestFitness then
best = canvas:newImageData()
bestImage = love.graphics.newImage(best)
bestFitness = fit
generation = generation + 1
nochange = 0
if generation % 250 == 0 then
bestImage:getData():encode("png", string.format("%03d.png", generation / 250))
end
break
else
nochange = nochange + 1
state = old
end
end

love.graphics.draw(reference)
love.graphics.draw(canvas, 0, reference:getHeight())
love.graphics.draw(bestImage, 0, reference:getHeight() * 2)
love.graphics.print(bestFitness, 0, 0)
love.graphics.print("Gen " .. generation .. " (" .. #state .. " primitives)", 0, reference:getHeight() * 2)
end

veethree
Inner party member
Posts: 820
Joined: Sat Dec 10, 2011 7:18 pm

Re: Code Doodles!

This is somewhat inspired by grump's thing, But significantly less interesting. It "evolves" the complete works of Shakespeare from a random string.

Code: Select all

function love.load()
goal = "The complete works of Shakespeare."
current = rndstring(#goal)
gen = 0
fit = fitness(current)
finished = false
run = false

local f = love.graphics.newFont(18)
love.graphics.setFont(f)
love.graphics.setBackgroundColor(220, 220, 220)

startTime = 0
endTime = 0
end

function fitness(str)
local f = 0
for i=0, #current do
local c = str:sub(i)
local g = goal:sub(i)
local d = dist(c, g)
f = f + d
end
return f
end

function rndstring(length)
local s = ""
local alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 '
for i=1, length do
local r = math.random(#alphabet)
s = s..alphabet:sub(r, r)
end
return s
end

function mutatestring(str)
local r = math.random(#str)
local s = str:sub(0, r-1)
local c = str:sub(r, r)
local e = str:sub(r+1)

local rd = math.random()

local new = c
if rd < 0.5 then
new = string.char(string.byte(c) - 1)
else
new = string.char(string.byte(c) + 1)
end

return s..new..e
end

function evolve()
gen = gen + 1

local n = mutatestring(current)
local f = fitness(n)
if f < fit then
current = n
fit = f
end

if fit == 0 then
finished = true
endTime = love.timer.getTime()
end
end

function love.update(dt)
if not finished and run then
evolve()
end
end

function love.draw()
if finished then
love.graphics.setColor(10, 200, 50)
else
love.graphics.setColor(255, 10, 10)
end
love.graphics.printf("CURRENT\n"..current.."\n\nGOAL\n"..goal.."\n\nGeneration: "..gen.. " | Fitness: "..fit, 0, 12, 800, "center")

if finished then
love.graphics.printf("Finished!\nTime: "..(math.floor( (endTime - startTime) * 1000) / 1000).."\nGenerations: "..gen.."\nPress r to reset", 0, 300, 800, "center")
end
end

function love.keypressed(key)
if key == "escape" then love.event.push("quit") end
if key == "space" then
run = not run
if run then
startTime = love.timer.getTime()
end
elseif key == "r" then
end
end

function dist(a, b)
return math.abs(string.byte(a) - string.byte(b))
end

jojomickymack
Prole
Posts: 45
Joined: Tue Dec 26, 2017 4:52 pm

Re: Code Doodles!

Rectangle mesh tilted back so it's like a chess board and made a waveform of the sketch shader from moonshine. This might be a good game ending animation?
explode.gif (430.94 KiB) Viewed 10243 times
Attachments
sketch002.love

SirRanjid
Prole
Posts: 37
Joined: Sun Nov 19, 2017 1:44 pm
Contact:

Re: Code Doodles!

Flow field with some random squre fish things.

You can drag the background with left mouse and apply some movement like scrolling with your smartphone.

flow_field.love

s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Code Doodles!

spiral around a 3d torus very simply projected to 2d and rendered as many circles:

(source)

This eventually led to an this 4d torus explorer program: https://github.com/s-ol/torus4d

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
baby:hurt(me)
end

SirRanjid
Prole
Posts: 37
Joined: Sun Nov 19, 2017 1:44 pm
Contact:

Re: Code Doodles!

Maybe not a doodle anymore but I've taken the fish to the next level.
Also cleaner and more commented code.

Attachments
particles_flowfield2.love

veethree
Inner party member
Posts: 820
Joined: Sat Dec 10, 2011 7:18 pm

Re: Code Doodles!

Why? Eye don't know.
eyedontknow.gif (769.9 KiB) Viewed 9708 times

Code: Select all

---EYE STUFF
eye = {}
local em = {__index = eye}

local function angle(x1, y1, x2, y2)
return math.atan2(y2-y1, x2-x1)
end

local function distance(x1, y1, x2, y2)
return ((x2-x1)^2+(y2-y1)^2)^0.5
end

local function normal(val, min, max)
return (val - min) / (max - min)
end

local e = {
x = x,
y = y,
dx = x,
dy = y,
ix = x,
iy = y,
dix = x,
diy = y,
irisColor = irisColor,
pupilDialation = pupilDialation,
}
return setmetatable(e, em)
end

function eye:update(dt)
self.dix = self.dix + (self.ix - self.dix) * 16 * dt
self.diy = self.diy + (self.iy - self.diy) * 16 * dt

end

end

function eye:draw()
love.graphics.setColor(1, 1, 1, 1)

love.graphics.setColor(self.irisColor)

love.graphics.setColor(0, 0, 0, 1)
love.graphics.circle("fill", self.dix, self.diy, self.irisRadius * self.pupilDialation)

end

function eye:look(x, y)
local a = angle(self.x, self.y, x, y)
local d = distance(self.x, self.y, x, y)

end

--Rest of the shit
love.window.setMode(800, 600, {msaa = 8} )
screen = {width = love.graphics.getWidth(), height = love.graphics.getHeight()}
math.randomseed(os.time() + love.mouse.getX())
love.graphics.setBackgroundColor(0.2, 0.2, 0.2)
love.mouse.setVisible(false)
space = 50
color = {math.random(), math.random(), math.random()}
e = {
eye.new( (screen.width / 2) - space, (screen.height / 2), radius, radius / 2, color, 0.4 ),
eye.new( (screen.width / 2) + space, (screen.height / 2), radius, radius / 2, color, 0.4 )
}
mx = 0
my = 0
h = 0

mood = "happy" --happy, concerned, sad

dist = 0
end

function love.update(dt)
h = h + 100 * dt
if h > 255 then h = 0 end

dist = distance(love.mouse.getX(), love.mouse.getY(), screen.width / 2, screen.height / 2)

if dist > radius * 8 then
mood = "happy"
elseif dist > radius * 4 then
mood = "concerned"
else
end

end

mx = mx + (love.mouse.getX() - mx) * 16 * dt
my = my + (love.mouse.getY() - my) * 16 * dt

for i,v in ipairs(e) do
v:update(dt)
v:look(love.mouse.getX(), love.mouse.getY())
end

end

function love.draw()
love.graphics.setColor(232 / 255, 209 / 255, 171 / 255)
love.graphics.circle("fill", screen.width / 2, screen.height / 2, radius * 4)

love.graphics.setColor(0, 0, 0)
if mood == "happy" then
love.graphics.arc("fill", screen.width / 2, screen.height * 0.58, radius * 1.5, 0, math.pi )
elseif mood == "concerned" then
love.graphics.arc("fill", screen.width / 2, screen.height * 0.65, radius * 1.5, math.pi, math.pi * 2)
end

for i,v in ipairs(e) do
v:draw()
end

--Eyelid
love.graphics.setColor(232 / 255, 209 / 255, 171 / 255)

love.graphics.setColor(hsl(h, 255, 126))
love.graphics.circle("fill", mx, my, 8)

end

function love.touchmoved( id, x, y, dx, dy, pressure )
for i,v in ipairs(e) do
v:update(dt)
v:look(x, y)
end
end

function love.keypressed(key)
if key == "escape" then love.event.push("quit") end
if key == "space" then love.load() end
if key == "f1" then
love.graphics.captureScreenshot(os.time()..".png")
end
if key == "b" then blink = 1 end
end

function hsl(h, s, l, a)
if s<=0 then return l,l,l,a end
h, s, l = h/256*6, s/255, l/255
local c = (1-math.abs(2*l-1))*s
local x = (1-math.abs(h%2-1))*c
local m,r,g,b = (l-.5*c), 0,0,0
if h < 1     then r,g,b = c,x,0
elseif h < 2 then r,g,b = x,c,0
elseif h < 3 then r,g,b = 0,c,x
elseif h < 4 then r,g,b = 0,x,c
elseif h < 5 then r,g,b = x,0,c
else              r,g,b = c,0,x
end return (r+m),(g+m),(b+m),a
end

Prole
Posts: 3
Joined: Tue Aug 22, 2017 6:07 pm

Re: Code Doodles!

Woah these are so cool!

SirRanjid
Prole
Posts: 37
Joined: Sun Nov 19, 2017 1:44 pm
Contact:

Re: Code Doodles!

First actual doodle. It's a bit psychedelic and plays seemlessly forever.

(No worries no fullscreen and console in case you can't close it directly for some reason like me. Maybe you could tell me what's wrong.)
DON'T disable the event module at least not in v0.10.2 .

Attachments
tri.love