Files
2026-07-13 13:13:17 +08:00

188 lines
5.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
using T3.Core.Operator;
using T3.Core.Operator.Slots;
namespace T3.Core.Utils;
// Todo: this class needs to be cleaned up!
public static class Utilities
{
// This has been implemented by the C# compiler and is unnecessary. i.e. (a,b) = (b,a), (a, b) = kvp
public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple, out T1 key, out T2 value)
{
key = tuple.Key;
value = tuple.Value;
}
public static void Dispose<T>(ref T obj) where T : class, IDisposable
{
obj?.Dispose();
obj = null;
}
public static void Swap<T>(this IList<T> list, int indexA, int indexB)
{
(list[indexA], list[indexB]) = (list[indexB], list[indexA]);
}
public static void Swap<T>(ref T a, ref T b)
{
(a, b) = (b, a);
}
public static float[] GetFloatsFromVector<T>(T v)
{
if (v is float v1)
{
return new[] { v1 };
}
if (v is Vector2 v2)
{
return new[] { v2.X, v2.Y };
}
if (v is Vector3 v3)
{
return new[] { v3.X, v3.Y, v3.Z };
}
if (v is Vector4 v4)
{
return new[] { v4.X, v4.Y, v4.Z, v4.W };
}
return Array.Empty<float>();
}
/// <summary>
/// Clamps an integer to the number of enums.
/// This prevents cast exceptions if index is out of range.
/// </summary>
/// <remarks>Note that this doesn't work for Enums with non-zero start index.</remarks>
public static T GetEnumValue<T>(this InputSlot<int> intInputSlot, EvaluationContext context) where T : Enum
{
var i = intInputSlot.GetValue(context).Clamp(0, Enum.GetValues(typeof(T)).Length - 1);
return CastTo<T>.From(i);
}
/// <summary>
/// Get a new value from an input slot and compare it with and old reference value.
/// This can be useful to quickly check if important input has changed and something
/// needs to be recomputed.
/// </summary>
/// <returns>true if value has changed</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool GetChangedValue<T>(
this Slot<T> input,
in EvaluationContext context,
ref T oldValue,
out T newValue)
{
newValue = input.GetValue(context);
if (EqualityComparer<T>.Default.Equals(newValue, oldValue))
return false;
oldValue = newValue;
return true;
}
public static int Hash<T>(T a, T b)
{
unchecked
{
int hash = 17;
hash = hash * 31 + a.GetHashCode();
hash = hash * 31 + b.GetHashCode();
return hash;
}
}
public static void CopyImageMemory(IntPtr srcData, IntPtr dstData, int height, int srcStride, int dstStride)
{
// Fast path, both strides are the same
if (srcStride == dstStride)
{
SharpDX.Utilities.CopyMemory(dstData, srcData, height * srcStride);
}
else
{
//We could pass row width as argument, bu the smallest of each stride is enough here
int rowWidth = Math.Min(srcStride, dstStride);
for (int i = 0; i < height; i++)
{
SharpDX.Utilities.CopyMemory(dstData, srcData, rowWidth);
srcData += srcStride;
dstData += dstStride;
}
}
}
/// <summary>
/// Infinite modulo indexer, also allows to reference array indexes using negative values
/// Note: assumes count is positive, does not perform check for it
/// </summary>
/// <param name="index">Index value</param>
/// <param name="count">Element count</param>
/// <returns>A valid index to lookup in the array</returns>
public static int InfiniteModIndexer(int index, int count)
{
if (count == 0)
return 0;
if (index < count)
{
if (index >= 0)
{
return index;
}
else
{
int remainder = index % count;
return remainder == 0 ? 0 : remainder + count;
}
}
else
{
return index % count;
}
}
/// <summary>
/// Useful for checking if a reference has changed without keeping an GC reference.
/// </summary>
public static bool HasObjectChanged(object obj, ref int? lastObjectId)
{
int? id = obj is null ? null : RuntimeHelpers.GetHashCode(obj);
if (id == lastObjectId)
return false;
lastObjectId = id;
return true;
}
}
public static class CastTo<TTarget>
{
public static TTarget From<TSource>(TSource source)
{
return Cache<TSource>.Caster(source);
}
private static class Cache<TSource>
{
public static readonly Func<TSource, TTarget> Caster = Get();
private static Func<TSource, TTarget> Get()
{
var p = Expression.Parameter(typeof(TSource));
var c = Expression.ConvertChecked(p, typeof(TTarget));
return Expression.Lambda<Func<TSource, TTarget>>(c, p).Compile();
}
}
}