using System.Collections.Generic; using UnityEngine; namespace XCharts.Runtime { /// /// the data struct of graph. /// ||数据结构-图。 /// public class Graph { public bool directed; public List nodes = new List(); public List edges = new List(); public Dictionary nodeMap = new Dictionary(); public Dictionary edgeMap = new Dictionary(); public Graph(bool directed) { this.directed = directed; } public void Clear() { nodes.Clear(); edges.Clear(); nodeMap.Clear(); edgeMap.Clear(); } public void Refresh() { foreach (var node in nodes) { node.depth = GetNodeDepth(node); } } public static double GetNodesTotalValue(List nodes) { double totalValue = 0; foreach (var node in nodes) { totalValue += node.totalValues; } return totalValue; } public List> GetDepthNodes() { List> depthNodes = new List>(); var maxDepth = GetMaxDepth(); for (int i = 0; i <= maxDepth; i++) { depthNodes.Add(new List()); } foreach (var node in nodes) { if (node.inDegree == 0) { depthNodes[0].Add(node); } else { int deep = GetNodeDepth(node); depthNodes[maxDepth - deep].Add(node); } } return depthNodes; } public List GetRootNodes() { List rootNodes = new List(); foreach (var node in nodes) { if (node.inDegree == 0) { rootNodes.Add(node); } } return rootNodes; } public int GetMaxDepth() { int maxDepth = 0; foreach (var node in nodes) { int deep = GetNodeDepth(node); if (deep > maxDepth) { maxDepth = deep; } } return maxDepth; } // public int GetNodeDepth(GraphNode node) // { // int depth = 0; // GetNodeDepth(node, ref depth); // return depth; // } // public void GetNodeDepth(GraphNode node, ref int depth, int recursiveCount = 0) // { // if (recursiveCount > 50) // { // XLog.Error("Graph.GetNodeDeep(): recursiveCount > 50, maybe graph is ring"); // return; // } // if (node.inDegree == 0) // { // return; // } // else // { // depth += 1; // foreach (var edge in node.inEdges) // { // GetNodeDepth(edge.node1, ref depth, recursiveCount + 1); // } // } // } public int GetNodeDepth(GraphNode node, int recursiveCount = 0) { if (recursiveCount > 50) { XLog.Error("Graph.GetNodeDeep(): recursiveCount > 50, maybe graph is ring"); return 0; } int depth = 0; if (node.outDegree == 0) { return depth; } else { foreach (var edge in node.outEdges) { int otherDeep = GetNodeDepth(edge.node2, recursiveCount + 1); if (otherDeep > depth) { depth = otherDeep; } } return depth + 1; } } public GraphNode GetNode(string nodeId) { if (nodeMap.ContainsKey(nodeId)) { return nodeMap[nodeId]; } else { return null; } } public GraphEdge GetEdge(string nodeId1, string nodeId2) { if (directed) { return edgeMap[nodeId1 + "_" + nodeId2]; } else { var key = nodeId1 + "_" + nodeId2; if (edgeMap.ContainsKey(key)) { return edgeMap[key]; } else { key = nodeId2 + "_" + nodeId1; if (edgeMap.ContainsKey(key)) { return edgeMap[key]; } else { return null; } } } } public GraphNode AddNode(string nodeId, string nodeName, int dataIndex) { if (nodeMap.ContainsKey(nodeId)) { return nodeMap[nodeId]; } else { GraphNode node = new GraphNode(nodeId, nodeName, dataIndex); node.hostGraph = this; nodeMap.Add(nodeId, node); nodes.Add(node); return node; } } public GraphEdge AddEdge(string nodeId1, string nodeId2, double value) { GraphNode node1, node2; if (!nodeMap.TryGetValue(nodeId1, out node1)) { XLog.Warning("Graph.AddEdge(): " + nodeId1 + " not exist"); return null; } if (!nodeMap.TryGetValue(nodeId2, out node2)) { XLog.Warning("Graph.AddEdge(): " + nodeId2 + " not exist"); return null; } if (node1 == null) { XLog.Warning("Graph.AddEdge(): node1 is null"); return null; } if (node2 == null) { XLog.Warning("Graph.AddEdge(): node2 is null"); return null; } if (node1 == node2) { XLog.Warning("Graph.AddEdge(): node1 == node2"); return null; } string edgeKey = nodeId1 + "_" + nodeId2; if (edgeMap.ContainsKey(edgeKey)) { return edgeMap[edgeKey]; } else { GraphEdge edge = new GraphEdge(node1, node2, value); edge.key = edgeKey; edge.hostGraph = this; if (directed) { node1.outEdges.Add(edge); node2.inEdges.Add(edge); } node1.edges.Add(edge); if (node1 != node2) { node2.edges.Add(edge); } edgeMap.Add(edgeKey, edge); edges.Add(edge); return edge; } } public void EachNode(System.Action onEach) { if (onEach == null) return; foreach (var node in nodes) { onEach(node); } } public void BreadthFirstTraverse(GraphNode startNode, System.Action onTraverse) { if (startNode == null) return; foreach (var node in nodes) { node.visited = false; } onTraverse(startNode); startNode.visited = true; Queue queue = new Queue(); queue.Enqueue(startNode); while (queue.Count > 0) { var currentNode = queue.Dequeue(); foreach (var edge in currentNode.edges) { var otherNode = edge.node1 == currentNode ? edge.node2 : edge.node1; if (!otherNode.visited) { onTraverse(otherNode); otherNode.visited = true; queue.Enqueue(otherNode); } } } } public void DeepFirstTraverse(GraphNode startNode, System.Action onTraverse) { if (startNode == null) return; foreach (var node in nodes) { node.visited = false; } Stack stack = new Stack(); stack.Push(startNode); while (stack.Count > 0) { var currentNode = stack.Pop(); if (!currentNode.visited) { onTraverse(currentNode); currentNode.visited = true; } foreach (var edge in currentNode.edges) { var otherNode = edge.node1 == currentNode ? edge.node2 : edge.node1; if (!otherNode.visited) { stack.Push(otherNode); } } } } } /// /// The node of graph. /// ||图的节点。 /// public class GraphNode { public string id; public string name; public List edges = new List(); public List inEdges = new List(); public List outEdges = new List(); public Graph hostGraph; public int dataIndex; public bool visited; public int depth = -1; public GraphNode(string id, string name, int dataIndex) { this.id = id; this.name = name; this.dataIndex = dataIndex; } public int degree { get { return edges.Count; } } public int inDegree { get { return inEdges.Count; } } public int outDegree { get { return outEdges.Count; } } public double totalValues { get { double totalValue = 0; if (inEdges.Count == 0) { foreach (var edge in outEdges) { totalValue += edge.value; } } else { foreach (var edge in inEdges) { totalValue += edge.value; } } return totalValue; } } public override string ToString() { return name; } } /// /// The edge of graph. /// ||图的边。 /// public class GraphEdge { public string key; public GraphNode node1; public GraphNode node2; public double value; public Graph hostGraph; public List points = new List(); public float width; public bool highlight; public GraphEdge(GraphNode node1, GraphNode node2, double value) { this.node1 = node1; this.node2 = node2; this.value = value; } } }