User:Darkfrei/example functions
Contents
- 1 Prints table
- 2 Draw grid
- 3 Draw mouse position
- 4 Beep
- 5 Point in area
- 6 Is value in list
- 7 Normalization and multiplication
- 8 Evaluate a point from any amount of control points
- 9 Split string with separator
- 10 Split string to list of symbols
- 11 Push rectangle from line
- 12 Easy vector math
- 13 Count vowels in the string
- 14 Reverse words, but not word sequence
- 15 Make snake matrix
- 16 Flatten deep list
- 17 Merge tables
- 18 Insert multiple values at once
- 19 Table partition
- 20 Memoize function
- 21 Sort by key values
- 22 Nearest point on the nearest diagonal
- 23 Get dx and dy for diagonal marching
- 24 Project point to line in 3D
- 25 Function overloading
- 26 Check if the way to point exists
- 27 Parabola equations
- 28 Get toroidal distance vector between two points in toroidal world
Prints table
print('{' ..table.concat(line,",")..'},')
Draw grid
function draw_grid ()
local grid_size = 20
love.graphics.setLineWidth (1)
love.graphics.setColor(0.25,0.25,0.25)
local width, height = love.graphics.getDimensions( )
for x = grid_size, width-1, grid_size do
love.graphics.line(x, 0, x, height)
end
for y = grid_size, height-1, grid_size do
love.graphics.line(0, y, width, y)
end
end
Draw mouse position
function draw_mouse ()
local mx, my = love.mouse.getPosition ()
local text = mx..' '..my
local font = love.graphics.getFont()
local w = font:getWidth(text)
local h = font:getHeight()
love.graphics.setColor(0,0,0)
love.graphics.rectangle('fill', mx, my-h, w, h)
love.graphics.setColor(1,1,1)
love.graphics.print(mx..' '..my,mx,my-h)
end
Beep
Define it:
local rate = 44100
local length = 1/32
local tone = 440 -- Hz
local p = math.floor(rate/tone) -- 128
local soundData = love.sound.newSoundData(length*rate, rate, 16, 1)
for i=0, length*rate-1 do soundData:setSample(i, i%p>p/2 and 1 or -1) end
local source = love.audio.newSource(soundData)
local function beep() source:play() end
Call it:
beep()
Point in area
function is_in_area (mx,my, x,y,w,h) -- mouse position and rectangle
if (mx > x) and (mx < (x + w)) and
(my > y) and (my < (y + h)) then
return true
end
end
Is value in list
function is_value_in_list (value, list)
for i, v in pairs (list) do
if v == value then
return true
end
end
end
Normalization and multiplication
Set magnitude to this vector:
function normul (x, y, factor) -- normalization and multiplication
local d = (x*x+y*y)^0.5
factor= factor or 1
return factor*x/d, factor*y/d
end
Evaluate a point from any amount of control points
local function evaluate (curve, t)
local ccpc = curve:getControlPointCount( )
if ccpc > 1 then
return curve:evaluate(t)
elseif ccpc == 1 then
return curve:getControlPoint(1)
else
return 0, 0
end
end
Split string with separator
local function split (string, separator)
local tabl = {}
for str in string.gmatch(string, "[^"..separator.."]+") do
table.insert (tabl, str)
end
return tabl
end
--split ("123#456", "#") -- results: {"123", "456"}
Split string to list of symbols
local function split2 (string)
local tabl = {}
string:gsub(".", function(c) table.insert(tabl, tonumber (c)) end)
return tabl
end
--split2 ("123") -- results: {1, 2, 3}
Push rectangle from line
(made with chatGPT Feb 13 2023)
function pushRectOutOfLine(x, y, w, h, x1, y1, x2, y2)
-- Вычисляем вектор направления линии
local lineDirX = x2 - x1
local lineDirY = y2 - y1
-- Нормализуем вектор направления линии
local lineLength = math.sqrt(lineDirX^2 + lineDirY^2)
lineDirX = lineDirX / lineLength
lineDirY = lineDirY / lineLength
-- Вычисляем вектор от начальной точки линии до прямоугольника
local toRectX = x - x1
local toRectY = y - y1
-- Проекция вектора от начальной точки линии до прямоугольника на вектор направления линии
local dot = toRectX * lineDirX + toRectY * lineDirY
-- Находим ближайшую точку на линии
local nearestX, nearestY
if dot <= 0 then
nearestX, nearestY = x1, y1
elseif dot >= lineLength then
nearestX, nearestY = x2, y2
else
nearestX = x1 + dot * lineDirX
nearestY = y1 + dot * lineDirY
end
-- Вычисляем вектор от ближайшей точки на линии до прямоугольника
local toRectX = x - nearestX
local toRectY = y - nearestY
-- Если прямоугольник пересекает линию
if math.abs(toRectX) < w/2 and math.abs(toRectY) < h/2 then
-- Вычисляем вектор выталкивания
local pushX = (w/2 - math.abs(toRectX)) * math.sign(toRectX)
local pushY = (h/2 - math.abs(toRectY)) * math.sign(toRectY)
-- Возвращаем вектор выталкивания и нормаль линии
return pushX, pushY, lineDirX, lineDirY
end
return 0, 0, 0, 0
end
Short version:
function PushOutRectangleFromLine(x, y, w, h, x1, y1, x2, y2)
local toRectX, toRectY = x - x1, y - y1
local lineDirectionX, lineDirectionY = x2 - x1, y2 - y1
local lineLength = math.sqrt(lineDirectionX^2 + lineDirectionY^2)
lineDirectionX, lineDirectionY = lineDirectionX / lineLength, lineDirectionY / lineLength
local dotProduct = toRectX * lineDirectionX + toRectY * lineDirectionY
local closestPointX, closestPointY = x1 + dotProduct * lineDirectionX, y1 + dotProduct * lineDirectionY
local distanceX, distanceY = x - closestPointX, y - closestPointY
local intersectX, intersectY = closestPointX, closestPointY
if dotProduct < 0 then
intersectX, intersectY = x1, y1
distanceX, distanceY = x - x1, y - y1
elseif dotProduct > lineLength then
intersectX, intersectY = x2, y2
distanceX, distanceY = x - x2, y - y2
end
if math.abs(distanceX) < w / 2 and math.abs(distanceY) < h / 2 then
local pushX, pushY = 0, 0
if w / 2 - math.abs(distanceX) < h / 2 - math.abs(distanceY) then
pushX = math.sign(distanceX) * (w / 2 - math.abs(distanceX))
else
pushY = math.sign(distanceY) * (h / 2 - math.abs(distanceY))
end
return pushX, pushY
end
return 0, 0
end
(fixed normal)
function PushOutRectangleFromLine(x, y, w, h, x1, y1, x2, y2)
local toRectX, toRectY = x - x1, y - y1
local lineLength = math.sqrt((x2 - x1) ^ 2 + (y2 - y1) ^ 2)
local normalX, normalY = (y2 - y1) / lineLength, -(x2 - x1) / lineLength
local dot = toRectX * normalX + toRectY * normalY
local intersectX, intersectY = x - dot * normalX, y - dot * normalY
if intersectX < x then
intersectX = x
elseif intersectX > x + w then
intersectX = x + w
end
if intersectY < y then
intersectY = y
elseif intersectY > y + h then
intersectY = y + h
end
local distance = (toRectX - intersectX) * normalX + (toRectY - intersectY) * normalY
if distance < 0 then
intersectX = intersectX + distance * normalX
intersectY = intersectY + distance * normalY
end
return intersectX, intersectY, normalX, normalY
end
Easy vector math
-- Определение метатаблицы для векторов
local Vector = {}
Vector.__index = Vector
-- Конструктор вектора
function Vector.new(x, y)
local v = {x = x or 0, y = y or 0}
setmetatable(v, Vector)
return v
end
-- Перегрузка оператора сложения
function Vector.__add(a, b)
return Vector.new(a.x + b.x, a.y + b.y)
end
-- Перегрузка оператора вычитания
function Vector.__sub(a, b)
return Vector.new(a.x - b.x, a.y - b.y)
end
-- Перегрузка оператора умножения на скаляр
function Vector.__mul(a, b)
if type(a) == "number" then
return Vector.new(b.x * a, b.y * a)
elseif type(b) == "number" then
return Vector.new(a.x * b, a.y * b)
else
error("Can only multiply vector by scalar.")
end
end
-- Пример использования
local v1 = Vector.new(2, 3)
local v2 = Vector.new(4, 5)
local v3 = v1 + v2 -- v3 будет иметь координаты (6, 8)
local v4 = v1 - v2 -- v4 будет иметь координаты (-2, -2)
local v5 = v1 * 2 -- v5 будет иметь координаты (4, 6)
local v6 = 3 * v2 -- v6 будет иметь координаты (12, 15)
print (
v1.x..' '..v1.y,
v2.x..' '..v2.y,
v3.x..' '..v3.y,
v4.x..' '..v4.y,
v5.x..' '..v5.y,
v6.x..' '..v6.y
)
Count vowels in the string
Here is a function to count vowels in the string:
function countVowels(str)
_, nvow = string.gsub(str, "[AEIOUaeiou]", "")
return nvow
end
print(countVowels("hello world")) -- 3
print(countVowels("Lua is awesome")) -- 7
print(countVowels("fbwklfbwjf")) -- 0
Reverse words, but not word sequence
function reverseWord (str)
local last = string.len (str)
local res = ""
for i = last, 1, -1 do
res = res .. string.sub(str,i, i)
end
return res
end
function reverseWords (str)
local text = ""
for word in str:gmatch("%S+") do
text = text .. ' ' .. reverseWord (word)
end
return text:sub(2, -1)
end
print(reverseWords("Hello world")) -- "olleH dlrow"
print(reverseWords("Lua is awesome")) -- "auL si emosewa"
Make snake matrix
as snakeMatrix (3) -- {{1, 2, 3}, {6, 5, 4}, {7, 8, 9}}
snakeMatrix (4) -- {{1, 2, 3, 4}, {8, 7, 6, 5}, {9, 10, 11, 12}, {16, 15, 14, 13}}
function snakeMatrix (n)
local matrix = {}
local index = 1
for i = 1, n do
local line = {}
if i%2 == 1 then
for j = 1, n do
table.insert (line, index)
index = index + 1
end
else
for j = 1, n do
table.insert (line, 1, index)
index = index + 1
end
end
matrix[i] = line
end
return matrix
end
function showDeepList (tabl)
local list = {}
if type(tabl[1]) == "table" then
for i = 1, #tabl do
table.insert (list, showDeepList (tabl[i]))
end
else
list = tabl
end
return '{'.. table.concat (list, ', ') ..'}'
end
print (showDeepList(snakeMatrix (3))) -- {{1, 2, 3}, {6, 5, 4}, {7, 8, 9}}
print (showDeepList(snakeMatrix (4))) -- {{1, 2, 3, 4}, {8, 7, 6, 5}, {9, 10, 11, 12}, {16, 15, 14, 13}}
Flatten deep list
function deepFlatten (list, tabl)
for i, v in ipairs (tabl) do
if type (v) == "table" then
deepFlatten (list, v)
else
table.insert (list, v)
end
end
end
function flatten (tabl)
local list = {}
deepFlatten (list, tabl)
return list
end
local t = {1, 2, {3, 4, {5, 6}, 7}, 8}
print(table.concat(flatten(t), ", ")) -- "1, 2, 3, 4, 5, 6, 7, 8"
Merge tables
(for Lua 5.3)
function mergeTables (...) -- ... is varargs
local res = {}
local arg = {...}
for i,list in ipairs(arg) do
for j, value in ipairs (list) do
table.insert (res, value)
end
end
return res
end
local t1 = {1, 2, 3}
local t2 = {4, 5}
local t3 = {6, 7, 8, 9}
local t4 = mergeTables(t1, t2, t3)
print(table.concat(t4, ", ")) -- "1, 2, 3, 4, 5, 6, 7, 8, 9"
Insert multiple values at once
(for Lua 5.3)
function insertMultiple (list, ...)
-- local arg = {...}
-- for _, value in ipairs(arg) do
-- table.insert (list, value)
-- end
-- same, but faster:
for i = 1, select("#", ...) do
local value = select(i, ...)
table.insert(t, value)
end
end
local line = {1, 2}
insertMultiple (line, 3, 4)
print (unpack (line)) -- 1 2 3 4
Table partition
function isEven(n)
return n % 2 == 0
end
function partition (list, f)
local p, n = {}, {} -- positive und negative
for i, v in ipairs (list) do
if f(v) then
table.insert (p, v)
else
table.insert (n, v)
end
end
return p, n
end
local numbers = {1, 2, 3, 4, 5, 6}
local evens, odds = partition(numbers, isEven)
print(table.concat(evens, ", ")) -- "2, 4, 6"
print(table.concat(odds, ", ")) -- "1, 3, 5"
Memoize function
function sleep (n)
while n > 0 do
print("Sleeping for ".. tonumber(n) .." seconds...")
os.execute("ping -n 2 localhost > nul")
n = n-1
end
-- os.execute("ping -n ".. tonumber(n+1) .." localhost > nul")
end
function slowFunction(n)
sleep (n)
return n * n
end
function memoize(f)
local cache = {}
return function (x)
if cache[x] == nil then
cache[x] = f(x)
end
return cache[x]
end
end
local memoizedFunction = memoize(slowFunction)
print(memoizedFunction(5)) -- Sleeping for 1 second... 25
print(memoizedFunction(5)) -- 25 (no waiting, just result)
Sort by key values
function ageSort (a, b)
return a.age < b.age
end
local people = {
{ name = "Alice", age = 25 },
{ name = "Bob", age = 32 },
{ name = "Charlie", age = 18 },
{ name = "Dave", age = 42 },
{ name = "Eve", age = 29 }
}
table.sort(people, ageSort)
for i, v in ipairs (people) do
print (i, v.name, v.age)
end
Result:
1 Charlie 18
2 Alice 25
3 Eve 29
4 Bob 32
5 Dave 42
Universal solution:
function sortByKey(key)
return function(a, b)
return a[key] < b[key]
end
end
local people = {
{ name = "Alice", age = 25 },
{ name = "Bob", age = 32 },
{ name = "Charlie", age = 18 },
{ name = "Dave", age = 42 },
{ name = "Eve", age = 29 }
}
table.sort(people, sortByKey("age"))
print (' sorted by age:')
for i, v in ipairs (people) do
print (i, v.name, v.age)
end
table.sort(people, sortByKey("name"))
print (' sorted by name:')
for i, v in ipairs (people) do
print (i, v.name, v.age)
end
Result:
sorted by age:
1 Charlie 18
2 Alice 25
3 Eve 29
4 Bob 32
5 Dave 42
sorted by name:
1 Alice 25
2 Bob 32
3 Charlie 18
4 Dave 42
5 Eve 29
Nearest point on the nearest diagonal
local function calculateNearestDiagonal(x, y)
-- x, y - point position
-- y1 = x; y2 = 1-x - tile diagonals in range x=[0, 1]
local dx, dy = x%1, y%1
local t1 = (dy-dx)/2
local t2 = (dy-(1-dx))/2
dx = dx+math.floor(x)
dy = dy+math.floor(y)
if math.abs (t1) < math.abs (t2) then
return dx + t1, dy - t1, t1
else
return dx - t2, dy - t2, t2
end
end
Get dx and dy for diagonal marching
function getDiagonalMarch (angle)
-- https://www.desmos.com/calculator/jmvgydivg3
angle = angle % (2*math.pi)
local dx = math.cos(angle)
local dy = math.sin(angle)
if angle < math.pi/2 then
local k = 1 / (dx + dy)
return dx * k, dy * k
elseif angle < math.pi then
local k = 1 / (-dx + dy)
return dx * k, dy * k
elseif angle < math.pi*3/2 then
local k = 1 / (dx + dy)
return -dx * k, -dy * k
else
local k = 1 / (dx + -dy)
return dx * k, dy * k
end
end
Project point to line in 3D
function projectionPointToLine3D (xa, ya, za, xb, yb, zb, xm, ym, zm)
local t = ((xb - xa) * (xm - xa) + (yb - ya) * (ym - ya) + (zb - za) * (zm - za)) / ((xb - xa)^2 + (yb - ya)^2 + (zb - za)^2)
local xp = xa + t * (xb - xa)
local yp = ya + t * (yb - ya)
local zp = za + t * (zb - za)
return xp, yp, zp, t
end
Function overloading
local exp_mt = {
__call = function(self, arr)
local res = {}
for i = 1, #arr do
res[i] = math.exp(arr[i])
end
return res
end
}
setmetatable(math.exp, exp_mt)
-- Пример использования
local input = {1, 2, 3, 4, 5}
local output = math.exp(input)
print(table.concat(output, ', ')) -- выводит: 2.718281828459, 7.3890560989307, 20.085536923188, 54.598150033145, 148.41315910258
-- создаем метатаблицу для чисел и массивов
local exp_mt = {
__call = function(self, x)
-- если x является числом, применяем exp к нему
if type(x) == "number" then
return math.exp(x)
end
-- если x является массивом, применяем exp к каждому элементу массива
if type(x) == "table" then
local result = {}
for i, v in ipairs(x) do
result[i] = math.exp(v)
end
return result
end
end
}
-- создаем новую функцию exp, которая использует метатаблицу
exp = setmetatable({}, exp_mt)
-- вычисление экспоненты числа
print(exp(2)) -- 7.3890560989307
-- вычисление экспоненты массива
local a = {1, 2, 3}
print(table.concat(exp(a), ", ")) -- 2.718281828459, 7.3890560989307, 20.085536923188
Check if the way to point exists
local function isWayTo (map, x1, y1, x2, y2) -- astar
local openSet = {}
local closedHashes = {}
local nodesGrid = {}
local neighborSet = {{x=0,y=-1}, {x=1,y=0}, {x=0,y=1}, {x=-1,y=0}}
local function heuristic(x, y)
return math.abs (x-x2) + math.abs (y-y2)
end
local function isFree (map, x, y)
if map[y] and map[y][x] and map[y][x] == 0 then
return true
end
return false
end
local function nearestNode (nodes)
local fMin = math.huge
local currentIndex = 1
for i, node in ipairs(nodes) do
local f = node.f
if f < fMin then
fMin = f
currentIndex = i
end
end
return currentIndex
end
local function getNeighbors(map, x0, y0)
local neighbors = {}
for i, d in ipairs (neighborSet) do
local x, y = x0+d.x, y0+d.y
if isFree (map, x, y) then
local node = nodesGrid[y][x]
table.insert (neighbors, node)
end
end
return neighbors
end
for y = 1, #map do
nodesGrid[y] = {}
for x = 1, #map[y] do
nodesGrid[y][x] = {x=x,y=y}
end
end
local current = nodesGrid[y1][x1]
current.g = 0
current.f = heuristic(x1, y1)
table.insert(openSet, current)
while #openSet > 0 do
local currentIndex = nearestNode (openSet)
current = table.remove (openSet, currentIndex)
closedHashes[current] = true
if current.x == x2 and current.y == y2 then
return true
end
local neighbors = getNeighbors(map, current.x, current.y)
for _, neighbor in ipairs(neighbors) do
if not closedHashes[neighbor] then
local gScore = current.g + 1
neighbor.g = gScore
neighbor.f = gScore + heuristic(neighbor.x, neighbor.y)
closedHashes[neighbor] = true
table.insert(openSet, neighbor)
end
end
end
return false
end
local map1 = {
{0,1,0},
{0,1,0},
{0,0,0},
}
print (isWayTo (map1, 1, 1, 3, 1)) -- true
Parabola equations
g is gravity (also ay) v0 is a speed by the start t is a time: by the start t = 0 alphaD is an angle in degrees alpha is an angle in radians, so alpha = 3.14/180*alphaD vx0, vy0 are velocities as vx0 = v0 * cos (alpha) vy0 = v0 * sin (alpha)
Get toroidal distance vector between two points in toroidal world
local function getToroidalDistanceVector (cellA, cellB, gridWidth, gridHeight)
local dx = cellB.x-cellA.x
local dy = cellB.y-cellA.y
if math.abs (dx) > gridWidth/2 then
if dx > 0 then
dx = dx - gridWidth
else
dx = dx + gridWidth
end
end
if math.abs(dy) > gridHeight/2 then
if dy > 0 then
dy = dy - gridHeight
else
dy = dy + gridHeight
end
end
return dx, dy
end
Example how to use it:
local dx, dy = getToroidalDistanceVector (cellA, cellB, gridWidth, gridHeight)
local toroidalDistance = math.sqrt(dx*dx + dy*dy)
local middlePoint = {x=cellA.x+dx/2, y=cellA.y+dy/2}