203 lines
4.9 KiB
GDScript
203 lines
4.9 KiB
GDScript
|
|
# 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
|
|
|