/// <summary> /// This method pushes changes from the GraphLayout.Graph objects /// back to the workspace models, but only for nodes placed by NodeAutocomplete. /// </summary> private static void SaveLayoutGraphForNodeAutoComplete(this WorkspaceModel workspace, List <GraphLayout.Graph> layoutSubgraphs, Guid?originalNodeGUID) { // Assign coordinates to nodes outside groups foreach (var node in workspace.Nodes) { GraphLayout.Graph graph = layoutSubgraphs .FirstOrDefault(g => g.FindNode(node.GUID) != null); if (graph != null) { GraphLayout.Node n = graph.FindNode(node.GUID); double offsetY = graph.OffsetY; //skipping the original node to avoid jumping of node if (node.GUID != originalNodeGUID) { node.X = n.X; node.Y = n.Y + n.NotesHeight + offsetY; } node.ReportPosition(); workspace.HasUnsavedChanges = true; double noteOffset = -n.NotesHeight; foreach (NoteModel note in n.LinkedNotes) { if (note.IsSelected || DynamoSelection.Instance.Selection.Count == 0) { note.X = node.X; note.Y = node.Y + noteOffset; noteOffset += note.Height + GraphLayout.Graph.VerticalNoteDistance; note.ReportPosition(); } } } } }
/// <summary> /// <param name="workspace">A <see cref="WorkspaceModel"/>.</param> /// This method extracts all models from the workspace and puts them /// into the combined graph object, LayoutSubgraphs.First() /// <param name="isGroupLayout">True if all the selected models are groups.</param> /// <param name="layoutSubgraphs"></param> /// <param name="subgraphClusters"></param> /// </summary> private static void GenerateCombinedGraph(this WorkspaceModel workspace, bool isGroupLayout, out List <GraphLayout.Graph> layoutSubgraphs, out List <List <GraphLayout.Node> > subgraphClusters) { layoutSubgraphs = new List <GraphLayout.Graph> { new GraphLayout.Graph() }; var combinedGraph = layoutSubgraphs.First(); subgraphClusters = new List <List <GraphLayout.Node> >(); if (!isGroupLayout) { foreach (AnnotationModel group in workspace.Annotations) { // Treat a group as a graph layout node/vertex combinedGraph.AddNode(group.GUID, group.Width, group.Height, group.X, group.Y, group.IsSelected || DynamoSelection.Instance.Selection.Count == 0); } } foreach (NodeModel node in workspace.Nodes) { if (!isGroupLayout) { AnnotationModel group = workspace.Annotations.Where( g => g.Nodes.Contains(node)).ToList().FirstOrDefault(); // Do not process nodes within groups if (group == null) { combinedGraph.AddNode(node.GUID, node.Width, node.Height, node.X, node.Y, node.IsSelected || DynamoSelection.Instance.Selection.Count == 0); } } else { // Process all nodes inside the selection combinedGraph.AddNode(node.GUID, node.Width, node.Height, node.X, node.Y, node.IsSelected || DynamoSelection.Instance.Selection.Count == 0); } } foreach (ConnectorModel edge in workspace.Connectors) { if (!isGroupLayout) { AnnotationModel startGroup = null, endGroup = null; startGroup = workspace.Annotations.Where( g => g.Nodes.Contains(edge.Start.Owner)).ToList().FirstOrDefault(); endGroup = workspace.Annotations.Where( g => g.Nodes.Contains(edge.End.Owner)).ToList().FirstOrDefault(); // Treat a group as a node, but do not process edges within a group if (startGroup == null || endGroup == null || startGroup != endGroup) { combinedGraph.AddEdge( startGroup == null ? edge.Start.Owner.GUID : startGroup.GUID, endGroup == null ? edge.End.Owner.GUID : endGroup.GUID, edge.Start.Center.X, edge.Start.Center.Y, edge.End.Center.X, edge.End.Center.Y); } } else { // Edges within a group are also processed combinedGraph.AddEdge(edge.Start.Owner.GUID, edge.End.Owner.GUID, edge.Start.Center.X, edge.Start.Center.Y, edge.End.Center.X, edge.End.Center.Y); } } foreach (NoteModel note in workspace.Notes) { AnnotationModel group = workspace.Annotations.Where( g => g.Nodes.Contains(note)).ToList().FirstOrDefault(); GraphLayout.Node nd = null; if (!isGroupLayout || group == null) { // If note is not part of a group, link to the nearest node in the graph nd = combinedGraph.Nodes.OrderBy(node => Math.Pow(node.X + node.Width / 2 - note.X - note.Width / 2, 2) + Math.Pow(node.Y + node.Height / 2 - note.Y - note.Height / 2, 2)).FirstOrDefault(); } else { // If note is part of a group, link to the nearest node in the group NodeModel ndm = group.Nodes.OfType <NodeModel>().OrderBy(node => Math.Pow(node.X + node.Width / 2 - note.X - note.Width / 2, 2) + Math.Pow(node.Y + node.Height / 2 - note.Y - note.Height / 2, 2)).FirstOrDefault(); // Skip processing the group if there is no node in the group if (ndm == null) { continue; } // If the nearest point is a node model nd = combinedGraph.FindNode(ndm.GUID); // If the nearest point is a group model nd = nd ?? combinedGraph.FindNode(group.GUID); } // Otherwise, leave the note unchanged if (nd != null) { nd.LinkNote(note, note.Width, note.Height); } } if (!isGroupLayout) { // Add all nodes to one big cluster List <GraphLayout.Node> bigcluster = new List <GraphLayout.Node>(); bigcluster.AddRange(combinedGraph.Nodes); subgraphClusters.Add(bigcluster); } else { // Each group becomes one cluster foreach (AnnotationModel group in DynamoSelection.Instance.Selection.OfType <AnnotationModel>()) { List <GraphLayout.Node> cluster = new List <GraphLayout.Node>(); cluster.AddRange(group.Nodes.OfType <NodeModel>().Select(x => combinedGraph.FindNode(x.GUID))); subgraphClusters.Add(cluster); } } }
/// <summary> /// This method pushes changes from the GraphLayout.Graph objects /// back to the workspace models. /// </summary> private static void SaveLayoutGraph(this WorkspaceModel workspace, List <GraphLayout.Graph> layoutSubgraphs) { // Assign coordinates to nodes inside groups foreach (var group in workspace.Annotations) { GraphLayout.Graph graph = layoutSubgraphs .FirstOrDefault(g => g.FindNode(group.GUID) != null); if (graph != null) { GraphLayout.Node n = graph.FindNode(group.GUID); double deltaX = n.X - group.X; double deltaY = n.Y - group.Y + graph.OffsetY; foreach (var node in group.Nodes.OfType <NodeModel>()) { node.X += deltaX; node.Y += deltaY; node.ReportPosition(); } foreach (NoteModel note in n.LinkedNotes) { if (note.IsSelected || DynamoSelection.Instance.Selection.Count == 0) { note.X += deltaX; note.Y += deltaY; note.ReportPosition(); } } } } // Assign coordinates to nodes outside groups foreach (var node in workspace.Nodes) { GraphLayout.Graph graph = layoutSubgraphs .FirstOrDefault(g => g.FindNode(node.GUID) != null); if (graph != null) { GraphLayout.Node n = graph.FindNode(node.GUID); double offsetY = graph.OffsetY; node.X = n.X; node.Y = n.Y + n.NotesHeight + offsetY; node.ReportPosition(); workspace.HasUnsavedChanges = true; double noteOffset = -n.NotesHeight; foreach (NoteModel note in n.LinkedNotes) { if (note.IsSelected || DynamoSelection.Instance.Selection.Count == 0) { note.X = node.X; note.Y = node.Y + noteOffset; noteOffset += note.Height + GraphLayout.Graph.VerticalNoteDistance; note.ReportPosition(); } } } } }
/// <summary> /// <param name="workspace">A <see cref="WorkspaceModel"/>.</param> /// This method extracts all models from the workspace and puts them /// into the combined graph object, LayoutSubgraphs.First() /// <param name="isGroupLayout">True if all the selected models are groups.</param> /// <param name="layoutSubgraphs"></param> /// <param name="subgraphClusters"></param> /// </summary> private static void GenerateCombinedGraph(this WorkspaceModel workspace, bool isGroupLayout, out List <GraphLayout.Graph> layoutSubgraphs, out List <List <GraphLayout.Node> > subgraphClusters) { layoutSubgraphs = new List <GraphLayout.Graph> { new GraphLayout.Graph() }; var combinedGraph = layoutSubgraphs.First(); subgraphClusters = new List <List <GraphLayout.Node> >(); if (!isGroupLayout) { foreach (AnnotationModel group in workspace.Annotations) { // Treat a group as a graph layout node/vertex combinedGraph.AddNode(group.GUID, group.Width, group.Height, group.X, group.Y, group.IsSelected || DynamoSelection.Instance.Selection.Count == 0); } } foreach (NodeModel node in workspace.Nodes) { if (!isGroupLayout) { AnnotationModel group = workspace.Annotations.Where( g => g.Nodes.Contains(node)).ToList().FirstOrDefault(); // Do not process nodes within groups if (group == null) { combinedGraph.AddNode(node.GUID, node.Width, node.Height, node.X, node.Y, node.IsSelected || DynamoSelection.Instance.Selection.Count == 0); } } else { // Process all nodes inside the selection combinedGraph.AddNode(node.GUID, node.Width, node.Height, node.X, node.Y, node.IsSelected || DynamoSelection.Instance.Selection.Count == 0); } } ///Adding all connectorPins (belonging to all connectors) as graph.nodes to the combined graph. foreach (ConnectorModel edge in workspace.Connectors) { foreach (var pin in edge.ConnectorPinModels) { combinedGraph.AddNode(pin.GUID, pin.Width, pin.Height, pin.CenterX, pin.CenterY, pin.IsSelected || DynamoSelection.Instance.Selection.Count == 0); } } foreach (ConnectorModel edge in workspace.Connectors) { if (!isGroupLayout) { AnnotationModel startGroup = null, endGroup = null; startGroup = workspace.Annotations.Where( g => g.Nodes.Contains(edge.Start.Owner)).ToList().FirstOrDefault(); endGroup = workspace.Annotations.Where( g => g.Nodes.Contains(edge.End.Owner)).ToList().FirstOrDefault(); // Treat a group as a node, but do not process edges within a group if (startGroup == null || endGroup == null || startGroup != endGroup) { var startGuid = startGroup == null ? edge.Start.Owner.GUID : startGroup.GUID; var endGuid = endGroup == null ? edge.End.Owner.GUID : endGroup.GUID; AddConnectorEdgesIncludingPinEdges(combinedGraph, edge, startGuid, endGuid); } } else { AddConnectorEdgesIncludingPinEdges(combinedGraph, edge); } } var sortedNotes = workspace.Notes.OrderBy(x => x.PinnedNode is null); foreach (NoteModel note in sortedNotes) { // If the note is pinned to a node we dont want to // modify its posistion as it is tied to the node. if (note.PinnedNode != null) { // We add this note to the LinkedNotes on the // pinned node. var graphNode = combinedGraph.FindNode(note.PinnedNode.GUID); var height = note.PinnedNode.Rect.Top - note.Rect.Top; graphNode.LinkNote(note, note.Width, height); continue; } AnnotationModel group = workspace.Annotations.Where( g => g.Nodes.Contains(note)).ToList().FirstOrDefault(); GraphLayout.Node nd = null; if (!isGroupLayout || group == null) { // If note is not part of a group, link to the nearest node in the graph nd = combinedGraph.Nodes.OrderBy(node => Math.Pow(node.X + node.Width / 2 - note.X - note.Width / 2, 2) + Math.Pow(node.Y + node.Height / 2 - note.Y - note.Height / 2, 2)).FirstOrDefault(); } else { // If note is part of a group, link to the nearest node in the group NodeModel ndm = group.Nodes.OfType <NodeModel>().OrderBy(node => Math.Pow(node.X + node.Width / 2 - note.X - note.Width / 2, 2) + Math.Pow(node.Y + node.Height / 2 - note.Y - note.Height / 2, 2)).FirstOrDefault(); // Skip processing the group if there is no node in the group if (ndm == null) { continue; } // If the nearest point is a node model nd = combinedGraph.FindNode(ndm.GUID); // If the nearest point is a group model nd = nd ?? combinedGraph.FindNode(group.GUID); } // Otherwise, leave the note unchanged if (nd != null) { nd.LinkNote(note, note.Width, note.Height); } } if (!isGroupLayout) { // Add all nodes to one big cluster List <GraphLayout.Node> bigcluster = new List <GraphLayout.Node>(); bigcluster.AddRange(combinedGraph.Nodes); subgraphClusters.Add(bigcluster); } else { // Each group becomes one cluster foreach (AnnotationModel group in DynamoSelection.Instance.Selection.OfType <AnnotationModel>()) { List <GraphLayout.Node> cluster = new List <GraphLayout.Node>(); cluster.AddRange(group.Nodes.OfType <NodeModel>().Select(x => combinedGraph.FindNode(x.GUID))); subgraphClusters.Add(cluster); } } }
/// <summary> /// This method pushes changes from the GraphLayout.Graph objects /// back to the workspace models. /// </summary> private static void SaveLayoutGraph(this WorkspaceModel workspace, List <GraphLayout.Graph> layoutSubgraphs) { // Assign coordinates to nodes inside groups foreach (var group in workspace.Annotations) { GraphLayout.Graph graph = layoutSubgraphs .FirstOrDefault(g => g.FindNode(group.GUID) != null); if (graph != null) { GraphLayout.Node n = graph.FindNode(group.GUID); double deltaX = n.X - group.X; double deltaY = n.Y - group.Y + graph.OffsetY; // We update the posistion of all nodes in the // parent group + all nodes in any potential // nested groups. foreach (var node in group.Nodes .OfType <NodeModel>() .Union(group.Nodes.OfType <AnnotationModel>().SelectMany(x => x.Nodes.OfType <NodeModel>()))) { node.X += deltaX; node.Y += deltaY; node.ReportPosition(); } foreach (NoteModel note in n.LinkedNotes) { if (note.PinnedNode != null) { continue; } if (note.IsSelected || DynamoSelection.Instance.Selection.Count == 0) { note.X += deltaX; note.Y += deltaY; note.ReportPosition(); } } group.ReportPosition(); } } // Assign coordinates to nodes outside groups foreach (var node in workspace.Nodes) { GraphLayout.Graph graph = layoutSubgraphs .FirstOrDefault(g => g.FindNode(node.GUID) != null); if (graph != null) { GraphLayout.Node n = graph.FindNode(node.GUID); double offsetY = graph.OffsetY; node.X = n.X; node.Y = n.Y + n.NotesHeight + offsetY; node.ReportPosition(); workspace.HasUnsavedChanges = true; double noteOffset = -n.NotesHeight; foreach (NoteModel note in n.LinkedNotes) { if (note.PinnedNode != null) { continue; } if (note.IsSelected || DynamoSelection.Instance.Selection.Count == 0) { note.X = node.X; note.Y = node.Y + noteOffset; noteOffset += note.Height + GraphLayout.Graph.VerticalNoteDistance; note.ReportPosition(); } } } } // Assign coordinates to connectors outside of groups foreach (var connector in workspace.Connectors) { foreach (var pin in connector.ConnectorPinModels) { GraphLayout.Graph graph = layoutSubgraphs .FirstOrDefault(g => g.FindNode(pin.GUID) != null); if (graph != null) { GraphLayout.Node n = graph.FindNode(pin.GUID); pin.CenterX = n.X; pin.CenterY = n.Y; pin.ReportPosition(); workspace.HasUnsavedChanges = true; } } } }