From 7fd70219ea4cf2da82add93e34ca349bdcf6c0f4 Mon Sep 17 00:00:00 2001 From: Ankitkumar Satapara Date: Sat, 18 Apr 2026 20:07:33 +0530 Subject: [PATCH] Implemented functionality for copy node --- .../Nodify.Calculator/CalculatorViewModel.cs | 93 ++++++++++++++++++- .../Nodify.Calculator/ConnectorViewModel.cs | 12 +++ .../Operations/OperationFactory.cs | 18 +++- 3 files changed, 119 insertions(+), 4 deletions(-) diff --git a/Examples/Nodify.Calculator/CalculatorViewModel.cs b/Examples/Nodify.Calculator/CalculatorViewModel.cs index 9f3b14d..c6b8dd6 100644 --- a/Examples/Nodify.Calculator/CalculatorViewModel.cs +++ b/Examples/Nodify.Calculator/CalculatorViewModel.cs @@ -28,6 +28,9 @@ namespace Nodify.Calculator // Dynamic Split node: populate outputs when a model connects to the Square input HandleSplitNodeConnected(c); + + // Dynamic Copy node: adapt connectors when something connects + HandleCopyNodeConnected(c); }) .WhenRemoved(c => { @@ -48,6 +51,9 @@ namespace Nodify.Calculator // Dynamic Split node: clear outputs when model disconnects HandleSplitNodeDisconnected(c); + + // Dynamic Copy node: reset on disconnect + HandleCopyNodeDisconnected(c); }); Operations.WhenAdded(x => @@ -116,7 +122,8 @@ namespace Nodify.Calculator internal bool CanCreateConnection(ConnectorViewModel source, ConnectorViewModel? target) => target == null || (source != target && - source.Shape == target.Shape && + (source.Shape == target.Shape || + ((source.IsCopyConnector || target.IsCopyConnector) && source.Shape != ConnectorShape.Triangle && target.Shape != ConnectorShape.Triangle)) && !source.IsConnected && !target.IsConnected && source.IsInput != target.IsInput && @@ -280,6 +287,90 @@ namespace Nodify.Calculator sysVm.Title = "Split"; } + + private void HandleCopyNodeConnected(ConnectionViewModel c) + { + // Find the COPY node's input connector + ConnectorViewModel copyConnector = null; + ConnectorViewModel sourceConnector = null; + + if (c.Input.IsCopyConnector) + { + copyConnector = c.Input; + sourceConnector = c.Output; + } + else if (c.Output.IsCopyConnector) + { + copyConnector = c.Output; + sourceConnector = c.Input; + } + + if (copyConnector == null) return; + + var copyOp = copyConnector.Operation; + if (copyOp is not SystemOperationViewModel sysVm || sysVm.SystemOperationType != SystemOperations.COPY) + return; + + // Adapt the input connector to match the source + copyConnector.Shape = sourceConnector.Shape; + copyConnector.ConnectorColor = sourceConnector.RawColor; + copyConnector.DataType = sourceConnector.DataType; + + // Remove any existing outputs and their connections + var toRemove = copyOp.Output.ToList(); + toRemove.ForEach(o => + { + DisconnectConnector(o); + copyOp.Output.Remove(o); + }); + + // Create 2 output copy connectors matching the source + for (int i = 0; i < 2; i++) + { + copyOp.Output.Add(new ConnectorViewModel + { + Title = sourceConnector.Title ?? "", + IsInput = false, + Shape = sourceConnector.Shape, + ConnectorColor = sourceConnector.RawColor, + DataType = sourceConnector.DataType, + IsCopyConnector = true + }); + } + } + + private void HandleCopyNodeDisconnected(ConnectionViewModel c) + { + ConnectorViewModel copyConnector = null; + + if (c.Input.IsCopyConnector && c.Input.IsInput) + copyConnector = c.Input; + else if (c.Output.IsCopyConnector && c.Output.IsInput) + copyConnector = c.Output; + + if (copyConnector == null) return; + + var copyOp = copyConnector.Operation; + if (copyOp is not SystemOperationViewModel sysVm || sysVm.SystemOperationType != SystemOperations.COPY) + return; + + // Check if input still has a connection + var stillConnected = Connections.Any(con => con.Input == copyConnector || con.Output == copyConnector); + if (stillConnected) return; + + // Reset input connector to universal + copyConnector.Shape = ConnectorShape.Circle; + copyConnector.ConnectorColor = System.Drawing.Color.White; + copyConnector.DataType = string.Empty; + + // Remove all outputs and their connections + var toRemove = copyOp.Output.ToList(); + toRemove.ForEach(o => + { + DisconnectConnector(o); + copyOp.Output.Remove(o); + }); + } } } diff --git a/Examples/Nodify.Calculator/ConnectorViewModel.cs b/Examples/Nodify.Calculator/ConnectorViewModel.cs index 468f2e0..169c974 100644 --- a/Examples/Nodify.Calculator/ConnectorViewModel.cs +++ b/Examples/Nodify.Calculator/ConnectorViewModel.cs @@ -67,6 +67,8 @@ namespace Nodify.Calculator } } + public System.Drawing.Color RawColor => _color; + public Brush Color { get @@ -108,6 +110,16 @@ namespace Nodify.Calculator set => SetProperty(ref _dataType, value); } + private bool _isCopyConnector; + /// + /// When true, this connector belongs to a COPY node and accepts any shape for connections. + /// + public bool IsCopyConnector + { + get => _isCopyConnector; + set => SetProperty(ref _isCopyConnector, value); + } + /// /// Returns a color based on the C# data type. /// diff --git a/Examples/Nodify.Calculator/Operations/OperationFactory.cs b/Examples/Nodify.Calculator/Operations/OperationFactory.cs index f3be48a..8786028 100644 --- a/Examples/Nodify.Calculator/Operations/OperationFactory.cs +++ b/Examples/Nodify.Calculator/Operations/OperationFactory.cs @@ -23,12 +23,10 @@ namespace Nodify.Calculator { Title = "COPY", Type = OperationType.System, - OPType = "copy", + sysOp = SystemOperations.COPY, IsFlowNode = false, }; copynode.Input.Add(""); - copynode.Output.Add(""); - copynode.Output.Add(""); var begin = new OperationInfoViewModel() { @@ -330,6 +328,20 @@ namespace Nodify.Calculator SystemOperationType = info.sysOp }; + if (info.sysOp == SystemOperations.COPY) + { + sysOp.Title = info.Title; + // Universal input — accepts any shape. Starts with no outputs. + foreach (var item in input) + { + item.Shape = ConnectorShape.Circle; + item.ConnectorColor = Color.White; + item.IsCopyConnector = true; + sysOp.Input.Add(item); + } + return sysOp; + } + if (info.sysOp == SystemOperations.SPLIT) { sysOp.Title = info.Title;