diff --git a/heightmap.lua b/heightmap.lua index e132283..1e7ae42 100644 --- a/heightmap.lua +++ b/heightmap.lua @@ -7,44 +7,44 @@ module(...) -- Find power of two sufficient for size local function pot(size) - local pot = 2 - while true do - if size <= pot then return pot end - pot = 2*pot - end + local pot = 2 + while true do + if size <= pot then return pot end + pot = 2*pot + end end -- Create a table with 0 to n zero values local function tcreate(n) - local t = {} - for i = 0, n do t[i] = 0 end - return t + local t = {} + for i = 0, n do t[i] = 0 end + return t end -- Square step -- Sets map[x][y] from square of radius d using height function f local function square(map, x, y, d, f) - local sum, num = 0, 0 - if 0 <= x-d then - if 0 <= y-d then sum, num = sum + map[x-d][y-d], num + 1 end - if y+d <= map.h then sum, num = sum + map[x-d][y+d], num + 1 end - end - if x+d <= map.w then - if 0 <= y-d then sum, num = sum + map[x+d][y-d], num + 1 end - if y+d <= map.h then sum, num = sum + map[x+d][y+d], num + 1 end - end - map[x][y] = f(map, x, y, d, sum/num) + local sum, num = 0, 0 + if 0 <= x-d then + if 0 <= y-d then sum, num = sum + map[x-d][y-d], num + 1 end + if y+d <= map.h then sum, num = sum + map[x-d][y+d], num + 1 end + end + if x+d <= map.w then + if 0 <= y-d then sum, num = sum + map[x+d][y-d], num + 1 end + if y+d <= map.h then sum, num = sum + map[x+d][y+d], num + 1 end + end + map[x][y] = f(map, x, y, d, sum/num) end -- Diamond step -- Sets map[x][y] from diamond of radius d using height function f local function diamond(map, x, y, d, f) - local sum, num = 0, 0 - if 0 <= x-d then sum, num = sum + map[x-d][y], num + 1 end - if x+d <= map.w then sum, num = sum + map[x+d][y], num + 1 end - if 0 <= y-d then sum, num = sum + map[x][y-d], num + 1 end - if y+d <= map.h then sum, num = sum + map[x][y+d], num + 1 end - map[x][y] = f(map, x, y, d, sum/num) + local sum, num = 0, 0 + if 0 <= x-d then sum, num = sum + map[x-d][y], num + 1 end + if x+d <= map.w then sum, num = sum + map[x+d][y], num + 1 end + if 0 <= y-d then sum, num = sum + map[x][y-d], num + 1 end + if y+d <= map.h then sum, num = sum + map[x][y+d], num + 1 end + map[x][y] = f(map, x, y, d, sum/num) end -- Diamond square algorithm generates cloud/plasma fractal heightmap @@ -52,36 +52,36 @@ end -- Size must be power of two -- Height function f must look like f(map, x, y, d, h) and return h' local function diamondsquare(size, f) - -- create map - local map = { w = size, h = size } - for c = 0, size do map[c] = tcreate(size) end - -- seed four corners - local d = size - map[0][0] = f(map, 0, 0, d, 0) - map[0][d] = f(map, 0, d, d, 0) - map[d][0] = f(map, d, 0, d, 0) - map[d][d] = f(map, d, d, d, 0) - d = d/2 - -- perform square and diamond steps - while 1 <= d do - for x = d, map.w-1, 2*d do - for y = d, map.h-1, 2*d do - square(map, x, y, d, f) - end - end - for x = d, map.w-1, 2*d do - for y = 0, map.h, 2*d do - diamond(map, x, y, d, f) - end - end - for x = 0, map.w, 2*d do - for y = d, map.h-1, 2*d do - diamond(map, x, y, d, f) - end - end - d = d/2 - end - return map + -- create map + local map = { w = size, h = size } + for c = 0, size do map[c] = tcreate(size) end + -- seed four corners + local d = size + map[0][0] = f(map, 0, 0, d, 0) + map[0][d] = f(map, 0, d, d, 0) + map[d][0] = f(map, d, 0, d, 0) + map[d][d] = f(map, d, d, d, 0) + d = d/2 + -- perform square and diamond steps + while 1 <= d do + for x = d, map.w-1, 2*d do + for y = d, map.h-1, 2*d do + square(map, x, y, d, f) + end + end + for x = d, map.w-1, 2*d do + for y = 0, map.h, 2*d do + diamond(map, x, y, d, f) + end + end + for x = 0, map.w, 2*d do + for y = d, map.h-1, 2*d do + diamond(map, x, y, d, f) + end + end + d = d/2 + end + return map end -- Default height function @@ -89,18 +89,18 @@ end -- h is mean height at map[x][y] (from square/diamond of radius d) -- returns h' which is used to set map[x][y] function defaultf(map, x, y, d, h) - return h + (random()-0.5)*d + return h + (random()-0.5)*d end -- Create a heightmap using the specified height function (or default) -- map[x][y] where x from 0 to map.w and y from 0 to map.h function create(width, height, f) - f = f and f or defaultf - -- make heightmap - local map = diamondsquare(pot(max(width, height)), f) - -- clip heightmap to desired size - for x = 0, map.w do for y = height+1, map.h do map[x][y] = nil end end - for x = width+1, map.w do map[x] = nil end - map.w, map.h = width, height - return map + f = f and f or defaultf + -- make heightmap + local map = diamondsquare(pot(max(width, height)), f) + -- clip heightmap to desired size + for x = 0, map.w do for y = height+1, map.h do map[x][y] = nil end end + for x = width+1, map.w do map[x] = nil end + map.w, map.h = width, height + return map end