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_CACHE
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- // Shared File Last Modified: 2023-09-02
- namespace 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
|