AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
BoundsExtensions.cs
Go to the documentation of this file.
1 //
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // Licensed under the MIT License. See LICENSE in the project root for license information.
4 //
5 
6 using UnityEngine;
7 
8 public static class BoundsExtensions
9 {
10  // Corners
11  public const int LBF = 0;
12  public const int LBB = 1;
13  public const int LTF = 2;
14  public const int LTB = 3;
15  public const int RBF = 4;
16  public const int RBB = 5;
17  public const int RTF = 6;
18  public const int RTB = 7;
19 
20  // X axis
21  public const int LTF_RTF = 8;
22  public const int LBF_RBF = 9;
23  public const int RTB_LTB = 10;
24  public const int RBB_LBB = 11;
25 
26  // Y axis
27  public const int LTF_LBF = 12;
28  public const int RTB_RBB = 13;
29  public const int LTB_LBB = 14;
30  public const int RTF_RBF = 15;
31 
32  // Z axis
33  public const int RBF_RBB = 16;
34  public const int RTF_RTB = 17;
35  public const int LBF_LBB = 18;
36  public const int LTF_LTB = 19;
37 
38  // 2D corners
39  public const int LT = 0;
40  public const int LB = 1;
41  public const int RT = 2;
42  public const int RB = 3;
43 
44  // 2D midpoints
45  public const int LT_RT = 4;
46  public const int RT_RB = 5;
47  public const int RB_LB = 6;
48  public const int LB_LT = 7;
49 
50  // Face points
51  public const int TOP = 0;
52  public const int BOT = 1;
53  public const int LFT = 2;
54  public const int RHT = 3;
55  public const int FWD = 4;
56  public const int BCK = 5;
57 
58  public enum Axis
59  {
60  X,
61  Y,
62  Z
63  }
64 
65  #region Public Static Functions
66  public static Bounds GetInvalidBoundsInstance()
72  {
73  return new Bounds(Vector3.zero, GetInvalidBoundsSize());
74  }
75 
80  public static bool IsValid(this Bounds bounds)
81  {
82  return bounds.size != GetInvalidBoundsSize();
83  }
84 
94  public static void GetCornerPositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
95  {
96  // Calculate the local points to transform.
97  Vector3 center = bounds.center;
98  Vector3 extents = bounds.extents;
99  float leftEdge = center.x - extents.x;
100  float rightEdge = center.x + extents.x;
101  float bottomEdge = center.y - extents.y;
102  float topEdge = center.y + extents.y;
103  float frontEdge = center.z - extents.z;
104  float backEdge = center.z + extents.z;
105 
106  // Allocate the array if needed.
107  const int numPoints = 8;
108  if (positions == null || positions.Length != numPoints)
109  {
110  positions = new Vector3[numPoints];
111  }
112 
113  // Transform all the local points to world space.
114  positions[BoundsExtensions.LBF] = transform.TransformPoint(leftEdge, bottomEdge, frontEdge);
115  positions[BoundsExtensions.LBB] = transform.TransformPoint(leftEdge, bottomEdge, backEdge);
116  positions[BoundsExtensions.LTF] = transform.TransformPoint(leftEdge, topEdge, frontEdge);
117  positions[BoundsExtensions.LTB] = transform.TransformPoint(leftEdge, topEdge, backEdge);
118  positions[BoundsExtensions.RBF] = transform.TransformPoint(rightEdge, bottomEdge, frontEdge);
119  positions[BoundsExtensions.RBB] = transform.TransformPoint(rightEdge, bottomEdge, backEdge);
120  positions[BoundsExtensions.RTF] = transform.TransformPoint(rightEdge, topEdge, frontEdge);
121  positions[BoundsExtensions.RTB] = transform.TransformPoint(rightEdge, topEdge, backEdge);
122  }
123 
129  public static void GetCornerPositionsFromRendererBounds(this Bounds bounds, ref Vector3[] positions)
130  {
131  Vector3 center = bounds.center;
132  Vector3 extents = bounds.extents;
133  float leftEdge = center.x - extents.x;
134  float rightEdge = center.x + extents.x;
135  float bottomEdge = center.y - extents.y;
136  float topEdge = center.y + extents.y;
137  float frontEdge = center.z - extents.z;
138  float backEdge = center.z + extents.z;
139 
140  const int numPoints = 8;
141  if (positions == null || positions.Length != numPoints)
142  {
143  positions = new Vector3[numPoints];
144  }
145 
146  positions[BoundsExtensions.LBF] = new Vector3(leftEdge, bottomEdge, frontEdge);
147  positions[BoundsExtensions.LBB] = new Vector3(leftEdge, bottomEdge, backEdge);
148  positions[BoundsExtensions.LTF] = new Vector3(leftEdge, topEdge, frontEdge);
149  positions[BoundsExtensions.LTB] = new Vector3(leftEdge, topEdge, backEdge);
150  positions[BoundsExtensions.RBF] = new Vector3(rightEdge, bottomEdge, frontEdge);
151  positions[BoundsExtensions.RBB] = new Vector3(rightEdge, bottomEdge, backEdge);
152  positions[BoundsExtensions.RTF] = new Vector3(rightEdge, topEdge, frontEdge);
153  positions[BoundsExtensions.RTB] = new Vector3(rightEdge, topEdge, backEdge);
154  }
155 
156  public static void GetFacePositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
157  {
158  Vector3 center = bounds.center;
159  Vector3 extents = bounds.extents;
160 
161  const int numPoints = 6;
162  if (positions == null || positions.Length != numPoints)
163  {
164  positions = new Vector3[numPoints];
165  }
166 
167  positions[BoundsExtensions.TOP] = transform.TransformPoint(center + Vector3.up * extents.y);
168  positions[BoundsExtensions.BOT] = transform.TransformPoint(center + Vector3.down * extents.y);
169  positions[BoundsExtensions.LFT] = transform.TransformPoint(center + Vector3.left * extents.x);
170  positions[BoundsExtensions.RHT] = transform.TransformPoint(center + Vector3.right * extents.x);
171  positions[BoundsExtensions.FWD] = transform.TransformPoint(center + Vector3.forward * extents.z);
172  positions[BoundsExtensions.BCK] = transform.TransformPoint(center + Vector3.back * extents.z);
173  }
174 
180  public static void GetCornerAndMidPointPositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
181  {
182  // Calculate the local points to transform.
183  Vector3 center = bounds.center;
184  Vector3 extents = bounds.extents;
185  float leftEdge = center.x - extents.x;
186  float rightEdge = center.x + extents.x;
187  float bottomEdge = center.y - extents.y;
188  float topEdge = center.y + extents.y;
189  float frontEdge = center.z - extents.z;
190  float backEdge = center.z + extents.z;
191 
192  // Allocate the array if needed.
193  const int numPoints = BoundsExtensions.LTF_LTB + 1;
194  if (positions == null || positions.Length != numPoints)
195  {
196  positions = new Vector3[numPoints];
197  }
198 
199  // Transform all the local points to world space.
200  positions[BoundsExtensions.LBF] = transform.TransformPoint(leftEdge, bottomEdge, frontEdge);
201  positions[BoundsExtensions.LBB] = transform.TransformPoint(leftEdge, bottomEdge, backEdge);
202  positions[BoundsExtensions.LTF] = transform.TransformPoint(leftEdge, topEdge, frontEdge);
203  positions[BoundsExtensions.LTB] = transform.TransformPoint(leftEdge, topEdge, backEdge);
204  positions[BoundsExtensions.RBF] = transform.TransformPoint(rightEdge, bottomEdge, frontEdge);
205  positions[BoundsExtensions.RBB] = transform.TransformPoint(rightEdge, bottomEdge, backEdge);
206  positions[BoundsExtensions.RTF] = transform.TransformPoint(rightEdge, topEdge, frontEdge);
207  positions[BoundsExtensions.RTB] = transform.TransformPoint(rightEdge, topEdge, backEdge);
208 
209  positions[BoundsExtensions.LTF_RTF] = Vector3.Lerp(positions[BoundsExtensions.LTF], positions[BoundsExtensions.RTF], 0.5f);
210  positions[BoundsExtensions.LBF_RBF] = Vector3.Lerp(positions[BoundsExtensions.LBF], positions[BoundsExtensions.RBF], 0.5f);
211  positions[BoundsExtensions.RTB_LTB] = Vector3.Lerp(positions[BoundsExtensions.RTB], positions[BoundsExtensions.LTB], 0.5f);
212  positions[BoundsExtensions.RBB_LBB] = Vector3.Lerp(positions[BoundsExtensions.RBB], positions[BoundsExtensions.LBB], 0.5f);
213 
214  positions[BoundsExtensions.LTF_LBF] = Vector3.Lerp(positions[BoundsExtensions.LTF], positions[BoundsExtensions.LBF], 0.5f);
215  positions[BoundsExtensions.RTB_RBB] = Vector3.Lerp(positions[BoundsExtensions.RTB], positions[BoundsExtensions.RBB], 0.5f);
216  positions[BoundsExtensions.LTB_LBB] = Vector3.Lerp(positions[BoundsExtensions.LTB], positions[BoundsExtensions.LBB], 0.5f);
217  positions[BoundsExtensions.RTF_RBF] = Vector3.Lerp(positions[BoundsExtensions.RTF], positions[BoundsExtensions.RBF], 0.5f);
218 
219  positions[BoundsExtensions.RBF_RBB] = Vector3.Lerp(positions[BoundsExtensions.RBF], positions[BoundsExtensions.RBB], 0.5f);
220  positions[BoundsExtensions.RTF_RTB] = Vector3.Lerp(positions[BoundsExtensions.RTF], positions[BoundsExtensions.RTB], 0.5f);
221  positions[BoundsExtensions.LBF_LBB] = Vector3.Lerp(positions[BoundsExtensions.LBF], positions[BoundsExtensions.LBB], 0.5f);
222  positions[BoundsExtensions.LTF_LTB] = Vector3.Lerp(positions[BoundsExtensions.LTF], positions[BoundsExtensions.LTB], 0.5f);
223  }
224 
230  public static void GetCornerAndMidPointPositions2D(this Bounds bounds, Transform transform, ref Vector3[] positions, Axis flattenAxis)
231  {
232  // Calculate the local points to transform.
233  Vector3 center = bounds.center;
234  Vector3 extents = bounds.extents;
235 
236  float leftEdge = 0;
237  float rightEdge = 0;
238  float bottomEdge = 0;
239  float topEdge = 0;
240 
241  // Allocate the array if needed.
242  const int numPoints = BoundsExtensions.LB_LT + 1;
243  if (positions == null || positions.Length != numPoints)
244  {
245  positions = new Vector3[numPoints];
246  }
247 
248  switch (flattenAxis)
249  {
250  case Axis.X:
251  default:
252  leftEdge = center.z - extents.z;
253  rightEdge = center.z + extents.z;
254  bottomEdge = center.y - extents.y;
255  topEdge = center.y + extents.y;
256  // Transform all the local points to world space.
257  positions[BoundsExtensions.LT] = transform.TransformPoint(0, topEdge, leftEdge);
258  positions[BoundsExtensions.LB] = transform.TransformPoint(0, bottomEdge, leftEdge);
259  positions[BoundsExtensions.RT] = transform.TransformPoint(0, topEdge, rightEdge);
260  positions[BoundsExtensions.RB] = transform.TransformPoint(0, bottomEdge, rightEdge);
261  break;
262 
263  case Axis.Y:
264  leftEdge = center.z - extents.z;
265  rightEdge = center.z + extents.z;
266  bottomEdge = center.x - extents.x;
267  topEdge = center.x + extents.x;
268  // Transform all the local points to world space.
269  positions[BoundsExtensions.LT] = transform.TransformPoint(topEdge, 0, leftEdge);
270  positions[BoundsExtensions.LB] = transform.TransformPoint(bottomEdge, 0, leftEdge);
271  positions[BoundsExtensions.RT] = transform.TransformPoint(topEdge, 0, rightEdge);
272  positions[BoundsExtensions.RB] = transform.TransformPoint(bottomEdge, 0, rightEdge);
273  break;
274 
275  case Axis.Z:
276  leftEdge = center.x - extents.x;
277  rightEdge = center.x + extents.x;
278  bottomEdge = center.y - extents.y;
279  topEdge = center.y + extents.y;
280  // Transform all the local points to world space.
281  positions[BoundsExtensions.LT] = transform.TransformPoint(leftEdge, topEdge, 0);
282  positions[BoundsExtensions.LB] = transform.TransformPoint(leftEdge, bottomEdge, 0);
283  positions[BoundsExtensions.RT] = transform.TransformPoint(rightEdge, topEdge, 0);
284  positions[BoundsExtensions.RB] = transform.TransformPoint(rightEdge, bottomEdge, 0);
285  break;
286  }
287 
288  positions[BoundsExtensions.LT_RT] = Vector3.Lerp(positions[BoundsExtensions.LT], positions[BoundsExtensions.RT], 0.5f);
289  positions[BoundsExtensions.RT_RB] = Vector3.Lerp(positions[BoundsExtensions.RT], positions[BoundsExtensions.RB], 0.5f);
290  positions[BoundsExtensions.RB_LB] = Vector3.Lerp(positions[BoundsExtensions.RB], positions[BoundsExtensions.LB], 0.5f);
291  positions[BoundsExtensions.LB_LT] = Vector3.Lerp(positions[BoundsExtensions.LB], positions[BoundsExtensions.LT], 0.5f);
292  }
293 
314  public static Bounds Transform(this Bounds bounds, Matrix4x4 transformMatrix)
315  {
316  // We will need access to the right, up and look vector which are encoded inside the transform matrix
317  Vector3 rightAxis = transformMatrix.GetColumn(0);
318  Vector3 upAxis = transformMatrix.GetColumn(1);
319  Vector3 lookAxis = transformMatrix.GetColumn(2);
320 
321  // We will 'imagine' that we want to rotate the bounds' extents vector using the rotation information
322  // stored inside the specified transform matrix. We will need these when calculating the new size if
323  // the transformed bounds.
324  Vector3 rotatedExtentsRight = rightAxis * bounds.extents.x;
325  Vector3 rotatedExtentsUp = upAxis * bounds.extents.y;
326  Vector3 rotatedExtentsLook = lookAxis * bounds.extents.z;
327 
328  // Calculate the new bounds size along each axis. The size on each axis is calculated by summing up the
329  // corresponding vector component values of the rotated extents vectors. We multiply by 2 because we want
330  // to get a size and curently we are working with extents which represent half the size.
331  float newSizeX = (Mathf.Abs(rotatedExtentsRight.x) + Mathf.Abs(rotatedExtentsUp.x) + Mathf.Abs(rotatedExtentsLook.x)) * 2.0f;
332  float newSizeY = (Mathf.Abs(rotatedExtentsRight.y) + Mathf.Abs(rotatedExtentsUp.y) + Mathf.Abs(rotatedExtentsLook.y)) * 2.0f;
333  float newSizeZ = (Mathf.Abs(rotatedExtentsRight.z) + Mathf.Abs(rotatedExtentsUp.z) + Mathf.Abs(rotatedExtentsLook.z)) * 2.0f;
334 
335  // Construct the transformed 'Bounds' instance
336  var transformedBounds = new Bounds();
337  transformedBounds.center = transformMatrix.MultiplyPoint(bounds.center);
338  transformedBounds.size = new Vector3(newSizeX, newSizeY, newSizeZ);
339 
340  // Return the instance to the caller
341  return transformedBounds;
342  }
343 
351  public static Vector2[] GetScreenSpaceCornerPoints(this Bounds bounds, Camera camera)
352  {
353  Vector3 aabbCenter = bounds.center;
354  Vector3 aabbExtents = bounds.extents;
355 
356  // Return the screen space point array
357  return new Vector2[]
358  {
359  camera.WorldToScreenPoint(new Vector3(aabbCenter.x - aabbExtents.x, aabbCenter.y - aabbExtents.y, aabbCenter.z - aabbExtents.z)),
360  camera.WorldToScreenPoint(new Vector3(aabbCenter.x + aabbExtents.x, aabbCenter.y - aabbExtents.y, aabbCenter.z - aabbExtents.z)),
361  camera.WorldToScreenPoint(new Vector3(aabbCenter.x + aabbExtents.x, aabbCenter.y + aabbExtents.y, aabbCenter.z - aabbExtents.z)),
362  camera.WorldToScreenPoint(new Vector3(aabbCenter.x - aabbExtents.x, aabbCenter.y + aabbExtents.y, aabbCenter.z - aabbExtents.z)),
363 
364  camera.WorldToScreenPoint(new Vector3(aabbCenter.x - aabbExtents.x, aabbCenter.y - aabbExtents.y, aabbCenter.z + aabbExtents.z)),
365  camera.WorldToScreenPoint(new Vector3(aabbCenter.x + aabbExtents.x, aabbCenter.y - aabbExtents.y, aabbCenter.z + aabbExtents.z)),
366  camera.WorldToScreenPoint(new Vector3(aabbCenter.x + aabbExtents.x, aabbCenter.y + aabbExtents.y, aabbCenter.z + aabbExtents.z)),
367  camera.WorldToScreenPoint(new Vector3(aabbCenter.x - aabbExtents.x, aabbCenter.y + aabbExtents.y, aabbCenter.z + aabbExtents.z))
368  };
369  }
370 
374  public static Rect GetScreenRectangle(this Bounds bounds, Camera camera)
375  {
376  // Retrieve the bounds' corner points in screen space
377  Vector2[] screenSpaceCornerPoints = bounds.GetScreenSpaceCornerPoints(camera);
378 
379  // Identify the minimum and maximum points in the array
380  Vector3 minScreenPoint = screenSpaceCornerPoints[0], maxScreenPoint = screenSpaceCornerPoints[0];
381  for (int screenPointIndex = 1; screenPointIndex < screenSpaceCornerPoints.Length; ++screenPointIndex)
382  {
383  minScreenPoint = Vector3.Min(minScreenPoint, screenSpaceCornerPoints[screenPointIndex]);
384  maxScreenPoint = Vector3.Max(maxScreenPoint, screenSpaceCornerPoints[screenPointIndex]);
385  }
386 
387  // Return the screen space rectangle
388  return new Rect(minScreenPoint.x, minScreenPoint.y, maxScreenPoint.x - minScreenPoint.x, maxScreenPoint.y - minScreenPoint.y);
389  }
390 
396  public static float Volume(this Bounds bounds)
397  {
398  return bounds.size.x * bounds.size.y * bounds.size.z;
399  }
400 
407  public static Bounds ExpandToContain(this Bounds originalBounds, Bounds otherBounds)
408  {
409  Bounds tmpBounds = originalBounds;
410 
411  tmpBounds.Encapsulate(otherBounds);
412 
413  return tmpBounds;
414  }
415 
422  public static bool ContainsBounds(this Bounds bounds, Bounds otherBounds)
423  {
424  return bounds.Contains(otherBounds.min) && bounds.Contains(otherBounds.max);
425  }
426 
434  public static bool CloserToPoint(this Bounds bounds, Vector3 point, Bounds otherBounds)
435  {
436  Vector3 distToClosestPoint1 = bounds.ClosestPoint(point) - point;
437  Vector3 distToClosestPoint2 = otherBounds.ClosestPoint(point) - point;
438 
439  if (distToClosestPoint1.magnitude == distToClosestPoint2.magnitude)
440  {
441  Vector3 toCenter1 = point - bounds.center;
442  Vector3 toCenter2 = point - otherBounds.center;
443  return (toCenter1.magnitude <= toCenter2.magnitude);
444 
445  }
446  else
447  {
448  return (distToClosestPoint1.magnitude <= distToClosestPoint2.magnitude);
449  }
450  }
451 
452  #endregion
453 
454  #region Private Static Functions
455  private static Vector3 GetInvalidBoundsSize()
459  {
460  return new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
461  }
462  #endregion
463 }
static void GetCornerAndMidPointPositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
Gets all the corner points and mid points from Renderer&#39;s Bounds
static Bounds ExpandToContain(this Bounds originalBounds, Bounds otherBounds)
Returns bounds that contain both this bounds and the bounds passed in.
static void GetCornerAndMidPointPositions2D(this Bounds bounds, Transform transform, ref Vector3[] positions, Axis flattenAxis)
Gets all the corner points and mid points from Renderer&#39;s Bounds, ignoring the z axis ...
static float Volume(this Bounds bounds)
Returns the volume of the bounds.
static Bounds Transform(this Bounds bounds, Matrix4x4 transformMatrix)
Transforms &#39;bounds&#39; using the specified transform matrix.
static Bounds GetInvalidBoundsInstance()
Returns an instance of the &#39;Bounds&#39; class which is invalid. An invalid &#39;Bounds&#39; instance is one which...
static bool IsValid(this Bounds bounds)
Checks if the specified bounds instance is valid. A valid &#39;Bounds&#39; instance is one whose size vector ...
static bool ContainsBounds(this Bounds bounds, Bounds otherBounds)
Checks to see if bounds contains the other bounds completely.
static void GetFacePositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
static Rect GetScreenRectangle(this Bounds bounds, Camera camera)
Returns the rectangle which encloses the specifies &#39;Bounds&#39; instance in screen space.
static bool CloserToPoint(this Bounds bounds, Vector3 point, Bounds otherBounds)
Checks to see whether point is closer to bounds or otherBounds
static Vector2 [] GetScreenSpaceCornerPoints(this Bounds bounds, Camera camera)
Returns the screen space corner points of the specified &#39;Bounds&#39; instance.
static void GetCornerPositions(this Bounds bounds, Transform transform, ref Vector3[] positions)
Gets all the corner points of the bounds in world space
static void GetCornerPositionsFromRendererBounds(this Bounds bounds, ref Vector3[] positions)
Gets all the corner points from Renderer&#39;s Bounds