using System; using System.Collections.Generic; using T3.Core.DataTypes; using T3.Core.DataTypes.Vector; using T3.Core.Operator.Slots; using Int3 = T3.Core.DataTypes.Vector.Int3; // ReSharper disable RedundantNameQualifier namespace T3.Core.Utils; public static class ValueUtils { /// /// Defines which values types can be blended and thus be part of presets and snapshots. /// public static readonly Dictionary> BlendMethods = new() { { typeof(float), (a, b, t) => { if (a is not InputValue aValue || b is not InputValue bValue) return null; var r = MathUtils.Lerp(aValue.Value, bValue.Value, t); return new InputValue(r); } }, { typeof(Vector2), (a, b, t) => { if (a is not InputValue aValue || b is not InputValue bValue) return null; var r = MathUtils.Lerp(aValue.Value, bValue.Value, t); return new InputValue(r); } }, { typeof(Vector3), (a, b, t) => { if (a is not InputValue aValue || b is not InputValue bValue) return null; var r = MathUtils.Lerp(aValue.Value, bValue.Value, t); return new InputValue(r); } }, { typeof(Vector4), (a, b, t) => { if (a is not InputValue aValue || b is not InputValue bValue) return null; var r = MathUtils.Lerp(aValue.Value, bValue.Value, t); return new InputValue(r); } }, { typeof(Quaternion), (a, b, t) => { if (a is not InputValue aValue || b is not InputValue bValue) return null; var r = Quaternion.Slerp(aValue.Value, bValue.Value, t); return new InputValue(r); } }, { typeof(int), (a, b, t) => { if (a is not InputValue aValue || b is not InputValue bValue) return null; var r = MathUtils.Lerp(aValue.Value, bValue.Value, t); return new InputValue(r); } }, { typeof(string), (a, b, t) => { if (a is not InputValue aValue2 || b is not InputValue bValue2) return null; return new InputValue(t <= 0.5f ? aValue2.Value : bValue2.Value); } }, { typeof(Int3), (a, b, t) => { if (a is not InputValue aValue || b is not InputValue bValue) return null; var r = new Int3(MathUtils.Lerp(aValue.Value.X, bValue.Value.X, t), MathUtils.Lerp(aValue.Value.Y, bValue.Value.Y, t), MathUtils.Lerp(aValue.Value.Z, bValue.Value.Z, t) ); return new InputValue(r); } }, { typeof(Int2), (a, b, t) => { if (a is not InputValue aValue || b is not InputValue bValue) return null; var r = new Int2(MathUtils.Lerp(aValue.Value.Width, bValue.Value.Width, t), MathUtils.Lerp(aValue.Value.Height, bValue.Value.Height, t) ); return new InputValue(r); } }, { typeof(bool), (aValue, bValue, t) => { if (aValue is not InputValue aValue2 || bValue is not InputValue bValue2) return null; return new InputValue(t <= 0.5f ? aValue2.Value : bValue2.Value); } }, { typeof(Gradient), (aGradient, bGradient, t) => { if (aGradient is not InputValue aGradient2 || bGradient is not InputValue bGradient2) return null; Gradient gradientA = aGradient2.Value; Gradient gradientB = bGradient2.Value; // Blend if possible if (gradientA.Interpolation == gradientB.Interpolation && gradientA.Steps.Count == gradientB.Steps.Count) { Gradient newGradient = gradientA.TypedClone(); for (int index = 0; index < gradientA.Steps.Count; index++) { //Log.Debug("Blending gradient steps..."); var stepA = gradientA.Steps[index]; var stepB = gradientB.Steps[index]; newGradient.Steps[index].NormalizedPosition = MathUtils.Lerp(stepA.NormalizedPosition, stepB.NormalizedPosition, t); newGradient.Steps[index].Color = MathUtils.Lerp(stepA.Color, stepB.Color, t); } return new InputValue(newGradient); } // If not possible, just switch between the two gradients return t < 0.5 ? aGradient2.Clone() : bGradient2.Clone(); } }, }; public static readonly Dictionary> ToStringMethods = new() { { typeof(float), v => v is not InputValue vv ? string.Empty : $"{vv.Value:0.000}" }, { typeof(Vector2), v => v is not InputValue vv ? string.Empty : $"{vv.Value.X:0.00} {vv.Value.Y:0.00}" }, { typeof(Vector3), v => v is not InputValue vv ? string.Empty : $"{vv.Value.X:0.00} {vv.Value.Y:0.00} {vv.Value.Z:0.00} " }, { typeof(Vector4), v => v is not InputValue vv ? string.Empty : $"{vv.Value.X:0.00} {vv.Value.Y:0.00} {vv.Value.Z:0.00} {vv.Value.W:0.00} " }, { typeof(int), v => v is not InputValue vv ? string.Empty : $"{vv.Value}" }, }; /// /// A set of functions that mix n values with blend factors /// /// /// Note that Quaternions can't be easily weight blended. /// public static readonly Dictionary> WeightedBlendMethods = new() { { typeof(float), (values, weights) => { var sum = 0f; for (var index = 0; index < values.Length; index++) { var inputV = values[index]; if (inputV is not InputValue v) continue; sum += v.Value * weights[index]; } return new InputValue(sum); } }, { typeof(Vector2), (values, weights) => { var sum = Vector2.Zero; for (var index = 0; index < values.Length; index++) { var inputV = values[index]; if (inputV is not InputValue v) continue; sum += v.Value * weights[index]; } return new InputValue(sum); } }, { typeof(Vector3), (values, weights) => { var sum = Vector3.Zero; for (var index = 0; index < values.Length; index++) { var inputV = values[index]; if (inputV is not InputValue v) continue; sum += v.Value * weights[index]; } return new InputValue(sum); } }, { typeof(Vector4), (values, weights) => { if (values.Length == 1) return values[0]; var sum = Vector4.Zero; for (var index = 0; index < values.Length; index++) { var inputV = values[index]; if (inputV is not InputValue v) continue; sum += v.Value * weights[index]; } return new InputValue(sum); } }, { typeof(int), (values, weights) => { var sum = 0f; for (var index = 0; index < values.Length; index++) { var inputV = values[index]; if (inputV is not InputValue v) continue; sum += v.Value * weights[index]; } return new InputValue((int)(sum + 0.5f)); } }, { typeof(string), (values, weights) => { //var sum = 0f; var best = string.Empty; var bestWeight = float.NegativeInfinity; for (var index = 0; index < values.Length; index++) { var inputV = values[index]; if (inputV is not InputValue v) continue; var weight = weights[index]; if (!(weight > bestWeight)) continue; bestWeight = weight; best = v.Value; } return new InputValue(best); } }, { typeof(Int2), (values, weights) => { var sum = new Vector2(); for (var index = 0; index < values.Length; index++) { var inputV = values[index]; if (inputV is not InputValue v) continue; sum += new Vector2(v.Value.Width * weights[index], v.Value.Height * weights[index]); } return new InputValue(new Int2((int)(sum.X + 0.5f), (int)(sum.Y + 0.5f))); } }, { typeof(Int3), (values, weights) => { var sum = new Vector3(); for (var index = 0; index < values.Length; index++) { var inputV = values[index]; if (inputV is not InputValue v) continue; sum += new Vector3(v.Value.X * weights[index], v.Value.Y * weights[index], v.Value.Z * weights[index]); } return new InputValue(new Int3((int)(sum.X + 0.5f), (int)(sum.Y + 0.5f), (int)(sum.Z + 0.5f))); } }, { typeof(Gradient), (gradients, weights) => { var tempGradients = new List(gradients.Length); var bestIndex = -1; var bestWeight = float.PositiveInfinity; var isBlendable = true; for (var index = 0; index < gradients.Length; index++) { var inputV = gradients[index]; if (inputV is not InputValue v) continue; if (weights[index] < bestWeight) { bestIndex = index; bestWeight = weights[index]; } if (index > 0) { if (v.Value.Interpolation != tempGradients[0].Interpolation || v.Value.Steps.Count != tempGradients[0].Steps.Count) { isBlendable = false; } } tempGradients.Add(v.Value); } if (isBlendable) { var newGradient = tempGradients[0].TypedClone(); for (var stepIndex = 0; stepIndex < tempGradients[0].Steps.Count; stepIndex++) { var step = tempGradients[0].Steps[stepIndex]; var color = step.Color * weights[0]; var position = step.NormalizedPosition * weights[0]; for (var index = 1; index < tempGradients.Count; index++) { color += tempGradients[index].Steps[stepIndex].Color * weights[index]; position += tempGradients[index].Steps[stepIndex].NormalizedPosition * weights[index]; } newGradient.Steps[stepIndex].NormalizedPosition = position.Clamp(0,1); newGradient.Steps[stepIndex].Color = color; } return new InputValue(newGradient); } return bestIndex >= 0 ? new InputValue(tempGradients[bestIndex]) : null; } }, }; public static readonly Dictionary> CompareFunctions = new() { { typeof(float), (a, b) => { if (a is not InputValue aValue || b is not InputValue bValue) return false; return Math.Abs(aValue.Value - bValue.Value) < float.Epsilon; } }, { typeof(Vector2), (a, b) => { if (a is not InputValue aValue || b is not InputValue bValue) return false; return aValue.Value == bValue.Value; } }, { typeof(Vector3), (a, b) => { if (a is not InputValue aValue || b is not InputValue bValue) return false; return aValue.Value == bValue.Value; } }, { typeof(Vector4), (a, b) => { if (a is not InputValue aValue || b is not InputValue bValue) return false; return aValue.Value == bValue.Value; } }, { typeof(Quaternion), (a, b) => { if (a is not InputValue aValue || b is not InputValue bValue) return false; return aValue.Value == bValue.Value; } }, { typeof(int), (a, b) => { if (a is not InputValue aValue || b is not InputValue bValue) return false; return aValue.Value == bValue.Value; } }, { typeof(Int3), (a, b) => { if (a is not InputValue aValue || b is not InputValue bValue) return false; return aValue.Value == bValue.Value; } }, { typeof(Int2), (a, b) => { if (a is not InputValue aValue || b is not InputValue bValue) return false; return aValue.Value == bValue.Value; } }, }; public static string GetValueString(InputValue inputValue) { return inputValue switch { InputValue f => $"{f.Value:0.000}", InputValue i => $"{i.Value:G3}", InputValue i => $"{i.Value:G3}", InputValue b => $"{b.Value}", InputValue v3 => $"{v3.Value:0.0}", InputValue v2 => $"{v2.Value:0.0}", InputValue s => s.Value.Truncate(), _ => "" }; } public static string GetValueString(IInputSlot inputSlot) { return inputSlot switch { InputSlot f => $"{f.GetCurrentValue():0.000}", InputSlot i => $"{i.GetCurrentValue():G3}", InputSlot i => $"{i.GetCurrentValue():G3}", InputSlot b => $"{b.GetCurrentValue()}", InputSlot v3 => $"{v3.GetCurrentValue():0.0}", InputSlot v2 => $"{v2.GetCurrentValue():0.0}", InputSlot s => s.GetCurrentValue().Truncate(), _ => "" }; } public static string GetValueString(ISlot inputSlot) { return inputSlot switch { Slot f => $"{f.Value:0.000}", Slot i => $"{i.Value:G3}", Slot i => $"{i.Value:G3}", Slot b => $"{b.Value}", Slot v3 => $"{v3.Value:0.0}", Slot v2 => $"{v2.Value:0.0}", Slot s => s.Value.Truncate(), _ => "" }; } }