mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Nodes - Implemented saving/loading
Nodes - Added data model node
This commit is contained in:
parent
61d7f1e1fb
commit
afc4bee7ac
@ -92,5 +92,17 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int IndexOf<T>(this IReadOnlyCollection<T> self, T elementToFind)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
foreach (T element in self)
|
||||||
|
{
|
||||||
|
if (Equals(element, elementToFind))
|
||||||
|
return i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,7 +72,12 @@ namespace Artemis.Core
|
|||||||
SubscribeToDataModelStore();
|
SubscribeToDataModelStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DataModelPath(DataModel? target, DataModelPathEntity entity)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="DataModelPath" /> class based on a <see cref="DataModelPathEntity" />
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target"></param>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
public DataModelPath(DataModel? target, DataModelPathEntity entity)
|
||||||
{
|
{
|
||||||
Target = target;
|
Target = target;
|
||||||
Path = entity.Path;
|
Path = entity.Path;
|
||||||
@ -110,7 +115,10 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyCollection<DataModelPathSegment> Segments => _segments.ToList().AsReadOnly();
|
public IReadOnlyCollection<DataModelPathSegment> Segments => _segments.ToList().AsReadOnly();
|
||||||
|
|
||||||
internal DataModelPathEntity Entity { get; }
|
/// <summary>
|
||||||
|
/// Gets the entity used for persistent storage
|
||||||
|
/// </summary>
|
||||||
|
public DataModelPathEntity Entity { get; }
|
||||||
|
|
||||||
internal Func<object, object>? Accessor { get; private set; }
|
internal Func<object, object>? Accessor { get; private set; }
|
||||||
|
|
||||||
@ -173,6 +181,52 @@ namespace Artemis.Core
|
|||||||
return string.IsNullOrWhiteSpace(Path) ? "this" : Path;
|
return string.IsNullOrWhiteSpace(Path) ? "this" : Path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs whenever the path becomes invalid
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler? PathInvalidated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs whenever the path becomes valid
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler? PathValidated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">
|
||||||
|
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||||
|
/// <see langword="false" /> to release only unmanaged resources.
|
||||||
|
/// </param>
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||||
|
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the <see cref="PathInvalidated" /> event
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnPathValidated()
|
||||||
|
{
|
||||||
|
PathValidated?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invokes the <see cref="PathValidated" /> event
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnPathInvalidated()
|
||||||
|
{
|
||||||
|
PathInvalidated?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
internal void Invalidate()
|
internal void Invalidate()
|
||||||
{
|
{
|
||||||
Target?.RemoveDataModelPath(this);
|
Target?.RemoveDataModelPath(this);
|
||||||
@ -263,26 +317,23 @@ namespace Artemis.Core
|
|||||||
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable
|
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing">
|
|
||||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
|
||||||
/// <see langword="false" /> to release only unmanaged resources.
|
|
||||||
/// </param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
||||||
{
|
return;
|
||||||
_disposed = true;
|
|
||||||
|
|
||||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
Invalidate();
|
||||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
Target = e.Registration.DataModel;
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
Invalidate();
|
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
|
||||||
}
|
{
|
||||||
|
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Invalidate();
|
||||||
|
Target = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -292,8 +343,6 @@ namespace Artemis.Core
|
|||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Storage
|
#region Storage
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -324,58 +373,5 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Event handlers
|
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
|
|
||||||
{
|
|
||||||
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Invalidate();
|
|
||||||
Target = e.Registration.DataModel;
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
|
|
||||||
{
|
|
||||||
if (e.Registration.DataModel.Module.Id != Entity.DataModelId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Invalidate();
|
|
||||||
Target = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Events
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs whenever the path becomes invalid
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler? PathInvalidated;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs whenever the path becomes valid
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler? PathValidated;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes the <see cref="PathInvalidated" /> event
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnPathValidated()
|
|
||||||
{
|
|
||||||
PathValidated?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invokes the <see cref="PathValidated" /> event
|
|
||||||
/// </summary>
|
|
||||||
protected virtual void OnPathInvalidated()
|
|
||||||
{
|
|
||||||
PathInvalidated?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7,6 +7,7 @@ using Artemis.Core.LayerEffects.Placeholder;
|
|||||||
using Artemis.Core.Properties;
|
using Artemis.Core.Properties;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
@ -66,6 +67,8 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal void LoadRenderElement()
|
internal void LoadRenderElement()
|
||||||
{
|
{
|
||||||
|
DisplayCondition = RenderElementEntity.NodeScript != null ? new NodeScript<bool>(RenderElementEntity.NodeScript) : null;
|
||||||
|
|
||||||
// DisplayCondition = RenderElementEntity.DisplayCondition != null
|
// DisplayCondition = RenderElementEntity.DisplayCondition != null
|
||||||
// ? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
|
// ? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
|
||||||
// : new DataModelConditionGroup(null);
|
// : new DataModelConditionGroup(null);
|
||||||
@ -97,6 +100,8 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Conditions
|
// Conditions
|
||||||
|
DisplayCondition?.Save();
|
||||||
|
RenderElementEntity.NodeScript = DisplayCondition?.Entity;
|
||||||
// RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
|
// RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
|
||||||
// DisplayCondition?.Save();
|
// DisplayCondition?.Save();
|
||||||
|
|
||||||
@ -126,7 +131,7 @@ namespace Artemis.Core
|
|||||||
// The play mode dictates whether to stick to the main segment unless the display conditions contains events
|
// The play mode dictates whether to stick to the main segment unless the display conditions contains events
|
||||||
bool stickToMainSegment = (Timeline.PlayMode == TimelinePlayMode.Repeat || Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle) && DisplayConditionMet;
|
bool stickToMainSegment = (Timeline.PlayMode == TimelinePlayMode.Repeat || Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle) && DisplayConditionMet;
|
||||||
// if (DisplayCondition != null && DisplayCondition.ContainsEvents && Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle)
|
// if (DisplayCondition != null && DisplayCondition.ContainsEvents && Timeline.EventOverlapMode != TimeLineEventOverlapMode.Toggle)
|
||||||
// stickToMainSegment = false;
|
// stickToMainSegment = false;
|
||||||
|
|
||||||
Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
|
Timeline.Update(TimeSpan.FromSeconds(deltaTime), stickToMainSegment);
|
||||||
}
|
}
|
||||||
@ -388,48 +393,49 @@ namespace Artemis.Core
|
|||||||
_toggledOnByEvent = false;
|
_toggledOnByEvent = false;
|
||||||
|
|
||||||
DisplayCondition.Run();
|
DisplayCondition.Run();
|
||||||
bool conditionMet = DisplayCondition.Result;
|
|
||||||
|
// TODO: Handle this nicely, right now when there's only an exit node we assume true
|
||||||
|
bool conditionMet = DisplayCondition.Nodes.Count() == 1 || DisplayCondition.Result;
|
||||||
if (Parent is RenderProfileElement parent && !parent.DisplayConditionMet)
|
if (Parent is RenderProfileElement parent && !parent.DisplayConditionMet)
|
||||||
conditionMet = false;
|
conditionMet = false;
|
||||||
|
|
||||||
// if (!DisplayCondition.ContainsEvents)
|
// if (!DisplayCondition.ContainsEvents)
|
||||||
// {
|
|
||||||
// // Regular conditions reset the timeline whenever their condition is met and was not met before that
|
|
||||||
// if (conditionMet && !DisplayConditionMet && Timeline.IsFinished)
|
|
||||||
// Timeline.JumpToStart();
|
|
||||||
// // If regular conditions are no longer met, jump to the end segment if stop mode requires it
|
|
||||||
// if (!conditionMet && Timeline.StopMode == TimelineStopMode.SkipToEnd)
|
|
||||||
// Timeline.JumpToEndSegment();
|
|
||||||
// }
|
|
||||||
// else if (conditionMet)
|
|
||||||
if (conditionMet)
|
|
||||||
{
|
{
|
||||||
if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle)
|
// Regular conditions reset the timeline whenever their condition is met and was not met before that
|
||||||
{
|
if (conditionMet && !DisplayConditionMet && Timeline.IsFinished)
|
||||||
_toggledOnByEvent = !_toggledOnByEvent;
|
Timeline.JumpToStart();
|
||||||
if (_toggledOnByEvent)
|
// If regular conditions are no longer met, jump to the end segment if stop mode requires it
|
||||||
Timeline.JumpToStart();
|
if (!conditionMet && Timeline.StopMode == TimelineStopMode.SkipToEnd)
|
||||||
}
|
Timeline.JumpToEndSegment();
|
||||||
else
|
|
||||||
{
|
|
||||||
// Event conditions reset if the timeline finished
|
|
||||||
if (Timeline.IsFinished)
|
|
||||||
{
|
|
||||||
Timeline.JumpToStart();
|
|
||||||
}
|
|
||||||
// and otherwise apply their overlap mode
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
|
||||||
Timeline.JumpToStart();
|
|
||||||
else if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Copy)
|
|
||||||
Timeline.AddExtraTimeline();
|
|
||||||
// The third option is ignore which is handled below:
|
|
||||||
|
|
||||||
// done
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// else if (conditionMet)
|
||||||
|
// {
|
||||||
|
// if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle)
|
||||||
|
// {
|
||||||
|
// _toggledOnByEvent = !_toggledOnByEvent;
|
||||||
|
// if (_toggledOnByEvent)
|
||||||
|
// Timeline.JumpToStart();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// // Event conditions reset if the timeline finished
|
||||||
|
// if (Timeline.IsFinished)
|
||||||
|
// {
|
||||||
|
// Timeline.JumpToStart();
|
||||||
|
// }
|
||||||
|
// // and otherwise apply their overlap mode
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Restart)
|
||||||
|
// Timeline.JumpToStart();
|
||||||
|
// else if (Timeline.EventOverlapMode == TimeLineEventOverlapMode.Copy)
|
||||||
|
// Timeline.AddExtraTimeline();
|
||||||
|
// // The third option is ignore which is handled below:
|
||||||
|
//
|
||||||
|
// // done
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
DisplayConditionMet = Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle
|
DisplayConditionMet = Timeline.EventOverlapMode == TimeLineEventOverlapMode.Toggle
|
||||||
? _toggledOnByEvent
|
? _toggledOnByEvent
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using Artemis.Storage.Entities.Profile.Nodes;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
using Ninject.Parameters;
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
@ -44,15 +46,25 @@ namespace Artemis.Core.Services
|
|||||||
string description = nodeAttribute?.Description ?? string.Empty;
|
string description = nodeAttribute?.Description ?? string.Empty;
|
||||||
string category = nodeAttribute?.Category ?? string.Empty;
|
string category = nodeAttribute?.Category ?? string.Empty;
|
||||||
|
|
||||||
NodeData nodeData = new(plugin, nodeType, name, description, category, () => CreateNode(nodeType));
|
NodeData nodeData = new(plugin, nodeType, name, description, category, (e) => CreateNode(e, nodeType));
|
||||||
return NodeTypeStore.Add(nodeData);
|
return NodeTypeStore.Add(nodeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private INode CreateNode(Type nodeType)
|
private INode CreateNode(NodeEntity? entity, Type nodeType)
|
||||||
{
|
{
|
||||||
INode node = _kernel.Get(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
|
INode node = _kernel.Get(nodeType) as INode ?? throw new InvalidOperationException($"Node {nodeType} is not an INode");
|
||||||
|
|
||||||
|
if (entity != null)
|
||||||
|
{
|
||||||
|
node.X = entity.X;
|
||||||
|
node.Y = entity.Y;
|
||||||
|
node.Storage = entity.Storage;
|
||||||
|
}
|
||||||
|
|
||||||
if (node is CustomViewModelNode customViewModelNode)
|
if (node is CustomViewModelNode customViewModelNode)
|
||||||
customViewModelNode.BaseCustomViewModel = _kernel.Get(customViewModelNode.CustomViewModelType);
|
customViewModelNode.BaseCustomViewModel = _kernel.Get(customViewModelNode.CustomViewModelType, new ConstructorArgument("node", node));
|
||||||
|
|
||||||
|
node.Initialize();
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ namespace Artemis.Core
|
|||||||
Registrations.Add(typeRegistration);
|
Registrations.Add(typeRegistration);
|
||||||
}
|
}
|
||||||
|
|
||||||
OnDataBindingModifierAdded(new NodeTypeStoreEvent(typeRegistration));
|
OnNodeTypeAdded(new NodeTypeStoreEvent(typeRegistration));
|
||||||
return typeRegistration;
|
return typeRegistration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ namespace Artemis.Core
|
|||||||
typeRegistration.IsInStore = false;
|
typeRegistration.IsInStore = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
OnDataBindingModifierRemoved(new NodeTypeStoreEvent(typeRegistration));
|
OnNodeTypeRemoved(new NodeTypeStoreEvent(typeRegistration));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IEnumerable<NodeData> GetAll()
|
public static IEnumerable<NodeData> GetAll()
|
||||||
@ -59,19 +59,27 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
public static event EventHandler<NodeTypeStoreEvent>? DataBindingModifierAdded;
|
public static event EventHandler<NodeTypeStoreEvent>? NodeTypeAdded;
|
||||||
public static event EventHandler<NodeTypeStoreEvent>? DataBindingModifierRemoved;
|
public static event EventHandler<NodeTypeStoreEvent>? NodeTypeRemoved;
|
||||||
|
|
||||||
private static void OnDataBindingModifierAdded(NodeTypeStoreEvent e)
|
private static void OnNodeTypeAdded(NodeTypeStoreEvent e)
|
||||||
{
|
{
|
||||||
DataBindingModifierAdded?.Invoke(null, e);
|
NodeTypeAdded?.Invoke(null, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnDataBindingModifierRemoved(NodeTypeStoreEvent e)
|
private static void OnNodeTypeRemoved(NodeTypeStoreEvent e)
|
||||||
{
|
{
|
||||||
DataBindingModifierRemoved?.Invoke(null, e);
|
NodeTypeRemoved?.Invoke(null, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public static Plugin? GetPlugin(INode node)
|
||||||
|
{
|
||||||
|
lock (Registrations)
|
||||||
|
{
|
||||||
|
return Registrations.FirstOrDefault(r => r.Plugin.GetType().Assembly == node.GetType().Assembly)?.Plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/Artemis.Core/VisualScripting/CustomNodeViewModel.cs
Normal file
15
src/Artemis.Core/VisualScripting/CustomNodeViewModel.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
public class CustomNodeViewModel : CorePropertyChanged
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public INode Node { get; }
|
||||||
|
|
||||||
|
public CustomNodeViewModel(INode node)
|
||||||
|
{
|
||||||
|
Node = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
@ -32,6 +33,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
internal InputPin(INode node, string name)
|
internal InputPin(INode node, string name)
|
||||||
: base(node, name)
|
: base(node, name)
|
||||||
{ }
|
{ }
|
||||||
|
|||||||
@ -12,12 +12,14 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
public double X { get; set; }
|
public double X { get; set; }
|
||||||
public double Y { get; set; }
|
public double Y { get; set; }
|
||||||
|
public object Storage { get; set; }
|
||||||
|
|
||||||
public IReadOnlyCollection<IPin> Pins { get; }
|
public IReadOnlyCollection<IPin> Pins { get; }
|
||||||
public IReadOnlyCollection<IPinCollection> PinCollections { get; }
|
public IReadOnlyCollection<IPinCollection> PinCollections { get; }
|
||||||
|
|
||||||
event EventHandler Resetting;
|
event EventHandler Resetting;
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
void Evaluate();
|
void Evaluate();
|
||||||
void Reset();
|
void Reset();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,6 +42,14 @@ namespace Artemis.Core
|
|||||||
set => SetAndNotify(ref _y, value);
|
set => SetAndNotify(ref _y, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object? _storage;
|
||||||
|
|
||||||
|
public object? Storage
|
||||||
|
{
|
||||||
|
get => _storage;
|
||||||
|
set => SetAndNotify(ref _storage, value);
|
||||||
|
}
|
||||||
|
|
||||||
public virtual bool IsExitNode => false;
|
public virtual bool IsExitNode => false;
|
||||||
|
|
||||||
private readonly List<IPin> _pins = new();
|
private readonly List<IPin> _pins = new();
|
||||||
@ -128,6 +136,8 @@ namespace Artemis.Core
|
|||||||
return pin;
|
return pin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void Initialize() { }
|
||||||
|
|
||||||
public abstract void Evaluate();
|
public abstract void Evaluate();
|
||||||
|
|
||||||
public virtual void Reset()
|
public virtual void Reset()
|
||||||
@ -138,7 +148,7 @@ namespace Artemis.Core
|
|||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class Node<T> : CustomViewModelNode
|
public abstract class Node<T> : CustomViewModelNode where T : CustomNodeViewModel
|
||||||
{
|
{
|
||||||
protected Node()
|
protected Node()
|
||||||
{
|
{
|
||||||
@ -149,7 +159,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override Type CustomViewModelType => typeof(T);
|
public override Type CustomViewModelType => typeof(T);
|
||||||
public T? CustomViewModel => (T?) BaseCustomViewModel;
|
public T CustomViewModel => (T) BaseCustomViewModel!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class CustomViewModelNode : Node
|
public abstract class CustomViewModelNode : Node
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Artemis.Storage.Entities.Profile.Nodes;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
@ -13,13 +14,13 @@ namespace Artemis.Core
|
|||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
public string Category { get; }
|
public string Category { get; }
|
||||||
|
|
||||||
private Func<INode> _create;
|
private Func<NodeEntity?, INode> _create;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Func<INode> create)
|
internal NodeData(Plugin plugin, Type type, string name, string description, string category, Func<NodeEntity?, INode>? create)
|
||||||
{
|
{
|
||||||
this.Plugin = plugin;
|
this.Plugin = plugin;
|
||||||
this.Type = type;
|
this.Type = type;
|
||||||
@ -33,7 +34,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#region Methods
|
#region Methods
|
||||||
|
|
||||||
public INode CreateNode() => _create();
|
public INode CreateNode(NodeEntity? entity) => _create(entity);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
using Artemis.Core.Internal;
|
using Artemis.Core.Internal;
|
||||||
using Artemis.Core.Properties;
|
using Artemis.Core.Properties;
|
||||||
|
using Artemis.Storage.Entities.Profile.Nodes;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
public abstract class NodeScript : CorePropertyChanged, INodeScript
|
public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageModel
|
||||||
{
|
{
|
||||||
#region Properties & Fields
|
#region Properties & Fields
|
||||||
|
|
||||||
|
internal NodeScriptEntity Entity { get; }
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public string Description { get; }
|
public string Description { get; }
|
||||||
|
|
||||||
@ -18,6 +22,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
protected INode ExitNode { get; set; }
|
protected INode ExitNode { get; set; }
|
||||||
public abstract Type ResultType { get; }
|
public abstract Type ResultType { get; }
|
||||||
|
public abstract void CreateExitNode(string name, string description = "");
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@ -27,6 +32,22 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
this.Name = name;
|
this.Name = name;
|
||||||
this.Description = description;
|
this.Description = description;
|
||||||
|
this.Entity = new NodeScriptEntity();
|
||||||
|
|
||||||
|
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
|
||||||
|
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal NodeScript(NodeScriptEntity entity)
|
||||||
|
{
|
||||||
|
this.Name = entity.Name;
|
||||||
|
this.Description = entity.Description;
|
||||||
|
this.Entity = entity;
|
||||||
|
|
||||||
|
Load();
|
||||||
|
|
||||||
|
NodeTypeStore.NodeTypeAdded += NodeTypeStoreOnNodeTypeChanged;
|
||||||
|
NodeTypeStore.NodeTypeRemoved += NodeTypeStoreOnNodeTypeChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -52,7 +73,201 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{ }
|
{
|
||||||
|
NodeTypeStore.NodeTypeAdded -= NodeTypeStoreOnNodeTypeChanged;
|
||||||
|
NodeTypeStore.NodeTypeRemoved -= NodeTypeStoreOnNodeTypeChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Implementation of IStorageModel
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
bool gotExitNode = false;
|
||||||
|
|
||||||
|
// Create nodes
|
||||||
|
Dictionary<int, INode> nodes = new();
|
||||||
|
foreach (NodeEntity entityNode in Entity.Nodes)
|
||||||
|
{
|
||||||
|
INode? node = LoadNode(entityNode);
|
||||||
|
if (node == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (node.IsExitNode)
|
||||||
|
gotExitNode = true;
|
||||||
|
|
||||||
|
nodes.Add(entityNode.Id, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gotExitNode)
|
||||||
|
CreateExitNode("Exit node");
|
||||||
|
|
||||||
|
LoadConnections(nodes);
|
||||||
|
|
||||||
|
_nodes.Clear();
|
||||||
|
_nodes.AddRange(nodes.Values);
|
||||||
|
}
|
||||||
|
|
||||||
|
private INode? LoadNode(NodeEntity nodeEntity)
|
||||||
|
{
|
||||||
|
INode node;
|
||||||
|
if (nodeEntity.IsExitNode)
|
||||||
|
{
|
||||||
|
CreateExitNode(nodeEntity.Name, nodeEntity.Description);
|
||||||
|
node = ExitNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NodeTypeRegistration? nodeTypeRegistration = NodeTypeStore.Get(nodeEntity.PluginId, nodeEntity.Type);
|
||||||
|
if (nodeTypeRegistration == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Create the node
|
||||||
|
node = nodeTypeRegistration.NodeData.CreateNode(nodeEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore pin collections
|
||||||
|
foreach (NodePinCollectionEntity entityNodePinCollection in nodeEntity.PinCollections)
|
||||||
|
{
|
||||||
|
IPinCollection? collection = node.PinCollections.FirstOrDefault(c => c.Name == entityNodePinCollection.Name &&
|
||||||
|
c.Type.Name == entityNodePinCollection.Type &&
|
||||||
|
(int) c.Direction == entityNodePinCollection.Direction);
|
||||||
|
if (collection == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
while (collection.Count() < entityNodePinCollection.Amount)
|
||||||
|
collection.AddPin();
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadConnections(Dictionary<int, INode> nodes)
|
||||||
|
{
|
||||||
|
foreach (NodeConnectionEntity nodeConnectionEntity in Entity.Connections)
|
||||||
|
{
|
||||||
|
// Find the source and target node
|
||||||
|
if (!nodes.TryGetValue(nodeConnectionEntity.SourceNode, out INode? source) || !nodes.TryGetValue(nodeConnectionEntity.TargetNode, out INode? target))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
IPin? sourcePin = nodeConnectionEntity.SourcePinCollectionId == -1
|
||||||
|
? source.Pins.ElementAtOrDefault(nodeConnectionEntity.SourcePinId)
|
||||||
|
: source.PinCollections.ElementAtOrDefault(nodeConnectionEntity.SourcePinCollectionId)?.ElementAtOrDefault(nodeConnectionEntity.SourcePinId);
|
||||||
|
IPin? targetPin = nodeConnectionEntity.TargetPinCollectionId == -1
|
||||||
|
? target.Pins.ElementAtOrDefault(nodeConnectionEntity.TargetPinId)
|
||||||
|
: target.PinCollections.ElementAtOrDefault(nodeConnectionEntity.TargetPinCollectionId)?.ElementAtOrDefault(nodeConnectionEntity.TargetPinId);
|
||||||
|
|
||||||
|
// Ensure both nodes have the required pins
|
||||||
|
if (sourcePin == null || targetPin == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
targetPin.ConnectTo(sourcePin);
|
||||||
|
sourcePin.ConnectTo(targetPin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
Entity.Name = Name;
|
||||||
|
Entity.Description = Description;
|
||||||
|
|
||||||
|
Entity.Nodes.Clear();
|
||||||
|
int id = 0;
|
||||||
|
|
||||||
|
Dictionary<INode, int> nodes = new();
|
||||||
|
foreach (INode node in Nodes)
|
||||||
|
{
|
||||||
|
NodeEntity nodeEntity = new()
|
||||||
|
{
|
||||||
|
Id = id,
|
||||||
|
PluginId = NodeTypeStore.GetPlugin(node)?.Guid ?? Constants.CorePlugin.Guid,
|
||||||
|
Type = node.GetType().Name,
|
||||||
|
X = node.X,
|
||||||
|
Y = node.Y,
|
||||||
|
Storage = node.Storage,
|
||||||
|
Name = node.Name,
|
||||||
|
Description = node.Description,
|
||||||
|
IsExitNode = node.IsExitNode
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (IPinCollection nodePinCollection in node.PinCollections)
|
||||||
|
{
|
||||||
|
nodeEntity.PinCollections.Add(new NodePinCollectionEntity
|
||||||
|
{
|
||||||
|
Name = nodePinCollection.Name,
|
||||||
|
Type = nodePinCollection.Type.Name,
|
||||||
|
Direction = (int) nodePinCollection.Direction,
|
||||||
|
Amount = nodePinCollection.Count()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity.Nodes.Add(nodeEntity);
|
||||||
|
nodes.Add(node, id);
|
||||||
|
id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store connections
|
||||||
|
Entity.Connections.Clear();
|
||||||
|
foreach (INode node in Nodes)
|
||||||
|
{
|
||||||
|
SavePins(nodes, node, -1, node.Pins);
|
||||||
|
|
||||||
|
int pinCollectionId = 0;
|
||||||
|
foreach (IPinCollection pinCollection in node.PinCollections)
|
||||||
|
{
|
||||||
|
SavePins(nodes, node, pinCollectionId, pinCollection);
|
||||||
|
pinCollectionId++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SavePins(Dictionary<INode, int> nodes, INode node, int collectionId, IEnumerable<IPin> pins)
|
||||||
|
{
|
||||||
|
int sourcePinId = 0;
|
||||||
|
foreach (IPin sourcePin in pins.Where(p => p.Direction == PinDirection.Input))
|
||||||
|
{
|
||||||
|
foreach (IPin targetPin in sourcePin.ConnectedTo)
|
||||||
|
{
|
||||||
|
int targetPinCollectionId = -1;
|
||||||
|
int targetPinId;
|
||||||
|
|
||||||
|
IPinCollection? targetCollection = targetPin.Node.PinCollections.FirstOrDefault(c => c.Contains(targetPin));
|
||||||
|
if (targetCollection != null)
|
||||||
|
{
|
||||||
|
targetPinCollectionId = targetPin.Node.PinCollections.IndexOf(targetCollection);
|
||||||
|
targetPinId = targetCollection.ToList().IndexOf(targetPin);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
targetPinId = targetPin.Node.Pins.IndexOf(targetPin);
|
||||||
|
|
||||||
|
Entity.Connections.Add(new NodeConnectionEntity()
|
||||||
|
{
|
||||||
|
SourceType = sourcePin.Type.Name,
|
||||||
|
SourceNode = nodes[node],
|
||||||
|
SourcePinCollectionId = collectionId,
|
||||||
|
SourcePinId = sourcePinId,
|
||||||
|
TargetType = targetPin.Type.Name,
|
||||||
|
TargetNode = nodes[targetPin.Node],
|
||||||
|
TargetPinCollectionId = targetPinCollectionId,
|
||||||
|
TargetPinId = targetPinId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sourcePinId++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Event handlers
|
||||||
|
|
||||||
|
private void NodeTypeStoreOnNodeTypeChanged(object? sender, NodeTypeStoreEvent e)
|
||||||
|
{
|
||||||
|
Load();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
@ -61,14 +276,24 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
#region Properties & Fields
|
#region Properties & Fields
|
||||||
|
|
||||||
public T Result => ((ExitNode<T>)ExitNode).Value;
|
public T Result => ((ExitNode<T>) ExitNode).Value;
|
||||||
|
|
||||||
public override Type ResultType => typeof(T);
|
public override Type ResultType => typeof(T);
|
||||||
|
|
||||||
|
public override void CreateExitNode(string name, string description = "")
|
||||||
|
{
|
||||||
|
ExitNode = new ExitNode<T>(name, description);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
|
internal NodeScript(NodeScriptEntity entity)
|
||||||
|
: base(entity)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
public NodeScript(string name, string description)
|
public NodeScript(string name, string description)
|
||||||
: base(name, description)
|
: base(name, description)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
@ -31,6 +32,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#region Constructors
|
#region Constructors
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
internal OutputPin(INode node, string name)
|
internal OutputPin(INode node, string name)
|
||||||
: base(node, name)
|
: base(node, name)
|
||||||
{ }
|
{ }
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
|
using Artemis.Storage.Entities.Profile.Nodes;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile.Abstract
|
namespace Artemis.Storage.Entities.Profile.Abstract
|
||||||
{
|
{
|
||||||
@ -15,5 +16,7 @@ namespace Artemis.Storage.Entities.Profile.Abstract
|
|||||||
|
|
||||||
public DataModelConditionGroupEntity DisplayCondition { get; set; }
|
public DataModelConditionGroupEntity DisplayCondition { get; set; }
|
||||||
public TimelineEntity Timeline { get; set; }
|
public TimelineEntity Timeline { get; set; }
|
||||||
|
|
||||||
|
public NodeScriptEntity NodeScript { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
namespace Artemis.Storage.Entities.Profile.Nodes
|
||||||
|
{
|
||||||
|
public class NodeConnectionEntity
|
||||||
|
{
|
||||||
|
public string SourceType { get; set; }
|
||||||
|
public int SourceNode { get; set; }
|
||||||
|
public int TargetNode { get; set; }
|
||||||
|
public int SourcePinCollectionId { get; set; }
|
||||||
|
public int SourcePinId { get; set; }
|
||||||
|
public string TargetType { get; set; }
|
||||||
|
public int TargetPinCollectionId { get; set; }
|
||||||
|
public int TargetPinId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/Artemis.Storage/Entities/Profile/Nodes/NodeEntity.cs
Normal file
26
src/Artemis.Storage/Entities/Profile/Nodes/NodeEntity.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Entities.Profile.Nodes
|
||||||
|
{
|
||||||
|
public class NodeEntity
|
||||||
|
{
|
||||||
|
public NodeEntity()
|
||||||
|
{
|
||||||
|
PinCollections = new List<NodePinCollectionEntity>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Id { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public Guid PluginId { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public bool IsExitNode { get; set; }
|
||||||
|
public double X { get; set; }
|
||||||
|
public double Y { get; set; }
|
||||||
|
public object Storage { get; set; }
|
||||||
|
|
||||||
|
public List<NodePinCollectionEntity> PinCollections { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace Artemis.Storage.Entities.Profile.Nodes
|
||||||
|
{
|
||||||
|
public class NodePinCollectionEntity
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Type { get; set; }
|
||||||
|
public int Direction { set; get; }
|
||||||
|
public int Amount { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Entities.Profile.Nodes
|
||||||
|
{
|
||||||
|
public class NodeScriptEntity
|
||||||
|
{
|
||||||
|
public NodeScriptEntity()
|
||||||
|
{
|
||||||
|
Nodes = new List<NodeEntity>();
|
||||||
|
Connections = new List<NodeConnectionEntity>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
public List<NodeEntity> Nodes { get; set; }
|
||||||
|
public List<NodeConnectionEntity> Connections { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1482,6 +1482,7 @@
|
|||||||
"type": "Project",
|
"type": "Project",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"Artemis.Core": "1.0.0",
|
"Artemis.Core": "1.0.0",
|
||||||
|
"Artemis.UI.Shared": "2.0.0",
|
||||||
"JetBrains.Annotations": "2021.1.0",
|
"JetBrains.Annotations": "2021.1.0",
|
||||||
"Stylet": "1.3.6"
|
"Stylet": "1.3.6"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<PlatformTarget>x64</PlatformTarget>
|
<PlatformTarget>x64</PlatformTarget>
|
||||||
<DocumentationFile>bin\Artemis.VisualScripting.xml</DocumentationFile>
|
<DocumentationFile></DocumentationFile>
|
||||||
<NoWarn></NoWarn>
|
<NoWarn></NoWarn>
|
||||||
<WarningLevel>5</WarningLevel>
|
<WarningLevel>5</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -42,6 +42,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
<ProjectReference Include="..\Artemis.Core\Artemis.Core.csproj" />
|
||||||
|
<ProjectReference Include="..\Artemis.UI.Shared\Artemis.UI.Shared.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -383,7 +383,7 @@ namespace Artemis.VisualScripting.Editor.Controls
|
|||||||
if (_creationBoxParent.ContextMenu != null)
|
if (_creationBoxParent.ContextMenu != null)
|
||||||
_creationBoxParent.ContextMenu.IsOpen = false;
|
_creationBoxParent.ContextMenu.IsOpen = false;
|
||||||
|
|
||||||
INode node = nodeData.CreateNode();
|
INode node = nodeData.CreateNode(null);
|
||||||
Script.AddNode(node);
|
Script.AddNode(node);
|
||||||
|
|
||||||
InitializeNode(node, _lastRightClickLocation);
|
InitializeNode(node, _lastRightClickLocation);
|
||||||
|
|||||||
@ -207,9 +207,18 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<!-- TODO: Figure out how to only use this for CustomViewModelNode -->
|
|
||||||
<Border x:Name="BrdCustomView" Grid.Column="1">
|
<Border x:Name="BrdCustomView" Grid.Column="1">
|
||||||
<ContentControl s:View.Model="{Binding Node.CustomViewModel}"/>
|
<Border.Resources>
|
||||||
|
<DataTemplate DataType="{x:Type core:CustomViewModelNode}">
|
||||||
|
<ContentControl s:View.Model="{Binding BaseCustomViewModel}"/>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<DataTemplate DataType="{x:Type core:Node}">
|
||||||
|
<Border />
|
||||||
|
</DataTemplate>
|
||||||
|
</Border.Resources>
|
||||||
|
|
||||||
|
<ContentControl Content="{Binding Node}" />
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Border x:Name="BrdOutputPins" Grid.Column="2">
|
<Border x:Name="BrdOutputPins" Grid.Column="2">
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.UI.Shared;
|
||||||
|
using Artemis.UI.Shared.Input;
|
||||||
|
using Artemis.UI.Shared.Services;
|
||||||
|
using Stylet;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||||
|
{
|
||||||
|
public class DataModelNodeCustomViewModel : CustomNodeViewModel
|
||||||
|
{
|
||||||
|
private readonly DataModelNode _node;
|
||||||
|
|
||||||
|
public DataModelNodeCustomViewModel(DataModelNode node, IDataModelUIService dataModelUIService) : base(node)
|
||||||
|
{
|
||||||
|
_node = node;
|
||||||
|
|
||||||
|
Execute.OnUIThreadSync(() =>
|
||||||
|
{
|
||||||
|
SelectionViewModel = dataModelUIService.GetDynamicSelectionViewModel(module: null);
|
||||||
|
SelectionViewModel.PropertySelected += SelectionViewModelOnPropertySelected;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataModelDynamicViewModel SelectionViewModel { get; set; }
|
||||||
|
|
||||||
|
private void SelectionViewModelOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e)
|
||||||
|
{
|
||||||
|
_node.DataModelPath = SelectionViewModel.DataModelPath;
|
||||||
|
if (_node.DataModelPath != null)
|
||||||
|
{
|
||||||
|
_node.DataModelPath.Save();
|
||||||
|
_node.Storage = _node.DataModelPath.Entity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_node.Storage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_node.UpdateOutputPin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +1,24 @@
|
|||||||
using Stylet;
|
using Artemis.Core;
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||||
{
|
{
|
||||||
public class StaticDoubleValueNodeCustomViewModel : PropertyChangedBase
|
public class StaticDoubleValueNodeCustomViewModel : CustomNodeViewModel
|
||||||
{
|
{
|
||||||
private double _input;
|
private readonly StaticDoubleValueNode _node;
|
||||||
|
|
||||||
|
public StaticDoubleValueNodeCustomViewModel(StaticDoubleValueNode node) : base(node)
|
||||||
|
{
|
||||||
|
_node = node;
|
||||||
|
}
|
||||||
|
|
||||||
public double Input
|
public double Input
|
||||||
{
|
{
|
||||||
get => _input;
|
get => (double) _node.Storage;
|
||||||
set => SetAndNotify(ref _input, value);
|
set
|
||||||
|
{
|
||||||
|
_node.Storage = value;
|
||||||
|
OnPropertyChanged(nameof(Input));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,15 +1,24 @@
|
|||||||
using Stylet;
|
using Artemis.Core;
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||||
{
|
{
|
||||||
public class StaticIntegerValueNodeCustomViewModel : PropertyChangedBase
|
public class StaticIntegerValueNodeCustomViewModel : CustomNodeViewModel
|
||||||
{
|
{
|
||||||
private int _input;
|
private readonly StaticIntegerValueNode _node;
|
||||||
|
|
||||||
|
public StaticIntegerValueNodeCustomViewModel(StaticIntegerValueNode node) : base(node)
|
||||||
|
{
|
||||||
|
_node = node;
|
||||||
|
}
|
||||||
|
|
||||||
public int Input
|
public int Input
|
||||||
{
|
{
|
||||||
get => _input;
|
get => (int)(long) _node.Storage;
|
||||||
set => SetAndNotify(ref _input, value);
|
set
|
||||||
|
{
|
||||||
|
_node.Storage = value;
|
||||||
|
OnPropertyChanged(nameof(Input));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,15 +1,24 @@
|
|||||||
using Stylet;
|
using Artemis.Core;
|
||||||
|
|
||||||
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
namespace Artemis.VisualScripting.Nodes.CustomViewModels
|
||||||
{
|
{
|
||||||
public class StaticStringValueNodeCustomViewModel : PropertyChangedBase
|
public class StaticStringValueNodeCustomViewModel : CustomNodeViewModel
|
||||||
{
|
{
|
||||||
private string _input;
|
private readonly StaticStringValueNode _node;
|
||||||
|
|
||||||
|
public StaticStringValueNodeCustomViewModel(StaticStringValueNode node) : base(node)
|
||||||
|
{
|
||||||
|
_node = node;
|
||||||
|
}
|
||||||
|
|
||||||
public string Input
|
public string Input
|
||||||
{
|
{
|
||||||
get => _input;
|
get => (string) _node.Storage;
|
||||||
set => SetAndNotify(ref _input, value);
|
set
|
||||||
|
{
|
||||||
|
_node.Storage = value;
|
||||||
|
OnPropertyChanged(nameof(Input));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
<UserControl x:Class="Artemis.VisualScripting.Nodes.CustomViews.DataModelNodeCustomView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Artemis.VisualScripting.Nodes.CustomViews"
|
||||||
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
|
<ContentControl s:View.Model="{Binding SelectionViewModel}"></ContentControl>
|
||||||
|
</UserControl>
|
||||||
48
src/Artemis.VisualScripting/Nodes/DataModelNode.cs
Normal file
48
src/Artemis.VisualScripting/Nodes/DataModelNode.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
|
using Artemis.Storage.Entities.Profile;
|
||||||
|
using Artemis.VisualScripting.Nodes.CustomViewModels;
|
||||||
|
|
||||||
|
namespace Artemis.VisualScripting.Nodes
|
||||||
|
{
|
||||||
|
[Node("Data Model-Value", "Outputs a selectable data model value.")]
|
||||||
|
public class DataModelNode : Node<DataModelNodeCustomViewModel>
|
||||||
|
{
|
||||||
|
public DataModelNode() : base("Data Model", "Outputs a selectable data model value")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public OutputPin Output { get; private set; }
|
||||||
|
public DataModelPath DataModelPath { get; set; }
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
if (Storage is not DataModelPathEntity pathEntity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DataModelPath = new DataModelPath(null, pathEntity);
|
||||||
|
CustomViewModel.SelectionViewModel.ChangeDataModelPath(DataModelPath);
|
||||||
|
UpdateOutputPin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Evaluate()
|
||||||
|
{
|
||||||
|
if (DataModelPath.IsValid && Output != null)
|
||||||
|
Output.Value = DataModelPath.GetValue()!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateOutputPin()
|
||||||
|
{
|
||||||
|
if (Pins.Contains(Output))
|
||||||
|
{
|
||||||
|
RemovePin(Output);
|
||||||
|
Output = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type type = DataModelPath?.GetPropertyType();
|
||||||
|
if (type != null)
|
||||||
|
Output = CreateOutputPin(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,9 +26,11 @@ namespace Artemis.VisualScripting.Nodes
|
|||||||
|
|
||||||
public override void Evaluate()
|
public override void Evaluate()
|
||||||
{
|
{
|
||||||
Output.Value = CustomViewModel.Input;
|
Output.Value = (int) (Storage as long? ?? 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Initialize() => Storage ??= 0;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,9 +57,11 @@ namespace Artemis.VisualScripting.Nodes
|
|||||||
|
|
||||||
public override void Evaluate()
|
public override void Evaluate()
|
||||||
{
|
{
|
||||||
Output.Value = CustomViewModel.Input;
|
Output.Value = Storage as double? ?? 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Initialize() => Storage ??= 0.0;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +88,7 @@ namespace Artemis.VisualScripting.Nodes
|
|||||||
|
|
||||||
public override void Evaluate()
|
public override void Evaluate()
|
||||||
{
|
{
|
||||||
Output.Value = CustomViewModel.Input;
|
Output.Value = Storage as string;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -57,6 +57,28 @@
|
|||||||
"resolved": "5.0.10",
|
"resolved": "5.0.10",
|
||||||
"contentHash": "x70WuqMDuP75dajqSLvO+AnI/BbwS6da+ukTO7rueV7VoXoQ5CRA9FV4r7cOS4OUr2NS1Up7LDIutjCxQycRvg=="
|
"contentHash": "x70WuqMDuP75dajqSLvO+AnI/BbwS6da+ukTO7rueV7VoXoQ5CRA9FV4r7cOS4OUr2NS1Up7LDIutjCxQycRvg=="
|
||||||
},
|
},
|
||||||
|
"MaterialDesignColors": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "2.0.1",
|
||||||
|
"contentHash": "Azl8nN23SD6QPE0PdsfpKiIqWTvH7rzXwgXPiFSEt91NFOrwB5cx3iq/sbINWMZunhXJ32+jVUHiV03B8eJbZw=="
|
||||||
|
},
|
||||||
|
"MaterialDesignExtensions": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "3.3.0",
|
||||||
|
"contentHash": "dlxWtdrMH8aHNib3dWJhNQ/nNiA2b/CNvr90w/5KB6erTisuTpyYVx2l2+UGCZvwhSX5mHTHQYHfjgAKbDrgjg==",
|
||||||
|
"dependencies": {
|
||||||
|
"MaterialDesignColors": "1.2.7",
|
||||||
|
"MaterialDesignThemes": "3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MaterialDesignThemes": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "4.1.0",
|
||||||
|
"contentHash": "WqrO9AbtdE4pLPtDk/C5BZRnkgWFwVGyUHWj7tRJrgnKl089DEobVXBCLeqp2mkgBeFHj4Xe3AfWyhmlnO6AZA==",
|
||||||
|
"dependencies": {
|
||||||
|
"MaterialDesignColors": "2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"McMaster.NETCore.Plugins": {
|
"McMaster.NETCore.Plugins": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.4.0",
|
"resolved": "1.4.0",
|
||||||
@ -104,6 +126,11 @@
|
|||||||
"Microsoft.NETCore.Platforms": "3.0.0"
|
"Microsoft.NETCore.Platforms": "3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"Microsoft.Xaml.Behaviors.Wpf": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.1.31",
|
||||||
|
"contentHash": "LZpuf82ACZWldmfMuv3CTUMDh3o0xo0uHUaybR5HgqVLDBJJ9RZLykplQ/bTJd0/VDt3EhD4iDgUgbdIUAM+Kg=="
|
||||||
|
},
|
||||||
"NETStandard.Library": {
|
"NETStandard.Library": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "1.6.1",
|
"resolved": "1.6.1",
|
||||||
@ -336,6 +363,11 @@
|
|||||||
"System.Threading.Timer": "4.0.1"
|
"System.Threading.Timer": "4.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SharpVectors.Reloaded": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "1.7.5",
|
||||||
|
"contentHash": "v9U5sSMGFE2zCMbh42BYHkaRYkmwwhsKMGcNRdHAKqD1ryOf4mhqnJR0o07hwg5KIEmCI9bDdrgYSmiZWlL+eA=="
|
||||||
|
},
|
||||||
"SkiaSharp": {
|
"SkiaSharp": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "2.80.2",
|
"resolved": "2.80.2",
|
||||||
@ -344,6 +376,23 @@
|
|||||||
"System.Memory": "4.5.3"
|
"System.Memory": "4.5.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"SkiaSharp.Views.Desktop.Common": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "2.80.2",
|
||||||
|
"contentHash": "0vBvweMysgl1wgjuTQUhdJMD5z5nBjtYqmnHPeX+qHfkc336Wj2L3jEqwmGb0YP+RV47gFGz0EzMAW6szZch9w==",
|
||||||
|
"dependencies": {
|
||||||
|
"SkiaSharp": "2.80.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"SkiaSharp.Views.WPF": {
|
||||||
|
"type": "Transitive",
|
||||||
|
"resolved": "2.80.2",
|
||||||
|
"contentHash": "Fzo2+MNwHDh9Cob8sk7OO26kp3bhofjXMwlEK8IncF1ehu9hi3sH9iQDJrue9a88VEJJ+yyLISPUFcmXlGHSyQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"SkiaSharp": "2.80.2",
|
||||||
|
"SkiaSharp.Views.Desktop.Common": "2.80.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"System.AppContext": {
|
"System.AppContext": {
|
||||||
"type": "Transitive",
|
"type": "Transitive",
|
||||||
"resolved": "4.3.0",
|
"resolved": "4.3.0",
|
||||||
@ -1266,6 +1315,24 @@
|
|||||||
"LiteDB": "5.0.10",
|
"LiteDB": "5.0.10",
|
||||||
"Serilog": "2.10.0"
|
"Serilog": "2.10.0"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"artemis.ui.shared": {
|
||||||
|
"type": "Project",
|
||||||
|
"dependencies": {
|
||||||
|
"Artemis.Core": "1.0.0",
|
||||||
|
"Humanizer.Core": "2.11.10",
|
||||||
|
"MaterialDesignExtensions": "3.3.0",
|
||||||
|
"MaterialDesignThemes": "4.1.0",
|
||||||
|
"Microsoft.Xaml.Behaviors.Wpf": "1.1.31",
|
||||||
|
"Ninject": "3.3.4",
|
||||||
|
"Ninject.Extensions.Conventions": "3.3.0",
|
||||||
|
"SharpVectors.Reloaded": "1.7.5",
|
||||||
|
"SkiaSharp": "2.80.2",
|
||||||
|
"SkiaSharp.Views.WPF": "2.80.2",
|
||||||
|
"Stylet": "1.3.6",
|
||||||
|
"System.Buffers": "4.5.1",
|
||||||
|
"System.Numerics.Vectors": "4.5.0"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user