private static object[] GetPartitionNodeDimensionAndPosition(GraphMapData graphMapData, PartitionNode partitionNode) { Point topLeft = new Point(double.MaxValue, double.MaxValue); Point bottomRight = new Point(double.MinValue, double.MinValue); // Loop through all node view models to get the bounding area foreach (NodeViewModelBase nodeViewModelBase in partitionNode.Nodes) { NodeMapData nodeMapData = graphMapData.Nodes[nodeViewModelBase.ParentNode.ID]; topLeft.X = Math.Min(topLeft.X, nodeMapData.Position.X - nodeMapData.Dimension.Width / 2D); topLeft.Y = Math.Min(topLeft.Y, nodeMapData.Position.Y - nodeMapData.Dimension.Height / 2D); bottomRight.X = Math.Max(bottomRight.X, nodeMapData.Position.X + nodeMapData.Dimension.Width / 2D); bottomRight.Y = Math.Max(bottomRight.Y, nodeMapData.Position.Y + nodeMapData.Dimension.Height / 2D); } // Set the new dimensions based on the calculation performed double width = Math.Max(bottomRight.X - topLeft.X, 1D); double height = Math.Max(bottomRight.Y - topLeft.Y, 1D); Size dimension = new Size(width, height); // get the center of the partitionNode Point position = new Point(topLeft.X + width / 2D, topLeft.Y + height / 2D); object[] result = new object[] { dimension, position }; return(result); }
/// <summary> /// Calulcates the length of the chord for the provided NodeViewModelBase /// object. A 'chord' is a line segment that connects two points on a /// curve. /// </summary> /// <param name="node">Node for which to calculate the chord length for</param> /// <returns>The chord length for the givenn node</returns> private static double CalculateChordLength(NodeMapData node) { // Use the Pythagorean theorem to calculate the length of the chord // segment between each node based on the height and width of the // provided node. return(Math.Sqrt(node.Width * node.Width + node.Height * node.Height + 1)); }
private static void WriteNode(XmlWriter writer, NodeMapData objNode) { writer.WriteStartElement("node"); writer.WriteAttributeString("id", objNode.Id); if (objNode.GetType().Equals(typeof(IconNodeMapData))) { WritePropData(writer, NODE_PROPERTY_PREFIX, "ImageSource", ((IconNodeMapData)objNode).ImageSource.ToString()); } WritePropData(writer, NODE_PROPERTY_PREFIX, "Description", objNode.Description); WritePropData(writer, NODE_PROPERTY_PREFIX, "DisplayValue", objNode.Label); WritePropData(writer, NODE_PROPERTY_PREFIX, "Height", objNode.Dimension.Height.ToString()); WritePropData(writer, NODE_PROPERTY_PREFIX, "Width", objNode.Dimension.Width.ToString()); WritePropData(writer, NODE_PROPERTY_PREFIX, "Position", objNode.Position.ToString()); WritePropData(writer, NODE_PROPERTY_PREFIX, "IsHidden", objNode.IsHidden.ToString()); string backgroundColor = GetSnaglStrColor(objNode.BackgroundColor); WritePropData(writer, NODE_PROPERTY_PREFIX, "BackgroundColor", backgroundColor); string selectionColor = GetSnaglStrColor(objNode.SelectionColor); WritePropData(writer, NODE_PROPERTY_PREFIX, "SelectionColor", selectionColor); foreach (KeyValuePair <string, AttributeMapData> kvp in objNode.Attributes) { string description = "{\"Name\":\"" + kvp.Value.Name + "\",\"PreferredSimilarityMeasure\":\"" + kvp.Value.SimilarityMeasure + "\",\"SemanticType\":" + (int)kvp.Value.SemanticType + ",\"Visible\":" + (!kvp.Value.IsHidden).ToString().ToLower() + "}"; WriteAttrData(writer, NODE_ATTRIBUTE_PREFIX, kvp.Value.Name, description, kvp.Value.Value); } writer.WriteEndElement(); }
private static int NodeSizeComparer(NodeMapData first, NodeMapData second) { double areaFirst = first.Dimension.Width * first.Dimension.Height; double areaSecond = second.Dimension.Width * second.Dimension.Height; return(-areaFirst.CompareTo(areaSecond)); }
public void AddNode(ScriptableNodeMapData scriptableNode) { NodeMapData objNode = ScriptableMapper.GetNode(scriptableNode); GraphComponents graphComponents = GraphManager.Instance.DefaultGraphComponentsInstance; MappingExtensions.AddNode(graphComponents, CreationType.Live, objNode); }
/// <summary> /// Adds the provided node view model to the specified layer /// </summary> /// <param name="node">The node view model to be added</param> /// <param name="currentLayer">The layer to add the view model too</param> private void AddToLayer(NodeMapData node, int currentLayer) { // Loop over all the layers up to the specified layer while (layers.Count <= currentLayer) { layers.Add(new List <NodeMapData>()); } // Add the provided node view model to the layer layers[currentLayer].Add(node); }
private NodeMapData GetOppositeNode(GraphMapData graph, EdgeMapData edge, NodeMapData node) { if (edge.Source.Equals(node.Id)) { return(graph.Nodes[edge.Target]); } else { return(graph.Nodes[edge.Source]); } }
private static double GetNumberOfEdges(GraphMapData graph, NodeMapData node) { double numEdges = 0D; foreach (EdgeMapData edge in graph.GetEdges()) { if (edge.Source.Equals(node.Id) || edge.Target.Equals(node.Id)) { numEdges++; } } return(numEdges); }
private static ICollection <EdgeMapData> GetNodesEdges(GraphMapData graph, NodeMapData node) { ICollection <EdgeMapData> edges = new List <EdgeMapData>(); foreach (EdgeMapData edge in graph.GetEdges()) { if (edge.Source.Equals(node.Id) || edge.Target.Equals(node.Id)) { edges.Add(edge); } } return(edges); }
/// <summary> /// /// </summary> /// <param name="lastNode"></param> /// <param name="thisNode"></param> /// <param name="radius"></param> /// <returns></returns> private static double GetAngle(NodeMapData thisNode, double radius) { // Is this the first node? //if (lastNode == null) return 0; // Lay out node after the double circumference = 2 * Math.PI * radius; double chordLength = CalculateChordLength(thisNode); // Percent of the total circumference we need to move double percent = (MIN_NODE_ARC_SPACING + chordLength) / circumference; return(360.0 * percent); }
/// <summary> /// Performs the actual layout algorithm. /// </summary> /// <param name="graph">The object containing the graph data</param> /// <param name="rootNode">Root node</param> protected override void PerformLayout(GraphMapData graph, INode rootNode) { NodeMapData nodeToProcess = null; if (rootNode != null) { nodeToProcess = graph.Nodes[rootNode.ID]; } // Loop over the nodes until they have all been positioned while (processedNodes.Count != graph.Nodes.Count) { // Get the highest rank node that has not already been // positioned if (nodeToProcess == null) { double maxRanking = double.MinValue; // Loop over the nodes looking for the highest ranked one foreach (NodeMapData node in graph.GetNodes()) { double currentRanking = GetNodeRanking(graph, node); if (!processedNodes.Contains(node.Id) && (nodeToProcess == null || maxRanking < currentRanking)) { maxRanking = currentRanking; nodeToProcess = node; } } } // Position the node SaveNodePosition(nodeToProcess, maxXPosition, 0D); processedNodes.Add(nodeToProcess.Id); AddToLayer(nodeToProcess, 0); // Layer the graph CalulateSubtreePosition(graph, nodeToProcess, 1); nodeToProcess = null; } // reset for next run maxXPosition = 0D; processedNodes.Clear(); layers.Clear(); layerSpans.Clear(); }
/// <summary> /// Determines the ranking of the provided node /// </summary> /// <param name="graph">The object containing the graph data</param> /// <param name="node">The node to get the rank for</param> /// <returns>the rank for this node</returns> private double GetNodeRanking(GraphMapData graph, NodeMapData node) { // Check if we have a custom ranker to use. A custom ranker // is a custom delegate that is used to determine the nodes // rank. if (CustomRootRanker != null) { return(CustomRootRanker(node)); } else { // As a fallback, determine the nodes rank base on the // number of edges that it has return(GetNumberOfEdges(graph, node)); } }
/// <summary> /// Executes the JavaScript function specified by <paramref name="callbackName"/> /// </summary> /// <param name="sender">The NodeViewModel that raised the event</param> /// <param name="callbackName">The name of the JavaScript function to execute</param> private static void ExecuteNodeJSCallback(object sender, string callbackName) { //TODO: VALIDATE CALLBACK try { NodeViewModelBase nodeVM = (NodeViewModelBase)sender; NodeMapData nodeMapData = MappingExtensions.GetNode(nodeVM); ScriptableNodeMapData scriptableNodeMapData = ScriptableMapper.GetNode(nodeMapData); HtmlPage.Window.Invoke(callbackName, scriptableNodeMapData); } catch (InvalidOperationException) { MessageBox.Show("Unable to find the '" + callbackName + "' method in the host application", "Unknown callback", MessageBoxButton.OK); // Swallow error for now //TODO: LOG THIS CASE } }
/// <summary> /// Pushes over all nodes in the specified layer by the specified amount /// </summary> /// <param name="layer">The current layer being analyzed</param> /// <param name="endNode">The last node view model to be processed</param> /// <param name="amount">The amount to shove the nodes over</param> private void PushNodesOver(int layer, NodeMapData endNode, double amount) { // Ensure that we have any layers if (layers.Count > layer) { // push the nodes over foreach (NodeMapData currentNode in layers[layer]) { if (currentNode.Id.Equals(endNode.Id)) { break; } currentNode.Position = new Point(currentNode.Position.X + amount, currentNode.Position.Y); } // Continue pushing nodes over for each layer (recursively) PushNodesOver(layer + 1, endNode, amount); } }
private static void ApplyOffsetToSubGraphs(GraphMapData graph, GraphComponents clusteredGraphComponents, IDictionary <string, Point> originalPositions, GraphMapData partitionGraph) { IEnumerable <INodeShape> clusteredComponents = clusteredGraphComponents.GetNodeViewModels(); foreach (PartitionNode clusteredComponent in clusteredComponents) { Point originalPosition = originalPositions[clusteredComponent.ID]; double xOffset = partitionGraph.Nodes[clusteredComponent.ID].Position.X - originalPosition.X; double yOffset = partitionGraph.Nodes[clusteredComponent.ID].Position.Y - originalPosition.Y; IList <NodeViewModelBase> viewModelNodes = clusteredComponent.Nodes; foreach (NodeViewModelBase viewModelNode in viewModelNodes) { NodeMapData nodeMapData = graph.Nodes[viewModelNode.ParentNode.ID]; Point offsetPosition = new Point(nodeMapData.Position.X + xOffset, nodeMapData.Position.Y + yOffset); nodeMapData.Position = offsetPosition; } } }
/// <summary> /// Converts the provided GraphComponents instance to a GraphMapData /// instance that can be exported to a target file format /// </summary> /// <param name="graphComponents">The graph to be exported</param> /// <returns>A GraphMapData instance ready to be exported to the target format</returns> public static GraphMapData ExportGraph(this GraphComponents graphComponents) { GraphMapData graph = new GraphMapData(); // Nodes IEnumerable <INodeShape> uiNodeViewModels = graphComponents.GetNodeViewModels(); foreach (NodeViewModelBase uiNodeVM in uiNodeViewModels) { NodeMapData objNode = GetNode(uiNodeVM); graph.Add(objNode); } // Edges IEnumerable <IEdgeViewModel> edgeViewModels = graphComponents.GetEdgeViewModels(); foreach (EdgeViewModelBase uiEdge in edgeViewModels) { EdgeMapData objEdge = GetEdge(uiEdge); graph.Add(objEdge); } return(graph); }
private static GraphMapData GetGraph(GraphMapData graphMapData, GraphComponents clusteredGraph) { GraphMapData clusteredGraphMapData = new GraphMapData(); // Nodes IEnumerable <INodeShape> uiNodeViewModels = clusteredGraph.GetNodeViewModels(); foreach (NodeViewModelBase uiNodeVM in uiNodeViewModels) { NodeMapData nodeMapData = graphMapData.Nodes[uiNodeVM.ParentNode.ID]; clusteredGraphMapData.Add(nodeMapData); // Edges IEnumerable <IEdge> uiEdgeViewModels = clusteredGraph.GetEdges(uiNodeVM.ParentNode); foreach (IEdge uiEdgeVM in uiEdgeViewModels) { string edgeKey = uiEdgeVM.Source.ID + uiEdgeVM.Target.ID; EdgeMapData edgeMapData = graphMapData.Edges[edgeKey]; clusteredGraphMapData.Add(edgeMapData); } } return(clusteredGraphMapData); }
/// <summary> /// Calulates the subtrees overall position /// </summary> /// <param name="graph">The object containing the graph data</param> /// <param name="node">The root node view model for this tree</param> /// <param name="layer">The layer (or row in the tree) being analyzed</param> private void CalulateSubtreePosition(GraphMapData graph, NodeMapData node, int layer) { IList <NodeMapData> children = new List <NodeMapData>(); double totalWidth = 0D; //TODO: MAYBE REVISE TO USE GET NEIGHBORS // Loop over the provided nodes edges in order to get its children foreach (EdgeMapData edge in GetNodesEdges(graph, node)) { // TODO: HANDLE EDGE VISIBILITY // Get the node at the opposite end of this edge NodeMapData childNode = GetOppositeNode(graph, edge, node); // Ensure that the node has not already been positioned if (childNode != null && !childNode.IsHidden && !processedNodes.Contains(childNode.Id)) { // Position the node children.Add(childNode); totalWidth += childNode.Dimension.Width; processedNodes.Add(childNode.Id); } } // If there are no children, then we are done if (children.Count == 0) { return; } // Now we need to analyze and lay out all the children as a unit totalWidth += (children.Count - 1D) * NODE_SPACING; double currentX = node.Position.X - ((totalWidth - children[0].Dimension.Width) / 2D); double currentY = node.Position.Y + node.Dimension.Height + LAYER_SPACING; // Determine if the layers overlap double intersectAmount = IntersectsOnLayer(layer, currentX, currentX + totalWidth); // Check if we had an intersection if (intersectAmount != 0) { double pushAmount = intersectAmount / 2D; currentX -= pushAmount; // Give the nodes a little shove so they no longer // intersect with another subtree PushNodesOver(layer, node, pushAmount); // Make sure to push over the nodes in the root layer as well } AddSpanToLayer(layer, currentX, currentX + totalWidth); // Once we get here we have a good position, so we need to set it foreach (NodeMapData currentChildNode in children) { SaveNodePosition(currentChildNode, currentX, currentY); AddToLayer(currentChildNode, layer); currentX += currentChildNode.Dimension.Width + NODE_SPACING; } // Now we need to do the same thing for each // of the children node, recursively foreach (NodeMapData currentChildNode in children) { CalulateSubtreePosition(graph, currentChildNode, layer + 1); } }
/// <summary> /// Adds the specificed node /// </summary> /// <param name="graphComponents">The Graph that data is being imported into</param> /// <param name="creationType">The specified CreationType</param> /// <param name="objNode">Node to be added</param> public static void AddNode(GraphComponents graphComponents, CreationType creationType, NodeMapData objNode) { // Create new node Node uiNode = new Node(objNode.Id); uiNode.SourceMechanism = creationType; // TODO as NodeMapData types expands, this needs to be adjusted NodeTypes uiNodeType = NodeTypes.Simple; if (objNode is IconNodeMapData) { uiNodeType = NodeTypes.Icon; } else if (objNode is TextNodeMapData) { uiNodeType = NodeTypes.Text; } NodeViewModelBase uiNodeVM = NodeViewModelBase.GetNodeViewModel(uiNodeType, uiNode, graphComponents.Scope); // Properties if (uiNodeType == NodeTypes.Icon) { IconNodeMapData objIconNode = (IconNodeMapData)objNode; if (objIconNode.ImageSource != null) { ((IconNodeViewModel)uiNodeVM).ImageSource = objIconNode.ImageSource.ToString(); } } uiNodeVM.Description = objNode.Description; uiNodeVM.DisplayValue = objNode.Label; uiNodeVM.Width = objNode.Dimension.Width; uiNodeVM.Height = objNode.Dimension.Height; uiNodeVM.Position = objNode.Position; uiNodeVM.IsHidden = objNode.IsHidden; SolidColorBrush uiBackgroundColorBrush = new SolidColorBrush(objNode.BackgroundColor); uiNodeVM.BackgroundColor = uiBackgroundColorBrush; SolidColorBrush uiSelectionColorBrush = new SolidColorBrush(objNode.SelectionColor); uiNodeVM.SelectionColor = uiSelectionColorBrush; if (uiNodeVM.Height == 0) { uiNodeVM.Height = 45; } if (uiNodeVM.Width == 0) { uiNodeVM.Width = 45; } // Add the node to the graph graphComponents.AddNodeViewModel(uiNodeVM); // Attributes foreach (KeyValuePair <string, AttributeMapData> objNodeAttrKVP in objNode.Attributes) { Attributes.Attribute uiNodeAttribute = new Attributes.Attribute(objNodeAttrKVP.Value.Name); AttributeValue uiNodeAttributeValue = new AttributeValue(objNodeAttrKVP.Value.Value); uiNode.Attributes.Add(uiNodeAttribute.Name, uiNodeAttributeValue); GlobalAttributeCollection.GetInstance(graphComponents.Scope).Add(uiNodeAttribute, uiNodeAttributeValue); uiNodeAttribute.SemanticType = objNodeAttrKVP.Value.SemanticType; uiNodeAttribute.PreferredSimilarityMeasure = objNodeAttrKVP.Value.SimilarityMeasure; uiNodeAttribute.Visible = !objNodeAttrKVP.Value.IsHidden; } }
private void SaveNodePosition(NodeMapData node, double x, double y) { node.Position = new Point(x, y); maxXPosition = Math.Max(x + node.Dimension.Width, maxXPosition); }
/// <summary> /// Performs the actual import for the given GraphML or SnaglML data. /// The Import method should be called rather than calling /// this method directly. /// </summary> /// <param name="data">GraphML or SnaglML</param> /// <returns>The graph data mapped as a <see cref="GraphMapData"/> object</returns> internal override GraphMapData ImportData(string data) { // TODO Should validate against XSD // TODO Processing should conform to GraphML defined processing rules GraphMapData graph = new GraphMapData(); bool haveEncounteredGraph = false; using (TextReader stringReader = new StringReader(data)) { XmlReaderSettings settings = new XmlReaderSettings { CloseInput = true, ConformanceLevel = ConformanceLevel.Document, DtdProcessing = DtdProcessing.Parse, IgnoreWhitespace = true }; using (XmlReader reader = XmlReader.Create(stringReader, settings)) { // while we know the graph element must be first as per the schema the compiler does not, so these is assigned a value here NodeTypes defaultNodeType = NodeTypes.Text; //GraphType defaultGraphType = GraphType.Undirected; while (reader.Read()) { if (reader.IsStartElement()) { switch (reader.LocalName) { case "graph": if (!haveEncounteredGraph) { haveEncounteredGraph = true; string nodeType = reader.GetAttribute("nodeType", BERICO_NAMESPACE_URI); defaultNodeType = GetDefaultNodeType(nodeType); //string edgeDefault = reader.GetAttribute("edgedefault"); //defaultGraphType = GetDefaultEdgeType(edgeDefault); } else { throw new Exception("Both multiple graphs per file and nested graphs are unsupported."); } break; case "node": NodeMapData node = ReadNode(reader, defaultNodeType); graph.Add(node); break; case "edge": EdgeMapData edge = ReadEdge(reader); graph.Add(edge); break; } } } } } return(graph); }