com.wallstop-studios.unity-helpers 2.0.0-rc13 → 2.0.0-rc15
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/EnsureTextureSizeWizard.cs +110 -0
- package/Editor/EnsureTextureSizeWizard.cs.meta +3 -0
- package/README.md +1 -1
- package/Runtime/Core/DataStructure/KDTree.cs +123 -19
- package/Runtime/Core/DataStructure/QuadTree.cs +118 -23
- package/Runtime/Core/DataStructure/RTree.cs +140 -51
- package/Runtime/Core/Extension/ColorExtensions.cs +267 -1
- package/Runtime/Core/Extension/UnityExtensions.cs +10 -0
- package/Runtime/Core/Helper/Geometry.cs +17 -0
- package/Runtime/Core/Helper/SpriteHelpers.cs +36 -3
- package/Runtime/Core/Random/PcgRandom.cs +2 -2
- package/Runtime/Core/Random/RomuDuo.cs +116 -0
- package/Runtime/Core/Random/RomuDuo.cs.meta +3 -0
- package/Runtime/Core/Random/SplitMix64.cs +94 -0
- package/Runtime/Core/Random/SplitMix64.cs.meta +3 -0
- package/Runtime/Core/Random/XorShiroRandom.cs +117 -0
- package/Runtime/Core/Random/XorShiroRandom.cs.meta +3 -0
- package/Runtime/UI/LayeredImage.cs +364 -0
- package/Runtime/UI/LayeredImage.cs.meta +3 -0
- package/Runtime/UI.meta +3 -0
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -0
- package/Tests/Runtime/Random/RomuDuoRandomTests.cs +9 -0
- package/Tests/Runtime/Random/RomuDuoRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/SplitMix64RandomTests.cs +9 -0
- package/Tests/Runtime/Random/SplitMix64RandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/XorShiroRandomTests.cs +9 -0
- package/Tests/Runtime/Random/XorShiroRandomTests.cs.meta +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
namespace UnityHelpers.Editor
|
|
2
|
+
{
|
|
3
|
+
using System.Collections.Generic;
|
|
4
|
+
using System.Linq;
|
|
5
|
+
using Core.Extension;
|
|
6
|
+
using UnityEditor;
|
|
7
|
+
using UnityEngine;
|
|
8
|
+
|
|
9
|
+
public sealed class EnsureTextureSizeWizard : ScriptableWizard
|
|
10
|
+
{
|
|
11
|
+
public List<Texture2D> textures = new();
|
|
12
|
+
|
|
13
|
+
public List<Object> textureSourcePaths = new();
|
|
14
|
+
|
|
15
|
+
[MenuItem("Tools/Unity Helpers/Ensure Texture Size")]
|
|
16
|
+
public static void EnsureSizes()
|
|
17
|
+
{
|
|
18
|
+
_ = DisplayWizard<EnsureTextureSizeWizard>("Ensure Texture Size", "Run");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private void OnWizardCreate()
|
|
22
|
+
{
|
|
23
|
+
textures ??= new List<Texture2D>();
|
|
24
|
+
textureSourcePaths ??= new List<Object>();
|
|
25
|
+
HashSet<string> texturePath = new();
|
|
26
|
+
foreach (Object textureSource in textureSourcePaths)
|
|
27
|
+
{
|
|
28
|
+
string assetPath = AssetDatabase.GetAssetPath(textureSource);
|
|
29
|
+
if (!string.IsNullOrWhiteSpace(assetPath))
|
|
30
|
+
{
|
|
31
|
+
_ = texturePath.Add(assetPath);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (texturePath.Any())
|
|
36
|
+
{
|
|
37
|
+
foreach (
|
|
38
|
+
string assetGuid in AssetDatabase.FindAssets(
|
|
39
|
+
"t:texture2D",
|
|
40
|
+
texturePath.ToArray()
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
{
|
|
44
|
+
string path = AssetDatabase.GUIDToAssetPath(assetGuid);
|
|
45
|
+
if (string.IsNullOrWhiteSpace(path))
|
|
46
|
+
{
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
|
|
51
|
+
if (texture != null)
|
|
52
|
+
{
|
|
53
|
+
textures.Add(texture);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
textures = textures.Distinct().OrderBy(texture => texture.name).ToList();
|
|
59
|
+
if (textures.Count <= 0)
|
|
60
|
+
{
|
|
61
|
+
this.Log("Failed to find any texture paths.");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
int changedCount = 0;
|
|
66
|
+
foreach (Texture2D inputTexture in textures)
|
|
67
|
+
{
|
|
68
|
+
Texture2D texture = inputTexture;
|
|
69
|
+
string assetPath = AssetDatabase.GetAssetPath(texture);
|
|
70
|
+
if (string.IsNullOrWhiteSpace(assetPath))
|
|
71
|
+
{
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
TextureImporter tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
|
|
76
|
+
if (tImporter == null)
|
|
77
|
+
{
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
tImporter.GetSourceTextureWidthAndHeight(out int width, out int height);
|
|
81
|
+
|
|
82
|
+
float size = Mathf.Max(width, height);
|
|
83
|
+
int textureSize = tImporter.maxTextureSize;
|
|
84
|
+
bool changed = false;
|
|
85
|
+
while (textureSize < size)
|
|
86
|
+
{
|
|
87
|
+
changed = true;
|
|
88
|
+
textureSize <<= 1;
|
|
89
|
+
}
|
|
90
|
+
tImporter.maxTextureSize = textureSize;
|
|
91
|
+
|
|
92
|
+
if (changed)
|
|
93
|
+
{
|
|
94
|
+
changedCount++;
|
|
95
|
+
tImporter.SaveAndReimport();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (changedCount != 0)
|
|
100
|
+
{
|
|
101
|
+
this.Log($"Updated {changedCount} textures.");
|
|
102
|
+
AssetDatabase.Refresh();
|
|
103
|
+
}
|
|
104
|
+
else
|
|
105
|
+
{
|
|
106
|
+
this.Log("No textures updated.");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
package/README.md
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
# A Grab-Bag
|
|
2
|
-
Various Unity Helpers. Includes deterministic
|
|
2
|
+
Various Unity Helpers. Includes many deterministic, seedable random number generators.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
using System.Linq;
|
|
7
7
|
using Extension;
|
|
8
8
|
using UnityEngine;
|
|
9
|
+
using Utils;
|
|
9
10
|
|
|
10
11
|
[Serializable]
|
|
11
12
|
public sealed class KDTree<T> : ISpatialTree<T>
|
|
@@ -13,7 +14,7 @@
|
|
|
13
14
|
public delegate float Axis<in V>(V element);
|
|
14
15
|
|
|
15
16
|
[Serializable]
|
|
16
|
-
|
|
17
|
+
public sealed class KDTreeNode<V>
|
|
17
18
|
{
|
|
18
19
|
public readonly Bounds boundary;
|
|
19
20
|
public readonly KDTreeNode<V> left;
|
|
@@ -21,7 +22,13 @@
|
|
|
21
22
|
public readonly V[] elements;
|
|
22
23
|
public readonly bool isTerminal;
|
|
23
24
|
|
|
24
|
-
public KDTreeNode(
|
|
25
|
+
public KDTreeNode(
|
|
26
|
+
List<V> elements,
|
|
27
|
+
Func<V, Vector2> elementTransformer,
|
|
28
|
+
int bucketSize,
|
|
29
|
+
bool isXAxis,
|
|
30
|
+
bool balanced
|
|
31
|
+
)
|
|
25
32
|
{
|
|
26
33
|
boundary = elements.Select(elementTransformer).GetBounds() ?? new Bounds();
|
|
27
34
|
this.elements = elements.ToArray();
|
|
@@ -44,21 +51,65 @@
|
|
|
44
51
|
elements.Sort(Comparison);
|
|
45
52
|
|
|
46
53
|
int cutoff = elements.Count / 2;
|
|
47
|
-
left = new KDTreeNode<V>(
|
|
48
|
-
|
|
54
|
+
left = new KDTreeNode<V>(
|
|
55
|
+
elements.Take(cutoff).ToList(),
|
|
56
|
+
elementTransformer,
|
|
57
|
+
bucketSize,
|
|
58
|
+
!isXAxis,
|
|
59
|
+
true
|
|
60
|
+
);
|
|
61
|
+
right = new KDTreeNode<V>(
|
|
62
|
+
elements.Skip(cutoff).ToList(),
|
|
63
|
+
elementTransformer,
|
|
64
|
+
bucketSize,
|
|
65
|
+
!isXAxis,
|
|
66
|
+
true
|
|
67
|
+
);
|
|
49
68
|
}
|
|
50
69
|
else
|
|
51
70
|
{
|
|
52
71
|
Vector2 cutoff = boundary.center;
|
|
53
72
|
if (isXAxis)
|
|
54
73
|
{
|
|
55
|
-
left = new KDTreeNode<V>(
|
|
56
|
-
|
|
74
|
+
left = new KDTreeNode<V>(
|
|
75
|
+
elements
|
|
76
|
+
.Where(element => elementTransformer(element).x <= cutoff.x)
|
|
77
|
+
.ToList(),
|
|
78
|
+
elementTransformer,
|
|
79
|
+
bucketSize,
|
|
80
|
+
false,
|
|
81
|
+
false
|
|
82
|
+
);
|
|
83
|
+
right = new KDTreeNode<V>(
|
|
84
|
+
elements
|
|
85
|
+
.Where(element => cutoff.x < elementTransformer(element).x)
|
|
86
|
+
.ToList(),
|
|
87
|
+
elementTransformer,
|
|
88
|
+
bucketSize,
|
|
89
|
+
false,
|
|
90
|
+
false
|
|
91
|
+
);
|
|
57
92
|
}
|
|
58
93
|
else
|
|
59
94
|
{
|
|
60
|
-
left = new KDTreeNode<V>(
|
|
61
|
-
|
|
95
|
+
left = new KDTreeNode<V>(
|
|
96
|
+
elements
|
|
97
|
+
.Where(element => elementTransformer(element).y <= cutoff.y)
|
|
98
|
+
.ToList(),
|
|
99
|
+
elementTransformer,
|
|
100
|
+
bucketSize,
|
|
101
|
+
true,
|
|
102
|
+
false
|
|
103
|
+
);
|
|
104
|
+
right = new KDTreeNode<V>(
|
|
105
|
+
elements
|
|
106
|
+
.Where(element => cutoff.y < elementTransformer(element).y)
|
|
107
|
+
.ToList(),
|
|
108
|
+
elementTransformer,
|
|
109
|
+
bucketSize,
|
|
110
|
+
true,
|
|
111
|
+
false
|
|
112
|
+
);
|
|
62
113
|
}
|
|
63
114
|
}
|
|
64
115
|
}
|
|
@@ -74,22 +125,42 @@
|
|
|
74
125
|
private readonly Func<T, Vector2> _elementTransformer;
|
|
75
126
|
private readonly KDTreeNode<T> _head;
|
|
76
127
|
|
|
77
|
-
public KDTree(
|
|
128
|
+
public KDTree(
|
|
129
|
+
IEnumerable<T> points,
|
|
130
|
+
Func<T, Vector2> elementTransformer,
|
|
131
|
+
int bucketSize = DefaultBucketSize,
|
|
132
|
+
bool balanced = true
|
|
133
|
+
)
|
|
78
134
|
{
|
|
79
|
-
_elementTransformer =
|
|
80
|
-
|
|
135
|
+
_elementTransformer =
|
|
136
|
+
elementTransformer ?? throw new ArgumentNullException(nameof(elementTransformer));
|
|
137
|
+
elements =
|
|
138
|
+
points?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(points));
|
|
81
139
|
_bounds = elements.Select(elementTransformer).GetBounds() ?? new Bounds();
|
|
82
|
-
_head = new KDTreeNode<T>(
|
|
140
|
+
_head = new KDTreeNode<T>(
|
|
141
|
+
elements.ToList(),
|
|
142
|
+
elementTransformer,
|
|
143
|
+
bucketSize: bucketSize,
|
|
144
|
+
isXAxis: true,
|
|
145
|
+
balanced: balanced
|
|
146
|
+
);
|
|
83
147
|
}
|
|
84
148
|
|
|
85
149
|
public IEnumerable<T> GetElementsInBounds(Bounds bounds)
|
|
150
|
+
{
|
|
151
|
+
Stack<KDTreeNode<T>> buffer = Buffers<KDTreeNode<T>>.Stack;
|
|
152
|
+
return GetElementsInBounds(bounds, buffer);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public IEnumerable<T> GetElementsInBounds(Bounds bounds, Stack<KDTreeNode<T>> nodeBuffer)
|
|
86
156
|
{
|
|
87
157
|
if (!bounds.FastIntersects2D(_bounds))
|
|
88
158
|
{
|
|
89
159
|
yield break;
|
|
90
160
|
}
|
|
91
161
|
|
|
92
|
-
Stack<KDTreeNode<T>> nodesToVisit = new();
|
|
162
|
+
Stack<KDTreeNode<T>> nodesToVisit = nodeBuffer ?? new Stack<KDTreeNode<T>>();
|
|
163
|
+
nodesToVisit.Clear();
|
|
93
164
|
nodesToVisit.Push(_head);
|
|
94
165
|
|
|
95
166
|
while (nodesToVisit.TryPop(out KDTreeNode<T> currentNode))
|
|
@@ -131,22 +202,49 @@
|
|
|
131
202
|
}
|
|
132
203
|
}
|
|
133
204
|
|
|
205
|
+
public void GetApproximateNearestNeighbors(
|
|
206
|
+
Vector2 position,
|
|
207
|
+
int count,
|
|
208
|
+
List<T> nearestNeighbors
|
|
209
|
+
)
|
|
210
|
+
{
|
|
211
|
+
Stack<KDTreeNode<T>> nodeBuffer = Buffers<KDTreeNode<T>>.Stack;
|
|
212
|
+
HashSet<T> nearestNeighborBuffer = Buffers<T>.HashSet;
|
|
213
|
+
GetApproximateNearestNeighbors(
|
|
214
|
+
position,
|
|
215
|
+
count,
|
|
216
|
+
nearestNeighbors,
|
|
217
|
+
nodeBuffer,
|
|
218
|
+
nearestNeighborBuffer
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
134
222
|
// Heavily adapted http://homepage.divms.uiowa.edu/%7Ekvaradar/sp2012/daa/ann.pdf
|
|
135
|
-
public void GetApproximateNearestNeighbors(
|
|
223
|
+
public void GetApproximateNearestNeighbors(
|
|
224
|
+
Vector2 position,
|
|
225
|
+
int count,
|
|
226
|
+
List<T> nearestNeighbors,
|
|
227
|
+
Stack<KDTreeNode<T>> nodeBuffer,
|
|
228
|
+
HashSet<T> nearestNeighborBuffer
|
|
229
|
+
)
|
|
136
230
|
{
|
|
137
231
|
nearestNeighbors.Clear();
|
|
138
232
|
|
|
139
233
|
KDTreeNode<T> current = _head;
|
|
140
|
-
Stack<KDTreeNode<T>> stack = new();
|
|
234
|
+
Stack<KDTreeNode<T>> stack = nodeBuffer ?? new Stack<KDTreeNode<T>>();
|
|
235
|
+
stack.Clear();
|
|
141
236
|
stack.Push(_head);
|
|
142
|
-
HashSet<T> nearestNeighborsSet = new(count);
|
|
237
|
+
HashSet<T> nearestNeighborsSet = nearestNeighborBuffer ?? new HashSet<T>(count);
|
|
238
|
+
nearestNeighborsSet.Clear();
|
|
143
239
|
|
|
144
240
|
while (!current.isTerminal)
|
|
145
241
|
{
|
|
146
242
|
KDTreeNode<T> left = current.left;
|
|
147
243
|
KDTreeNode<T> right = current.right;
|
|
148
|
-
if (
|
|
149
|
-
((Vector2)
|
|
244
|
+
if (
|
|
245
|
+
((Vector2)left.boundary.center - position).sqrMagnitude
|
|
246
|
+
< ((Vector2)right.boundary.center - position).sqrMagnitude
|
|
247
|
+
)
|
|
150
248
|
{
|
|
151
249
|
stack.Push(left);
|
|
152
250
|
current = left;
|
|
@@ -177,10 +275,16 @@
|
|
|
177
275
|
nearestNeighbors.AddRange(nearestNeighborsSet);
|
|
178
276
|
if (count < nearestNeighbors.Count)
|
|
179
277
|
{
|
|
180
|
-
int NearestComparison(T lhs, T rhs) => (_elementTransformer(lhs) - position).sqrMagnitude.CompareTo((_elementTransformer(rhs) - position).sqrMagnitude);
|
|
181
278
|
nearestNeighbors.Sort(NearestComparison);
|
|
182
279
|
nearestNeighbors.RemoveRange(count, nearestNeighbors.Count - count);
|
|
183
280
|
}
|
|
281
|
+
|
|
282
|
+
return;
|
|
283
|
+
|
|
284
|
+
int NearestComparison(T lhs, T rhs) =>
|
|
285
|
+
(_elementTransformer(lhs) - position).sqrMagnitude.CompareTo(
|
|
286
|
+
(_elementTransformer(rhs) - position).sqrMagnitude
|
|
287
|
+
);
|
|
184
288
|
}
|
|
185
289
|
}
|
|
186
290
|
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
using System.Linq;
|
|
7
7
|
using Extension;
|
|
8
8
|
using UnityEngine;
|
|
9
|
+
using Utils;
|
|
9
10
|
|
|
10
11
|
[Serializable]
|
|
11
12
|
public sealed class QuadTree<T> : ISpatialTree<T>
|
|
@@ -13,17 +14,21 @@
|
|
|
13
14
|
private const int NumChildren = 4;
|
|
14
15
|
|
|
15
16
|
[Serializable]
|
|
16
|
-
|
|
17
|
+
public sealed class QuadTreeNode<V>
|
|
17
18
|
{
|
|
18
19
|
private static readonly List<V> Buffer = new();
|
|
19
20
|
|
|
20
21
|
public readonly Bounds boundary;
|
|
21
|
-
|
|
22
|
+
internal readonly QuadTreeNode<V>[] children;
|
|
22
23
|
public readonly V[] elements;
|
|
23
24
|
public readonly bool isTerminal;
|
|
24
25
|
|
|
25
|
-
public QuadTreeNode(
|
|
26
|
-
|
|
26
|
+
public QuadTreeNode(
|
|
27
|
+
V[] elements,
|
|
28
|
+
Func<V, Vector2> elementTransformer,
|
|
29
|
+
Bounds boundary,
|
|
30
|
+
int bucketSize
|
|
31
|
+
)
|
|
27
32
|
{
|
|
28
33
|
this.boundary = boundary;
|
|
29
34
|
this.elements = elements;
|
|
@@ -34,16 +39,44 @@
|
|
|
34
39
|
return;
|
|
35
40
|
}
|
|
36
41
|
children = new QuadTreeNode<V>[NumChildren];
|
|
37
|
-
|
|
42
|
+
|
|
38
43
|
Vector3 quadrantSize = boundary.size / 2f;
|
|
39
44
|
Vector2 halfQuadrantSize = quadrantSize / 2f;
|
|
40
45
|
|
|
41
46
|
Bounds[] quadrants =
|
|
42
47
|
{
|
|
43
|
-
new Bounds(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
new Bounds(
|
|
49
|
+
new Vector3(
|
|
50
|
+
boundary.center.x - halfQuadrantSize.x,
|
|
51
|
+
boundary.center.y + halfQuadrantSize.y,
|
|
52
|
+
boundary.center.z
|
|
53
|
+
),
|
|
54
|
+
quadrantSize
|
|
55
|
+
),
|
|
56
|
+
new Bounds(
|
|
57
|
+
new Vector3(
|
|
58
|
+
boundary.center.x + halfQuadrantSize.x,
|
|
59
|
+
boundary.center.y + halfQuadrantSize.y,
|
|
60
|
+
boundary.center.z
|
|
61
|
+
),
|
|
62
|
+
quadrantSize
|
|
63
|
+
),
|
|
64
|
+
new Bounds(
|
|
65
|
+
new Vector3(
|
|
66
|
+
boundary.center.x + halfQuadrantSize.x,
|
|
67
|
+
boundary.center.y - halfQuadrantSize.y,
|
|
68
|
+
boundary.center.z
|
|
69
|
+
),
|
|
70
|
+
quadrantSize
|
|
71
|
+
),
|
|
72
|
+
new Bounds(
|
|
73
|
+
new Vector3(
|
|
74
|
+
boundary.center.x - halfQuadrantSize.x,
|
|
75
|
+
boundary.center.y - halfQuadrantSize.y,
|
|
76
|
+
boundary.center.z
|
|
77
|
+
),
|
|
78
|
+
quadrantSize
|
|
79
|
+
),
|
|
47
80
|
};
|
|
48
81
|
|
|
49
82
|
for (int i = 0; i < quadrants.Length; ++i)
|
|
@@ -58,7 +91,12 @@
|
|
|
58
91
|
}
|
|
59
92
|
}
|
|
60
93
|
|
|
61
|
-
children[i] = new QuadTreeNode<V>(
|
|
94
|
+
children[i] = new QuadTreeNode<V>(
|
|
95
|
+
Buffer.ToArray(),
|
|
96
|
+
elementTransformer,
|
|
97
|
+
quadrant,
|
|
98
|
+
bucketSize
|
|
99
|
+
);
|
|
62
100
|
}
|
|
63
101
|
}
|
|
64
102
|
}
|
|
@@ -73,23 +111,41 @@
|
|
|
73
111
|
private readonly Func<T, Vector2> _elementTransformer;
|
|
74
112
|
private readonly QuadTreeNode<T> _head;
|
|
75
113
|
|
|
76
|
-
public QuadTree(
|
|
77
|
-
|
|
114
|
+
public QuadTree(
|
|
115
|
+
IEnumerable<T> points,
|
|
116
|
+
Func<T, Vector2> elementTransformer,
|
|
117
|
+
Bounds? boundary = null,
|
|
118
|
+
int bucketSize = DefaultBucketSize
|
|
119
|
+
)
|
|
78
120
|
{
|
|
79
|
-
_elementTransformer =
|
|
80
|
-
|
|
121
|
+
_elementTransformer =
|
|
122
|
+
elementTransformer ?? throw new ArgumentNullException(nameof(elementTransformer));
|
|
123
|
+
elements =
|
|
124
|
+
points?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(points));
|
|
81
125
|
_bounds = boundary ?? elements.Select(elementTransformer).GetBounds() ?? new Bounds();
|
|
82
|
-
_head = new QuadTreeNode<T>(
|
|
126
|
+
_head = new QuadTreeNode<T>(
|
|
127
|
+
elements.ToArray(),
|
|
128
|
+
elementTransformer,
|
|
129
|
+
_bounds,
|
|
130
|
+
bucketSize
|
|
131
|
+
);
|
|
83
132
|
}
|
|
84
133
|
|
|
85
134
|
public IEnumerable<T> GetElementsInBounds(Bounds bounds)
|
|
135
|
+
{
|
|
136
|
+
Stack<QuadTreeNode<T>> nodeBuffer = Buffers<QuadTreeNode<T>>.Stack;
|
|
137
|
+
return GetElementsInBounds(bounds, nodeBuffer);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
public IEnumerable<T> GetElementsInBounds(Bounds bounds, Stack<QuadTreeNode<T>> nodeBuffer)
|
|
86
141
|
{
|
|
87
142
|
if (!bounds.FastIntersects2D(_bounds))
|
|
88
143
|
{
|
|
89
144
|
yield break;
|
|
90
145
|
}
|
|
91
146
|
|
|
92
|
-
Stack<QuadTreeNode<T>> nodesToVisit = new();
|
|
147
|
+
Stack<QuadTreeNode<T>> nodesToVisit = nodeBuffer ?? new Stack<QuadTreeNode<T>>();
|
|
148
|
+
nodesToVisit.Clear();
|
|
93
149
|
nodesToVisit.Push(_head);
|
|
94
150
|
|
|
95
151
|
while (nodesToVisit.TryPop(out QuadTreeNode<T> currentNode))
|
|
@@ -134,19 +190,47 @@
|
|
|
134
190
|
}
|
|
135
191
|
}
|
|
136
192
|
|
|
193
|
+
public void GetApproximateNearestNeighbors(
|
|
194
|
+
Vector2 position,
|
|
195
|
+
int count,
|
|
196
|
+
List<T> nearestNeighbors
|
|
197
|
+
)
|
|
198
|
+
{
|
|
199
|
+
Stack<QuadTreeNode<T>> nodeBuffer = Buffers<QuadTreeNode<T>>.Stack;
|
|
200
|
+
List<QuadTreeNode<T>> childrenBuffer = Buffers<QuadTreeNode<T>>.List;
|
|
201
|
+
HashSet<T> nearestNeighborBuffer = Buffers<T>.HashSet;
|
|
202
|
+
GetApproximateNearestNeighbors(
|
|
203
|
+
position,
|
|
204
|
+
count,
|
|
205
|
+
nearestNeighbors,
|
|
206
|
+
nodeBuffer,
|
|
207
|
+
childrenBuffer,
|
|
208
|
+
nearestNeighborBuffer
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
137
212
|
// Heavily adapted http://homepage.divms.uiowa.edu/%7Ekvaradar/sp2012/daa/ann.pdf
|
|
138
|
-
public void GetApproximateNearestNeighbors(
|
|
213
|
+
public void GetApproximateNearestNeighbors(
|
|
214
|
+
Vector2 position,
|
|
215
|
+
int count,
|
|
216
|
+
List<T> nearestNeighbors,
|
|
217
|
+
Stack<QuadTreeNode<T>> nodeBuffer,
|
|
218
|
+
List<QuadTreeNode<T>> childrenBuffer,
|
|
219
|
+
HashSet<T> nearestNeighborBuffer
|
|
220
|
+
)
|
|
139
221
|
{
|
|
140
222
|
nearestNeighbors.Clear();
|
|
141
223
|
|
|
142
224
|
QuadTreeNode<T> current = _head;
|
|
143
|
-
Stack<QuadTreeNode<T>> stack = new();
|
|
225
|
+
Stack<QuadTreeNode<T>> stack = nodeBuffer ?? new Stack<QuadTreeNode<T>>();
|
|
226
|
+
stack.Clear();
|
|
144
227
|
stack.Push(_head);
|
|
145
|
-
List<QuadTreeNode<T>> childrenCopy =
|
|
146
|
-
|
|
228
|
+
List<QuadTreeNode<T>> childrenCopy =
|
|
229
|
+
childrenBuffer ?? new List<QuadTreeNode<T>>(NumChildren);
|
|
230
|
+
childrenCopy.Clear();
|
|
231
|
+
HashSet<T> nearestNeighborsSet = nearestNeighborBuffer ?? new HashSet<T>(count);
|
|
232
|
+
nearestNeighborsSet.Clear();
|
|
147
233
|
|
|
148
|
-
int Comparison(QuadTreeNode<T> lhs, QuadTreeNode<T> rhs) => ((Vector2)lhs.boundary.center - position).sqrMagnitude.CompareTo(((Vector2)rhs.boundary.center - position).sqrMagnitude);
|
|
149
|
-
|
|
150
234
|
while (!current.isTerminal)
|
|
151
235
|
{
|
|
152
236
|
childrenCopy.Clear();
|
|
@@ -175,10 +259,21 @@
|
|
|
175
259
|
nearestNeighbors.AddRange(nearestNeighborsSet);
|
|
176
260
|
if (count < nearestNeighbors.Count)
|
|
177
261
|
{
|
|
178
|
-
int NearestComparison(T lhs, T rhs) => (_elementTransformer(lhs) - position).sqrMagnitude.CompareTo((_elementTransformer(rhs) - position).sqrMagnitude);
|
|
179
262
|
nearestNeighbors.Sort(NearestComparison);
|
|
180
263
|
nearestNeighbors.RemoveRange(count, nearestNeighbors.Count - count);
|
|
181
264
|
}
|
|
265
|
+
|
|
266
|
+
return;
|
|
267
|
+
|
|
268
|
+
int Comparison(QuadTreeNode<T> lhs, QuadTreeNode<T> rhs) =>
|
|
269
|
+
((Vector2)lhs.boundary.center - position).sqrMagnitude.CompareTo(
|
|
270
|
+
((Vector2)rhs.boundary.center - position).sqrMagnitude
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
int NearestComparison(T lhs, T rhs) =>
|
|
274
|
+
(_elementTransformer(lhs) - position).sqrMagnitude.CompareTo(
|
|
275
|
+
(_elementTransformer(rhs) - position).sqrMagnitude
|
|
276
|
+
);
|
|
182
277
|
}
|
|
183
278
|
}
|
|
184
279
|
}
|