// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2024 Kybernetik // // Inspector Gadgets // https://kybernetik.com.au/inspector-gadgets // Copyright 2017-2024 Kybernetik // #if UNITY_EDITOR //#define LOG_CONVERSION_CACHE using System; using System.Collections.Generic; using UnityEngine; // Shared File Last Modified: 2023-09-02 namespace Animancer.Editor //namespace InspectorGadgets.Editor { /// [Editor-Only] /// A simple system for converting objects and storing the results so they can be reused to minimise the need for /// garbage collection, particularly for string construction. /// /// This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache_2 /// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/ConversionCache_2 /// public class ConversionCache { /************************************************************************************************************************/ private class CachedValue { public int lastFrameAccessed; public TValue value; } /************************************************************************************************************************/ private readonly Dictionary Cache = new(); private readonly List Keys = new(); private readonly Func Converter; private int _LastCleanupFrame; /************************************************************************************************************************/ /// /// Creates a new which uses the specified delegate to convert values. /// public ConversionCache(Func converter) => Converter = converter; /************************************************************************************************************************/ /// /// If a value has already been cached for the specified `key`, return it. Otherwise create a new one using /// the delegate provided in the constructor and cache it. /// /// If the `key` is null, this method returns the default . /// /// This method also periodically removes values that have not been used recently. public TValue Convert(TKey key) { if (key == null) return default; CachedValue cached; // The next time a value is retrieved after at least 100 frames, clear out any old ones. var frame = Time.frameCount; if (_LastCleanupFrame + 100 < frame) { for (int i = Keys.Count - 1; i >= 0; i--) { var checkKey = Keys[i]; if (!Cache.TryGetValue(checkKey, out cached) || cached.lastFrameAccessed <= _LastCleanupFrame) { Cache.Remove(checkKey); Keys.RemoveAt(i); } } _LastCleanupFrame = frame; } if (!Cache.TryGetValue(key, out cached)) { Cache.Add(key, cached = new() { value = Converter(key) }); Keys.Add(key); } cached.lastFrameAccessed = frame; return cached.value; } /************************************************************************************************************************/ } /// [Editor-Only] Utilities for . /// This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime. /// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache /// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/ConversionCache /// public static class ConversionCache { /************************************************************************************************************************/ /// /// Creates a for calculating the GUI width occupied by text using /// the specified `style`. /// public static ConversionCache CreateWidthCache(GUIStyle style) => new(style.CalculateWidth); /************************************************************************************************************************/ // The "g" format gives a lower case 'e' for exponentials instead of upper case 'E'. private static readonly ConversionCache FloatToString = new((value) => $"{value:g}"); /// [Animancer Extension] /// Calls using "g" as the format and caches the result. /// public static string ToStringCached(this float value) => FloatToString.Convert(value); /************************************************************************************************************************/ } } #endif