1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-01 17:38:41 +00:00

Add new vector utils (ceil, sign, abs, random_in_area) (#14807)

This commit is contained in:
kromka-chleba 2024-08-31 09:43:52 +00:00 committed by GitHub
parent 3971b6afcc
commit a6ba5304c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 143 additions and 52 deletions

41
builtin/common/math.lua Normal file
View file

@ -0,0 +1,41 @@
--[[
Math utils.
--]]
function math.hypot(x, y)
return math.sqrt(x * x + y * y)
end
function math.sign(x, tolerance)
tolerance = tolerance or 0
if x > tolerance then
return 1
elseif x < -tolerance then
return -1
end
return 0
end
function math.factorial(x)
assert(x % 1 == 0 and x >= 0, "factorial expects a non-negative integer")
if x >= 171 then
-- 171! is greater than the biggest double, no need to calculate
return math.huge
end
local v = 1
for k = 2, x do
v = v * k
end
return v
end
function math.round(x)
if x < 0 then
local int = math.ceil(x)
local frac = x - int
return int - ((frac <= -0.5) and 1 or 0)
end
local int = math.floor(x)
local frac = x - int
return int + ((frac >= 0.5) and 1 or 0)
end

View file

@ -3,6 +3,7 @@
--------------------------------------------------------------------------------
-- Localize functions to avoid table lookups (better performance).
local string_sub, string_find = string.sub, string.find
local math = math
--------------------------------------------------------------------------------
local function basic_dump(o)
@ -220,47 +221,6 @@ function string:trim()
return self:match("^%s*(.-)%s*$")
end
--------------------------------------------------------------------------------
function math.hypot(x, y)
return math.sqrt(x * x + y * y)
end
--------------------------------------------------------------------------------
function math.sign(x, tolerance)
tolerance = tolerance or 0
if x > tolerance then
return 1
elseif x < -tolerance then
return -1
end
return 0
end
--------------------------------------------------------------------------------
function math.factorial(x)
assert(x % 1 == 0 and x >= 0, "factorial expects a non-negative integer")
if x >= 171 then
-- 171! is greater than the biggest double, no need to calculate
return math.huge
end
local v = 1
for k = 2, x do
v = v * k
end
return v
end
function math.round(x)
if x < 0 then
local int = math.ceil(x)
local frac = x - int
return int - ((frac <= -0.5) and 1 or 0)
end
local int = math.floor(x)
local frac = x - int
return int + ((frac >= 0.5) and 1 or 0)
end
local formspec_escapes = {
["\\"] = "\\\\",
["["] = "\\[",

View file

@ -1,6 +1,7 @@
_G.core = {}
_G.vector = {metatable = {}}
dofile("builtin/common/math.lua")
dofile("builtin/common/vector.lua")
dofile("builtin/common/misc_helpers.lua")

View file

@ -0,0 +1,16 @@
_G.core = {}
dofile("builtin/common/math.lua")
describe("math", function()
it("round()", function()
assert.equal(0, math.round(0))
assert.equal(10, math.round(10.3))
assert.equal(11, math.round(10.5))
assert.equal(11, math.round(10.7))
assert.equal(-10, math.round(-10.3))
assert.equal(-11, math.round(-10.5))
assert.equal(-11, math.round(-10.7))
assert.equal(0, math.round(0.49999999999999994))
assert.equal(0, math.round(-0.49999999999999994))
end)
end)

View file

@ -1,4 +1,5 @@
_G.core = {}
dofile("builtin/common/math.lua")
dofile("builtin/common/vector.lua")
dofile("builtin/common/misc_helpers.lua")

View file

@ -1,4 +1,5 @@
_G.vector = {}
dofile("builtin/common/math.lua")
dofile("builtin/common/vector.lua")
describe("vector", function()
@ -113,12 +114,35 @@ describe("vector", function()
assert.equal(vector.new(0, 1, -1), a:round())
end)
it("ceil()", function()
local a = vector.new(0.1, 0.9, -0.5)
assert.equal(vector.new(1, 1, 0), vector.ceil(a))
assert.equal(vector.new(1, 1, 0), a:ceil())
end)
it("sign()", function()
local a = vector.new(-120.3, 0, 231.5)
assert.equal(vector.new(-1, 0, 1), vector.sign(a))
assert.equal(vector.new(-1, 0, 1), a:sign())
assert.equal(vector.new(0, 0, 1), vector.sign(a, 200))
assert.equal(vector.new(0, 0, 1), a:sign(200))
end)
it("abs()", function()
local a = vector.new(-123.456, 0, 13)
assert.equal(vector.new(123.456, 0, 13), vector.abs(a))
assert.equal(vector.new(123.456, 0, 13), a:abs())
end)
it("apply()", function()
local i = 0
local f = function(x)
i = i + 1
return x + i
end
local f2 = function(x, opt1, opt2, opt3)
return x + opt1 + opt2 + opt3
end
local a = vector.new(0.1, 0.9, -0.5)
assert.equal(vector.new(1, 1, 0), vector.apply(a, math.ceil))
assert.equal(vector.new(1, 1, 0), a:apply(math.ceil))
@ -126,6 +150,9 @@ describe("vector", function()
assert.equal(vector.new(0.1, 0.9, 0.5), a:apply(math.abs))
assert.equal(vector.new(1.1, 2.9, 2.5), vector.apply(a, f))
assert.equal(vector.new(4.1, 5.9, 5.5), a:apply(f))
local b = vector.new(1, 2, 3)
assert.equal(vector.new(4, 5, 6), vector.apply(b, f2, 1, 1, 1))
assert.equal(vector.new(4, 5, 6), b:apply(f2, 1, 1, 1))
end)
it("combine()", function()
@ -469,4 +496,13 @@ describe("vector", function()
assert.True(vector.in_area(vector.new(-10, -10, -10), vector.new(-10, -10, -10), vector.new(10, 10, 10)))
assert.False(vector.in_area(vector.new(-10, -10, -10), vector.new(10, 10, 10), vector.new(-11, -10, -10)))
end)
it("random_in_area()", function()
local min = vector.new(-100, -100, -100)
local max = vector.new(100, 100, 100)
for i = 1, 1000 do
local random = vector.random_in_area(min, max)
assert.True(vector.in_area(random, min, max))
end
end)
end)

View file

@ -5,6 +5,7 @@ Note: The vector.*-functions must be able to accept old vectors that had no meta
-- localize functions
local setmetatable = setmetatable
local math = math
vector = {}
@ -97,18 +98,26 @@ function vector.floor(v)
end
function vector.round(v)
return fast_new(
math.round(v.x),
math.round(v.y),
math.round(v.z)
)
return vector.apply(v, math.round)
end
function vector.apply(v, func)
function vector.ceil(v)
return vector.apply(v, math.ceil)
end
function vector.sign(v, tolerance)
return vector.apply(v, math.sign, tolerance)
end
function vector.abs(v)
return vector.apply(v, math.abs)
end
function vector.apply(v, func, ...)
return fast_new(
func(v.x),
func(v.y),
func(v.z)
func(v.x, ...),
func(v.y, ...),
func(v.z, ...)
)
end
@ -387,6 +396,14 @@ function vector.random_direction()
return fast_new(x/l, y/l, z/l)
end
function vector.random_in_area(min, max)
return fast_new(
math.random(min.x, max.x),
math.random(min.y, max.y),
math.random(min.z, max.z)
)
end
if rawget(_G, "core") and core.set_read_vector and core.set_push_vector then
local function read_vector(v)
return v.x, v.y, v.z