// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;
using System.Collections.Generic;
using UnityEngine;
namespace Microsoft.MixedReality.Toolkit.UI
{
///
/// This class must be instantiated by a script that implements the ,
/// and .
///
/// ***It must receive EventData arguments from OnInputDown(), OnInputUp(), OnInputChanged() and OnSourceLost().***
///
/// This class manages the states of input necessary to calculate a proper grab position
/// The eventData received on inputdown has the point on the target that was hit by the gaze;
/// the mixedrealitypose - eventdata received on input changed contains the handposition in eventdata.inputdata.position
/// It also contains useful retrieval functions.
///
[System.Obsolete("This component is no longer supported", true)]
public class GazeHandHelper
{
#region Private Variables
private readonly Dictionary positionAvailableMap = new Dictionary();
private readonly Dictionary handSourceMap = new Dictionary();
private readonly Dictionary handStartPositionMap = new Dictionary();
private readonly Dictionary handPositionMap = new Dictionary();
private readonly Dictionary gazePointMap = new Dictionary();
#endregion Private Variables
#region Public Methods
///
/// This function must be called from the OnInputDown handler in a script implementing the .
///
/// The InputEventData argument 'eventData' is passed through to GazeHandHelper
public void AddSource(InputEventData eventData)
{
IMixedRealityInputSource source = eventData.InputSource;
if (source != null && IsInDictionary(source.SourceId) == false && source.Pointers != null && source.Pointers.Length > 0)
{
handSourceMap.Add(source.SourceId, source);
gazePointMap.Add(source.SourceId, source.Pointers[0].Position);
handStartPositionMap.Add(source.SourceId, Vector3.zero);
handPositionMap.Add(source.SourceId, Vector3.zero);
positionAvailableMap.Add(source.SourceId, false);
}
}
///
/// This function must be called from the OnInputUp handler in a script implementing the .
///
/// he InputEventData argument 'eventData' is passed through to GazeHandHelper
public void RemoveSource(InputEventData eventData)
{
uint sourceId = eventData.SourceId;
handPositionMap.Remove(sourceId);
handSourceMap.Remove(sourceId);
gazePointMap.Remove(sourceId);
handStartPositionMap.Remove(sourceId);
positionAvailableMap.Remove(sourceId);
}
///
/// This function must be called from the OnSourceLost handler in a script implementing the IMixedRealitySourceStateHandler interface.
///
public void RemoveSource(SourceStateEventData eventData)
{
uint sourceId = eventData.SourceId;
handPositionMap.Remove(sourceId);
handSourceMap.Remove(sourceId);
gazePointMap.Remove(sourceId);
handStartPositionMap.Remove(sourceId);
positionAvailableMap.Remove(sourceId);
}
///
/// This function must be called from the OnInputChanged handler in a script implementing the .
///
public void UpdateSource(InputEventData eventData)
{
uint id = eventData.SourceId;
Vector3 handPosition = eventData.InputData.Position;
if (IsInDictionary(id) == true)
{
if (handStartPositionMap[id] == Vector3.zero)
{
handStartPositionMap[id] = handPosition;
positionAvailableMap[id] = true;
}
if (true == TryGetPointerPosition(id, out Vector3 currentGazePoint))
{
handPositionMap[id] = handPosition + (currentGazePoint - gazePointMap[id]);
}
}
}
///
/// This function returns the number of active hands.
///
public int GetActiveHandCount()
{
int count = 0;
foreach (uint key in positionAvailableMap.Keys)
{
if (positionAvailableMap[key] == true)
{
count++;
}
}
return count;
}
///
/// This function gets the average of the positions of all active hands.
///
/// Vector3 representing the average position
public Vector3 GetHandsCentroid()
{
if (true == TryGetHandsCentroid(out Vector3 centroid))
{
return centroid;
}
return Vector3.zero;
}
///
/// This function gets an array of all active hand positions
///
/// enumerable of Vector3
public IEnumerable GetAllHandPositions()
{
foreach (uint key in positionAvailableMap.Keys)
{
if (positionAvailableMap[key] == true)
{
yield return handPositionMap[key];
}
}
yield break;
}
///
/// This function retrieves the position of the first active hand.
///
/// Vector3 representing position
public Vector3 GetFirstHand()
{
foreach (Vector3 hand in GetAllHandPositions())
return hand;
return Vector3.zero;
}
///
/// This function retrieves a reference to the Dictionary that maps hand positions to sourceIds.
/// This return value is NOT filtered for whether the hands are active. User should check first
/// using GetActiveHandCount().
///
/// Dictionary with uint Keys mapping to Vector3 positions
public Dictionary GetHandPositionsDictionary()
{
return handPositionMap;
}
#endregion Public Methods
#region Safe TryGet Style Public Methods
///
/// TryGet style function to return HandPosition of a certain handedness if available.
///
/// asks for left or right hand or either
/// out value that gets filled with a Vector3 representing position
/// true or false- whether the hand existed
public bool TryGetHandPosition(Handedness handedness, out Vector3 position)
{
foreach (uint key in positionAvailableMap.Keys)
{
if (positionAvailableMap[key] == true)
{
if (handSourceMap[key].Pointers != null && handSourceMap[key].Pointers.Length > 0 && handSourceMap[key].Pointers[0].Controller.ControllerHandedness == handedness)
{
position = handPositionMap[key];
return true;
}
}
}
position = Vector3.zero;
return false;
}
///
/// TryGet style function to return HandPosition of a certain sourceId if available.
///
/// asks for the hand position associated with a certain IMixedRealityInputSource id
/// out value that gets filled with a Vector3 representing position
/// true or false- whether the hand existed
public bool TryGetHandPosition(uint id, out Vector3 handPosition)
{
if (IsInDictionary(id) == true && positionAvailableMap[id] == true)
{
handPosition = handPositionMap[id];
return true;
}
handPosition = Vector3.zero;
return false;
}
///
/// TryGet style function to get the average of all active hand positions.
///
/// out value filled with Vector3 representing average of hand positions
/// true if there were any active hands; false if there were no active hands
public bool TryGetHandsCentroid(out Vector3 handsCentroid)
{
if (GetActiveHandCount() > 0)
{
Vector3 agg = Vector3.zero;
int activeCount = 0;
foreach (uint key in handPositionMap.Keys)
{
if (positionAvailableMap[key] == true)
{
agg += handPositionMap[key];
activeCount++;
}
}
if (activeCount > 0)
{
handsCentroid = agg / (float)activeCount;
return true;
}
}
handsCentroid = Vector3.zero;
return false;
}
#endregion Safe TryGet Style Public Methods
#region Private Methods
private bool IsInDictionary(uint id)
{
return handSourceMap.ContainsKey(id);
}
private bool GetInitialHandPosition(uint id, out Vector3 initialHandPosition)
{
if (IsInDictionary(id) == true)
{
initialHandPosition = handStartPositionMap[id];
return true;
}
initialHandPosition = Vector3.zero;
return false;
}
private bool GetInitialGazePosition(uint id, out Vector3 initialGazePosition)
{
if (IsInDictionary(id) == true)
{
initialGazePosition = gazePointMap[id];
return true;
}
initialGazePosition = Vector3.zero;
return false;
}
private bool TryGetPointerPosition(uint id, out Vector3 position)
{
IMixedRealityInputSource source = handSourceMap[id];
if (source != null && source.Pointers != null && source.Pointers.Length > 0)
{
position = source.Pointers[0].Position;
return true;
}
position = Vector3.zero;
return false;
}
#endregion Private Methods
}
}