public void TestConnections() { NodeOutputViewModel nodeAOutput = new NodeOutputViewModel(); NodeViewModel nodeA = new NodeViewModel(); nodeA.Outputs.Add(nodeAOutput); NodeInputViewModel nodeBInput = new NodeInputViewModel(); NodeOutputViewModel nodeBOutput = new NodeOutputViewModel(); NodeViewModel nodeB = new NodeViewModel { CanBeRemovedByUser = false, IsSelected = true }; nodeB.Inputs.Add(nodeBInput); nodeB.Outputs.Add(nodeBOutput); NodeInputViewModel nodeCInput = new NodeInputViewModel { MaxConnections = 2 }; NodeViewModel nodeC = new NodeViewModel { IsSelected = true }; nodeC.Inputs.Add(nodeCInput); NodeViewModel nodeD = new NodeViewModel { IsSelected = true }; NetworkViewModel network = new NetworkViewModel { Nodes = { nodeA, nodeB, nodeC, nodeD } }; Assert.IsTrue(nodeBInput.Connections.Count == 0); var conAB = network.ConnectionFactory(nodeBInput, nodeAOutput); var conBC = network.ConnectionFactory(nodeCInput, nodeBOutput); network.Connections.Add(conAB); network.Connections.Add(conBC); Assert.IsTrue(Enumerable.SequenceEqual(nodeBInput.Connections.Items, new[] { conAB })); network.Connections.Remove(conAB); Assert.IsTrue(nodeBInput.Connections.Count == 0); var conAC = network.ConnectionFactory(nodeCInput, nodeAOutput); network.Connections.Add(conAC); Assert.IsTrue(Enumerable.SequenceEqual(nodeCInput.Connections.Items, new[] { conBC, conAC })); }
public void TestConnections() { NodeOutputViewModel nodeAOutput = new NodeOutputViewModel(); NodeViewModel nodeA = new NodeViewModel { Outputs = { nodeAOutput } }; NodeInputViewModel nodeBInput = new NodeInputViewModel(); NodeViewModel nodeB = new NodeViewModel { Inputs = { nodeBInput } }; NodeInputViewModel nodeCInput = new NodeInputViewModel(); NodeViewModel nodeC = new NodeViewModel { Inputs = { nodeCInput } }; NetworkViewModel network = new NetworkViewModel { Nodes = { nodeA, nodeB, nodeC } }; Assert.AreEqual(0, nodeAOutput.Connections.Count); var conAB = network.ConnectionFactory(nodeBInput, nodeAOutput); network.Connections.Add(conAB); Assert.IsTrue(nodeAOutput.Connections.SequenceEqual(new[] { conAB })); var conAC = network.ConnectionFactory(nodeCInput, nodeAOutput); network.Connections.Add(conAC); Assert.IsTrue(nodeAOutput.Connections.SequenceEqual(new [] { conAB, conAC })); network.Connections.Remove(conAB); Assert.IsTrue(nodeAOutput.Connections.SequenceEqual(new[] { conAC })); network.Connections.Remove(conAC); Assert.AreEqual(0, nodeAOutput.Connections.Count); }
public void TestCutLine() { NodeOutputViewModel nodeAOutput = new NodeOutputViewModel(); NodeViewModel nodeA = new NodeViewModel(); nodeA.Outputs.Add(nodeAOutput); NodeInputViewModel nodeBInput = new NodeInputViewModel(); NodeOutputViewModel nodeBOutput = new NodeOutputViewModel(); NodeViewModel nodeB = new NodeViewModel { CanBeRemovedByUser = false, IsSelected = true }; nodeB.Inputs.Add(nodeBInput); nodeB.Outputs.Add(nodeBOutput); NodeInputViewModel nodeCInput = new NodeInputViewModel(); NodeViewModel nodeC = new NodeViewModel { IsSelected = true }; nodeC.Inputs.Add(nodeCInput); NodeViewModel nodeD = new NodeViewModel { IsSelected = true }; NetworkViewModel network = new NetworkViewModel { Nodes = { nodeA, nodeB, nodeC, nodeD } }; var conAB = network.ConnectionFactory(nodeBInput, nodeAOutput); var conBC = network.ConnectionFactory(nodeCInput, nodeBOutput); network.Connections.Add(conAB); network.Connections.Add(conBC); network.StartCut(); network.CutLine.IntersectingConnections.Add(conAB); network.FinishCut(); Assert.IsTrue(network.Connections.Items.SequenceEqual(new [] { conBC })); Assert.IsFalse(network.CutLine.IsVisible); }
public void TestValidateAfterConnectionsAllUpdated() { NodeInputViewModel input1 = new NodeInputViewModel(); NodeOutputViewModel output1 = new NodeOutputViewModel(); NodeViewModel node1 = new NodeViewModel { Inputs = { input1 }, Outputs = { output1 } }; NodeInputViewModel input2 = new NodeInputViewModel(); NodeOutputViewModel output2 = new NodeOutputViewModel(); NodeViewModel node2 = new NodeViewModel { Inputs = { input2 }, Outputs = { output2 } }; NetworkViewModel network = new NetworkViewModel(); network.Validator = n => { if (GraphAlgorithms.FindLoops(network).Any()) { return(new NetworkValidationResult(false, false, null)); } return(new NetworkValidationResult(true, true, null)); }; network.Nodes.Add(node1); network.Nodes.Add(node2); var conn1 = network.ConnectionFactory(input1, output2); network.Connections.Add(conn1); Assert.IsTrue(network.LatestValidation.IsValid); var conn2 = network.ConnectionFactory(input2, output1); network.Connections.Add(conn2); Assert.IsFalse(network.LatestValidation.IsValid); network.Connections.Remove(conn1); Assert.IsTrue(network.LatestValidation.IsValid); }
public void TestHideEditorIfConnected() { TestableOutput output = new TestableOutput(); TestableInput input = new TestableInput(); var outputNode = new NodeViewModel(); outputNode.Outputs.Add(output); var inputNode = new NodeViewModel(); inputNode.Inputs.Add(input); NetworkViewModel network = new NetworkViewModel { Nodes = { outputNode, inputNode } }; input.HideEditorIfConnected = true; Assert.IsTrue(input.IsEditorVisible); network.Connections.Add(network.ConnectionFactory(input, output)); Assert.IsFalse(input.IsEditorVisible); }
public void TestHideEditorIfConnected() { TestableOutput output = new TestableOutput(); TestableInput input = new TestableInput(); NetworkViewModel network = new NetworkViewModel { Nodes = { new NodeViewModel { Outputs ={ output } }, new NodeViewModel { Inputs ={ input } } } }; input.HideEditorIfConnected = true; Assert.IsTrue(input.IsEditorVisible); network.Connections.Add(network.ConnectionFactory(input, output)); Assert.IsFalse(input.IsEditorVisible); }
public void TestListInputDisconnect() { using (TestUtils.WithScheduler(ImmediateScheduler.Instance)) { var input1 = new ValueListNodeInputViewModel <string>(); NodeViewModel node1 = new NodeViewModel { Inputs = { input1 } }; var output2 = new ValueNodeOutputViewModel <string> { Value = Observable.Return("Test") }; NodeViewModel node2 = new NodeViewModel { Outputs = { output2 } }; NetworkViewModel network = new NetworkViewModel(); network.Nodes.Add(node1); network.Nodes.Add(node2); var conn1 = network.ConnectionFactory(input1, output2); network.Connections.Add(conn1); CollectionAssert.AreEqual(new[] { "Test" }, input1.Values.ToArray()); network.Connections.Remove(conn1); CollectionAssert.AreEqual(new string[0], input1.Values.ToArray()); } }
public void TestAddConnectionShouldUpdateInputAndOutputConnLists() { NodeOutputViewModel output = new NodeOutputViewModel(); NodeViewModel node1 = new NodeViewModel(); node1.Outputs.Add(output); NodeInputViewModel input = new NodeInputViewModel(); NodeViewModel node2 = new NodeViewModel(); node2.Inputs.Add(input); NetworkViewModel network = new NetworkViewModel(); network.Nodes.Add(node1); network.Nodes.Add(node2); var conn = network.ConnectionFactory(input, output); network.Connections.Add(conn); CollectionAssert.AreEqual(new [] { conn }, input.Connections.Items.AsArray()); CollectionAssert.AreEqual(new[] { conn }, output.Connections.Items.AsArray()); network.Connections.Clear(); CollectionAssert.AreEqual(new ConnectionViewModel[0], input.Connections.Items.AsArray()); CollectionAssert.AreEqual(new ConnectionViewModel[0], output.Connections.Items.AsArray()); }
private void GenerateConnections(object sender, RoutedEventArgs e) { var connections = _network.Nodes.Zip(_network.Nodes.Skip(1), (node1, node2) => _network.ConnectionFactory(node2.Inputs[0], node1.Outputs[0])); _network.Connections.AddRange(connections); }
public void TestListInputDisconnect() { var input1 = new ValueListNodeInputViewModel <string>(); NodeViewModel node1 = new NodeViewModel(); node1.Inputs.Add(input1); var output2 = new ValueNodeOutputViewModel <string> { Value = Observable.Return("Test") }; NodeViewModel node2 = new NodeViewModel(); node2.Outputs.Add(output2); NetworkViewModel network = new NetworkViewModel(); network.Nodes.Add(node1); network.Nodes.Add(node2); var conn1 = network.ConnectionFactory(input1, output2); network.Connections.Add(conn1); CollectionAssert.AreEqual(new[] { "Test" }, input1.Values.Items.AsArray()); network.Connections.Remove(conn1); CollectionAssert.AreEqual(new string[0], input1.Values.Items.AsArray()); }
public void TestDeleteOutputShouldRemoveConnections() { NodeOutputViewModel output = new NodeOutputViewModel(); NodeViewModel node1 = new NodeViewModel(); node1.Outputs.Add(output); NodeInputViewModel input = new NodeInputViewModel(); NodeViewModel node2 = new NodeViewModel(); node2.Inputs.Add(input); NetworkViewModel network = new NetworkViewModel(); network.Nodes.Add(node1); network.Nodes.Add(node2); var conn = network.ConnectionFactory(input, output); network.Connections.Add(conn); Assert.IsTrue(network.Connections.Items.Contains(conn)); node1.Outputs.Remove(output); Assert.IsFalse(network.Connections.Items.Contains(conn)); node1.Outputs.Add(output); network.Connections.Add(conn); Assert.IsTrue(network.Connections.Items.Contains(conn)); node1.Outputs.Clear(); Assert.IsFalse(network.Connections.Items.Contains(conn)); }
public void TestDeleteSelectedNodes() { NodeOutputViewModel nodeAOutput = new NodeOutputViewModel(); NodeViewModel nodeA = new NodeViewModel(); nodeA.Outputs.Add(nodeAOutput); NodeInputViewModel nodeBInput = new NodeInputViewModel(); NodeOutputViewModel nodeBOutput = new NodeOutputViewModel(); NodeViewModel nodeB = new NodeViewModel { CanBeRemovedByUser = false, IsSelected = true, }; nodeB.Inputs.Add(nodeBInput); nodeB.Outputs.Add(nodeBOutput); NodeInputViewModel nodeCInput = new NodeInputViewModel(); NodeViewModel nodeC = new NodeViewModel { IsSelected = true }; nodeC.Inputs.Add(nodeCInput); NodeViewModel nodeD = new NodeViewModel { IsSelected = true }; NetworkViewModel network = new NetworkViewModel { Nodes = { nodeA, nodeB, nodeC, nodeD } }; network.Connections.Add(network.ConnectionFactory(nodeBInput, nodeAOutput)); network.Connections.Add(network.ConnectionFactory(nodeCInput, nodeBOutput)); Observable.Return(Unit.Default).InvokeCommand(network.DeleteSelectedNodes); Assert.AreEqual(1, network.Connections.Count); Assert.IsTrue(network.Nodes.SequenceEqual(new [] { nodeA, nodeB })); }
public void TestDeleteNodeShouldRemoveConnections() { NodeInputViewModel input1 = new NodeInputViewModel(); NodeOutputViewModel output1 = new NodeOutputViewModel(); NodeViewModel node1 = new NodeViewModel { Inputs = { input1 }, Outputs = { output1 } }; NodeInputViewModel input2 = new NodeInputViewModel(); NodeOutputViewModel output2 = new NodeOutputViewModel(); NodeViewModel node2 = new NodeViewModel { Inputs = { input2 }, Outputs = { output2 } }; NetworkViewModel network = new NetworkViewModel(); network.Nodes.Add(node1); network.Nodes.Add(node2); var conn1 = network.ConnectionFactory(input1, output2); var conn2 = network.ConnectionFactory(input2, output1); network.Connections.Add(conn1); network.Connections.Add(conn2); Assert.IsTrue(network.Connections.Contains(conn1)); Assert.IsTrue(network.Connections.Contains(conn2)); network.Nodes.Remove(node1); Assert.IsFalse(network.Connections.Contains(conn1)); Assert.IsFalse(network.Connections.Contains(conn2)); network.Nodes.AddRange(new [] { node1, node2 }); network.Connections.AddRange(new[] { conn1, conn2 }); Assert.IsTrue(network.Connections.Contains(conn1)); Assert.IsTrue(network.Connections.Contains(conn2)); network.Nodes.Clear(); Assert.IsFalse(network.Connections.Contains(conn1)); Assert.IsFalse(network.Connections.Contains(conn2)); }
public void TestNodeCollapse() { NodeOutputViewModel nodeAOutput = new NodeOutputViewModel(); NodeViewModel nodeA = new NodeViewModel { Outputs = { nodeAOutput } }; NodeInputViewModel nodeBInput = new NodeInputViewModel(); NodeInputViewModel nodeBInput2 = new NodeInputViewModel(); NodeOutputViewModel nodeBOutput = new NodeOutputViewModel(); NodeOutputViewModel nodeBOutput2 = new NodeOutputViewModel(); NodeViewModel nodeB = new NodeViewModel { Inputs = { nodeBInput, nodeBInput2 }, Outputs = { nodeBOutput, nodeBOutput2 } }; NodeInputViewModel nodeCInput = new NodeInputViewModel(); NodeViewModel nodeC = new NodeViewModel { Inputs = { nodeCInput } }; NetworkViewModel network = new NetworkViewModel { Nodes = { nodeA, nodeB, nodeC } }; network.Connections.Add(network.ConnectionFactory(nodeBInput, nodeAOutput)); network.Connections.Add(network.ConnectionFactory(nodeCInput, nodeBOutput)); nodeB.IsCollapsed = true; Assert.IsTrue(nodeB.VisibleInputs.SequenceEqual(new [] { nodeBInput })); Assert.IsTrue(nodeB.VisibleOutputs.SequenceEqual(new[] { nodeBOutput })); }
public void TestValuePropagation() { //Setup var scheduler = new TestScheduler(); var nodeA = new NodeViewModel(); Subject <int> sourceA = new Subject <int>(); var outputA = new ValueNodeOutputViewModel <int> { Value = sourceA }; nodeA.Outputs.Add(outputA); var nodeB = new NodeViewModel(); var inputB = new ValueListNodeInputViewModel <int>(); nodeB.Inputs.Add(inputB); NetworkViewModel network = new NetworkViewModel(); network.Nodes.AddRange(new [] { nodeA, nodeB }); network.Connections.Add(network.ConnectionFactory(inputB, outputA)); //Define actions scheduler.Schedule(TimeSpan.FromTicks(10), () => sourceA.OnNext(1)); scheduler.Schedule(TimeSpan.FromTicks(20), () => sourceA.OnNext(0)); scheduler.Schedule(TimeSpan.FromTicks(30), () => sourceA.OnNext(1)); scheduler.Schedule(TimeSpan.FromTicks(40), () => sourceA.OnNext(0)); scheduler.Schedule(TimeSpan.FromTicks(50), () => sourceA.OnNext(2)); var actual = scheduler.Start(() => inputB.Values.Connect().QueryWhenChanged(), created: 0, subscribed: 0, disposed: 100); //Assert Assert.AreEqual(actual.Messages.Count, 6); Assert.AreEqual(actual.Messages[0].Time, 1); Assert.IsTrue(actual.Messages[0].Value.Value.SequenceEqual(new[] { 0 })); Assert.AreEqual(actual.Messages[1].Time, 10); Assert.IsTrue(actual.Messages[1].Value.Value.SequenceEqual(new[] { 1 })); Assert.AreEqual(actual.Messages[2].Time, 20); Assert.IsTrue(actual.Messages[2].Value.Value.SequenceEqual(new[] { 0 })); Assert.AreEqual(actual.Messages[3].Time, 30); Assert.IsTrue(actual.Messages[3].Value.Value.SequenceEqual(new[] { 1 })); Assert.AreEqual(actual.Messages[4].Time, 40); Assert.IsTrue(actual.Messages[4].Value.Value.SequenceEqual(new[] { 0 })); Assert.AreEqual(actual.Messages[5].Time, 50); Assert.IsTrue(actual.Messages[5].Value.Value.SequenceEqual(new[] { 2 })); }
public void TestNestedObserving() { //Setup var scheduler = new TestScheduler(); NodeInputViewModel input1 = new NodeInputViewModel(); NodeOutputViewModel output1 = new NodeOutputViewModel(); NodeViewModel node1 = new NodeViewModel { Inputs = { input1 }, Outputs = { output1 } }; NodeInputViewModel input2 = new NodeInputViewModel(); NodeOutputViewModel output2 = new NodeOutputViewModel(); NodeViewModel node2 = new NodeViewModel { Inputs = { input2 }, Outputs = { output2 } }; NetworkViewModel network = new NetworkViewModel(); network.Nodes.Add(node1); network.Nodes.Add(node2); var conn1 = network.ConnectionFactory(input1, output2); network.Connections.Add(conn1); var obs = network.ConnectionsUpdated; //Define actions scheduler.Schedule(TimeSpan.FromTicks(10), () => network.Connections.Remove(conn1)); var actual = scheduler.Start(() => obs, created: 0, subscribed: 0, disposed: 100); // But subscribe to it here //Assert var expected = new[] { ReactiveTest.OnNext(10, Unit.Default) }; ReactiveAssert.AreElementsEqual(expected, actual.Messages); }
public void TestConnectionsUpdatedAfterPreexistingConnectionRemoved() { //Setup var scheduler = new TestScheduler(); NodeInputViewModel input1 = new NodeInputViewModel(); NodeOutputViewModel output1 = new NodeOutputViewModel(); NodeViewModel node1 = new NodeViewModel(); node1.Inputs.Add(input1); node1.Outputs.Add(output1); NodeInputViewModel input2 = new NodeInputViewModel(); NodeOutputViewModel output2 = new NodeOutputViewModel(); NodeViewModel node2 = new NodeViewModel(); node2.Inputs.Add(input2); node2.Outputs.Add(output2); NetworkViewModel network = new NetworkViewModel(); var observable = network.ConnectionsUpdated; // Create observable before nodes/connections are added network.Nodes.Add(node1); network.Nodes.Add(node2); var conn1 = network.ConnectionFactory(input1, output2); network.Connections.Add(conn1); //Define actions scheduler.Schedule(TimeSpan.FromTicks(10), () => network.Connections.Remove(conn1)); var actual = scheduler.Start(() => observable, created: 0, subscribed: 0, disposed: 100); // But subscribe to it here //Assert var expected = new[] { ReactiveTest.OnNext(10, Unit.Default) }; ReactiveAssert.AreElementsEqual(expected, actual.Messages); }
public void TestCollapseWithGroups() { NodeViewModel node = new NodeViewModel(); EndpointGroup groupA = new EndpointGroup { Name = "Group A" }; EndpointGroup groupB = new EndpointGroup { Name = "Group B" }; EndpointGroup groupC = new EndpointGroup(groupA) { Name = "Group C" }; EndpointGroup groupD = new EndpointGroup(groupB) { Name = "Group D" }; NodeInputViewModel inputC = new NodeInputViewModel { Group = groupC, Name = "Input C" }; NodeOutputViewModel outputC = new NodeOutputViewModel { Group = groupC, Name = "Output C" }; NodeInputViewModel inputD = new NodeInputViewModel { Group = groupD, Name = "Input D" }; NodeOutputViewModel outputD = new NodeOutputViewModel { Group = groupD, Name = "Output D" }; node.Inputs.Add(inputC); node.Inputs.Add(inputD); node.Outputs.Add(outputC); node.Outputs.Add(outputD); var network = new NetworkViewModel(); network.Nodes.Add(node); network.Connections.Add(network.ConnectionFactory(inputC, new NodeOutputViewModel())); node.IsCollapsed = true; Assert.IsTrue(node.VisibleInputs.Count == 0); Assert.IsTrue(node.VisibleOutputs.Count == 0); Assert.IsTrue(node.VisibleEndpointGroups.Count == 1); EndpointGroupViewModel groupAViewModel = node.VisibleEndpointGroups[0]; Assert.AreEqual(groupA, groupAViewModel.Group); Assert.IsTrue(groupAViewModel.VisibleInputs.Count == 0); Assert.IsTrue(groupAViewModel.VisibleOutputs.Count == 0); Assert.IsTrue(groupAViewModel.Children.Count == 1); EndpointGroupViewModel groupCViewModel = groupAViewModel.Children[0]; Assert.AreEqual(groupC, groupCViewModel.Group); Assert.IsTrue(groupCViewModel.VisibleInputs.Count == 1); Assert.AreEqual(inputC, groupCViewModel.VisibleInputs.Items.First()); Assert.IsTrue(groupCViewModel.VisibleOutputs.Count == 0); }
public void TestNodeCollapse(EndpointVisibility visibility, bool nonCollapsedNonConnectedVisible, bool nonCollapsedConnectedVisible, bool collapsedNonConnectedVisible, bool collapsedConnectedVisible) { NodeOutputViewModel nodeAOutput = new NodeOutputViewModel(); NodeViewModel nodeA = new NodeViewModel { Outputs = { nodeAOutput } }; NodeInputViewModel nodeBInput = new NodeInputViewModel { Visibility = visibility }; NodeInputViewModel nodeBInput2 = new NodeInputViewModel { Visibility = visibility }; NodeOutputViewModel nodeBOutput = new NodeOutputViewModel { Visibility = visibility }; NodeOutputViewModel nodeBOutput2 = new NodeOutputViewModel { Visibility = visibility }; NodeViewModel nodeB = new NodeViewModel { Inputs = { nodeBInput, nodeBInput2 }, Outputs = { nodeBOutput, nodeBOutput2 } }; NodeInputViewModel nodeCInput = new NodeInputViewModel(); NodeViewModel nodeC = new NodeViewModel { Inputs = { nodeCInput } }; NetworkViewModel network = new NetworkViewModel { Nodes = { nodeA, nodeB, nodeC } }; network.Connections.Add(network.ConnectionFactory(nodeBInput, nodeAOutput)); network.Connections.Add(network.ConnectionFactory(nodeCInput, nodeBOutput)); var expectedInputSeq = Enumerable.Empty <Endpoint>(); var expectedOutputSeq = Enumerable.Empty <Endpoint>(); if (nonCollapsedConnectedVisible) { expectedInputSeq = expectedInputSeq.Concat(new[] { nodeBInput }); expectedOutputSeq = expectedOutputSeq.Concat(new[] { nodeBOutput }); } if (nonCollapsedNonConnectedVisible) { expectedInputSeq = expectedInputSeq.Concat(new[] { nodeBInput2 }); expectedOutputSeq = expectedOutputSeq.Concat(new[] { nodeBOutput2 }); } Assert.IsTrue(nodeB.VisibleInputs.SequenceEqual(expectedInputSeq)); Assert.IsTrue(nodeB.VisibleOutputs.SequenceEqual(expectedOutputSeq)); nodeB.IsCollapsed = true; expectedInputSeq = Enumerable.Empty <Endpoint>(); expectedOutputSeq = Enumerable.Empty <Endpoint>(); if (collapsedConnectedVisible) { expectedInputSeq = expectedInputSeq.Concat(new[] { nodeBInput }); expectedOutputSeq = expectedOutputSeq.Concat(new[] { nodeBOutput }); } if (collapsedNonConnectedVisible) { expectedInputSeq = expectedInputSeq.Concat(new[] { nodeBInput2 }); expectedOutputSeq = expectedOutputSeq.Concat(new[] { nodeBOutput2 }); } Assert.IsTrue(nodeB.VisibleInputs.SequenceEqual(expectedInputSeq)); Assert.IsTrue(nodeB.VisibleOutputs.SequenceEqual(expectedOutputSeq)); }
/// <summary> /// Move the specified set of nodes to a new subnetwork, create a new group node that contains this subnet, /// restore inter- and intra-network connections. /// </summary> /// <param name="network">The parent network</param> /// <param name="nodesToGroup">The nodes to group</param> /// <returns>Returns the NodeGroupIOBinding that was constructed for this group using the IOBindingFactory.</returns> public NodeGroupIOBinding MergeIntoGroup(NetworkViewModel network, IEnumerable <NodeViewModel> nodesToGroup) { if (!CheckPropertiesValid()) { throw new InvalidOperationException("All properties must be set before usage"); } else if (network == null) { throw new ArgumentNullException(nameof(network)); } else if (nodesToGroup == null) { throw new ArgumentNullException(nameof(nodesToGroup)); } var groupNodesSet = nodesToGroup is HashSet <NodeViewModel> set ? set : new HashSet <NodeViewModel>(nodesToGroup); // Check if nodesToGroup can be combined into a single group if (groupNodesSet.Count == 0 || !GraphAlgorithms.IsContinuousSubGraphSet(groupNodesSet)) { return(null); } // Create new empty group var subnet = SubNetworkFactory(); var groupNode = GroupNodeFactory(subnet); network.Nodes.Add(groupNode); var groupEntranceNode = EntranceNodeFactory(); var groupExitNode = ExitNodeFactory(); subnet.Nodes.AddRange(new [] { groupEntranceNode, groupExitNode }); // Map from input on a group member node to group node input var groupNodeInputs = new Dictionary <NodeInputViewModel, NodeInputViewModel>(); // Map from output on a group member node to group node output var groupNodeOutputs = new Dictionary <NodeOutputViewModel, NodeOutputViewModel>(); // Move the new nodes to appropriate positions groupNode.Position = new Point( groupNodesSet.Average(n => n.Position.X), groupNodesSet.Average(n => n.Position.Y) ); double yCoord = groupNodesSet.Average(n => n.Position.Y); groupEntranceNode.Position = new Point( groupNodesSet.Min(n => n.Position.X) - 100, yCoord ); groupExitNode.Position = new Point( groupNodesSet.Max(n => n.Position.X) + 100, yCoord ); // Setup binding between entrance/exit inputs and outputs var ioBinding = IOBindingFactory(groupNode, groupEntranceNode, groupExitNode); // Calculate set of connections to replace var subnetConnections = new List <ConnectionViewModel>(); var borderInputConnections = new List <ConnectionViewModel>(); var borderOutputConnections = new List <ConnectionViewModel>(); foreach (var con in network.Connections.Items) { bool inputIsInSubnet = groupNodesSet.Contains(con.Input.Parent); bool outputIsInSubnet = groupNodesSet.Contains(con.Output.Parent); if (inputIsInSubnet && outputIsInSubnet) { subnetConnections.Add(con); } else if (inputIsInSubnet) { borderInputConnections.Add(con); } else if (outputIsInSubnet) { borderOutputConnections.Add(con); } } // Construct inputs/outputs into/out of the group foreach (var borderInCon in borderInputConnections) { if (!groupNodeInputs.ContainsKey(borderInCon.Input)) { groupNodeInputs[borderInCon.Input] = ioBinding.AddNewGroupNodeInput(borderInCon.Output); } } foreach (var borderOutCon in borderOutputConnections) { if (!groupNodeOutputs.ContainsKey(borderOutCon.Output)) { groupNodeOutputs[borderOutCon.Output] = ioBinding.AddNewGroupNodeOutput(borderOutCon.Input); } } // Transfer nodes and inner connections to subnet network.Connections.Edit(l => { l.RemoveMany(subnetConnections); l.RemoveMany(borderInputConnections); l.RemoveMany(borderOutputConnections); }); network.Nodes.RemoveMany(groupNodesSet); subnet.Nodes.AddRange(groupNodesSet); subnet.Connections.AddRange(subnetConnections.Select(con => subnet.ConnectionFactory(con.Input, con.Output))); // Restore connections in/out of group network.Connections.AddRange(Enumerable.Concat( borderInputConnections.Select(con => network.ConnectionFactory(groupNodeInputs[con.Input], con.Output)), borderOutputConnections.Select(con => network.ConnectionFactory(con.Input, groupNodeOutputs[con.Output])) )); subnet.Connections.AddRange(Enumerable.Concat( borderInputConnections.Select(con => subnet.ConnectionFactory(con.Input, ioBinding.GetSubnetInlet(groupNodeInputs[con.Input]))), borderOutputConnections.Select(con => subnet.ConnectionFactory(ioBinding.GetSubnetOutlet(groupNodeOutputs[con.Output]), con.Output)) )); return(ioBinding); }