using System.Collections.Generic; using UnityEngine; using UnityEngine.Profiling; namespace Coffee.UISoftMaskInternal { /// /// Extension methods for Transform class. /// internal static class TransformExtensions { private const float k_DefaultEpsilon = 1f / (2 ^ 8); private static readonly Vector3[] s_Corners = new Vector3[4]; /// /// Compare the hierarchy index of one transform with another transform. /// public static int CompareHierarchyIndex(this Transform self, Transform other, Transform stopAt) { if (self == other) return 0; Profiler.BeginSample("(COF)[TransformExt] CompareHierarchyIndex > GetTransforms"); var lTrs = self.GetTransforms(stopAt, InternalListPool.Rent()); var rTrs = other.GetTransforms(stopAt, InternalListPool.Rent()); Profiler.EndSample(); Profiler.BeginSample("(COF)[TransformExt] CompareHierarchyIndex > Calc"); var loop = Mathf.Max(lTrs.Count, rTrs.Count); var result = 0; for (var i = 0; i < loop; ++i) { var selfIndex = 0 <= lTrs.Count - i - 1 ? lTrs[lTrs.Count - i - 1].GetSiblingIndex() : -1; var otherIndex = 0 <= rTrs.Count - i - 1 ? rTrs[rTrs.Count - i - 1].GetSiblingIndex() : -1; if (selfIndex == otherIndex) continue; result = selfIndex - otherIndex; break; } Profiler.EndSample(); Profiler.BeginSample("(COF)[TransformExt] CompareHierarchyIndex > Return"); InternalListPool.Return(ref lTrs); InternalListPool.Return(ref rTrs); Profiler.EndSample(); return result; } private static List GetTransforms(this Transform self, Transform stopAt, List results) { results.Clear(); while (self != stopAt) { results.Add(self); self = self.parent; } return results; } /// /// Check if a transform has changed. /// public static bool HasChanged(this Transform self, ref Matrix4x4 prev, float epsilon = k_DefaultEpsilon) { return self.HasChanged(null, ref prev, epsilon); } /// /// Check if a transform has changed. /// public static bool HasChanged(this Transform self, Transform baseTransform, ref Matrix4x4 prev, float epsilon = k_DefaultEpsilon) { if (!self) return false; var hash = baseTransform ? baseTransform.GetHashCode() : 0; if (FrameCache.TryGet(self, nameof(HasChanged), hash, out bool result)) return result; var matrix = baseTransform ? baseTransform.worldToLocalMatrix * self.localToWorldMatrix : self.localToWorldMatrix; var current = matrix * Matrix4x4.Scale(Vector3.one * 10000); result = !Approximately(current, prev, epsilon); FrameCache.Set(self, nameof(HasChanged), hash, result); if (result) { prev = current; } return result; } private static bool Approximately(Matrix4x4 self, Matrix4x4 other, float epsilon = k_DefaultEpsilon) { for (var i = 0; i < 16; i++) { if (epsilon < Mathf.Abs(self[i] - other[i])) { return false; } } return true; } public static Bounds GetRelativeBounds(this Transform self, Transform child) { if (!self || !child) { return new Bounds(Vector3.zero, Vector3.zero); } var list = InternalListPool.Rent(); child.GetComponentsInChildren(false, list); if (list.Count == 0) { InternalListPool.Return(ref list); return new Bounds(Vector3.zero, Vector3.zero); } var max = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); var min = new Vector3(float.MinValue, float.MinValue, float.MinValue); var worldToLocalMatrix = self.worldToLocalMatrix; for (var i = 0; i < list.Count; i++) { list[i].GetWorldCorners(s_Corners); for (var j = 0; j < 4; j++) { var lhs = worldToLocalMatrix.MultiplyPoint3x4(s_Corners[j]); max = Vector3.Min(lhs, max); min = Vector3.Max(lhs, min); } } InternalListPool.Return(ref list); var rectTransformBounds = new Bounds(max, Vector3.zero); rectTransformBounds.Encapsulate(min); return rectTransformBounds; } } }