IMHOTEP Framework
 All Classes Namespaces Functions Variables Enumerations Enumerator Properties Pages
Util.cs
1 //======= Copyright (c) Valve Corporation, All rights reserved. ===============
2 //
3 // Purpose: Utility functions used in several places
4 //
5 //=============================================================================
6 
7 using UnityEngine;
8 using System;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.IO;
12 using System.Linq;
13 
14 namespace Valve.VR.InteractionSystem
15 {
16  //-------------------------------------------------------------------------
17  public static class Util
18  {
19  public const float FeetToMeters = 0.3048f;
20  public const float FeetToCentimeters = 30.48f;
21  public const float InchesToMeters = 0.0254f;
22  public const float InchesToCentimeters = 2.54f;
23  public const float MetersToFeet = 3.28084f;
24  public const float MetersToInches = 39.3701f;
25  public const float CentimetersToFeet = 0.0328084f;
26  public const float CentimetersToInches = 0.393701f;
27  public const float KilometersToMiles = 0.621371f;
28  public const float MilesToKilometers = 1.60934f;
29 
30  //-------------------------------------------------
31  // Remap num from range 1 to range 2
32  //-------------------------------------------------
33  public static float RemapNumber( float num, float low1, float high1, float low2, float high2 )
34  {
35  return low2 + ( num - low1 ) * ( high2 - low2 ) / ( high1 - low1 );
36  }
37 
38 
39  //-------------------------------------------------
40  public static float RemapNumberClamped( float num, float low1, float high1, float low2, float high2 )
41  {
42  return Mathf.Clamp( RemapNumber( num, low1, high1, low2, high2 ), Mathf.Min( low2, high2 ), Mathf.Max( low2, high2 ) );
43  }
44 
45 
46  //-------------------------------------------------
47  public static float Approach( float target, float value, float speed )
48  {
49  float delta = target - value;
50 
51  if ( delta > speed )
52  value += speed;
53  else if ( delta < -speed )
54  value -= speed;
55  else
56  value = target;
57 
58  return value;
59  }
60 
61 
62  //-------------------------------------------------
63  public static Vector3 BezierInterpolate3( Vector3 p0, Vector3 c0, Vector3 p1, float t )
64  {
65  Vector3 p0c0 = Vector3.Lerp( p0, c0, t );
66  Vector3 c0p1 = Vector3.Lerp( c0, p1, t );
67 
68  return Vector3.Lerp( p0c0, c0p1, t );
69  }
70 
71 
72  //-------------------------------------------------
73  public static Vector3 BezierInterpolate4( Vector3 p0, Vector3 c0, Vector3 c1, Vector3 p1, float t )
74  {
75  Vector3 p0c0 = Vector3.Lerp( p0, c0, t );
76  Vector3 c0c1 = Vector3.Lerp( c0, c1, t );
77  Vector3 c1p1 = Vector3.Lerp( c1, p1, t );
78 
79  Vector3 x = Vector3.Lerp( p0c0, c0c1, t );
80  Vector3 y = Vector3.Lerp( c0c1, c1p1, t );
81 
82  //Debug.DrawRay(p0, Vector3.forward);
83  //Debug.DrawRay(c0, Vector3.forward);
84  //Debug.DrawRay(c1, Vector3.forward);
85  //Debug.DrawRay(p1, Vector3.forward);
86 
87  //Gizmos.DrawSphere(p0c0, 0.5F);
88  //Gizmos.DrawSphere(c0c1, 0.5F);
89  //Gizmos.DrawSphere(c1p1, 0.5F);
90  //Gizmos.DrawSphere(x, 0.5F);
91  //Gizmos.DrawSphere(y, 0.5F);
92 
93  return Vector3.Lerp( x, y, t );
94  }
95 
96 
97  //-------------------------------------------------
98  public static Vector3 Vector3FromString( string szString )
99  {
100  string[] szParseString = szString.Substring( 1, szString.Length - 1 ).Split( ',' );
101 
102  float x = float.Parse( szParseString[0] );
103  float y = float.Parse( szParseString[1] );
104  float z = float.Parse( szParseString[2] );
105 
106  Vector3 vReturn = new Vector3( x, y, z );
107 
108  return vReturn;
109  }
110 
111 
112  //-------------------------------------------------
113  public static Vector2 Vector2FromString( string szString )
114  {
115  string[] szParseString = szString.Substring( 1, szString.Length - 1 ).Split( ',' );
116 
117  float x = float.Parse( szParseString[0] );
118  float y = float.Parse( szParseString[1] );
119 
120  Vector3 vReturn = new Vector2( x, y );
121 
122  return vReturn;
123  }
124 
125 
126  //-------------------------------------------------
127  public static float Normalize( float value, float min, float max )
128  {
129  float normalizedValue = ( value - min ) / ( max - min );
130 
131  return normalizedValue;
132  }
133 
134 
135  //-------------------------------------------------
136  public static Vector3 Vector2AsVector3( Vector2 v )
137  {
138  return new Vector3( v.x, 0.0f, v.y );
139  }
140 
141 
142  //-------------------------------------------------
143  public static Vector2 Vector3AsVector2( Vector3 v )
144  {
145  return new Vector2( v.x, v.z );
146  }
147 
148 
149  //-------------------------------------------------
150  public static float AngleOf( Vector2 v )
151  {
152  float fDist = v.magnitude;
153 
154  if ( v.y >= 0.0f )
155  {
156  return Mathf.Acos( v.x / fDist );
157  }
158  else
159  {
160  return Mathf.Acos( -v.x / fDist ) + Mathf.PI;
161  }
162  }
163 
164 
165  //-------------------------------------------------
166  public static float YawOf( Vector3 v )
167  {
168  float fDist = v.magnitude;
169 
170  if ( v.z >= 0.0f )
171  {
172  return Mathf.Acos( v.x / fDist );
173  }
174  else
175  {
176  return Mathf.Acos( -v.x / fDist ) + Mathf.PI;
177  }
178  }
179 
180 
181  //-------------------------------------------------
182  public static void Swap<T>( ref T lhs, ref T rhs )
183  {
184  T temp = lhs;
185  lhs = rhs;
186  rhs = temp;
187  }
188 
189 
190  //-------------------------------------------------
191  public static void Shuffle<T>( T[] array )
192  {
193  for ( int i = array.Length - 1; i > 0; i-- )
194  {
195  int r = UnityEngine.Random.Range( 0, i );
196  Swap( ref array[i], ref array[r] );
197  }
198  }
199 
200 
201  //-------------------------------------------------
202  public static void Shuffle<T>( List<T> list )
203  {
204  for ( int i = list.Count - 1; i > 0; i-- )
205  {
206  int r = UnityEngine.Random.Range( 0, i );
207  T temp = list[i];
208  list[i] = list[r];
209  list[r] = temp;
210  }
211  }
212 
213 
214  //-------------------------------------------------
215  public static int RandomWithLookback( int min, int max, List<int> history, int historyCount )
216  {
217  int index = UnityEngine.Random.Range( min, max - history.Count );
218 
219  for ( int i = 0; i < history.Count; i++ )
220  {
221  if ( index >= history[i] )
222  {
223  index++;
224  }
225  }
226 
227  history.Add( index );
228 
229  if ( history.Count > historyCount )
230  {
231  history.RemoveRange( 0, history.Count - historyCount );
232  }
233 
234  return index;
235  }
236 
237 
238  //-------------------------------------------------
239  public static Transform FindChild( Transform parent, string name )
240  {
241  if ( parent.name == name )
242  return parent;
243 
244  foreach ( Transform child in parent )
245  {
246  var found = FindChild( child, name );
247  if ( found != null )
248  return found;
249  }
250 
251  return null;
252  }
253 
254 
255  //-------------------------------------------------
256  public static bool IsNullOrEmpty<T>( T[] array )
257  {
258  if ( array == null )
259  return true;
260 
261  if ( array.Length == 0 )
262  return true;
263 
264  return false;
265  }
266 
267 
268  //-------------------------------------------------
269  public static bool IsValidIndex<T>( T[] array, int i )
270  {
271  if ( array == null )
272  return false;
273 
274  return ( i >= 0 ) && ( i < array.Length );
275  }
276 
277 
278  //-------------------------------------------------
279  public static bool IsValidIndex<T>( List<T> list, int i )
280  {
281  if ( list == null || list.Count == 0 )
282  return false;
283 
284  return ( i >= 0 ) && ( i < list.Count );
285  }
286 
287 
288  //-------------------------------------------------
289  public static int FindOrAdd<T>( List<T> list, T item )
290  {
291  int index = list.IndexOf( item );
292 
293  if ( index == -1 )
294  {
295  list.Add( item );
296  index = list.Count - 1;
297  }
298 
299  return index;
300  }
301 
302 
303  //-------------------------------------------------
304  public static List<T> FindAndRemove<T>( List<T> list, System.Predicate<T> match )
305  {
306  List<T> retVal = list.FindAll( match );
307  list.RemoveAll( match );
308  return retVal;
309  }
310 
311 
312  //-------------------------------------------------
313  public static T FindOrAddComponent<T>( GameObject gameObject ) where T : Component
314  {
315  T component = gameObject.GetComponent<T>();
316  if ( component )
317  return component;
318 
319  return gameObject.AddComponent<T>();
320  }
321 
322 
323  //-------------------------------------------------
324  public static void FastRemove<T>( List<T> list, int index )
325  {
326  list[index] = list[list.Count - 1];
327  list.RemoveAt( list.Count - 1 );
328  }
329 
330 
331  //-------------------------------------------------
332  public static void ReplaceGameObject<T, U>( T replace, U replaceWith )
333  where T : MonoBehaviour
334  where U : MonoBehaviour
335  {
336  replace.gameObject.SetActive( false );
337  replaceWith.gameObject.SetActive( true );
338  }
339 
340 
341  //-------------------------------------------------
342  public static void SwitchLayerRecursively( Transform transform, int fromLayer, int toLayer )
343  {
344  if ( transform.gameObject.layer == fromLayer )
345  transform.gameObject.layer = toLayer;
346 
347  int childCount = transform.childCount;
348  for ( int i = 0; i < childCount; i++ )
349  {
350  SwitchLayerRecursively( transform.GetChild( i ), fromLayer, toLayer );
351  }
352  }
353 
354 
355  //-------------------------------------------------
356  public static void DrawCross( Vector3 origin, Color crossColor, float size )
357  {
358  Vector3 line1Start = origin + ( Vector3.right * size );
359  Vector3 line1End = origin - ( Vector3.right * size );
360 
361  Debug.DrawLine( line1Start, line1End, crossColor );
362 
363  Vector3 line2Start = origin + ( Vector3.up * size );
364  Vector3 line2End = origin - ( Vector3.up * size );
365 
366  Debug.DrawLine( line2Start, line2End, crossColor );
367 
368  Vector3 line3Start = origin + ( Vector3.forward * size );
369  Vector3 line3End = origin - ( Vector3.forward * size );
370 
371  Debug.DrawLine( line3Start, line3End, crossColor );
372  }
373 
374 
375  //-------------------------------------------------
376  public static void ResetTransform( Transform t, bool resetScale = true )
377  {
378  t.localPosition = Vector3.zero;
379  t.localRotation = Quaternion.identity;
380  if ( resetScale )
381  {
382  t.localScale = new Vector3( 1f, 1f, 1f );
383  }
384  }
385 
386 
387  //-------------------------------------------------
388  public static Vector3 ClosestPointOnLine( Vector3 vA, Vector3 vB, Vector3 vPoint )
389  {
390  var vVector1 = vPoint - vA;
391  var vVector2 = ( vB - vA ).normalized;
392 
393  var d = Vector3.Distance( vA, vB );
394  var t = Vector3.Dot( vVector2, vVector1 );
395 
396  if ( t <= 0 )
397  return vA;
398 
399  if ( t >= d )
400  return vB;
401 
402  var vVector3 = vVector2 * t;
403 
404  var vClosestPoint = vA + vVector3;
405 
406  return vClosestPoint;
407  }
408 
409 
410  //-------------------------------------------------
411  public static void AfterTimer( GameObject go, float _time, System.Action callback, bool trigger_if_destroyed_early = false )
412  {
413  AfterTimer_Component afterTimer_component = go.AddComponent<AfterTimer_Component>();
414  afterTimer_component.Init( _time, callback, trigger_if_destroyed_early );
415  }
416 
417 
418  //-------------------------------------------------
419  public static void SendPhysicsMessage( Collider collider, string message, SendMessageOptions sendMessageOptions )
420  {
421  Rigidbody rb = collider.attachedRigidbody;
422  if ( rb && rb.gameObject != collider.gameObject )
423  {
424  rb.SendMessage( message, sendMessageOptions );
425  }
426 
427  collider.SendMessage( message, sendMessageOptions );
428  }
429 
430 
431  //-------------------------------------------------
432  public static void SendPhysicsMessage( Collider collider, string message, object arg, SendMessageOptions sendMessageOptions )
433  {
434  Rigidbody rb = collider.attachedRigidbody;
435  if ( rb && rb.gameObject != collider.gameObject )
436  {
437  rb.SendMessage( message, arg, sendMessageOptions );
438  }
439 
440  collider.SendMessage( message, arg, sendMessageOptions );
441  }
442 
443 
444  //-------------------------------------------------
445  public static void IgnoreCollisions( GameObject goA, GameObject goB )
446  {
447  Collider[] goA_colliders = goA.GetComponentsInChildren<Collider>();
448  Collider[] goB_colliders = goB.GetComponentsInChildren<Collider>();
449 
450  if ( goA_colliders.Length == 0 || goB_colliders.Length == 0 )
451  {
452  return;
453  }
454 
455  foreach ( Collider cA in goA_colliders )
456  {
457  foreach ( Collider cB in goB_colliders )
458  {
459  if ( cA.enabled && cB.enabled )
460  {
461  Physics.IgnoreCollision( cA, cB, true );
462  }
463  }
464  }
465  }
466 
467 
468  //-------------------------------------------------
469  public static IEnumerator WrapCoroutine( IEnumerator coroutine, System.Action onCoroutineFinished )
470  {
471  while ( coroutine.MoveNext() )
472  {
473  yield return coroutine.Current;
474  }
475 
476  onCoroutineFinished();
477  }
478 
479 
480  //-------------------------------------------------
481  public static Color ColorWithAlpha( this Color color, float alpha )
482  {
483  color.a = alpha;
484  return color;
485  }
486 
487  //-------------------------------------------------
488  // Exits the application if running standalone, or stops playback if running in the editor.
489  //-------------------------------------------------
490  public static void Quit()
491  {
492 #if UNITY_EDITOR
493  UnityEditor.EditorApplication.isPlaying = false;
494 #else
495  // NOTE: The recommended call for exiting a Unity app is UnityEngine.Application.Quit(), but as
496  // of 5.1.0f3 this was causing the application to crash. The following works without crashing:
497  System.Diagnostics.Process.GetCurrentProcess().Kill();
498 #endif
499  }
500 
501  //-------------------------------------------------
502  // Truncate floats to the specified # of decimal places when you want easier-to-read numbers without clamping to an int
503  //-------------------------------------------------
504  public static decimal FloatToDecimal( float value, int decimalPlaces = 2 )
505  {
506  return Math.Round( (decimal)value, decimalPlaces );
507  }
508 
509 
510  //-------------------------------------------------
511  public static T Median<T>( this IEnumerable<T> source )
512  {
513  if ( source == null )
514  {
515  throw new ArgumentException( "Argument cannot be null.", "source" );
516  }
517 
518  int count = source.Count();
519  if ( count == 0 )
520  {
521  throw new InvalidOperationException( "Enumerable must contain at least one element." );
522  }
523 
524  return source.OrderBy( x => x ).ElementAt( count / 2 );
525  }
526 
527 
528  //-------------------------------------------------
529  public static void ForEach<T>( this IEnumerable<T> source, Action<T> action )
530  {
531  if ( source == null )
532  {
533  throw new ArgumentException( "Argument cannot be null.", "source" );
534  }
535 
536  foreach ( T value in source )
537  {
538  action( value );
539  }
540  }
541 
542 
543  //-------------------------------------------------
544  // In some cases Unity/C# don't correctly interpret the newline control character (\n).
545  // This function replaces every instance of "\\n" with the actual newline control character.
546  //-------------------------------------------------
547  public static string FixupNewlines( string text )
548  {
549  bool newLinesRemaining = true;
550 
551  while ( newLinesRemaining )
552  {
553  int CIndex = text.IndexOf( "\\n" );
554 
555  if ( CIndex == -1 )
556  {
557  newLinesRemaining = false;
558  }
559  else
560  {
561  text = text.Remove( CIndex - 1, 3 );
562  text = text.Insert( CIndex - 1, "\n" );
563  }
564  }
565 
566  return text;
567  }
568 
569 
570  //-------------------------------------------------
571 #if ( UNITY_5_4 )
572  public static float PathLength( NavMeshPath path )
573 #else
574  public static float PathLength( UnityEngine.AI.NavMeshPath path )
575 #endif
576  {
577  if ( path.corners.Length < 2 )
578  return 0;
579 
580  Vector3 previousCorner = path.corners[0];
581  float lengthSoFar = 0.0f;
582  int i = 1;
583  while ( i < path.corners.Length )
584  {
585  Vector3 currentCorner = path.corners[i];
586  lengthSoFar += Vector3.Distance( previousCorner, currentCorner );
587  previousCorner = currentCorner;
588  i++;
589  }
590  return lengthSoFar;
591  }
592 
593 
594  //-------------------------------------------------
595  public static bool HasCommandLineArgument( string argumentName )
596  {
597  string[] args = System.Environment.GetCommandLineArgs();
598  for ( int i = 0; i < args.Length; i++ )
599  {
600  if ( args[i].Equals( argumentName ) )
601  {
602  return true;
603  }
604  }
605 
606  return false;
607  }
608 
609 
610  //-------------------------------------------------
611  public static int GetCommandLineArgValue( string argumentName, int nDefaultValue )
612  {
613  string[] args = System.Environment.GetCommandLineArgs();
614  for ( int i = 0; i < args.Length; i++ )
615  {
616  if ( args[i].Equals( argumentName ) )
617  {
618  if ( i == ( args.Length - 1 ) ) // Last arg, return default
619  {
620  return nDefaultValue;
621  }
622 
623  return System.Int32.Parse( args[i + 1] );
624  }
625  }
626 
627  return nDefaultValue;
628  }
629 
630 
631  //-------------------------------------------------
632  public static float GetCommandLineArgValue( string argumentName, float flDefaultValue )
633  {
634  string[] args = System.Environment.GetCommandLineArgs();
635  for ( int i = 0; i < args.Length; i++ )
636  {
637  if ( args[i].Equals( argumentName ) )
638  {
639  if ( i == ( args.Length - 1 ) ) // Last arg, return default
640  {
641  return flDefaultValue;
642  }
643 
644  return (float)Double.Parse( args[i + 1] );
645  }
646  }
647 
648  return flDefaultValue;
649  }
650 
651 
652  //-------------------------------------------------
653  public static void SetActive( GameObject gameObject, bool active )
654  {
655  if ( gameObject != null )
656  {
657  gameObject.SetActive( active );
658  }
659  }
660 
661 
662  //-------------------------------------------------
663  // The version of Path.Combine() included with Unity can only combine two paths.
664  // This version mimics the modern .NET version, which allows for any number of
665  // paths to be combined.
666  //-------------------------------------------------
667  public static string CombinePaths( params string[] paths )
668  {
669  if ( paths.Length == 0 )
670  {
671  return "";
672  }
673  else
674  {
675  string combinedPath = paths[0];
676  for ( int i = 1; i < paths.Length; i++ )
677  {
678  combinedPath = Path.Combine( combinedPath, paths[i] );
679  }
680 
681  return combinedPath;
682  }
683  }
684  }
685 
686 
687  //-------------------------------------------------------------------------
688  //Component used by the static AfterTimer function
689  //-------------------------------------------------------------------------
690  [System.Serializable]
691  public class AfterTimer_Component : MonoBehaviour
692  {
693  private System.Action callback;
694  private float triggerTime;
695  private bool timerActive = false;
696  private bool triggerOnEarlyDestroy = false;
697 
698  //-------------------------------------------------
699  public void Init( float _time, System.Action _callback, bool earlydestroy )
700  {
701  triggerTime = _time;
702  callback = _callback;
703  triggerOnEarlyDestroy = earlydestroy;
704  timerActive = true;
705  StartCoroutine( Wait() );
706  }
707 
708 
709  //-------------------------------------------------
710  private IEnumerator Wait()
711  {
712  yield return new WaitForSeconds( triggerTime );
713  timerActive = false;
714  callback.Invoke();
715  Destroy( this );
716  }
717 
718 
719  //-------------------------------------------------
720  void OnDestroy()
721  {
722  if ( timerActive )
723  {
724  //If the component or its GameObject get destroyed before the timer is complete, clean up
725  StopCoroutine( Wait() );
726  timerActive = false;
727 
728  if ( triggerOnEarlyDestroy )
729  {
730  callback.Invoke();
731  }
732  }
733  }
734  }
735 }