Page 16 of 16

Re: Code Doodles!

Posted: Sun Jul 01, 2018 5:52 am
by PGUp
frac.png
frac.png (26.3 KiB) Viewed 6499 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

function love.load()
math.randomseed(os.time())
lg = love.graphics
lw = love.window
lw.setMode(500, 500)
poly = {}
for i=1, 3, 1 do
local p = {}
p.x = 250 - math.sin(math.rad(60) + i*math.rad(360)/3)*200
p.y = 250 + math.cos(math.rad(60) + i*math.rad(360)/3)*200
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

Re: Code Doodles!

Posted: Tue Oct 02, 2018 8:48 pm
by rmcode

Re: Code Doodles!

Posted: Thu Nov 08, 2018 5:53 pm
by veethree
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
1541699302.png (432.87 KiB) Viewed 5720 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)

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

			if add then
				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
		love.load()
	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

Re: Code Doodles!

Posted: Wed Dec 05, 2018 3:33 pm
by pgimeno
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

function love.load()
  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:
Image

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:
Image

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:
Image

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:

Image

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.

Re: Code Doodles!

Posted: Thu Jan 24, 2019 12:41 am
by veethree
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
(5.57 KiB) Downloaded 149 times
ga1.png
ga1.png (53.29 KiB) Viewed 4092 times

Re: Code Doodles!

Posted: Mon Jun 10, 2019 9:53 pm
by veethree
GOO SIMULATOR 2020

You need moonshine for it to work cause i'm bad at shaders.
1560203306.png
1560203306.png (10.56 KiB) Viewed 2109 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

	useShader = true

	threshold = love.graphics.newShader([[
			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("'1' to toggle shader")
	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()
	if useShader then
		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)
	if useShader then love.graphics.setShader(threshold) end
	love.graphics.draw(snow)
	love.graphics.setShader()

	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
		love.load()
	elseif key == "1" then
		useShader = not useShader
	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