diff options
Diffstat (limited to 'game/addons/zylann.hterrain/util/grid.gd')
-rw-r--r-- | game/addons/zylann.hterrain/util/grid.gd | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/game/addons/zylann.hterrain/util/grid.gd b/game/addons/zylann.hterrain/util/grid.gd new file mode 100644 index 0000000..b28e83b --- /dev/null +++ b/game/addons/zylann.hterrain/util/grid.gd @@ -0,0 +1,203 @@ + +# Note: `tool` is optional but without it there are no error reporting in the editor +@tool + +# TODO Remove grid_ prefixes, context is already given by the script itself + + +# Performs a positive integer division rounded to upper (4/2 = 2, 5/3 = 2) +static func up_div(a: int, b: int): + if a % b != 0: + return a / b + 1 + return a / b + + +# Creates a 2D array as an array of arrays. +# if v is provided, all cells will contain the same value. +# if v is a funcref, it will be executed to fill the grid cell per cell. +static func create_grid(w: int, h: int, v=null): + var is_create_func = typeof(v) == TYPE_CALLABLE + var grid := [] + grid.resize(h) + for y in range(grid.size()): + var row := [] + row.resize(w) + if is_create_func: + for x in range(row.size()): + row[x] = v.call(x, y) + else: + for x in range(row.size()): + row[x] = v + grid[y] = row + return grid + + +# Creates a 2D array that is a copy of another 2D array +static func clone_grid(other_grid): + var grid := [] + grid.resize(other_grid.size()) + for y in range(0, grid.size()): + var row := [] + var other_row = other_grid[y] + row.resize(other_row.size()) + grid[y] = row + for x in range(0, row.size()): + row[x] = other_row[x] + return grid + + +# Resizes a 2D array and allows to set or call functions for each deleted and created cells. +# This is especially useful if cells contain objects and you don't want to loose existing data. +static func resize_grid(grid, new_width, new_height, create_func=null, delete_func=null): + # Check parameters + assert(new_width >= 0 and new_height >= 0) + assert(grid != null) + if delete_func != null: + assert(typeof(delete_func) == TYPE_CALLABLE) + # `create_func` can also be a default value + var is_create_func = typeof(create_func) == TYPE_CALLABLE + + # Get old size (supposed to be rectangular!) + var old_height = grid.size() + var old_width = 0 + if grid.size() != 0: + old_width = grid[0].size() + + # Delete old rows + if new_height < old_height: + if delete_func != null: + for y in range(new_height, grid.size()): + var row = grid[y] + for x in len(row): + var elem = row[x] + delete_func.call(elem) + grid.resize(new_height) + + # Delete old columns + if new_width < old_width: + for y in len(grid): + var row = grid[y] + if delete_func != null: + for x in range(new_width, row.size()): + var elem = row[x] + delete_func.call(elem) + row.resize(new_width) + + # Create new columns + if new_width > old_width: + for y in len(grid): + var row = grid[y] + row.resize(new_width) + if is_create_func: + for x in range(old_width, new_width): + row[x] = create_func.call(x,y) + else: + for x in range(old_width, new_width): + row[x] = create_func + + # Create new rows + if new_height > old_height: + grid.resize(new_height) + for y in range(old_height, new_height): + var row = [] + row.resize(new_width) + grid[y] = row + if is_create_func: + for x in new_width: + row[x] = create_func.call(x,y) + else: + for x in new_width: + row[x] = create_func + + # Debug test check + assert(grid.size() == new_height) + for y in len(grid): + assert(len(grid[y]) == new_width) + + +# Retrieves the minimum and maximum values from a grid +static func grid_min_max(grid): + if grid.size() == 0 or grid[0].size() == 0: + return [0,0] + var vmin = grid[0][0] + var vmax = vmin + for y in len(grid): + var row = grid[y] + for x in len(row): + var v = row[x] + if v > vmax: + vmax = v + elif v < vmin: + vmin = v + return [vmin, vmax] + + +# Copies a sub-region of a grid as a new grid. No boundary check! +static func grid_extract_area(src_grid, x0, y0, w, h): + var dst = create_grid(w, h) + for y in h: + var dst_row = dst[y] + var src_row = src_grid[y0+y] + for x in w: + dst_row[x] = src_row[x0+x] + return dst + + +# Extracts data and crops the result if the requested rect crosses the bounds +static func grid_extract_area_safe_crop(src_grid, x0, y0, w, h): + # Return empty is completely out of bounds + var gw = src_grid.size() + if gw == 0: + return [] + var gh = src_grid[0].size() + if x0 >= gw or y0 >= gh: + return [] + + # Crop min pos + if x0 < 0: + w += x0 + x0 = 0 + if y0 < 0: + h += y0 + y0 = 0 + + # Crop max pos + if x0 + w >= gw: + w = gw-x0 + if y0 + h >= gh: + h = gh-y0 + + return grid_extract_area(src_grid, x0, y0, w, h) + + +# Sets values from a grid inside another grid. No boundary check! +static func grid_paste(src_grid, dst_grid, x0, y0): + for y in range(0, src_grid.size()): + var src_row = src_grid[y] + var dst_row = dst_grid[y0+y] + for x in range(0, src_row.size()): + dst_row[x0+x] = src_row[x] + + +# Tests if two grids are the same size and contain the same values +static func grid_equals(a, b): + if a.size() != b.size(): + return false + for y in a.size(): + var a_row = a[y] + var b_row = b[y] + if a_row.size() != b_row.size(): + return false + for x in b_row.size(): + if a_row[x] != b_row[x]: + return false + return true + + +static func grid_get_or_default(grid, x, y, defval=null): + if y >= 0 and y < len(grid): + var row = grid[y] + if x >= 0 and x < len(row): + return row[x] + return defval + |