AR Design
UBC EML collab with UBC SALA - visualizing IoT data in AR
Interpolator.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 
4 using UnityEngine;
5 
6 namespace HoloToolkit.Unity
7 {
11  public class Interpolator : MonoBehaviour
12  {
13  [Tooltip("When interpolating, use unscaled time. This is useful for games that have a pause mechanism or otherwise adjust the game timescale.")]
14  public bool UseUnscaledTime = true;
15 
16  // A very small number that is used in determining if the Interpolator
17  // needs to run at all.
18  private const float smallNumber = 0.0000001f;
19 
20  // The movement speed in meters per second
21  public float PositionPerSecond = 30.0f;
22 
23  // The rotation speed, in degrees per second
24  public float RotationDegreesPerSecond = 720.0f;
25 
26  // Adjusts rotation speed based on angular distance
27  public float RotationSpeedScaler = 0.0f;
28 
29  // The amount to scale per second
30  public float ScalePerSecond = 5.0f;
31 
32  // Lerp the estimated targets towards the object each update,
33  // slowing and smoothing movement.
34  [HideInInspector]
35  public bool SmoothLerpToTarget = false;
36  [HideInInspector]
37  public float SmoothPositionLerpRatio = 0.5f;
38  [HideInInspector]
39  public float SmoothRotationLerpRatio = 0.5f;
40  [HideInInspector]
41  public float SmoothScaleLerpRatio = 0.5f;
42 
43  // Position data
44  private Vector3 targetPosition;
45 
49  public bool AnimatingPosition { get; private set; }
50 
51  // Rotation data
52  private Quaternion targetRotation;
53 
57  public bool AnimatingRotation { get; private set; }
58 
59  // Local Rotation data
60  private Quaternion targetLocalRotation;
61 
65  public bool AnimatingLocalRotation { get; private set; }
66 
67  // Scale data
68  private Vector3 targetLocalScale;
69 
73  public bool AnimatingLocalScale { get; private set; }
74 
78  public event System.Action InterpolationStarted;
79 
83  public event System.Action InterpolationDone;
84 
88  public Vector3 PositionVelocity { get; private set; }
89 
90  private Vector3 oldPosition = Vector3.zero;
91 
95  public bool Running
96  {
97  get
98  {
99  return (AnimatingPosition || AnimatingRotation || AnimatingLocalRotation || AnimatingLocalScale);
100  }
101  }
102 
103  public void Awake()
104  {
105  targetPosition = transform.position;
106  targetRotation = transform.rotation;
107  targetLocalRotation = transform.localRotation;
108  targetLocalScale = transform.localScale;
109 
110  enabled = false;
111  }
112 
118  public void SetTargetPosition(Vector3 target)
119  {
120  bool wasRunning = Running;
121 
122  targetPosition = target;
123 
124  float magsq = (targetPosition - transform.position).sqrMagnitude;
125  if (magsq > smallNumber)
126  {
127  AnimatingPosition = true;
128  enabled = true;
129 
130  if (InterpolationStarted != null && !wasRunning)
131  {
132  InterpolationStarted();
133  }
134  }
135  else
136  {
137  // Set immediately to prevent accumulation of error.
138  transform.position = target;
139  AnimatingPosition = false;
140  }
141  }
142 
148  public void SetTargetRotation(Quaternion target)
149  {
150  bool wasRunning = Running;
151 
152  targetRotation = target;
153 
154  if (Quaternion.Dot(transform.rotation, target) < 1.0f)
155  {
156  AnimatingRotation = true;
157  enabled = true;
158 
159  if (InterpolationStarted != null && !wasRunning)
160  {
161  InterpolationStarted();
162  }
163  }
164  else
165  {
166  // Set immediately to prevent accumulation of error.
167  transform.rotation = target;
168  AnimatingRotation = false;
169  }
170  }
171 
177  public void SetTargetLocalRotation(Quaternion target)
178  {
179  bool wasRunning = Running;
180 
181  targetLocalRotation = target;
182 
183  if (Quaternion.Dot(transform.localRotation, target) < 1.0f)
184  {
185  AnimatingLocalRotation = true;
186  enabled = true;
187 
188  if (InterpolationStarted != null && !wasRunning)
189  {
190  InterpolationStarted();
191  }
192  }
193  else
194  {
195  // Set immediately to prevent accumulation of error.
196  transform.localRotation = target;
197  AnimatingLocalRotation = false;
198  }
199  }
200 
206  public void SetTargetLocalScale(Vector3 target)
207  {
208  bool wasRunning = Running;
209 
210  targetLocalScale = target;
211 
212  float magsq = (targetLocalScale - transform.localScale).sqrMagnitude;
213  if (magsq > Mathf.Epsilon)
214  {
215  AnimatingLocalScale = true;
216  enabled = true;
217 
218  if (InterpolationStarted != null && !wasRunning)
219  {
220  InterpolationStarted();
221  }
222  }
223  else
224  {
225  // set immediately to prevent accumulation of error
226  transform.localScale = target;
227  AnimatingLocalScale = false;
228  }
229  }
230 
239  public static Vector3 NonLinearInterpolateTo(Vector3 start, Vector3 target, float deltaTime, float speed)
240  {
241  // If no interpolation speed, jump to target value.
242  if (speed <= 0.0f)
243  {
244  return target;
245  }
246 
247  Vector3 distance = (target - start);
248 
249  // When close enough, jump to the target
250  if (distance.sqrMagnitude <= Mathf.Epsilon)
251  {
252  return target;
253  }
254 
255  // Apply the delta, then clamp so we don't overshoot the target
256  Vector3 deltaMove = distance * Mathf.Clamp(deltaTime * speed, 0.0f, 1.0f);
257 
258  return start + deltaMove;
259  }
260 
261  public void Update()
262  {
263  float deltaTime = UseUnscaledTime
264  ? Time.unscaledDeltaTime
265  : Time.deltaTime;
266 
267  bool interpOccuredThisFrame = false;
268 
269  if (AnimatingPosition)
270  {
271  Vector3 lerpTargetPosition = targetPosition;
272  if (SmoothLerpToTarget)
273  {
274  lerpTargetPosition = Vector3.Lerp(transform.position, lerpTargetPosition, SmoothPositionLerpRatio);
275  }
276 
277  Vector3 newPosition = NonLinearInterpolateTo(transform.position, lerpTargetPosition, deltaTime, PositionPerSecond);
278  if ((targetPosition - newPosition).sqrMagnitude <= smallNumber)
279  {
280  // Snap to final position
281  newPosition = targetPosition;
282  AnimatingPosition = false;
283  }
284  else
285  {
286  interpOccuredThisFrame = true;
287  }
288 
289  transform.position = newPosition;
290 
291  //calculate interpolatedVelocity and store position for next frame
292  PositionVelocity = oldPosition - newPosition;
293  oldPosition = newPosition;
294  }
295 
296  // Determine how far we need to rotate
297  if (AnimatingRotation)
298  {
299  Quaternion lerpTargetRotation = targetRotation;
300  if (SmoothLerpToTarget)
301  {
302  lerpTargetRotation = Quaternion.Lerp(transform.rotation, lerpTargetRotation, SmoothRotationLerpRatio);
303  }
304 
305  float angleDiff = Quaternion.Angle(transform.rotation, lerpTargetRotation);
306  float speedScale = 1.0f + (Mathf.Pow(angleDiff, RotationSpeedScaler) / 180.0f);
307  float ratio = Mathf.Clamp01((speedScale * RotationDegreesPerSecond * deltaTime) / angleDiff);
308 
309  if (angleDiff < Mathf.Epsilon)
310  {
311  AnimatingRotation = false;
312  transform.rotation = targetRotation;
313  }
314  else
315  {
316  // Only lerp rotation here, as ratio is NaN if angleDiff is 0.0f
317  transform.rotation = Quaternion.Slerp(transform.rotation, lerpTargetRotation, ratio);
318  interpOccuredThisFrame = true;
319  }
320  }
321 
322  // Determine how far we need to rotate
323  if (AnimatingLocalRotation)
324  {
325  Quaternion lerpTargetLocalRotation = targetLocalRotation;
326  if (SmoothLerpToTarget)
327  {
328  lerpTargetLocalRotation = Quaternion.Lerp(transform.localRotation, lerpTargetLocalRotation, SmoothRotationLerpRatio);
329  }
330 
331  float angleDiff = Quaternion.Angle(transform.localRotation, lerpTargetLocalRotation);
332  float speedScale = 1.0f + (Mathf.Pow(angleDiff, RotationSpeedScaler) / 180.0f);
333  float ratio = Mathf.Clamp01((speedScale * RotationDegreesPerSecond * deltaTime) / angleDiff);
334 
335  if (angleDiff < Mathf.Epsilon)
336  {
337  AnimatingLocalRotation = false;
338  transform.localRotation = targetLocalRotation;
339  }
340  else
341  {
342  // Only lerp rotation here, as ratio is NaN if angleDiff is 0.0f
343  transform.localRotation = Quaternion.Slerp(transform.localRotation, lerpTargetLocalRotation, ratio);
344  interpOccuredThisFrame = true;
345  }
346  }
347 
348  if (AnimatingLocalScale)
349  {
350  Vector3 lerpTargetLocalScale = targetLocalScale;
351  if (SmoothLerpToTarget)
352  {
353  lerpTargetLocalScale = Vector3.Lerp(transform.localScale, lerpTargetLocalScale, SmoothScaleLerpRatio);
354  }
355 
356  Vector3 newScale = NonLinearInterpolateTo(transform.localScale, lerpTargetLocalScale, deltaTime, ScalePerSecond);
357  if ((targetLocalScale - newScale).sqrMagnitude <= smallNumber)
358  {
359  // Snap to final scale
360  newScale = targetLocalScale;
361  AnimatingLocalScale = false;
362  }
363  else
364  {
365  interpOccuredThisFrame = true;
366  }
367 
368  transform.localScale = newScale;
369  }
370 
371  // If all interpolations have completed, stop updating
372  if (!interpOccuredThisFrame)
373  {
374  if (InterpolationDone != null)
375  {
376  InterpolationDone();
377  }
378  enabled = false;
379  }
380  }
381 
385  public void SnapToTarget()
386  {
387  if (enabled)
388  {
389  transform.position = TargetPosition;
390  transform.rotation = TargetRotation;
391  transform.localRotation = TargetLocalRotation;
392  transform.localScale = TargetLocalScale;
393 
394  AnimatingPosition = false;
395  AnimatingLocalScale = false;
396  AnimatingRotation = false;
397  AnimatingLocalRotation = false;
398 
399  enabled = false;
400 
401  if (InterpolationDone != null)
402  {
403  InterpolationDone();
404  }
405  }
406  }
407 
411  public void StopInterpolating()
412  {
413  if (enabled)
414  {
415  Reset();
416 
417  if (InterpolationDone != null)
418  {
419  InterpolationDone();
420  }
421  }
422  }
423 
427  public void Reset()
428  {
429  targetPosition = transform.position;
430  targetRotation = transform.rotation;
431  targetLocalRotation = transform.localRotation;
432  targetLocalScale = transform.localScale;
433 
434  AnimatingPosition = false;
435  AnimatingRotation = false;
436  AnimatingLocalRotation = false;
437  AnimatingLocalScale = false;
438 
439  enabled = false;
440  }
441 
447  public Vector3 TargetPosition
448  {
449  get
450  {
451  if (AnimatingPosition)
452  {
453  return targetPosition;
454  }
455  return transform.position;
456  }
457  }
458 
464  public Quaternion TargetRotation
465  {
466  get
467  {
468  if (AnimatingRotation)
469  {
470  return targetRotation;
471  }
472  return transform.rotation;
473  }
474  }
475 
481  public Quaternion TargetLocalRotation
482  {
483  get
484  {
485  if (AnimatingLocalRotation)
486  {
487  return targetLocalRotation;
488  }
489  return transform.localRotation;
490  }
491  }
492 
498  public Vector3 TargetLocalScale
499  {
500  get
501  {
502  if (AnimatingLocalScale)
503  {
504  return targetLocalScale;
505  }
506  return transform.localScale;
507  }
508  }
509  }
510 }
void SetTargetPosition(Vector3 target)
Sets the target position for the transform and if position wasn&#39;t already animating, fires the InterpolationStarted event.
static Vector3 NonLinearInterpolateTo(Vector3 start, Vector3 target, float deltaTime, float speed)
Interpolates smoothly to a target position.
void StopInterpolating()
Stops the interpolation regardless if it has reached the target
void SetTargetLocalScale(Vector3 target)
Sets the target local scale for the transform and if scale wasn&#39;t already animating, fires the InterpolationStarted event.
void SnapToTarget()
Snaps to the final target and stops interpolating
void Reset()
Stops the transform in place and terminates any animations.
void SetTargetLocalRotation(Quaternion target)
Sets the target local rotation for the transform and if rotation wasn&#39;t already animating, fires the InterpolationStarted event.
A MonoBehaviour that interpolates a transform&#39;s position, rotation or scale.
Definition: Interpolator.cs:11
void SetTargetRotation(Quaternion target)
Sets the target rotation for the transform and if rotation wasn&#39;t already animating, fires the InterpolationStarted event.
System.Action InterpolationStarted
The event fired when an Interpolation is started.
Definition: Interpolator.cs:78
System.Action InterpolationDone
The event fired when an Interpolation is completed.
Definition: Interpolator.cs:83