// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik //
#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Animancer.Editor
{
/// [Editor-Only] Various utilities used throughout Animancer.
/// https://kybernetik.com.au/animancer/api/Animancer.Editor/AnimancerEditorUtilities
public static partial class AnimancerEditorUtilities
{
/************************************************************************************************************************/
#region Misc
/************************************************************************************************************************/
/// [Animancer Extension] [Editor-Only] Is the or NaN?
public static bool IsNaN(this Vector2 vector)
=> float.IsNaN(vector.x)
|| float.IsNaN(vector.y);
/// [Animancer Extension] [Editor-Only] Is the , , or NaN?
public static bool IsNaN(this Vector3 vector)
=> float.IsNaN(vector.x)
|| float.IsNaN(vector.y)
|| float.IsNaN(vector.z);
/************************************************************************************************************************/
/// Returns the value of `t` linearly interpolated along the X axis of the `rect`.
public static float LerpUnclampedX(this Rect rect, float t)
=> rect.x + rect.width * t;
/// Returns the value of `t` inverse linearly interpolated along the X axis of the `rect`.
public static float InverseLerpUnclampedX(this Rect rect, float t)
=> (t - rect.x) / rect.width;
/************************************************************************************************************************/
/// Finds an asset of the specified type anywhere in the project.
public static T FindAssetOfType()
where T : Object
{
var filter = typeof(Component).IsAssignableFrom(typeof(T))
? $"t:{nameof(GameObject)}"
: $"t:{typeof(T).Name}";
var guids = AssetDatabase.FindAssets(filter);
if (guids.Length == 0)
return null;
for (int i = 0; i < guids.Length; i++)
{
var path = AssetDatabase.GUIDToAssetPath(guids[i]);
var asset = AssetDatabase.LoadAssetAtPath(path);
if (asset != null)
return asset;
}
return null;
}
/************************************************************************************************************************/
/// Finds or creates an instance of .
public static T FindOrCreate(ref T scriptableObject, HideFlags hideFlags = default)
where T : ScriptableObject
{
if (scriptableObject != null)
return scriptableObject;
var instances = Resources.FindObjectsOfTypeAll();
if (instances.Length > 0)
{
scriptableObject = instances[0];
}
else
{
scriptableObject = ScriptableObject.CreateInstance();
scriptableObject.hideFlags = hideFlags;
}
return scriptableObject;
}
/************************************************************************************************************************/
/// The most recent .
public static PlayModeStateChange PlayModeState { get; private set; }
/// Is the Unity Editor is currently changing between Play Mode and Edit Mode?
public static bool IsChangingPlayMode =>
PlayModeState == PlayModeStateChange.ExitingEditMode ||
PlayModeState == PlayModeStateChange.ExitingPlayMode;
[InitializeOnLoadMethod]
private static void WatchForPlayModeChanges()
{
PlayModeState = EditorApplication.isPlayingOrWillChangePlaymode
? EditorApplication.isPlaying
? PlayModeStateChange.EnteredPlayMode
: PlayModeStateChange.ExitingEditMode
: PlayModeStateChange.EnteredEditMode;
EditorApplication.playModeStateChanged += change => PlayModeState = change;
}
/************************************************************************************************************************/
/// Deletes the specified `subAsset`.
public static void DeleteSubAsset(Object subAsset)
{
AssetDatabase.RemoveObjectFromAsset(subAsset);
AssetDatabase.SaveAssets();
Object.DestroyImmediate(subAsset, true);
}
/************************************************************************************************************************/
/// Calculates the overall bounds of all renderers under the `transform`.
public static Bounds CalculateBounds(Transform transform)
{
using var _ = ListPool.Instance.Acquire(out var renderers);
transform.GetComponentsInChildren(renderers);
if (renderers.Count == 0)
return default;
var bounds = renderers[0].bounds;
for (int i = 1; i < renderers.Count; i++)
bounds.Encapsulate(renderers[i].bounds);
return bounds;
}
/************************************************************************************************************************/
#endregion
/************************************************************************************************************************/
#region Collections
/************************************************************************************************************************/
/// Adds default items or removes items to make the equal to the `count`.
public static void SetCount(List list, int count)
{
if (list.Count < count)
{
while (list.Count < count)
list.Add(default);
}
else
{
list.RemoveRange(count, list.Count - count);
}
}
/************************************************************************************************************************/
///
/// Removes any items from the `list` that are null and items that appear multiple times.
/// Returns true if the `list` was modified.
///
public static bool RemoveMissingAndDuplicates(ref List list)
{
if (list == null)
{
list = new();
return false;
}
var modified = false;
using (SetPool