// 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