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.
@@ -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
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 8e55e0cc59d640b3affc8f4d9f865c50
3
+ timeCreated: 1735605040
package/README.md CHANGED
@@ -1,2 +1,2 @@
1
1
  # A Grab-Bag
2
- Various Unity Helpers. Includes deterministic randoms including a PCGRandom implementation,
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
- private sealed class KDTreeNode<V>
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(List<V> elements, Func<V, Vector2> elementTransformer, int bucketSize, bool isXAxis, bool balanced)
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>(elements.Take(cutoff).ToList(), elementTransformer, bucketSize, !isXAxis, true);
48
- right = new KDTreeNode<V>(elements.Skip(cutoff).ToList(), elementTransformer, bucketSize, !isXAxis, true);
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>(elements.Where(element => elementTransformer(element).x <= cutoff.x).ToList(), elementTransformer, bucketSize, false, false);
56
- right = new KDTreeNode<V>(elements.Where(element => cutoff.x < elementTransformer(element).x).ToList(), elementTransformer, bucketSize, false, false);
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>(elements.Where(element => elementTransformer(element).y <= cutoff.y).ToList(), elementTransformer, bucketSize, true, false);
61
- right = new KDTreeNode<V>(elements.Where(element => cutoff.y < elementTransformer(element).y).ToList(), elementTransformer, bucketSize, true, false);
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(IEnumerable<T> points, Func<T, Vector2> elementTransformer, int bucketSize = DefaultBucketSize, bool balanced = true)
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 = elementTransformer ?? throw new ArgumentNullException(nameof(elementTransformer));
80
- elements = points?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(points));
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>(elements.ToList(), elementTransformer, bucketSize: bucketSize, isXAxis:true, balanced: balanced);
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(Vector2 position, int count, List<T> nearestNeighbors)
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 (((Vector2)left.boundary.center - position).sqrMagnitude <
149
- ((Vector2)right.boundary.center - position).sqrMagnitude)
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
- private sealed class QuadTreeNode<V>
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
- public readonly QuadTreeNode<V>[] children;
22
+ internal readonly QuadTreeNode<V>[] children;
22
23
  public readonly V[] elements;
23
24
  public readonly bool isTerminal;
24
25
 
25
- public QuadTreeNode(V[] elements, Func<V, Vector2> elementTransformer, Bounds boundary,
26
- int bucketSize)
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(new Vector3(boundary.center.x - halfQuadrantSize.x, boundary.center.y + halfQuadrantSize.y, boundary.center.z), quadrantSize),
44
- new Bounds(new Vector3(boundary.center.x + halfQuadrantSize.x, boundary.center.y + halfQuadrantSize.y, boundary.center.z), quadrantSize),
45
- new Bounds(new Vector3(boundary.center.x + halfQuadrantSize.x, boundary.center.y - halfQuadrantSize.y, boundary.center.z), quadrantSize),
46
- new Bounds(new Vector3(boundary.center.x - halfQuadrantSize.x, boundary.center.y - halfQuadrantSize.y, boundary.center.z), quadrantSize),
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>(Buffer.ToArray(), elementTransformer, quadrant, bucketSize);
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(IEnumerable<T> points, Func<T, Vector2> elementTransformer, Bounds? boundary = null,
77
- int bucketSize = DefaultBucketSize)
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 = elementTransformer ?? throw new ArgumentNullException(nameof(elementTransformer));
80
- elements = points?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(points));
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>(elements.ToArray(), elementTransformer, _bounds, bucketSize);
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(Vector2 position, int count, List<T> nearestNeighbors)
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 = new(NumChildren);
146
- HashSet<T> nearestNeighborsSet = new(count);
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
  }