| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137 | // 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_CACHEusing System;using System.Collections.Generic;using UnityEngine;// Shared File Last Modified: 2023-09-02namespace Animancer.Editor//namespace InspectorGadgets.Editor{    /// <summary>[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.    /// </summary>    /// <remarks>This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime.</remarks>    /// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache_2    /// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/ConversionCache_2    ///     public class ConversionCache<TKey, TValue>    {        /************************************************************************************************************************/        private class CachedValue        {            public int lastFrameAccessed;            public TValue value;        }        /************************************************************************************************************************/        private readonly Dictionary<TKey, CachedValue>            Cache = new();        private readonly List<TKey>            Keys = new();        private readonly Func<TKey, TValue>            Converter;        private int _LastCleanupFrame;        /************************************************************************************************************************/        /// <summary>        /// Creates a new <see cref="ConversionCache{TKey, TValue}"/> which uses the specified delegate to convert values.        /// </summary>        public ConversionCache(Func<TKey, TValue> converter)            => Converter = converter;        /************************************************************************************************************************/        /// <summary>        /// 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.        /// <para></para>        /// If the `key` is <c>null</c>, this method returns the default <typeparamref name="TValue"/>.        /// </summary>        /// <remarks>This method also periodically removes values that have not been used recently.</remarks>        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;        }        /************************************************************************************************************************/    }    /// <summary>[Editor-Only] Utilities for <see cref="ConversionCache{TKey, TValue}"/>.</summary>    /// <remarks>This class doesn't use any Editor-Only functionality, but it's unlikely to be useful at runtime.</remarks>    /// https://kybernetik.com.au/animancer/api/Animancer.Editor/ConversionCache    /// https://kybernetik.com.au/inspector-gadgets/api/InspectorGadgets.Editor/ConversionCache    ///     public static class ConversionCache    {        /************************************************************************************************************************/        /// <summary>        /// Creates a <see cref="ConversionCache{TKey, TValue}"/> for calculating the GUI width occupied by text using        /// the specified `style`.        /// </summary>        public static ConversionCache<string, float> 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<float, string>            FloatToString = new((value) => $"{value:g}");        /// <summary>[Animancer Extension]        /// Calls <see cref="float.ToString(string)"/> using <c>"g"</c> as the format and caches the result.        /// </summary>        public static string ToStringCached(this float value)            => FloatToString.Convert(value);        /************************************************************************************************************************/    }}#endif
 |