1
+ /******************************************************************************
2
+ * Copyright (C) Ultraleap, Inc. 2011-2021. *
3
+ * *
4
+ * Use subject to the terms of the Apache License 2.0 available at *
5
+ * http://www.apache.org/licenses/LICENSE-2.0, or another agreement *
6
+ * between Ultraleap and you, your company or other organization. *
7
+ ******************************************************************************/
8
+
9
+ using Leap . Unity . Encoding ;
10
+ using System . Collections ;
11
+ using System . Collections . Generic ;
12
+ using UnityEngine ;
13
+
14
+
15
+ namespace Leap . Unity
16
+ {
17
+
18
+ /// <summary>
19
+ /// John's and Flo's aggregation code. An example of how aggregation could be implemented.
20
+ /// only works for the first two hands it sees (of the same chirality)
21
+ /// </summary>
22
+ public class AggregationProviderAngularInterpolation : LeapAggregatedProviderBase
23
+ {
24
+
25
+ private Vector3 tempHandPalmPosition ;
26
+ private Vector3 midDevicePointPosition ;
27
+ private Vector3 midDevicePointForward ;
28
+ private Vector3 midDevicePointUp ;
29
+
30
+ public float cam1Aplha ;
31
+ public float cam2Aplha ;
32
+
33
+ public float leftAngle ;
34
+ public float rightAngle ;
35
+
36
+ public float maxInterpolationAngle = 60 ;
37
+
38
+ public Transform midpointDevices ; //used to calculate relative angle and weight hands accordingly. Transform should face direction that bisects FOV of devices
39
+
40
+
41
+ protected override Frame MergeFrames ( Frame [ ] frames )
42
+ {
43
+ Frame mergedFrame = frames [ 0 ] ;
44
+ Hand [ ] mergedHands = MergeHands ( frames ) ;
45
+ mergedFrame . Hands = mergedHands == null ? new List < Hand > ( ) : new List < Hand > ( MergeHands ( frames ) ) ;
46
+ return mergedFrame ;
47
+ }
48
+
49
+ private Hand [ ] MergeHands ( Frame [ ] frames )
50
+ {
51
+ /*
52
+ * This function returns one set of hands from multiple Leap Providers, weighing their influence using hands' positions relative to devices.
53
+ */
54
+
55
+ if ( frames . Length == 0 ) Debug . Log ( "frames has a length of 0" ) ;
56
+ //sort Left and Right hands (some values may be null since never know how many hands are visible, but we clean it up at the end)
57
+ Hand [ ] LeftHands = new Hand [ providers . Length ] ;
58
+ Hand [ ] RightHands = new Hand [ providers . Length ] ;
59
+ Hand [ ] mergedHands ;
60
+ for ( int i = 0 ; i < frames . Length ; i ++ )
61
+ {
62
+ if ( frames [ i ] . Hands == null ) Debug . Log ( "hand is null in frame " + i ) ;
63
+ //frame_timestamps = new List<long>();
64
+ //frame_timestamps.Add(providers[i].CurrentFrame.Timestamp);
65
+ foreach ( Hand tempHand in frames [ i ] . Hands )
66
+ {
67
+ if ( tempHand . IsLeft )
68
+ LeftHands [ i ] = tempHand ;
69
+ else
70
+ RightHands [ i ] = tempHand ;
71
+ }
72
+ }
73
+
74
+ //combine hands using relative angle between devices:
75
+ Hand confidentLeft = AngularInterpolate ( LeftHands , ref cam1Aplha , ref leftAngle ) ;
76
+ Hand confidentRight = AngularInterpolate ( RightHands , ref cam2Aplha , ref rightAngle ) ;
77
+
78
+ //clean up and return hand arrays with only valid hands
79
+ #region Clean hand arrays
80
+ if ( confidentLeft != null && confidentRight != null )
81
+ {
82
+ mergedHands = new Hand [ 2 ] ;
83
+ mergedHands [ 0 ] = confidentLeft ;
84
+ mergedHands [ 1 ] = confidentRight ;
85
+ }
86
+ else if ( confidentLeft == null && confidentRight == null )
87
+ {
88
+ mergedHands = null ;
89
+ }
90
+ else
91
+ {
92
+ mergedHands = new Hand [ 1 ] ;
93
+ if ( confidentLeft != null )
94
+ mergedHands [ 0 ] = confidentLeft ;
95
+ else
96
+ mergedHands [ 0 ] = confidentRight ;
97
+ }
98
+ #endregion
99
+
100
+ return mergedHands ;
101
+ }
102
+
103
+ public static float AngleSigned ( Vector3 v1 , Vector3 v2 , Vector3 n )
104
+ {
105
+ // v1 = average palm position
106
+ // v2 = device midpoint up vector
107
+ // n = device midpoint forward vector
108
+
109
+ return Mathf . Atan2 (
110
+ Vector3 . Dot ( n , Vector3 . Cross ( v1 , v2 ) ) ,
111
+ Vector3 . Dot ( v1 , v2 ) ) * Mathf . Rad2Deg ;
112
+
113
+
114
+ }
115
+
116
+ private Hand AngularInterpolate ( Hand [ ] handList , ref float alpha , ref float angle )
117
+ {
118
+ /*
119
+ * Combines list of hands (of one chiarality) into one Leap.Hand, by weighing the relative angle to the devices
120
+ */
121
+
122
+ /*
123
+ * TODO: fix handoff of hand when changing providers, check if chirality changed, etc
124
+ * */
125
+
126
+ //find average palm position since we don't exactly know which provider is closest to reality:
127
+ #region PreProcess Raw Hand Data
128
+ Hand tempHand = null ;
129
+ int numValidHands = 0 ;
130
+ foreach ( Hand hand in handList )
131
+ {
132
+ if ( hand != null && hand . Confidence > 0.98f && hand . TimeVisible > 0.5f ) //only use hands with high confidence to avoid when hand is barely in view
133
+ {
134
+ if ( tempHand == null )
135
+ {
136
+ tempHand = new Hand ( ) ;
137
+ tempHand . CopyFrom ( hand ) ;
138
+ }
139
+ else
140
+ {
141
+ Leap . Vector nH = hand . PalmPosition ;
142
+ Leap . Vector tH = tempHand . PalmPosition ;
143
+ tempHand . PalmPosition = new Leap . Vector ( aprxAvg ( nH . x , tH . x ) , aprxAvg ( nH . y , tH . y ) , aprxAvg ( nH . z , tH . z ) ) ;
144
+ //tempHand.PalmPosition = hand.PalmPosition;
145
+
146
+
147
+ }
148
+ numValidHands ++ ;
149
+ }
150
+ }
151
+
152
+ if ( tempHand != null )
153
+ {
154
+ tempHandPalmPosition = tempHand . PalmPosition . ToVector3 ( ) ;
155
+ }
156
+ #endregion
157
+
158
+ if ( numValidHands > 0 )
159
+ {
160
+
161
+ //calculate angle between midpoint between devices(i.e. providers):
162
+ Vector3 devicesMiddle = Vector3 . zero ;
163
+ Vector3 devicesAvgForward = Vector3 . zero ;
164
+ for ( int i = 0 ; i < providers . Length ; i ++ )
165
+ {
166
+ devicesAvgForward += providers [ i ] . transform . forward ;
167
+ devicesMiddle += providers [ i ] . transform . position ;
168
+ }
169
+ devicesAvgForward = devicesAvgForward / providers . Length ;
170
+ devicesMiddle = devicesMiddle / providers . Length ;
171
+
172
+ midpointDevices . position = devicesMiddle ;
173
+ midpointDevices . forward = devicesAvgForward ;
174
+
175
+ midDevicePointPosition = midpointDevices . position ;
176
+ midDevicePointForward = midpointDevices . forward ;
177
+ midDevicePointUp = midpointDevices . up ;
178
+
179
+ Vector3 angleCalculationHandPosition = tempHand . PalmPosition . ToVector3 ( ) ;
180
+
181
+ angle = AngleSigned ( angleCalculationHandPosition , midpointDevices . position + midpointDevices . up , midpointDevices . forward ) ;
182
+ //Debug.Log(angle);
183
+
184
+ alpha = Mathf . Clamp ( angle , - maxInterpolationAngle / 2 , maxInterpolationAngle / 2 ) ;
185
+ alpha = ( ( alpha + ( maxInterpolationAngle / 2 ) ) / ( maxInterpolationAngle ) ) ; //normalize to a 0-1 scale
186
+
187
+ //Interpolate using alpha:
188
+ Hand interpolateHand = new Hand ( ) ;
189
+
190
+ if ( numValidHands == 1 )
191
+ {
192
+ interpolateHand . CopyFrom ( tempHand ) ;
193
+ }
194
+ else if ( numValidHands > 1 ) //Note: this implementation only works with first 2 hands:
195
+ {
196
+ VectorHand vectorInterpolatedHand = new VectorHand ( ) ;
197
+ vectorInterpolatedHand . FillLerped ( new VectorHand ( handList [ 0 ] ) , new VectorHand ( handList [ 1 ] ) , alpha ) ;
198
+ vectorInterpolatedHand . Decode ( interpolateHand ) ;
199
+ }
200
+
201
+ tempHand = interpolateHand ;
202
+ }
203
+
204
+ return tempHand ;
205
+ }
206
+
207
+ private float aprxAvg ( float avg , float new_sample )
208
+ {
209
+ /*
210
+ * Utility function of running average
211
+ */
212
+
213
+ avg -= avg / 2 ;
214
+ avg += new_sample / 2 ;
215
+
216
+ return avg ;
217
+ }
218
+
219
+ public void OnDrawGizmos ( )
220
+ {
221
+ if ( tempHandPalmPosition != null )
222
+ {
223
+ Gizmos . DrawSphere ( tempHandPalmPosition , 0.01f ) ;
224
+ }
225
+
226
+ midDevicePointForward . Normalize ( ) ;
227
+ Gizmos . DrawSphere ( midDevicePointPosition , 0.01f ) ;
228
+ Gizmos . DrawSphere ( midDevicePointForward + midDevicePointPosition , 0.02f ) ;
229
+
230
+ Gizmos . color = Color . yellow ;
231
+ Gizmos . DrawLine ( midDevicePointPosition , midDevicePointForward ) ;
232
+
233
+ Gizmos . color = Color . green ;
234
+ midDevicePointUp . Normalize ( ) ;
235
+ Gizmos . DrawSphere ( midDevicePointUp , 0.01f ) ;
236
+ Gizmos . DrawSphere ( midDevicePointUp + midDevicePointPosition , 0.02f ) ;
237
+ Gizmos . DrawLine ( midDevicePointPosition , midDevicePointUp ) ;
238
+
239
+ var leftLimit = Quaternion . Euler ( 0 , 0 , - maxInterpolationAngle * 0.5f ) * midDevicePointUp ;
240
+ Gizmos . DrawLine ( midDevicePointPosition , leftLimit ) ;
241
+
242
+ var rightLimit = Quaternion . Euler ( 0 , 0 , maxInterpolationAngle * 0.5f ) * midDevicePointUp ;
243
+ Gizmos . DrawLine ( midDevicePointPosition , rightLimit ) ;
244
+ }
245
+ }
246
+ }
0 commit comments