AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
AppBar.cs
Go to the documentation of this file.
1 // Copyright (c) Microsoft Corporation. All rights reserved.
2 // Licensed under the MIT License. See LICENSE in the project root for license information.
3 
7 using System;
8 using System.Collections.Generic;
9 using UnityEngine;
10 using UnityEngine.Events;
11 
12 namespace HoloToolkit.Unity.UX
13 {
17  public class AppBar : InteractionReceiver
18  {
19  private float buttonWidth = 1.50f;
20 
24  public const int MaxCustomButtons = 5;
25 
31  public float HoverOffsetYScale = 0.25f;
32 
36  public float HoverOffsetZ = 0f;
37 
38  [SerializeField]
39  [Tooltip("Uses an alternate follow style that works better for very oblong objects.")]
40  private bool useTightFollow = false;
41 
45  public bool UseTightFollow
46  {
47  get { return useTightFollow; }
48  set { useTightFollow = value; }
49  }
50 
55  [Serializable]
56  public struct ButtonTemplate
57  {
58  public ButtonTemplate(ButtonTypeEnum type, string name, string icon, string text, int defaultPosition, int manipulationPosition)
59  {
60  Type = type;
61  Name = name;
62  Icon = icon;
63  Text = text;
64  DefaultPosition = defaultPosition;
65  ManipulationPosition = manipulationPosition;
66  EventTarget = null;
67  OnTappedEvent = null;
68  }
69 
70  public bool IsEmpty
71  {
72  get { return string.IsNullOrEmpty(Name); }
73  }
74 
75  public int DefaultPosition;
78  public string Name;
79  public string Icon;
80  public string Text;
82  public UnityEvent OnTappedEvent;
83  }
84 
85  [Flags]
86  public enum ButtonTypeEnum
87  {
88  Custom = 0,
89  Remove = 1,
90  Adjust = 2,
91  Hide = 4,
92  Show = 8,
93  Done = 16
94  }
95 
97  {
98  Manipulation,
99  Standalone
100  }
101 
102  public enum AppBarStateEnum
103  {
104  Default,
105  Manipulation,
106  Hidden
107  }
108 
109  public BoundingBox BoundingBox
110  {
111  get { return boundingBox; }
112  set { boundingBox = value; }
113  }
114 
115  private BoundingBoxRig boundingRig;
116 
120  public BoundingBoxRig BoundingRig
121  {
122  get { return boundingRig; }
123  set { boundingRig = value; }
124  }
125 
126  public GameObject SquareButtonPrefab;
127 
128  public int NumDefaultButtons { get; private set; }
129 
130  public int NumManipulationButtons { get; private set; }
131 
132  public bool UseRemove = true;
133  public bool UseAdjust = true;
134  public bool UseHide = true;
135 
136  public ButtonTemplate[] Buttons
137  {
138  get { return buttons; }
139  set { buttons = value; }
140  }
141 
142  public ButtonTemplate[] DefaultButtons { get; private set; }
143 
144  public AppBarDisplayTypeEnum DisplayType = AppBarDisplayTypeEnum.Manipulation;
145 
146  public AppBarStateEnum State = AppBarStateEnum.Default;
147 
153 
154  [SerializeField]
155  private ButtonTemplate[] buttons = new ButtonTemplate[MaxCustomButtons];
156 
157  [SerializeField]
158  private Transform buttonParent = null;
159 
160  [SerializeField]
161  private GameObject baseRenderer = null;
162 
163  [SerializeField]
164  private GameObject backgroundBar = null;
165 
166  [SerializeField]
167  private BoundingBox boundingBox;
168 
169  private Vector3 targetBarSize = Vector3.one;
170  private float lastTimeTapped = 0f;
171  private float coolDownTime = 0.5f;
172  private int numHiddenButtons;
173  private BoundingBoxHelper helper;
174 
175  public void Reset()
176  {
177  State = AppBarStateEnum.Default;
178  FollowBoundingBox(false);
179  lastTimeTapped = Time.time + coolDownTime;
180  }
181 
182  public void Start()
183  {
184  State = AppBarStateEnum.Default;
185 
186  if (interactables.Count == 0)
187  {
188  RefreshTemplates();
189  for (int i = 0; i < DefaultButtons.Length; i++)
190  {
191  CreateButton(DefaultButtons[i], null);
192  }
193 
194  for (int i = 0; i < buttons.Length; i++)
195  {
196  CreateButton(buttons[i], CustomButtonIconProfile);
197  }
198  }
199 
200  helper = new BoundingBoxHelper();
201  }
202 
203  protected override void InputClicked(GameObject obj, InputClickedEventData eventData)
204  {
205  if (Time.time < lastTimeTapped + coolDownTime)
206  {
207  return;
208  }
209 
210  lastTimeTapped = Time.time;
211 
212  base.InputClicked(obj, eventData);
213 
214  switch (obj.name)
215  {
216  case "Remove":
217  // Destroy the target object, Bounding Box, Bounding Box Rig and App Bar
218  boundingBox.Target.GetComponent<BoundingBoxRig>().Deactivate();
219  Destroy(boundingBox.Target.GetComponent<BoundingBoxRig>());
220  Destroy(boundingBox.Target);
221  Destroy(gameObject);
222  break;
223 
224  case "Adjust":
225  // Make the bounding box active so users can manipulate it
226  State = AppBarStateEnum.Manipulation;
227  // Activate BoundingBoxRig
228  boundingBox.Target.GetComponent<BoundingBoxRig>().Activate();
229  break;
230 
231  case "Hide":
232  // Make the bounding box inactive and invisible
233  State = AppBarStateEnum.Hidden;
234  break;
235 
236  case "Show":
237  State = AppBarStateEnum.Default;
238  // Deactivate BoundingBoxRig
239  boundingBox.Target.GetComponent<BoundingBoxRig>().Deactivate();
240  break;
241 
242  case "Done":
243  State = AppBarStateEnum.Default;
244  // Deactivate BoundingBoxRig
245  boundingBox.Target.GetComponent<BoundingBoxRig>().Deactivate();
246  break;
247 
248  default:
249  break;
250  }
251  }
252 
253  private void CreateButton(ButtonTemplate template, ButtonIconProfile customIconProfile)
254  {
255  if (template.IsEmpty)
256  {
257  return;
258  }
259 
260  switch (template.Type)
261  {
262  case ButtonTypeEnum.Custom:
263  NumDefaultButtons++;
264  break;
265 
266  case ButtonTypeEnum.Adjust:
267  NumDefaultButtons++;
268  break;
269 
270  case ButtonTypeEnum.Done:
271  NumManipulationButtons++;
272  break;
273 
274  case ButtonTypeEnum.Remove:
275  NumManipulationButtons++;
276  NumDefaultButtons++;
277  break;
278 
279  case ButtonTypeEnum.Hide:
280  NumDefaultButtons++;
281  break;
282 
283  case ButtonTypeEnum.Show:
284  numHiddenButtons++;
285  break;
286 
287  default:
288  throw new ArgumentOutOfRangeException();
289  }
290 
291  GameObject newButton = Instantiate(SquareButtonPrefab, buttonParent);
292  newButton.name = template.Name;
293  newButton.transform.localPosition = Vector3.zero;
294  newButton.transform.localRotation = Quaternion.identity;
295  AppBarButton mtb = newButton.AddComponent<AppBarButton>();
296  mtb.Initialize(this, template, customIconProfile);
297  }
298 
299  private void FollowBoundingBox(bool smooth)
300  {
301  if (boundingBox == null)
302  {
303  if (DisplayType == AppBarDisplayTypeEnum.Manipulation)
304  {
305  // Hide our buttons
306  baseRenderer.SetActive(false);
307  }
308  else
309  {
310  baseRenderer.SetActive(true);
311  }
312  return;
313  }
314 
315  // Show our buttons
316  baseRenderer.SetActive(true);
317 
318  //calculate best follow position for AppBar
319  Vector3 finalPosition = Vector3.zero;
320  Vector3 headPosition = Camera.main.transform.position;
321  LayerMask ignoreLayers = new LayerMask();
322  List<Vector3> boundsPoints = new List<Vector3>();
323  if (boundingBox != null)
324  {
325  helper.UpdateNonAABoundingBoxCornerPositions(boundingBox.Target, boundsPoints, ignoreLayers);
326  int followingFaceIndex = helper.GetIndexOfForwardFace(headPosition);
327  Vector3 faceNormal = helper.GetFaceNormal(followingFaceIndex);
328 
329  //finally we have new position
330  finalPosition = helper.GetFaceBottomCentroid(followingFaceIndex) + (faceNormal * HoverOffsetZ);
331  }
332 
333  // Follow our bounding box
334  transform.position = smooth ? Vector3.Lerp(transform.position, finalPosition, 0.5f) : finalPosition;
335 
336  // Rotate on the y axis
337  Vector3 eulerAngles = Quaternion.LookRotation((boundingBox.transform.position - finalPosition).normalized, Vector3.up).eulerAngles;
338  eulerAngles.x = 0f;
339  eulerAngles.z = 0f;
340  transform.eulerAngles = eulerAngles;
341  }
342 
343  private void Update()
344  {
345  FollowBoundingBox(true);
346 
347  switch (State)
348  {
349  case AppBarStateEnum.Default:
350  targetBarSize = new Vector3(NumDefaultButtons * buttonWidth, buttonWidth, 1f);
351  break;
352 
353  case AppBarStateEnum.Hidden:
354  targetBarSize = new Vector3(numHiddenButtons * buttonWidth, buttonWidth, 1f);
355  break;
356 
357  case AppBarStateEnum.Manipulation:
358  targetBarSize = new Vector3(NumManipulationButtons * buttonWidth, buttonWidth, 1f);
359  break;
360 
361  default:
362  throw new ArgumentOutOfRangeException();
363  }
364 
365  backgroundBar.transform.localScale = Vector3.Lerp(backgroundBar.transform.localScale, targetBarSize, 0.5f);
366  }
367 
368  private void RefreshTemplates()
369  {
370  int numCustomButtons = 0;
371  for (int i = 0; i < buttons.Length; i++)
372  {
373  if (!buttons[i].IsEmpty)
374  {
375  numCustomButtons++;
376  }
377  }
378 
379  var defaultButtonsList = new List<ButtonTemplate>();
380 
381  // Create our default button templates based on user preferences
382  if (UseRemove)
383  {
384  defaultButtonsList.Add(GetDefaultButtonTemplateFromType(ButtonTypeEnum.Remove, numCustomButtons, UseHide, UseAdjust));
385  }
386 
387  if (UseAdjust)
388  {
389  defaultButtonsList.Add(GetDefaultButtonTemplateFromType(ButtonTypeEnum.Adjust, numCustomButtons, UseHide, UseAdjust));
390  defaultButtonsList.Add(GetDefaultButtonTemplateFromType(ButtonTypeEnum.Done, numCustomButtons, UseHide, UseAdjust));
391  }
392 
393  if (UseHide)
394  {
395  defaultButtonsList.Add(GetDefaultButtonTemplateFromType(ButtonTypeEnum.Hide, numCustomButtons, UseHide, UseAdjust));
396  defaultButtonsList.Add(GetDefaultButtonTemplateFromType(ButtonTypeEnum.Show, numCustomButtons, UseHide, UseAdjust));
397  }
398  DefaultButtons = defaultButtonsList.ToArray();
399  }
400 
401 #if UNITY_EDITOR
402  public void EditorRefreshTemplates()
403  {
404  RefreshTemplates();
405  }
406 #endif
407 
416  private static ButtonTemplate GetDefaultButtonTemplateFromType(ButtonTypeEnum type, int numCustomButtons, bool useHide, bool useAdjust)
417  {
418  // Button position is based on custom buttons
419  // In the app bar, Hide/Show
420  switch (type)
421  {
422  case ButtonTypeEnum.Custom:
423  return new ButtonTemplate(
424  ButtonTypeEnum.Custom,
425  "Custom",
426  "",
427  "Custom",
428  0,
429  0);
430 
431  case ButtonTypeEnum.Adjust:
432  int adjustPosition = numCustomButtons + 1;
433 
434  if (!useHide)
435  {
436  adjustPosition--;
437  }
438 
439  return new ButtonTemplate(
440  ButtonTypeEnum.Adjust,
441  "Adjust",
442  "AppBarAdjust",
443  "Adjust",
444  adjustPosition, // Always next-to-last to appear
445  0);
446 
447  case ButtonTypeEnum.Done:
448  return new ButtonTemplate(
449  ButtonTypeEnum.Done,
450  "Done",
451  "AppBarDone",
452  "Done",
453  0,
454  0);
455 
456  case ButtonTypeEnum.Hide:
457  return new ButtonTemplate(
458  ButtonTypeEnum.Hide,
459  "Hide",
460  "AppBarHide",
461  "Hide Menu",
462  0, // Always the first to appear
463  0);
464 
465  case ButtonTypeEnum.Remove:
466  int removePosition = numCustomButtons + 1;
467  if (useAdjust)
468  {
469  removePosition++;
470  }
471 
472  if (!useHide)
473  {
474  removePosition--;
475  }
476 
477  return new ButtonTemplate(
478  ButtonTypeEnum.Remove,
479  "Remove",
480  "KeyboardKeyGlyphs_Close",
481  "Remove",
482  removePosition, // Always the last to appear
483  1);
484 
485  case ButtonTypeEnum.Show:
486  return new ButtonTemplate(
487  ButtonTypeEnum.Show,
488  "Show",
489  "AppBarShow",
490  "Show Menu",
491  0,
492  0);
493 
494  default:
495  throw new ArgumentOutOfRangeException("type", type, null);
496  }
497  }
498  }
499 }
Constructs the scale and rotate gizmo handles for the Bounding Box
Button logic for the App Bar. Determines position of the button in the App Bar, visibility based on t...
Definition: AppBarButton.cs:13
virtual GameObject Target
The target object being manipulated
Definition: BoundingBox.cs:147
Vector3 GetFaceBottomCentroid(int index)
Get the center of the bottom edge of a face of the bounding box determined by index ...
GameObject SquareButtonPrefab
Definition: AppBar.cs:126
int GetIndexOfForwardFace(Vector3 lookAtPoint)
This function gets the index of the face of the bounding cube that is most facing the lookAtPoint...
The base class for button icon profiles
Class used for building toolbar buttons (not yet in use)
Definition: AppBar.cs:56
void UpdateNonAABoundingBoxCornerPositions(GameObject target, List< Vector3 > boundsPoints, LayerMask ignoreLayers)
Objects that align to an target&#39;s bounding box can call this function in the object&#39;s UpdateLoop to g...
Vector3 GetFaceNormal(int index)
Get the normal of the face of the bounding cube specified by index
Base class for bounding box objects
Definition: BoundingBox.cs:14
override void InputClicked(GameObject obj, InputClickedEventData eventData)
Definition: AppBar.cs:203
An interaction receiver is simply a component that attached to a list of interactable objects and doe...
ButtonIconProfile CustomButtonIconProfile
Custom icon profile If null, the profile in the SquareButtonPrefab object will be used ...
Definition: AppBar.cs:152
Describes an input event that involves a tap.
Logic for the App Bar. Generates buttons, manages states.
Definition: AppBar.cs:17
The BoundingBoxHelper class contains functions for getting geometric info from the non-axis-aligned b...
void Initialize(AppBar newParentToolBar, AppBar.ButtonTemplate newTemplate, ButtonIconProfile newCustomProfile)
Definition: AppBarButton.cs:32
ButtonTemplate(ButtonTypeEnum type, string name, string icon, string text, int defaultPosition, int manipulationPosition)
Definition: AppBar.cs:58