1
2
3
4
5
6
7
8
9
10
11 package edu.uci.ics.jung.algorithms.layout;
12
13 import java.awt.Dimension;
14 import java.awt.geom.Point2D;
15 import java.util.ConcurrentModificationException;
16 import java.util.HashSet;
17 import java.util.Set;
18
19 import com.google.common.base.Function;
20 import com.google.common.base.Functions;
21 import com.google.common.base.Preconditions;
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.Graph;
27
28
29
30
31
32
33
34
35
36
37
38 abstract public class AbstractLayout<V, E> implements Layout<V,E> {
39
40
41
42
43 private Set<V> dontmove = new HashSet<V>();
44
45 protected Dimension size;
46 protected Graph<V, E> graph;
47 protected boolean initialized;
48
49 protected LoadingCache<V, Point2D> locations =
50 CacheBuilder.newBuilder().build(new CacheLoader<V, Point2D>() {
51 public Point2D load(V vertex) {
52 return new Point2D.Double();
53 }
54 });
55
56
57
58
59
60
61 protected AbstractLayout(Graph<V, E> graph) {
62 if (graph == null)
63 {
64 throw new IllegalArgumentException("Graph must be non-null");
65 }
66 this.graph = graph;
67 }
68
69
70
71
72
73
74
75
76 protected AbstractLayout(Graph<V,E> graph, Function<V,Point2D> initializer) {
77 this.graph = graph;
78 Function<V, Point2D> chain =
79 Functions.<V,Point2D,Point2D>compose(
80 new Function<Point2D,Point2D>(){
81 public Point2D apply(Point2D p) {
82 return (Point2D)p.clone();
83 }},
84 initializer
85 );
86 this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain));
87 initialized = true;
88 }
89
90
91
92
93
94
95
96 protected AbstractLayout(Graph<V,E> graph, Dimension size) {
97 this.graph = graph;
98 this.size = size;
99 }
100
101
102
103
104
105
106
107
108
109 protected AbstractLayout(Graph<V,E> graph, Function<V,Point2D> initializer, Dimension size) {
110 this.graph = graph;
111 Function<V, Point2D> chain =
112 Functions.<V,Point2D,Point2D>compose(
113 new Function<Point2D,Point2D>(){
114 public Point2D apply(Point2D p) {
115 return (Point2D)p.clone();
116 }},
117 initializer
118 );
119 this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain));
120 this.size = size;
121 }
122
123 public void setGraph(Graph<V,E> graph) {
124 this.graph = graph;
125 if(size != null && graph != null) {
126 initialize();
127 }
128 }
129
130
131
132
133
134
135 public void setSize(Dimension size) {
136
137 if(size != null && graph != null) {
138
139 Dimension oldSize = this.size;
140 this.size = size;
141 initialize();
142
143 if(oldSize != null) {
144 adjustLocations(oldSize, size);
145 }
146 }
147 }
148
149 private void adjustLocations(Dimension oldSize, Dimension size) {
150
151 int xOffset = (size.width - oldSize.width) / 2;
152 int yOffset = (size.height - oldSize.height) / 2;
153
154
155 while(true) {
156 try {
157 for(V v : getGraph().getVertices()) {
158 offsetVertex(v, xOffset, yOffset);
159 }
160 break;
161 } catch(ConcurrentModificationException cme) {
162 }
163 }
164 }
165
166 public boolean isLocked(V v) {
167 return dontmove.contains(v);
168 }
169
170 public void setInitializer(Function<V,Point2D> initializer) {
171 if(this.equals(initializer)) {
172 throw new IllegalArgumentException("Layout cannot be initialized with itself");
173 }
174 Function<V, Point2D> chain =
175 Functions.<V,Point2D,Point2D>compose(
176 new Function<Point2D,Point2D>(){
177 public Point2D apply(Point2D p) {
178 return (Point2D)p.clone();
179 }},
180 initializer
181 );
182 this.locations = CacheBuilder.newBuilder().build(CacheLoader.from(chain));
183 initialized = true;
184 }
185
186
187
188
189
190
191
192 public Dimension getSize() {
193 return size;
194 }
195
196
197
198
199
200
201
202
203 private Point2D getCoordinates(V v) {
204 return locations.getUnchecked(v);
205 }
206
207 public Point2D apply(V v) {
208 return getCoordinates(v);
209 }
210
211
212
213
214
215
216
217
218 public double getX(V v) {
219 Preconditions.checkNotNull(getCoordinates(v), "Cannot getX for an unmapped vertex "+v);
220 return getCoordinates(v).getX();
221 }
222
223
224
225
226
227
228
229
230 public double getY(V v) {
231 Preconditions.checkNotNull(getCoordinates(v), "Cannot getY for an unmapped vertex "+v);
232 return getCoordinates(v).getY();
233 }
234
235
236
237
238
239
240 protected void offsetVertex(V v, double xOffset, double yOffset) {
241 Point2D c = getCoordinates(v);
242 c.setLocation(c.getX()+xOffset, c.getY()+yOffset);
243 setLocation(v, c);
244 }
245
246
247
248
249 public Graph<V, E> getGraph() {
250 return graph;
251 }
252
253
254
255
256
257
258
259
260
261
262 public void setLocation(V picked, double x, double y) {
263 Point2D coord = getCoordinates(picked);
264 coord.setLocation(x, y);
265 }
266
267 public void setLocation(V picked, Point2D p) {
268 Point2D coord = getCoordinates(picked);
269 coord.setLocation(p);
270 }
271
272
273
274
275
276
277 public void lock(V v, boolean state) {
278 if(state == true)
279 dontmove.add(v);
280 else
281 dontmove.remove(v);
282 }
283
284
285
286
287 public void lock(boolean lock) {
288 for(V v : graph.getVertices()) {
289 lock(v, lock);
290 }
291 }
292 }