Share your Utils

General discussion about LÖVE, Lua, game development, puns, and unicorns.
User avatar
tentus
Inner party member
Posts: 1060
Joined: Sun Oct 31, 2010 7:56 pm
Location: Appalachia
Contact:

Share your Utils

Post by tentus »

Simple idea: share little utility functions that you find helpful. For example, below is a function which takes a KeyConstant and returns its proper name, or failing that, returns it in proper case. It's useful for printing keys in a more legible manner.

Code: Select all

function keyConstToWord(k)
	if k == " " then
		return "Space"
	elseif k == "!" then
		return "Exclamation	Mark"
	elseif k == "\"" then
		return "Double Quote"
	elseif k == "#" then
		return "Hash"
	elseif k == "$" then
		return "Dollar Sign"
	elseif k == "&" then
		return "Ampersand"
	elseif k == "'" then
		return "Single Quote"
	elseif k == "(" then
		return "Left Parenthesis"
	elseif k == ")" then
		return "Right Parenthesis"
	elseif k == "*" then
		return "Asterisk"
	elseif k == "+" then
		return "Plus"
	elseif k == "," then
		return "Comma"
	elseif k == "-" then
		return "Hyphen"
	elseif k == "." then
		return "Period"
	elseif k == "/" then
		return "Slash"
	elseif k == ":" then
		return "Colon"
	elseif k == ";" then
		return "Semicolon"
	elseif k == "<" then
		return "Less-than"
	elseif k == "=" then
		return "Equal"
	elseif k == ">" then
		return "Greater-than"
	elseif k == "?" then
		return "Question Mark"
	elseif k == "@" then
		return "At Sign"
	elseif k == "[" then
		return "Left Bracket"
	elseif k == "\\" then
		return "Backslash"
	elseif k == "]" then
		return "Right Bracket"
	elseif k == "^" then
		return "Caret"
	elseif k == "_" then
		return "Underscore"
	elseif k == "`" then
		return "Grave Accent"
	elseif k == "rshift" then
		return "Right Shift"
	elseif k == "lshift" then
		return "Left Shift"
	elseif k == "rctrl" then
		return "Right Control"
	elseif k == "lctrl" then
		return "Left Control"
	elseif k == "ralt" then
		return "Right Alt"
	elseif k == "lalt" then
		return "Left Alt"
	elseif k == "rmeta" then
		return "Right Meta"
	elseif k == "lmeta" then
		return "Left Meta"
	elseif k == "lsuper" then
		return "Left Super"
	elseif k == "rsuper" then
		return "Right Super"
	else
		return string.upper(string.sub(k, 1, 1)) .. string.sub(k, 2)
	end
end
What functions do you find yourself using everywhere?
Kurosuke needs beta testers
User avatar
Taehl
Dreaming in associative arrays
Posts: 1025
Joined: Mon Jan 11, 2010 5:07 am
Location: CA, USA
Contact:

Re: Share your Utils

Post by Taehl »

I keep a file handy with a list of such functions. Here it is. If you use any of these in a project, please give me credit in a readme or something. If you want to use one of them but can't figure it out, just ask and I can explain it better (the comments are mainly a reminder to myself).

Code: Select all

----------------------------------------------------------------
----------------------------- Math -----------------------------
----------------------------------------------------------------

-- Averages angles (properly).
function math.averageAngles(...)
	local x,y = 0,0
	for i=1,select('#',...) do local a= select(i,...) x, y = x+math.cos(a), y+math.sin(a) end
	return math.atan2(y, x)
end


-- Returns the distance between two points.
function math.dist(x1,y1, x2,y2) return ((x2-x1)^2+(y2-y1)^2)^0.5 end
-- Optional 3D capacity:
function math.dist(x1,y1,z1, x2,y2,z2) return ((x2-x1)^2+(y2-y1)^2+((z2 or 0) - (z1 or 0))^2)^0.5 end


-- Returns the angle between two points.
function math.getAngle(x1,y1, x2,y2) return math.atan2(y2-y1, x2-x1) end


-- Returns the closest multiple of 'size' (defaulting to 10).
function math.multiple(n, size) size = size or 10 return math.round(n/size)*size end


-- Clamps a number to within a certain range.
function math.clamp(low, n, high) return math.min(math.max(n, low), high) end


-- Linear interpolation between two numbers
function lerp(a,b,t) return a+(b-a)*t end
-- Cosine interpolation between two numbers
function cerp(a,b,t) local f=(1-math.cos(t*math.pi))*.5 return a*(1-f)+b*f end


-- Normalizes a table of numbers.
function math.normalize(t) local n,m = #t,0 for i=1,n do m=m+t[i] end m=1/m for i=1,n do t[i]=t[i]*m end return t end
-- Normalizes two numbers.
function math.normalize(x,y) local l=(x*x+y*y)^.5 if l==0 then return 0,0,0 else return x/l,y/l,l end end


-- Returns 'n' rounded to the nearest 'deci'th.
function math.round(n, deci) deci = 10^(deci or 0) return math.floor(n*deci+.5)/deci end


-- Randomly returns either -1 or 1.
function math.rsign() return math.random(2) == 2 and 1 or -1 end


-- Returns 1 if number is positive, -1 if it's negative, or 0 if it's 0.
function math.sign(n) return n>0 and 1 or n<0 and -1 or 0 end


-- Checks if two lines intersect (or line segments if seg is true)
function findIntersect(l1p1x,l1p1y, l1p2x,l1p2y, l2p1x,l2p1y, l2p2x,l2p2y, seg1, seg2)
	local a1,b1,a2,b2 = l1p2y-l1p1y, l1p1x-l1p2x, l2p2y-l2p1y, l2p1x-l2p2x
	local c1,c2 = a1*l1p1x+b1*l1p1y, a2*l2p1x+b2*l2p1y
	local det,x,y = a1*b2 - a2*b1
	if det==0 then return false, "The lines are parallel." end
	x,y = (b2*c1-b1*c2)/det, (a1*c2-a2*c1)/det
	if seg1 or seg2 then
		local min,max = math.min, math.max
		if seg1 and not (min(l1p1x,l1p2x) <= x and x <= max(l1p1x,l1p2x) and min(l1p1y,l1p2y) <= y and y <= max(l1p1y,l1p2y)) or
		   seg2 and not (min(l2p1x,l2p2x) <= x and x <= max(l2p1x,l2p2x) and min(l2p1y,l2p2y) <= y and y <= max(l2p1y,l2p2y)) then
			return false, "The lines don't intersect."
		end
	end
	return x,y
end


-- Turns eight true/false values (bits) into a character (byte)
function formByte(a,b,c,d,e,f,g,h) return string.char((a and 128 or 0)+(b and 64 or 0)+(c and 32 or 0)+(d and 16 or 0)+(e and 8 or 0)+(f and 4 or 0)+(g and 2 or 0)+(h and 1 or 0)) end


-- Splits a character (byte) into eight true/false values (bits)
function splitByte(input) local b,p,f = string.byte(input),128,{false,false,false,false,false,false,false,false}
for i=1,8 do if b>=p then f[i],b = true,b-p end p=p*.5 end return f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8] end



----------------------------------------------------------------
--------------------------- Strings ----------------------------
----------------------------------------------------------------

-- Correct the print function to wrap text correctly to a given width (defaulting to 80).
oldprint = print
function print(s, n)
	n = (n or 80) - 1
	repeat
		line = string.sub(s, 1, n)		-- Get one console-width of the string
		local ended = false
		if string.find(line, " ") and string.len(line) >= n then
			while not ended do			-- Find the last " " in it
				local length = string.len(line)
				if string.sub(line, length, length) ~= " " then line = string.sub(line, 1, length-1)
				else ended = true end
			end
		end
		s = string.sub(s, string.len(line)+1, string.len(s))	-- Remove the single line from the string
		local length = string.len(line) length length length
		if string.sub(line, length, length) == " " then line = string.sub(line, 1, length-1) end
		oldprint(" "..line)
	until string.len(s) < 1
end


-- Turns a table into an English-correct comma-delimited list string.
function listTable(t, combiner, keys)
	local l, e, current, last, combiner = "", "", 0, #t, combiner or "and"
	combiner = combiner == "" or combiner.." "
	for k, v in pairs(t) do
		current = current + 1
		if keys then e = k else e = v end
		if last == 1 then l = e
		elseif current == last then l = l..combiner..e
		elseif last < 3 and combiner ~= "" then l = e.." "
		else l = l..e..", "
		end
	end
	return l
end


-- Capitalize a string.
function string.capitalize(s) if string.len(s)<2 then return string.upper(s) end return string.upper(string.sub(s, 1, 1))..string.sub(s,1-string.len(s)) end



----------------------------------------------------------------
---------------------------- Tables ----------------------------
----------------------------------------------------------------

-- Like ipairs(t), but lets you specify starting position.
function iipairs(t,i) local function iter(t,i) i=i+1 if t[i] then return i, t[i] end end return iter,t,(i or 1)-1 end


-- An ordered version of pairs().
function kpairs(t, f)
	local a, i = {}, 0 for n in pairs(t) do table.insert(a, n) end table.sort(a, f)
	return function() i = i+1 if a[i] == nil then return nil else return a[i], t[a[i]] end end
end


-- Combines an arbitrary number of tables. Tables are not cross-contaminated in this process. Can't handle circular references.
function table.combine(replace, ...) local nt = {} for i=1, select('#', ...) do local t = select(i, ...) for k, v in pairs(t) do
	if replace then if type(v) == "table" then nt[k] = table.combine(replace, v) else nt[k] = v end
	else if type(v) == "table" then table.insert(nt, table.combine(true, v)) else table.insert(nt, v) end
end end end return nt end


-- Create a new copy a table. Can't handle circular references.
function table.copy(t) local t2={} for k,v in pairs(t) do if type(v)=="table" then t2[k]=table.copy(v) else t2[k]=v end end return t2 end


-- Find the key for a given value in a table.
function table.getkey(t,value) for k,v in pairs(t) do if v==value then return k end end end


-- Returns the nth key of a table.
function table.nkey(t,n) local a={} for k in pairs(t) do if type(k)=="number" then table.insert(a, k) end end table.sort(a) return a[n] end


-- Turns a table into a printable string.
function table.tostring(t,n) local s,n = "", (n or -1)+1 for k,v in pairs(t) do for i=1,n do s=s.."  " end
s=s..tostring(k).." = "..(type(v)=="table" and "\n"..table.tostring(v,n) or tostring(v)).."\n" end return s end



----------------------------------------------------------------
---------------------------- Other -----------------------------
----------------------------------------------------------------

-- Time how fast a function is
function time(n,f,...) local t1=os.clock() for i=1,n do f(...) end return(os.clock()-t1) end


-- Converts HSL to RGB. (input and output range: 0 - 255)
function HSL(h, s, l)
	if s <= 0 then return l,l,l 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)*255,(g+m)*255,(b+m)*255
end


-- ASCII Vigenere cypher
function vigenere(key, data, decrypt)
   local result, kl, method = "", string.len(key), decrypt and -1 or 1
   local char, byte = string.char, string.byte
   for i = 1,string.len(data) do
      local ki = (i-1) % kl + 1
      result = result .. char( (byte(data, i,i) + byte(key, ki,ki)*method) % 256 )
   end
   return result
end


-- Bresenham's line algorithm (slower than the following function)
function Bline(x0,y0, x1,y1, f)
	local dx,dy = math.abs(x1-x0), math.abs(y1-y0)
	local sx,sy = x0<x1 and 1 or -1, y0<y1 and 1 or -1
	local err = dx-dy
	while true do
		f(x0,y0)
		if x0 == x1 and y0 == y1 then break end
		local e2 = err*2
		if e2 > -dy then err,x0 = err-dy, x0+sx end
		if e2 < dx then err,y0 = err+dx, y0+sy end
	end
end


-- Iterate function f over all coordinates from one point to another
function line(x0,y0, x1,y1, f)
	local t = math.abs(y1-y0) > math.abs(x1-x0)
	if t then x0,y0, x1,y1 = y0,x0, y1,x1 end
	if x0>x1 then x0,x1, y0,y1 = x1,x0, y1,y0 end
	local dx,dy = x1-x0, math.abs(y1-y0)
	local err,y,ystep = dx*.5, y0, y0<y1 and 1 or -1
	for x=x0,x1 do if t then f(y,x) else f(x,y) end err=err-dy if err<0 then y,err=y+ystep,err+dx end end
end


-- Trigger some Instant Elevator Music (but pops up a command window...)
os.execute[[""%ProgramFiles%\Instant Elevator Music\instantelevatormusic.exe"" -play app_name]]
EDIT) Updated with the most recent version of my list.
Last edited by Taehl on Wed Oct 05, 2011 4:07 pm, edited 1 time in total.
Earliest Love2D supporter who can't Love anymore. Let me disable pixel shaders if I don't use them, dammit!
Lenovo Thinkpad X60 Tablet, built like a tank. But not fancy enough for Love2D 0.10.0+.
User avatar
slime
Solid Snayke
Posts: 3144
Joined: Mon Aug 23, 2010 6:45 am
Location: Nova Scotia, Canada
Contact:

Re: Share your Utils

Post by slime »

I rewrote your KeyConstant function using a table instead of elseif's because I felt like it <.< >.>

Code: Select all

local constwords = {
	[" "] = "Space",
	["!"] = "Exclamation Mark",
	["\""] = "Double Quote",
	["#"] = "Hash",
	["$"] = "Dollar Sign",
	["&"] = "ampersand",
	["'"] = "Single Quote",
	["("] = "Left Parenthesis",
	[")"] = "Right Parenthesis",
	["*"] = "Asterisk",
	["+"] = "Plus",
	[","] = "Comma",
	["-"] = "Hyphen",
	["."] = "Period",
	["/"] = "Slash",
	[":"] = "Colon",
	[";"] = "Semicolon",
	["<"] = "Less-than",
	["="] = "Equal",
	[">"] = "Greater-than",
	["?"] = "Question Mark",
	["@"] = "At Sign",
	["["] = " Left Bracket",
	["\\"] = "Backslash",
	["]"] = "Right Bracket",
	["^"] = "Caret",
	["_"] = "Underscore",
	["`"] = "Grave Accent",
	["rshift"] = "Right Shift",
	["lshift"] = "Left Shift",
	["rctrl"] = "Right Control",
	["lctrl"] = "Left Control",
	["ralt"] = "Right Alt",
	["lalt"] = "Left Alt",
	["rmeta"] = "Right Meta",
	["lmeta"] = "Left Meta",
	["rsuper"] = "Right Super",
	["lsuper"] = "Left Super",
}

function KeyConstToWord(k)
	if k and type(k) == "string" and #k > 0 then
		return constwords[k] or k:sub(1,1):upper()..k:sub(2)
	end
end
User avatar
tentus
Inner party member
Posts: 1060
Joined: Sun Oct 31, 2010 7:56 pm
Location: Appalachia
Contact:

Re: Share your Utils

Post by tentus »

Clever, I like your way much more than mine. Easier to read, that's for sure.

Here's one that ghostwriter coined that I really like:

Code: Select all

-- negates the force of gravity on a given box2d physics object
function negateGravity(body)
	local mass = body:getMass()
	local gravX, gravY = world:getGravity()
	local meter = world:getMeter()
	body:applyForce(-gravX * mass / meter, -gravY * mass / meter)
end
Kurosuke needs beta testers
User avatar
BlackBulletIV
Inner party member
Posts: 1261
Joined: Wed Dec 29, 2010 8:19 pm
Location: Queensland, Australia
Contact:

Re: Share your Utils

Post by BlackBulletIV »

I guess I'll throw in my gravity well code wrapped in a function (maths taken from Flint Particles):

Code: Select all

--[[
    body is a physics body.
    x/y is the position of the well.
    power is the power you want applied. Something in the hundreds is good.
    epsilon is the minimum distance for which gravity is calculated. 
        Particles closer than this distance experience a gravity force as if 
        they were this distance away. This stops the gravity effect blowing 
	up as distances get small. 
--]]
function gravityWell(body, x, y, power, epsilon)
    local lx = x - body:getX()
    local ly = y - body:getY()
    local ldSq = lx * lx + ly * ly
    power = power * 10000 or 100000
    epsilon = epsilon * epsilon or 50 * 50

    if ldSq ~= 0 then
        local ld = math.sqrt(ldSq)
        if ldSq < epsilon then ldSq = epsilon end
        
        local lfactor = (power * love.timer.getDelta()) / (ldSq * ld)
        local oldX, oldY = body:getLinearVelocity()
        body:setLinearVelocity(oldX + lx * lfactor, oldY + ly * lfactor)
    end
end
User avatar
leiradel
Party member
Posts: 184
Joined: Thu Mar 11, 2010 3:40 am
Location: Lisbon, Portugal

Re: Share your Utils

Post by leiradel »

I don't think these lines do what you expect:

Code: Select all

power = power * 10000 or 100000
epsilon = epsilon * epsilon or 50 * 50
or is the operator with lowest precedence in Lua. So these expressions are:

Code: Select all

power = ( power * 10000 ) or 100000
epsilon = ( epsilon * epsilon ) or ( 50 * 50 )
In both cases if the variable is nil you'll get an error, attempt to perform arithmetic on local 'x' (a nil value.) You could use if statements or

Code: Select all

power = ( power or 10 ) * 10000
epsilon = ( epsilon or 50 ) * ( epsilon or 50 ) -- or epsilon = epsilon or 50; epsilon = epsilon * epsilon
Cheers,

Andre
User avatar
Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

Re: Share your Utils

Post by Robin »

leiradel wrote:

Code: Select all

power = ( power or 10 ) * 10000
epsilon = ( epsilon or 50 ) * ( epsilon or 50 ) -- or epsilon = epsilon or 50; epsilon = epsilon * epsilon
or, in one line:

Code: Select all

epsilon = epsilon and epsilon ^ 2 or 2500 -- or epsilon = (epsilon or 50) ^ 2
Help us help you: attach a .love.
User avatar
BlackBulletIV
Inner party member
Posts: 1261
Joined: Wed Dec 29, 2010 8:19 pm
Location: Queensland, Australia
Contact:

Re: Share your Utils

Post by BlackBulletIV »

leiradel wrote:I don't think these lines do what you expect:

Code: Select all

power = power * 10000 or 100000
epsilon = epsilon * epsilon or 50 * 50
or is the operator with lowest precedence in Lua. So these expressions are:

Code: Select all

power = ( power * 10000 ) or 100000
epsilon = ( epsilon * epsilon ) or ( 50 * 50 )
In both cases if the variable is nil you'll get an error, attempt to perform arithmetic on local 'x' (a nil value.) You could use if statements or

Code: Select all

power = ( power or 10 ) * 10000
epsilon = ( epsilon or 50 ) * ( epsilon or 50 ) -- or epsilon = epsilon or 50; epsilon = epsilon * epsilon
Cheers,

Andre
No, apart from the error, that's exactly what I wanted; the epsilon must be squared and the power must be multiplied by 10000 (100000 is 10 * 10000). The error could be fixed like this:

Code: Select all

epsilon = epsilon or 50
epsilon = epsilon * epsilon
But I like Robin's suggestion:

Code: Select all

epsilon = (epsilon or 50) ^ 2
User avatar
leiradel
Party member
Posts: 184
Joined: Thu Mar 11, 2010 3:40 am
Location: Lisbon, Portugal

Re: Share your Utils

Post by leiradel »

BlackBulletIV wrote:But I like Robin's suggestion:

Code: Select all

epsilon = (epsilon or 50) ^ 2
Me either, but I have the very bad habit of premature-optimizing code even though the speed gain is close to insignificant, and since exponentiation has to deal with general cases (i.e. the exponent is negative or a fraction) it's likely that squaring by multiplication is faster.

Cheers,

Andre
User avatar
BlackBulletIV
Inner party member
Posts: 1261
Joined: Wed Dec 29, 2010 8:19 pm
Location: Queensland, Australia
Contact:

Re: Share your Utils

Post by BlackBulletIV »

leiradel wrote:
BlackBulletIV wrote:But I like Robin's suggestion:

Code: Select all

epsilon = (epsilon or 50) ^ 2
Me either, but I have the very bad habit of premature-optimizing code even though the speed gain is close to insignificant, and since exponentiation has to deal with general cases (i.e. the exponent is negative or a fraction) it's likely that squaring by multiplication is faster.

Cheers,

Andre
Lol, I didn't even think of that. It probably likely, but then you have to consider the two "or" statements (I'd guess "or" not as resource intensive as multiplication or exponentiation though). But as you said, the gain of such a little thing as that is unnoticeable (unless you did it like, a hundred million times a second).
Post Reply

Who is online

Users browsing this forum: ddabrahim and 1 guest