private void GetUpstreamIDrawable(List <IDrawable> drawables, Dictionary <int, Tuple <int, dynNodeModel> > inputs) { foreach (KeyValuePair <int, Tuple <int, dynNodeModel> > pair in inputs) { if (pair.Value == null) { continue; } dynNodeModel node = pair.Value.Item2; IDrawable drawable = node as IDrawable; if (node.IsVisible && drawable != null) { drawables.Add(drawable); } if (node.IsUpstreamVisible) { GetUpstreamIDrawable(drawables, node.Inputs); } else { continue; // don't bother checking if function } //if the node is function then get all the //drawables inside that node. only do this if the //node's workspace is the home space to avoid infinite //recursion in the case of custom nodes in custom nodes if (node is dynFunction && node.WorkSpace == dynSettings.Controller.DynamoModel.HomeSpace) { dynFunction func = (dynFunction)node; IEnumerable <dynNodeModel> topElements = func.Definition.Workspace.GetTopMostNodes(); foreach (dynNodeModel innerNode in topElements) { GetUpstreamIDrawable(drawables, innerNode.Inputs); } } } }
/// <summary> /// Get a dynFunction from a guid, also stores type internally info for future instantiation. /// And add the compiled node to the enviro /// As a side effect, any of its dependent nodes are also initialized. /// </summary> /// <param name="environment">The environment from which to get the </param> /// <param name="guid">Open a definition from a path, without instantiating the nodes or dependents</param> public bool GetNodeInstance(DynamoController controller, Guid guid, out dynFunction result) { if ( !this.Contains(guid) ) { result = null; return false; } FunctionDefinition def = null; if (!this.IsInitialized(guid)) { if (!GetDefinitionFromPath(guid, controller, out def)) { result = null; return false; } } else { def = this.loadedNodes[guid]; } dynWorkspace ws = def.Workspace; //TODO: Update to base off of Definition IEnumerable<string> inputs = ws.Nodes.Where(e => e is dynSymbol) .Select(s => (s as dynSymbol).Symbol); IEnumerable<string> outputs = ws.Nodes.Where(e => e is dynOutput) .Select(o => (o as dynOutput).Symbol); if (!outputs.Any()) { var topMost = new List<Tuple<int, dynNode>>(); IEnumerable<dynNode> topMostNodes = ws.GetTopMostNodes(); foreach (dynNode topNode in topMostNodes) { foreach (int output in Enumerable.Range(0, topNode.OutPortData.Count)) { if (!topNode.HasOutput(output)) topMost.Add(Tuple.Create(output, topNode)); } } outputs = topMost.Select(x => x.Item2.OutPortData[x.Item1].NickName); } result = new dynFunction(inputs, outputs, def); result.NodeUI.NickName = ws.Name; return true; }
/// <summary> /// Get a guid from the name of a node. If it doesn't exist, returns Guid.Empty. /// </summary> /// <param name="guid">Open a definition from a path, without instantiating the nodes or dependents</param> public bool GetNodeInstance(DynamoController controller, string name, out dynFunction result) { if (!this.Contains(name)) { result = null; return false; } return this.GetNodeInstance(controller, GetGuidFromName(name), out result); }
/// <summary> /// Collapse a set of nodes in a given workspace. Has the side effects of prompting the user /// first in order to obtain the name and category for the new node, /// writes the function to a dyf file, adds it to the FunctionDict, adds it to search, and compiles and /// places the newly created symbol (defining a lambda) in the Controller's FScheme Environment. /// </summary> /// <param name="selectedNodes"> The function definition for the user-defined node </param> /// <param name="currentWorkspace"> The workspace where</param> internal static void Collapse(IEnumerable<dynNode> selectedNodes, dynWorkspace currentWorkspace) { var selectedNodeSet = new HashSet<dynNode>(selectedNodes); // TODO: this code needs refactoring #region Prompt //First, prompt the user to enter a name string newNodeName, newNodeCategory; string error = ""; do { var dialog = new FunctionNamePrompt(dynSettings.Controller.SearchViewModel.Categories, error); if (dialog.ShowDialog() != true) { return; } newNodeName = dialog.Text; newNodeCategory = dialog.Category; if (dynSettings.Controller.CustomNodeLoader.Contains(newNodeName)) { error = "A function with this name already exists."; } else if (newNodeCategory.Equals("")) { error = "Please enter a valid category."; } else { error = ""; } } while (!error.Equals("")); var newNodeWorkspace = new FuncWorkspace(newNodeName, newNodeCategory, 0, 0); var newNodeDefinition = new FunctionDefinition(Guid.NewGuid()); newNodeDefinition.Workspace = newNodeWorkspace; #endregion currentWorkspace.DisableReporting(); #region Determine Inputs and Outputs //Step 1: determine which nodes will be inputs to the new node var inputs = new HashSet<Tuple<dynNode, int, Tuple<int, dynNode>>>( selectedNodeSet .SelectMany(node => Enumerable.Range(0, node.InPortData.Count) .Where(node.HasInput) .Select(data => Tuple.Create(node, data, node.Inputs[data])) .Where(input => !selectedNodeSet.Contains(input.Item3.Item2)))); var outputs = new HashSet<Tuple<dynNode, int, Tuple<int, dynNode>>>( selectedNodeSet.SelectMany( node => Enumerable.Range(0, node.OutPortData.Count).Where(node.HasOutput).SelectMany( data => node.Outputs[data] .Where(output => !selectedNodeSet.Contains(output.Item2)) .Select(output => Tuple.Create(node, data, output))))); #endregion #region Detect 1-node holes (higher-order function extraction) var curriedNodeArgs = new HashSet<dynNode>( inputs .Select(x => x.Item3.Item2) .Intersect(outputs.Select(x => x.Item3.Item2))) .Select( outerNode => { var node = new dynApply1(); dynNodeUI nodeUI = node.NodeUI; var elNameAttrib = node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), true)[0] as NodeNameAttribute; if (elNameAttrib != null) { nodeUI.NickName = elNameAttrib.Name; } nodeUI.GUID = Guid.NewGuid(); //store the element in the elements list newNodeWorkspace.Nodes.Add(node); node.WorkSpace = newNodeWorkspace; node.DisableReporting(); dynSettings.Bench.WorkBench.Children.Add(nodeUI); //Place it in an appropriate spot Canvas.SetLeft(nodeUI, Canvas.GetLeft(outerNode.NodeUI)); Canvas.SetTop(nodeUI, Canvas.GetTop(outerNode.NodeUI)); //Fetch all input ports // in order // that have inputs // and whose input comes from an inner node List<int> inPortsConnected = Enumerable.Range(0, outerNode.InPortData.Count) .Where( x => outerNode.HasInput(x) && selectedNodeSet.Contains( outerNode.Inputs[x].Item2)) .ToList(); var nodeInputs = outputs .Where(output => output.Item3.Item2 == outerNode) .Select( output => new { InnerNodeInputSender = output.Item1, OuterNodeInPortData = output.Item3.Item1 }).ToList(); nodeInputs.ForEach(_ => node.AddInput()); node.NodeUI.RegisterAllPorts(); dynSettings.Bench.WorkBench.UpdateLayout(); return new { OuterNode = outerNode, InnerNode = node, Outputs = inputs.Where(input => input.Item3.Item2 == outerNode) .Select(input => input.Item3.Item1), Inputs = nodeInputs, OuterNodePortDataList = inPortsConnected }; }).ToList(); #endregion #region UI Positioning Calculations double avgX = selectedNodeSet.Average(node => Canvas.GetLeft(node.NodeUI)); double avgY = selectedNodeSet.Average(node => Canvas.GetTop(node.NodeUI)); double leftMost = selectedNodeSet.Min(node => Canvas.GetLeft(node.NodeUI)) + 24; double topMost = selectedNodeSet.Min(node => Canvas.GetTop(node.NodeUI)); double rightMost = selectedNodeSet.Max(node => Canvas.GetLeft(node.NodeUI) + node.NodeUI.Width); #endregion #region Move selection to new workspace var connectors = new HashSet<dynConnector>( currentWorkspace.Connectors.Where( conn => selectedNodeSet.Contains(conn.Start.Owner.NodeLogic) && selectedNodeSet.Contains(conn.End.Owner.NodeLogic))); //Step 2: move all nodes to new workspace // remove from old currentWorkspace.Nodes.RemoveAll(selectedNodeSet.Contains); currentWorkspace.Connectors.RemoveAll(connectors.Contains); // add to new newNodeWorkspace.Nodes.AddRange(selectedNodeSet); newNodeWorkspace.Connectors.AddRange(connectors); double leftShift = leftMost - 250; foreach (dynNodeUI node in newNodeWorkspace.Nodes.Select(x => x.NodeUI)) { Canvas.SetLeft(node, Canvas.GetLeft(node) - leftShift); Canvas.SetTop(node, Canvas.GetTop(node) - topMost + 120); } #endregion #region Insert new node into the current workspace //Step 5: insert new node into original workspace var collapsedNode = new dynFunction( inputs.Select(x => x.Item1.InPortData[x.Item2].NickName), outputs .Where(x => !curriedNodeArgs.Any(y => y.OuterNode == x.Item3.Item2)) .Select(x => x.Item1.OutPortData[x.Item2].NickName), newNodeDefinition); collapsedNode.NodeUI.GUID = Guid.NewGuid(); currentWorkspace.Nodes.Add(collapsedNode); collapsedNode.WorkSpace = currentWorkspace; dynSettings.Bench.WorkBench.Children.Add(collapsedNode.NodeUI); Canvas.SetLeft(collapsedNode.NodeUI, avgX); Canvas.SetTop(collapsedNode.NodeUI, avgY); #endregion #region Destroy all hanging connectors //Step 6: connect inputs and outputs foreach (dynConnector connector in currentWorkspace.Connectors .Where( c => selectedNodeSet.Contains(c.Start.Owner.NodeLogic) && !selectedNodeSet.Contains(c.End.Owner.NodeLogic)) .ToList()) { connector.Kill(); } foreach (dynConnector connector in currentWorkspace.Connectors .Where( c => !selectedNodeSet.Contains(c.Start.Owner.NodeLogic) && selectedNodeSet.Contains(c.End.Owner.NodeLogic)).ToList() ) { connector.Kill(); } #endregion newNodeWorkspace.Nodes.ForEach(x => x.DisableReporting()); var inConnectors = new List<Tuple<dynNodeUI, int, int>>(); #region Process inputs //Step 3: insert variables (reference step 1) foreach (var input in Enumerable.Range(0, inputs.Count).Zip(inputs, Tuple.Create)) { int inputIndex = input.Item1; dynNode inputReceiverNode = input.Item2.Item1; int inputReceiverData = input.Item2.Item2; dynNode inputNode = input.Item2.Item3.Item2; int inputData = input.Item2.Item3.Item1; inConnectors.Add(new Tuple<dynNodeUI, int, int>(inputNode.NodeUI, inputData, inputIndex)); //Create Symbol Node var node = new dynSymbol { Symbol = inputReceiverNode.InPortData[inputReceiverData].NickName }; dynNodeUI nodeUI = node.NodeUI; var elNameAttrib = node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), true)[0] as NodeNameAttribute; if (elNameAttrib != null) { nodeUI.NickName = elNameAttrib.Name; } nodeUI.GUID = Guid.NewGuid(); //store the element in the elements list newNodeWorkspace.Nodes.Add(node); node.WorkSpace = newNodeWorkspace; node.DisableReporting(); dynSettings.Bench.WorkBench.Children.Add(nodeUI); //Place it in an appropriate spot Canvas.SetLeft(nodeUI, 0); Canvas.SetTop(nodeUI, inputIndex * (50 + node.NodeUI.Height)); dynSettings.Bench.WorkBench.UpdateLayout(); var curriedNode = curriedNodeArgs.FirstOrDefault( x => x.OuterNode == inputNode); if (curriedNode == null) { //Connect it (new dynConnector) newNodeWorkspace.Connectors.Add(new dynConnector( nodeUI, inputReceiverNode.NodeUI, 0, inputReceiverData, 0, false)); } else { //Connect it to the applier newNodeWorkspace.Connectors.Add(new dynConnector( nodeUI, curriedNode.InnerNode.NodeUI, 0, 0, 0, false)); //Connect applier to the inner input receiver newNodeWorkspace.Connectors.Add(new dynConnector( curriedNode.InnerNode.NodeUI, inputReceiverNode.NodeUI, 0, inputReceiverData, 0, false)); } } #endregion #region Process outputs //List of all inner nodes to connect an output. Unique. var outportList = new List<Tuple<dynNode, int>>(); var outConnectors = new List<Tuple<dynNodeUI, int, int>>(); int i = 0; foreach (var output in outputs) { if (outportList.All(x => !(x.Item1 == output.Item1 && x.Item2 == output.Item2))) { dynNode outputSenderNode = output.Item1; int outputSenderData = output.Item2; dynNode outputReceiverNode = output.Item3.Item2; if (curriedNodeArgs.Any(x => x.OuterNode == outputReceiverNode)) continue; outportList.Add(Tuple.Create(outputSenderNode, outputSenderData)); //Create Symbol Node var node = new dynOutput { Symbol = outputSenderNode.OutPortData[outputSenderData].NickName }; dynNodeUI nodeUI = node.NodeUI; var elNameAttrib = node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), false)[0] as NodeNameAttribute; if (elNameAttrib != null) { nodeUI.NickName = elNameAttrib.Name; } nodeUI.GUID = Guid.NewGuid(); //store the element in the elements list newNodeWorkspace.Nodes.Add(node); node.WorkSpace = newNodeWorkspace; node.DisableReporting(); dynSettings.Bench.WorkBench.Children.Add(nodeUI); //Place it in an appropriate spot Canvas.SetLeft(nodeUI, rightMost + 75 - leftShift); Canvas.SetTop(nodeUI, i * (50 + node.NodeUI.Height)); dynSettings.Bench.WorkBench.UpdateLayout(); newNodeWorkspace.Connectors.Add(new dynConnector( outputSenderNode.NodeUI, nodeUI, outputSenderData, 0, 0, false)); i++; } } //Connect outputs to new node foreach (var output in outputs) { //Node to be connected to in CurrentSpace dynNode outputSenderNode = output.Item1; //Port to be connected to on outPutNode_outer int outputSenderData = output.Item2; int outputReceiverData = output.Item3.Item1; dynNode outputReceiverNode = output.Item3.Item2; var curriedNode = curriedNodeArgs.FirstOrDefault( x => x.OuterNode == outputReceiverNode); if (curriedNode == null) { // we create the connectors in the current space later outConnectors.Add(new Tuple<dynNodeUI, int, int>(outputReceiverNode.NodeUI, outportList.FindIndex(x => x.Item1 == outputSenderNode && x.Item2 == outputSenderData), outputReceiverData)); } else { int targetPort = curriedNode.Inputs .First( x => x.InnerNodeInputSender == outputSenderNode) .OuterNodeInPortData; int targetPortIndex = curriedNode.OuterNodePortDataList.IndexOf(targetPort); //Connect it (new dynConnector) newNodeWorkspace.Connectors.Add(new dynConnector( outputSenderNode.NodeUI, curriedNode.InnerNode.NodeUI, outputSenderData, targetPortIndex + 1, 0)); } } #endregion #region Make new workspace invisible //Step 4: make nodes invisible // and update positions foreach (dynNodeUI node in newNodeWorkspace.Nodes.Select(x => x.NodeUI)) node.Visibility = Visibility.Hidden; foreach (dynConnector connector in newNodeWorkspace.Connectors) connector.Visible = false; #endregion //set the name on the node collapsedNode.NodeUI.NickName = newNodeName; currentWorkspace.Nodes.Remove(collapsedNode); dynSettings.Bench.WorkBench.Children.Remove(collapsedNode.NodeUI); // save and load the definition from file var path = dynSettings.Controller.SaveFunctionOnly(newNodeDefinition); dynSettings.Controller.CustomNodeLoader.SetNodeInfo(newNodeName, newNodeCategory, newNodeDefinition.FunctionId, path); dynSettings.Controller.SearchViewModel.Add(newNodeName, newNodeCategory, newNodeDefinition.FunctionId); DynamoCommands.CreateNodeCmd.Execute(new Dictionary<string, object>() { {"name", collapsedNode.Definition.FunctionId.ToString() }, {"x", avgX }, {"y", avgY } }); var newlyPlacedCollapsedNode = currentWorkspace.Nodes .Where(node => node is dynFunction) .First(node => ((dynFunction)node).Definition.FunctionId == newNodeDefinition.FunctionId); newlyPlacedCollapsedNode.DisableReporting(); dynSettings.Bench.WorkBench.UpdateLayout(); // without doing this, connectors fail to be created foreach (var nodeTuple in inConnectors) { currentWorkspace.Connectors.Add( new dynConnector( nodeTuple.Item1, newlyPlacedCollapsedNode.NodeUI, nodeTuple.Item2, nodeTuple.Item3, 0, true)); } foreach (var nodeTuple in outConnectors) { currentWorkspace.Connectors.Add( new dynConnector( newlyPlacedCollapsedNode.NodeUI, nodeTuple.Item1, nodeTuple.Item2, nodeTuple.Item3, 0, true)); } newlyPlacedCollapsedNode.EnableReporting(); currentWorkspace.EnableReporting(); }
internal void NodeFromSelection(IEnumerable<dynNode> selectedNodes) { var selectedNodeSet = new HashSet<dynNode>(selectedNodes); #region Prompt //First, prompt the user to enter a name string newNodeName, newNodeCategory; string error = ""; do { var dialog = new FunctionNamePrompt(Bench.addMenuCategoryDict.Keys, error); if (dialog.ShowDialog() != true) { return; } newNodeName = dialog.Text; newNodeCategory = dialog.Category; if (FunctionDict.ContainsKey(newNodeName)) { error = "A function with this name already exists."; } else if (newNodeCategory.Equals("")) { error = "Please enter a valid category."; } else { error = ""; } } while (!error.Equals("")); var newNodeWorkspace = NewFunction(newNodeName, newNodeCategory, false); #endregion CurrentSpace.DisableReporting(); #region UI Positioning Calculations var avgX = selectedNodeSet.Average(node => Canvas.GetLeft(node.NodeUI)); var avgY = selectedNodeSet.Average(node => Canvas.GetTop(node.NodeUI)); var leftMost = selectedNodeSet.Min(node => Canvas.GetLeft(node.NodeUI)); var topMost = selectedNodeSet.Min(node => Canvas.GetTop(node.NodeUI)); var rightMost = selectedNodeSet.Max(node => Canvas.GetLeft(node.NodeUI) + node.NodeUI.Width); #endregion #region Determine Inputs and Outputs //Step 1: determine which nodes will be inputs to the new node var inputs = new HashSet<Tuple<dynNode, int, Tuple<int, dynNode>>>( selectedNodeSet.SelectMany( node => Enumerable.Range(0, node.InPortData.Count).Where(node.HasInput).Select( data => Tuple.Create(node, data, node.Inputs[data])).Where( input => !selectedNodeSet.Contains(input.Item3.Item2)))); var outputs = new HashSet<Tuple<dynNode, int, Tuple<int, dynNode>>>( selectedNodeSet.SelectMany( node => Enumerable.Range(0, node.OutPortData.Count).Where(node.HasOutput).SelectMany( data => node.Outputs[data] .Where(output => !selectedNodeSet.Contains(output.Item2)) .Select(output => Tuple.Create(node, data, output))))); #endregion #region Detect 1-node holes (higher-order function extraction) var curriedNodeArgs = new HashSet<dynNode>( inputs .Select(x => x.Item3.Item2) .Intersect(outputs.Select(x => x.Item3.Item2))) .Select( outerNode => { var node = new dynApply1(); var nodeUI = node.NodeUI; NodeNameAttribute elNameAttrib = node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), true)[0] as NodeNameAttribute; if (elNameAttrib != null) { nodeUI.NickName = elNameAttrib.Name; } nodeUI.GUID = Guid.NewGuid(); //store the element in the elements list newNodeWorkspace.Nodes.Add(node); node.WorkSpace = newNodeWorkspace; node.DisableReporting(); Bench.WorkBench.Children.Add(nodeUI); //Place it in an appropriate spot Canvas.SetLeft(nodeUI, Canvas.GetLeft(outerNode.NodeUI)); Canvas.SetTop(nodeUI, Canvas.GetTop(outerNode.NodeUI)); //Fetch all input ports // in order // that have inputs // and whose input comes from an inner node var inPortsConnected = Enumerable.Range(0, outerNode.InPortData.Count) .Where(x => outerNode.HasInput(x) && selectedNodeSet.Contains(outerNode.Inputs[x].Item2)) .ToList(); var nodeInputs = outputs .Where(output => output.Item3.Item2 == outerNode) .Select( output => new { InnerNodeInputSender = output.Item1, OuterNodeInPortData = output.Item3.Item1 }).ToList(); nodeInputs.ForEach(_ => node.AddInput()); node.NodeUI.RegisterAllPorts(); Bench.WorkBench.UpdateLayout(); return new { OuterNode = outerNode, InnerNode = node, Outputs = inputs.Where(input => input.Item3.Item2 == outerNode) .Select(input => input.Item3.Item1), Inputs = nodeInputs, OuterNodePortDataList = inPortsConnected }; }).ToList(); #endregion #region Move selection to new workspace var connectors = new HashSet<dynConnector>( CurrentSpace.Connectors.Where( conn => selectedNodeSet.Contains(conn.Start.Owner.NodeLogic) && selectedNodeSet.Contains(conn.End.Owner.NodeLogic))); //Step 2: move all nodes to new workspace // remove from old CurrentSpace.Nodes.RemoveAll(selectedNodeSet.Contains); CurrentSpace.Connectors.RemoveAll(connectors.Contains); // add to new newNodeWorkspace.Nodes.AddRange(selectedNodeSet); newNodeWorkspace.Connectors.AddRange(connectors); var leftShift = leftMost - 250; foreach (var node in newNodeWorkspace.Nodes.Select(x => x.NodeUI)) { Canvas.SetLeft(node, Canvas.GetLeft(node) - leftShift); Canvas.SetTop(node, Canvas.GetTop(node) - topMost); } #endregion #region Insert new node replacement into the current workspace //Step 5: insert new node into original workspace var collapsedNode = new dynFunction( inputs.Select(x => x.Item1.InPortData[x.Item2].NickName), outputs .Where(x => !curriedNodeArgs.Any(y => y.OuterNode == x.Item3.Item2)) .Select(x => x.Item1.OutPortData[x.Item2].NickName), newNodeName); collapsedNode.NodeUI.GUID = Guid.NewGuid(); CurrentSpace.Nodes.Add(collapsedNode); collapsedNode.WorkSpace = CurrentSpace; Bench.WorkBench.Children.Add(collapsedNode.NodeUI); Canvas.SetLeft(collapsedNode.NodeUI, avgX); Canvas.SetTop(collapsedNode.NodeUI, avgY); Bench.WorkBench.UpdateLayout(); #endregion #region Destroy all hanging connectors //Step 6: connect inputs and outputs foreach (var connector in CurrentSpace.Connectors .Where(c => selectedNodeSet.Contains(c.Start.Owner.NodeLogic) && !selectedNodeSet.Contains(c.End.Owner.NodeLogic)).ToList()) { connector.Kill(); } foreach (var connector in CurrentSpace.Connectors .Where(c => !selectedNodeSet.Contains(c.Start.Owner.NodeLogic) && selectedNodeSet.Contains(c.End.Owner.NodeLogic)).ToList()) { connector.Kill(); } #endregion newNodeWorkspace.Nodes.ForEach(x => x.DisableReporting()); #region Process inputs //Step 3: insert variables (reference step 1) foreach (var input in Enumerable.Range(0, inputs.Count).Zip(inputs, Tuple.Create)) { var inputIndex = input.Item1; var inputReceiverNode = input.Item2.Item1; var inputReceiverData = input.Item2.Item2; var inputNode = input.Item2.Item3.Item2; var inputData = input.Item2.Item3.Item1; //Connect outside input to the node CurrentSpace.Connectors.Add( new dynConnector( inputNode.NodeUI, collapsedNode.NodeUI, inputData, inputIndex, 0, true)); //Create Symbol Node dynSymbol node = new dynSymbol() { Symbol = inputReceiverNode.InPortData[inputReceiverData].NickName }; var nodeUI = node.NodeUI; NodeNameAttribute elNameAttrib = node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), true)[0] as NodeNameAttribute; if (elNameAttrib != null) { nodeUI.NickName = elNameAttrib.Name; } nodeUI.GUID = Guid.NewGuid(); //store the element in the elements list newNodeWorkspace.Nodes.Add(node); node.WorkSpace = newNodeWorkspace; node.DisableReporting(); Bench.WorkBench.Children.Add(nodeUI); //Place it in an appropriate spot Canvas.SetLeft(nodeUI, 0); Canvas.SetTop(nodeUI, inputIndex * (50 + node.NodeUI.Height)); Bench.WorkBench.UpdateLayout(); var curriedNode = curriedNodeArgs.FirstOrDefault( x => x.OuterNode == inputNode); if (curriedNode == null) { //Connect it (new dynConnector) newNodeWorkspace.Connectors.Add(new dynConnector( nodeUI, inputReceiverNode.NodeUI, 0, inputReceiverData, 0, false)); } else { //Connect it to the applier newNodeWorkspace.Connectors.Add(new dynConnector( nodeUI, curriedNode.InnerNode.NodeUI, 0, 0, 0, false)); //Connect applier to the inner input receiver newNodeWorkspace.Connectors.Add(new dynConnector( curriedNode.InnerNode.NodeUI, inputReceiverNode.NodeUI, 0, inputReceiverData, 0, false)); } } #endregion #region Process outputs //List of all inner nodes to connect an output. Unique. var outportList = new List<Tuple<dynNode, int>>(); int i = 0; foreach (var output in outputs) { if (outportList.All(x => !(x.Item1 == output.Item1 && x.Item2 == output.Item2))) { var outputSenderNode = output.Item1; var outputSenderData = output.Item2; var outputReceiverNode = output.Item3.Item2; if (curriedNodeArgs.Any(x => x.OuterNode == outputReceiverNode)) continue; outportList.Add(Tuple.Create(outputSenderNode, outputSenderData)); //Create Symbol Node var node = new dynOutput() { Symbol = outputSenderNode.OutPortData[outputSenderData].NickName }; var nodeUI = node.NodeUI; NodeNameAttribute elNameAttrib = node.GetType().GetCustomAttributes(typeof(NodeNameAttribute), false)[0] as NodeNameAttribute; if (elNameAttrib != null) { nodeUI.NickName = elNameAttrib.Name; } nodeUI.GUID = Guid.NewGuid(); //store the element in the elements list newNodeWorkspace.Nodes.Add(node); node.WorkSpace = newNodeWorkspace; node.DisableReporting(); Bench.WorkBench.Children.Add(nodeUI); //Place it in an appropriate spot Canvas.SetLeft(nodeUI, rightMost + 75 - leftShift); Canvas.SetTop(nodeUI, i * (50 + node.NodeUI.Height)); Bench.WorkBench.UpdateLayout(); newNodeWorkspace.Connectors.Add(new dynConnector( outputSenderNode.NodeUI, nodeUI, outputSenderData, 0, 0, false)); i++; } } //Connect outputs to new node foreach (var output in outputs) { //Node to be connected to in CurrentSpace var outputSenderNode = output.Item1; //Port to be connected to on outPutNode_outer var outputSenderData = output.Item2; var outputReceiverData = output.Item3.Item1; var outputReceiverNode = output.Item3.Item2; var curriedNode = curriedNodeArgs.FirstOrDefault( x => x.OuterNode == outputReceiverNode); if (curriedNode == null) { CurrentSpace.Connectors.Add( new dynConnector( collapsedNode.NodeUI, outputReceiverNode.NodeUI, outportList.FindIndex(x => x.Item1 == outputSenderNode && x.Item2 == outputSenderData), outputReceiverData, 0, true)); } else { var targetPort = curriedNode.Inputs .First( x => x.InnerNodeInputSender == outputSenderNode) .OuterNodeInPortData; var targetPortIndex = curriedNode.OuterNodePortDataList.IndexOf(targetPort); //Connect it (new dynConnector) newNodeWorkspace.Connectors.Add(new dynConnector( outputSenderNode.NodeUI, curriedNode.InnerNode.NodeUI, outputSenderData, targetPortIndex + 1, 0)); } } #endregion #region Make new workspace invisible //Step 4: make nodes invisible // and update positions foreach (var node in newNodeWorkspace.Nodes.Select(x => x.NodeUI)) node.Visibility = Visibility.Hidden; foreach (var connector in newNodeWorkspace.Connectors) connector.Visible = false; #endregion newNodeWorkspace.Nodes.ForEach(x => { x.EnableReporting(); x.NodeUI.UpdateConnections(); }); collapsedNode.EnableReporting(); collapsedNode.NodeUI.UpdateConnections(); CurrentSpace.EnableReporting(); SaveFunction(newNodeWorkspace, true); }
internal dynWorkspace NewFunction(string name, string category, bool display) { //Add an entry to the funcdict var workSpace = new FuncWorkspace(name, category, dynBench.CANVAS_OFFSET_X, dynBench.CANVAS_OFFSET_Y); var newElements = workSpace.Nodes; var newConnectors = workSpace.Connectors; this.FunctionDict[name] = workSpace; //Add an entry to the View menu System.Windows.Controls.MenuItem i = new System.Windows.Controls.MenuItem(); i.Header = name; i.Click += new RoutedEventHandler(Bench.ChangeView_Click); Bench.viewMenu.Items.Add(i); Bench.viewMenuItemsDict[name] = i; //Add an entry to the Add menu //System.Windows.Controls.MenuItem mi = new System.Windows.Controls.MenuItem(); //mi.Header = name; //mi.Click += new RoutedEventHandler(AddElement_Click); //AddMenu.Items.Add(mi); //this.addMenuItemsDict[name] = mi; dynFunction newEl = new dynFunction( workSpace.Nodes.Where(el => el is dynSymbol) .Select(s => ((dynSymbol)s).Symbol), new List<String>() { "out" }, name ); newEl.NodeUI.DisableInteraction(); newEl.NodeUI.MouseDown += delegate { Bench.BeginDragElement(newEl.NodeUI, name, Mouse.GetPosition(newEl.NodeUI)); newEl.NodeUI.Visibility = System.Windows.Visibility.Hidden; }; newEl.NodeUI.GUID = Guid.NewGuid(); newEl.NodeUI.Margin = new Thickness(5, 30, 5, 5); newEl.NodeUI.LayoutTransform = new ScaleTransform(.8, .8); newEl.NodeUI.State = ElementState.DEAD; Expander expander; if (Bench.addMenuCategoryDict.ContainsKey(category)) { expander = Bench.addMenuCategoryDict[category]; } else { expander = new Expander() { Header = category, Height = double.NaN, Margin = new Thickness(0, 5, 0, 0), Content = new WrapPanel() { Height = double.NaN, Width = 240 }, HorizontalAlignment = System.Windows.HorizontalAlignment.Left, //FontWeight = FontWeights.Bold }; Bench.addMenuCategoryDict[category] = expander; var sortedExpanders = new SortedList<string, Expander>(); foreach (Expander child in Bench.SideStackPanel.Children) { sortedExpanders.Add((string)child.Header, child); } sortedExpanders.Add(category, expander); Bench.SideStackPanel.Children.Clear(); foreach (Expander child in sortedExpanders.Values) { Bench.SideStackPanel.Children.Add(child); } } var wp = (WrapPanel)expander.Content; var sortedElements = new SortedList<string, dynNodeUI>(); foreach (dynNodeUI child in wp.Children) { sortedElements.Add(child.NickName, child); } sortedElements.Add(name, newEl.NodeUI); wp.Children.Clear(); foreach (dynNodeUI child in sortedElements.Values) { wp.Children.Add(child); } Bench.addMenuItemsDictNew[name] = newEl.NodeUI; searchDict.Add(newEl.NodeUI, name.Split(' ').Where(x => x.Length > 0)); if (display) { //Store old workspace //var ws = new dynWorkspace(this.elements, this.connectors, this.CurrentX, this.CurrentY); if (!this.ViewingHomespace) { //Step 2: Store function workspace in the function dictionary this.FunctionDict[this.CurrentSpace.Name] = this.CurrentSpace; //Step 3: Save function this.SaveFunction(this.CurrentSpace); } //Make old workspace invisible foreach (dynNode dynE in this.Nodes) { dynE.NodeUI.Visibility = System.Windows.Visibility.Collapsed; } foreach (dynConnector dynC in this.CurrentSpace.Connectors) { dynC.Visible = false; } foreach (dynNote note in this.CurrentSpace.Notes) { note.Visibility = System.Windows.Visibility.Hidden; } //this.currentFunctionName = name; ////Clear the bench for the new function //this.elements = newElements; //this.connectors = newConnectors; //this.CurrentX = CANVAS_OFFSET_X; //this.CurrentY = CANVAS_OFFSET_Y; this.CurrentSpace = workSpace; //this.saveFuncItem.IsEnabled = true; Bench.homeButton.IsEnabled = true; //this.varItem.IsEnabled = true; Bench.workspaceLabel.Content = this.CurrentSpace.Name; Bench.editNameButton.Visibility = System.Windows.Visibility.Visible; Bench.editNameButton.IsHitTestVisible = true; Bench.setFunctionBackground(); } return workSpace; }
internal dynNode CreateDragNode(string name) { dynNode result; if (FunctionDict.ContainsKey(name)) { dynWorkspace ws = FunctionDict[name]; var inputs = ws.Nodes.Where(e => e is dynSymbol) .Select(s => (s as dynSymbol).Symbol); var outputs = ws.Nodes.Where(e => e is dynOutput) .Select(o => (o as dynOutput).Symbol); if (!outputs.Any()) { var topMost = new List<Tuple<int, dynNode>>(); var topMostNodes = ws.GetTopMostNodes(); foreach (var topNode in topMostNodes) { foreach (var output in Enumerable.Range(0, topNode.OutPortData.Count)) { if (!topNode.HasOutput(output)) topMost.Add(Tuple.Create(output, topNode)); } } outputs = topMost.Select(x => x.Item2.OutPortData[x.Item1].NickName); } result = new dynFunction(inputs, outputs, name); } else { TypeLoadData tld = builtinTypesByNickname[name]; var obj = Activator.CreateInstanceFrom(tld.Assembly.Location, tld.Type.FullName); var newEl = (dynNode)obj.Unwrap(); newEl.NodeUI.DisableInteraction(); result = newEl; } if (result is dynDouble) (result as dynDouble).Value = this.storedSearchNum; else if (result is dynStringInput) (result as dynStringInput).Value = this.storedSearchStr; else if (result is dynBool) (result as dynBool).Value = this.storedSearchBool; return result; }