Example #1
0
        /// <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);
        }