terrainlib_lua: Hardcode flow_local for performance

as it is unlikely that it will be changed one day.
This results in a drastic performance improvement (x4 speed for step 1)
This commit is contained in:
Gaël C 2024-01-18 18:37:48 +01:00
parent d00295600d
commit 0bc100030c
2 changed files with 25 additions and 43 deletions

View File

@ -134,7 +134,7 @@ local rivermapper = dofile(modpath .. "rivermapper.lua")
local gaussian = dofile(modpath .. "gaussian.lua")
local function flow(model)
model.dirs, model.lakes = rivermapper.flow_routing(model.dem, model.dirs, model.lakes, 'semirandom')
model.dirs, model.lakes = rivermapper.flow_routing(model.dem, model.dirs, model.lakes)
model.rivers = rivermapper.accumulate(model.dirs, model.rivers)
end

View File

@ -10,50 +10,18 @@
-- Big thanks to them for releasing this paper under a free license ! :)
-- The algorithm here makes use of most of the paper's concepts, including the Planar Borůvka algorithm.
-- Only flow_local and accumulate_flow are custom algorithms.
local function flow_local_semirandom(plist)
-- Determines how water should flow at 1 node scale.
-- The straightforward approach would be "Water will flow to the lowest of the 4 neighbours", but here water flows to one of the lower neighbours, chosen randomly, but probability depends on height difference.
-- This makes rivers better follow the curvature of the topography at large scale, and be less biased by pure N/E/S/W directions.
-- 'plist': array of downward height differences (0 if upward)
local sum = 0
for i=1, #plist do
sum = sum + plist[i] -- Sum of probabilities
end
if sum == 0 then
return 0
end
local r = math.random() * sum
for i=1, #plist do
local p = plist[i]
if r < p then
return i
end
r = r - p
end
return 0
end
-- Maybe implement more flow methods in the future?
local flow_methods = {
semirandom = flow_local_semirandom,
}
-- Applies all steps of the flow routing, to calculate flow direction for every node, and lake surface elevation.
-- It's quite a hard piece of code, but we will go step by step and explain what's going on, so stay with me and... let's goooooooo!
local function flow_routing(dem, dirs, lakes, method) -- 'dirs' and 'lakes' are optional tables to reuse for memory optimization, they may contain any data.
method = method or 'semirandom'
local flow_local = flow_methods[method] or flow_local_semirandom
local function flow_routing(dem, dirs, lakes) -- 'dirs' and 'lakes' are optional tables to reuse for memory optimization, they may contain any data.
dirs = dirs or {}
lakes = lakes or {}
-- Localize for performance
local tremove = table.remove
local mmax = math.max
local mrand = math.random
local X, Y = dem.X, dem.Y
dirs.X = X
@ -74,14 +42,29 @@ local function flow_routing(dem, dirs, lakes, method) -- 'dirs' and 'lakes' are
for y=1, Y do
for x=1, X do
local zi = dem[i]
local plist = { -- Get the height difference of the 4 neighbours (and 0 if uphill)
y<Y and mmax(zi-dem[i+X], 0) or 0, -- Southward
x<X and mmax(zi-dem[i+1], 0) or 0, -- Eastward
y>1 and mmax(zi-dem[i-X], 0) or 0, -- Northward
x>1 and mmax(zi-dem[i-1], 0) or 0, -- Westward
}
-- Determine how water should flow at 1 node scale.
-- The straightforward approach would be "Water will flow to the lowest of the 4 neighbours", but here water flows to one of the lower neighbours, chosen randomly, with probability depending on height difference.
-- This makes rivers better follow the curvature of the topography at large scale, and be less biased by pure N/E/S/W directions.
local pSouth = y<Y and mmax(zi-dem[i+X], 0) or 0
local pEast = x<X and mmax(zi-dem[i+1], 0) or 0
local pNorth = y>1 and mmax(zi-dem[i-X], 0) or 0
local pWest = x>1 and mmax(zi-dem[i-1], 0) or 0
local d = 0
local sum = pSouth + pEast + pNorth + pWest
local r = mrand() * sum
if sum > 0 then
if r < pSouth then
d = 1
elseif r-pSouth < pEast then
d = 2
elseif r-pSouth-pEast < pNorth then
d = 3
else
d = 4
end
end
local d = flow_local(plist)
-- 'dirs': Direction toward which water flow
-- 'dirs2': Directions from which water comes
dirs[i] = d
@ -438,5 +421,4 @@ end
return {
flow_routing = flow_routing,
accumulate = accumulate,
flow_methods = flow_methods,
}