Skip to content

Commit bb351d7

Browse files
committed
Moved some extensions from Longbow into MRTK. Merged those that already exist in MRTK.
1 parent 7156ecf commit bb351d7

File tree

8 files changed

+484
-0
lines changed

8 files changed

+484
-0
lines changed

Assets/MixedRealityToolkit/Extensions/ArrayExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,15 @@ public static int WrapIndex(this Array array, int index)
2121
int length = array.Length;
2222
return ((index % length) + length) % length;
2323
}
24+
25+
/// <summary>
26+
/// Checks whether the given array is not null and has at least one entry
27+
/// </summary>
28+
/// <param name="array"></param>
29+
/// <returns></returns>
30+
public static bool IsValidArray(this Array array)
31+
{
32+
return array != null && array.Length > 0;
33+
}
2434
}
2535
}

Assets/MixedRealityToolkit/Extensions/CameraExtensions.cs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,33 @@ public static bool IsInFOV(this Camera camera, Vector3 position)
3939

4040
return (Mathf.Abs(yaw) < horizontalFovHalf && Mathf.Abs(pitch) < verticalFovHalf);
4141
}
42+
43+
/// <summary>
44+
/// Gets the frustrum size at a given distance from the camera.
45+
/// </summary>
46+
/// <param name="camera">The camera to get the frustrum size for</param>
47+
/// <param name="distanceFromCamera">The distance from the camera to get the frustrum size at</param>
48+
/// <returns></returns>
49+
public static Vector2 GetFrustrumSizeForDistance(this Camera camera, float distanceFromCamera)
50+
{
51+
Vector2 frustrumSize = new Vector2
52+
{
53+
y = 2.0f * distanceFromCamera * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad)
54+
};
55+
frustrumSize.x = frustrumSize.y * camera.aspect;
56+
57+
return frustrumSize;
58+
}
59+
60+
/// <summary>
61+
/// Gets the distance to the camera that a specific frustrum height would be at.
62+
/// </summary>
63+
/// <param name="camera">The camera to get the distance from</param>
64+
/// <param name="frustrumHeight">The frustrum height</param>
65+
/// <returns></returns>
66+
public static float GetDistanceForFrustrumHeight(this Camera camera, float frustrumHeight)
67+
{
68+
return frustrumHeight * 0.5f / Mathf.Max(0.00001f, Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad));
69+
}
4270
}
4371
}
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using UnityEngine;
5+
using UnityEngine.UI;
6+
7+
namespace Microsoft.MixedReality.Toolkit
8+
{
9+
/// <summary>
10+
/// Extensions for the Canvas class.
11+
/// </summary>
12+
public static class CanvasExtensions
13+
{
14+
/// <summary>
15+
/// Convenience method for getting a plane for this canvas in world coordinates.
16+
/// </summary>
17+
/// <param name="canvas">The canvas to get the plane from.</param>
18+
/// <returns>A Plane for this canvas.</returns>
19+
public static Plane GetPlane(this Canvas canvas)
20+
{
21+
Vector3[] corners = canvas.GetWorldCorners();
22+
23+
// Now set a plane from any of the 3 corners (clockwise) so that we can compute our gaze intersection
24+
Plane plane = new Plane(corners[0], corners[1], corners[2]);
25+
26+
return plane;
27+
}
28+
29+
/// <summary>
30+
/// Convenience method for getting the corners of the canvas in world coordinates. Ordered clockwise from bottom-left.
31+
/// </summary>
32+
/// <param name="canvas">The canvas to get the world corners from.</param>
33+
/// <returns>An array of Vector3s that represent the corners of the canvas in world coordinates.</returns>
34+
public static Vector3[] GetWorldCorners(this Canvas canvas)
35+
{
36+
Vector3[] worldCorners = new Vector3[4];
37+
RectTransform rect = canvas.GetComponent<RectTransform>();
38+
rect.GetWorldCorners(worldCorners);
39+
return worldCorners;
40+
}
41+
42+
/// <summary>
43+
/// Convenience method for getting the corners of the canvas in local coordinates. Ordered clockwise from bottom-left.
44+
/// </summary>
45+
/// <param name="canvas">The canvas to get the local corners from.</param>
46+
/// <returns>An array of Vector3s that represent the corners of the canvas in local coordinates.</returns>
47+
public static Vector3[] GetLocalCorners(this Canvas canvas)
48+
{
49+
Vector3[] localCorners = new Vector3[4];
50+
RectTransform rect = canvas.GetComponent<RectTransform>();
51+
rect.GetLocalCorners(localCorners);
52+
return localCorners;
53+
}
54+
55+
/// <summary>
56+
/// Convenience method for getting the corners of the canvas in viewport coordinates. Note
57+
/// that the points have the same ordering as the array returned in GetWorldCorners()
58+
/// </summary>
59+
/// <param name="canvas">The canvas to get the viewport corners from</param>
60+
/// <returns>An array of Vector3s that represent the corners of the canvas in viewport coordinates</returns>
61+
public static Vector3[] GetViewportCorners(this Canvas canvas)
62+
{
63+
Vector3[] viewportCorners = new Vector3[4];
64+
65+
Vector3[] worldCorners = canvas.GetWorldCorners();
66+
67+
for (int i = 0; i < 4; i++)
68+
{
69+
viewportCorners[i] = Camera.main.WorldToViewportPoint(worldCorners[i]);
70+
}
71+
72+
return viewportCorners;
73+
}
74+
75+
/// <summary>
76+
/// Gets the position of the corners for a canvas in screen space.
77+
/// 1 -- 2
78+
/// | |
79+
/// 0 -- 3
80+
/// </summary>
81+
/// <param name="canvas">The canvas to get the screen corners for.</param>
82+
/// <returns></returns>
83+
public static Vector3[] GetScreenCorners(this Canvas canvas)
84+
{
85+
Vector3[] screenCorners = new Vector3[4];
86+
Vector3[] worldCorners = canvas.GetWorldCorners();
87+
88+
for (int i = 0; i < 4; i++)
89+
{
90+
screenCorners[i] = Camera.main.WorldToScreenPoint(worldCorners[i]);
91+
}
92+
93+
return screenCorners;
94+
}
95+
96+
/// <summary>
97+
/// Returns a rectangle in screen coordinates that encompasses the bounds of the target canvas.
98+
/// </summary>
99+
/// <param name="canvas">The canvas the get the screen rect for</param>
100+
/// <returns></returns>
101+
public static Rect GetScreenRect(this Canvas canvas)
102+
{
103+
Vector3[] screenCorners = canvas.GetScreenCorners();
104+
float x = Mathf.Min(screenCorners[0].x, screenCorners[1].x);
105+
float y = Mathf.Min(screenCorners[0].y, screenCorners[3].y);
106+
float xMax = Mathf.Max(screenCorners[2].x, screenCorners[3].x);
107+
float yMax = Mathf.Max(screenCorners[1].y, screenCorners[2].y);
108+
return new Rect(x, y, xMax - x, yMax - y);
109+
}
110+
111+
/// <summary>
112+
/// Raycast against a canvas using a ray.
113+
/// </summary>
114+
/// <param name="canvas">The canvas to raycast against</param>
115+
/// <param name="rayOrigin">The origin of the ray</param>
116+
/// <param name="rayDirection">The direction of the ray</param>
117+
/// <param name="distance">The distance of the ray</param>
118+
/// <param name="hitPoint">The hitpoint of the ray</param>
119+
/// <param name="hitChildObject">The child object that was hit or the canvas itself if it has no active children that were within the hit range.</param>
120+
/// <returns></returns>
121+
public static bool Raycast(this Canvas canvas, Vector3 rayOrigin, Vector3 rayDirection, out float distance, out Vector3 hitPoint, out GameObject hitChildObject)
122+
{
123+
hitChildObject =null;
124+
Plane plane = canvas.GetPlane();
125+
Ray ray = new Ray(rayOrigin, rayDirection);
126+
127+
if (plane.Raycast(ray, out distance))
128+
{
129+
// See if the point lies within the local canvas rect of the plane
130+
Vector3[] corners = canvas.GetLocalCorners();
131+
hitPoint = rayOrigin + (rayDirection.normalized * distance);
132+
Vector3 localHitPoint = canvas.transform.InverseTransformPoint(hitPoint);
133+
if (localHitPoint.x >= corners[0].x
134+
&& localHitPoint.x <= corners[3].x
135+
&& localHitPoint.y <= corners[2].y
136+
&& localHitPoint.y >= corners[3].y)
137+
{
138+
hitChildObject = canvas.gameObject;
139+
140+
// look for the child object that was hit
141+
RectTransform rectTransform = GetChildRectTransformAtPoint(canvas.GetComponent<RectTransform>(), hitPoint, true, true, true);
142+
if (rectTransform != null)
143+
{
144+
hitChildObject = rectTransform.gameObject;
145+
}
146+
else
147+
{
148+
hitChildObject = canvas.gameObject;
149+
}
150+
151+
return true;
152+
}
153+
}
154+
155+
hitPoint = Vector3.zero;
156+
157+
return false;
158+
}
159+
160+
/// <summary>
161+
/// Gets a child rect transform for the given point and parameters.
162+
/// </summary>
163+
/// <param name="rectTransformParent">The rect transform to look for children that may contain the projected (orthogonal to the child's normal) world point</param>
164+
/// <param name="worldPoint">The world point</param>
165+
/// <param name="recursive">Indicates if the check should be done recursively</param>
166+
/// <param name="shouldReturnActive">If true, will only check children that are active, otherwise it will check all children.</param>
167+
/// <param name="shouldReturnRaycastable">If true, will only check children that if they have a graphic and have it's member raycastTarget set to true, otherwise will ignore the raycastTarget value. Will still allow children to be checked that do not have a graphic component.</param>
168+
/// <returns></returns>
169+
public static RectTransform GetChildRectTransformAtPoint(this RectTransform rectTransformParent, Vector3 worldPoint, bool recursive, bool shouldReturnActive, bool shouldReturnRaycastable)
170+
{
171+
Vector3[] localCorners = new Vector3[4];
172+
Vector3 childLocalPoint;
173+
RectTransform rectTransform;
174+
bool shouldRaycast = false;
175+
176+
for (int i=rectTransformParent.childCount-1; i >= 0; i--)
177+
{
178+
rectTransform = rectTransformParent.GetChild(i).GetComponent<RectTransform>();
179+
Graphic graphic = rectTransform.GetComponent<Graphic>();
180+
shouldRaycast = ((shouldReturnRaycastable && graphic != null && graphic.raycastTarget) || graphic == null || !shouldReturnRaycastable);
181+
182+
if (((shouldReturnActive && rectTransform.gameObject.activeSelf) || !shouldReturnActive))
183+
{
184+
rectTransform.GetLocalCorners(localCorners);
185+
childLocalPoint = rectTransform.InverseTransformPoint(worldPoint);
186+
187+
if (recursive)
188+
{
189+
RectTransform childRect = GetChildRectTransformAtPoint(rectTransform, worldPoint, recursive, shouldReturnActive, shouldReturnRaycastable);
190+
191+
if (childRect != null)
192+
{
193+
return childRect;
194+
}
195+
}
196+
197+
if (shouldRaycast
198+
&& childLocalPoint.x >= localCorners[0].x
199+
&& childLocalPoint.x <= localCorners[3].x
200+
&& childLocalPoint.y <= localCorners[2].y
201+
&& childLocalPoint.y >= localCorners[3].y)
202+
{
203+
return rectTransform;
204+
}
205+
}
206+
}
207+
208+
return null;
209+
}
210+
}
211+
}

Assets/MixedRealityToolkit/Extensions/CanvasExtensions.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/MixedRealityToolkit/Extensions/Color32Extensions.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4+
using System;
5+
using System.Globalization;
46
using UnityEngine;
57

68
namespace Microsoft.MixedReality.Toolkit
@@ -24,5 +26,42 @@ public static Color32 PremultiplyAlpha(Color32 col)
2426
Color floatCol = col;
2527
return (Color32)PremultiplyAlpha(floatCol);
2628
}
29+
30+
/// <summary>
31+
/// Creates a Color from a hexcode string
32+
/// </summary>
33+
/// <param name="hexstring"></param>
34+
/// <returns></returns>
35+
public static Color ParseHexcode(string hexstring)
36+
{
37+
if (hexstring.StartsWith("#"))
38+
{
39+
hexstring = hexstring.Substring(1);
40+
}
41+
42+
if (hexstring.StartsWith("0x"))
43+
{
44+
hexstring = hexstring.Substring(2);
45+
}
46+
47+
if (hexstring.Length == 6)
48+
{
49+
hexstring += "FF";
50+
}
51+
52+
if (hexstring.Length != 8)
53+
{
54+
throw new ArgumentException(string.Format("{0} is not a valid color string.", hexstring));
55+
}
56+
57+
byte r = byte.Parse(hexstring.Substring(0, 2), NumberStyles.HexNumber);
58+
byte g = byte.Parse(hexstring.Substring(2, 2), NumberStyles.HexNumber);
59+
byte b = byte.Parse(hexstring.Substring(4, 2), NumberStyles.HexNumber);
60+
byte a = byte.Parse(hexstring.Substring(6, 2), NumberStyles.HexNumber);
61+
62+
const float maxRgbValue = 255;
63+
Color c = new Color(r / maxRgbValue, g / maxRgbValue, b / maxRgbValue, a / maxRgbValue);
64+
return c;
65+
}
2766
}
2867
}

0 commit comments

Comments
 (0)