@quenty/octree 11.8.1 → 11.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [11.8.2](https://github.com/Quenty/NevermoreEngine/compare/@quenty/octree@11.8.1...@quenty/octree@11.8.2) (2025-04-05)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Add types to packages ([2374fb2](https://github.com/Quenty/NevermoreEngine/commit/2374fb2b043cfbe0e9b507b3316eec46a4e353a0))
12
+
13
+
14
+
15
+
16
+
6
17
  ## [11.8.1](https://github.com/Quenty/NevermoreEngine/compare/@quenty/octree@11.8.0...@quenty/octree@11.8.1) (2025-03-21)
7
18
 
8
19
  **Note:** Version bump only for package @quenty/octree
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/octree",
3
- "version": "11.8.1",
3
+ "version": "11.8.2",
4
4
  "description": "Octree implementation for Roblox - fast spatial queries",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -26,11 +26,11 @@
26
26
  "Quenty"
27
27
  ],
28
28
  "dependencies": {
29
- "@quenty/draw": "^7.8.1",
30
- "@quenty/loader": "^10.8.0"
29
+ "@quenty/draw": "^7.8.2",
30
+ "@quenty/loader": "^10.8.1"
31
31
  },
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
- "gitHead": "6b7c3e15e60cdb185986207b574e2b5591261e7a"
35
+ "gitHead": "78c3ac0ab08dd18085b6e6e6e4f745e76ed99f68"
36
36
  }
@@ -87,10 +87,10 @@ end
87
87
  function Octree:GetAllNodes()
88
88
  local options = {}
89
89
 
90
- for _, regionList in pairs(self._regionHashMap) do
91
- for _, region in pairs(regionList) do
92
- for node, _ in pairs(region.nodes) do
93
- options[#options+1] = node
90
+ for _, regionList in self._regionHashMap do
91
+ for _, region in regionList do
92
+ for node, _ in region.nodes do
93
+ options[#options + 1] = node
94
94
  end
95
95
  end
96
96
  end
@@ -116,7 +116,7 @@ end
116
116
  @param object T
117
117
  @return OctreeNode<T>
118
118
  ]=]
119
- function Octree:CreateNode(position, object)
119
+ function Octree:CreateNode(position: Vector3, object: any)
120
120
  assert(typeof(position) == "Vector3", "Bad position value")
121
121
  assert(object, "Bad object value")
122
122
 
@@ -144,11 +144,11 @@ end
144
144
  @return { T } -- Objects found
145
145
  @return { number } -- Distances squared
146
146
  ]=]
147
- function Octree:RadiusSearch(position, radius)
147
+ function Octree:RadiusSearch(position: Vector3, radius: number)
148
148
  assert(typeof(position) == "Vector3", "Bad position")
149
149
  assert(type(radius) == "number", "Bad radius")
150
150
 
151
- local px, py, pz = position.x, position.y, position.z
151
+ local px, py, pz = position.X, position.Y, position.Z
152
152
  return self:_radiusSearch(px, py, pz, radius)
153
153
  end
154
154
 
@@ -164,18 +164,18 @@ end
164
164
  @return { any } -- Objects found
165
165
  @return { number } -- Distances squared
166
166
  ]=]
167
- function Octree:KNearestNeighborsSearch(position, k, radius)
167
+ function Octree:KNearestNeighborsSearch(position: Vector3, k: number, radius: number)
168
168
  assert(typeof(position) == "Vector3", "Bad position")
169
169
  assert(type(radius) == "number", "Bad radius")
170
170
 
171
- local px, py, pz = position.x, position.y, position.z
171
+ local px, py, pz = position.X, position.Y, position.Z
172
172
  local objects, nodeDistances2 = self:_radiusSearch(px, py, pz, radius)
173
173
 
174
174
  local sortable = {}
175
- for index, dist2 in pairs(nodeDistances2) do
175
+ for index, dist2 in nodeDistances2 do
176
176
  table.insert(sortable, {
177
- dist2 = dist2;
178
- index = index;
177
+ dist2 = dist2,
178
+ index = index,
179
179
  })
180
180
  end
181
181
 
@@ -203,28 +203,36 @@ end
203
203
  @param pz number
204
204
  @return OctreeSubregion
205
205
  ]=]
206
- function Octree:GetOrCreateLowestSubRegion(px, py, pz)
206
+ function Octree:GetOrCreateLowestSubRegion(px: number, py: number, pz: number)
207
207
  local region = self:_getOrCreateRegion(px, py, pz)
208
208
  return OctreeRegionUtils.getOrCreateSubRegionAtDepth(region, px, py, pz, self._maxDepth)
209
209
  end
210
210
 
211
- function Octree:_radiusSearch(px, py, pz, radius)
211
+ function Octree:_radiusSearch(px: number, py: number, pz: number, radius: number)
212
212
  local objectsFound = {}
213
213
  local nodeDistances2 = {}
214
214
 
215
215
  local diameter = self._maxRegionSize[1]
216
216
  local searchRadiusSquared = OctreeRegionUtils.getSearchRadiusSquared(radius, diameter, EPSILON)
217
217
 
218
- for _, regionList in pairs(self._regionHashMap) do
219
- for _, region in pairs(regionList) do
218
+ for _, regionList in self._regionHashMap do
219
+ for _, region in regionList do
220
220
  local rpos = region.position
221
221
  local rpx, rpy, rpz = rpos[1], rpos[2], rpos[3]
222
222
  local ox, oy, oz = px - rpx, py - rpy, pz - rpz
223
- local dist2 = ox*ox + oy*oy + oz*oz
223
+ local dist2 = ox * ox + oy * oy + oz * oz
224
224
 
225
225
  if dist2 <= searchRadiusSquared then
226
226
  OctreeRegionUtils.getNeighborsWithinRadius(
227
- region, radius, px, py, pz, objectsFound, nodeDistances2, self._maxDepth)
227
+ region,
228
+ radius,
229
+ px,
230
+ py,
231
+ pz,
232
+ objectsFound,
233
+ nodeDistances2,
234
+ self._maxDepth
235
+ )
228
236
  end
229
237
  end
230
238
  end
@@ -232,11 +240,11 @@ function Octree:_radiusSearch(px, py, pz, radius)
232
240
  return objectsFound, nodeDistances2
233
241
  end
234
242
 
235
- function Octree:_getRegion(px, py, pz)
243
+ function Octree:_getRegion(px: number, py: number, pz: number)
236
244
  return OctreeRegionUtils.findRegion(self._regionHashMap, self._maxRegionSize, px, py, pz)
237
245
  end
238
246
 
239
- function Octree:_getOrCreateRegion(px, py, pz)
247
+ function Octree:_getOrCreateRegion(px: number, py: number, pz: number)
240
248
  return OctreeRegionUtils.getOrCreateRegion(self._regionHashMap, self._maxRegionSize, px, py, pz)
241
249
  end
242
250
 
@@ -1,4 +1,5 @@
1
1
  --!native
2
+ --!strict
2
3
  --[=[
3
4
  Octree implementation utilities. Primarily this utility code
4
5
  should not be used directly and should be considered private to
@@ -14,39 +15,20 @@ local require = require(script.Parent.loader).load(script)
14
15
  local Draw = require("Draw")
15
16
 
16
17
  local EPSILON = 1e-6
17
- local SQRT_3_OVER_2 = math.sqrt(3)/2
18
+ local SQRT_3_OVER_2 = math.sqrt(3) / 2
18
19
  local SUB_REGION_POSITION_OFFSET = {
19
- { 0.25, 0.25, -0.25 };
20
- { -0.25, 0.25, -0.25 };
21
- { 0.25, 0.25, 0.25 };
22
- { -0.25, 0.25, 0.25 };
23
- { 0.25, -0.25, -0.25 };
24
- { -0.25, -0.25, -0.25 };
25
- { 0.25, -0.25, 0.25 };
26
- { -0.25, -0.25, 0.25 };
20
+ { 0.25, 0.25, -0.25 },
21
+ { -0.25, 0.25, -0.25 },
22
+ { 0.25, 0.25, 0.25 },
23
+ { -0.25, 0.25, 0.25 },
24
+ { 0.25, -0.25, -0.25 },
25
+ { -0.25, -0.25, -0.25 },
26
+ { 0.25, -0.25, 0.25 },
27
+ { -0.25, -0.25, 0.25 },
27
28
  }
28
29
 
29
30
  local OctreeRegionUtils = {}
30
31
 
31
- --[=[
32
- Visualizes the octree region.
33
-
34
- @param region OctreeRegion<T>
35
- @return MaidTask
36
- ]=]
37
- function OctreeRegionUtils.visualize(region)
38
- local size = region.size
39
- local position = region.position
40
- local sx, sy, sz = size[1], size[2], size[3]
41
- local px, py, pz = position[1], position[2], position[3]
42
-
43
- local box = Draw.box(Vector3.new(px, py, pz), Vector3.new(sx, sy, sz))
44
- box.Transparency = 0.9
45
- box.Name = "OctreeRegion_" .. tostring(region.depth)
46
-
47
- return box
48
- end
49
-
50
32
  --[=[
51
33
  A Vector3 equivalent for octrees. This type is primarily internal and
52
34
  used for faster access than a Vector3.
@@ -54,6 +36,12 @@ end
54
36
  @type OctreeVector3 { [1]: number, [2]: number, [3]: number }
55
37
  @within OctreeRegionUtils
56
38
  ]=]
39
+ export type OctreeVector3 = { number }
40
+
41
+ export type OctreeNode<T> = {
42
+ GetRawPosition: (self: OctreeNode<T>) -> (number, number, number),
43
+ GetObject: (self: OctreeNode<T>) -> T,
44
+ }
57
45
 
58
46
  --[=[
59
47
  An internal region which stores the data.
@@ -71,6 +59,39 @@ end
71
59
  .node_count number
72
60
  @within OctreeRegionUtils
73
61
  ]=]
62
+ export type OctreeRegion<T> = {
63
+ subRegions: { OctreeRegion<T> },
64
+ lowerBounds: OctreeVector3,
65
+ upperBounds: OctreeVector3,
66
+ position: OctreeVector3,
67
+ size: OctreeVector3,
68
+ parent: OctreeRegion<T>?,
69
+ parentIndex: number?,
70
+ depth: number,
71
+ nodes: { [OctreeNode<T>]: OctreeNode<T> },
72
+ node_count: number,
73
+ }
74
+
75
+ export type OctreeRegionHashMap<T> = { [number]: { OctreeRegion<T> } }
76
+
77
+ --[=[
78
+ Visualizes the octree region.
79
+
80
+ @param region OctreeRegion<T>
81
+ @return MaidTask
82
+ ]=]
83
+ function OctreeRegionUtils.visualize<T>(region: OctreeRegion<T>): Instance
84
+ local size = region.size
85
+ local position = region.position
86
+ local sx, sy, sz = size[1], size[2], size[3]
87
+ local px, py, pz = position[1], position[2], position[3]
88
+
89
+ local box = Draw.box(Vector3.new(px, py, pz), Vector3.new(sx, sy, sz))
90
+ box.Transparency = 0.9
91
+ box.Name = "OctreeRegion_" .. tostring(region.depth)
92
+
93
+ return box
94
+ end
74
95
 
75
96
  --[=[
76
97
  Creates a new OctreeRegion<T>
@@ -85,8 +106,17 @@ end
85
106
  @param parentIndex number?
86
107
  @return OctreeRegion<T>
87
108
  ]=]
88
- function OctreeRegionUtils.create(px, py, pz, sx, sy, sz, parent, parentIndex)
89
- local hsx, hsy, hsz = sx/2, sy/2, sz/2
109
+ function OctreeRegionUtils.create<T>(
110
+ px: number,
111
+ py: number,
112
+ pz: number,
113
+ sx: number,
114
+ sy: number,
115
+ sz: number,
116
+ parent: OctreeRegion<T>?,
117
+ parentIndex: number?
118
+ ): OctreeRegion<T>
119
+ local hsx, hsy, hsz = sx / 2, sy / 2, sz / 2
90
120
 
91
121
  local region = {
92
122
  subRegions = {
@@ -98,16 +128,16 @@ function OctreeRegionUtils.create(px, py, pz, sx, sy, sz, parent, parentIndex)
98
128
  --bottomNorthWest
99
129
  --bottomSouthEast
100
130
  --bottomSouthWest
101
- };
102
- lowerBounds = { px - hsx, py - hsy, pz - hsz };
103
- upperBounds = { px + hsx, py + hsy, pz + hsz };
104
- position = { px, py, pz };
105
- size = { sx, sy, sz }; -- { sx, sy, sz }
106
- parent = parent;
107
- depth = parent and (parent.depth + 1) or 1;
108
- parentIndex = parentIndex;
109
- nodes = {}; -- [node] = true (contains subchild nodes too)
110
- node_count = 0;
131
+ },
132
+ lowerBounds = { px - hsx, py - hsy, pz - hsz },
133
+ upperBounds = { px + hsx, py + hsy, pz + hsz },
134
+ position = { px, py, pz },
135
+ size = { sx, sy, sz }, -- { sx, sy, sz }
136
+ parent = parent,
137
+ depth = parent and (parent.depth + 1) or 1,
138
+ parentIndex = parentIndex,
139
+ nodes = {}, -- [node] = true (contains subchild nodes too)
140
+ node_count = 0,
111
141
  }
112
142
 
113
143
  -- if region.depth >= 5 then
@@ -122,16 +152,16 @@ end
122
152
  @param lowestSubregion OctreeRegion<T>
123
153
  @param node OctreeNode
124
154
  ]=]
125
- function OctreeRegionUtils.addNode(lowestSubregion, node)
155
+ function OctreeRegionUtils.addNode<T>(lowestSubregion: OctreeRegion<T>, node: OctreeNode<T>)
126
156
  assert(node, "Bad node")
127
157
 
128
- local current = lowestSubregion
158
+ local current: OctreeRegion<T> = lowestSubregion
129
159
  while current do
130
160
  if not current.nodes[node] then
131
- current.nodes[node] = node
161
+ current.nodes[node] = node :: OctreeNode<T>
132
162
  current.node_count = current.node_count + 1
133
163
  end
134
- current = current.parent
164
+ current = current.parent :: OctreeRegion<T>
135
165
  end
136
166
  end
137
167
 
@@ -142,12 +172,12 @@ end
142
172
  @param toLowest OctreeRegion<T>
143
173
  @param node OctreeNode
144
174
  ]=]
145
- function OctreeRegionUtils.moveNode(fromLowest, toLowest, node)
175
+ function OctreeRegionUtils.moveNode<T>(fromLowest: OctreeRegion<T>, toLowest: OctreeRegion<T>, node: OctreeNode<T>)
146
176
  assert(fromLowest.depth == toLowest.depth, "fromLowest.depth ~= toLowest.depth")
147
177
  assert(fromLowest ~= toLowest, "fromLowest == toLowest")
148
178
 
149
- local currentFrom = fromLowest
150
- local currentTo = toLowest
179
+ local currentFrom: OctreeRegion<T> = fromLowest
180
+ local currentTo: OctreeRegion<T> = toLowest
151
181
  while currentFrom ~= currentTo do
152
182
  -- remove from current
153
183
  do
@@ -155,13 +185,14 @@ function OctreeRegionUtils.moveNode(fromLowest, toLowest, node)
155
185
  assert(currentFrom.node_count > 0, "No nodes in currentFrom")
156
186
 
157
187
  currentFrom.nodes[node] = nil
158
- currentFrom.node_count = currentFrom.node_count - 1
188
+ currentFrom.node_count -= 1
159
189
 
160
190
  -- remove subregion!
161
- if currentFrom.node_count <= 0 and currentFrom.parentIndex then
191
+ local parentIndex = currentFrom.parentIndex
192
+ if currentFrom.node_count <= 0 and parentIndex then
162
193
  assert(currentFrom.parent, "Bad currentFrom.parent")
163
- assert(currentFrom.parent.subRegions[currentFrom.parentIndex] == currentFrom, "Not in subregion")
164
- currentFrom.parent.subRegions[currentFrom.parentIndex] = nil
194
+ assert(currentFrom.parent.subRegions[parentIndex] == currentFrom, "Not in subregion")
195
+ currentFrom.parent.subRegions[parentIndex] = nil
165
196
  end
166
197
  end
167
198
 
@@ -172,8 +203,8 @@ function OctreeRegionUtils.moveNode(fromLowest, toLowest, node)
172
203
  currentTo.node_count = currentTo.node_count + 1
173
204
  end
174
205
 
175
- currentFrom = currentFrom.parent
176
- currentTo = currentTo.parent
206
+ currentFrom = currentFrom.parent :: OctreeRegion<T>
207
+ currentTo = currentTo.parent :: OctreeRegion<T>
177
208
  end
178
209
  end
179
210
 
@@ -183,29 +214,29 @@ end
183
214
  @param lowestSubregion OctreeRegion<T>
184
215
  @param node OctreeNode
185
216
  ]=]
186
- function OctreeRegionUtils.removeNode(lowestSubregion, node)
217
+ function OctreeRegionUtils.removeNode<T>(lowestSubregion: OctreeRegion<T>, node: OctreeNode<T>)
187
218
  assert(node, "Bad node")
188
219
 
189
- local current = lowestSubregion
220
+ local current: OctreeRegion<T> = lowestSubregion
190
221
  while current do
191
222
  assert(current.nodes[node], "Not in current")
192
223
  assert(current.node_count > 0, "Current has bad node count")
193
224
 
194
225
  current.nodes[node] = nil
195
- current.node_count = current.node_count - 1
226
+ current.node_count -= 1
196
227
 
197
228
  -- remove subregion!
198
- if current.node_count <= 0 and current.parentIndex then
229
+ local parentIndex = current.parentIndex
230
+ if current.node_count <= 0 and parentIndex then
199
231
  assert(current.parent, "No parent")
200
- assert(current.parent.subRegions[current.parentIndex] == current, "Not in subregion")
201
- current.parent.subRegions[current.parentIndex] = nil
232
+ assert(current.parent.subRegions[parentIndex] == current, "Not in subregion")
233
+ current.parent.subRegions[parentIndex] = nil
202
234
  end
203
235
 
204
- current = current.parent
236
+ current = current.parent :: OctreeRegion<T>
205
237
  end
206
238
  end
207
239
 
208
-
209
240
  --[=[
210
241
  Retrieves the search radius for a given radius given the region
211
242
  diameter
@@ -215,10 +246,10 @@ end
215
246
  @param epsilon number
216
247
  @return number
217
248
  ]=]
218
- function OctreeRegionUtils.getSearchRadiusSquared(radius, diameter, epsilon)
219
- local diagonal = SQRT_3_OVER_2*diameter
249
+ function OctreeRegionUtils.getSearchRadiusSquared(radius: number, diameter: number, epsilon: number): number
250
+ local diagonal = SQRT_3_OVER_2 * diameter
220
251
  local searchRadius = radius + diagonal
221
- return searchRadius*searchRadius + epsilon
252
+ return searchRadius * searchRadius + epsilon
222
253
  end
223
254
 
224
255
  -- luacheck: push ignore
@@ -237,30 +268,39 @@ end
237
268
  @param nodeDistances2 { number }
238
269
  @param maxDepth number
239
270
  ]=]
240
- function OctreeRegionUtils.getNeighborsWithinRadius(region, radius, px, py, pz, objectsFound, nodeDistances2, maxDepth)
241
- -- luacheck: pop
271
+ function OctreeRegionUtils.getNeighborsWithinRadius<T>(
272
+ region: OctreeRegion<T>,
273
+ radius: number,
274
+ px: number,
275
+ py: number,
276
+ pz: number,
277
+ objectsFound: { T },
278
+ nodeDistances2: { number },
279
+ maxDepth: number
280
+ )
281
+ -- luacheck: pop
242
282
  assert(maxDepth, "Bad maxDepth")
243
283
 
244
- local childDiameter = region.size[1]/2
284
+ local childDiameter = region.size[1] / 2
245
285
  local searchRadiusSquared = OctreeRegionUtils.getSearchRadiusSquared(radius, childDiameter, EPSILON)
246
286
 
247
- local radiusSquared = radius*radius
287
+ local radiusSquared = radius * radius
248
288
 
249
289
  -- for each child
250
- for _, childRegion in pairs(region.subRegions) do
290
+ for _, childRegion in region.subRegions do
251
291
  local cposition = childRegion.position
252
292
  local cpx, cpy, cpz = cposition[1], cposition[2], cposition[3]
253
293
 
254
294
  local ox, oy, oz = px - cpx, py - cpy, pz - cpz
255
- local dist2 = ox*ox + oy*oy + oz*oz
295
+ local dist2 = ox * ox + oy * oy + oz * oz
256
296
 
257
297
  -- within search radius
258
298
  if dist2 <= searchRadiusSquared then
259
299
  if childRegion.depth == maxDepth then
260
- for node, _ in pairs(childRegion.nodes) do
300
+ for node, _ in childRegion.nodes do
261
301
  local npx, npy, npz = node:GetRawPosition()
262
302
  local nox, noy, noz = px - npx, py - npy, pz - npz
263
- local ndist2 = nox*nox + noy*noy + noz*noz
303
+ local ndist2 = nox * nox + noy * noy + noz * noz
264
304
  if ndist2 <= radiusSquared then
265
305
  objectsFound[#objectsFound + 1] = node:GetObject()
266
306
  nodeDistances2[#nodeDistances2 + 1] = ndist2
@@ -268,7 +308,15 @@ function OctreeRegionUtils.getNeighborsWithinRadius(region, radius, px, py, pz,
268
308
  end
269
309
  else
270
310
  OctreeRegionUtils.getNeighborsWithinRadius(
271
- childRegion, radius, px, py, pz, objectsFound, nodeDistances2, maxDepth)
311
+ childRegion,
312
+ radius,
313
+ px,
314
+ py,
315
+ pz,
316
+ objectsFound,
317
+ nodeDistances2,
318
+ maxDepth
319
+ )
272
320
  end
273
321
  end
274
322
  end
@@ -285,7 +333,13 @@ end
285
333
  @param maxDepth number
286
334
  @return OctreeRegion<T>
287
335
  ]=]
288
- function OctreeRegionUtils.getOrCreateSubRegionAtDepth(region, px, py, pz, maxDepth)
336
+ function OctreeRegionUtils.getOrCreateSubRegionAtDepth<T>(
337
+ region: OctreeRegion<T>,
338
+ px: number,
339
+ py: number,
340
+ pz: number,
341
+ maxDepth: number
342
+ ): OctreeRegion<T>
289
343
  local current = region
290
344
  for _ = region.depth, maxDepth do
291
345
  local index = OctreeRegionUtils.getSubRegionIndex(current, px, py, pz)
@@ -309,15 +363,15 @@ end
309
363
  @param parentIndex number
310
364
  @return OctreeRegion<T>
311
365
  ]=]
312
- function OctreeRegionUtils.createSubRegion(parentRegion, parentIndex)
366
+ function OctreeRegionUtils.createSubRegion<T>(parentRegion: OctreeRegion<T>, parentIndex: number): OctreeRegion<T>
313
367
  local size = parentRegion.size
314
368
  local position = parentRegion.position
315
369
  local multiplier = SUB_REGION_POSITION_OFFSET[parentIndex]
316
370
 
317
- local px = position[1] + multiplier[1]*size[1]
318
- local py = position[2] + multiplier[2]*size[2]
319
- local pz = position[3] + multiplier[3]*size[3]
320
- local sx, sy, sz = size[1]/2, size[2]/2, size[3]/2
371
+ local px = position[1] + multiplier[1] * size[1]
372
+ local py = position[2] + multiplier[2] * size[2]
373
+ local pz = position[3] + multiplier[3] * size[3]
374
+ local sx, sy, sz = size[1] / 2, size[2] / 2, size[3] / 2
321
375
 
322
376
  return OctreeRegionUtils.create(px, py, pz, sx, sy, sz, parentRegion, parentIndex)
323
377
  end
@@ -333,13 +387,16 @@ end
333
387
  @param pz number
334
388
  @return boolean
335
389
  ]=]
336
- function OctreeRegionUtils.inRegionBounds(region, px, py, pz)
390
+ function OctreeRegionUtils.inRegionBounds<T>(region: OctreeRegion<T>, px: number, py: number, pz: number): boolean
337
391
  local lowerBounds = region.lowerBounds
338
392
  local upperBounds = region.upperBounds
339
393
  return (
340
- px >= lowerBounds[1] and px <= upperBounds[1] and
341
- py >= lowerBounds[2] and py <= upperBounds[2] and
342
- pz >= lowerBounds[3] and pz <= upperBounds[3]
394
+ px >= lowerBounds[1]
395
+ and px <= upperBounds[1]
396
+ and py >= lowerBounds[2]
397
+ and py <= upperBounds[2]
398
+ and pz >= lowerBounds[3]
399
+ and pz <= upperBounds[3]
343
400
  )
344
401
  end
345
402
 
@@ -352,7 +409,7 @@ end
352
409
  @param pz number
353
410
  @return number
354
411
  ]=]
355
- function OctreeRegionUtils.getSubRegionIndex(region, px, py, pz)
412
+ function OctreeRegionUtils.getSubRegionIndex<T>(region: OctreeRegion<T>, px: number, py: number, pz: number): number
356
413
  local index = px > region.position[1] and 1 or 2
357
414
  if py <= region.position[2] then
358
415
  index = index + 4
@@ -374,9 +431,9 @@ end
374
431
  @param cz number
375
432
  @return number
376
433
  ]=]
377
- function OctreeRegionUtils.getTopLevelRegionHash(cx, cy, cz)
434
+ function OctreeRegionUtils.getTopLevelRegionHash(cx: number, cy: number, cz: number): number
378
435
  -- Normally you would modulus this to hash table size, but we want as flat of a structure as possible
379
- return cx * 73856093 + cy*19351301 + cz*83492791
436
+ return cx * 73856093 + cy * 19351301 + cz * 83492791
380
437
  end
381
438
 
382
439
  --[=[
@@ -390,7 +447,12 @@ end
390
447
  @return number -- rpy
391
448
  @return number -- rpz
392
449
  ]=]
393
- function OctreeRegionUtils.getTopLevelRegionCellIndex(maxRegionSize, px, py, pz)
450
+ function OctreeRegionUtils.getTopLevelRegionCellIndex(
451
+ maxRegionSize: OctreeVector3,
452
+ px: number,
453
+ py: number,
454
+ pz: number
455
+ ): (number, number, number)
394
456
  return math.floor(px / maxRegionSize[1] + 0.5),
395
457
  math.floor(py / maxRegionSize[2] + 0.5),
396
458
  math.floor(pz / maxRegionSize[3] + 0.5)
@@ -407,10 +469,13 @@ end
407
469
  @return number
408
470
  @return number
409
471
  ]=]
410
- function OctreeRegionUtils.getTopLevelRegionPosition(maxRegionSize, cx, cy, cz)
411
- return maxRegionSize[1] * cx,
412
- maxRegionSize[2] * cy,
413
- maxRegionSize[3] * cz
472
+ function OctreeRegionUtils.getTopLevelRegionPosition(
473
+ maxRegionSize: OctreeVector3,
474
+ cx: number,
475
+ cy: number,
476
+ cz: number
477
+ ): (number, number, number)
478
+ return maxRegionSize[1] * cx, maxRegionSize[2] * cy, maxRegionSize[3] * cz
414
479
  end
415
480
 
416
481
  --[=[
@@ -423,11 +488,9 @@ end
423
488
  @param rpz number
424
489
  @return boolean
425
490
  ]=]
426
- function OctreeRegionUtils.areEqualTopRegions(region, rpx, rpy, rpz)
491
+ function OctreeRegionUtils.areEqualTopRegions<T>(region: OctreeRegion<T>, rpx: number, rpy: number, rpz: number)
427
492
  local position = region.position
428
- return position[1] == rpx
429
- and position[2] == rpy
430
- and position[3] == rpz
493
+ return position[1] == rpx and position[2] == rpy and position[3] == rpz
431
494
  end
432
495
 
433
496
  --[=[
@@ -440,17 +503,23 @@ end
440
503
  @param pz number
441
504
  @return OctreeRegion3?
442
505
  ]=]
443
- function OctreeRegionUtils.findRegion(regionHashMap, maxRegionSize, px, py, pz)
506
+ function OctreeRegionUtils.findRegion<T>(
507
+ regionHashMap: OctreeRegionHashMap<T>,
508
+ maxRegionSize: OctreeVector3,
509
+ px: number,
510
+ py: number,
511
+ pz: number
512
+ ): OctreeRegion<T>?
444
513
  local cx, cy, cz = OctreeRegionUtils.getTopLevelRegionCellIndex(maxRegionSize, px, py, pz)
445
514
  local hash = OctreeRegionUtils.getTopLevelRegionHash(cx, cy, cz)
446
515
 
447
- local regionList = regionHashMap[hash]
516
+ local regionList: { OctreeRegion<T> } = regionHashMap[hash]
448
517
  if not regionList then
449
518
  return nil
450
519
  end
451
520
 
452
521
  local rpx, rpy, rpz = OctreeRegionUtils.getTopLevelRegionPosition(maxRegionSize, cx, cy, cz)
453
- for _, region in pairs(regionList) do
522
+ for _, region in regionList do
454
523
  if OctreeRegionUtils.areEqualTopRegions(region, rpx, rpy, rpz) then
455
524
  return region
456
525
  end
@@ -469,7 +538,7 @@ end
469
538
  @param pz number
470
539
  @return OctreeRegion<T>
471
540
  ]=]
472
- function OctreeRegionUtils.getOrCreateRegion(regionHashMap, maxRegionSize, px, py, pz)
541
+ function OctreeRegionUtils.getOrCreateRegion<T>(regionHashMap: OctreeRegionHashMap<T>, maxRegionSize: OctreeVector3, px: number, py: never, pz: number): OctreeRegion<T>
473
542
  local cx, cy, cz = OctreeRegionUtils.getTopLevelRegionCellIndex(maxRegionSize, px, py, pz)
474
543
  local hash = OctreeRegionUtils.getTopLevelRegionHash(cx, cy, cz)
475
544
 
@@ -480,7 +549,7 @@ function OctreeRegionUtils.getOrCreateRegion(regionHashMap, maxRegionSize, px, p
480
549
  end
481
550
 
482
551
  local rpx, rpy, rpz = OctreeRegionUtils.getTopLevelRegionPosition(maxRegionSize, cx, cy, cz)
483
- for _, region in pairs(regionList) do
552
+ for _, region in regionList do
484
553
  if OctreeRegionUtils.areEqualTopRegions(region, rpx, rpy, rpz) then
485
554
  return region
486
555
  end