Tanoda
Utils.cs
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) Ultraleap, Inc. 2011-2020. *
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
9using UnityEngine;
10using UnityEngine.Assertions;
11using System;
12using System.IO;
13using System.Collections.Generic;
14using Leap.Unity.Query;
15
16namespace Leap.Unity {
17
18 public static class Utils {
19
20 #region C# Utilities
21
22 #region Generic Utils
23
28 public static void Swap<T>(ref T a, ref T b) {
29 T temp = a;
30 a = b;
31 b = temp;
32 }
33
37 public static void Swap<T>(this IList<T> list, int a, int b) {
38 T temp = list[a];
39 list[a] = list[b];
40 list[b] = temp;
41 }
42
46 public static void Swap<T>(this T[] array, int a, int b) {
47 Swap(ref array[a], ref array[b]);
48 }
49
55 public static T[] Reverse<T>(this T[] array) {
56 int mid = array.Length / 2;
57 int i = 0;
58 int j = array.Length;
59 while (i < mid) {
60 array.Swap(i++, --j);
61 }
62 return array;
63 }
64
69 public static void Reverse<T>(this T[] array, int start, int length) {
70 int mid = start + length / 2;
71 int i = start;
72 int j = start + length;
73 while (i < mid) {
74 array.Swap(i++, --j);
75 }
76 }
77
81 public static void Shuffle<T>(this IList<T> list) {
82 for (int i = 0; i < list.Count; i++) {
83 Utils.Swap(list, i, UnityEngine.Random.Range(i, list.Count));
84 }
85 }
86
87 public static void DoubleCapacity<T>(ref T[] array) {
88 T[] newArray = new T[array.Length * 2];
89 Array.Copy(array, newArray, array.Length);
90 array = newArray;
91 }
92
96 public static bool AreEqualUnordered<T>(IList<T> a, IList<T> b) {
97 var _count = Pool<Dictionary<T, int>>.Spawn();
98 try {
99 int _nullCount = 0;
100
101 foreach (var i in a) {
102 if (i == null) {
103 _nullCount++;
104 } else {
105 int count;
106 if (!_count.TryGetValue(i, out count)) {
107 count = 0;
108 }
109 _count[i] = count + 1;
110 }
111 }
112
113 foreach (var i in b) {
114 if (i == null) {
115 _nullCount--;
116 } else {
117 int count;
118 if (!_count.TryGetValue(i, out count)) {
119 return false;
120 }
121 _count[i] = count - 1;
122 }
123 }
124
125 if (_nullCount != 0) {
126 return false;
127 }
128
129 foreach (var pair in _count) {
130 if (pair.Value != 0) {
131 return false;
132 }
133 }
134
135 return true;
136 } finally {
137 _count.Clear();
138 Pool<Dictionary<T, int>>.Recycle(_count);
139 }
140 }
141
142 // http://stackoverflow.com/a/19317229/2471635
147 public static bool ImplementsInterface(this Type type, Type ifaceType) {
148 Type[] intf = type.GetInterfaces();
149 for (int i = 0; i < intf.Length; i++) {
150 if (intf[i] == ifaceType) {
151 return true;
152 }
153 }
154 return false;
155 }
156
157 public static bool IsActiveRelativeToParent(this Transform obj, Transform parent) {
158 Assert.IsTrue(obj.IsChildOf(parent));
159
160 if (!obj.gameObject.activeSelf) {
161 return false;
162 } else {
163 if (obj.parent == null || obj.parent == parent) {
164 return true;
165 } else {
166 return obj.parent.IsActiveRelativeToParent(parent);
167 }
168 }
169 }
170
176 public static List<int> GetSortedOrder<T>(this IList<T> list) where T : IComparable<T> {
177 Assert.IsNotNull(list);
178
179 List<int> ordering = new List<int>();
180 for (int i = 0; i < list.Count; i++) {
181 ordering.Add(i);
182 }
183
184 ordering.Sort((a, b) => list[a].CompareTo(list[b]));
185
186 return ordering;
187 }
188
193 public static void ApplyOrdering<T>(this IList<T> list, List<int> ordering) {
194 Assert.IsNotNull(list);
195 Assert.IsNotNull(ordering);
196 Assert.AreEqual(list.Count, ordering.Count, "List must be the same length as the ordering.");
197
198 List<T> copy = Pool<List<T>>.Spawn();
199 try {
200 copy.AddRange(list);
201 for (int i = 0; i < list.Count; i++) {
202 list[i] = copy[ordering[i]];
203 }
204 } finally {
205 copy.Clear();
206 Pool<List<T>>.Recycle(copy);
207 }
208 }
209
210 public static string MakeRelativePath(string relativeTo, string path) {
211 if (string.IsNullOrEmpty(relativeTo)) throw new ArgumentNullException("relativeTo");
212 if (string.IsNullOrEmpty(path)) throw new ArgumentNullException("path");
213
214 Uri relativeToUri = new Uri(relativeTo);
215 Uri pathUri = new Uri(path);
216
217 if (relativeToUri.Scheme != pathUri.Scheme) { return path; } // path can't be made relative.
218
219 Uri relativeUri = relativeToUri.MakeRelativeUri(pathUri);
220 string relativePath = Uri.UnescapeDataString(relativeUri.ToString());
221
222 if (pathUri.Scheme.Equals("file", StringComparison.InvariantCultureIgnoreCase)) {
223 relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
224 }
225
226 return relativePath;
227 }
228
233 public static T[] Require<T>(ref T[] arr) {
234 if (arr == null) {
235 var oldArr = arr;
236 arr = new T[0];
237 if (oldArr != null) {
238 for (var i = 0; i < oldArr.Length && i < arr.Length; i++) {
239 arr[i] = oldArr[i];
240 }
241 }
242 }
243 return arr;
244 }
245
248 public static T[] Require<T>(ref T[] arr, int length, Func<int, T> createAtIdx) {
249 if (arr == null || arr.Length != length) {
250 arr = new T[length];
251 for (var i = 0; i < length; i++) {
252 arr[i] = createAtIdx(i);
253 }
254 }
255 return arr;
256 }
257
267 public static T[] Require<T>(ref T[] arr, int length) {
268 if (arr == null || arr.Length != length) {
269 var oldArr = arr;
270 arr = new T[length];
271 if (oldArr != null) {
272 for (var i = 0; i < oldArr.Length && i < arr.Length; i++) {
273 arr[i] = oldArr[i];
274 }
275 }
276 }
277 return arr;
278 }
279
290 public static T[] Require<T>(ref T[] arr, T item0) {
291 Require(ref arr, 1);
292 arr[0] = item0;
293 return arr;
294 }
295
306 public static T[] Require<T>(ref T[] arr, T item0, T item1) {
307 Require(ref arr, 2);
308 arr[0] = item0; arr[1] = item1;
309 return arr;
310 }
311
322 public static T[] Require<T>(ref T[] arr, T item0, T item1, T item2, T item3) {
323 Require(ref arr, 4);
324 arr[0] = item0; arr[1] = item1; arr[2] = item2; arr[3] = item3;
325 return arr;
326 }
327
338 public static T[] Require<T>(ref T[] arr, T item0, T item1, T item2, T item3,
339 T item4)
340 {
341 Require(ref arr, 5);
342 arr[0] = item0; arr[1] = item1; arr[2] = item2; arr[3] = item3;
343 arr[4] = item4;
344 return arr;
345 }
346
350 public static List<T> RequireLen<T>(ref List<T> list, int length) {
351 list = Require(ref list);
352 list.Clear();
353 while (list.Count < length) { list.Add(default(T)); }
354 return list;
355 }
356
363 public static List<T> Require<T>(ref List<T> list, T item0) {
364 list = Require(ref list);
365 list.Clear();
366 list[0] = item0;
367 return list;
368 }
369
376 public static List<T> Require<T>(ref List<T> list, T item0, T item1) {
377 list = Require(ref list);
378 list.Clear();
379 list.Add(item0);
380 list.Add(item1);
381 return list;
382 }
383
387 public static List<T> Require<T>(ref List<T> list, T item, T[] items) {
388 list = Require(ref list);
389 list.Clear();
390 list.Add(item);
391 list.AddRange(items);
392 return list;
393 }
394
403 public static T Require<T>(ref T t, Func<T> makeValidT) where T : class {
404 if (t == default(T)) {
405 t = makeValidT();
406 }
407 return t;
408 }
409
418 public static T? Require<T>(ref T? t, Func<T> makeValidT) where T : struct {
419 if (t == null) {
420 t = makeValidT();
421 }
422 return t;
423 }
424
433 public static T Require<T>(ref T t) where T : class, new() {
434 if (t == default(T)) {
435 t = new T();
436 }
437 return t;
438 }
439
442 public static T OrIfNull<T>(this T t, T otherwise) where T : class {
443 if (t == null) { return otherwise; }
444 return t;
445 }
446
447 #endregion
448
449 #region String Utils
450
457 public static string TrimEnd(this string str, int characters) {
458 return str.Substring(0, Mathf.Max(0, str.Length - characters));
459 }
460
467 public static string TrimStart(this string str, int characters) {
468 return str.Substring(Mathf.Min(str.Length, characters));
469 }
470
476 public static string Capitalize(this string str) {
477 char c = str[0];
478 if (char.IsLetter(c)) {
479 return char.ToUpper(c) + str.Substring(1);
480 } else {
481 return str;
482 }
483 }
484
485 public static string[] GetNamePieces(string value) {
486 var niceName = GenerateNiceName(value).ToLower();
487 return niceName.Split(new char[] {' '});
488 }
489
503 public static string GenerateNiceName(string value) {
504 string result = "";
505 string curr = "";
506
507 Func<char, bool> wordFunc = c => {
508 //Can't build any further if it's already capitalized
509 if (curr.Length > 0 && char.IsUpper(curr[0])) {
510 return false;
511 }
512
513 //Can't add non-letters to words
514 if (!char.IsLetter(c)) {
515 return false;
516 }
517
518 curr = c + curr;
519 return true;
520 };
521
522 Func<char, bool> acronymFunc = c => {
523 //Can't add non-letters to acronyms
524 if (!char.IsLetter(c)) {
525 return false;
526 }
527
528 //Can't add lowercase letters to acronyms
529 if (char.IsLower(c)) {
530 return false;
531 }
532
533 curr = c + curr;
534 return true;
535 };
536
537 Func<char, bool> numberFunc = c => {
538 //Can't add non-digits to a number
539 if (!char.IsDigit(c)) {
540 return false;
541 }
542
543 curr = c + curr;
544 return true;
545 };
546
547 Func<char, bool> fluffFunc = c => {
548 //Can't add digits or numbers to 'fluff'
549 if (char.IsDigit(c) || char.IsLetter(c)) {
550 return false;
551 }
552
553 return true;
554 };
555
556
557 Func<char, bool> currFunc = null;
558 int currIndex = value.Length;
559
560 while (currIndex != 0) {
561 currIndex--;
562 char c = value[currIndex];
563
564 if (currFunc != null && currFunc(c)) {
565 continue;
566 }
567
568 if (curr != "") {
569 result = " " + curr.Capitalize() + result;
570 curr = "";
571 }
572
573 if (acronymFunc(c)) {
574 currFunc = acronymFunc;
575 } else if (wordFunc(c)) {
576 currFunc = wordFunc;
577 } else if (numberFunc(c)) {
578 currFunc = numberFunc;
579 } else if (fluffFunc(c)) {
580 currFunc = fluffFunc;
581 } else {
582 throw new Exception("Unexpected state, no function matched character " + c);
583 }
584 }
585
586 if (curr != "") {
587 result = curr.Capitalize() + result;
588 }
589
590 result = result.Trim();
591
592 if (result.StartsWith("M ") || result.StartsWith("K ")) {
593 result = result.Substring(2);
594 }
595
596 return result.Trim();
597 }
598
599 public static int Count(this string str, char toCount) {
600 int count = 0;
601 foreach (var c in str) {
602 if (c == toCount) { count++; }
603 }
604 return count;
605 }
606
607 #endregion
608
609 #region Print Utils
610
615 public static string ToArrayString<T>(this IEnumerable<T> enumerable,
616 System.Func<T, string> toStringFunc = null)
617 {
618 var str = "[" + typeof(T).Name + ": ";
619 bool addedFirstElement = false;
620 foreach (var t in enumerable) {
621 if (addedFirstElement) {
622 str += ", ";
623 }
624 if (toStringFunc != null) {
625 if (t == null) { str += "<null>"; }
626 else { str += toStringFunc(t); }
627 }
628 else {
629 if (t == null) { str += "<null>"; }
630 else { str += t.ToString(); }
631 }
632
633 addedFirstElement = true;
634 }
635 str += "]";
636
637 return str;
638 }
639
641 public static string ToCodeArrayString(this IEnumerable<Vector3> vectors, string language = null) {
642 var sb = new System.Text.StringBuilder();
643
644 bool csharp = false, python = false;
645 if (language == null || language.Equals("csharp")) { csharp = true; }
646 if (language.Equals("python")) { python = true; }
647
648 var arrPrefix = "";
649 if (csharp) { arrPrefix = "new Vector3[] { \n"; }
650 else if (python) { arrPrefix = "np.array([ \n"; }
651 sb.Append(arrPrefix);
652
653 var elPrefix = "";
654 if (csharp) { elPrefix = " new Vector3("; }
655 else if (python) { elPrefix = " ["; }
656
657 Func<float, string> f2s = f => f.ToString("R") + "";
658 if (csharp) { f2s = f => f.ToString("R") + "f"; }
659 else if (python) { f2s = f => (f * 1000).ToString("R") + ""; }
660
661 var compSep = ", ";
662 if (csharp) { compSep = ", "; }
663 else if (python) { compSep = ", "; }
664
665 var elPostfix = "";
666 if (csharp) { elPostfix = "),\n"; }
667 else if (python) { elPostfix = "],\n"; }
668
669 foreach (var v in vectors) {
670 sb.Append(elPrefix);
671 sb.Append(f2s(v[0]) + compSep);
672 sb.Append(f2s(v[1]) + compSep);
673 sb.Append(f2s(v[2]));
674 sb.Append(elPostfix);
675 }
676
677 if (csharp) {
678 sb.Length -= 2;
679 sb.Append("\n};");
680 } else if (python) {
681 sb.Length -= 2;
682 sb.Append("\n])");
683 }
684
685 return sb.ToString();
686 }
687
688 public static string ToCodeArrayString(this IEnumerable<Quaternion> quats) {
689 var sb = new System.Text.StringBuilder();
690 sb.Append("new Quaternion[] { \n");
691 foreach (var q in quats) {
692 sb.Append(" new Quaternion(");
693 sb.Append(q[0].ToString("R") + "f, ");
694 sb.Append(q[1].ToString("R") + "f, ");
695 sb.Append(q[2].ToString("R") + "f, ");
696 sb.Append(q[3].ToString("R") + "f),\n");
697 }
698 sb.Length -= 2;
699 sb.Append("\n};");
700
701 return sb.ToString();
702 }
703
704 #endregion
705
706 #region Math Utils
707
708 public static class Math {
710 public const float PHI = 1.61803398875f;
711 }
712
713 public static int Repeat(int x, int m) {
714 int r = x % m;
715 return r < 0 ? r + m : r;
716 }
717
718 public static int Sign(int value) {
719 if (value == 0) {
720 return 0;
721 } else if (value > 0) {
722 return 1;
723 } else {
724 return -1;
725 }
726 }
727
733 public static Vector2 Perpendicular(this Vector2 vector) {
734 return new Vector2(vector.y, -vector.x);
735 }
736
743 public static Vector3 Perpendicular(this Vector3 vector) {
744 float x2 = vector.x * vector.x;
745 float y2 = vector.y * vector.y;
746 float z2 = vector.z * vector.z;
747
748 float mag0 = z2 + x2;
749 float mag1 = y2 + x2;
750 float mag2 = z2 + y2;
751
752 if (mag0 > mag1) {
753 if (mag0 > mag2) {
754 return new Vector3(-vector.z, 0, vector.x);
755 } else {
756 return new Vector3(0, vector.z, -vector.y);
757 }
758 } else {
759 if (mag1 > mag2) {
760 return new Vector3(vector.y, -vector.x, 0);
761 } else {
762 return new Vector3(0, vector.z, -vector.y);
763 }
764 }
765 }
766
767 public static bool ContainsNaN(this Vector3 v) {
768 return float.IsNaN(v.x)
769 || float.IsNaN(v.y)
770 || float.IsNaN(v.z);
771 }
772
773 public static bool IsBetween(this float f, float f0, float f1) {
774 if (f0 > f1) Utils.Swap(ref f0, ref f1);
775
776 return f0 <= f && f <= f1;
777 }
778
779 public static bool IsBetween(this double d, double d0, double d1) {
780 if (d0 > d1) Utils.Swap(ref d0, ref d1);
781
782 return d0 <= d && d <= d1;
783 }
784
788 public static Vector3 TimedExtrapolate(Vector3 a, float aTime,
789 Vector3 b, float bTime,
790 float extrapolatedTime) {
791 return Vector3.LerpUnclamped(a, b, extrapolatedTime.MapUnclamped(aTime, bTime, 0f, 1f));
792 }
793
797 public static Quaternion TimedExtrapolate(Quaternion a, float aTime,
798 Quaternion b, float bTime,
799 float extrapolatedTime) {
800 return Quaternion.SlerpUnclamped(a, b, extrapolatedTime.MapUnclamped(aTime, bTime, 0f, 1f));
801 }
802
807 public static bool NextTuple(IList<int> tuple, int maxValue) {
808 return NextTuple(tuple, i => (i + 1) % maxValue);
809 }
810
822 public static bool NextTuple<T>(IList<T> tuple, Func<T, T> nextItem) where T : IComparable<T> {
823 int index = tuple.Count - 1;
824 while (index >= 0) {
825 T value = tuple[index];
826 T newValue = nextItem(value);
827 tuple[index] = newValue;
828
829 if (newValue.CompareTo(value) > 0) {
830 return true;
831 }
832
833 index--;
834 }
835
836 return false;
837 }
838
839 #endregion
840
841 #region Array Utils
842
846 public static T[] ClearWithDefaults<T>(this T[] arr) {
847 for (int i = 0; i < arr.Length; i++) {
848 arr[i] = default(T);
849 }
850 return arr;
851 }
852
856 public static T[] ClearWith<T>(this T[] arr, T value) {
857 for (int i = 0; i < arr.Length; i++) {
858 arr[i] = value;
859 }
860 return arr;
861 }
862
865 public static void ForEach<T>(this T[] arr, Action<T> doFunc)
866 where T : class
867 {
868 foreach (var t in arr) {
869 if (t != null) { doFunc(t); }
870 }
871 }
872
876 public static void ForEach<T>(this object[] arr, Action<T> doFunc)
877 where T : class
878 {
879 foreach (var obj in arr) {
880 var t = obj as T;
881 if (t != null) { doFunc(t); }
882 }
883 }
884
887 public static void Transform<T>(this T[] arr, Func<T, T> mapFunc) {
888 for (var i = 0; i < arr.Length; i++) {
889 arr[i] = mapFunc(arr[i]);
890 }
891 }
892
895 public static bool ContainsValue<T>(this T[] arr, T value) where T: IEquatable<T> {
896 for (var i = 0; i < arr.Length; i++) {
897 if (arr[i].Equals(value)) { return true; }
898 }
899 return false;
900 }
901
903 public static bool Contains<T>(this T[] arr, T value) {
904 return System.Array.IndexOf(arr, value) != -1;
905 }
906
907 #endregion
908
909 #region List Utils
910
911 public static void EnsureListExists<T>(ref List<T> list) {
912 if (list == null) {
913 list = new List<T>();
914 }
915 }
916
917 public static void EnsureListCount<T>(this List<T> list, int count) {
918 if (list.Count == count) return;
919
920 while (list.Count < count) {
921 list.Add(default(T));
922 }
923
924 while (list.Count > count) {
925 list.RemoveAt(list.Count - 1);
926 }
927 }
928
929 public static void EnsureListCount<T>(this List<T> list, int count, Func<T> createT, Action<T> deleteT = null) {
930 while (list.Count < count) {
931 list.Add(createT());
932 }
933
934 while (list.Count > count) {
935 T tempT = list[list.Count - 1];
936 list.RemoveAt(list.Count - 1);
937
938 if (deleteT != null) {
939 deleteT(tempT);
940 }
941 }
942 }
943
947 public static void Add<T>(this List<T> list, T t0, T t1) {
948 list.Add(t0);
949 list.Add(t1);
950 }
951
955 public static void Add<T>(this List<T> list, T t0, T t1, T t2) {
956 list.Add(t0);
957 list.Add(t1);
958 list.Add(t2);
959 }
960
964 public static void Add<T>(this List<T> list, T t0, T t1, T t2, T t3) {
965 list.Add(t0);
966 list.Add(t1);
967 list.Add(t2);
968 list.Add(t3);
969 }
970
973 public static void ForEach<T>(this List<T> list, Func<T, T> applyFunc) {
974 for (var i = 0; i < list.Count; i++) {
975 list[i] = applyFunc(list[i]);
976 }
977 }
978
982 public static List<T> Cleared<T>(this List<T> list) {
983 list.Clear(); return list;
984 }
985
991 public static List<T> CopyFrom<T>(this List<T> dst, List<T> src,
992 System.Action<T, T> copyElementFunc, bool dontClear = false)
993 where T: class, new()
994 {
995 if (!dontClear) { dst.Clear(); }
996 if (src == null) { return dst; }
997 foreach (var srcT in src) {
998 var dstT = new T();
999 copyElementFunc(srcT, dstT);
1000 dst.Add(dstT);
1001 }
1002 return dst;
1003 }
1004
1005 #endregion
1006
1007 #region Nullable Utils
1008
1012 public static T UnwrapOr<T>(this T? nullable, T defaultValue)
1013 where T : struct
1014 {
1015 if (nullable.HasValue) {
1016 return nullable.Value;
1017 }
1018 return defaultValue;
1019 }
1020
1021 #endregion
1022
1023 #endregion
1024
1025 #region Unity Utilities
1026
1027 #region Unity Object Utils
1028
1030 public static bool IsObjectPartOfPrefabAsset(UnityEngine.Object obj) {
1031 #if UNITY_EDITOR
1032 #if UNITY_2018_3_OR_NEWER
1033 // Exclude objects that are not part of any prefab, and exclude prefab _instances_.
1034 return UnityEditor.PrefabUtility.IsPartOfAnyPrefab(obj) &&
1035 UnityEditor.PrefabUtility.GetPrefabInstanceStatus(obj) == UnityEditor.PrefabInstanceStatus.NotAPrefab;
1036 #else
1037 // Before 2018.3, use GetPrefabType.
1038 return UnityEditor.PrefabUtility.GetPrefabType(obj) == UnityEditor.PrefabType.Prefab;
1039 #endif
1040 #else
1041 return false;
1042 #endif
1043 }
1044
1052 public static T FindObjectInHierarchy<T>() where T : UnityEngine.Object {
1053 return Resources.FindObjectsOfTypeAll<T>().Query()
1054 .Where(o => {
1055#if UNITY_EDITOR
1056 // Exclude prefab assets found by the Resources scan.
1057 if (IsObjectPartOfPrefabAsset(o)) { return false; }
1058#endif
1059 return true;
1060 })
1061 .FirstOrDefault();
1062 }
1063
1064 #endregion
1065
1066 #region Transform Utils
1067
1071 public static ChildrenEnumerator GetChildren(this Transform t) {
1072 return new ChildrenEnumerator(t);
1073 }
1074
1075 public struct ChildrenEnumerator : IEnumerator<Transform> {
1076 private Transform _t;
1077 private int _idx;
1078 private int _count;
1079
1080 public ChildrenEnumerator(Transform t) {
1081 _t = t;
1082 _idx = -1;
1083 _count = t.childCount;
1084 }
1085
1086 public ChildrenEnumerator GetEnumerator() { return this; }
1087
1088 public bool MoveNext() {
1089 if (_idx < _count) _idx += 1;
1090 if (_idx == _count) { return false; } else { return true; }
1091 }
1092 public Transform Current {
1093 get { return _t == null ? null : _t.GetChild(_idx); }
1094 }
1095 object System.Collections.IEnumerator.Current { get { return Current; } }
1096 public void Reset() {
1097 _idx = -1;
1098 _count = _t.childCount;
1099 }
1100 public void Dispose() { }
1101 }
1102
1103 public static List<Transform> GetSelfAndAllChildren(this Transform t,
1104 bool breadthFirst = false)
1105 {
1106 var transforms = new List<Transform>();
1107 transforms.Add(t);
1108 GetAllChildren(t, transforms, breadthFirst);
1109 return transforms;
1110 }
1111
1118 public static void GetAllChildren(this Transform t, List<Transform> toFill,
1119 bool breadthFirst = false) {
1120 if (breadthFirst) {
1121 var cursor = t; var cursorIdx = toFill.Count; var endIdx = cursorIdx;
1122 do {
1123 endIdx += addImmediateChildren(cursor, toFill);
1124 cursorIdx += 1;
1125 if (cursorIdx >= endIdx) break;
1126 cursor = toFill[cursorIdx];
1127 } while (true);
1128 }
1129 else {
1130 addChildrenRecursive(t, toFill);
1131 }
1132 }
1133 private static void addChildrenRecursive(Transform t, List<Transform> list) {
1134 if (t == null) { return; }
1135 foreach (var child in t.GetChildren()) {
1136 list.Add(child);
1137 addChildrenRecursive(child, list);
1138 }
1139 }
1140 private static int addImmediateChildren(Transform t, List<Transform> list) {
1141 int numChildren = 0;
1142 foreach (var child in t.GetChildren()) {
1143 list.Add(child); numChildren++;
1144 }
1145 return numChildren;
1146 }
1147
1149 public static Transform FindChild(this Transform t, string[] possibleNames,
1150 bool caseSensitive = true)
1151 {
1152 foreach (var name in possibleNames) {
1153 var found = FindChild(t, name, caseSensitive);
1154 if (found != null) { return found; }
1155 }
1156 return null;
1157 }
1158
1160 public static Transform FindChild(this Transform t, string withName,
1161 bool caseSensitive = true)
1162 {
1163 var children = Utils.Require(ref _b_findChildBuffer);
1164 children.Clear();
1165 t.GetAllChildren(children);
1166 if (!caseSensitive) { withName = withName.ToLower(); }
1167 foreach (var child in children) {
1168 var name = child.name;
1169 if (!caseSensitive) { name = name.ToLower(); }
1170 if (child.name.Contains(withName)) { return child; }
1171 }
1172 return null;
1173 }
1174 private static List<Transform> _b_findChildBuffer = new List<Transform>();
1175
1180 public static void ResetLocalTransform(this Transform t) {
1181 t.localPosition = Vector3.zero;
1182 t.localRotation = Quaternion.identity;
1183 t.localScale = Vector3.one;
1184 }
1185
1190 public static void ResetLocalPose(this Transform t) {
1191 t.localPosition = Vector3.zero;
1192 t.localRotation = Quaternion.identity;
1193 }
1194
1200 public static Vector3 GetClosestAxisDirection(this Transform t, Vector3 toDir) {
1201 return t.rotation.GetClosestAxisDirection(toDir);
1202 }
1203
1210 public static void SetMatrix(this Transform t, Matrix4x4 targetMatrix,
1211 bool allowAtEditTime = false)
1212 {
1213 var pose = targetMatrix.GetPose();
1214 var scale = targetMatrix.lossyScale;
1215
1216 if (!Application.isPlaying && !allowAtEditTime) {
1217 throw new System.Exception("Transform.SetMatrix extension was called " +
1218 "at edit-time without `allowAtEditTime` set. Because attempting " +
1219 "to set the matrix of a Transform is a non-guaranteed operation and " +
1220 "would modify the Transform data at edit-time, you must opt-in to this " +
1221 "behavior (at your own risk).");
1222 }
1223 else {
1224 t.SetPose(pose);
1225 if (t.parent != null) { scale = scale.CompDiv(t.parent.lossyScale); }
1226 t.localScale = scale;
1227 }
1228 }
1229
1231 public static Matrix4x4 LocalFromWorld(this Transform t) {
1232 return t.worldToLocalMatrix;
1233 }
1234
1236 public static Matrix4x4 WorldFromLocal(this Transform t) {
1237 return t.localToWorldMatrix;
1238 }
1239
1242 public static void SetLossyScale(this Transform t, Vector3 lossyScale) {
1243 var scale = lossyScale;
1244 if (t.parent != null) { scale = scale.CompDiv(t.parent.lossyScale); }
1245 t.localScale = scale;
1246 }
1247
1250 public static float Distance(Transform t0, Transform t1) {
1251 return Vector3.Distance(t0.position, t1.position);
1252 }
1253
1254 #endregion
1255
1256 #region Component Utils
1257
1287 public static void FindOwnedChildComponents<ComponentType, OwnerType>
1288 (OwnerType rootObj,
1289 List<ComponentType> ownedComponents,
1290 bool includeInactiveObjects = false)
1291 where OwnerType : Component {
1292 ownedComponents.Clear();
1293 Stack<Transform> toVisit = Pool<Stack<Transform>>.Spawn();
1294 List<ComponentType> componentsBuffer = Pool<List<ComponentType>>.Spawn();
1295
1296 try {
1297 toVisit.Push(rootObj.transform);
1298 Transform curTransform;
1299 while (toVisit.Count > 0) {
1300 curTransform = toVisit.Pop();
1301
1302 // Recursively search children and children's children.
1303 foreach (var child in curTransform.GetChildren()) {
1304 // Ignore children with OwnerType components of their own; its own OwnerType
1305 // component owns its own ComponentType components and the ComponentType
1306 // components of its children.
1307 if (child.GetComponent<OwnerType>() == null
1308 && (includeInactiveObjects || child.gameObject.activeInHierarchy)) {
1309 toVisit.Push(child);
1310 }
1311 }
1312
1313 // Since we'll visit every valid child, all we need to do is add the
1314 // ComponentType components of every transform we visit.
1315 componentsBuffer.Clear();
1316 curTransform.GetComponents<ComponentType>(componentsBuffer);
1317 foreach (var component in componentsBuffer) {
1318 ownedComponents.Add(component);
1319 }
1320 }
1321 } finally {
1322 toVisit.Clear();
1323 Pool<Stack<Transform>>.Recycle(toVisit);
1324
1325 componentsBuffer.Clear();
1326 Pool<List<ComponentType>>.Recycle(componentsBuffer);
1327 }
1328 }
1329
1330 #endregion
1331
1332 #region Orientation Utils
1333
1344 public static void LookAwayFrom(this Transform thisTransform, Transform transform) {
1345 thisTransform.rotation = Quaternion.LookRotation(thisTransform.position - transform.position, Vector3.up);
1346 }
1347
1356 public static void LookAwayFrom(this Transform thisTransform,
1357 Transform transform, Vector3 upwards)
1358 {
1359 thisTransform.rotation = Quaternion.LookRotation(thisTransform.position - transform.position, upwards);
1360 }
1361
1367 public static Vector3 GetClosestAxisDirection(this Quaternion q,
1368 Vector3 toDir)
1369 {
1370 var localDir = (Quaternion.Inverse(q) * toDir).normalized;
1371 var closestAxis = Vector3.right;
1372 var largestDot = -1f;
1373 for (var sign = 1; sign >= -1; sign -= 2) {
1374 for (var axis = 0; axis < 3; axis++) {
1375 var testAxis = Vector3.zero; testAxis[axis] = 1f * sign;
1376 var testDot = Vector3.Dot(localDir, testAxis);
1377 if (testDot > largestDot) {
1378 largestDot = testDot;
1379 closestAxis = testAxis;
1380 }
1381 }
1382 }
1383 return (q * closestAxis).normalized;
1384 }
1385
1391 public static Vector3 GetClosestLocalAxisDirection(Vector3 toLocalDir)
1392 {
1393 var closestAxis = Vector3.right;
1394 var largestDot = -1f;
1395 for (var sign = 1; sign >= -1; sign -= 2) {
1396 for (var axis = 0; axis < 3; axis++) {
1397 var testAxis = Vector3.zero; testAxis[axis] = 1f * sign;
1398 var testDot = Vector3.Dot(toLocalDir, testAxis);
1399 if (testDot > largestDot) {
1400 largestDot = testDot;
1401 closestAxis = testAxis;
1402 }
1403 }
1404 }
1405 return closestAxis.normalized;
1406 }
1407
1408 #endregion
1409
1410 #region Vector3 Utils
1411
1417 public static Vector3 ToVector3(this Vector4 v4) {
1418 return new Vector3(v4.x, v4.y, v4.z);
1419 }
1420
1425 public static Vector3 InLocalSpace(this Vector3 v, Transform t) {
1426 return t.InverseTransformPoint(v);
1427 }
1428
1432 public static Vector4 WithW(this Vector3 v, float w) {
1433 return new Vector4(v.x, v.y, v.z, w);
1434 }
1435
1438 public static Vector3 Pivot(this Vector3 point, Vector3 pivot,
1439 Quaternion rotation)
1440 {
1441 var pointFromPivot = point - pivot;
1442 var rotatedPointFromPivot = rotation * pointFromPivot;
1443 return pivot + rotatedPointFromPivot;
1444 }
1445
1453 public static Quaternion GetAxisFromToRotation(this Vector3 v, Vector3 toDir,
1454 Vector3 axis, out float angle, float? minAngle = null,
1455 float? maxAngle = null)
1456 {
1457 v = Vector3.ProjectOnPlane(v, axis);
1458 var toDir_axis = Vector3.ProjectOnPlane(toDir, axis);
1459 angle = Vector3.SignedAngle(v, toDir_axis, axis);
1460 if (minAngle != null) { angle = Mathf.Max(minAngle.Value, angle); }
1461 if (maxAngle != null) { angle = Mathf.Min(maxAngle.Value, angle); }
1462 var rotation = Quaternion.AngleAxis(angle, axis);
1463 return rotation;
1464 }
1465
1470 public static Quaternion GetAxisFromToRotation(this Vector3 v, Vector3 toDir,
1471 Vector3 axis, float? minAngle = null, float? maxAngle = null)
1472 {
1473 var unusedAngle = 0f;
1474 return GetAxisFromToRotation(v, toDir, axis, out unusedAngle, minAngle,
1475 maxAngle);
1476 }
1477
1478 public static Vector3 GetCentroid(
1479 System.Action<List<Vector3>> fillPoints)
1480 {
1481 var points = Pool<List<Vector3>>.Spawn().Cleared();
1482 fillPoints(points);
1483 if (points.Count == 0) { return default(Vector3); }
1484
1485 var centroid = Vector3.zero;
1486 for (var i = 0; i < points.Count; i++) {
1487 centroid += points[i];
1488 }
1489 centroid /= points.Count;
1490
1491 return centroid;
1492 }
1493
1494 public static Vector3 GetCentroid(Vector3[] points)
1495 {
1496 var centroid = Vector3.zero;
1497 for (var i = 0; i < points.Length; i++) {
1498 centroid += points[i];
1499 }
1500 centroid /= points.Length;
1501
1502 return centroid;
1503 }
1504
1505 public static Vector3 ConstrainToNormal(this Vector3 direction,
1506 Vector3 normalDirection, float maxAngle)
1507 {
1508 if (maxAngle <= 0f) return normalDirection.normalized * direction.magnitude;
1509 if (maxAngle >= 180f) return direction;
1510 float angle = Mathf.Acos(Mathf.Clamp(
1511 Vector3.Dot(direction.normalized, normalDirection.normalized),
1512 -1f, 1f)) * Mathf.Rad2Deg;
1513 return Vector3.Slerp(direction.normalized, normalDirection.normalized,
1514 (angle - maxAngle) / angle) * direction.magnitude;
1515 }
1516
1517 #endregion
1518
1519 #region Quaternion Utils
1520
1521 public static bool ContainsNaN(this Quaternion q) {
1522 return float.IsNaN(q.x)
1523 || float.IsNaN(q.y)
1524 || float.IsNaN(q.z)
1525 || float.IsNaN(q.w);
1526 }
1527
1534 public static Vector3 ToAngleAxisVector(this Quaternion q) {
1535 float angle;
1536 Vector3 axis;
1537 q.ToAngleAxis(out angle, out axis);
1538 return axis * angle;
1539 }
1540
1545 public static Quaternion QuaternionFromAngleAxisVector(Vector3 angleAxisVector) {
1546 if (angleAxisVector == Vector3.zero) return Quaternion.identity;
1547 return Quaternion.AngleAxis(angleAxisVector.magnitude, angleAxisVector);
1548 }
1549
1555 public static Quaternion ToNormalized(this Quaternion quaternion) {
1556 float x = quaternion.x, y = quaternion.y, z = quaternion.z, w = quaternion.w;
1557 float magnitude = Mathf.Sqrt(x * x + y * y + z * z + w * w);
1558
1559 if (Mathf.Approximately(magnitude, 0f)) {
1560 return Quaternion.identity;
1561 }
1562
1563 return new Quaternion(x / magnitude, y / magnitude, z / magnitude, w / magnitude);
1564 }
1565
1575 public static Quaternion FaceTargetWithoutTwist(Vector3 fromPosition,
1576 Vector3 targetPosition,
1577 bool flip180 = false) {
1578 return FaceTargetWithoutTwist(fromPosition, targetPosition, Vector3.up, flip180);
1579 }
1580
1589 public static Quaternion FaceTargetWithoutTwist(Vector3 fromPosition,
1590 Vector3 targetPosition,
1591 Vector3 upwardDirection,
1592 bool flip180 = false) {
1593 Vector3 objToTarget = targetPosition - fromPosition;
1594 return Quaternion.LookRotation((flip180 ? -1 : 1) * objToTarget,
1595 upwardDirection);
1596 }
1597
1599 public static Quaternion Flipped(this Quaternion q) {
1600 return new Quaternion(-q.x, -q.y, -q.z, -q.w);
1601 }
1602
1604 public static Quaternion MirroredX(this Quaternion q) {
1605 return new Quaternion(-q.x, q.y, q.z, -q.w);
1606 }
1607
1609 public static Quaternion MirroredY(this Quaternion q) {
1610 return new Quaternion(q.x, -q.y, q.z, -q.w);
1611 }
1612
1614 public static Quaternion MirroredZ(this Quaternion q) {
1615 return new Quaternion(q.x, q.y, -q.z, -q.w);
1616 }
1617
1618 #region Compression
1619
1634 public static void CompressQuatToBytes(Quaternion quat,
1635 byte[] buffer,
1636 ref int offset) {
1637 int largest = 0;
1638 float a, b, c;
1639
1640 float abs_w = Mathf.Abs(quat.w);
1641 float abs_x = Mathf.Abs(quat.x);
1642 float abs_y = Mathf.Abs(quat.y);
1643 float abs_z = Mathf.Abs(quat.z);
1644
1645 float largest_value = abs_x;
1646
1647 if (abs_y > largest_value) {
1648 largest = 1;
1649 largest_value = abs_y;
1650 }
1651 if (abs_z > largest_value) {
1652 largest = 2;
1653 largest_value = abs_z;
1654 }
1655 if (abs_w > largest_value) {
1656 largest = 3;
1657 largest_value = abs_w;
1658 }
1659 if (quat[largest] >= 0f) {
1660 a = quat[(largest + 1) % 4];
1661 b = quat[(largest + 2) % 4];
1662 c = quat[(largest + 3) % 4];
1663 }
1664 else {
1665 a = -quat[(largest + 1) % 4];
1666 b = -quat[(largest + 2) % 4];
1667 c = -quat[(largest + 3) % 4];
1668 }
1669
1670 // serialize
1671 const float minimum = -1.0f / 1.414214f; // note: 1.0f / sqrt(2)
1672 const float maximum = +1.0f / 1.414214f;
1673 const float delta = maximum - minimum;
1674 const uint maxIntegerValue = (1 << 10) - 1; // 10 bits
1675 const float maxIntegerValueF = (float)maxIntegerValue;
1676 float normalizedValue;
1677 uint integerValue;
1678
1679 uint sentData = ((uint)largest) << 30;
1680 // a
1681 normalizedValue = Mathf.Clamp01((a - minimum) / delta);
1682 integerValue = (uint)Mathf.Floor(normalizedValue * maxIntegerValueF + 0.5f);
1683 sentData = sentData | ((integerValue & maxIntegerValue) << 20);
1684 // b
1685 normalizedValue = Mathf.Clamp01((b - minimum) / delta);
1686 integerValue = (uint)Mathf.Floor(normalizedValue * maxIntegerValueF + 0.5f);
1687 sentData = sentData | ((integerValue & maxIntegerValue) << 10);
1688 // c
1689 normalizedValue = Mathf.Clamp01((c - minimum) / delta);
1690 integerValue = (uint)Mathf.Floor(normalizedValue * maxIntegerValueF + 0.5f);
1691 sentData = sentData | (integerValue & maxIntegerValue);
1692
1693 BitConverterNonAlloc.GetBytes(sentData, buffer, ref offset);
1694 }
1695
1709 public static Quaternion DecompressBytesToQuat(byte[] bytes, ref int offset) {
1710 uint readData = BitConverterNonAlloc.ToUInt32(bytes, ref offset);
1711
1712 int largest = (int)(readData >> 30);
1713 float a, b, c;
1714
1715 const float minimum = -1.0f / 1.414214f; // note: 1.0f / sqrt(2)
1716 const float maximum = +1.0f / 1.414214f;
1717 const float delta = maximum - minimum;
1718 const uint maxIntegerValue = (1 << 10) - 1; // 10 bits
1719 const float maxIntegerValueF = (float)maxIntegerValue;
1720 uint integerValue;
1721 float normalizedValue;
1722 // a
1723 integerValue = (readData >> 20) & maxIntegerValue;
1724 normalizedValue = (float)integerValue / maxIntegerValueF;
1725 a = (normalizedValue * delta) + minimum;
1726 // b
1727 integerValue = (readData >> 10) & maxIntegerValue;
1728 normalizedValue = (float)integerValue / maxIntegerValueF;
1729 b = (normalizedValue * delta) + minimum;
1730 // c
1731 integerValue = readData & maxIntegerValue;
1732 normalizedValue = (float)integerValue / maxIntegerValueF;
1733 c = (normalizedValue * delta) + minimum;
1734
1735 Quaternion value = Quaternion.identity;
1736 float d = Mathf.Sqrt(1f - a * a - b * b - c * c);
1737 value[largest] = d;
1738 value[(largest + 1) % 4] = a;
1739 value[(largest + 2) % 4] = b;
1740 value[(largest + 3) % 4] = c;
1741
1742 return value;
1743 }
1744
1745 #endregion
1746
1747 #endregion
1748
1749 #region Matrix4x4 Utils
1750
1751 public static Matrix4x4 CompMul(Matrix4x4 m, float f) {
1752#if UNITY_2017_1_OR_NEWER
1753 return new Matrix4x4(m.GetColumn(0) * f,
1754 m.GetColumn(1) * f,
1755 m.GetColumn(2) * f,
1756 m.GetColumn(3) * f);
1757#else
1758 Matrix4x4 toReturn = m;
1759 for (int i = 0; i < 4; i++) {
1760 toReturn.SetColumn(i, toReturn.GetColumn(i) * f);
1761 }
1762 return toReturn;
1763#endif
1764
1765 }
1766
1767 public static Vector3 GetTranslation(this Matrix4x4 m) {
1768 return m.GetColumn(3);
1769 }
1770
1771 public static Vector3 GetVector3(this Matrix4x4 m) {
1772 return m.GetColumn(3);
1773 }
1774
1775 public static Quaternion GetQuaternion_LookRot(this Matrix4x4 m) {
1776 if (m.GetColumn(2) == m.GetColumn(1)) { return Quaternion.identity; }
1777 return Quaternion.LookRotation(m.GetColumn(2), m.GetColumn(1));
1778 }
1779
1780 public static Quaternion GetQuaternion_CopySign(this Matrix4x4 m) {
1781 // quaternion.w = sqrt( max( 0, 1 + m00 + m11 + m22 ) ) / 2;
1782 // quaternion.x = sqrt( max( 0, 1 + m00 - m11 - m22 ) ) / 2;
1783 // quaternion.y = sqrt( max( 0, 1 - m00 + m11 - m22 ) ) / 2;
1784 // quaternion.z = sqrt( max( 0, 1 - m00 - m11 + m22 ) ) / 2;
1785 // Q.x = _copysign( Q.x, m21 - m12 )
1786 // Q.y = _copysign( Q.y, m02 - m20 )
1787 // Q.z = _copysign( Q.z, m10 - m01 )
1788 var q = new Quaternion();
1789
1790 q.w = Mathf.Sqrt( Mathf.Max( 0, 1 + m.m00 + m.m11 + m.m22 ) ) / 2;
1791
1792 q.x = Mathf.Sqrt( Mathf.Max( 0, 1 + m.m00 - m.m11 - m.m22 ) ) / 2;
1793 q.y = Mathf.Sqrt( Mathf.Max( 0, 1 - m.m00 + m.m11 - m.m22 ) ) / 2;
1794 q.z = Mathf.Sqrt( Mathf.Max( 0, 1 - m.m00 - m.m11 + m.m22 ) ) / 2;
1795 q.x = copySign(q.x, m.m21 - m.m12);
1796 q.y = copySign(q.y, m.m02 - m.m20);
1797 q.z = copySign(q.z, m.m10 - m.m01);
1798 // X Y Z //no
1799 // X Z Y //no
1800 // Y Z X //no
1801 // Y X Z //no
1802 // Z X Y //no
1803 // Z Y X //no
1804 //q = Quaternion.Lerp(q, Quaternion.identity, 0f); // Safety normalize.
1805 return q;
1806 }
1807
1810 private static float copySign(float toValue, float signSource) {
1811 if (signSource == 0f) { throw new System.InvalidOperationException(
1812 "signSource of zero is not supported in copySign."); }
1813 return Mathf.Abs(toValue) * Mathf.Sign(signSource);
1814 }
1815
1818 public static Quaternion GetQuaternion_Manual(this Matrix4x4 m) {
1819 // float trace = a[0][0] + a[1][1] + a[2][2]; // I removed + 1.0f; see discussion with Ethan
1820 // if( trace > 0 ) {// I changed M_EPSILON to 0
1821 // float s = 0.5f / sqrtf(trace+ 1.0f);
1822 // q.w = 0.25f / s;
1823 // q.x = ( a[2][1] - a[1][2] ) * s;
1824 // q.y = ( a[0][2] - a[2][0] ) * s;
1825 // q.z = ( a[1][0] - a[0][1] ) * s;
1826 // } else {
1827 // if ( a[0][0] > a[1][1] && a[0][0] > a[2][2] ) {
1828 // float s = 2.0f * sqrtf( 1.0f + a[0][0] - a[1][1] - a[2][2]);
1829 // q.w = (a[2][1] - a[1][2] ) / s;
1830 // q.x = 0.25f * s;
1831 // q.y = (a[0][1] + a[1][0] ) / s;
1832 // q.z = (a[0][2] + a[2][0] ) / s;
1833 // } else if (a[1][1] > a[2][2]) {
1834 // float s = 2.0f * sqrtf( 1.0f + a[1][1] - a[0][0] - a[2][2]);
1835 // q.w = (a[0][2] - a[2][0] ) / s;
1836 // q.x = (a[0][1] + a[1][0] ) / s;
1837 // q.y = 0.25f * s;
1838 // q.z = (a[1][2] + a[2][1] ) / s;
1839 // } else {
1840 // float s = 2.0f * sqrtf( 1.0f + a[2][2] - a[0][0] - a[1][1] );
1841 // q.w = (a[1][0] - a[0][1] ) / s;
1842 // q.x = (a[0][2] + a[2][0] ) / s;
1843 // q.y = (a[1][2] + a[2][1] ) / s;
1844 // q.z = 0.25f * s;
1845 // }
1846 // }
1847 var q = new Quaternion();
1848 var trace = m.m00 + m.m11 + m.m22;
1849 if (trace > 0) {
1850 var s = 0.5f / Mathf.Sqrt(trace + 1.0f);
1851 q.w = 0.25f / s;
1852 q.x = (m.m21 - m.m12) * s;
1853 q.y = (m.m02 - m.m20) * s;
1854 q.z = (m.m10 - m.m01) * s;
1855 }
1856 else {
1857 if (m.m00 > m.m11 && m.m00 > m.m22) {
1858 var s = 2.0f * Mathf.Sqrt(1f + m.m00 - m.m11 - m.m22);
1859 q.w = (m.m21 - m.m12) / s;
1860 q.x = 0.25f * s;
1861 q.y = (m.m01 + m.m10) / s;
1862 q.z = (m.m02 + m.m20) / s;
1863 }
1864 else if (m.m11 > m.m22) {
1865 var s = 2.0f * Mathf.Sqrt(1f + m.m11 - m.m00 - m.m22);
1866 q.w = (m.m02 - m.m20) / s;
1867 q.x = (m.m01 + m.m10) / s;
1868 q.y = 0.25f * s;
1869 q.z = (m.m12 + m.m21) / s;
1870 }
1871 else {
1872 var s = 2.0f * Mathf.Sqrt(1f + m.m22 - m.m00 - m.m11);
1873 q.w = (m.m10 - m.m01) / s;
1874 q.x = (m.m02 + m.m20) / s;
1875 q.y = (m.m12 + m.m21) / s;
1876 q.z = 0.25f * s;
1877 }
1878 }
1879 return q;
1880 }
1881
1882 // Found here: https://github.com/lordofduct/space.../master/SpacepuppyBase/Utils/TransformUtil.cs
1883 public static Quaternion GetQuaternion_SpacePuppy(this Matrix4x4 m) {
1884 // Adapted from: http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
1885 Quaternion q = new Quaternion();
1886 q.w = Mathf.Sqrt(Mathf.Max(0, 1 + m[0,0] + m[1,1] + m[2,2])) / 2;
1887 q.x = Mathf.Sqrt(Mathf.Max(0, 1 + m[0,0] - m[1,1] - m[2,2])) / 2;
1888 q.y = Mathf.Sqrt(Mathf.Max(0, 1 - m[0,0] + m[1,1] - m[2,2])) / 2;
1889 q.z = Mathf.Sqrt(Mathf.Max(0, 1 - m[0,0] - m[1,1] + m[2,2])) / 2;
1890 q.x *= Mathf.Sign(q.x *(m[2,1] - m[1,2]));
1891 q.y *= Mathf.Sign(q.y *(m[0,2] - m[2,0]));
1892 q.z *= Mathf.Sign(q.z *(m[1,0] - m[0,1]));
1893 return q;
1894 }
1895
1901 public static Quaternion GetQuaternion(this Matrix4x4 m) {
1902 var forward = m.MultiplyVector(Vector3.forward);
1903 var up = m.MultiplyVector(Vector3.up);
1904 if (forward == Vector3.zero || up == Vector3.zero) {
1905 return Quaternion.identity;
1906 }
1907 return Quaternion.LookRotation(forward, up);
1908 }
1909
1910 public static void FillMatrixFromQuaternion(this Quaternion q,
1911 ref Vector3[] matrix) {
1912 matrix[0] = q * Vector3.right;
1913 matrix[1] = q * Vector3.up;
1914 matrix[2] = q * Vector3.forward;
1915 }
1916
1917
1923 public static Vector3 GetClosestAxisDirection(this Matrix4x4 m, Vector3 toDir) {
1924 var localDir = (m.inverse.MultiplyVector(toDir)).normalized;
1925 var closestAxis = Vector3.right;
1926 var largestDot = -1f;
1927 for (var sign = 1; sign >= -1; sign -= 2) {
1928 for (var axis = 0; axis < 2; axis++) {
1929 var testAxis = Vector3.zero; testAxis[axis] = 1f * sign;
1930 var testDot = Vector3.Dot(localDir, testAxis);
1931 if (testDot > largestDot) {
1932 largestDot = testDot;
1933 closestAxis = testAxis;
1934 }
1935 }
1936 }
1937 return (m.MultiplyVector(closestAxis)).normalized;
1938 }
1939
1941 public static Vector3 GetPosition(this Matrix4x4 m) {
1942 return m.MultiplyPoint3x4(Vector3.zero);
1943 }
1944
1945 public static Vector3 GetRight(this Matrix4x4 m) {
1946 return m.MultiplyVector(Vector3.right);
1947 }
1948
1949 public static Vector3 GetUp(this Matrix4x4 m) {
1950 return m.MultiplyVector(Vector3.up);
1951 }
1952
1953 public static Vector3 GetForward(this Matrix4x4 m) {
1954 return m.MultiplyVector(Vector3.forward);
1955 }
1956
1957 public static Vector3 GetAxis(this Matrix4x4 m, int i) {
1958 if (i == 0) { return m.GetRight(); }
1959 if (i == 1) { return m.GetUp(); }
1960 if (i == 2) { return m.GetForward(); }
1961 throw new System.InvalidOperationException("Invalid axis index " + i);
1962 }
1963
1970 public static Matrix4x4 Pivot(this Matrix4x4 m, Quaternion q) {
1971 var origPos = m.GetPosition();
1972 var toTranslateBack = Matrix4x4.Rotate(q) * m;
1973 var newPos = toTranslateBack.GetPosition();
1974 var translatedBack = Matrix4x4.Translate(origPos - newPos) * toTranslateBack;
1975 return translatedBack;
1976 }
1977
1982 public static Matrix4x4 Pivot(this Matrix4x4 m, Quaternion q, Vector3 p) {
1983 // m is worldFromRoot
1984 // preservePos = worldFromRoot * MultiplyPoint3x4(p == wristFromRoot.inverse)
1985 var preservePos = p;
1986 var preservePos_local = m.inverse.MultiplyPoint3x4(p);
1987 var toTranslateBack = Matrix4x4.Rotate(q) * m;
1988 var newPos = toTranslateBack.MultiplyPoint3x4(preservePos_local);
1989 var translatedBack = Matrix4x4.Translate(preservePos - newPos) *
1990 toTranslateBack;
1991 return translatedBack;
1992 }
1993
1996 public static Matrix4x4 PivotTo(this Matrix4x4 m, Quaternion q) {
1997 var origPos = m.GetPosition();
1998 var origRot = m.GetQuaternion();
1999 var toTranslateBack = Matrix4x4.Rotate(q * Quaternion.Inverse(origRot)) *
2000 m;
2001 var newPos = toTranslateBack.GetPosition();
2002 var translatedBack = Matrix4x4.Translate(origPos - newPos) * toTranslateBack;
2003 return translatedBack;
2004 }
2005
2006 #endregion
2007
2008 #region Physics Utils
2009
2017 public static void IgnoreCollisions(GameObject first, GameObject second,
2018 bool ignore = true) {
2019 if (first == null || second == null)
2020 return;
2021
2022 var firstColliders = Pool<List<Collider>>.Spawn(); firstColliders.Clear();
2023 var secondColliders = Pool<List<Collider>>.Spawn(); secondColliders.Clear();
2024 try {
2025 first.GetComponentsInChildren(firstColliders);
2026 second.GetComponentsInChildren(secondColliders);
2027
2028 for (int i = 0; i < firstColliders.Count; ++i) {
2029 for (int j = 0; j < secondColliders.Count; ++j) {
2030 if (firstColliders[i] != secondColliders[j] &&
2031 firstColliders[i].enabled && secondColliders[j].enabled) {
2032 Physics.IgnoreCollision(firstColliders[i], secondColliders[j], ignore);
2033 }
2034 }
2035 }
2036 }
2037 finally {
2038 firstColliders.Clear(); Pool<List<Collider>>.Recycle(firstColliders);
2039 secondColliders.Clear(); Pool<List<Collider>>.Recycle(secondColliders);
2040 }
2041 }
2042
2043 #endregion
2044
2045 #region Collider Utils
2046
2047 #region Capsule Collider Utils
2048
2049 public static Vector3 GetDirection(this CapsuleCollider capsule) {
2050 switch (capsule.direction) {
2051 case 0: return Vector3.right;
2052 case 1: return Vector3.up;
2053 case 2: default: return Vector3.forward;
2054 }
2055 }
2056
2057 public static float GetEffectiveRadius(this CapsuleCollider capsule) {
2058 return capsule.radius * capsule.GetEffectiveRadiusMultiplier();
2059 }
2060
2061 public static float GetEffectiveRadiusMultiplier(this CapsuleCollider capsule) {
2062 var effRadiusMult = 0f;
2063 switch (capsule.direction) {
2064 case 0:
2065 effRadiusMult = Swizzle.Swizzle.yz(capsule.transform.lossyScale).CompMax();
2066 break;
2067 case 1:
2068 effRadiusMult = Swizzle.Swizzle.xz(capsule.transform.lossyScale).CompMax();
2069 break;
2070 case 2:
2071 default:
2072 effRadiusMult = Swizzle.Swizzle.xy(capsule.transform.lossyScale).CompMax();
2073 break;
2074 }
2075 return effRadiusMult;
2076 }
2077
2078 public static void GetCapsulePoints(this CapsuleCollider capsule, out Vector3 a,
2079 out Vector3 b) {
2080 var effRadiusMult = capsule.GetEffectiveRadiusMultiplier();
2081 var capsuleDir = capsule.GetDirection();
2082
2083 a = capsuleDir * (capsule.height / 2f);
2084 b = -a;
2085
2086 a = capsule.transform.TransformPoint(a);
2087 b = capsule.transform.TransformPoint(b);
2088
2089 a -= capsuleDir * effRadiusMult * capsule.radius;
2090 b += capsuleDir * effRadiusMult * capsule.radius;
2091 }
2092
2097 public static void SetCapsulePoints(this CapsuleCollider capsule, Vector3 a, Vector3 b) {
2098 capsule.center = Vector3.zero;
2099
2100 capsule.transform.position = (a + b) / 2F;
2101
2102 Vector3 capsuleDirection = capsule.GetDirection();
2103
2104 Vector3 capsuleDirWorldSpace = capsule.transform.TransformDirection(capsuleDirection);
2105 Quaternion necessaryRotation = Quaternion.FromToRotation(capsuleDirWorldSpace, a - capsule.transform.position);
2106 capsule.transform.rotation = necessaryRotation * capsule.transform.rotation;
2107
2108 Vector3 aCapsuleSpace = capsule.transform.InverseTransformPoint(a);
2109 float capsuleSpaceDistToA = aCapsuleSpace.magnitude;
2110 capsule.height = (capsuleSpaceDistToA + capsule.radius) * 2;
2111 }
2112
2113 #endregion
2114
2126 public static void FindColliders<T>(GameObject obj, List<T> colliders,
2127 bool includeInactiveObjects = false)
2128 where T : Collider {
2129 colliders.Clear();
2130 Stack<Transform> toVisit = Pool<Stack<Transform>>.Spawn();
2131 List<T> collidersBuffer = Pool<List<T>>.Spawn();
2132
2133 try {
2134 // Traverse the hierarchy of this object's transform to find
2135 // all of its Colliders.
2136 toVisit.Push(obj.transform);
2137 Transform curTransform;
2138 while (toVisit.Count > 0) {
2139 curTransform = toVisit.Pop();
2140
2141 // Recursively search children and children's children
2142 foreach (var child in curTransform.GetChildren()) {
2143 // Ignore children with Rigidbodies of their own; its own Rigidbody
2144 // owns its own colliders and the colliders of its children
2145 if (child.GetComponent<Rigidbody>() == null
2146 && (includeInactiveObjects || child.gameObject.activeSelf)) {
2147 toVisit.Push(child);
2148 }
2149 }
2150
2151 // Since we'll visit every valid child, all we need to do is add the colliders
2152 // of every transform we visit.
2153 collidersBuffer.Clear();
2154 curTransform.GetComponents<T>(collidersBuffer);
2155 foreach (var collider in collidersBuffer) {
2156 colliders.Add(collider);
2157 }
2158 }
2159 } finally {
2160 toVisit.Clear();
2161 Pool<Stack<Transform>>.Recycle(toVisit);
2162
2163 collidersBuffer.Clear();
2164 Pool<List<T>>.Recycle(collidersBuffer);
2165 }
2166 }
2167
2168 #endregion
2169
2170 #region Color Utils
2171
2172 public static Color WithAlpha(this Color color, float alpha) {
2173 return new Color(color.r, color.g, color.b, alpha);
2174 }
2175
2180 public static Color ParseHtmlColorString(string htmlString) {
2181 Color color;
2182 if (!ColorUtility.TryParseHtmlString(htmlString, out color)) {
2183 throw new ArgumentException("The string [" + htmlString + "] is not a valid color code. Valid color codes include:\n" +
2184 "#RGB\n" +
2185 "#RGBA\n" +
2186 "#RRGGBB\n" +
2187 "#RRGGBBAA\n" +
2188 "For more information, see the documentation for ColorUtility.TryParseHtmlString.");
2189 }
2190
2191 return color;
2192 }
2193
2198 public static Color LerpHSV(this Color color, Color towardsColor, float t) {
2199 float h0, s0, v0;
2200 Color.RGBToHSV(color, out h0, out s0, out v0);
2201
2202 float h1, s1, v1;
2203 Color.RGBToHSV(towardsColor, out h1, out s1, out v1);
2204
2205 // Cyclically lerp hue. (Input hues are always between 0 and 1.)
2206 if (h0 - h1 < -0.5f) h0 += 1f;
2207 if (h0 - h1 > 0.5f) h1 += 1f;
2208 float hL = Mathf.Lerp(h0, h1, t) % 1f;
2209
2210 float sL = Mathf.Lerp(s0, s1, t);
2211 float vL = Mathf.Lerp(v0, v1, t);
2212 return Color.HSVToRGB(hL, sL, vL);
2213 }
2214
2218 public static float LerpHue(float h0, float h1, float t) {
2219 // Enforce hue values between 0f and 1f.
2220 if (h0 < 0f) h0 = 1f - (-h0 % 1f);
2221 if (h1 < 0f) h1 = 1f - (-h1 % 1f);
2222 if (h0 > 1f) h0 = h0 % 1f;
2223 if (h1 > 1f) h1 = h1 % 1f;
2224
2225 if (h0 - h1 < -0.5f) h0 += 1f;
2226 if (h0 - h1 > 0.5f) h1 += 1f;
2227 return Mathf.Lerp(h0, h1, t) % 1f;
2228 }
2229
2232 public static Vector3 HSVToRGB(Vector3 hsv) {
2233 var c = Color.HSVToRGB(hsv.x, hsv.y, hsv.z);
2234 return new Vector3(c.r, c.g, c.b);
2235 }
2236
2237 #endregion
2238
2239 #region Gizmo Utils
2240
2241 public static void DrawCircle(Vector3 center,
2242 Vector3 normal,
2243 float radius,
2244 Color color,
2245 int quality = 32,
2246 float duration = 0,
2247 bool depthTest = true) {
2248 Vector3 planeA = Vector3.Slerp(normal, -normal, 0.5f);
2249 DrawArc(360, center, planeA, normal, radius, color, quality);
2250 }
2251
2252 /* Adapted from: Zarrax (http://math.stackexchange.com/users/3035/zarrax), Parametric Equation of a Circle in 3D Space?,
2253 * URL (version: 2014-09-09): http://math.stackexchange.com/q/73242 */
2254 public static void DrawArc(float arc,
2255 Vector3 center,
2256 Vector3 forward,
2257 Vector3 normal,
2258 float radius,
2259 Color color,
2260 int quality = 32) {
2261
2262 Gizmos.color = color;
2263 Vector3 right = Vector3.Cross(normal, forward).normalized;
2264 float deltaAngle = arc / quality;
2265 Vector3 thisPoint = center + forward * radius;
2266 Vector3 nextPoint = new Vector3();
2267 for (float angle = 0; Mathf.Abs(angle) <= Mathf.Abs(arc); angle += deltaAngle) {
2268 float cosAngle = Mathf.Cos(angle * Constants.DEG_TO_RAD);
2269 float sinAngle = Mathf.Sin(angle * Constants.DEG_TO_RAD);
2270 nextPoint.x = center.x + radius * (cosAngle * forward.x + sinAngle * right.x);
2271 nextPoint.y = center.y + radius * (cosAngle * forward.y + sinAngle * right.y);
2272 nextPoint.z = center.z + radius * (cosAngle * forward.z + sinAngle * right.z);
2273 Gizmos.DrawLine(thisPoint, nextPoint);
2274 thisPoint = nextPoint;
2275 }
2276 }
2277
2278 public static void DrawCone(Vector3 origin,
2279 Vector3 direction,
2280 float angle,
2281 float height,
2282 Color color,
2283 int quality = 4,
2284 float duration = 0,
2285 bool depthTest = true) {
2286
2287 float step = height / quality;
2288 for (float q = step; q <= height; q += step) {
2289 DrawCircle(origin + direction * q, direction, Mathf.Tan(angle * Constants.DEG_TO_RAD) * q, color, quality * 8, duration, depthTest);
2290 }
2291 }
2292
2293 #endregion
2294
2295 #region Texture Utils
2296
2297 private static TextureFormat[] _incompressibleFormats = new TextureFormat[] {
2298 TextureFormat.R16,
2299 TextureFormat.EAC_R,
2300 TextureFormat.EAC_R_SIGNED,
2301 TextureFormat.EAC_RG,
2302 TextureFormat.EAC_RG_SIGNED
2303 #if !UNITY_2018_2_OR_NEWER
2304 ,
2305 TextureFormat.ETC_RGB4_3DS,
2306 TextureFormat.ETC_RGBA8_3DS
2307 #endif
2308 };
2309
2313 public static bool IsCompressible(TextureFormat format) {
2314 if (format < 0) {
2315 return false;
2316 }
2317
2318 return Array.IndexOf(_incompressibleFormats, format) < 0;
2319 }
2320
2321 #endregion
2322
2323 #region Rect Utils
2324
2328 public static float Area(this Rect rect) {
2329 return rect.width * rect.height;
2330 }
2331
2336 public static Rect Extrude(this Rect r, float margin) {
2337 return new Rect(r.x - margin, r.y - margin,
2338 r.width + (margin * 2f), r.height + (margin * 2f));
2339 }
2340
2345 public static Rect PadInner(this Rect r, float padding) {
2346 return PadInner(r, padding, padding, padding, padding);
2347 }
2348
2355 public static Rect PadInner(this Rect r, float padTop, float padBottom,
2356 float padLeft, float padRight) {
2357 var x = r.x + padLeft;
2358 var y = r.y + padBottom;
2359 var w = r.width - padRight - padLeft;
2360 var h = r.height - padTop - padBottom;
2361 if (w < 0f) {
2362 x = r.x + (padLeft / (padLeft + padRight)) * r.width;
2363 w = 0;
2364 }
2365 if (h < 0f) {
2366 y = r.y + (padBottom / (padBottom + padTop)) * r.height;
2367 h = 0;
2368 }
2369 return new Rect(x, y, w, h);
2370 }
2371
2372 #region Pad, No Out
2373
2374 public static Rect PadTop(this Rect r, float padding) {
2375 return PadInner(r, padding, 0f, 0f, 0f);
2376 }
2377
2378 public static Rect PadBottom(this Rect r, float padding) {
2379 return PadInner(r, 0f, padding, 0f, 0f);
2380 }
2381
2382 public static Rect PadLeft(this Rect r, float padding) {
2383 return PadInner(r, 0f, 0f, padding, 0f);
2384 }
2385
2386 public static Rect PadRight(this Rect r, float padding) {
2387 return PadInner(r, 0f, 0f, 0f, padding);
2388 }
2389
2390 #endregion
2391
2392 #region Pad, With Out
2393
2398 public static Rect PadTop(this Rect r, float padding, out Rect marginRect) {
2399 marginRect = r.TakeTop(padding);
2400 return PadTop(r, padding);
2401 }
2402
2407 public static Rect PadBottom(this Rect r, float padding, out Rect marginRect) {
2408 marginRect = r.TakeBottom(padding);
2409 return PadBottom(r, padding);
2410 }
2411
2416 public static Rect PadLeft(this Rect r, float padding, out Rect marginRect) {
2417 marginRect = r.TakeLeft(padding);
2418 return PadLeft(r, padding);
2419 }
2420
2425 public static Rect PadRight(this Rect r, float padding, out Rect marginRect) {
2426 marginRect = r.TakeRight(padding);
2427 return PadRight(r, padding);
2428 }
2429
2430 #endregion
2431
2432 #region Pad Percent, Two Sides
2433
2434 public static Rect PadTopBottomPercent(this Rect r, float padPercent) {
2435 float padHeight = r.height * padPercent;
2436 return r.PadInner(padHeight, padHeight, 0f, 0f);
2437 }
2438
2439 public static Rect PadLeftRightPercent(this Rect r, float padPercent) {
2440 float padWidth = r.width * padPercent;
2441 return r.PadInner(0f, 0f, padWidth, padWidth);
2442 }
2443
2444 #endregion
2445
2446 #region Pad Percent
2447
2448 public static Rect PadTopPercent(this Rect r, float padPercent) {
2449 float padHeight = r.height * padPercent;
2450 return PadTop(r, padHeight);
2451 }
2452
2453 public static Rect PadBottomPercent(this Rect r, float padPercent) {
2454 float padHeight = r.height * padPercent;
2455 return PadBottom(r, padHeight);
2456 }
2457
2458 public static Rect PadLeftPercent(this Rect r, float padPercent) {
2459 return PadLeft(r, r.width * padPercent);
2460 }
2461
2462 public static Rect PadRightPercent(this Rect r, float padPercent) {
2463 return PadRight(r, r.width * padPercent);
2464 }
2465
2466 #endregion
2467
2468 #region Take, No Out
2469
2474 public static Rect TakeTop(this Rect r, float heightFromTop) {
2475 heightFromTop = Mathf.Clamp(heightFromTop, 0f, r.height);
2476 return new Rect(r.x, r.y + r.height - heightFromTop, r.width, heightFromTop);
2477 }
2478
2483 public static Rect TakeBottom(this Rect r, float heightFromBottom) {
2484 heightFromBottom = Mathf.Clamp(heightFromBottom, 0f, r.height);
2485 return new Rect(r.x, r.y, r.width, heightFromBottom);
2486 }
2487
2492 public static Rect TakeLeft(this Rect r, float widthFromLeft) {
2493 widthFromLeft = Mathf.Clamp(widthFromLeft, 0f, r.width);
2494 return new Rect(r.x, r.y, widthFromLeft, r.height);
2495 }
2496
2501 public static Rect TakeRight(this Rect r, float widthFromRight) {
2502 widthFromRight = Mathf.Clamp(widthFromRight, 0f, r.width);
2503 return new Rect(r.x + r.width - widthFromRight, r.y, widthFromRight, r.height);
2504 }
2505
2506 #endregion
2507
2508 #region Take, With Out
2509
2514 public static Rect TakeTop(this Rect r, float padding, out Rect theRest) {
2515 theRest = r.PadTop(padding);
2516 return r.TakeTop(padding);
2517 }
2518
2523 public static Rect TakeBottom(this Rect r, float padding, out Rect theRest) {
2524 theRest = r.PadBottom(padding);
2525 return r.TakeBottom(padding);
2526 }
2527
2532 public static Rect TakeLeft(this Rect r, float padding, out Rect theRest) {
2533 theRest = r.PadLeft(padding);
2534 return r.TakeLeft(padding);
2535 }
2536
2541 public static Rect TakeRight(this Rect r, float padding, out Rect theRest) {
2542 theRest = r.PadRight(padding);
2543 return r.TakeRight(padding);
2544 }
2545
2546 #endregion
2547
2552 public static Rect TakeHorizontal(this Rect r, float lineHeight,
2553 out Rect theRest,
2554 bool fromTop = true) {
2555 theRest = new Rect(r.x, (fromTop ? r.y + lineHeight : r.y), r.width, r.height - lineHeight);
2556 return new Rect(r.x, (fromTop ? r.y : r.y + r.height - lineHeight), r.width, lineHeight);
2557 }
2558
2559 public static void SplitHorizontallyWithLeft(this Rect rect, out Rect left, out Rect right, float leftWidth) {
2560 left = rect;
2561 left.width = leftWidth;
2562 right = rect;
2563 right.x += left.width;
2564 right.width = rect.width - leftWidth;
2565 }
2566
2567 #region Enumerators
2568
2576 public static HorizontalLineRectEnumerator TakeAllLines(this Rect r, int numLines) {
2577 return new HorizontalLineRectEnumerator(r, numLines);
2578 }
2579
2581 Rect rect;
2584
2586 this.rect = rect;
2587 this.numLines = numLines;
2588 this.index = -1;
2589 }
2590
2591 public float eachHeight { get { return this.rect.height / numLines; } }
2592
2593 public Rect Current { get {
2594 return new Rect(rect.x, rect.y + eachHeight * index, rect.width,
2595 eachHeight);
2596 }}
2597 public bool MoveNext() {
2598 index += 1;
2599 return index < numLines;
2600 }
2602
2603 public void Reset() {
2604 index = -1;
2605 }
2606
2608 List<Rect> rects = Pool<List<Rect>>.Spawn();
2609 try {
2610
2611 foreach (var rect in this) {
2612 rects.Add(rect);
2613 }
2614 return new Query<Rect>(rects);
2615
2616 } finally {
2617 rects.Clear();
2618 Pool<List<Rect>>.Recycle(rects);
2619 }
2620 }
2621 }
2622
2623 #endregion
2624
2625 #endregion
2626
2627 #endregion
2628
2629 #region Leap Utilities
2630
2631 #region Pose Utils
2632
2637 public static Pose From(this Vector3 position, Pose fromPose) {
2638 return new Pose(position, fromPose.rotation).From(fromPose);
2639 }
2640
2641 public static Pose GetPose(this Rigidbody rigidbody) {
2642 return new Pose(rigidbody.position, rigidbody.rotation);
2643 }
2644
2648 public static Pose MirroredX(this Pose pose) {
2649 var v = pose.position;
2650 var q = pose.rotation;
2651 return new Pose(new Vector3(-v.x, v.y, v.z),
2652 new Quaternion(-q.x, q.y, q.z, -q.w).Flipped());
2653 }
2654
2658 public static Pose Negated(this Pose pose) {
2659 var v = pose.position;
2660 var q = pose.rotation;
2661 return new Pose(new Vector3(-v.x, -v.y, -v.z),
2662 new Quaternion(-q.z, -q.y, -q.z, q.w));
2663 }
2664
2666 public static Pose Pivot(this Pose p, Quaternion q) {
2667 var origPos = p.position;
2668 var toTranslateBack = q * p;
2669 var newPos = toTranslateBack.position;
2670 var translatedBack = new Pose(origPos - newPos) * toTranslateBack;
2671 return translatedBack;
2672 }
2673
2675 public static Pose Pivot(this Pose p, Quaternion q, Vector3 pivotPoint) {
2676 var preservePos = pivotPoint;
2677 var preservePos_local = p.inverse * pivotPoint;
2678 var toTranslateBack = q * p;
2679 var newPos = (toTranslateBack * preservePos_local).position;
2680 var translatedBack = new Pose(preservePos - newPos) * toTranslateBack;
2681 return translatedBack;
2682 }
2683
2685 public static Pose PivotTo(this Pose p, Quaternion q) {
2686 var origPos = p.position;
2687 var origRot = p.rotation;
2688 var toTranslateBack = new Pose(q * Quaternion.Inverse(origRot)) *
2689 p;
2690 var newPos = toTranslateBack.position;
2691 var translatedBack = new Pose(origPos - newPos) * toTranslateBack;
2692 return translatedBack;
2693 }
2694
2695 #endregion
2696
2697 #endregion
2698
2699 #region Value Mapping Utils ("Map")
2700
2705 public static float Map(this float value, float valueMin, float valueMax, float resultMin, float resultMax) {
2706 if (valueMin == valueMax) return resultMin;
2707 return Mathf.Lerp(resultMin, resultMax, ((value - valueMin) / (valueMax - valueMin)));
2708 }
2709
2714 public static float MapUnclamped(this float value, float valueMin, float valueMax, float resultMin, float resultMax) {
2715 if (valueMin == valueMax) return resultMin;
2716 return Mathf.LerpUnclamped(resultMin, resultMax, ((value - valueMin) / (valueMax - valueMin)));
2717 }
2718
2723 public static Vector2 Map(this Vector2 value, float valueMin, float valueMax, float resultMin, float resultMax) {
2724 return new Vector2(value.x.Map(valueMin, valueMax, resultMin, resultMax),
2725 value.y.Map(valueMin, valueMax, resultMin, resultMax));
2726 }
2727
2732 public static Vector2 MapUnclamped(this Vector2 value, float valueMin, float valueMax, float resultMin, float resultMax) {
2733 return new Vector2(value.x.MapUnclamped(valueMin, valueMax, resultMin, resultMax),
2734 value.y.MapUnclamped(valueMin, valueMax, resultMin, resultMax));
2735 }
2736
2741 public static Vector3 Map(this Vector3 value, float valueMin, float valueMax, float resultMin, float resultMax) {
2742 return new Vector3(value.x.Map(valueMin, valueMax, resultMin, resultMax),
2743 value.y.Map(valueMin, valueMax, resultMin, resultMax),
2744 value.z.Map(valueMin, valueMax, resultMin, resultMax));
2745 }
2746
2751 public static Vector3 MapUnclamped(this Vector3 value, float valueMin, float valueMax, float resultMin, float resultMax) {
2752 return new Vector3(value.x.MapUnclamped(valueMin, valueMax, resultMin, resultMax),
2753 value.y.MapUnclamped(valueMin, valueMax, resultMin, resultMax),
2754 value.z.MapUnclamped(valueMin, valueMax, resultMin, resultMax));
2755 }
2756
2761 public static Vector4 Map(this Vector4 value, float valueMin, float valueMax, float resultMin, float resultMax) {
2762 return new Vector4(value.x.Map(valueMin, valueMax, resultMin, resultMax),
2763 value.y.Map(valueMin, valueMax, resultMin, resultMax),
2764 value.z.Map(valueMin, valueMax, resultMin, resultMax),
2765 value.w.Map(valueMin, valueMax, resultMin, resultMax));
2766 }
2767
2772 public static Vector4 MapUnclamped(this Vector4 value, float valueMin, float valueMax, float resultMin, float resultMax) {
2773 return new Vector4(value.x.MapUnclamped(valueMin, valueMax, resultMin, resultMax),
2774 value.y.MapUnclamped(valueMin, valueMax, resultMin, resultMax),
2775 value.z.MapUnclamped(valueMin, valueMax, resultMin, resultMax),
2776 value.w.MapUnclamped(valueMin, valueMax, resultMin, resultMax));
2777 }
2778
2784 public static Vector2 Map(float input, float valueMin, float valueMax, Vector2 resultMin, Vector2 resultMax) {
2785 return Vector2.Lerp(resultMin, resultMax, Mathf.InverseLerp(valueMin, valueMax, input));
2786 }
2787
2793 public static Vector3 Map(float input, float valueMin, float valueMax, Vector3 resultMin, Vector3 resultMax) {
2794 return Vector3.Lerp(resultMin, resultMax, Mathf.InverseLerp(valueMin, valueMax, input));
2795 }
2796
2802 public static Vector4 Map(float input, float valueMin, float valueMax, Vector4 resultMin, Vector4 resultMax) {
2803 return Vector4.Lerp(resultMin, resultMax, Mathf.InverseLerp(valueMin, valueMax, input));
2804 }
2805
2810 public static Vector2 CompMul(this Vector2 A, Vector2 B) {
2811 return new Vector2(A.x * B.x, A.y * B.y);
2812 }
2813
2818 public static Vector3 CompMul(this Vector3 A, Vector3 B) {
2819 return new Vector3(A.x * B.x, A.y * B.y, A.z * B.z);
2820 }
2821
2826 public static Vector4 CompMul(this Vector4 A, Vector4 B) {
2827 return new Vector4(A.x * B.x, A.y * B.y, A.z * B.z, A.w * B.w);
2828 }
2829
2834 public static Vector2 CompDiv(this Vector2 A, Vector2 B) {
2835 return new Vector2(A.x / B.x, A.y / B.y);
2836 }
2837
2842 public static Vector3 CompDiv(this Vector3 A, Vector3 B) {
2843 return new Vector3(A.x / B.x, A.y / B.y, A.z / B.z);
2844 }
2845
2850 public static Vector4 CompDiv(this Vector4 A, Vector4 B) {
2851 return new Vector4(A.x / B.x, A.y / B.y, A.z / B.z, A.w / B.w);
2852 }
2853
2858 public static Vector2 CompAdd(this Vector2 A, Vector2 B) {
2859 return new Vector2(A.x + B.x, A.y + B.y);
2860 }
2861
2866 public static Vector3 CompAdd(this Vector3 A, Vector3 B) {
2867 return new Vector3(A.x + B.x, A.y + B.y, A.z + B.z);
2868 }
2869
2874 public static Vector4 CompAdd(this Vector4 A, Vector4 B) {
2875 return new Vector4(A.x + B.x, A.y + B.y, A.z + B.z, A.w + B.w);
2876 }
2877
2882 public static Vector2 CompSub(this Vector2 A, Vector2 B) {
2883 return new Vector2(A.x - B.x, A.y - B.y);
2884 }
2885
2890 public static Vector3 CompSub(this Vector3 A, Vector3 B) {
2891 return new Vector3(A.x - B.x, A.y - B.y, A.z - B.z);
2892 }
2893
2898 public static Vector4 CompSub(this Vector4 A, Vector4 B) {
2899 return new Vector4(A.x - B.x, A.y - B.y, A.z - B.z, A.w - B.w);
2900 }
2901
2905 public static float CompSum(this Vector2 v) {
2906 return v.x + v.y;
2907 }
2908
2912 public static float CompSum(this Vector3 v) {
2913 return v.x + v.y + v.z;
2914 }
2915
2919 public static float CompSum(this Vector4 v) {
2920 return v.x + v.y + v.z + v.w;
2921 }
2922
2926 public static float CompMax(this Vector2 v) {
2927 return Mathf.Max(v.x, v.y);
2928 }
2929
2933 public static float CompMax(this Vector3 v) {
2934 return Mathf.Max(Mathf.Max(v.x, v.y), v.z);
2935 }
2936
2940 public static float CompMax(this Vector4 v) {
2941 return Mathf.Max(Mathf.Max(Mathf.Max(v.x, v.y), v.z), v.w);
2942 }
2943
2947 public static float CompMin(this Vector2 v) {
2948 return Mathf.Min(v.x, v.y);
2949 }
2950
2954 public static float CompMin(this Vector3 v) {
2955 return Mathf.Min(Mathf.Min(v.x, v.y), v.z);
2956 }
2957
2961 public static float CompMin(this Vector4 v) {
2962 return Mathf.Min(Mathf.Min(Mathf.Min(v.x, v.y), v.z), v.w);
2963 }
2964
2968 public static Vector2 CompLerp(this Vector2 A, Vector2 B, Vector2 Ts) {
2969 return new Vector2(Mathf.Lerp(A.x, B.x, Ts.x), Mathf.Lerp(A.y, B.y, Ts.y));
2970 }
2971
2975 public static Vector3 CompLerp(this Vector3 A, Vector3 B, Vector3 Ts) {
2976 return new Vector3(Mathf.Lerp(A.x, B.x, Ts.x), Mathf.Lerp(A.y, B.y, Ts.y),
2977 Mathf.Lerp(A.z, B.z, Ts.z));
2978 }
2979
2983 public static Vector4 CompLerp(this Vector4 A, Vector4 B, Vector4 Ts) {
2984 return new Vector4(Mathf.Lerp(A.x, B.x, Ts.x), Mathf.Lerp(A.y, B.y, Ts.y),
2985 Mathf.Lerp(A.z, B.z, Ts.z), Mathf.Lerp(A.w, B.w, Ts.w));
2986 }
2987
2991 public static Vector2 CompWise(this Vector2 A, Func<float, float> op) {
2992 return new Vector2(op(A.x), op(A.y));
2993 }
2994
2998 public static Vector3 CompWise(this Vector3 A, Func<float, float> op) {
2999 return new Vector3(op(A.x), op(A.y), op(A.z));
3000 }
3001
3005 public static Vector4 CompWise(this Vector4 A, Func<float, float> op) {
3006 return new Vector4(op(A.x), op(A.y), op(A.z), op(A.w));
3007 }
3008
3009 #endregion
3010
3011 #region From/Then Utilities
3012
3013 #region Float
3014
3022 public static float From(this float thisFloat, float otherFloat) {
3023 return thisFloat - otherFloat;
3024 }
3025
3033 public static float To(this float thisFloat, float otherFloat) {
3034 return otherFloat - thisFloat;
3035 }
3036
3040 public static float Then(this float thisFloat, float otherFloat) {
3041 return thisFloat + otherFloat;
3042 }
3043
3044 #endregion
3045
3046 #region Vector2
3047
3049 public static float Lerp(this Vector2 betweenXAndY, float t) {
3050 return Mathf.Lerp(betweenXAndY.x, betweenXAndY.y, t);
3051 }
3052
3053 #endregion
3054
3055 #region Vector3
3056
3060 public static Vector3 From(this Vector3 thisVector, Vector3 otherVector) {
3061 return thisVector - otherVector;
3062 }
3063
3067 public static Vector3 To(this Vector3 thisVector, Vector3 otherVector) {
3068 return otherVector - thisVector;
3069 }
3070
3075 public static Vector3 Then(this Vector3 thisVector, Vector3 otherVector) {
3076 return thisVector + otherVector;
3077 }
3078
3079 #endregion
3080
3081 #region Quaternion
3082
3088 public static Quaternion From(this Quaternion thisQuaternion, Quaternion otherQuaternion) {
3089 return Quaternion.Inverse(otherQuaternion) * thisQuaternion;
3090 }
3091
3097 public static Quaternion To(this Quaternion thisQuaternion, Quaternion otherQuaternion) {
3098 return Quaternion.Inverse(thisQuaternion) * otherQuaternion;
3099 }
3100
3105 public static Quaternion Then(this Quaternion thisQuaternion, Quaternion otherQuaternion) {
3106 return thisQuaternion * otherQuaternion;
3107 }
3108
3109 #endregion
3110
3111 #region Pose
3112
3119 public static Pose From(this Pose thisPose, Pose otherPose) {
3120 return otherPose.inverse * thisPose;
3121 }
3122
3129 public static Pose To(this Pose thisPose, Pose otherPose) {
3130 return thisPose.inverse * otherPose;
3131 }
3132
3141 public static Pose Then(this Pose thisPose, Pose otherPose) {
3142 return thisPose * otherPose;
3143 }
3144
3145 #endregion
3146
3147 #region Matrix4x4
3148
3156 public static Matrix4x4 From(this Matrix4x4 thisMatrix, Matrix4x4 otherMatrix) {
3157 return thisMatrix * otherMatrix.inverse;
3158 }
3159
3167 public static Matrix4x4 To(this Matrix4x4 thisMatrix, Matrix4x4 otherMatrix) {
3168 return otherMatrix * thisMatrix.inverse;
3169 }
3170
3175 public static Matrix4x4 Then(this Matrix4x4 thisMatrix, Matrix4x4 otherMatrix) {
3176 return otherMatrix * thisMatrix;
3177 }
3178
3179 #endregion
3180
3181 #endregion
3182
3183 }
3184
3185}
UnityEngine.Component Component
Es.InkPainter.Math Math
Definition: PaintTest.cs:7
UnityEngine.Color Color
Definition: TestScript.cs:32
UnityEngine.Object Object
A position and rotation. You can multiply two poses; this acts like Matrix4x4 multiplication,...
Definition: Pose.cs:21
Quaternion rotation
Definition: Pose.cs:24
A Query object is a type of immutable ordered collection of elements that can be used to perform usef...
Definition: Query.cs:90
ChildrenEnumerator GetEnumerator()
Definition: Utils.cs:1086
object System.Collections.IEnumerator. Current
Definition: Utils.cs:1095
HorizontalLineRectEnumerator(Rect rect, int numLines)
Definition: Utils.cs:2585
HorizontalLineRectEnumerator GetEnumerator()
Definition: Utils.cs:2601