Core/Library.cs
namespace Nodebox;
public static class Library {
public abstract record All { }
public abstract record Float { }
public abstract record Integer { }
public abstract record IntegerUnsigned { }
public abstract record Vector { }
public abstract record FloatVector { }
public abstract record IntegerVector { }
public abstract record FloatN { }
public abstract record IntegerN { }
public static Dictionary<Type, HashSet<Type>> Types;
public static Dictionary<Type, HashSet<Type>> TypeCollections;
private static Dictionary<Type, HashSet<Type>> GenerateTypeCollections() {
var result = new Dictionary<Type, HashSet<Type>>();
var all = result.GetOrCreate(typeof(All));
Types.ForEach(x => {
var type = x.Key;
all.Add(type);
x.Value.ForEach(collection => {
var set = result.GetOrCreate(collection);
set.Add(type);
});
});
return result;
}
public static HashSet<Type> AllTypes => GetTypeCollection<All>();
public static HashSet<Type> GetTypeCollection<T>() => GetTypeCollection(typeof(T));
public static HashSet<Type> GetTypeCollection(Type type) {
TypeCollections.TryGetValue(type, out var types);
return types;
}
public static Dictionary<(Type In, Type Out), Func<object, object>> ImplicitConvertions { get; set; }
private static bool IsGenericType(Type In) => TypeLibrary.GetType(In).IsGenericType;
public static bool TryGetImplicitConversion(Type In, Type Out, out Func<object, object> func) {
if (ImplicitConvertions.TryGetValue((In, Out), out func))
return true;
if (IsGenericType(In)) {
var InGeneric = TypeLibrary.GetType(In).TargetType; // GetGenericTypeDefinition
if (ImplicitConvertions.TryGetValue((InGeneric, Out), out func))
return true;
}
return false;
}
public struct Entry: IComparable<Entry> {
[Property] public Type Type { get; private set; }
public Entry(Type type) {
Type = type;
}
public Entry(Type type, IEnumerable<Type> generics) : this(type, [.. generics]) { }
public Entry(Type type, Type[] generics) {
var typeDescription = TypeLibrary.GetType(type);
if (!typeDescription.IsGenericType) {
if (generics.Length > 0)
Log.Warning("Attempt to create an Entry with generics for a non-generic type");
Type = type;
return;
}
Type = typeDescription.MakeGenericType( [.. generics] );
}
public readonly string Name => Type.GetDisplayName();
public readonly bool IsGenericType => TypeLibrary.GetType(Type).IsGenericType;
public readonly Type[] Generics { get {
if (!IsGenericType) {
return null;
}
return TypeLibrary.GetGenericArguments(Type);
} }
public readonly bool IsPolymorphic => TypeLibrary.GetType(Type).HasAttribute<PolymorphicAttribute>(false);
public readonly Type PolymorphParent => TypeLibrary.GetType(Type).GetAttribute<PolymorphicAttribute>(false).Parent;
public readonly bool IsPolymorphRequired => IsPolymorphic && TypeLibrary.GetType(Type).GetAttribute<PolymorphicAttribute>(false).PolymorphRequired;
public readonly bool IsInitialized => TypeLibrary.GetType(Type).HasAttribute<InitializedAttribute>(false);
public readonly int CompareTo(Entry other) {
return Type.GetDisplayName().CompareTo(other.Type.GetDisplayName());
}
[Pure]
[System.Diagnostics.Contracts.Pure]
public readonly Node CreateNode(params object[] args) => Type.CreateClosedGeneric<Node>(args);
}
public static List<Entry> Entries { get; set; } = [];
static Library() {
Reload();
}
[ConCmd("nodebox_reload")]
public static void Reload() {
Types = new(){
{ typeof(bool), new() {
} },
{ typeof(float), new() {
typeof(Float),
typeof(FloatN),
} },
{ typeof(double), new() {
typeof(Float),
typeof(FloatN),
} },
{ typeof(byte), new() {
typeof(IntegerUnsigned),
} },
{ typeof(int), new() {
typeof(Integer),
typeof(IntegerN),
} },
{ typeof(long), new() {
typeof(Integer),
typeof(IntegerN),
} },
{ typeof(char), new() {
typeof(IntegerUnsigned),
} },
{ typeof(string), new() {
} },
{ typeof(Vector2), new() {
typeof(Vector),
typeof(FloatVector),
typeof(FloatN),
} },
{ typeof(Vector3), new() {
typeof(Vector),
typeof(FloatVector),
typeof(FloatN),
} },
{ typeof(Vector4), new() {
typeof(Vector),
typeof(FloatVector),
typeof(FloatN),
} },
{ typeof(Vector2Int), new() {
typeof(Vector),
typeof(IntegerVector),
typeof(IntegerN),
} },
{ typeof(Vector3Int), new() {
typeof(Vector),
typeof(IntegerVector),
typeof(IntegerN),
} },
{ typeof(Angles), new() {
} },
{ typeof(Rotation), new() {
} },
{ typeof(Color), new() {
} },
{ typeof(GameObject), new() {
} },
{ typeof(Reference), new() {
} },
{ typeof(Reference<>), new() {
} },
{ typeof(List<>), new() {
} },
{ typeof(Dictionary<,>), new() {
} },
};
ImplicitConvertions = new() {
{ (typeof(float), typeof(double)), value => Convert.ToDouble(value) },
{ (typeof(float), typeof(byte)), x => Convert.ToByte(x) },
{ (typeof(float), typeof(int)), x => Convert.ToInt32(x) },
{ (typeof(float), typeof(long)), value => Convert.ToInt64(value) },
{ (typeof(double), typeof(float)), value => Convert.ToSingle(value) },
{ (typeof(double), typeof(byte)), value => Convert.ToByte(value) },
{ (typeof(double), typeof(int)), value => Convert.ToInt32(value) },
{ (typeof(double), typeof(long)), value => Convert.ToInt64(value) },
{ (typeof(int), typeof(float)), x => Convert.ToSingle(x) },
{ (typeof(int), typeof(double)), x => Convert.ToDouble(x) },
{ (typeof(int), typeof(byte)), x => Convert.ToByte(x) },
{ (typeof(int), typeof(long)), x => Convert.ToInt64(x) },
{ (typeof(int), typeof(char)), x => Convert.ToChar(x) },
{ (typeof(long), typeof(float)), x => Convert.ToSingle(x) },
{ (typeof(long), typeof(double)), x => Convert.ToDouble(x) },
{ (typeof(long), typeof(byte)), x => Convert.ToByte(x) },
{ (typeof(long), typeof(int)), x => Convert.ToInt32(x) },
{ (typeof(long), typeof(char)), x => Convert.ToChar(x) },
{ (typeof(float), typeof(Vector2)), x => new Vector2((float)x, (float)x) },
{ (typeof(float), typeof(Vector3)), x => new Vector3((float)x, (float)x, (float)x) },
{ (typeof(float), typeof(Vector4)), x => new Vector4((float)x, (float)x, (float)x, (float)x) },
{ (typeof(int), typeof(Vector2Int)), x => new Vector2Int((int)x, (int)x) },
{ (typeof(int), typeof(Vector3Int)), x => new Vector3Int((int)x, (int)x, (int)x) },
{ (typeof(float), typeof(Angles)), x => new Angles((float)x, (float)x, (float)x) },
{ (typeof(float), typeof(Color)), x => new Color((float)x, (float)x, (float)x, 1.0f) },
{ (typeof(Reference<>), typeof(Reference)), x => (Reference)x },
};
TypeCollections = GenerateTypeCollections();
// Log.Info("Type Collections:");
// TypeCollections.Keys.ForEach(Log.Info);
Entries.Clear();
TypeLibrary.GetTypesWithAttribute<RegisterAttribute>(false)
.ForEach(x => {
var typeDescription = x.Type;
var type = typeDescription.TargetType;
if (!typeDescription.IsGenericType) {
Entries.Add(new(type));
return;
}
var generics = x.Attribute.Array;
if (generics.Length == 0) {
Log.Error($"Bad RegisterAttribute on {type.GetDisplayName()}");
return;
}
var collection = generics[0];
var substitutions = GetTypeCollection(collection);
if (substitutions == null) {
Entries.Add(new(type, generics));
return;
}
substitutions
.Select(sub => generics.Select(type => {
if (type == collection) {
return sub;
}
return type;
}))
.ForEach(subGenerics => {
try {
Entries.Add(new(type, subGenerics));
}
catch (Exception e) {
var genericsStr = string.Join(", ", subGenerics.Select(x => x.GetDisplayName()));
Log.Warning($"Couldn't register {type.GetDisplayName()} with {genericsStr}!");
Log.Warning(e);
}
});
});
Entries.Sort();
}
}