View Javadoc

1   /*
2    * This file is a part of CAST project.
3    * (c) Copyright 2007, AGH University of Science & Technology
4    * All rights reserved. Check the documentation for licensing terms.
5    * https://caribou.iisg.agh.edu.pl/trac/cast
6    */
7   package pl.edu.agh.cast.schema.layout.algorithm;
8   
9   import java.util.Arrays;
10  import java.util.Collection;
11  import java.util.HashMap;
12  import java.util.HashSet;
13  import java.util.Iterator;
14  import java.util.LinkedList;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Queue;
18  import java.util.Set;
19  
20  import org.eclipse.core.runtime.IProgressMonitor;
21  import org.eclipse.draw2d.geometry.Point;
22  
23  import pl.edu.agh.cast.model.visual.backward.ConnectionGroup;
24  import pl.edu.agh.cast.model.visual.backward.Node;
25  import pl.edu.agh.cast.schema.util.Messages;
26  
27  /**
28   * This class represents version 3 of peacock layout algorithm.
29   *
30   * @author Kornel Skalkowski kornelsk@gmail.com
31   * @author Jakub Fibinger jfibinger@gmail.com
32   *
33   */
34  public class Peacock3LayoutAlgorithm extends AbstractGroupingLayoutAlgorithm {
35  
36      private final int STEPS_NUMBER = 5;
37  
38      private final int AREA_CENTER_X = 0;
39  
40      private final int AREA_CENTER_Y = 0;
41  
42      private final double RADIUS_SCALE = 40;
43  
44      private final double MIN_RADIUS_SCALE_FACTOR = 1;
45  
46      private final double MAX_RADIUS_SCALE_FACTOR = 7;
47  
48      private final double DISTANCE_FACTOR = 0.001;
49  
50      private final double ANGLE_RANGE_FACTOR = 1.4;
51  
52      private int maxWeight = -1;
53  
54      private int minWeight = -1;
55  
56      private int treeSize = 0;
57  
58      private double radius = 150;
59  
60      private double radiusScaleFactor = 2;
61  
62      private Collection<Node> nodes = null;
63  
64      private boolean[][] adiacentTable = null;
65  
66      private Map<String, Integer> distanceSums = new HashMap<String, Integer>();
67  
68      private Map<String, Integer> adiacentTablePositions = new HashMap<String, Integer>();
69  
70      private Map<String, Map<String, Integer>> distancesTable = new HashMap<String, Map<String, Integer>>();
71  
72      /**
73       * This class represents single node in BFS tree.
74       *
75       * @author Jakub Fibinger jfibinger@gmail.com
76       * @author Kornel Skalkowski kornelsk@gmail.com
77       *
78       */
79      private class TreeNode {
80  
81          Node node;
82  
83          TreeNode parent;
84  
85          Set<TreeNode> children = new HashSet<TreeNode>();
86  
87          /**
88           * Public constructor.
89           *
90           * @param node
91           *            graph's node.
92           */
93          public TreeNode(Node node) {
94              this.node = node;
95          }
96  
97      }
98  
99      @Override
100     /*
101      * This method sets nodes positions on board.
102      *
103      * @param positionNodes collection of nodes to position.
104      */
105     protected void setNodesPositions(Collection<Node> positionNodes, IProgressMonitor monitor) {
106 
107         monitor.subTask(Messages.Peacock3LayoutAlgorithm_0);
108 
109         this.nodes = positionNodes;
110         this.setRadiusScaleFactor();
111         this.setRootNode(new LinkedList<Node>(positionNodes));
112 
113         List<Edge> edges = new LinkedList<Edge>();
114         minWeight = maxWeight = -1;
115         for (Node n : nodes) {
116             for (Node neighbor : getNeighbors(nodes, n)) {
117                 if (findEdge(edges, n, neighbor) == null) {
118                     int weight = getWeight(n, neighbor);
119                     edges.add(new Edge(n, neighbor, weight));
120                     if (minWeight == -1) {
121                         minWeight = maxWeight = weight;
122                     } else if (weight > maxWeight) {
123                         maxWeight = weight;
124                     } else if (weight < minWeight) {
125                         minWeight = weight;
126                     }
127                 }
128             }
129             monitor.worked(positionNodes.size());
130         }
131 
132         for (int i = 0; i < this.STEPS_NUMBER; i++) {
133             steepestDescent(edges);
134         }
135         monitor.worked(positionNodes.size());
136 
137     }
138 
139     /**
140      * This method sets scale factor field.
141      */
142     private void setRadiusScaleFactor() {
143         this.radiusScaleFactor = this.nodes.size() / this.RADIUS_SCALE;
144         if (this.radiusScaleFactor < this.MIN_RADIUS_SCALE_FACTOR) {
145             this.radiusScaleFactor = this.MIN_RADIUS_SCALE_FACTOR;
146         }
147         if (this.radiusScaleFactor > this.MAX_RADIUS_SCALE_FACTOR) {
148             this.radiusScaleFactor = this.MAX_RADIUS_SCALE_FACTOR;
149         }
150     }
151 
152     /**
153      * This class represents single edge in graph.
154      *
155      * @author Jakub Fibinger jfibinger@gmail.com
156      * @author Kornel Skalkowski kornelsk@gmail.com
157      *
158      */
159     private class Edge {
160 
161         Node n1, n2;
162 
163         int weight;
164 
165         /**
166          * Public constructor.
167          *
168          * @param n1
169          *            first node of edge.
170          * @param n2
171          *            second node of edge.
172          * @param weight
173          *            edge weight.
174          */
175         public Edge(Node n1, Node n2, int weight) {
176             this.n1 = n1;
177             this.n2 = n2;
178             this.weight = weight;
179         }
180 
181     }
182 
183     /**
184      * This method looks for Edge object connecting two nodes
185      *
186      * @param edges
187      *            list of all edges
188      * @param n1
189      *            first node
190      * @param n2
191      *            second node
192      * @return found edge or null if doesn't exist
193      */
194     private Edge findEdge(List<Edge> edges, Node n1, Node n2) {
195         for (Edge e : edges) {
196             if (e.n2 == n1 && e.n1 == n2) {
197                 return e;
198             }
199         }
200         return null;
201     }
202 
203     /**
204      * This method returns number of calls between two nodes
205      *
206      * @param n1
207      *            first node
208      * @param n2
209      *            second node
210      * @return sum of calls in both directions
211      */
212     private int getWeight(Node n1, Node n2) {
213         for (ConnectionGroup cg : n1.getConnectionGroups()) {
214             if (cg.getTarget() == n2 || cg.getSource() == n2) {
215                 int weight = cg.getConnectionCount();
216                 return weight > 40 ? 40 : weight;
217             }
218         }
219         return 0;
220     }
221 
222     /**
223      * This method calculates euclides distance between two nodes
224      *
225      * @param n1
226      *            first node
227      * @param n2
228      *            second node
229      * @return calculated distance
230      */
231     private double distance(Node n1, Node n2) {
232         double length = n1.getLocation().x - n2.getLocation().x;
233         double height = n1.getLocation().y - n2.getLocation().y;
234         return Math.sqrt(length * length + height * height);
235     }
236 
237     /**
238      * Potential is function that is sum of potential of each edge. For edge e potential is caltulated as follows:
239      * potential(e) = 1 - const_param * weight(e) * distance(e) where weight() is number of calls between two nodes
240      * distance() is euclides distance between two nodes const_param is number used to scale the product of weight and
241      * distance
242      *
243      * @param edges
244      *            list of all edges between nodes
245      * @return calculated potential
246      */
247     private double potential(List<Edge> edges) {
248         double result = 0;
249         for (Edge e : edges) {
250             double a = 1 - DISTANCE_FACTOR * getWeight(e.n1, e.n2) * distance(e.n1, e.n2);
251             result += a * a;
252         }
253         return result;
254     }
255 
256     /**
257      * This method calculates derivative of potential function with respect to a variable x of node n
258      *
259      * @param edges
260      *            list of all edges between nodes
261      * @param n
262      *            node
263      * @return calculated partial derivative
264      */
265     private double derivativeX(List<Edge> edges, Node n) {
266         double result = 0;
267         double x1 = n.getLocation().x;
268         double x2 = 0;
269         for (Edge e : edges) {
270             if (e.n1 == n || e.n2 == n) {
271                 if (e.n1 == n) {
272                     x2 = e.n2.getLocation().x;
273                 } else {
274                     x2 = e.n1.getLocation().x;
275                 }
276 
277                 double a = x1 - x2;
278                 double b = e.n1.getLocation().y - e.n2.getLocation().y;
279                 double sqrt = Math.sqrt(a * a + b * b);
280                 double w = getWeight(e.n1, e.n2);
281                 w *= DISTANCE_FACTOR;
282                 result += -(2 * a * w * (w * sqrt - 1)) / sqrt;
283             }
284         }
285         return result;
286     }
287 
288     /**
289      * This method calculates derivative of potential function with respect to a variable y of node n
290      *
291      * @param edges
292      *            list of all edges between nodes
293      * @param n
294      *            node
295      * @return calculated partial derivative
296      */
297     private double derivativeY(List<Edge> edges, Node n) {
298         double result = 0;
299         double x1 = n.getLocation().y;
300         double x2 = 0;
301         for (Edge e : edges) {
302             if (e.n1 == n || e.n2 == n) {
303                 if (e.n1 == n) {
304                     x2 = e.n2.getLocation().y;
305                 } else {
306                     x2 = e.n1.getLocation().y;
307                 }
308 
309                 double a = x1 - x2;
310                 double b = e.n1.getLocation().x - e.n2.getLocation().x;
311                 double sqrt = Math.sqrt(a * a + b * b);
312                 double w = getWeight(e.n1, e.n2);
313                 w *= DISTANCE_FACTOR;
314 
315                 result += -(2 * a * w * (w * sqrt - 1)) / sqrt;
316             }
317         }
318         return result;
319     }
320 
321     /**
322      * This method is just a trial, it calculates potential for changed arrangement of nodes using gradient multiplied
323      * by multiplier
324      *
325      * @param gradient
326      *            vector of derivatives for all nodes, defines the directions for nodes to move
327      * @param multiplier
328      *            number that says how big the jump of nodes has to be
329      * @param edges
330      *            list of all edges between nodes
331      * @return value of calculated potential
332      */
333     private double potentialForGradient(double[] gradient, int multiplier, List<Edge> edges) {
334         int i = 0;
335         Point[] oldCoords = new Point[nodes.size()];
336         for (Node n : nodes) {
337             int x = n.getLocation().x;
338             int y = n.getLocation().y;
339             oldCoords[i / 2] = n.getLocation();
340 
341             n.setLocation(new Point(x + gradient[i++] / multiplier, y + gradient[i++] / multiplier));
342         }
343         double result = potential(edges);
344         i = 0;
345         for (Node n : nodes) {
346             n.setLocation(oldCoords[i++]);
347         }
348         return result;
349     }
350 
351     /**
352      * This method calculates arrangement of nodes using steepest descent algorithm. It tries to set the best distances
353      * between nodes - lower distance for connection group with bigger amount of connections.
354      *
355      * @param edges
356      *            list of all edges between nodes
357      */
358     private void steepestDescent(List<Edge> edges) {
359         double[] gradient = new double[nodes.size() * 2];
360         int i = 0;
361         for (Node n : nodes) {
362             gradient[i++] = derivativeX(edges, n) * 1000000;
363             gradient[i++] = derivativeY(edges, n) * 1000000;
364         }
365 
366         int multi = 8;
367         double result = potentialForGradient(gradient, multi, edges);
368         double resultForLess = potentialForGradient(gradient, multi / 2, edges);
369         double resultForMore = potentialForGradient(gradient, multi * 2, edges);
370         if (resultForLess < result) {
371             result = resultForLess;
372             multi /= 2;
373             resultForLess = potentialForGradient(gradient, multi / 2, edges);
374             while (resultForLess < result) {
375                 multi /= 2;
376                 result = resultForLess;
377                 resultForLess = potentialForGradient(gradient, multi / 2, edges);
378             }
379         } else if (resultForMore < result) {
380             result = resultForMore;
381             multi *= 2;
382             resultForMore = potentialForGradient(gradient, multi * 2, edges);
383             while (resultForMore < result) {
384                 multi *= 2;
385                 result = resultForMore;
386                 resultForMore = potentialForGradient(gradient, multi * 2, edges);
387             }
388         }
389         int multiplier = multi;
390 
391         i = 0;
392         for (Node n : nodes) {
393             int x = n.getLocation().x;
394             int y = n.getLocation().y;
395             n.setLocation(new Point(x + gradient[i++] / multiplier, y + gradient[i++] / multiplier));
396         }
397     }
398 
399     /**
400      * This method makes BFS.
401      *
402      * @param rootNode
403      *            root of tree being made.
404      */
405     private void makeTree(TreeNode rootNode, Collection<Node> nodes, List<String> connectionNodes,
406             List<String> rootNodes) {
407         TreeNode nextNode = null;
408         Queue<TreeNode> queue = new LinkedList<TreeNode>();
409         Map<Node, TreeNode> nodeToTreeNodeMap = new HashMap<Node, TreeNode>();
410 
411         queue.offer(rootNode);
412         nodeToTreeNodeMap.put(rootNode.node, rootNode);
413 
414         while (!queue.isEmpty()) {
415             nextNode = queue.poll();
416 
417             if (connectionNodes.contains(nextNode.node.getId())) {
418                 String connectionNodeID = nextNode.node.getId();
419                 connectionNodes.remove(connectionNodeID);
420                 Node fakeChild = this.getNodeFromID(rootNodes.remove(0), nodes);
421                 TreeNode fakeTreeChild = new TreeNode(fakeChild);
422                 nextNode.children.add(fakeTreeChild);
423                 this.treeSize++;
424                 nodes.remove(fakeChild);
425                 queue.offer(fakeTreeChild);
426             }
427 
428             for (Node neighbor : getNeighbors(nodes, nextNode.node)) {
429                 if (!nodeToTreeNodeMap.containsKey(neighbor)) {
430                     TreeNode treeNeighbor = new TreeNode(neighbor);
431                     this.treeSize++;
432                     nodes.remove(neighbor);
433                     nodeToTreeNodeMap.put(neighbor, treeNeighbor);
434                     treeNeighbor.parent = nextNode;
435                     nextNode.children.add(treeNeighbor);
436 
437                     queue.offer(treeNeighbor);
438                 }
439             }
440         }
441     }
442 
443     /**
444      * This method fills adjacent table class field. It maps graph given as parameter to adjacent table.
445      *
446      * @param nodes
447      *            list of nodes in graph.
448      */
449     private void fillAdiacentTable(List<Node> nodes) {
450         this.adiacentTable = new boolean[nodes.size()][nodes.size()];
451 
452         int position = 0;
453         for (Node node : this.nodes) {
454             this.adiacentTablePositions.put(node.getId(), position++);
455         }
456 
457         for (Node node1 : nodes) {
458             for (Node node2 : this.getNeighbors(nodes, node1)) {
459                 this.adiacentTable[this.adiacentTablePositions.get(node1.getId())][this.adiacentTablePositions
460                         .get(node2.getId())] = true;
461             }
462         }
463     }
464 
465     /**
466      * This method initializes nodes' distance tables. It adds to them node's neighbors with distance value set on 1. It
467      * should be called after fillAdiacentTable() method call.
468      *
469      * @param nodes
470      *            list of nodes in graph.
471      */
472     private void initNodesDistanceTables(List<Node> nodes) {
473         for (Node node : nodes) {
474             Map<String, Integer> neighbours = new HashMap<String, Integer>();
475             this.distancesTable.put(node.getId(), neighbours);
476             for (Node neighbour : this.getNeighbors(nodes, node)) {
477                 neighbours.put(neighbour.getId(), new Integer(1));
478             }
479         }
480     }
481 
482     /**
483      * This method fills nodes' distance tables. It should be called after initNodesDistanceTables() method call.
484      *
485      * @param nodes
486      *            list of nodes in graph.
487      */
488     private void fillNodesDistanceTables(List<Node> nodes) {
489         boolean wasChange = true;
490 
491         while (wasChange) {
492             wasChange = false;
493 
494             for (Node node : nodes) {
495                 Map<String, Integer> distancesFromNode = this.distancesTable.get(node.getId());
496                 for (Node neighbour : this.getNeighbors(nodes, node)) {
497                     String neighbourID = neighbour.getId();
498                     for (String nodeID : distancesFromNode.keySet()) {
499                         if (nodeID.compareTo(neighbourID) != 0) {
500                             if (this.distancesTable.get(neighbourID).containsKey(nodeID)) {
501                                 if (this.distancesTable.get(neighbourID).get(nodeID) > distancesFromNode.get(nodeID) + 1) {
502                                     this.distancesTable.get(neighbourID).put(nodeID, distancesFromNode.get(nodeID) + 1);
503                                     wasChange = true;
504                                 }
505                             } else {
506                                 this.distancesTable.get(neighbourID).put(nodeID, distancesFromNode.get(nodeID) + 1);
507                                 wasChange = true;
508                             }
509                         }
510                     }
511                 }
512             }
513         }
514     }
515 
516     /**
517      * This method computes sums of distances from every node to others.
518      */
519     private void computeDistanceSums() {
520         for (String nodeID : this.distancesTable.keySet()) {
521             int distancesSum = 0;
522 
523             for (Integer distance : this.distancesTable.get(nodeID).values()) {
524                 distancesSum += distance.intValue();
525             }
526 
527             this.distanceSums.put(nodeID, new Integer(distancesSum));
528         }
529     }
530 
531     /**
532      * This method returns root nodes ID numbers.
533      *
534      * @param positionNodes
535      *            list of nodes in graph.
536      * @return list of root nodes' IDs.
537      */
538     private List<String> getRootNodesIDs(List<Node> positionNodes) {
539         int nrOfNodesInGraph = this.distancesTable.size();
540         List<String> rootNodeIDs = new LinkedList<String>();
541         while (nrOfNodesInGraph > 0) {
542             String rootID = this.distanceSums.keySet().iterator().next();
543             Map<String, Integer> rootDistancesTable = this.distancesTable.get(rootID);
544             int distancesSum = this.distanceSums.remove(rootID);
545 
546             nrOfNodesInGraph -= (rootDistancesTable.size() + 1);
547 
548             for (String nodeID : rootDistancesTable.keySet()) {
549                 if (distancesSum > this.distanceSums.get(nodeID).intValue()) {
550                     distancesSum = this.distanceSums.get(nodeID).intValue();
551                     rootID = nodeID;
552                 }
553                 this.distanceSums.remove(nodeID);
554             }
555             rootNodeIDs.add(rootID);
556         }
557         return rootNodeIDs;
558     }
559 
560     /**
561      * This method returns main root from root nodes given as parameter.
562      *
563      * @param rootNodesIDs
564      *            list of possible root nodes' IDs.
565      * @return ID of root node; null if no rootNodesIDs are specified
566      */
567     private String getMainRoot(String[] rootNodesIDs) {
568         String rootNodeID = null;
569         if (rootNodesIDs.length > 0) {
570             rootNodeID = rootNodesIDs[0];
571             int maxSum = this.distanceSums.get(rootNodeID).intValue();
572             for (int i = 1; i < rootNodesIDs.length; i++) {
573                 if (maxSum < this.distanceSums.get(rootNodesIDs[i]).intValue()) {
574                     maxSum = this.distanceSums.get(rootNodesIDs[i]).intValue();
575                     rootNodeID = rootNodesIDs[i];
576                 }
577             }
578 
579         }
580         return rootNodeID;
581 
582     }
583 
584     /**
585      * This method returns proper number of nodes which can be used to join with subgraphs. Number of returned nodes is
586      * defined by numberOfNodesToReturn parameter. Root node of graph which is searched for connection nodes is defined
587      * by rootID parameter.
588      *
589      * @param numberOfNodesToReturn
590      *            number of connection nodes which has to be returned.
591      * @param rootID
592      *            id of root node in graph.
593      * @return list of connection nodes.
594      */
595     private List<String> getRootConnectionNodes(int numberOfNodesToReturn, String rootID) {
596         List<String> connectionNodesIDs = new LinkedList<String>();
597         int numberOfNodesAddedToResultTable = 0;
598 
599         if (this.distancesTable.get(rootID).size() <= numberOfNodesToReturn) {
600             while (numberOfNodesAddedToResultTable < numberOfNodesToReturn) {
601                 Iterator<String> it = this.distancesTable.get(rootID).keySet().iterator();
602                 while (it.hasNext()) {
603                     connectionNodesIDs.add(it.next());
604                     numberOfNodesAddedToResultTable++;
605                     if (numberOfNodesAddedToResultTable == numberOfNodesToReturn) {
606                         break;
607                     }
608                 }
609             }
610         } else {
611             Integer[] distanceSumsArray = this.distancesTable.get(rootID).values().toArray(new Integer[0]);
612             Arrays.sort(distanceSumsArray);
613             int minimumSumValue = distanceSumsArray[distanceSumsArray.length - numberOfNodesToReturn];
614 
615             for (String nodeID : this.distancesTable.get(rootID).keySet()) {
616                 if (this.distancesTable.get(rootID).get(nodeID).intValue() >= minimumSumValue) {
617                     connectionNodesIDs.add(nodeID);
618                     numberOfNodesAddedToResultTable++;
619                     if (numberOfNodesAddedToResultTable == numberOfNodesToReturn) {
620                         break;
621                     }
622                 }
623             }
624         }
625         return connectionNodesIDs;
626     }
627 
628     /**
629      * This method returns node which has ID given as parameter.
630      *
631      * @param nodeID
632      *            ID of searched node.
633      * @param positionNodes
634      *            list of nodes in graph.
635      * @return node which has given ID.
636      */
637     private Node getNodeFromID(String nodeID, Collection<Node> positionNodes) {
638         Iterator<Node> it = positionNodes.iterator();
639         while (it.hasNext()) {
640             Node node = it.next();
641             if (node.getId().compareTo(nodeID) == 0) {
642                 return node;
643             }
644         }
645         return null;
646     }
647 
648     /**
649      * This method merge unconnected graph in one and make BTS tree.
650      *
651      * @param positionNodes
652      *            list of nodes in graph.
653      * @return root node of connected graph.
654      */
655     private TreeNode mergeGraphAndMakeTree(List<Node> positionNodes) {
656         this.fillAdiacentTable(positionNodes);
657         this.initNodesDistanceTables(positionNodes);
658         this.fillNodesDistanceTables(positionNodes);
659         this.computeDistanceSums();
660         List<String> rootNodesIDs = this.getRootNodesIDs(positionNodes);
661         this.computeDistanceSums();
662         String mainRootNode = this.getMainRoot(rootNodesIDs.toArray(new String[0]));
663 
664         rootNodesIDs.remove(mainRootNode);
665 
666         List<String> connectionNodesIDs = null;
667         if (rootNodesIDs.size() > 0) {
668             connectionNodesIDs = this.getRootConnectionNodes(rootNodesIDs.size(), mainRootNode);
669         } else {
670             connectionNodesIDs = new LinkedList<String>();
671         }
672 
673         positionNodes.remove(mainRootNode);
674         TreeNode treeRoot = new TreeNode(this.getNodeFromID(mainRootNode, positionNodes));
675         this.makeTree(treeRoot, positionNodes, connectionNodesIDs, rootNodesIDs);
676         return treeRoot;
677     }
678 
679     /**
680      * This method sets root node and root's neighbors in proper locations.
681      *
682      * @param positionNodes
683      *            collection of nodes to position (with or without main root). If the list is empty the method does
684      *            nothing
685      */
686     private void setRootNode(List<Node> positionNodes) {
687         if (positionNodes == null || positionNodes.isEmpty()) {
688             return; // cannot set root node in there is no nodes specified
689         }
690 
691         TreeNode treeRoot = this.mergeGraphAndMakeTree(positionNodes);
692         treeRoot.node.setLocation(new Point(this.AREA_CENTER_X, this.AREA_CENTER_Y));
693 
694         boolean radiusFactorInvert = true;
695         double alfa = 0;
696         for (TreeNode treeNode : treeRoot.children) {
697             Node n = treeNode.node;
698             double temp = this.getAngleRange(2 * Math.PI, this.countChildren(treeRoot), this.treeSize);
699             int x = (int)((this.AREA_CENTER_X - this.radius) * Math.cos(alfa + temp / 2.0));
700             int y = (int)((this.AREA_CENTER_Y - this.radius) * Math.sin(alfa + temp / 2.0));
701             n.setLocation(new Point(x, y));
702 
703             if (radiusFactorInvert) {
704                 this.setNode(treeRoot, 2, countChildren(treeRoot), alfa, this.ANGLE_RANGE_FACTOR * temp,
705                         (this.radiusScaleFactor + 1) * 0.8);
706                 radiusFactorInvert = false;
707             } else {
708                 this.setNode(treeRoot, 2, countChildren(treeRoot), alfa, this.ANGLE_RANGE_FACTOR * temp,
709                         this.radiusScaleFactor + 1);
710                 radiusFactorInvert = true;
711             }
712             alfa += temp;
713         }
714         this.treeSize = 0;
715     }
716 
717     /**
718      * This method sets children's locations of node given as parameter.
719      *
720      * @param node
721      *            method sets positions of this node's children.
722      * @param deepLevel
723      *            distance from root.
724      * @param nrOfNodes
725      *            number of all nodes in given angle.
726      * @param angle
727      *            initial angle.
728      * @param angleRange
729      *            angle interval.
730      */
731     private void setNode(TreeNode node, int deepLevel, int nrOfNodes, double angle, double angleRange,
732             double radiusFactor) {
733         boolean radiusFactorInvert = true;
734         for (TreeNode n : node.children) {
735             double temp = this.getAngleRange(angleRange, this.countChildren(n), nrOfNodes);
736             int x = (int)((this.AREA_CENTER_X - this.radius * radiusFactor * deepLevel) * Math.cos(angle + temp / 2.0));
737             int y = (int)((this.AREA_CENTER_Y - this.radius * radiusFactor * deepLevel) * Math.sin(angle + temp / 2.0));
738             n.node.setLocation(new Point(x, y));
739 
740             if (radiusFactorInvert) {
741                 this.setNode(n, deepLevel + 1, this.countChildren(n), angle, this.ANGLE_RANGE_FACTOR * temp,
742                         this.radiusScaleFactor * 0.8);
743                 radiusFactorInvert = false;
744             } else {
745                 this.setNode(n, deepLevel + 1, this.countChildren(n), angle, this.ANGLE_RANGE_FACTOR * temp,
746                         this.radiusScaleFactor);
747                 radiusFactorInvert = true;
748             }
749             angle += temp;
750         }
751     }
752 
753     /**
754      * This method returns angle interval depends on number of node's children.
755      *
756      * @param angle
757      *            angle to divide between node's children.
758      * @param nrOfChildren
759      *            number of node's children.
760      * @param nrOfNodes
761      *            number of all nodes in angle.
762      * @return angle range for single node.
763      */
764     private double getAngleRange(double angle, double nrOfChildren, double nrOfNodes) {
765         return angle * nrOfChildren / nrOfNodes;
766     }
767 
768     /**
769      * This method returns number of nodes children.
770      *
771      * @param node
772      *            method counts number of this node's children.
773      * @return number of node's children.
774      */
775     private int countChildren(TreeNode node) {
776         Collection<TreeNode> children = node.children;
777         int result = 0;
778 
779         if (children.size() == 0) {
780             result = 1;
781         }
782 
783         for (TreeNode n : children) {
784             result += this.countChildren(n);
785         }
786 
787         return result;
788     }
789 
790     /**
791      * This method returns number of working units.
792      *
793      * @return number of work units
794      */
795     @Override
796     protected int getWorkUnits(int nodesCount) {
797         return (nodesCount + 1) * nodesCount;
798     }
799 }