aboutsummaryrefslogtreecommitdiff
path: root/game/addons/zylann.hterrain/util/grid.gd
blob: b28e83b08c91203e35709b6bb8542bfb3ef3819d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
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