Code Doodles!

General discussion about LÖVE, Lua, game development, puns, and unicorns.
PGUp
Citizen
Posts: 95
Joined: Fri Apr 21, 2017 9:17 am

Re: Code Doodles!

frac.png (26.3 KiB) Viewed 4514 times
generating fractal, one pixel at a time

Code: Select all

function getDist(x1, y1, x2, y2)
return math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2))
end

function getPos(x, y, rot, dist)
return x - math.sin(rot) * dist, y + math.cos(rot) * dist
end

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

math.randomseed(os.time())
lg = love.graphics
lw = love.window
lw.setMode(500, 500)
poly = {}
for i=1, 3, 1 do
local p = {}
table.insert(poly, p)
end
points = {}
local r1 = math.random()
local r2 = math.random()
local p = {}
p.x = (1 - math.sqrt(r1)) * poly[1].x + (math.sqrt(r1) * (1 - r2)) * poly[2].x + (math.sqrt(r1) * r2) * poly[3].x
p.y = (1 - math.sqrt(r1)) * poly[1].y + (math.sqrt(r1) * (1 - r2)) * poly[2].y + (math.sqrt(r1) * r2) * poly[3].y
table.insert(points, p)
--MAX POINTS
spawn = 10000
end

function love.update()
if spawn > 0 then
--MODIFY THIS FOR LOOP TO CHANGE THE SPEED OF THE GENERATOR
for i=1, 10, 1 do
local p = {}
local pick = math.random(1, 3)
local distance = getDist(points[#points].x, points[#points].y, poly[pick].x, poly[pick].y)
local angle = getAngle(points[#points].x, points[#points].y, poly[pick].x, poly[pick].y)
p.x, p.y = getPos(points[#points].x, points[#points].y, angle, distance/2)
table.insert(points, p)
spawn = spawn - 1
end
end
end

function love.draw()
for i,v in pairs(poly) do
lg.setColor(1,1,1)
lg.circle("fill", v.x, v.y, 5)
end
lg.setColor(1,0,0)
for i,v in pairs(points) do
lg.circle("fill", v.x, v.y, 1)
end
end

-

rmcode
Party member
Posts: 454
Joined: Tue Jul 15, 2014 12:04 pm
Location: Germany
Contact:

Re: Code Doodles!

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

Re: Code Doodles!

Wrote a function that extracts colors from an image. It does this in a very naive way so it only works good sometimes.
1541699302.png (432.87 KiB) Viewed 3735 times
The attachment 1541699312.png is no longer available
The attachment 1541699302.png is no longer available

Code: Select all

function love.load()
http = require("socket.http")
s = ""
love.window.setMode(640, 480)
love.graphics.setLineStyle("rough")

local img = getImage()
image = love.graphics.newImage(img)

color = scanImage(img)
sprint("Total pixels: "..(640 * 480))
sprint("Extracted colors: "..#color)
sprint("Hold space to show colors")
end

function sprint(t)
s = s..t.."\n"
end

function scanImage(data, skip, distance)
skip = skip or 10
distance = distance or 1

local colors = {}
for y=0, data:getHeight()-1, skip do
for x=0, data:getWidth()-1, skip do
local r, g, b, a = data:getPixel(x, y)

for i,v in ipairs(colors) do
if colorDistance(v, {r, g, b, a}) < distance then
end
end

colors[#colors + 1] = {r, g, b, a}
end
end
end

return colors
end

function love.update(dt)

end

function love.draw()
if image then love.graphics.draw(image) end

for i,v in ipairs(color) do
love.graphics.setColor(v)
love.graphics.rectangle("fill", (love.graphics.getWidth() / #color) * (i - 1), love.graphics.getHeight() - 64, love.graphics.getWidth() / #color, 64)
end

love.graphics.setColor(1, 1, 1)
love.graphics.print(s, 12, 12)
end

function love.keypressed(key)
if key == "escape" then love.event.push("quit") end
if key == "r" then
end

if key == "1" then
love.graphics.captureScreenshot(os.time()..".png")
end
end

function colorDistance(a, b)
return math.abs(a[1] - b[1]) + math.abs(a[2] - b[2]) + math.abs(a[3] - b[3])
end

function getImage()
local body, code = http.request("https://picsum.photos/640/480/?random")
local data = false
if body then
love.filesystem.write("tmp.jpg", body)
data = love.image.newImageData("tmp.jpg")
love.filesystem.remove("tmp.jpg")
else
error(code)
end

return data
end

pgimeno
Party member
Posts: 1633
Joined: Sun Oct 18, 2015 2:58 pm

Re: Code Doodles!

I made this to visualize the bias in formulas for picking random points from the surface of a unit sphere, using four different methods. It rotates the sphere (in orthographic projection) so it can be seen from several angles. It looked nice so I thought I'd add it to this thread too:

Code: Select all

local rng
local sin, cos, sqrt, pi = math.sin, math.cos, math.sqrt, math.pi

local tf = love.math.newTransform()
local tf2 = love.math.newTransform()
local pts = {}

local npts = 80000

local function randvec3d_1()
local x = rng:random() * 2 * pi
local y = rng:random() * 2 * pi
local z = sin(y)
return { x = cos(x) * z, y = sin(x) * z, z = cos(y) }
end

local function randvec3d_2()
local x = rng:random() * 2 - 1
local y = rng:random() * 2 - 1
local z = rng:random() * 2 - 1
local r = sqrt(x*x+y*y+z*z)
return {x = x/r, y = y/r, z = z/r}
end

local function randvec3d_3()
local x, y, z, r
repeat
x = rng:random() * 2 - 1
y = rng:random() * 2 - 1
z = rng:random() * 2 - 1
r = sqrt(x*x+y*y+z*z)
until r >= 0.0001 and r < 1
return {x = x/r, y = y/r, z = z/r}
end

local function randvec3d_4()
local z = rng:random()*2 - 1
local a = rng:random()*(2*pi)
local r = sqrt(1 - z*z)
return { x = r*cos(a), y = r*sin(a), z = z }
end

local randvec_3d = randvec3d_1

local function regen()
local len = 0
for i = 1, npts do
v = randvec_3d()
len = len + 3
pts[len - 2] = v.x
pts[len - 1] = v.y
pts[len] = v.z
end
end

rng = love.math.newRandomGenerator(love.timer.getTime()*1000000)
regen()
end

local pt2d = {}

function love.update(dt)
for i = 1, npts do
local x, y, z = pts[i*3 - 2], tf:transformPoint(pts[i*3 - 1], pts[i*3])
pt2d[i*2 - 1], pt2d[i*2] = tf2:transformPoint(x, y), z
end
tf:rotate(dt/pi)
tf2:rotate(dt/3)
end

function love.draw()
love.graphics.translate(400, 300)
love.graphics.scale(290, 290)
love.graphics.points(pt2d)
end

function love.keypressed(_, k)
if k == "escape" then return love.event.quit() end
if k == "1" then randvec_3d = randvec3d_1 regen() end
if k == "2" then randvec_3d = randvec3d_2 regen() end
if k == "3" then randvec_3d = randvec3d_3 regen() end
if k == "4" then randvec_3d = randvec3d_4 regen() end
end

The method is chosen with the numbers 1 to 4 in the regular keyboard.

Method 1 picks two random angles and uses them as the spherical coordinates of a point in the sphere. This results in a clear clustering near the poles:

Method 2 picks three random numbers in [-1, 1] and normalizes the resulting vector. This causes clustering in the projection of the edges and vertices of the cube that circumscribes the sphere:

Method 3 is like method 2, but if the length of the vector is > 1 or < 0.0001, it's rejected and another one is picked until one within range is found. The time to obtain a valid vector is unbound, but in practice it doesn't take too many iterations to get a valid one. This results in a good uniform distribution no matter the angle:

Method 4 picks a random point in the curved side of a cylinder with radius 1 that circumscribes the sphere, and then projects the cylinder on the sphere horizontally. This results in a uniform distribution too, essentially equal to method 3, so I haven't taken a snapshot for it.

We can modify the code to prevent projecting the cylinder, by forcing the radius to 1:

Code: Select all

--  local r = sqrt(1 - z*z)
local r = 1

and this is the result:

I chose the number of points (npts) so that they can be calculated in a single frame in my computer; you may need to adjust that depending on the required frame rate and how fast your computer is.
Attachments
random-vector-visualize.love

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

Re: Code Doodles!

This one isn't contained in main.lua, But i'd consider it a doodle. It's a bunch of randomly moving balls who evolve the ability to hit a target and avoid an obstacle via a genetic algorithm.

It's pretty heavily inspired by this tutorial.

The code is pretty haphazardly thrown together, But it works.
Genetic.love
ga1.png (53.29 KiB) Viewed 2107 times
Attachments
ga2.png (52.65 KiB) Viewed 2107 times

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

Re: Code Doodles!

GOO SIMULATOR 2020

You need moonshine for it to work cause i'm bad at shaders.
1560203306.png (10.56 KiB) Viewed 124 times

Code: Select all

function love.load()
s = ""
--love.graphics.setBackgroundColor(1, 1, 1)
love.physics.setMeter(40)
world = love.physics.newWorld(0, love.physics:getMeter() * 8, true)

moonshine = require 'moonshine'

blur = moonshine(moonshine.effects.gaussianblur)
blur.gaussianblur.sigma = 6

vec4 effect( vec4 color, Image tex, vec2 tc, vec2 sc )
{
vec4 pixel = Texel(tex, tc);
number avg = (pixel.r + pixel.g + pixel.b) / 3;
if (avg > 0.01) {
return vec4(1.0, 1.0, 1.0, 1.0) * color;
} else {
return pixel;
}
}
]])

balls = {}
count = 500
ballSize = 2
sizeVariation = 2

for i=1, count do
balls[i] = {}
balls[i].body = love.physics.newBody(world, love.graphics.getWidth() / 2 + math.random(), -(ballSize * 2) * i, "dynamic")
balls[i].body:setMass(0.1)
balls[i].size = ballSize + (sizeVariation * math.random())
balls[i].shape = love.physics.newCircleShape(balls[i].size)
balls[i].fixture = love.physics.newFixture(balls[i].body, balls[i].shape, 1)
balls[i].fixture:setRestitution(0.3)
balls[i].fixture:setFriction(0.1)
end

walls = {}
walls[1] = {}
walls[1].body = love.physics.newBody(world, love.graphics.getWidth() / 2, love.graphics.getHeight() - 5, "static")
walls[1].shape = love.physics.newRectangleShape(love.graphics.getWidth(), 10)
walls[1].fixture = love.physics.newFixture(walls[1].body, walls[1].shape, 1)

walls[2] = {}
walls[2].body = love.physics.newBody(world, 0, love.graphics.getHeight() / 2, "static")
walls[2].shape = love.physics.newRectangleShape(10, love.graphics.getWidth())
walls[2].fixture = love.physics.newFixture(walls[2].body, walls[2].shape, 1)

walls[3] = {}
walls[3].body = love.physics.newBody(world, love.graphics.getWidth() - 10, love.graphics.getHeight() / 2, "static")
walls[3].shape = love.physics.newRectangleShape(10, love.graphics.getWidth())
walls[3].fixture = love.physics.newFixture(walls[3].body, walls[3].shape, 1)

walls[4] = {}
walls[4].body = love.physics.newBody(world, love.graphics.getWidth() / 2, love.graphics.getHeight() / 2, "static")
walls[4].shape = love.physics.newRectangleShape(64, 16)
walls[4].fixture = love.physics.newFixture(walls[4].body, walls[4].shape, 1)

walls[5] = {}
walls[5].body = love.physics.newBody(world, love.graphics.getWidth() / 4, love.graphics.getHeight() / 1.5, "static")
walls[5].shape = love.physics.newRectangleShape(0, 0, 100, 8, math.pi / 4)
walls[5].fixture = love.physics.newFixture(walls[5].body, walls[5].shape, 1)

walls[6] = {}
walls[6].body = love.physics.newBody(world, love.graphics.getWidth() - (love.graphics.getWidth() / 4), love.graphics.getHeight() / 1.5, "static")
walls[6].shape = love.physics.newRectangleShape(0, 0, 100, 8, -(math.pi / 4))
walls[6].fixture = love.physics.newFixture(walls[6].body, walls[6].shape, 1)

player = {}
player.body = love.physics.newBody(world, love.graphics.getWidth() / 2, love.graphics.getHeight() / 2, "dynamic")
player.size = ballSize * 16
player.body:setMass(player.size * 8)
player.speed = 1000
player.maxVel = 200
player.jump = 30
player.shape = love.physics.newCircleShape(player.size)
player.fixture = love.physics.newFixture(player.body, player.shape, 1)
player.fixture:setRestitution(0.3 * math.random() + 0.2)

snow = love.graphics.newCanvas()

sprint("'SPACE' to reset")
sprint("'R' to make shit fly")
end

function sprint(t)
s = s..t.."\n"
end

function love.update(dt)
local mx, my = love.mouse.getPosition()

local xVel, yVel = player.body:getLinearVelocity()
local nx, ny = xVel, yVel
if love.keyboard.isDown("d") then
nx = nx + player.speed * dt
if nx > player.maxVel then
nx = player.maxVel
end
elseif love.keyboard.isDown("a") then
nx = nx - player.speed * dt
if nx < -player.maxVel then
nx = -player.maxVel
end
end

player.body:setLinearVelocity(nx, ny)

world:update(dt)
end

function love.draw()
love.graphics.setCanvas(snow)
love.graphics.clear()
blur(function()
love.graphics.setColor(1, 1, 1, 1)
for i,v in ipairs(balls) do
love.graphics.circle("fill", v.body:getX(), v.body:getY(), v.size)
end
end)
else
love.graphics.setColor(1, 1, 1, 1)
for i,v in ipairs(balls) do
love.graphics.circle("fill", v.body:getX(), v.body:getY(), v.size)
end
end
love.graphics.setCanvas()

love.graphics.setColor(0.2, 0.8, 0.4, 1)
love.graphics.draw(snow)

love.graphics.setColor(1, 1, 1, 1)
for i,v in ipairs(walls) do
love.graphics.polygon("fill", v.body:getWorldPoints(v.shape:getPoints()))
end

love.graphics.setColor(1, 0.6, 0.4, 1)
love.graphics.circle("fill", player.body:getX(), player.body:getY(), player.size)

love.graphics.setColor(1, 1, 1, 1)
love.graphics.printf(s, 12, 12, love.graphics.getWidth(), "left")
love.graphics.printf(love.timer.getFPS(), 0, 12, love.graphics.getWidth(), "center")
end

function love.keypressed(key)
if key == "escape" then
love.event.push("quit")
elseif key == "space" then
elseif key == "1" then
elseif key == "2" then
love.graphics.captureScreenshot(os.time()..".png")
--sprint("Screenshot captured.")
end

if key == "w" then
player.body:applyLinearImpulse(0, -(player.size * 18) )
end

if key == "r" then
for i,v in ipairs(balls) do
v.body:applyLinearImpulse(0, -10 * math.random())
end
end
end
Attachments
1560203302.png (12.35 KiB) Viewed 124 times

Who is online

Users browsing this forum: No registered users and 8 guests