com.wallstop-studios.unity-helpers 2.0.0-rc76.1 → 2.0.0-rc76.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/Editor/Sprites/SpriteCropper.cs +9 -2
- package/Editor/Sprites/SpritePivotAdjustor.cs +259 -0
- package/Editor/Sprites/SpritePivotAdjustor.cs.meta +3 -0
- package/Runtime/Core/DataStructure/Circle.cs +1 -1
- package/Runtime/Core/DataStructure/CyclicBuffer.cs +65 -2
- package/Runtime/Core/DataStructure/ISpatialTree.cs +7 -46
- package/Runtime/Core/DataStructure/KDTree.cs +266 -130
- package/Runtime/Core/DataStructure/QuadTree.cs +258 -128
- package/Runtime/Core/DataStructure/Trie.cs +359 -0
- package/Runtime/Core/DataStructure/Trie.cs.meta +3 -0
- package/Runtime/Core/Helper/UnityMainThreadDispatcher.cs +3 -1
- package/Runtime/Core/Threading/SingleThreadedThreadPool.cs +7 -2
- package/Runtime/UI/LayeredImage.cs +0 -1
- package/Tests/Runtime/DataStructures/SpatialTreeTests.cs +6 -6
- package/Tests/Runtime/Performance/QuadTreePerformanceTests.cs +1 -1
- package/Tests/Runtime/Performance/SpatialTreePerformanceTest.cs +23 -18
- package/package.json +2 -1
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
namespace WallstopStudios.UnityHelpers.Core.DataStructure
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using System.Linq;
|
|
6
|
+
using System.Text;
|
|
7
|
+
using UnityEngine;
|
|
8
|
+
|
|
9
|
+
/// <summary>
|
|
10
|
+
/// A highly optimized, array-backed Trie implementation for fast prefix search and exact word lookup.
|
|
11
|
+
/// Preallocates storage based on total characters in the input set and uses integer indices for traversal,
|
|
12
|
+
/// minimizing memory allocations and indirections. Provides allocation-free prefix search method (aside from
|
|
13
|
+
/// returned string allocations).
|
|
14
|
+
/// </summary>
|
|
15
|
+
public sealed class Trie
|
|
16
|
+
{
|
|
17
|
+
private const int Poison = -1;
|
|
18
|
+
|
|
19
|
+
private readonly char[] _chars;
|
|
20
|
+
private readonly int[] _firstChild;
|
|
21
|
+
private readonly int[] _nextSibling;
|
|
22
|
+
private readonly bool[] _isWord;
|
|
23
|
+
private readonly StringBuilder _stringBuilder;
|
|
24
|
+
private int _nodeCount;
|
|
25
|
+
|
|
26
|
+
/// <summary>
|
|
27
|
+
/// Constructs the Trie from the provided collection of words.
|
|
28
|
+
/// </summary>
|
|
29
|
+
/// <param name="words">All possible words to insert into the Trie.</param>
|
|
30
|
+
public Trie(IEnumerable<string> words)
|
|
31
|
+
{
|
|
32
|
+
IReadOnlyList<string> wordList = words as IReadOnlyList<string> ?? words.ToList();
|
|
33
|
+
int maxWordLength;
|
|
34
|
+
if (wordList.Count > 0)
|
|
35
|
+
{
|
|
36
|
+
maxWordLength = wordList[0].Length;
|
|
37
|
+
for (int i = 1; i < wordList.Count; ++i)
|
|
38
|
+
{
|
|
39
|
+
maxWordLength = Mathf.Max(maxWordLength, wordList[i].Length);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
{
|
|
44
|
+
maxWordLength = 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
int capacity = 1;
|
|
48
|
+
for (int i = 0; i < wordList.Count; ++i)
|
|
49
|
+
{
|
|
50
|
+
capacity += wordList[i].Length;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
_chars = new char[capacity];
|
|
54
|
+
_firstChild = new int[capacity];
|
|
55
|
+
_nextSibling = new int[capacity];
|
|
56
|
+
_isWord = new bool[capacity];
|
|
57
|
+
|
|
58
|
+
Array.Fill(_firstChild, Poison);
|
|
59
|
+
Array.Fill(_nextSibling, Poison);
|
|
60
|
+
|
|
61
|
+
_stringBuilder = new StringBuilder(maxWordLength);
|
|
62
|
+
|
|
63
|
+
_nodeCount = 1; // root node index
|
|
64
|
+
for (int i = 0; i < wordList.Count; ++i)
|
|
65
|
+
{
|
|
66
|
+
string word = wordList[i];
|
|
67
|
+
Insert(word);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Inserts a single word into the Trie
|
|
72
|
+
private void Insert(string word)
|
|
73
|
+
{
|
|
74
|
+
int node = 0;
|
|
75
|
+
foreach (char c in word)
|
|
76
|
+
{
|
|
77
|
+
int prev = Poison;
|
|
78
|
+
int child = _firstChild[node];
|
|
79
|
+
while (child != Poison && _chars[child] != c)
|
|
80
|
+
{
|
|
81
|
+
prev = child;
|
|
82
|
+
child = _nextSibling[child];
|
|
83
|
+
}
|
|
84
|
+
if (child == Poison)
|
|
85
|
+
{
|
|
86
|
+
child = _nodeCount++;
|
|
87
|
+
_chars[child] = c;
|
|
88
|
+
_firstChild[child] = Poison;
|
|
89
|
+
_nextSibling[child] = Poison;
|
|
90
|
+
if (prev == Poison)
|
|
91
|
+
{
|
|
92
|
+
_firstChild[node] = child;
|
|
93
|
+
}
|
|
94
|
+
else
|
|
95
|
+
{
|
|
96
|
+
_nextSibling[prev] = child;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
node = child;
|
|
100
|
+
}
|
|
101
|
+
_isWord[node] = true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// <summary>
|
|
105
|
+
/// Determines whether the exact word exists in the Trie.
|
|
106
|
+
/// </summary>
|
|
107
|
+
public bool Contains(string word)
|
|
108
|
+
{
|
|
109
|
+
int node = 0;
|
|
110
|
+
foreach (char c in word)
|
|
111
|
+
{
|
|
112
|
+
int child = _firstChild[node];
|
|
113
|
+
while (child != Poison && _chars[child] != c)
|
|
114
|
+
{
|
|
115
|
+
child = _nextSibling[child];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (child == Poison)
|
|
119
|
+
{
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
node = child;
|
|
124
|
+
}
|
|
125
|
+
return _isWord[node];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// <summary>
|
|
129
|
+
/// Collects up to maxResults words that start with the given prefix.
|
|
130
|
+
/// Results are added into the provided list (which is cleared at the start).
|
|
131
|
+
/// Returns the number of results added.
|
|
132
|
+
/// </summary>
|
|
133
|
+
public int GetWordsWithPrefix(
|
|
134
|
+
string prefix,
|
|
135
|
+
List<string> results,
|
|
136
|
+
int maxResults = int.MaxValue
|
|
137
|
+
)
|
|
138
|
+
{
|
|
139
|
+
results.Clear();
|
|
140
|
+
int node = 0;
|
|
141
|
+
foreach (char c in prefix)
|
|
142
|
+
{
|
|
143
|
+
int child = _firstChild[node];
|
|
144
|
+
while (child != Poison && _chars[child] != c)
|
|
145
|
+
{
|
|
146
|
+
child = _nextSibling[child];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (child == Poison)
|
|
150
|
+
{
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
node = child;
|
|
155
|
+
}
|
|
156
|
+
_stringBuilder.Clear();
|
|
157
|
+
_stringBuilder.Append(prefix);
|
|
158
|
+
Collect(node, results, maxResults);
|
|
159
|
+
return results.Count;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Recursive collection without allocations
|
|
163
|
+
private void Collect(int node, List<string> results, int maxResults)
|
|
164
|
+
{
|
|
165
|
+
if (results.Count >= maxResults)
|
|
166
|
+
{
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (_isWord[node])
|
|
171
|
+
{
|
|
172
|
+
results.Add(_stringBuilder.ToString());
|
|
173
|
+
if (results.Count >= maxResults)
|
|
174
|
+
{
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
for (int child = _firstChild[node]; child != Poison; child = _nextSibling[child])
|
|
179
|
+
{
|
|
180
|
+
_stringBuilder.Append(_chars[child]);
|
|
181
|
+
Collect(child, results, maxResults);
|
|
182
|
+
_stringBuilder.Length--;
|
|
183
|
+
if (results.Count >= maxResults)
|
|
184
|
+
{
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/// <summary>
|
|
192
|
+
/// A highly optimized, array-backed generic Trie for mapping string keys to values of type T.
|
|
193
|
+
/// Preallocates storage based on total characters in the key set and uses integer indices for traversal,
|
|
194
|
+
/// minimizing memory allocations and indirections. Provides allocation-free prefix search (aside from
|
|
195
|
+
/// the output list allocations themselves).
|
|
196
|
+
/// </summary>
|
|
197
|
+
public sealed class Trie<T>
|
|
198
|
+
{
|
|
199
|
+
private const int Poison = -1;
|
|
200
|
+
|
|
201
|
+
private readonly char[] _chars;
|
|
202
|
+
private readonly int[] _firstChild;
|
|
203
|
+
private readonly int[] _nextSibling;
|
|
204
|
+
private readonly bool[] _hasValue;
|
|
205
|
+
private readonly T[] _values;
|
|
206
|
+
private int _nodeCount;
|
|
207
|
+
|
|
208
|
+
/// <summary>
|
|
209
|
+
/// Constructs the Trie from the provided dictionary of keys to values.
|
|
210
|
+
/// </summary>
|
|
211
|
+
/// <param name="items">Mapping from unique string keys to values of type T.</param>
|
|
212
|
+
public Trie(IReadOnlyDictionary<string, T> items)
|
|
213
|
+
{
|
|
214
|
+
KeyValuePair<string, T>[] array = items.ToArray();
|
|
215
|
+
int capacity = 1;
|
|
216
|
+
foreach (KeyValuePair<string, T> entry in array)
|
|
217
|
+
{
|
|
218
|
+
capacity += entry.Key.Length;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
_chars = new char[capacity];
|
|
222
|
+
_firstChild = new int[capacity];
|
|
223
|
+
_nextSibling = new int[capacity];
|
|
224
|
+
_hasValue = new bool[capacity];
|
|
225
|
+
_values = new T[capacity];
|
|
226
|
+
|
|
227
|
+
Array.Fill(_firstChild, Poison);
|
|
228
|
+
Array.Fill(_nextSibling, Poison);
|
|
229
|
+
|
|
230
|
+
_nodeCount = 1;
|
|
231
|
+
foreach (KeyValuePair<string, T> kv in array)
|
|
232
|
+
{
|
|
233
|
+
Insert(kv.Key, kv.Value);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Inserts a single key-value pair into the Trie
|
|
238
|
+
private void Insert(string key, T value)
|
|
239
|
+
{
|
|
240
|
+
int node = 0;
|
|
241
|
+
foreach (char c in key)
|
|
242
|
+
{
|
|
243
|
+
int prev = Poison;
|
|
244
|
+
int child = _firstChild[node];
|
|
245
|
+
while (child != Poison && _chars[child] != c)
|
|
246
|
+
{
|
|
247
|
+
prev = child;
|
|
248
|
+
child = _nextSibling[child];
|
|
249
|
+
}
|
|
250
|
+
if (child == Poison)
|
|
251
|
+
{
|
|
252
|
+
child = _nodeCount++;
|
|
253
|
+
_chars[child] = c;
|
|
254
|
+
_firstChild[child] = Poison;
|
|
255
|
+
_nextSibling[child] = Poison;
|
|
256
|
+
if (prev == Poison)
|
|
257
|
+
{
|
|
258
|
+
_firstChild[node] = child;
|
|
259
|
+
}
|
|
260
|
+
else
|
|
261
|
+
{
|
|
262
|
+
_nextSibling[prev] = child;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
node = child;
|
|
266
|
+
}
|
|
267
|
+
_hasValue[node] = true;
|
|
268
|
+
_values[node] = value;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/// <summary>
|
|
272
|
+
/// Attempts to retrieve the value associated with the exact key.
|
|
273
|
+
/// </summary>
|
|
274
|
+
public bool TryGetValue(string key, out T value)
|
|
275
|
+
{
|
|
276
|
+
int node = 0;
|
|
277
|
+
foreach (char c in key)
|
|
278
|
+
{
|
|
279
|
+
int child = _firstChild[node];
|
|
280
|
+
while (child != Poison && _chars[child] != c)
|
|
281
|
+
{
|
|
282
|
+
child = _nextSibling[child];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (child == Poison)
|
|
286
|
+
{
|
|
287
|
+
value = default;
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
node = child;
|
|
291
|
+
}
|
|
292
|
+
if (_hasValue[node])
|
|
293
|
+
{
|
|
294
|
+
value = _values[node];
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
value = default;
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/// <summary>
|
|
302
|
+
/// Collects up to maxResults values whose keys start with the given prefix.
|
|
303
|
+
/// Results are added into the provided list (which is cleared at the start).
|
|
304
|
+
/// Returns the number of results added.
|
|
305
|
+
/// </summary>
|
|
306
|
+
public int GetValuesWithPrefix(
|
|
307
|
+
string prefix,
|
|
308
|
+
List<T> results,
|
|
309
|
+
int maxResults = int.MaxValue
|
|
310
|
+
)
|
|
311
|
+
{
|
|
312
|
+
results.Clear();
|
|
313
|
+
int node = 0;
|
|
314
|
+
foreach (char c in prefix)
|
|
315
|
+
{
|
|
316
|
+
int child = _firstChild[node];
|
|
317
|
+
while (child != Poison && _chars[child] != c)
|
|
318
|
+
{
|
|
319
|
+
child = _nextSibling[child];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (child == Poison)
|
|
323
|
+
{
|
|
324
|
+
return 0;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
node = child;
|
|
328
|
+
}
|
|
329
|
+
Collect(node, results, maxResults);
|
|
330
|
+
return results.Count;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Recursive collection without extra allocations
|
|
334
|
+
private void Collect(int node, List<T> results, int maxResults)
|
|
335
|
+
{
|
|
336
|
+
if (results.Count >= maxResults)
|
|
337
|
+
{
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (_hasValue[node])
|
|
342
|
+
{
|
|
343
|
+
results.Add(_values[node]);
|
|
344
|
+
if (results.Count >= maxResults)
|
|
345
|
+
{
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
for (int child = _firstChild[node]; child != Poison; child = _nextSibling[child])
|
|
350
|
+
{
|
|
351
|
+
Collect(child, results, maxResults);
|
|
352
|
+
if (results.Count >= maxResults)
|
|
353
|
+
{
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
{
|
|
12
12
|
private readonly ConcurrentQueue<Action> _actions = new();
|
|
13
13
|
|
|
14
|
+
protected override bool Preserve => false;
|
|
15
|
+
|
|
14
16
|
#if UNITY_EDITOR
|
|
15
17
|
private readonly EditorApplication.CallbackFunction _update;
|
|
16
18
|
private bool _attachedEditorUpdate;
|
|
@@ -31,7 +33,7 @@
|
|
|
31
33
|
private void OnEnable()
|
|
32
34
|
{
|
|
33
35
|
#if UNITY_EDITOR
|
|
34
|
-
if (!_attachedEditorUpdate)
|
|
36
|
+
if (!_attachedEditorUpdate && !Application.isPlaying)
|
|
35
37
|
{
|
|
36
38
|
EditorApplication.update += _update;
|
|
37
39
|
_attachedEditorUpdate = true;
|
|
@@ -16,14 +16,19 @@
|
|
|
16
16
|
private AutoResetEvent _waitHandle;
|
|
17
17
|
private bool _disposed;
|
|
18
18
|
private readonly ConcurrentQueue<Exception> _exceptions;
|
|
19
|
+
private readonly TimeSpan _noWorkWaitTime;
|
|
19
20
|
|
|
20
|
-
public SingleThreadedThreadPool(
|
|
21
|
+
public SingleThreadedThreadPool(
|
|
22
|
+
bool runInBackground = false,
|
|
23
|
+
TimeSpan? noWorkWaitTime = null
|
|
24
|
+
)
|
|
21
25
|
{
|
|
22
26
|
_active = 1;
|
|
23
27
|
_working = 1;
|
|
24
28
|
_work = new ConcurrentQueue<Action>();
|
|
25
29
|
_exceptions = new ConcurrentQueue<Exception>();
|
|
26
30
|
_waitHandle = new AutoResetEvent(false);
|
|
31
|
+
_noWorkWaitTime = noWorkWaitTime ?? TimeSpan.FromSeconds(1);
|
|
27
32
|
_worker = new Thread(DoWork) { IsBackground = runInBackground };
|
|
28
33
|
_worker.Start();
|
|
29
34
|
}
|
|
@@ -95,7 +100,7 @@
|
|
|
95
100
|
{
|
|
96
101
|
try
|
|
97
102
|
{
|
|
98
|
-
_ = _waitHandle?.WaitOne(
|
|
103
|
+
_ = _waitHandle?.WaitOne(_noWorkWaitTime);
|
|
99
104
|
}
|
|
100
105
|
catch (ObjectDisposedException)
|
|
101
106
|
{
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
|
|
35
35
|
TTree quadTree = CreateTree(points);
|
|
36
36
|
|
|
37
|
-
List<Vector2> pointsInRange =
|
|
37
|
+
List<Vector2> pointsInRange = new();
|
|
38
|
+
quadTree.GetElementsInRange(center, radius, pointsInRange);
|
|
38
39
|
Assert.IsTrue(
|
|
39
40
|
points.SetEquals(pointsInRange),
|
|
40
41
|
"Found {0} points in range, expected {1}.",
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
offset.x -= radius * 2;
|
|
47
48
|
offset.y -= radius * 2;
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
quadTree.GetElementsInRange(offset, radius, pointsInRange);
|
|
50
51
|
Assert.AreEqual(
|
|
51
52
|
0,
|
|
52
53
|
pointsInRange.Count,
|
|
@@ -69,11 +70,10 @@
|
|
|
69
70
|
List<Vector2> points = new(1) { testPoint };
|
|
70
71
|
|
|
71
72
|
TTree quadTree = CreateTree(points);
|
|
72
|
-
List<Vector2> pointsInRange =
|
|
73
|
-
|
|
74
|
-
.ToList();
|
|
73
|
+
List<Vector2> pointsInRange = new();
|
|
74
|
+
quadTree.GetElementsInRange(point, range * 0.99f, pointsInRange).ToList();
|
|
75
75
|
Assert.AreEqual(0, pointsInRange.Count);
|
|
76
|
-
|
|
76
|
+
quadTree.GetElementsInRange(point, range * 1.01f, pointsInRange);
|
|
77
77
|
Assert.AreEqual(
|
|
78
78
|
1,
|
|
79
79
|
pointsInRange.Count,
|
|
@@ -24,21 +24,19 @@
|
|
|
24
24
|
|
|
25
25
|
Vector2[] points = new Vector2[1_000_000];
|
|
26
26
|
float radius = 500;
|
|
27
|
-
|
|
27
|
+
Parallel.For(
|
|
28
28
|
0,
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
1_000,
|
|
30
|
+
y =>
|
|
31
31
|
{
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
for (int x = 0; x < 1_000; ++x)
|
|
33
|
+
{
|
|
34
|
+
int index = y * 1_000 + x;
|
|
35
|
+
points[index] = new Vector2(x, y);
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
);
|
|
36
39
|
|
|
37
|
-
while (!result.IsCompleted)
|
|
38
|
-
{
|
|
39
|
-
yield return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
40
|
Stopwatch timer = Stopwatch.StartNew();
|
|
43
41
|
TTree tree = CreateTree(points);
|
|
44
42
|
timer.Stop();
|
|
@@ -49,10 +47,12 @@
|
|
|
49
47
|
int count = 0;
|
|
50
48
|
TimeSpan timeout = TimeSpan.FromSeconds(1);
|
|
51
49
|
timer.Restart();
|
|
50
|
+
List<Vector2> elementsInRange = new();
|
|
52
51
|
do
|
|
53
52
|
{
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
tree.GetElementsInRange(center, radius, elementsInRange);
|
|
54
|
+
int elementCount = elementsInRange.Count;
|
|
55
|
+
Assert.AreEqual(785456, elementCount);
|
|
56
56
|
++count;
|
|
57
57
|
} while (timer.Elapsed < timeout);
|
|
58
58
|
|
|
@@ -65,8 +65,9 @@
|
|
|
65
65
|
timer.Restart();
|
|
66
66
|
do
|
|
67
67
|
{
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
tree.GetElementsInRange(center, radius, elementsInRange);
|
|
69
|
+
int elementCount = elementsInRange.Count;
|
|
70
|
+
Assert.AreEqual(196364, elementCount);
|
|
70
71
|
++count;
|
|
71
72
|
} while (timer.Elapsed < timeout);
|
|
72
73
|
UnityEngine.Debug.Log(
|
|
@@ -78,8 +79,9 @@
|
|
|
78
79
|
timer.Restart();
|
|
79
80
|
do
|
|
80
81
|
{
|
|
81
|
-
|
|
82
|
-
|
|
82
|
+
tree.GetElementsInRange(center, radius, elementsInRange);
|
|
83
|
+
int elementCount = elementsInRange.Count;
|
|
84
|
+
Assert.AreEqual(49080, elementCount);
|
|
83
85
|
++count;
|
|
84
86
|
} while (timer.Elapsed < timeout);
|
|
85
87
|
UnityEngine.Debug.Log(
|
|
@@ -91,8 +93,9 @@
|
|
|
91
93
|
timer.Restart();
|
|
92
94
|
do
|
|
93
95
|
{
|
|
94
|
-
|
|
95
|
-
|
|
96
|
+
tree.GetElementsInRange(center, radius, elementsInRange);
|
|
97
|
+
int elementCount = elementsInRange.Count;
|
|
98
|
+
Assert.AreEqual(4, elementCount);
|
|
96
99
|
++count;
|
|
97
100
|
} while (timer.Elapsed < timeout);
|
|
98
101
|
UnityEngine.Debug.Log(
|
|
@@ -149,6 +152,8 @@
|
|
|
149
152
|
++count;
|
|
150
153
|
} while (timer.Elapsed < timeout);
|
|
151
154
|
UnityEngine.Debug.Log($"| ANN - 1 | {(int)Math.Floor(count / timeout.TotalSeconds)} |");
|
|
155
|
+
|
|
156
|
+
yield break;
|
|
152
157
|
}
|
|
153
158
|
}
|
|
154
159
|
}
|
package/package.json
CHANGED