1
2
3
4
5
6
7
8
9
10
11 package edu.uci.ics.jung.algorithms.layout;
12 import java.awt.Dimension;
13 import java.awt.Point;
14 import java.awt.geom.Point2D;
15 import java.util.Collection;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Map;
19 import java.util.Set;
20
21 import com.google.common.base.Function;
22 import com.google.common.cache.CacheBuilder;
23 import com.google.common.cache.CacheLoader;
24 import com.google.common.cache.LoadingCache;
25
26 import edu.uci.ics.jung.graph.Forest;
27 import edu.uci.ics.jung.graph.Graph;
28 import edu.uci.ics.jung.graph.util.TreeUtils;
29
30
31
32
33
34 public class TreeLayout<V,E> implements Layout<V,E> {
35
36 protected Dimension size = new Dimension(600,600);
37 protected Forest<V,E> graph;
38 protected Map<V,Integer> basePositions = new HashMap<V,Integer>();
39
40 protected LoadingCache<V, Point2D> locations =
41 CacheBuilder.newBuilder().build(new CacheLoader<V, Point2D>() {
42 public Point2D load(V vertex) {
43 return new Point2D.Double();
44 }
45 });
46
47 protected transient Set<V> alreadyDone = new HashSet<V>();
48
49
50
51
52 public static int DEFAULT_DISTX = 50;
53
54
55
56
57 public static int DEFAULT_DISTY = 50;
58
59
60
61
62 protected int distX = 50;
63
64
65
66
67 protected int distY = 50;
68
69 protected transient Point m_currentPoint = new Point();
70
71
72
73
74
75 public TreeLayout(Forest<V,E> g) {
76 this(g, DEFAULT_DISTX, DEFAULT_DISTY);
77 }
78
79
80
81
82
83
84
85 public TreeLayout(Forest<V,E> g, int distx) {
86 this(g, distx, DEFAULT_DISTY);
87 }
88
89
90
91
92
93
94
95 public TreeLayout(Forest<V,E> g, int distx, int disty) {
96 if (g == null)
97 throw new IllegalArgumentException("Graph must be non-null");
98 if (distx < 1 || disty < 1)
99 throw new IllegalArgumentException("X and Y distances must each be positive");
100 this.graph = g;
101 this.distX = distx;
102 this.distY = disty;
103 buildTree();
104 }
105
106 protected void buildTree() {
107 this.m_currentPoint = new Point(0, 20);
108 Collection<V> roots = TreeUtils.getRoots(graph);
109 if (roots.size() > 0 && graph != null) {
110 calculateDimensionX(roots);
111 for(V v : roots) {
112 calculateDimensionX(v);
113 m_currentPoint.x += this.basePositions.get(v)/2 + this.distX;
114 buildTree(v, this.m_currentPoint.x);
115 }
116 }
117 }
118
119 protected void buildTree(V v, int x) {
120
121 if (alreadyDone.add(v)) {
122
123 this.m_currentPoint.y += this.distY;
124 this.m_currentPoint.x = x;
125
126 this.setCurrentPositionFor(v);
127
128 int sizeXofCurrent = basePositions.get(v);
129
130 int lastX = x - sizeXofCurrent / 2;
131
132 int sizeXofChild;
133 int startXofChild;
134
135 for (V element : graph.getSuccessors(v)) {
136 sizeXofChild = this.basePositions.get(element);
137 startXofChild = lastX + sizeXofChild / 2;
138 buildTree(element, startXofChild);
139 lastX = lastX + sizeXofChild + distX;
140 }
141 this.m_currentPoint.y -= this.distY;
142 }
143 }
144
145 private int calculateDimensionX(V v) {
146
147 int size = 0;
148 int childrenNum = graph.getSuccessors(v).size();
149
150 if (childrenNum != 0) {
151 for (V element : graph.getSuccessors(v)) {
152 size += calculateDimensionX(element) + distX;
153 }
154 }
155 size = Math.max(0, size - distX);
156 basePositions.put(v, size);
157
158 return size;
159 }
160
161 private int calculateDimensionX(Collection<V> roots) {
162
163 int size = 0;
164 for(V v : roots) {
165 int childrenNum = graph.getSuccessors(v).size();
166
167 if (childrenNum != 0) {
168 for (V element : graph.getSuccessors(v)) {
169 size += calculateDimensionX(element) + distX;
170 }
171 }
172 size = Math.max(0, size - distX);
173 basePositions.put(v, size);
174 }
175
176 return size;
177 }
178
179
180
181
182
183
184 public void setSize(Dimension size) {
185 throw new UnsupportedOperationException("Size of TreeLayout is set" +
186 " by vertex spacing in constructor");
187 }
188
189 protected void setCurrentPositionFor(V vertex) {
190 int x = m_currentPoint.x;
191 int y = m_currentPoint.y;
192 if(x < 0) size.width -= x;
193
194 if(x > size.width-distX)
195 size.width = x + distX;
196
197 if(y < 0) size.height -= y;
198 if(y > size.height-distY)
199 size.height = y + distY;
200 locations.getUnchecked(vertex).setLocation(m_currentPoint);
201
202 }
203
204 public Graph<V,E> getGraph() {
205 return graph;
206 }
207
208 public Dimension getSize() {
209 return size;
210 }
211
212 public void initialize() {
213
214 }
215
216 public boolean isLocked(V v) {
217 return false;
218 }
219
220 public void lock(V v, boolean state) {
221 }
222
223 public void reset() {
224 }
225
226 public void setGraph(Graph<V,E> graph) {
227 if(graph instanceof Forest) {
228 this.graph = (Forest<V,E>)graph;
229 buildTree();
230 } else {
231 throw new IllegalArgumentException("graph must be a Forest");
232 }
233 }
234
235 public void setInitializer(Function<V, Point2D> initializer) {
236 }
237
238
239
240
241 public Point2D getCenter() {
242 return new Point2D.Double(size.getWidth()/2,size.getHeight()/2);
243 }
244
245 public void setLocation(V v, Point2D location) {
246 locations.getUnchecked(v).setLocation(location);
247 }
248
249 public Point2D apply(V v) {
250 return locations.getUnchecked(v);
251 }
252 }