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+ }
0 commit comments