using System.Collections; using System.Collections.Generic; using UnityEngine; using System.Linq; #if UNITY_EDITOR using UnityEditor; #endif namespace Pinwheel.Jupiter { /// /// Utility class for helper function /// public static class JUtilities { public static float DELTA_TIME = 0.0167f; private static Mesh quadMesh; public static Mesh QuadMesh { get { if (quadMesh == null) { quadMesh = JUtilities.GetPrimitiveMesh(PrimitiveType.Quad); } return quadMesh; } } public static string ListElementsToString(this IEnumerable list, string separator) { IEnumerator i = list.GetEnumerator(); System.Text.StringBuilder s = new System.Text.StringBuilder(); if (i.MoveNext()) s.Append(i.Current.ToString()); while (i.MoveNext()) s.Append(separator).Append(i.Current.ToString()); return s.ToString(); } public static T[][] CreateJaggedArray(int dimension1, int dimension2) { T[][] jaggedArray = new T[dimension1][]; for (int i = 0; i < dimension1; ++i) { jaggedArray[i] = new T[dimension2]; } return jaggedArray; } public static T[] To1dArray(T[][] jaggedArray) { List result = new List(); for (int z = 0; z < jaggedArray.Length; ++z) { for (int x = 0; x < jaggedArray[z].Length; ++x) { result.Add(jaggedArray[z][x]); } } return result.ToArray(); } public static T[] To1dArray(T[,] grid) { int height = grid.GetLength(0); int width = grid.GetLength(1); T[] result = new T[height * width]; for (int z = 0; z < height; ++z) { for (int x = 0; x < width; ++x) { result[To1DIndex(x, z, width)] = grid[z, x]; } } return result; } public static void Fill(T[] array, T value) { for (int i = 0; i < array.Length; ++i) { array[i] = value; } } public static void CopyTo(T[] src, T[] des) { int limit = Mathf.Min(src.Length, des.Length); for (int i = 0; i < limit; ++i) { des[i] = src[i]; } } public static int Sum(int[,] array) { int sum = 0; int length = array.GetLength(0); int width = array.GetLength(1); for (int z = 0; z < length; ++z) { for (int x = 0; x < width; ++x) { sum += array[z, x]; } } return sum; } public static int To1DIndex(int x, int z, int width) { return z * width + x; } public static bool IsInRange(float number, float minValue, float maxValue) { return number >= minValue && number <= maxValue; } public static bool IsInRangeExclusive(float number, float minValue, float maxValue) { return number > minValue && number < maxValue; } public static float GetFraction(float value, float floor, float ceil) { return (value - floor) / (ceil - floor); } public static void ClearChildren(Transform t) { int childCount = t.childCount; for (int i = childCount - 1; i >= 0; --i) { #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlaying) { GameObject.DestroyImmediate(t.GetChild(i).gameObject); } else { GameObject.Destroy(t.GetChild(i).gameObject); } #else GameObject.Destroy(t.GetChild(i).gameObject); #endif } } public static Gradient CreateFullWhiteGradient() { Gradient g = new Gradient(); GradientColorKey color = new GradientColorKey(Color.white, 1); GradientAlphaKey alpha = new GradientAlphaKey(1, 1); g.SetKeys(new GradientColorKey[] { color }, new GradientAlphaKey[] { alpha }); return g; } public static Gradient CreateFullTransparentGradient() { Gradient g = new Gradient(); GradientColorKey color = new GradientColorKey(Color.white, 1); GradientAlphaKey alpha = new GradientAlphaKey(0, 1); g.SetKeys(new GradientColorKey[] { color }, new GradientAlphaKey[] { alpha }); return g; } public static void CalculateBarycentricCoord(Vector2 p, Vector2 p1, Vector2 p2, Vector2 p3, ref Vector3 bary) { Vector2 v0 = p2 - p1; Vector2 v1 = p3 - p1; Vector2 v2 = p - p1; float d00 = Vector2.Dot(v0, v0); float d01 = Vector2.Dot(v0, v1); float d11 = Vector2.Dot(v1, v1); float d20 = Vector2.Dot(v2, v0); float d21 = Vector2.Dot(v2, v1); float inverseDenom = 1 / (d00 * d11 - d01 * d01); bary.y = (d11 * d20 - d01 * d21) * inverseDenom; bary.z = (d00 * d21 - d01 * d20) * inverseDenom; bary.x = 1.0f - bary.y - bary.z; } public static void ExpandTrisUvCoord(Texture2D tex, Vector2[] trisUv) { Vector2 texelSize = tex.texelSize; Vector2 center = (trisUv[0] + trisUv[1] + trisUv[2]) / 3; trisUv[0] += (trisUv[0] - center).normalized * texelSize.magnitude; trisUv[0].x = Mathf.Clamp01(trisUv[0].x); trisUv[0].y = Mathf.Clamp01(trisUv[0].y); trisUv[1] += (trisUv[1] - center).normalized * texelSize.magnitude; trisUv[1].x = Mathf.Clamp01(trisUv[1].x); trisUv[1].y = Mathf.Clamp01(trisUv[1].y); trisUv[2] += (trisUv[2] - center).normalized * texelSize.magnitude; trisUv[2].x = Mathf.Clamp01(trisUv[2].x); trisUv[2].y = Mathf.Clamp01(trisUv[2].y); } public static bool ContainIdenticalElements(T[] arr1, T[] arr2) { if (arr1 == null && arr2 == null) return true; if (arr1 == null && arr2 != null) return false; if (arr1 != null && arr2 == null) return false; if (arr1.Length != arr2.Length) return false; for (int i = 0; i < arr1.Length; ++i) { if (!arr1[i].Equals(arr2[i])) return false; } return true; } public static float GetNearestMultiple(float number, float multipleOf) { int multiplier = 0; while (multipleOf * multiplier < number) { multiplier += 1; } float floor = multipleOf * (multiplier - 1); float ceil = multipleOf * multiplier; float f0 = number - floor; float f1 = ceil - number; if (f0 < f1) return floor; else return ceil; } public static Transform GetChildrenWithName(Transform parent, string name) { Transform t = parent.Find(name); if (t == null) { GameObject g = new GameObject(name); g.transform.parent = parent; ResetTransform(g.transform, parent); t = g.transform; } return t; } public static void ResetTransform(Transform t, Transform parent) { t.parent = parent; t.localPosition = Vector3.zero; t.localRotation = Quaternion.identity; t.localScale = Vector3.one; } public static void DestroyGameobject(GameObject g) { #if UNITY_EDITOR if (UnityEditor.EditorApplication.isPlaying) GameObject.Destroy(g); else GameObject.DestroyImmediate(g); #else GameObject.Destroy(g); #endif } public static void DestroyGameObjectWithUndo(GameObject g) { #if UNITY_EDITOR Undo.DestroyObjectImmediate(g); #else DestroyGameobject(g); #endif } public static void DestroyObject(Object o) { #if UNITY_EDITOR if (UnityEditor.EditorApplication.isPlaying) Object.Destroy(o); else Object.DestroyImmediate(o, true); #else GameObject.Destroy(o); #endif } public static string Repeat(char src, int count) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); sb.Append(src, count); return sb.ToString(); } public static void MarkCurrentSceneDirty() { #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlaying) UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()); #endif } public static GameObject[] GetChildrenGameObjects(Transform parent) { GameObject[] children = new GameObject[parent.childCount]; for (int i = 0; i < parent.childCount; ++i) { children[i] = parent.GetChild(i).gameObject; } return children; } public static Transform[] GetChildrenTransforms(Transform parent) { Transform[] children = new Transform[parent.childCount]; for (int i = 0; i < parent.childCount; ++i) { children[i] = parent.GetChild(i); } return children; } /// /// https://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula /// /// /// /// /// public static Vector3 RotateVectorAroundNormal(Vector3 vector, Vector3 normal, float angleDegree) { float sin = Mathf.Sin(angleDegree * Mathf.Deg2Rad); float cos = Mathf.Cos(angleDegree * Mathf.Deg2Rad); Vector3 rotatedVector = vector * cos + Vector3.Cross(normal, vector) * sin + normal * Vector3.Dot(normal, vector) * (1 - cos); return rotatedVector; } public static Mesh GetPrimitiveMesh(PrimitiveType type) { GameObject g = GameObject.CreatePrimitive(type); Mesh m = g.GetComponent().sharedMesh; DestroyGameobject(g); return m; } public static void ShuffleList(List list) { int length = list.Count; for (int i = 0; i < length - 1; ++i) { int j = UnityEngine.Random.Range(0, length); T tmp = list[i]; list[i] = list[j]; list[j] = tmp; } } public static int[] GetShuffleIndicesArray(int length) { int[] indices = GetIndicesArray(length); for (int i = 0; i < length - 1; ++i) { int j = UnityEngine.Random.Range(0, length); int tmp = indices[i]; indices[i] = indices[j]; indices[j] = tmp; } return indices; } public static int[] GetIndicesArray(int length) { int[] indices = new int[length]; for (int i = 0; i < length; ++i) { indices[i] = i; } return indices; } public static void ResetArray(ref T[] array, int count, T defaultValue) { if (array != null && array.Length == count) { JUtilities.Fill(array, defaultValue); } else { array = new T[count]; } } public static bool EnsureArrayLength(ref T[] array, int count) { if (array == null || array.Length != count) { array = new T[count]; return false; } return true; } public static float GetValueBilinear(float[] data, int width, int height, Vector2 uv) { float value = 0; Vector2 pixelCoord = new Vector2( Mathf.Lerp(0, width - 1, uv.x), Mathf.Lerp(0, height - 1, uv.y)); //apply a bilinear filter int xFloor = Mathf.FloorToInt(pixelCoord.x); int xCeil = Mathf.CeilToInt(pixelCoord.x); int yFloor = Mathf.FloorToInt(pixelCoord.y); int yCeil = Mathf.CeilToInt(pixelCoord.y); float f00 = data[JUtilities.To1DIndex(xFloor, yFloor, width)]; float f01 = data[JUtilities.To1DIndex(xFloor, yCeil, width)]; float f10 = data[JUtilities.To1DIndex(xCeil, yFloor, width)]; float f11 = data[JUtilities.To1DIndex(xCeil, yCeil, width)]; Vector2 unitCoord = new Vector2( pixelCoord.x - xFloor, pixelCoord.y - yFloor); value = f00 * (1 - unitCoord.x) * (1 - unitCoord.y) + f01 * (1 - unitCoord.x) * unitCoord.y + f10 * unitCoord.x * (1 - unitCoord.y) + f11 * unitCoord.x * unitCoord.y; return value; } public static Color GetColorBilinear(Color[] textureData, int width, int height, Vector2 uv) { Color color = Color.clear; Vector2 pixelCoord = new Vector2( Mathf.Lerp(0, width - 1, uv.x), Mathf.Lerp(0, height - 1, uv.y)); //apply a bilinear filter int xFloor = Mathf.FloorToInt(pixelCoord.x); int xCeil = Mathf.CeilToInt(pixelCoord.x); int yFloor = Mathf.FloorToInt(pixelCoord.y); int yCeil = Mathf.CeilToInt(pixelCoord.y); Color f00 = textureData[JUtilities.To1DIndex(xFloor, yFloor, width)]; Color f01 = textureData[JUtilities.To1DIndex(xFloor, yCeil, width)]; Color f10 = textureData[JUtilities.To1DIndex(xCeil, yFloor, width)]; Color f11 = textureData[JUtilities.To1DIndex(xCeil, yCeil, width)]; Vector2 unitCoord = new Vector2( pixelCoord.x - xFloor, pixelCoord.y - yFloor); color = f00 * (1 - unitCoord.x) * (1 - unitCoord.y) + f01 * (1 - unitCoord.x) * unitCoord.y + f10 * unitCoord.x * (1 - unitCoord.y) + f11 * unitCoord.x * unitCoord.y; return color; } public static GameObject CreatePreviewGameObject(Mesh m, Material mat, Vector3 position) { GameObject g = new GameObject("GO"); g.transform.position = position; g.transform.rotation = Quaternion.identity; g.transform.localScale = Vector3.one; MeshFilter mf = g.AddComponent(); mf.sharedMesh = m; MeshRenderer mr = g.AddComponent(); mr.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off; mr.receiveShadows = false; mr.sharedMaterial = mat; return g; } public static Camera CreatePreviewCamera(GameObject target, float distance, float padding) { GameObject g = new GameObject("CAM"); g.transform.rotation = Quaternion.identity; g.transform.localScale = Vector3.one; Camera cam = g.AddComponent(); cam.orthographic = true; cam.clearFlags = CameraClearFlags.Nothing; cam.aspect = 1; MeshRenderer mr = target.GetComponent(); Bounds bounds = mr.bounds; cam.transform.position = bounds.center + new Vector3(-1, 0.5f, -1) * distance; cam.transform.LookAt(bounds.center); cam.orthographicSize = Mathf.Max(bounds.size.x, bounds.size.y, bounds.size.z) / 2 + padding; return cam; } public static void EnsureDirectoryExists(string dir) { if (!System.IO.Directory.Exists(dir)) { System.IO.Directory.CreateDirectory(dir); } } public static void SetStaticRecursively(GameObject g, bool isStatic) { #if UNITY_EDITOR g.isStatic = isStatic; GameObject[] children = GetChildrenGameObjects(g.transform); for (int i = 0; i < children.Length; ++i) { SetStaticRecursively(children[i], isStatic); } #endif } public static void EnsureLengthSufficient(List list, int preferredLength) where T : Object { if (list == null) list = new List(); while (list.Count < preferredLength) { list.Add(null); } } public static void EnsureLengthSufficient(List list, int preferredLength) { if (list == null) list = new List(); while (list.Count < preferredLength) { list.Add(string.Empty); } } public static void EnsureLengthSufficient(List list, int preferredLength) { if (list == null) list = new List(); while (list.Count < preferredLength) { list.Add(false); } } public static string Ellipsis(string s, int length) { if (s.Length < length) return s; string s0 = s.Substring(0, length); return s0 + "..."; } public static bool IsRectContainPointExclusive(Rect r, Vector2 p) { return p.x > r.min.x && p.x < r.max.x && p.y > r.min.y && p.y < r.max.y; } public static Color GetColor(Color baseColor, float alpha) { return new Color(baseColor.r, baseColor.g, baseColor.b, alpha); } public static Rect GetRectContainsPoints(params Vector2[] points) { float xMin = float.MaxValue; float xMax = float.MinValue; float yMin = float.MaxValue; float yMax = float.MinValue; for (int i = 0; i < points.Length; ++i) { xMin = points[i].x < xMin ? points[i].x : xMin; xMax = points[i].x > xMax ? points[i].x : xMax; yMin = points[i].y < yMin ? points[i].y : yMin; yMax = points[i].y > yMax ? points[i].y : yMax; } return Rect.MinMaxRect(xMin, yMin, xMax, yMax); } public static float InverseLerpUnclamped(float a, float b, float value) { if (a != b) { return (value - a) / (b - a); } return 0f; } public static Vector2 PointToNormalizedUnclampled(Rect r, Vector2 point) { return new Vector2(InverseLerpUnclamped(r.x, r.xMax, point.x), InverseLerpUnclamped(r.y, r.yMax, point.y)); } public static Rect GetUvRect(Vector2 v0, Vector2 v1, Vector2 v2) { return Rect.MinMaxRect( Mathf.Min(v0.x, v1.x, v2.x), Mathf.Min(v0.y, v1.y, v2.y), Mathf.Max(v0.x, v1.x, v2.x), Mathf.Max(v0.y, v1.y, v2.y)); } public static Gradient Clone(Gradient src) { if (src == null) return null; Gradient des = new Gradient(); des.SetKeys(src.colorKeys, src.alphaKeys); return des; } public static AnimationCurve Clone(AnimationCurve src) { if (src == null) return null; AnimationCurve des = new AnimationCurve(); Keyframe[] keys = src.keys; for (int i = 0; i < keys.Length; ++i) { des.AddKey(keys[i]); } des.preWrapMode = src.preWrapMode; des.postWrapMode = src.postWrapMode; return des; } public static bool IsPointInsideQuadXZ(Vector3 point, Vector3[] quad) { Vector3 bary = Vector3.zero; CalculateBarycentricCoord( new Vector2(point.x, point.z), new Vector2(quad[0].x, quad[0].z), new Vector2(quad[1].x, quad[1].z), new Vector2(quad[2].x, quad[2].z), ref bary); if (bary.x >= 0 && bary.y >= 0 && bary.z >= 0) return true; CalculateBarycentricCoord( new Vector2(point.x, point.z), new Vector2(quad[0].x, quad[0].z), new Vector2(quad[2].x, quad[2].z), new Vector2(quad[3].x, quad[3].z), ref bary); if (bary.x >= 0 && bary.y >= 0 && bary.z >= 0) return true; return false; } public static void DestroyMeshArray(Mesh[] meshes) { for (int i = 0; i < meshes.Length; ++i) { if (meshes[i] != null) { Object.DestroyImmediate(meshes[i], true); } } } public static Vector2 FlipY(Vector2 v) { return new Vector2(v.x, 1 - v.y); } /// /// https://en.wikipedia.org/wiki/Delaunay_triangulation#Algorithms /// /// /// /// /// /// public static bool IsPointInCircumcircle(Vector2 v0, Vector2 v1, Vector2 v2, Vector2 p) { Matrix4x4 mat = new Matrix4x4(); mat.SetRow(0, new Vector4(v0.x, v0.y, v0.x * v0.x + v0.y * v0.y, 1)); mat.SetRow(1, new Vector4(v2.x, v2.y, v2.x * v2.x + v2.y * v2.y, 1)); //a,b,c counterclockwise mat.SetRow(2, new Vector4(v1.x, v1.y, v1.x * v1.x + v1.y * v1.y, 1)); mat.SetRow(3, new Vector4(p.x, p.y, p.x * p.x + p.y * p.y, 1)); return mat.determinant > 0; } public static bool IsPointInCircumcircleXZ(Vector3 v0, Vector3 v1, Vector3 v2, Vector3 p) { Matrix4x4 mat = new Matrix4x4(); mat.SetRow(0, new Vector4(v0.x, v0.z, v0.x * v0.x + v0.z * v0.z, 1)); mat.SetRow(1, new Vector4(v2.x, v2.z, v2.x * v2.x + v2.z * v2.z, 1)); //a,b,c counterclockwise mat.SetRow(2, new Vector4(v1.x, v1.z, v1.x * v1.x + v1.z * v1.z, 1)); mat.SetRow(3, new Vector4(p.x, p.z, p.x * p.x + p.z * p.z, 1)); return mat.determinant > 0; } public static bool AreSetEqual(ushort[] setA, ushort[] setB) { HashSet a = new HashSet(setA); HashSet b = new HashSet(setB); return a.SetEquals(b); } public static void Distinct(this List list) { list.Distinct(); } public static void AddIfNotContains(this IList list, IEnumerable items) { IEnumerator iter = items.GetEnumerator(); while (iter.MoveNext()) { T current = iter.Current; if (!list.Contains(current)) { list.Add(current); } } } public static void AddIfNotContains(this IList list, T item) { if (!list.Contains(item)) { list.Add(item); } } public static Vector3 ToX0Y(this Vector2 v) { return new Vector3(v.x, 0, v.y); } } }