View Javadoc
1   /*
2    * Copyright (c) 2005, The JUNG Authors
3    * All rights reserved.
4    *
5    * This software is open-source under the BSD license; see either "license.txt"
6    * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
7    *
8    * Created on Jul 9, 2005
9    */
10  
11  package edu.uci.ics.jung.algorithms.layout;
12  import java.awt.Dimension;
13  import java.awt.geom.Point2D;
14  import java.util.ArrayList;
15  import java.util.HashMap;
16  import java.util.List;
17  import java.util.Map;
18  
19  import com.google.common.cache.CacheBuilder;
20  import com.google.common.cache.CacheLoader;
21  import com.google.common.cache.LoadingCache;
22  
23  import edu.uci.ics.jung.graph.Forest;
24  import edu.uci.ics.jung.graph.util.TreeUtils;
25  
26  /**
27   * A {@code Layout} implementation that assigns positions to {@code Tree} or 
28   * {@code Forest} vertices using associations with nested circles ("balloons").  
29   * A balloon is nested inside another balloon if the first balloon's subtree
30   * is a subtree of the second balloon's subtree.
31   * 
32   * @author Tom Nelson 
33   *  
34   */
35  public class BalloonLayout<V,E> extends TreeLayout<V,E> {
36  
37      protected LoadingCache<V, PolarPoint> polarLocations
38      	= CacheBuilder.newBuilder().build(new CacheLoader<V, PolarPoint>() {
39      		public PolarPoint load(V vertex) {
40      			return new PolarPoint();
41      		}
42  	});
43      
44      protected Map<V,Double> radii = new HashMap<V,Double>();
45      
46      /**
47       * Creates an instance based on the input forest.
48       * @param g the forest on which this layout will operate
49       */
50      public BalloonLayout(Forest<V,E> g) 
51      {
52          super(g);
53      }
54      
55      protected void setRootPolars() 
56      {
57          List<V> roots = TreeUtils.getRoots(graph);
58          if(roots.size() == 1) {
59      		// its a Tree
60      		V root = roots.get(0);
61      		setRootPolar(root);
62              setPolars(new ArrayList<V>(graph.getChildren(root)),
63                      getCenter(), getSize().width/2);
64      	} else if (roots.size() > 1) {
65      		// its a Forest
66      		setPolars(roots, getCenter(), getSize().width/2);
67      	}
68      }
69      
70      protected void setRootPolar(V root) {
71      	PolarPoint pp = new PolarPoint(0,0);
72      	Point2D p = getCenter();
73      	polarLocations.put(root, pp);
74      	locations.put(root, p);
75      }
76      
77  
78      protected void setPolars(List<V> kids, Point2D parentLocation, double parentRadius) {
79  
80      	int childCount = kids.size();
81      	if(childCount == 0) return;
82      	// handle the 1-child case with 0 limit on angle.
83      	double angle = Math.max(0, Math.PI / 2 * (1 - 2.0/childCount));
84      	double childRadius = parentRadius*Math.cos(angle) / (1 + Math.cos(angle));
85      	double radius = parentRadius - childRadius;
86  
87      	double rand = Math.random();
88  
89      	for(int i=0; i< childCount; i++) {
90      		V child = kids.get(i);
91      		double theta = i* 2*Math.PI/childCount + rand;
92      		radii.put(child, childRadius);
93      		
94      		PolarPoint pp = new PolarPoint(theta, radius);
95      		polarLocations.put(child, pp);
96      		
97      		Point2D p = PolarPoint.polarToCartesian(pp);
98      		p.setLocation(p.getX()+parentLocation.getX(), p.getY()+parentLocation.getY());
99      		locations.put(child, p);
100     		setPolars(new ArrayList<V>(graph.getChildren(child)), p, childRadius);
101     	}
102     }
103 
104     @Override
105     public void setSize(Dimension size) {
106     	this.size = size;
107     	setRootPolars();
108     }
109 
110 	/**
111 	 * @param v the vertex whose center is to be returned
112 	 * @return the coordinates of {@code v}'s parent, or the center of this layout's area if it's a root.
113 	 */
114 	public Point2D getCenter(V v) {
115 		V parent = graph.getParent(v);
116 		if(parent == null) {
117 			return getCenter();
118 		}
119 		return locations.getUnchecked(parent);
120 	}
121 
122 	@Override
123     public void setLocation(V v, Point2D location) {
124 		Point2D c = getCenter(v);
125 		Point2D pv = new Point2D.Double(location.getX()-c.getX(),location.getY()-c.getY());
126 		PolarPoint newLocation = PolarPoint.cartesianToPolar(pv);
127 		polarLocations.getUnchecked(v).setLocation(newLocation);
128 		
129 		Point2D center = getCenter(v);
130 		pv.setLocation(pv.getX()+center.getX(), pv.getY()+center.getY());
131 		locations.put(v, pv);
132 	}
133 
134 	@Override
135     public Point2D apply(V v) {
136 		return locations.getUnchecked(v);
137 	}
138 
139 	/**
140 	 * @return the radii
141 	 */
142 	public Map<V, Double> getRadii() {
143 		return radii;
144 	}
145 }