chore: initial commit
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
// Animancer // https://kybernetik.com.au/animancer // Copyright 2018-2026 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
|
||||
|
||||
Reference in New Issue
Block a user