Compare commits
2 Commits
e79457a952
...
acdaecf8de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
acdaecf8de | ||
|
|
f604f58068 |
@@ -42,6 +42,9 @@ namespace Nodify.Calculator
|
||||
|
||||
// Dynamic ForEach node: adapt Current Item output based on list type
|
||||
if (!IsLoading) HandleForEachNodeConnected(c);
|
||||
|
||||
// Dynamic Knot node: adapt shape/color to match connected wire
|
||||
if (!IsLoading) HandleKnotNodeConnected(c);
|
||||
})
|
||||
.WhenRemoved(c =>
|
||||
{
|
||||
@@ -74,6 +77,9 @@ namespace Nodify.Calculator
|
||||
|
||||
// Dynamic ForEach node: reset Current Item output on disconnect
|
||||
HandleForEachNodeDisconnected(c);
|
||||
|
||||
// Dynamic Knot node: reset on disconnect
|
||||
HandleKnotNodeDisconnected(c);
|
||||
});
|
||||
|
||||
Operations.WhenAdded(x =>
|
||||
@@ -137,12 +143,37 @@ namespace Nodify.Calculator
|
||||
public PendingConnectionViewModel PendingConnection { get; set; } = new PendingConnectionViewModel();
|
||||
public OperationsMenuViewModel OperationsMenu { get; set; }
|
||||
|
||||
public void CreateKnotNodeAt(Point location)
|
||||
{
|
||||
var knot = new KnotOperationViewModel { Location = location };
|
||||
Operations.Add(knot);
|
||||
}
|
||||
|
||||
public INodifyCommand StartConnectionCommand { get; }
|
||||
public INodifyCommand CreateConnectionCommand { get; }
|
||||
public INodifyCommand DisconnectConnectorCommand { get; }
|
||||
public INodifyCommand DeleteSelectionCommand { get; }
|
||||
public INodifyCommand GroupSelectionCommand { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A knot connector allows any shape only when its knot node has no existing connections (unadapted).
|
||||
/// Once adapted (input connected), shape must match.
|
||||
/// </summary>
|
||||
private bool IsKnotConnectorAllowed(ConnectorViewModel source, ConnectorViewModel target)
|
||||
{
|
||||
ConnectorViewModel knotConn = source.IsKnotConnector ? source : target.IsKnotConnector ? target : null;
|
||||
if (knotConn == null) return false;
|
||||
|
||||
var knotOp = knotConn.Operation as KnotOperationViewModel;
|
||||
if (knotOp == null) return false;
|
||||
|
||||
// If knot has no connections yet, allow any shape
|
||||
bool hasAnyConnection = Connections.Any(con =>
|
||||
con.Input.Operation == knotOp || con.Output.Operation == knotOp);
|
||||
|
||||
return !hasAnyConnection;
|
||||
}
|
||||
|
||||
private void DisconnectConnector(ConnectorViewModel connector)
|
||||
{
|
||||
var connections = Connections.Where(c => c.Input == connector || c.Output == connector).ToList();
|
||||
@@ -152,6 +183,7 @@ namespace Nodify.Calculator
|
||||
internal bool CanCreateConnection(ConnectorViewModel source, ConnectorViewModel? target)
|
||||
=> target == null || (source != target &&
|
||||
(source.Shape == target.Shape ||
|
||||
IsKnotConnectorAllowed(source, target) ||
|
||||
((source.IsCopyConnector || target.IsCopyConnector) && source.Shape != ConnectorShape.Triangle && target.Shape != ConnectorShape.Triangle)) &&
|
||||
!source.IsConnected &&
|
||||
!target.IsConnected &&
|
||||
@@ -714,6 +746,68 @@ namespace Nodify.Calculator
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleKnotNodeConnected(ConnectionViewModel c)
|
||||
{
|
||||
// Determine if a knot node's INPUT connector is being connected
|
||||
// c.Input is the receiving connector, c.Output is the source connector
|
||||
KnotOperationViewModel knotOp = null;
|
||||
ConnectorViewModel sourceConn = null;
|
||||
|
||||
// Connection into the knot's input: c.Input belongs to knot, c.Output is the source
|
||||
if (c.Input.IsKnotConnector && c.Input.IsInput && c.Input.Operation is KnotOperationViewModel k1)
|
||||
{
|
||||
knotOp = k1;
|
||||
sourceConn = c.Output;
|
||||
}
|
||||
// Connection from knot's output into something: c.Output belongs to knot, c.Input is the target
|
||||
// If knot is unadapted and output connects first, adapt from the target
|
||||
else if (c.Output.IsKnotConnector && !c.Output.IsInput && c.Output.Operation is KnotOperationViewModel k2)
|
||||
{
|
||||
knotOp = k2;
|
||||
sourceConn = c.Input;
|
||||
}
|
||||
|
||||
if (knotOp == null || sourceConn == null) return;
|
||||
|
||||
// Adapt ALL connectors (both input and output) to match the connected wire's shape/color/type
|
||||
foreach (var conn in knotOp.Input.Concat(knotOp.Output))
|
||||
{
|
||||
conn.Shape = sourceConn.Shape;
|
||||
conn.ConnectorColor = sourceConn.RawColor;
|
||||
conn.DataType = sourceConn.DataType;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleKnotNodeDisconnected(ConnectionViewModel c)
|
||||
{
|
||||
ConnectorViewModel knotConn = null;
|
||||
|
||||
if (c.Input.IsKnotConnector)
|
||||
knotConn = c.Input;
|
||||
else if (c.Output.IsKnotConnector)
|
||||
knotConn = c.Output;
|
||||
|
||||
if (knotConn == null) return;
|
||||
|
||||
var knotOp = knotConn.Operation as KnotOperationViewModel;
|
||||
if (knotOp == null) return;
|
||||
|
||||
// Check if any connection still exists on the knot
|
||||
var stillConnected = Connections.Any(con =>
|
||||
(con.Input.IsKnotConnector && con.Input.Operation == knotOp) ||
|
||||
(con.Output.IsKnotConnector && con.Output.Operation == knotOp));
|
||||
|
||||
if (stillConnected) return;
|
||||
|
||||
// Reset to default
|
||||
foreach (var conn in knotOp.Input.Concat(knotOp.Output))
|
||||
{
|
||||
conn.Shape = ConnectorShape.Circle;
|
||||
conn.ConnectorColor = System.Drawing.Color.DodgerBlue;
|
||||
conn.DataType = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleForEachNodeDisconnected(ConnectionViewModel c)
|
||||
{
|
||||
ConnectorViewModel forEachListInput = null;
|
||||
|
||||
@@ -65,6 +65,8 @@ namespace Nodify.Calculator
|
||||
set
|
||||
{
|
||||
_color = value;
|
||||
OnPropertyChanged(nameof(Color));
|
||||
OnPropertyChanged(nameof(RawColor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,6 +123,16 @@ namespace Nodify.Calculator
|
||||
set => SetProperty(ref _isCopyConnector, value);
|
||||
}
|
||||
|
||||
private bool _isKnotConnector;
|
||||
/// <summary>
|
||||
/// When true, this connector belongs to a Knot/Reroute node and accepts any shape for connections.
|
||||
/// </summary>
|
||||
public bool IsKnotConnector
|
||||
{
|
||||
get => _isKnotConnector;
|
||||
set => SetProperty(ref _isKnotConnector, value);
|
||||
}
|
||||
|
||||
private bool _isTakeListConnector;
|
||||
/// <summary>
|
||||
/// When true, this connector belongs to a TAKE node and only accepts list/array types.
|
||||
|
||||
@@ -646,6 +646,20 @@
|
||||
</nodify:Node>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate DataType="{x:Type local:KnotOperationViewModel}">
|
||||
<nodify:Node Input="{Binding Input}"
|
||||
Output="{Binding Output}"
|
||||
Background="#FF2A2A2A"
|
||||
BorderBrush="#FF666666"
|
||||
BorderThickness="1"
|
||||
Padding="0"
|
||||
ToolTip="Knot (Reroute) — Ctrl+Click to create">
|
||||
<Ellipse Width="8" Height="8"
|
||||
Fill="#FFAAAAAA"
|
||||
Margin="2" />
|
||||
</nodify:Node>
|
||||
</DataTemplate>
|
||||
|
||||
<DataTemplate DataType="{x:Type local:OperationViewModel}">
|
||||
<nodify:Node Content="{Binding Title}"
|
||||
Input="{Binding Input}"
|
||||
|
||||
@@ -34,6 +34,26 @@ namespace Nodify.Calculator
|
||||
}
|
||||
}
|
||||
|
||||
public class KnotNodeHandler : InputElementState<NodifyEditor>
|
||||
{
|
||||
private static InputGesture CreateKnotGesture { get; } = new Interactivity.MouseGesture(MouseAction.LeftClick, ModifierKeys.Control);
|
||||
|
||||
public KnotNodeHandler(NodifyEditor element) : base(element)
|
||||
{
|
||||
ProcessHandledEvents = true;
|
||||
}
|
||||
|
||||
protected override void OnMouseUp(MouseButtonEventArgs e)
|
||||
{
|
||||
if (CreateKnotGesture.Matches(e.Source, e) && Element.DataContext is CalculatorViewModel calculator)
|
||||
{
|
||||
var location = Element.MouseLocation;
|
||||
calculator.CreateKnotNodeAt(location);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class EditorView : UserControl
|
||||
{
|
||||
public EditorView()
|
||||
@@ -44,6 +64,7 @@ namespace Nodify.Calculator
|
||||
static EditorView()
|
||||
{
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(editor => new OperationsMenuHandler(editor));
|
||||
InputProcessor.Shared<NodifyEditor>.RegisterHandlerFactory(editor => new KnotNodeHandler(editor));
|
||||
}
|
||||
|
||||
private void OnDropNode(object sender, DragEventArgs e)
|
||||
|
||||
@@ -428,6 +428,20 @@ namespace Nodify.Calculator
|
||||
OnLogMe?.Invoke($"Auth node resolved. Base URL: {_authBaseUrl}, Auth Type: {_authType}");
|
||||
return;
|
||||
}
|
||||
// Knot/Reroute node: passthrough - forward input data to output
|
||||
if (op is KnotOperationViewModel)
|
||||
{
|
||||
var knotInputCon = connections.Where(c => c.Input.Operation == op && c.Input.Shape != ConnectorShape.Triangle).FirstOrDefault();
|
||||
if (knotInputCon != null)
|
||||
{
|
||||
var srcNodeId = knotInputCon.Output?.Operation?.NodeId;
|
||||
if (srcNodeId != null && outputs.TryGetValue(srcNodeId, out var srcVal))
|
||||
{
|
||||
outputs[op.NodeId] = srcVal;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (op is SystemOperationViewModel debugSysOp && debugSysOp.SystemOperationType == SystemOperations.DEBUG)
|
||||
{
|
||||
// Find the data input connection (non-triangle)
|
||||
|
||||
15
Examples/Nodify.Calculator/KnotOperationViewModel.cs
Normal file
15
Examples/Nodify.Calculator/KnotOperationViewModel.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Nodify.Calculator
|
||||
{
|
||||
public class KnotOperationViewModel : SystemOperationViewModel
|
||||
{
|
||||
public KnotOperationViewModel()
|
||||
{
|
||||
Title = "⬥";
|
||||
SystemOperationType = SystemOperations.KNOT;
|
||||
|
||||
// Single flow-through input and output (will adapt shape when connected)
|
||||
Input.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Circle, IsKnotConnector = true });
|
||||
Output.Add(new ConnectorViewModel { Title = "", Shape = ConnectorShape.Circle, IsKnotConnector = true });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,8 @@ namespace Nodify.Calculator
|
||||
DEBUG,
|
||||
NEW_OBJECT,
|
||||
FOREACH,
|
||||
ASSERT
|
||||
ASSERT,
|
||||
KNOT
|
||||
}
|
||||
|
||||
public class SystemOperationViewModel : OperationViewModel
|
||||
|
||||
1
push command.txt
Normal file
1
push command.txt
Normal file
@@ -0,0 +1 @@
|
||||
git push -u origin master
|
||||
Reference in New Issue
Block a user