/// <summary> /// Clears the votings for a given node representative, but leaves the index references. /// </summary> /// <param name="nodeVoting"></param> void ClearVoting(NodeVoting nodeVoting) { foreach (VoteBlock block in nodeVoting.VotingBlocks) { block.Votings.Clear(); } }
/// <summary> /// Local optimization for a given node, as described in Graph Drawing by Stress Majorization by Gansner et. al. (Sect. 2.3). /// </summary> /// <param name="nodeVoting"></param> /// <returns></returns> Point LocalizedOptimization(NodeVoting nodeVoting) { Point currentPosition = Positions[nodeVoting.VotedNodeIndex]; double nextX = 0; double nextY = 0; double sumWeights = 0; // if there is not voting vlock, the position of this node will not change if (nodeVoting.VotingBlocks == null || nodeVoting.VotingBlocks.Count == 0) { return(currentPosition); } foreach (VoteBlock votingBlock in nodeVoting.VotingBlocks) { double blockWeight = votingBlock.BlockWeight; foreach (Vote vote in votingBlock.Votings) { Point voterPos = Positions[vote.VoterIndex]; double votingDistance = vote.Distance; double diffX = currentPosition.X - voterPos.X; double diffY = currentPosition.Y - voterPos.Y; double euclidDistance = Math.Sqrt(diffX * diffX + diffY * diffY); double weight = blockWeight * vote.Weight; double voteX = voterPos.X + votingDistance * diffX / euclidDistance; nextX += voteX * weight; double voteY = voterPos.Y + votingDistance * diffY / euclidDistance; nextY += voteY * weight; sumWeights += weight; } } if (sumWeights == 0) { // there was no single voting, just return the current position return(currentPosition); } return(new Point(nextX / sumWeights, nextY / sumWeights)); }
/// <summary> /// Iterates only once. /// </summary> /// <returns></returns> public List <Point> IterateSingleLocalizedMethod() { List <Point> newPositions; if (Settings.UpdateMethod == UpdateMethod.Serial) { newPositions = Positions; } else { newPositions = new List <Point>(Positions.Count); } // For each node compute the new position by averaging over all votes. for (int i = 0; i < NodeVotings.Count; i++) { NodeVoting nodeVoting = NodeVotings[i]; int votedIndex = nodeVoting.VotedNodeIndex; Point newPos = LocalizedOptimization(nodeVoting); if (Settings.UpdateMethod == UpdateMethod.Serial) { newPositions[votedIndex] = newPos; } else { newPositions.Add(newPos); } } if (Settings.UpdateMethod == UpdateMethod.Parallel) { for (int i = 0; i < NodeVotings.Count; i++) { NodeVoting nodeVoting = NodeVotings[i]; int index = nodeVoting.VotedNodeIndex; Positions[index] = newPositions[i]; } } return(newPositions); }
/// <summary> /// Local optimization for a given node, as described in Graph Drawing by Stress Majorization by Gansner et. al. (Sect. 2.3). /// </summary> /// <param name="nodeVoting"></param> /// <returns></returns> Point LocalizedOptimization(NodeVoting nodeVoting) { Point currentPosition = Positions[nodeVoting.VotedNodeIndex]; double nextX = 0; double nextY = 0; double sumWeights = 0; // if there is not voting vlock, the position of this node will not change if (nodeVoting.VotingBlocks == null || nodeVoting.VotingBlocks.Count == 0) { return currentPosition; } foreach (VoteBlock votingBlock in nodeVoting.VotingBlocks) { double blockWeight = votingBlock.BlockWeight; foreach (Vote vote in votingBlock.Votings) { Point voterPos = Positions[vote.VoterIndex]; double votingDistance = vote.Distance; double diffX = currentPosition.X - voterPos.X; double diffY = currentPosition.Y - voterPos.Y; double euclidDistance = Math.Sqrt(diffX*diffX + diffY*diffY); double weight = blockWeight*vote.Weight; double voteX = voterPos.X + votingDistance*diffX/euclidDistance; nextX += voteX*weight; double voteY = voterPos.Y + votingDistance*diffY/euclidDistance; nextY += voteY*weight; sumWeights += weight; } } if (sumWeights == 0) { // there was no single voting, just return the current position return currentPosition; } return new Point(nextX/sumWeights, nextY/sumWeights); }
/// <summary> /// Example on how to use Stress Majorization with a small graph and Localized method. /// </summary> public static void RunStressMajorizationExample() { //create a star graph where three nodes are connected to the center GeometryGraph graph = new GeometryGraph(); graph.Nodes.Add(new Node()); graph.Nodes.Add(new Node()); graph.Nodes.Add(new Node()); graph.Nodes.Add(new Node()); //set initial positions, e.g., random graph.Nodes[0].BoundaryCurve = CurveFactory.CreateRectangle(20, 10, new Point(5, 5)); graph.Nodes[1].BoundaryCurve = CurveFactory.CreateRectangle(20, 10, new Point(7, 10)); graph.Nodes[2].BoundaryCurve = CurveFactory.CreateRectangle(20, 10, new Point(7, 2)); graph.Nodes[3].BoundaryCurve = CurveFactory.CreateRectangle(20, 10, new Point(35, 1)); graph.Edges.Add(new Edge(graph.Nodes[0], graph.Nodes[1])); graph.Edges.Add(new Edge(graph.Nodes[0], graph.Nodes[2])); graph.Edges.Add(new Edge(graph.Nodes[0], graph.Nodes[3])); //array with desired distances between the nodes for every edge double[] idealEdgeLength = new double[graph.Edges.Count]; for (int i = 0; i < graph.Edges.Count; i++) { idealEdgeLength[i] = 100; //all edges should have this euclidean length } //create stress majorization class and set the desired distances on the edges StressMajorization majorizer = new StressMajorization(); majorizer.Positions = new List <Point>(graph.Nodes.Select(v => v.Center)); majorizer.NodeVotings = new List <NodeVoting>(graph.Nodes.Count); // initialize for every node an empty block for (int i = 0; i < graph.Nodes.Count; i++) { var nodeVote = new NodeVoting(i); //by default there is already a block with weighting 1 //optional: add second block with different type of edges, e.g., with stronger weight //var secondBlock = new VoteBlock(new List<Vote>(), 100); //nodeVote.VotingBlocks.Add(secondBlock); //var block2=nodeVote.VotingBlocks[1]; //block could be accessed like this in a later stage majorizer.NodeVotings.Add(nodeVote); } // for every edge set the desired distances by setting votings among the two end nodes. Dictionary <Node, int> posDict = new Dictionary <Node, int>(); for (int i = 0; i < graph.Nodes.Count; i++) { posDict[graph.Nodes[i]] = i; } var edges = graph.Edges.ToArray(); for (int i = 0; i < graph.Edges.Count; i++) { var edge = edges[i]; int nodeId1 = posDict[edge.Source]; int nodeId2 = posDict[edge.Target]; double idealDistance = idealEdgeLength[i]; double weight = 1 / (idealDistance * idealDistance); var voteFromNode1 = new Vote(nodeId1, idealDistance, weight); // vote from node1 for node2 var voteFromNode2 = new Vote(nodeId2, idealDistance, weight); // vote from node2 for node1 // add vote of node1 to list of node2 (in first voting block) majorizer.NodeVotings[nodeId2].VotingBlocks[0].Votings.Add(voteFromNode1); // add vote of node2 to list of node1 (in first voting block) majorizer.NodeVotings[nodeId1].VotingBlocks[0].Votings.Add(voteFromNode2); } //used localized method to reduce stress majorizer.Settings = new StressMajorizationSettings(); majorizer.Settings.SolvingMethod = SolvingMethod.Localized; List <Point> result = majorizer.IterateAll(); for (int i = 0; i < result.Count; i++) { graph.Nodes[i].Center = result[i]; } #if DEBUG && !SHARPKIT LayoutAlgorithmSettings.ShowGraph(graph); #endif }
/// <summary> /// Example on how to use Stress Majorization with a small graph and Localized method. /// </summary> public static void RunStressMajorizationExample() { //create a star graph where three nodes are connected to the center GeometryGraph graph = new GeometryGraph(); graph.Nodes.Add(new Node()); graph.Nodes.Add(new Node()); graph.Nodes.Add(new Node()); graph.Nodes.Add(new Node()); //set initial positions, e.g., random graph.Nodes[0].BoundaryCurve = CurveFactory.CreateRectangle(20, 10, new Point(5, 5)); graph.Nodes[1].BoundaryCurve = CurveFactory.CreateRectangle(20, 10, new Point(7, 10)); graph.Nodes[2].BoundaryCurve = CurveFactory.CreateRectangle(20, 10, new Point(7, 2)); graph.Nodes[3].BoundaryCurve = CurveFactory.CreateRectangle(20, 10, new Point(35, 1)); graph.Edges.Add(new Edge(graph.Nodes[0], graph.Nodes[1])); graph.Edges.Add(new Edge(graph.Nodes[0], graph.Nodes[2])); graph.Edges.Add(new Edge(graph.Nodes[0], graph.Nodes[3])); //array with desired distances between the nodes for every edge double[] idealEdgeLength=new double[graph.Edges.Count]; for (int i = 0; i < graph.Edges.Count; i++) { idealEdgeLength[i] = 100; //all edges should have this euclidean length } //create stress majorization class and set the desired distances on the edges StressMajorization majorizer = new StressMajorization(); majorizer.Positions = new List<Point>(graph.Nodes.Select(v=>v.Center)); majorizer.NodeVotings = new List<NodeVoting>(graph.Nodes.Count); // initialize for every node an empty block for (int i = 0; i < graph.Nodes.Count; i++) { var nodeVote = new NodeVoting(i); //by default there is already a block with weighting 1 //optional: add second block with different type of edges, e.g., with stronger weight //var secondBlock = new VoteBlock(new List<Vote>(), 100); //nodeVote.VotingBlocks.Add(secondBlock); //var block2=nodeVote.VotingBlocks[1]; //block could be accessed like this in a later stage majorizer.NodeVotings.Add(nodeVote); } // for every edge set the desired distances by setting votings among the two end nodes. Dictionary<Node,int> posDict=new Dictionary<Node, int>(); for (int i = 0; i < graph.Nodes.Count; i++) { posDict[graph.Nodes[i]] = i; } var edges = graph.Edges.ToArray(); for (int i=0; i<graph.Edges.Count;i++) { var edge = edges[i]; int nodeId1 = posDict[edge.Source]; int nodeId2 = posDict[edge.Target]; double idealDistance = idealEdgeLength[i]; double weight = 1 / (idealDistance * idealDistance); var voteFromNode1 = new Vote(nodeId1, idealDistance, weight); // vote from node1 for node2 var voteFromNode2 = new Vote(nodeId2, idealDistance, weight); // vote from node2 for node1 // add vote of node1 to list of node2 (in first voting block) majorizer.NodeVotings[nodeId2].VotingBlocks[0].Votings.Add(voteFromNode1); // add vote of node2 to list of node1 (in first voting block) majorizer.NodeVotings[nodeId1].VotingBlocks[0].Votings.Add(voteFromNode2); } //used localized method to reduce stress majorizer.Settings=new StressMajorizationSettings(); majorizer.Settings.SolvingMethod=SolvingMethod.Localized; List<Point> result = majorizer.IterateAll(); for (int i = 0; i < result.Count; i++) { graph.Nodes[i].Center = result[i]; } #if DEBUG && !SILVERLIGHT && !SHARPKIT LayoutAlgorithmSettings.ShowGraph(graph); #endif }
/// <summary> /// Inits the datastructures, later forces can be defined on the nodes. /// </summary> /// <param name="majorizer"></param> /// <param name="nodes"></param> /// <param name="nodePositions"></param> static void InitStressWithGraph(StressMajorization majorizer, Node[] nodes, Point[] nodePositions) { majorizer.Positions = new List<Point>(nodePositions); majorizer.NodeVotings = new List<NodeVoting>(nodes.Length); for (int i = 0; i < nodes.Length; i++) { var nodeVote = new NodeVoting(i); //add second block for separate weighting of the overlap distances var voteBlock = new VoteBlock(new List<Vote>(), 100); // nodeVote.VotingBlocks[0].BlockWeight = 0; nodeVote.VotingBlocks.Add(voteBlock); majorizer.NodeVotings.Add(nodeVote); } }