vector_extras/init.lua
HybridDog f400a91e90 Remove functions which are deprecated and which I do not recommend to use
Removing deprecated functionality can help to clean up the code a bit.

I remove the following deprecated functions and values:
* vector.zero
* vector.plane()
* vector.pos_to_string()
* vector.quickadd()
* vector.scalar()
* vector.get_data_from_pos()
* vector.set_data_to_pos()
* vector.set_data_to_pos_optional()
* vector.remove_data_from_pos()
* vector.get_data_pos_table()

Furthermore, I remove the unused, experimental vector_meta.lua file
and make luacheck linting more strict so that it can show accidental
uses of deprecated functions in the code of this mod if there are any.

This change can break compatility with old mods which use vector_extras.
2024-08-27 17:38:30 +02:00

949 lines
22 KiB
Lua

local path = minetest.get_modpath"vector_extras"
local funcs = {}
local r_corr = 0.25 --remove a bit more nodes (if shooting diagonal) to let it
-- look like a hole (sth like antialiasing)
-- this doesn't need to be calculated every time
local f_1 = 0.5-r_corr
local f_2 = 0.5+r_corr
--returns information about the direction
local function get_used_dir(dir)
local abs_dir = {x=math.abs(dir.x), y=math.abs(dir.y), z=math.abs(dir.z)}
local dir_max = math.max(abs_dir.x, abs_dir.y, abs_dir.z)
if dir_max == abs_dir.x then
local tab = {"x", {x=1, y=dir.y/dir.x, z=dir.z/dir.x}}
if dir.x >= 0 then
tab[3] = "+"
end
return tab
end
if dir_max == abs_dir.y then
local tab = {"y", {x=dir.x/dir.y, y=1, z=dir.z/dir.y}}
if dir.y >= 0 then
tab[3] = "+"
end
return tab
end
local tab = {"z", {x=dir.x/dir.z, y=dir.y/dir.z, z=1}}
if dir.z >= 0 then
tab[3] = "+"
end
return tab
end
local function node_tab(z, d)
local n1 = math.floor(z*d+f_1)
local n2 = math.floor(z*d+f_2)
if n1 == n2 then
return {n1}
end
return {n1, n2}
end
local function return_line(pos, dir, range) --range ~= length
local tab = {}
local num = 1
local t_dir = get_used_dir(dir)
local dir_typ = t_dir[1]
local f_tab
if t_dir[3] == "+" then
f_tab = {0, range, 1}
else
f_tab = {0, -range, -1}
end
local d_ch = t_dir[2]
if dir_typ == "x" then
for d = f_tab[1],f_tab[2],f_tab[3] do
local x = d
local ytab = node_tab(d_ch.y, d)
local ztab = node_tab(d_ch.z, d)
for _,y in ipairs(ytab) do
for _,z in ipairs(ztab) do
tab[num] = {x=pos.x+x, y=pos.y+y, z=pos.z+z}
num = num+1
end
end
end
elseif dir_typ == "y" then
for d = f_tab[1],f_tab[2],f_tab[3] do
local xtab = node_tab(d_ch.x, d)
local y = d
local ztab = node_tab(d_ch.z, d)
for _,x in ipairs(xtab) do
for _,z in ipairs(ztab) do
tab[num] = {x=pos.x+x, y=pos.y+y, z=pos.z+z}
num = num+1
end
end
end
else
for d = f_tab[1],f_tab[2],f_tab[3] do
local xtab = node_tab(d_ch.x, d)
local ytab = node_tab(d_ch.y, d)
local z = d
for _,x in ipairs(xtab) do
for _,y in ipairs(ytab) do
tab[num] = {x=pos.x+x, y=pos.y+y, z=pos.z+z}
num = num+1
end
end
end
end
return tab
end
function funcs.rayIter(pos, dir)
-- make a table of possible movements
local step = {}
for i in pairs(pos) do
local v = math.sign(dir[i])
if v ~= 0 then
step[i] = v
end
end
local p
return function()
if not p then
-- avoid skipping the first position
p = vector.round(pos)
return vector.new(p)
end
-- find the position which has the smallest distance to the line
local choose = {}
local choosefit = vector.new()
for i in pairs(step) do
choose[i] = vector.new(p)
choose[i][i] = choose[i][i] + step[i]
choosefit[i] = vector.dot(vector.normalize(vector.subtract(choose[i], pos)), dir)
end
p = choose[vector.get_max_coord(choosefit)]
return vector.new(p)
end
end
function funcs.fine_line(pos, dir, range)
if not range then --dir = pos2
dir, range = vector.direction(pos, dir), vector.distance(pos, dir)
end
local result,n = {},1
for p in vector.rayIter(pos, dir) do
if vector.distance(p, pos) > range then
break
end
result[n] = p
n = n+1
end
return result
end
function funcs.line(pos, dir, range, alt)
--assert_vector(pos)
if alt then
if not range then --dir = pos2
dir, range = vector.direction(pos, dir), vector.distance(pos, dir)
end
return return_line(pos, dir, range)
end
if range then
dir = vector.round(vector.multiply(dir, range))
else --dir = pos2
dir = vector.subtract(dir, pos)
end
local line,n = {},1
for _,i in ipairs(vector.threeline(dir.x, dir.y, dir.z)) do
line[n] = {x=pos.x+i[1], y=pos.y+i[2], z=pos.z+i[3]}
n = n+1
end
return line
end
local twolines = {}
function funcs.twoline(x, y)
local pstr = x.." "..y
local line = twolines[pstr]
if line then
return line
end
line = {}
local n = 1
local dirx = 1
if x < 0 then
dirx = -dirx
end
local ymin, ymax = 0, y
if y < 0 then
ymin, ymax = ymax, ymin
end
local m = y/x --y/0 works too
local dir = 1
if m < 0 then
dir = -dir
end
for i = 0,x,dirx do
local p1 = math.max(math.min(math.floor((i-0.5)*m+0.5), ymax), ymin)
local p2 = math.max(math.min(math.floor((i+0.5)*m+0.5), ymax), ymin)
for j = p1,p2,dir do
line[n] = {i, j}
n = n+1
end
end
twolines[pstr] = line
return line
end
local threelines = {}
function funcs.threeline(x, y, z)
local pstr = x.." "..y.." "..z
local line = threelines[pstr]
if line then
return line
end
if x ~= math.floor(x) then
minetest.log("error", "[vector_extras] INFO: The position used for " ..
"vector.threeline isn't round.")
end
local two_line = vector.twoline(x, y)
line = {}
local n = 1
local zmin, zmax = 0, z
if z < 0 then
zmin, zmax = zmax, zmin
end
local m = z/math.hypot(x, y)
local dir = 1
if m < 0 then
dir = -dir
end
for _,i in ipairs(two_line) do
local px, py = unpack(i)
local ph = math.hypot(px, py)
local z1 = math.max(math.min(math.floor((ph-0.5)*m+0.5), zmax), zmin)
local z2 = math.max(math.min(math.floor((ph+0.5)*m+0.5), zmax), zmin)
for pz = z1,z2,dir do
line[n] = {px, py, pz}
n = n+1
end
end
threelines[pstr] = line
return line
end
function funcs.sort_positions(ps, preferred_coords)
preferred_coords = preferred_coords or {"z", "y", "x"}
local a,b,c = unpack(preferred_coords)
local function ps_sorting(p1, p2)
if p1[a] == p2[a] then
if p1[b] == p2[a] then
if p1[c] < p2[c] then
return true
end
elseif p1[b] < p2[b] then
return true
end
elseif p1[a] < p2[a] then
return true
end
end
table.sort(ps, ps_sorting)
end
-- Tschebyschew norm
function funcs.maxnorm(v)
return math.max(math.max(math.abs(v.x), math.abs(v.y)), math.abs(v.z))
end
function funcs.sumnorm(v)
return math.abs(v.x) + math.abs(v.y) + math.abs(v.z)
end
function funcs.pnorm(v, p)
return (math.abs(v.x)^p + math.abs(v.y)^p + math.abs(v.z)^p)^(1 / p)
end
function funcs.straightdelay(s, v, a)
if not a then
return s/v
end
return (math.sqrt(v*v+2*a*s)-v)/a
end
function funcs.sun_dir(time)
if not time then
time = minetest.get_timeofday()
end
local t = (time-0.5)*5/6+0.5 --the sun rises at 5 o'clock, not at 6
if t < 0.25
or t > 0.75 then
return
end
local tmp = math.cos(math.pi*(2*t-0.5))
return {x=tmp, y=math.sqrt(1-tmp*tmp), z=0}
end
function funcs.inside(pos, minp, maxp)
for _,i in pairs({"x", "y", "z"}) do
if pos[i] < minp[i]
or pos[i] > maxp[i] then
return false
end
end
return true
end
function funcs.minmax(pos1, pos2)
local p1 = vector.new(pos1)
local p2 = vector.new(pos2)
for _,i in ipairs({"x", "y", "z"}) do
if p1[i] > p2[i] then
p1[i], p2[i] = p2[i], p1[i]
end
end
return p1, p2
end
function funcs.move(p1, p2, s)
return vector.round(
vector.add(
vector.multiply(
vector.direction(
p1,
p2
),
s
),
p1
)
)
end
function funcs.from_number(i)
return {x=i, y=i, z=i}
end
local adammil_fill = dofile(path .. "/adammil_flood_fill.lua")
function funcs.search_2d(go_test, x0, y0, allow_revisit, give_map)
local marked_places = adammil_fill(go_test, x0, y0, allow_revisit)
if give_map then
return marked_places
end
local l = {}
for vi in pairs(marked_places) do
local x = (vi % 65536) - 32768
local y = (math.floor(x / 65536) % 65536) - 32768
l[#l+1] = {x, y}
end
return l
end
local fallings_search = dofile(path .. "/fill_3d.lua")
local moves_touch = {
{x = -1, y = 0, z = 0},
{x = 0, y = 0, z = 0}, -- FIXME should this be here?
{x = 1, y = 0, z = 0},
{x = 0, y = -1, z = 0},
{x = 0, y = 1, z = 0},
{x = 0, y = 0, z = -1},
{x = 0, y = 0, z = 1},
}
local moves_near = {}
for z = -1,1 do
for y = -1,1 do
for x = -1,1 do
moves_near[#moves_near+1] = {x = x, y = y, z = z}
end
end
end
function funcs.search_3d(can_go, startpos, apply_move, moves)
local visited = {}
local found = {}
local function on_visit(pos)
local vi = minetest.hash_node_position(pos)
if visited[vi] then
return false
end
visited[vi] = true
local valid_pos = can_go(pos)
if valid_pos then
found[#found+1] = pos
end
return valid_pos
end
if apply_move == "touch" then
apply_move = vector.add
moves = moves_touch
elseif apply_move == "near" then
apply_move = vector.add
moves = moves_near
end
fallings_search(on_visit, startpos, apply_move, moves)
end
local explosion_tables = {}
function funcs.explosion_table(r)
local table = explosion_tables[r]
if table then
return table
end
--~ local t1 = os.clock()
local tab, n = {}, 1
local tmp = r*r + r
for x=-r,r do
for y=-r,r do
for z=-r,r do
local rc = x*x+y*y+z*z
if rc <= tmp then
local np={x=x, y=y, z=z}
if math.floor(math.sqrt(rc) +0.5) > r-1 then
tab[n] = {np, true}
else
tab[n] = {np}
end
n = n+1
end
end
end
end
explosion_tables[r] = tab
--~ minetest.log("info", string.format("[vector_extras] table created after ca. %.2fs", os.clock() - t1))
return tab
end
local default_nparams = {
offset = 0,
scale = 1,
seed = 1337,
octaves = 6,
persist = 0.6
}
function funcs.explosion_perlin(rmin, rmax, nparams)
local t1 = os.clock()
local r = math.ceil(rmax)
nparams = nparams or {}
for i,v in pairs(default_nparams) do
nparams[i] = nparams[i] or v
end
nparams.spread = nparams.spread or vector.from_number(r*5)
local pos = {x=math.random(-30000, 30000), y=math.random(-30000, 30000), z=math.random(-30000, 30000)}
local map = minetest.get_perlin_map(nparams, vector.from_number(r+r+1)
):get3dMap_flat(pos)
local id = 1
local bare_maxdist = rmax*rmax
local bare_mindist = rmin*rmin
local mindist = math.sqrt(bare_mindist)
local dist_diff = math.sqrt(bare_maxdist)-mindist
mindist = mindist/dist_diff
local pval_min, pval_max
local tab, n = {}, 1
for z=-r,r do
local bare_dist_z = z*z
for y=-r,r do
local bare_dist_yz = bare_dist_z + y*y
for x=-r,r do
local bare_dist = bare_dist_yz + x*x
local add = bare_dist < bare_mindist
local pval, distdiv
if not add
and bare_dist <= bare_maxdist then
distdiv = math.sqrt(bare_dist)/dist_diff-mindist
pval = math.abs(map[id]) -- strange perlin values…
if not pval_min then
pval_min = pval
pval_max = pval
else
pval_min = math.min(pval, pval_min)
pval_max = math.max(pval, pval_max)
end
add = true--distdiv < 1-math.abs(map[id])
end
if add then
tab[n] = {{x=x, y=y, z=z}, pval, distdiv}
n = n+1
end
id = id+1
end
end
end
-- change strange values
local pval_diff = pval_max - pval_min
pval_min = pval_min/pval_diff
for k,i in pairs(tab) do
if i[2] then
local new_pval = math.abs(i[2]/pval_diff - pval_min)
if i[3]+0.33 < new_pval then
tab[k] = {i[1]}
elseif i[3] < new_pval then
tab[k] = {i[1], true}
else
tab[k] = nil
end
end
end
minetest.log("info", string.format("[vector_extras] table created after ca. %.2fs", os.clock() - t1))
return tab
end
local circle_tables = {}
function funcs.circle(r)
local table = circle_tables[r]
if table then
return table
end
local t1 = os.clock()
local tab, n = {}, 1
for i = -r, r do
for j = -r, r do
if math.floor(math.sqrt(i*i+j*j)+0.5) == r then
tab[n] = {x=i, y=0, z=j}
n = n+1
end
end
end
circle_tables[r] = tab
minetest.log("info", string.format("[vector_extras] table created after ca. %.2fs", os.clock() - t1))
return tab
end
local ring_tables = {}
function funcs.ring(r)
local table = ring_tables[r]
if table then
return table
end
local t1 = os.clock()
local tab, n = {}, 1
local tmp = r*r
local p = {x=math.floor(r+0.5), z=0}
while p.x > 0 do
tab[n] = p
n = n+1
local p1, p2 = {x=p.x-1, z=p.z}, {x=p.x, z=p.z+1}
local dif1 = math.abs(tmp-p1.x*p1.x-p1.z*p1.z)
local dif2 = math.abs(tmp-p2.x*p2.x-p2.z*p2.z)
if dif1 <= dif2 then
p = p1
else
p = p2
end
end
local tab2 = {}
n = 1
for _,i in ipairs(tab) do
for _,j in ipairs({
{i.x, i.z},
{-i.z, i.x},
{-i.x, -i.z},
{i.z, -i.x},
}) do
tab2[n] = {x=j[1], y=0, z=j[2]}
n = n+1
end
end
ring_tables[r] = tab2
minetest.log("info", string.format("[vector_extras] table created after ca. %.2fs", os.clock() - t1))
return tab2
end
local function get_parabola_points(pos, vel, gravity, waypoints, max_pointcount,
time)
local pointcount = 0
-- the height of the 45° angle point
local yswitch = -0.5 * (vel.x^2 + vel.z^2 - vel.y^2)
/ gravity + pos.y
-- the times of the 45° angle point
local vel_len = math.sqrt(vel.x^2 + vel.z^2)
local t_raise_end = (-vel_len + vel.y) / gravity
local t_fall_start = (vel_len + vel.y) / gravity
if t_fall_start > 0 then
-- the right 45° angle point wasn't passed yet
if t_raise_end > 0 then
-- put points from before the 45° angle
for y = math.ceil(pos.y), math.floor(yswitch +.5) do
local t = (vel.y -
math.sqrt(vel.y^2 + 2 * gravity * (pos.y - y))) / gravity
if t > time then
return
end
local p = {
x = math.floor(vel.x * t + pos.x +.5),
y = y,
z = math.floor(vel.z * t + pos.z +.5),
}
pointcount = pointcount+1
waypoints[pointcount] = {p, t}
if pointcount == max_pointcount then
return
end
end
end
-- smaller and bigger horizonzal pivot
local shp, bhp
if math.abs(vel.x) > math.abs(vel.z) then
shp = "z"
bhp = "x"
else
shp = "x"
bhp = "z"
end
-- put points between the 45° angles
local cstart, cdir
local cend = math.floor(vel[bhp] * t_fall_start + pos[bhp] +.5)
if vel[bhp] > 0 then
cstart = math.floor(math.max(pos[bhp],
vel[bhp] * t_raise_end + pos[bhp]) +.5)
cdir = 1
else
cstart = math.floor(math.min(pos[bhp],
vel[bhp] * t_raise_end + pos[bhp]) +.5)
cdir = -1
end
for i = cstart, cend, cdir do
local t = (i - pos[bhp]) / vel[bhp]
if t > time then
return
end
local p = {
[bhp] = i,
y = math.floor(-0.5 * gravity * t * t + vel.y * t + pos.y +.5),
[shp] = math.floor(vel[shp] * t + pos[shp] +.5),
}
pointcount = pointcount+1
waypoints[pointcount] = {p, t}
if pointcount == max_pointcount then
return
end
end
end
-- put points from after the 45° angle
local y = yswitch
if vel.y < 0
and pos.y < yswitch then
y = pos.y
end
y = math.floor(y +.5)
while pointcount < max_pointcount do
local t = (vel.y +
math.sqrt(vel.y^2 + 2 * gravity * (pos.y - y))) / gravity
if t > time then
return
end
local p = {
x = math.floor(vel.x * t + pos.x +.5),
y = y,
z = math.floor(vel.z * t + pos.z +.5),
}
pointcount = pointcount+1
waypoints[pointcount] = {p, t}
y = y-1
end
end
--[[
minetest.override_item("default:axe_wood", {
on_use = function(_, player)
local dir = player:get_look_dir()
local pos = player:getpos()
local grav = 0.03
local ps = vector.throw_parabola(pos, dir, grav, 80)
for i = 1,#ps do
minetest.set_node(ps[i], {name="default:stone"})
end
--~ for t = 0,50,3 do
--~ local p = {
--~ x = dir.x * t + pos.x,
--~ y = -0.5*grav*t*t + dir.y*t + pos.y,
--~ z = dir.z * t + pos.z
--~ }
--~ minetest.set_node(p, {name="default:sandstone"})
--~ end
end,
})--]]
function funcs.throw_parabola(pos, vel, gravity, point_count, time)
local waypoints = {}
get_parabola_points(pos, vel, gravity, waypoints, point_count,
time or math.huge)
local ps = {}
local ptscnt = #waypoints
local i = 1
while i < ptscnt do
local p,t = unpack(waypoints[i])
i = i+1
local p2,t2 = unpack(waypoints[i])
ps[#ps+1] = p
local dist = vector.distance(p, p2)
if dist < 1.1 then
if dist < 0.9 then
-- same position
i = i+1
end
-- touching
elseif dist < 1.7 then
-- common edge
-- get a list of possible positions between
local diff = vector.subtract(p2, p)
local possible_positions = {}
for c,v in pairs(diff) do
if v ~= 0 then
local pos_moved = vector.new(p)
pos_moved[c] = pos_moved[c] + v
possible_positions[#possible_positions+1] = pos_moved
end
end
-- test which one fits best
t = 0.5 * (t + t2)
local near_p = {
x = vel.x * t + pos.x,
y = -0.5 * gravity * t * t + vel.y * t + pos.y,
z = vel.z * t + pos.z,
}
local d = math.huge
for k = 1,2 do
local pos_moved = possible_positions[k]
local dist_current = vector.distance(pos_moved, near_p)
if dist_current < d then
p = pos_moved
d = dist_current
end
end
-- add it
ps[#ps+1] = p
elseif dist < 1.8 then
-- common vertex
for k = 1,2 do
-- get a list of possible positions between
local diff = vector.subtract(p2, p)
local possible_positions = {}
for c,v in pairs(diff) do
if v ~= 0 then
local pos_moved = vector.new(p)
pos_moved[c] = pos_moved[c] + v
possible_positions[#possible_positions+1] = pos_moved
end
end
-- test which one fits best
t = k / 3 * (t + t2)
local near_p = {
x = vel.x * t + pos.x,
y = -0.5 * gravity * t * t + vel.y * t + pos.y,
z = vel.z * t + pos.z,
}
local d = math.huge
assert(#possible_positions == 4-k, "how, number positions?")
for j = 1,4-k do
local pos_moved = possible_positions[j]
local dist_current = vector.distance(pos_moved, near_p)
if dist_current < d then
p = pos_moved
d = dist_current
end
end
-- add it
ps[#ps+1] = p
end
else
minetest.log("warning", "[vector_extras] A gap: " .. dist)
--~ error("A gap, it's a gap!: " .. dist)
end
end
if i == ptscnt then
ps[#ps+1] = waypoints[i]
end
return ps
end
function funcs.chunkcorner(pos)
return {x=pos.x-pos.x%16, y=pos.y-pos.y%16, z=pos.z-pos.z%16}
end
function funcs.point_distance_minmax(pos1, pos2)
local p1 = vector.new(pos1)
local p2 = vector.new(pos2)
local min, max, vmin, vmax, num
for _,i in ipairs({"x", "y", "z"}) do
num = math.abs(p1[i] - p2[i])
if not vmin or num < vmin then
vmin = num
min = i
end
if not vmax or num > vmax then
vmax = num
max = i
end
end
return min, max
end
function funcs.collision(p1, p2)
local clear, node_pos = minetest.line_of_sight(p1, p2)
if clear then
return false
end
local collision_pos = {}
local _, max = funcs.point_distance_minmax(node_pos, p2)
if node_pos[max] > p2[max] then
collision_pos[max] = node_pos[max] - 0.5
else
collision_pos[max] = node_pos[max] + 0.5
end
local dmax = p2[max] - node_pos[max]
local dcmax = p2[max] - collision_pos[max]
local pt = dcmax / dmax
for _,i in ipairs({"x", "y", "z"}) do
collision_pos[i] = p2[i] - (p2[i] - node_pos[i]) * pt
end
return true, collision_pos, node_pos
end
function funcs.update_minp_maxp(minp, maxp, pos)
for _,i in pairs({"z", "y", "x"}) do
minp[i] = math.min(minp[i], pos[i])
maxp[i] = math.max(maxp[i], pos[i])
end
end
function funcs.unpack(pos)
return pos.z, pos.y, pos.x
end
function funcs.get_max_coord(vec)
if vec.x < vec.y then
if vec.y < vec.z then
return "z"
end
return "y"
end
if vec.x < vec.z then
return "z"
end
return "x"
end
function funcs.get_max_coords(pos)
if pos.x < pos.y then
if pos.y < pos.z then
return "z", "y", "x"
end
if pos.x < pos.z then
return "y", "z", "x"
end
return "y", "x", "z"
end
if pos.x < pos.z then
return "z", "x", "y"
end
if pos.y < pos.z then
return "x", "z", "y"
end
return "x", "y", "z"
end
function funcs.serialize(vec)
return "{x=" .. vec.x .. ",y=" .. vec.y .. ",z=" .. vec.z .. "}"
end
function funcs.triangle(pos1, pos2, pos3)
local normal = vector.cross(vector.subtract(pos2, pos1),
vector.subtract(pos3, pos1))
-- Find the biggest absolute component of the normal vector
local dir = vector.get_max_coord({
x = math.abs(normal.x),
y = math.abs(normal.y),
z = math.abs(normal.z),
})
-- Find the other directions for the for loops
local all_other_dirs = {
x = {"z", "y"},
y = {"z", "x"},
z = {"y", "x"},
}
local other_dirs = all_other_dirs[dir]
local odir1, odir2 = other_dirs[1], other_dirs[2]
local pos1_2d = {pos1[odir1], pos1[odir2]}
local pos2_2d = {pos2[odir1], pos2[odir2]}
local pos3_2d = {pos3[odir1], pos3[odir2]}
-- The boundaries of the 2D AABB along other_dirs
local p1 = {}
local p2 = {}
for i = 1,2 do
p1[i] = math.floor(math.min(pos1_2d[i], pos2_2d[i], pos3_2d[i]))
p2[i] = math.ceil(math.max(pos1_2d[i], pos2_2d[i], pos3_2d[i]))
end
-- https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/rasterization-stage
local function edgefunc(vert1, vert2, pos)
return (pos[1] - vert1[1]) * (vert2[2] - vert1[2])
- (pos[2] - vert1[2]) * (vert2[1] - vert1[1])
end
-- eps is used to prevend holes in neighbouring triangles
-- It should be smaller than the smallest possible barycentric value
-- FIXME: I'm not sure if it really does what it should.
local eps = 0.5 / math.max(p2[1] - p1[1], p2[2] - p1[2])
local a_all_inv = 1.0 / edgefunc(pos1_2d, pos2_2d, pos3_2d)
local step_k3 = - (pos2_2d[1] - pos1_2d[1]) * a_all_inv
local step_k1 = - (pos3_2d[1] - pos2_2d[1]) * a_all_inv
-- Calculate the triangle points
local points = {}
local barycentric_coords = {}
local n = 0
-- It is possible to further optimize this
for v1 = p1[1], p2[1] do
local p = {v1, p1[2]}
local k3 = edgefunc(pos1_2d, pos2_2d, p) * a_all_inv
local k1 = edgefunc(pos2_2d, pos3_2d, p) * a_all_inv
for _ = p1[2], p2[2] do
local k2 = 1 - k1 - k3
if k1 >= -eps and k2 >= -eps and k3 >= -eps then
-- On triangle
local h = math.floor(k1 * pos1[dir] + k2 * pos2[dir] +
k3 * pos3[dir] + 0.5)
n = n+1
points[n] = {[odir1] = v1, [odir2] = p[2], [dir] = h}
barycentric_coords[n] = {k1, k2, k3}
end
p[2] = p[2]+1
k3 = k3 + step_k3
k1 = k1 + step_k1
end
end
return points, n, barycentric_coords
end
for name,func in pairs(funcs) do
if vector[name] then
minetest.log("error", "[vector_extras] vector."..name..
" already exists.")
else
-- luacheck: globals vector
vector[name] = func
end
end