Difference between revisions of "User:Darkfrei/example functions"

(Draw grid)
 
(43 intermediate revisions by 2 users not shown)
Line 88: Line 88:
 
return factor*x/d, factor*y/d
 
return factor*x/d, factor*y/d
 
end
 
end
 +
</source>
 +
 +
 +
== Evaluate a point from any amount of control points ==
 +
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
 +
== Split string with separator ==
 +
<source lang="lua">
 +
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"}</source>
 +
 +
== Split string to list of symbols ==
 +
<source lang="lua">
 +
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}</source>
 +
 +
== Push rectangle from line ==
 +
(made with chatGPT Feb 13 2023)
 +
<source lang="lua">
 +
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
 +
 +
</source>
 +
 +
Short version:
 +
<source lang="lua">
 +
 +
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
 +
</source>
 +
 +
(fixed normal)
 +
<source lang="lua">
 +
 +
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
 +
</source>
 +
 +
 +
== Easy vector math ==
 +
 +
<source lang="lua">
 +
-- Определение метатаблицы для векторов
 +
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
 +
)
 +
</source>
 +
 +
 +
== Count vowels in the string ==
 +
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
 +
== Reverse words, but not word sequence ==
 +
<source lang="lua">
 +
 +
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"
 +
</source>
 +
 +
 +
== 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}}
 +
 +
<source lang="lua">
 +
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}}
 +
</source>
 +
 +
== Flatten deep list ==
 +
<source lang="lua">
 +
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"
 +
</source>
 +
 +
 +
== Merge tables ==
 +
(for Lua 5.3)
 +
<source lang="lua">
 +
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"
 +
</source>
 +
 +
 +
== Insert multiple values at once ==
 +
(for Lua 5.3)
 +
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
== Table partition ==
 +
<source lang="lua">
 +
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"
 +
</source>
 +
 +
== Memoize function ==
 +
<source lang="lua">
 +
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)
 +
</source>
 +
 +
== Sort by key values ==
 +
<source lang="lua">
 +
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
 +
</source>
 +
Result:
 +
<source>
 +
1 Charlie 18
 +
2 Alice 25
 +
3 Eve 29
 +
4 Bob 32
 +
5 Dave 42
 +
</source>
 +
 +
 +
Universal solution:
 +
<source lang="lua">
 +
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
 +
</source>
 +
Result:
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
== Nearest point on the nearest diagonal ==
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
 +
== Get dx and dy for diagonal marching ==
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
 +
== Project point to line in 3D ==
 +
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
 +
== Function overloading ==
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
 +
<source lang="lua">
 +
-- создаем метатаблицу для чисел и массивов
 +
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
 +
 +
</source>
 +
 +
 +
== Check if the way to point exists ==
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
 +
== Get toroidal distance vector between two points in toroidal world ==
 +
<source lang="lua">
 +
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
 +
</source>
 +
 +
Example how to use it:
 +
<source lang="lua">
 +
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}
 +
</source>
 +
 +
 +
 +
== 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)
 +
 +
x0, y0 are starting coordinates.
 +
 +
<source lang="lua">
 +
function parabolaUpdate(dt)
 +
  local vy1 = vy or vy0
 +
  vy1 = vy1 - dt * g -- or plus
 +
 +
  x = x or x0
 +
  x = x + dt * vx0
 +
  y = y + dt * (vy + vy1) / 2
 +
  vy = vy1
 +
end
 +
</source>
 +
 +
<source lang="lua">
 +
function parabolaGetValueByTime(t)
 +
  local y = y0 + vy0 * t - 0.5 * g * t^2
 +
  return y
 +
end
 +
</source>
 +
 +
<source lang="lua">
 +
function parabolaGetValue(a, b, c, x)
 +
  local y = a*x^2 + b*x + c
 +
  return y
 +
end
 +
 +
<source lang="lua">
 +
function getParabolaCoefficients (g, v0, alpha, x0, y0)
 +
  local a = -0.5*g/(v0*cos(alpha))^2
 +
  local b = tan(alpha) - 2*a*x0
 +
  local c = y0 - b*x0 - a*x0^2
 +
  return a, b, c
 +
end
 +
</source>
 +
 +
<source lang="lua">
 +
g = 10.5
 +
v0 = 14.142
 +
alphaD = 45
 +
x0, y0 = 2, 1
 +
a, b, c = getParabolaCoefficients (g, v0, 3.14/180*alphaD, x0, y0)
 +
-0.0525
 +
1.21
 +
-1.21
 
</source>
 
</source>

Latest revision as of 13:53, 23 September 2023

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


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}


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)

x0, y0 are starting coordinates.

function parabolaUpdate(dt)
  local vy1 = vy or vy0
  vy1 = vy1 - dt * g -- or plus

  x = x or x0
  x = x + dt * vx0
  y = y + dt * (vy + vy1) / 2
  vy = vy1
end
function parabolaGetValueByTime(t)
  local y = y0 + vy0 * t - 0.5 * g * t^2
  return y
end
function parabolaGetValue(a, b, c, x)
  local y = a*x^2 + b*x + c
  return y
end

<source lang="lua">
function getParabolaCoefficients (g, v0, alpha, x0, y0)
  local a = -0.5*g/(v0*cos(alpha))^2
  local b = tan(alpha) - 2*a*x0
  local c = y0 - b*x0 - a*x0^2
  return a, b, c
end
g = 10.5
v0 = 14.142
alphaD = 45
x0, y0 = 2, 1
a, b, c = getParabolaCoefficients (g, v0, 3.14/180*alphaD, x0, y0)
-0.0525
1.21
-1.21