Skip to content

Commit a2d8f8a

Browse files
committed
Abandon base class impl in favor of composition
COMPOSITION OVER INHERITANCE YO
1 parent c162a65 commit a2d8f8a

File tree

6 files changed

+147
-79
lines changed

6 files changed

+147
-79
lines changed

Assets/MRTK/Core/Interfaces/InputSystem/IMixedRealityEyeSaccadeProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ namespace Microsoft.MixedReality.Toolkit.Input
88
/// <summary>
99
/// Provides eye tracking saccade events.
1010
/// </summary>
11-
public interface IMixedRealityEyeSaccadeProvider : IMixedRealityDataProvider
11+
public interface IMixedRealityEyeSaccadeProvider
1212
{
1313
/// <summary>
1414
/// Triggered when user is saccading across the view (jumping quickly with their eye gaze above a certain threshold in visual angles).

Assets/MRTK/Core/Providers/BaseEyeGazeDataProvider.cs renamed to Assets/MRTK/Core/Utilities/EyeGazeSmoother.cs

Lines changed: 16 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,19 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using Microsoft.MixedReality.Toolkit.Input;
45
using System;
56
using System.Collections.Generic;
67
using Unity.Profiling;
78
using UnityEngine;
89

9-
namespace Microsoft.MixedReality.Toolkit.Input
10+
namespace Microsoft.MixedReality.Toolkit.Utilities
1011
{
1112
/// <summary>
1213
/// Provides some predefined parameters for eye gaze smoothing and saccade detection.
1314
/// </summary>
14-
public abstract class BaseEyeGazeDataProvider : BaseInputDeviceManager, IMixedRealityEyeGazeDataProvider, IMixedRealityEyeSaccadeProvider
15+
public class EyeGazeSmoother : IMixedRealityEyeSaccadeProvider
1516
{
16-
/// <summary>
17-
/// Constructor.
18-
/// </summary>
19-
/// <param name="inputSystem">The <see cref="Microsoft.MixedReality.Toolkit.Input.IMixedRealityInputSystem"/> instance that receives data from this provider.</param>
20-
/// <param name="name">Friendly name of the service.</param>
21-
/// <param name="priority">Service priority. Used to determine order of instantiation.</param>
22-
/// <param name="profile">The service's configuration profile.</param>
23-
public BaseEyeGazeDataProvider(
24-
IMixedRealityInputSystem inputSystem,
25-
string name,
26-
uint priority,
27-
BaseMixedRealityProfile profile) : base(inputSystem, name, priority, profile) { }
28-
29-
/// <inheritdoc />
30-
public bool SmoothEyeTracking { get; set; } = false;
31-
32-
/// <inheritdoc />
33-
public IMixedRealityEyeSaccadeProvider SaccadeProvider => this;
34-
3517
/// <inheritdoc />
3618
public event Action OnSaccade;
3719

@@ -50,56 +32,21 @@ public BaseEyeGazeDataProvider(
5032
private Ray saccade_initialGazePoint;
5133
private readonly List<Ray> saccade_newGazeCluster = new List<Ray>();
5234

53-
/// <inheritdoc />
54-
public override void Initialize()
55-
{
56-
#if UNITY_EDITOR && UNITY_WSA && UNITY_2019_3_OR_NEWER
57-
Utilities.Editor.UWPCapabilityUtility.RequireCapability(
58-
UnityEditor.PlayerSettings.WSACapability.GazeInput,
59-
GetType());
60-
#endif // UNITY_EDITOR && UNITY_WSA && UNITY_2019_3_OR_NEWER
61-
62-
if (Application.isPlaying)
63-
{
64-
ReadProfile();
65-
}
66-
67-
base.Initialize();
68-
}
69-
70-
private void ReadProfile()
71-
{
72-
if (ConfigurationProfile == null)
73-
{
74-
Debug.LogError($"{GetType()} requires a configuration profile to run properly.");
75-
return;
76-
}
77-
78-
MixedRealityEyeTrackingProfile profile = ConfigurationProfile as MixedRealityEyeTrackingProfile;
79-
if (profile == null)
80-
{
81-
Debug.LogError($"{GetType()}'s configuration profile must be a MixedRealityEyeTrackingProfile.");
82-
return;
83-
}
84-
85-
SmoothEyeTracking = profile.SmoothEyeTracking;
86-
}
87-
88-
private static readonly ProfilerMarker SmoothGazePerfMarker = new ProfilerMarker("[MRTK] BaseEyeGazeDataProvider.SmoothGaze");
35+
private static readonly ProfilerMarker SmoothGazePerfMarker = new ProfilerMarker("[MRTK] EyeGazeSmoother.SmoothGaze");
8936

9037
/// <summary>
9138
/// Smooths eye gaze by detecting saccades and tracking gaze clusters.
9239
/// </summary>
9340
/// <param name="newGaze">The ray to smooth.</param>
9441
/// <returns>The smoothed ray.</returns>
95-
protected Ray SmoothGaze(Ray? newGaze)
42+
public Ray SmoothGaze(Ray newGaze)
9643
{
9744
using (SmoothGazePerfMarker.Auto())
9845
{
9946
if (!oldGaze.HasValue)
10047
{
10148
oldGaze = newGaze;
102-
return newGaze.Value;
49+
return newGaze;
10350
}
10451

10552
Ray smoothedGaze = new Ray();
@@ -109,12 +56,12 @@ protected Ray SmoothGaze(Ray? newGaze)
10956
// apart, we check for clusters of gaze points instead.
11057
// 1. If the user's gaze points are far enough apart, this may be a saccade, but also could be an outlier.
11158
// So, let's mark it as a potential saccade.
112-
if (IsSaccading(oldGaze.Value, newGaze.Value) && confidenceOfSaccade == 0)
59+
if (IsSaccading(oldGaze.Value, newGaze) && confidenceOfSaccade == 0)
11360
{
11461
confidenceOfSaccade++;
11562
saccade_initialGazePoint = oldGaze.Value;
11663
saccade_newGazeCluster.Clear();
117-
saccade_newGazeCluster.Add(newGaze.Value);
64+
saccade_newGazeCluster.Add(newGaze);
11865
}
11966
// 2. If we have a potential saccade marked, let's check if the new points are within the proximity of
12067
// the initial saccade point.
@@ -127,19 +74,19 @@ protected Ray SmoothGaze(Ray? newGaze)
12774
// amount of time resulting in a cluster of gaze points.
12875
for (int i = 0; i < saccade_newGazeCluster.Count; i++)
12976
{
130-
if (IsSaccading(saccade_newGazeCluster[i], newGaze.Value))
77+
if (IsSaccading(saccade_newGazeCluster[i], newGaze))
13178
{
13279
confidenceOfSaccade = 0;
13380
}
13481

13582
// Meanwhile we want to make sure that we are still looking sufficiently far away from our
13683
// original gaze point before saccading.
137-
if (!IsSaccading(saccade_initialGazePoint, newGaze.Value))
84+
if (!IsSaccading(saccade_initialGazePoint, newGaze))
13885
{
13986
confidenceOfSaccade = 0;
14087
}
14188
}
142-
saccade_newGazeCluster.Add(newGaze.Value);
89+
saccade_newGazeCluster.Add(newGaze);
14390
}
14491
else if (confidenceOfSaccade == confidenceOfSaccadeThreshold)
14592
{
@@ -149,22 +96,22 @@ protected Ray SmoothGaze(Ray? newGaze)
14996
// Saccade-dependent local smoothing
15097
if (isSaccading)
15198
{
152-
smoothedGaze.direction = newGaze.Value.direction;
153-
smoothedGaze.origin = newGaze.Value.origin;
99+
smoothedGaze.direction = newGaze.direction;
100+
smoothedGaze.origin = newGaze.origin;
154101
confidenceOfSaccade = 0;
155102
}
156103
else
157104
{
158-
smoothedGaze.direction = oldGaze.Value.direction * smoothFactorNormalized + newGaze.Value.direction * (1 - smoothFactorNormalized);
159-
smoothedGaze.origin = oldGaze.Value.origin * smoothFactorNormalized + newGaze.Value.origin * (1 - smoothFactorNormalized);
105+
smoothedGaze.direction = oldGaze.Value.direction * smoothFactorNormalized + newGaze.direction * (1 - smoothFactorNormalized);
106+
smoothedGaze.origin = oldGaze.Value.origin * smoothFactorNormalized + newGaze.origin * (1 - smoothFactorNormalized);
160107
}
161108

162109
oldGaze = smoothedGaze;
163110
return smoothedGaze;
164111
}
165112
}
166113

167-
private static readonly ProfilerMarker IsSaccadingPerfMarker = new ProfilerMarker("[MRTK] BaseEyeGazeDataProvider.IsSaccading");
114+
private static readonly ProfilerMarker IsSaccadingPerfMarker = new ProfilerMarker("[MRTK] EyeGazeSmoother.IsSaccading");
168115

169116
private bool IsSaccading(Ray rayOld, Ray rayNew)
170117
{

Assets/MRTK/Core/Providers/BaseEyeGazeDataProvider.cs.meta renamed to Assets/MRTK/Core/Utilities/EyeGazeSmoother.cs.meta

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/MRTK/Providers/WindowsMixedReality/XR2018/WindowsMixedRealityEyeGazeDataProvider.cs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace Microsoft.MixedReality.Toolkit.WindowsMixedReality.Input
2626
"Windows Mixed Reality Eye Gaze Provider",
2727
"Profiles/DefaultMixedRealityEyeTrackingProfile.asset", "MixedRealityToolkit.SDK",
2828
true)]
29-
public class WindowsMixedRealityEyeGazeDataProvider : BaseEyeGazeDataProvider, IMixedRealityCapabilityCheck
29+
public class WindowsMixedRealityEyeGazeDataProvider : BaseInputDeviceManager, IMixedRealityEyeGazeDataProvider, IMixedRealityEyeSaccadeProvider, IMixedRealityCapabilityCheck
3030
{
3131
/// <summary>
3232
/// Constructor.
@@ -71,8 +71,37 @@ public WindowsMixedRealityEyeGazeDataProvider(
7171
eyesApiAvailable &= EyesPose.IsSupported();
7272
}
7373
#endif // (UNITY_WSA && DOTNETWINRT_PRESENT) || WINDOWS_UWP
74+
75+
gazeSmoother = new EyeGazeSmoother();
76+
77+
// Register for these events to forward along, in case code is still registering for the obsolete actions
78+
gazeSmoother.OnSaccade += GazeSmoother_OnSaccade;
79+
gazeSmoother.OnSaccadeX += GazeSmoother_OnSaccadeX;
80+
gazeSmoother.OnSaccadeY += GazeSmoother_OnSaccadeY;
7481
}
7582

83+
/// <inheritdoc />
84+
public bool SmoothEyeTracking { get; set; } = false;
85+
86+
/// <inheritdoc />
87+
public IMixedRealityEyeSaccadeProvider SaccadeProvider => gazeSmoother;
88+
private readonly EyeGazeSmoother gazeSmoother;
89+
90+
/// <inheritdoc />
91+
[Obsolete("Register for this provider's SaccadeProvider's actions instead")]
92+
public event Action OnSaccade;
93+
private void GazeSmoother_OnSaccade() => OnSaccade?.Invoke();
94+
95+
/// <inheritdoc />
96+
[Obsolete("Register for this provider's SaccadeProvider's actions instead")]
97+
public event Action OnSaccadeX;
98+
private void GazeSmoother_OnSaccadeX() => OnSaccadeX?.Invoke();
99+
100+
/// <inheritdoc />
101+
[Obsolete("Register for this provider's SaccadeProvider's actions instead")]
102+
public event Action OnSaccadeY;
103+
private void GazeSmoother_OnSaccadeY() => OnSaccadeY?.Invoke();
104+
76105
private readonly bool eyesApiAvailable = false;
77106

78107
#if (UNITY_WSA && DOTNETWINRT_PRESENT) || WINDOWS_UWP
@@ -89,16 +118,44 @@ public WindowsMixedRealityEyeGazeDataProvider(
89118
/// <inheritdoc />
90119
public override void Initialize()
91120
{
121+
#if UNITY_EDITOR && UNITY_WSA && UNITY_2019_3_OR_NEWER
122+
Utilities.Editor.UWPCapabilityUtility.RequireCapability(
123+
UnityEditor.PlayerSettings.WSACapability.GazeInput,
124+
GetType());
125+
#endif // UNITY_EDITOR && UNITY_WSA && UNITY_2019_3_OR_NEWER
126+
92127
if (Application.isPlaying && eyesApiAvailable)
93128
{
94129
#if (UNITY_WSA && DOTNETWINRT_PRESENT) || WINDOWS_UWP
95130
AskForETPermission();
96131
#endif // (UNITY_WSA && DOTNETWINRT_PRESENT) || WINDOWS_UWP
97132
}
98133

134+
ReadProfile();
135+
136+
// Call the base after initialization to ensure any early exits do not
137+
// artificially declare the service as initialized.
99138
base.Initialize();
100139
}
101140

141+
private void ReadProfile()
142+
{
143+
if (ConfigurationProfile == null)
144+
{
145+
Debug.LogError($"{Name} requires a configuration profile to run properly.");
146+
return;
147+
}
148+
149+
MixedRealityEyeTrackingProfile profile = ConfigurationProfile as MixedRealityEyeTrackingProfile;
150+
if (profile == null)
151+
{
152+
Debug.LogError($"{Name}'s configuration profile must be a MixedRealityEyeTrackingProfile.");
153+
return;
154+
}
155+
156+
SmoothEyeTracking = profile.SmoothEyeTracking;
157+
}
158+
102159
#if (UNITY_WSA && DOTNETWINRT_PRESENT) || WINDOWS_UWP
103160
private static readonly ProfilerMarker UpdatePerfMarker = new ProfilerMarker("[MRTK] WindowsMixedRealityEyeGazeDataProvider.Update");
104161

@@ -131,7 +188,7 @@ public override void Update()
131188

132189
if (SmoothEyeTracking)
133190
{
134-
newGaze = SmoothGaze(newGaze);
191+
newGaze = gazeSmoother.SmoothGaze(newGaze);
135192
}
136193

137194
Service?.EyeGazeProvider?.UpdateEyeGaze(this, newGaze, eyes.UpdateTimestamp.TargetTime.UtcDateTime);

Assets/MRTK/Providers/WindowsMixedReality/XRSDK/MRTK.WMR.XRSDK.asmdef

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"name": "Microsoft.MixedReality.Toolkit.Providers.XRSDK.WindowsMixedReality",
33
"references": [
44
"Microsoft.MixedReality.Toolkit",
5+
"Microsoft.MixedReality.Toolkit.Editor.Utilities",
56
"Microsoft.MixedReality.Toolkit.Providers.WindowsMixedReality.Shared",
67
"Microsoft.MixedReality.Toolkit.Providers.XRSDK",
78
"Unity.XR.WindowsMixedReality"

0 commit comments

Comments
 (0)