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.
@@ -6,19 +6,25 @@
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 RTree<T>
12
13
  {
13
14
  [Serializable]
14
- private sealed class RTreeNode<V>
15
+ public sealed class RTreeNode<V>
15
16
  {
16
17
  public readonly Bounds boundary;
17
- public readonly RTreeNode<V>[] children;
18
+ internal readonly RTreeNode<V>[] children;
18
19
  public readonly V[] elements;
19
20
  public readonly bool isTerminal;
20
21
 
21
- public RTreeNode(List<V> elements, Func<V, Bounds> elementTransformer, int bucketSize, int branchFactor)
22
+ public RTreeNode(
23
+ List<V> elements,
24
+ Func<V, Bounds> elementTransformer,
25
+ int bucketSize,
26
+ int branchFactor
27
+ )
22
28
  {
23
29
  float minX = float.MaxValue;
24
30
  float minY = float.MaxValue;
@@ -35,7 +41,13 @@
35
41
  maxY = Math.Max(maxY, max.y);
36
42
  }
37
43
 
38
- boundary = elements.Count <= 0 ? new Bounds() : new Bounds(new Vector3(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2), new Vector3(maxX - minX, maxY - minY));
44
+ boundary =
45
+ elements.Count <= 0
46
+ ? new Bounds()
47
+ : new Bounds(
48
+ new Vector3(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2),
49
+ new Vector3(maxX - minX, maxY - minY)
50
+ );
39
51
  this.elements = elements.ToArray();
40
52
  isTerminal = elements.Count <= bucketSize;
41
53
  if (isTerminal)
@@ -61,28 +73,44 @@
61
73
  double slicesPerAxis = Math.Sqrt(branchFactor);
62
74
  int rectanglesPerPagePerAxis = (int)(slicesPerAxis * targetSize);
63
75
 
64
- int XAxis(V lhs, V rhs)
65
- {
66
- return elementTransformer(lhs).center.x.CompareTo(elementTransformer(rhs).center.x);
67
- }
68
-
69
- int YAxis(V lhs, V rhs)
70
- {
71
- return elementTransformer(lhs).center.y.CompareTo(elementTransformer(rhs).center.y);
72
- }
73
-
74
76
  elements.Sort(XAxis);
75
- foreach (List<V> xSlice in elements.Partition(rectanglesPerPagePerAxis).Select(enumerable => enumerable as List<V> ?? enumerable.ToList()))
77
+ foreach (
78
+ List<V> xSlice in elements
79
+ .Partition(rectanglesPerPagePerAxis)
80
+ .Select(enumerable => enumerable as List<V> ?? enumerable.ToList())
81
+ )
76
82
  {
77
83
  xSlice.Sort(YAxis);
78
- foreach (List<V> ySlice in xSlice.Partition(intTargetSize).Select(enumerable => enumerable as List<V> ?? enumerable.ToList()))
84
+ foreach (
85
+ List<V> ySlice in xSlice
86
+ .Partition(intTargetSize)
87
+ .Select(enumerable => enumerable as List<V> ?? enumerable.ToList())
88
+ )
79
89
  {
80
- RTreeNode<V> node = new(ySlice, elementTransformer, bucketSize, branchFactor);
90
+ RTreeNode<V> node = new(
91
+ ySlice,
92
+ elementTransformer,
93
+ bucketSize,
94
+ branchFactor
95
+ );
81
96
  tempChildren.Add(node);
82
97
  }
83
98
  }
84
99
 
85
100
  children = tempChildren.ToArray();
101
+ return;
102
+
103
+ int XAxis(V lhs, V rhs)
104
+ {
105
+ return elementTransformer(lhs)
106
+ .center.x.CompareTo(elementTransformer(rhs).center.x);
107
+ }
108
+
109
+ int YAxis(V lhs, V rhs)
110
+ {
111
+ return elementTransformer(lhs)
112
+ .center.y.CompareTo(elementTransformer(rhs).center.y);
113
+ }
86
114
  }
87
115
  }
88
116
 
@@ -97,16 +125,30 @@
97
125
  private readonly RTreeNode<T> _head;
98
126
 
99
127
  public RTree(
100
- IEnumerable<T> points, Func<T, Bounds> elementTransformer, int bucketSize = DefaultBucketSize,
101
- int branchFactor = DefaultBranchFactor)
128
+ IEnumerable<T> points,
129
+ Func<T, Bounds> elementTransformer,
130
+ int bucketSize = DefaultBucketSize,
131
+ int branchFactor = DefaultBranchFactor
132
+ )
102
133
  {
103
- _elementTransformer = elementTransformer ?? throw new ArgumentNullException(nameof(elementTransformer));
104
- elements = points?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(points));
134
+ _elementTransformer =
135
+ elementTransformer ?? throw new ArgumentNullException(nameof(elementTransformer));
136
+ elements =
137
+ points?.ToImmutableArray() ?? throw new ArgumentNullException(nameof(points));
105
138
  _bounds = elements.Select(elementTransformer).GetBounds() ?? new Bounds();
106
- _head = new RTreeNode<T>(elements.ToList(), elementTransformer, bucketSize, branchFactor);
139
+ _head = new RTreeNode<T>(
140
+ elements.ToList(),
141
+ elementTransformer,
142
+ bucketSize,
143
+ branchFactor
144
+ );
107
145
  }
108
146
 
109
- public IEnumerable<T> GetElementsInRange(Vector2 position, float range, float minimumRange = 0f)
147
+ public IEnumerable<T> GetElementsInRange(
148
+ Vector2 position,
149
+ float range,
150
+ float minimumRange = 0f
151
+ )
110
152
  {
111
153
  Circle area = new(position, range);
112
154
  if (0 < minimumRange)
@@ -115,25 +157,10 @@
115
157
  return GetElementsInBounds(
116
158
  new Bounds(
117
159
  new Vector3(position.x, position.y, 0f),
118
- new Vector3(range * 2f, range * 2f, 1f)))
119
- .Where(
120
- element =>
121
- {
122
- Bounds elementBoundary = _elementTransformer(element);
123
- if (!area.Intersects(elementBoundary))
124
- {
125
- return false;
126
- }
127
-
128
- return !minimumArea.Intersects(elementBoundary);
129
- });
130
- }
131
- return GetElementsInBounds(
132
- new Bounds(
133
- new Vector3(position.x, position.y, 0f),
134
- new Vector3(range * 2f, range * 2f, 1f)))
135
- .Where(
136
- element =>
160
+ new Vector3(range * 2f, range * 2f, 1f)
161
+ )
162
+ )
163
+ .Where(element =>
137
164
  {
138
165
  Bounds elementBoundary = _elementTransformer(element);
139
166
  if (!area.Intersects(elementBoundary))
@@ -141,18 +168,42 @@
141
168
  return false;
142
169
  }
143
170
 
144
- return true;
171
+ return !minimumArea.Intersects(elementBoundary);
145
172
  });
173
+ }
174
+ return GetElementsInBounds(
175
+ new Bounds(
176
+ new Vector3(position.x, position.y, 0f),
177
+ new Vector3(range * 2f, range * 2f, 1f)
178
+ )
179
+ )
180
+ .Where(element =>
181
+ {
182
+ Bounds elementBoundary = _elementTransformer(element);
183
+ if (!area.Intersects(elementBoundary))
184
+ {
185
+ return false;
186
+ }
187
+
188
+ return true;
189
+ });
146
190
  }
147
191
 
148
192
  public IEnumerable<T> GetElementsInBounds(Bounds bounds)
193
+ {
194
+ Stack<RTreeNode<T>> nodeBuffer = Buffers<RTreeNode<T>>.Stack;
195
+ return GetElementsInBounds(bounds, nodeBuffer);
196
+ }
197
+
198
+ public IEnumerable<T> GetElementsInBounds(Bounds bounds, Stack<RTreeNode<T>> nodeBuffer)
149
199
  {
150
200
  if (!bounds.FastIntersects2D(_bounds))
151
201
  {
152
202
  yield break;
153
203
  }
154
204
 
155
- Stack<RTreeNode<T>> nodesToVisit = new();
205
+ Stack<RTreeNode<T>> nodesToVisit = nodeBuffer ?? new Stack<RTreeNode<T>>();
206
+ nodeBuffer.Clear();
156
207
  nodesToVisit.Push(_head);
157
208
 
158
209
  while (nodesToVisit.TryPop(out RTreeNode<T> currentNode))
@@ -197,18 +248,45 @@
197
248
  }
198
249
  }
199
250
 
251
+ public void GetApproximateNearestNeighbors(
252
+ Vector2 position,
253
+ int count,
254
+ List<T> nearestNeighbors
255
+ )
256
+ {
257
+ Stack<RTreeNode<T>> nodeBuffer = Buffers<RTreeNode<T>>.Stack;
258
+ List<RTreeNode<T>> childrenBuffer = Buffers<RTreeNode<T>>.List;
259
+ HashSet<T> nearestNeighborBuffer = Buffers<T>.HashSet;
260
+ GetApproximateNearestNeighbors(
261
+ position,
262
+ count,
263
+ nearestNeighbors,
264
+ nodeBuffer,
265
+ childrenBuffer,
266
+ nearestNeighborBuffer
267
+ );
268
+ }
269
+
200
270
  // Heavily adapted http://homepage.divms.uiowa.edu/%7Ekvaradar/sp2012/daa/ann.pdf
201
- public void GetApproximateNearestNeighbors(Vector2 position, int count, List<T> nearestNeighbors)
271
+ public void GetApproximateNearestNeighbors(
272
+ Vector2 position,
273
+ int count,
274
+ List<T> nearestNeighbors,
275
+ Stack<RTreeNode<T>> nodeBuffer,
276
+ List<RTreeNode<T>> childrenBuffer,
277
+ HashSet<T> nearestNeighborsBuffer
278
+ )
202
279
  {
203
280
  nearestNeighbors.Clear();
204
281
 
205
282
  RTreeNode<T> current = _head;
206
- Stack<RTreeNode<T>> stack = new();
283
+ Stack<RTreeNode<T>> stack = nodeBuffer ?? new Stack<RTreeNode<T>>();
284
+ stack.Clear();
207
285
  stack.Push(_head);
208
- List<RTreeNode<T>> childrenCopy = new();
209
- HashSet<T> nearestNeighborsSet = new(count);
210
-
211
- int Comparison(RTreeNode<T> lhs, RTreeNode<T> rhs) => ((Vector2)lhs.boundary.center - position).sqrMagnitude.CompareTo(((Vector2)rhs.boundary.center - position).sqrMagnitude);
286
+ List<RTreeNode<T>> childrenCopy = childrenBuffer ?? new List<RTreeNode<T>>();
287
+ childrenCopy.Clear();
288
+ HashSet<T> nearestNeighborsSet = nearestNeighborsBuffer ?? new HashSet<T>(count);
289
+ nearestNeighborsSet.Clear();
212
290
 
213
291
  while (!current.isTerminal)
214
292
  {
@@ -238,10 +316,21 @@
238
316
  nearestNeighbors.AddRange(nearestNeighborsSet);
239
317
  if (count < nearestNeighbors.Count)
240
318
  {
241
- int NearestComparison(T lhs, T rhs) => ((Vector2)_elementTransformer(lhs).center - position).sqrMagnitude.CompareTo(((Vector2)_elementTransformer(rhs).center - position).sqrMagnitude);
242
319
  nearestNeighbors.Sort(NearestComparison);
243
320
  nearestNeighbors.RemoveRange(count, nearestNeighbors.Count - count);
244
321
  }
322
+
323
+ return;
324
+
325
+ int Comparison(RTreeNode<T> lhs, RTreeNode<T> rhs) =>
326
+ ((Vector2)lhs.boundary.center - position).sqrMagnitude.CompareTo(
327
+ ((Vector2)rhs.boundary.center - position).sqrMagnitude
328
+ );
329
+
330
+ int NearestComparison(T lhs, T rhs) =>
331
+ ((Vector2)_elementTransformer(lhs).center - position).sqrMagnitude.CompareTo(
332
+ ((Vector2)_elementTransformer(rhs).center - position).sqrMagnitude
333
+ );
245
334
  }
246
335
  }
247
336
  }
@@ -1,13 +1,279 @@
1
1
  namespace UnityHelpers.Core.Extension
2
2
  {
3
3
  using System;
4
+ using System.Collections.Generic;
5
+ using System.ComponentModel;
6
+ using System.Linq;
7
+ using Helper;
4
8
  using Random;
5
9
  using UnityEngine;
6
10
 
11
+ public enum ColorAveragingMethod
12
+ {
13
+ LAB = 0, // CIE L*a*b* space averaging
14
+ HSV = 1, // HSV space averaging
15
+ Weighted = 2, // Weighted RGB averaging using perceived luminance
16
+ Dominant = 3, // Find most dominant color cluster
17
+ }
18
+
7
19
  // https://sharpsnippets.wordpress.com/2014/03/11/c-extension-complementary-color/
8
20
  public static class ColorExtensions
9
21
  {
10
- public static Color GetComplement(this Color source, IRandom random = null, float variance = 0f)
22
+ public static Color GetAverageColor(
23
+ this Sprite sprite,
24
+ ColorAveragingMethod method = ColorAveragingMethod.LAB,
25
+ float alphaCutoff = 0.01f
26
+ )
27
+ {
28
+ return GetAverageColor(Enumerables.Of(sprite), method, alphaCutoff);
29
+ }
30
+
31
+ public static Color GetAverageColor(
32
+ this IEnumerable<Sprite> sprites,
33
+ ColorAveragingMethod method = ColorAveragingMethod.LAB,
34
+ float alphaCutoff = 0.01f
35
+ )
36
+ {
37
+ return GetAverageColor(
38
+ sprites
39
+ .Where(Objects.NotNull)
40
+ .Select(sprite => sprite.texture)
41
+ .Where(Objects.NotNull)
42
+ .SelectMany(texture =>
43
+ {
44
+ texture.MakeReadable();
45
+ Color[] pixels = texture.GetPixels();
46
+ return pixels;
47
+ }),
48
+ method,
49
+ alphaCutoff
50
+ );
51
+ }
52
+
53
+ public static Color GetAverageColor(
54
+ this IEnumerable<Color> pixels,
55
+ ColorAveragingMethod method = ColorAveragingMethod.LAB,
56
+ float alphaCutoff = 0.01f
57
+ )
58
+ {
59
+ switch (method)
60
+ {
61
+ case ColorAveragingMethod.LAB:
62
+ {
63
+ return AverageInLABSpace(pixels, alphaCutoff);
64
+ }
65
+ case ColorAveragingMethod.HSV:
66
+ {
67
+ return AverageInHSVSpace(pixels, alphaCutoff);
68
+ }
69
+ case ColorAveragingMethod.Weighted:
70
+ {
71
+ return WeightedRGBAverage(pixels, alphaCutoff);
72
+ }
73
+ case ColorAveragingMethod.Dominant:
74
+ {
75
+ return GetDominantColor(pixels, alphaCutoff);
76
+ }
77
+ default:
78
+ {
79
+ throw new InvalidEnumArgumentException(
80
+ nameof(method),
81
+ (int)method,
82
+ typeof(ColorAveragingMethod)
83
+ );
84
+ }
85
+ }
86
+ }
87
+
88
+ // CIE L*a*b* space averaging - most perceptually accurate
89
+ private static Color AverageInLABSpace(IEnumerable<Color> pixels, float alphaCutoff)
90
+ {
91
+ List<LABColor> labValues = pixels
92
+ .Where(pixel => pixel.a > alphaCutoff)
93
+ .Select(RGBToLAB)
94
+ .ToList();
95
+
96
+ double avgL = labValues.Average(lab => lab.l);
97
+ double avgA = labValues.Average(lab => lab.a);
98
+ double avgB = labValues.Average(lab => lab.b);
99
+
100
+ return LABToRGB(avgL, avgA, avgB);
101
+ }
102
+
103
+ // HSV space averaging - good for preserving vibrant colors
104
+ private static Color AverageInHSVSpace(IEnumerable<Color> pixels, float alphaCutoff)
105
+ {
106
+ float avgH = 0f;
107
+ float avgS = 0f;
108
+ float avgV = 0f;
109
+ int count = 0;
110
+
111
+ foreach (Color pixel in pixels.Where(pixel => pixel.a > alphaCutoff))
112
+ {
113
+ Color.RGBToHSV(pixel, out float h, out float s, out float v);
114
+
115
+ // Handle hue wrapping around 360 degrees
116
+ float hRad = h * 2f * Mathf.PI;
117
+ avgH += Mathf.Cos(hRad);
118
+ avgH += Mathf.Sin(hRad);
119
+
120
+ avgS += s;
121
+ avgV += v;
122
+ count++;
123
+ }
124
+
125
+ avgH = Mathf.Atan2(avgH / count, avgH / count) / (2f * Mathf.PI);
126
+ if (avgH < 0)
127
+ {
128
+ avgH += 1f;
129
+ }
130
+
131
+ if (count <= 0)
132
+ {
133
+ count = 1;
134
+ }
135
+
136
+ avgS /= count;
137
+ avgV /= count;
138
+
139
+ return Color.HSVToRGB(avgH, avgS, avgV);
140
+ }
141
+
142
+ // Weighted RGB averaging using perceived luminance
143
+ private static Color WeightedRGBAverage(IEnumerable<Color> pixels, float alphaCutoff)
144
+ {
145
+ // Use perceived luminance weights
146
+ const float rWeight = 0.299f;
147
+ const float gWeight = 0.587f;
148
+ const float bWeight = 0.114f;
149
+
150
+ float totalWeight = 0f;
151
+ float r = 0f,
152
+ g = 0f,
153
+ b = 0f,
154
+ a = 0f;
155
+
156
+ foreach (Color pixel in pixels.Where(pixel => pixel.a > alphaCutoff))
157
+ {
158
+ float weight = pixel.r * rWeight + pixel.g * gWeight + pixel.b * bWeight;
159
+ r += pixel.r * weight;
160
+ g += pixel.g * weight;
161
+ b += pixel.b * weight;
162
+ a += pixel.a * weight;
163
+ totalWeight += weight;
164
+ }
165
+
166
+ if (totalWeight > 0f)
167
+ {
168
+ r /= totalWeight;
169
+ g /= totalWeight;
170
+ b /= totalWeight;
171
+ a /= totalWeight;
172
+ }
173
+
174
+ return new Color(r, g, b, a);
175
+ }
176
+
177
+ // Find dominant color using simple clustering
178
+ private static Color GetDominantColor(IEnumerable<Color> pixels, float alphaCutoff)
179
+ {
180
+ Dictionary<Vector3Int, int> colorBuckets = new();
181
+ const int bucketSize = 32; // Adjust for different precision
182
+
183
+ foreach (Color pixel in pixels.Where(pixel => pixel.a > alphaCutoff))
184
+ {
185
+ Vector3Int bucket = new(
186
+ Mathf.RoundToInt(pixel.r * 255 / bucketSize),
187
+ Mathf.RoundToInt(pixel.g * 255 / bucketSize),
188
+ Mathf.RoundToInt(pixel.b * 255 / bucketSize)
189
+ );
190
+
191
+ colorBuckets.TryAdd(bucket, 0);
192
+ colorBuckets[bucket]++;
193
+ }
194
+
195
+ Vector3Int dominantBucket = colorBuckets
196
+ .OrderByDescending(kvp => kvp.Value)
197
+ .First()
198
+ .Key;
199
+ return new Color(
200
+ dominantBucket.x * bucketSize / 255f,
201
+ dominantBucket.y * bucketSize / 255f,
202
+ dominantBucket.z * bucketSize / 255f
203
+ );
204
+ }
205
+
206
+ // Helper struct for LAB color space
207
+ private readonly struct LABColor
208
+ {
209
+ public readonly double l;
210
+ public readonly double a;
211
+ public readonly double b;
212
+
213
+ public LABColor(double l, double a, double b)
214
+ {
215
+ this.l = l;
216
+ this.a = a;
217
+ this.b = b;
218
+ }
219
+ }
220
+
221
+ private static LABColor RGBToLAB(Color rgb)
222
+ {
223
+ // First convert to XYZ
224
+ double r =
225
+ rgb.r > 0.04045 ? Mathf.Pow((rgb.r + 0.055f) / 1.055f, 2.4f) : rgb.r / 12.92f;
226
+ double g =
227
+ rgb.g > 0.04045 ? Mathf.Pow((rgb.g + 0.055f) / 1.055f, 2.4f) : rgb.g / 12.92f;
228
+ double b =
229
+ rgb.b > 0.04045 ? Mathf.Pow((rgb.b + 0.055f) / 1.055f, 2.4f) : rgb.b / 12.92f;
230
+
231
+ double x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047;
232
+ double y = (r * 0.2126 + g * 0.7152 + b * 0.0722);
233
+ double z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883;
234
+
235
+ x = x > 0.008856 ? Mathf.Pow((float)x, 1f / 3f) : (7.787 * x) + 16f / 116f;
236
+ y = y > 0.008856 ? Mathf.Pow((float)y, 1f / 3f) : (7.787 * y) + 16f / 116f;
237
+ z = z > 0.008856 ? Mathf.Pow((float)z, 1f / 3f) : (7.787 * z) + 16f / 116f;
238
+
239
+ return new LABColor((116 * y) - 16, 500 * (x - y), 200 * (y - z));
240
+ }
241
+
242
+ private static Color LABToRGB(double l, double a, double b)
243
+ {
244
+ double y = (l + 16) / 116;
245
+ double x = a / 500 + y;
246
+ double z = y - b / 200;
247
+
248
+ double x3 = x * x * x;
249
+ double y3 = y * y * y;
250
+ double z3 = z * z * z;
251
+
252
+ x = 0.95047 * (x3 > 0.008856 ? x3 : (x - 16.0 / 116.0) / 7.787);
253
+ y = y3 > 0.008856 ? y3 : (y - 16.0 / 116.0) / 7.787;
254
+ z = 1.08883 * (z3 > 0.008856 ? z3 : (z - 16.0 / 116.0) / 7.787);
255
+
256
+ double r = x * 3.2406 + y * -1.5372 + z * -0.4986;
257
+ double g = x * -0.9689 + y * 1.8758 + z * 0.0415;
258
+ double b2 = x * 0.0557 + y * -0.2040 + z * 1.0570;
259
+
260
+ r = r > 0.0031308 ? 1.055 * Mathf.Pow((float)r, 1 / 2.4f) - 0.055 : 12.92 * r;
261
+ g = g > 0.0031308 ? 1.055 * Mathf.Pow((float)g, 1 / 2.4f) - 0.055 : 12.92 * g;
262
+ b2 = b2 > 0.0031308 ? 1.055 * Mathf.Pow((float)b2, 1 / 2.4f) - 0.055 : 12.92 * b2;
263
+
264
+ return new Color(
265
+ Mathf.Clamp01((float)r),
266
+ Mathf.Clamp01((float)g),
267
+ Mathf.Clamp01((float)b2),
268
+ 1f
269
+ );
270
+ }
271
+
272
+ public static Color GetComplement(
273
+ this Color source,
274
+ IRandom random = null,
275
+ float variance = 0f
276
+ )
11
277
  {
12
278
  Color inputColor = source;
13
279
  //if RGB values are close to each other by a diff less than 10%, then if RGB values are lighter side, decrease the blue by 50% (eventually it will increase in conversion below), if RBB values are on darker side, decrease yellow by about 50% (it will increase in conversion)
@@ -14,6 +14,16 @@
14
14
 
15
15
  public static class UnityExtensions
16
16
  {
17
+ public static Vector2 GetCenter(this GameObject gameObject)
18
+ {
19
+ if (gameObject.TryGetComponent(out CenterPointOffset centerPointOffset))
20
+ {
21
+ return centerPointOffset.CenterPoint;
22
+ }
23
+
24
+ return gameObject.transform.position;
25
+ }
26
+
17
27
  public static Bounds Bounds(this Rect rect)
18
28
  {
19
29
  return new Bounds(rect.center, rect.size);
@@ -1,9 +1,26 @@
1
1
  namespace UnityHelpers.Core.Helper
2
2
  {
3
+ using System.Collections.Generic;
4
+ using System.Linq;
3
5
  using UnityEngine;
4
6
 
5
7
  public static class Geometry
6
8
  {
9
+ public static Rect Accumulate(this IEnumerable<Rect> rects)
10
+ {
11
+ return rects.Aggregate(
12
+ (accumulated, next) =>
13
+ new Rect(
14
+ Mathf.Min(accumulated.xMin, next.xMin),
15
+ Mathf.Min(accumulated.yMin, next.yMin),
16
+ Mathf.Max(accumulated.xMax, next.xMax)
17
+ - Mathf.Min(accumulated.xMin, next.xMin),
18
+ Mathf.Max(accumulated.yMax, next.yMax)
19
+ - Mathf.Min(accumulated.yMin, next.yMin)
20
+ )
21
+ );
22
+ }
23
+
7
24
  //Where is p in relation to a-b
8
25
  // < 0 -> to the right
9
26
  // = 0 -> on the line
@@ -1,11 +1,41 @@
1
1
  namespace UnityHelpers.Core.Helper
2
2
  {
3
+ using Extension;
3
4
  using UnityEditor;
4
5
  using UnityEngine;
5
6
  using Utils;
6
7
 
7
8
  public static class SpriteHelpers
8
9
  {
10
+ public static void MakeReadable(this Texture2D texture)
11
+ {
12
+ if (texture.isReadable)
13
+ {
14
+ return;
15
+ }
16
+
17
+ #if UNITY_EDITOR
18
+ string assetPath = AssetDatabase.GetAssetPath(texture);
19
+ if (string.IsNullOrEmpty(assetPath))
20
+ {
21
+ texture.LogError("Failed to get asset path.");
22
+ return;
23
+ }
24
+
25
+ TextureImporter tImporter = AssetImporter.GetAtPath(assetPath) as TextureImporter;
26
+ if (tImporter == null)
27
+ {
28
+ texture.LogError("Failed to get texture importer.");
29
+ return;
30
+ }
31
+
32
+ tImporter.isReadable = true;
33
+ EditorUtility.SetDirty(tImporter);
34
+ tImporter.SaveAndReimport();
35
+ EditorUtility.SetDirty(texture);
36
+ #endif
37
+ }
38
+
9
39
  public static void SetSpritePivot(string fullSpritePath, Vector2 pivot)
10
40
  {
11
41
  #if UNITY_EDITOR
@@ -16,7 +46,10 @@
16
46
  public static void SetSpritePivot(Sprite sprite, Vector2 pivot)
17
47
  {
18
48
  #if UNITY_EDITOR
19
- SetSpritePivot(AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sprite)) as TextureImporter, pivot);
49
+ SetSpritePivot(
50
+ AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(sprite)) as TextureImporter,
51
+ pivot
52
+ );
20
53
  #endif
21
54
  }
22
55
 
@@ -30,7 +63,7 @@
30
63
 
31
64
  TextureImporterSettings textureImportSettings = new TextureImporterSettings();
32
65
  textureImporter.ReadTextureSettings(textureImportSettings);
33
- textureImportSettings.spriteAlignment = (int) SpriteAlignment.Custom;
66
+ textureImportSettings.spriteAlignment = (int)SpriteAlignment.Custom;
34
67
  textureImportSettings.wrapMode = TextureWrapMode.Clamp;
35
68
  textureImportSettings.filterMode = FilterMode.Trilinear;
36
69
  textureImporter.SetTextureSettings(textureImportSettings);
@@ -40,7 +73,7 @@
40
73
  resizeAlgorithm = TextureResizeAlgorithm.Bilinear,
41
74
  maxTextureSize = SetTextureImportData.RegularTextureSize,
42
75
  textureCompression = TextureImporterCompression.Compressed,
43
- format = TextureImporterFormat.Automatic
76
+ format = TextureImporterFormat.Automatic,
44
77
  };
45
78
 
46
79
  textureImporter.SetPlatformTextureSettings(importerSettings);