1
2
3
4
5
6
7
8 package edu.uci.ics.jung.algorithms.layout3d;
9
10 import java.util.ConcurrentModificationException;
11 import java.util.HashMap;
12 import java.util.Map;
13
14 import javax.media.j3d.BoundingSphere;
15 import javax.vecmath.Point3f;
16 import javax.vecmath.Vector3f;
17
18 import org.apache.commons.collections15.Factory;
19 import org.apache.commons.collections15.map.LazyMap;
20
21 import edu.uci.ics.jung.algorithms.util.IterativeContext;
22 import edu.uci.ics.jung.graph.Graph;
23 import edu.uci.ics.jung.graph.util.Pair;
24
25
26
27
28
29
30 public class FRLayout<V, E> extends AbstractLayout<V, E> implements IterativeContext {
31
32 private double forceConstant;
33
34 private double temperature;
35
36 private int currentIteration;
37
38 private int mMaxIterations = 700;
39
40
41
42
43
44
45
46 private Map<V, Vector3f> frVertexData =
47 LazyMap.decorate(new HashMap<V, Vector3f>(), new Factory<Vector3f>() {
48 public Vector3f create() {
49 return new Vector3f();
50 }});
51
52 private double attraction_multiplier = 0.75;
53
54 private double attraction_constant;
55
56 private double repulsion_multiplier = 0.75;
57
58 private double repulsion_constant;
59
60 public FRLayout(Graph<V, E> g) {
61 super(g);
62 }
63
64 public FRLayout(Graph<V, E> g, BoundingSphere d) {
65 super(g, new RandomLocationTransformer<V>(d), d);
66 initialize();
67 }
68
69
70
71
72 @Override
73 public void setSize(BoundingSphere size) {
74 setInitializer(new RandomLocationTransformer<V>(size));
75 super.setSize(size);
76 }
77
78 public void setAttractionMultiplier(double attraction) {
79 this.attraction_multiplier = attraction;
80 }
81
82 public void setRepulsionMultiplier(double repulsion) {
83 this.repulsion_multiplier = repulsion;
84 }
85
86 public void reset() {
87 doInit();
88 }
89
90 public void initialize() {
91 doInit();
92 }
93
94 private void doInit() {
95 Graph<V,E> graph = getGraph();
96 BoundingSphere d = getSize();
97 if(graph != null) {
98 currentIteration = 0;
99 temperature = d.getRadius() / 10;
100
101 forceConstant =
102 Math
103 .sqrt(d.getRadius()
104 * d.getRadius() * d.getRadius()
105 / graph.getVertexCount());
106
107 attraction_constant = attraction_multiplier * forceConstant;
108 repulsion_constant = repulsion_multiplier * forceConstant;
109 }
110 }
111
112 private double EPSILON = 0.000001D;
113
114
115
116
117
118 public synchronized void step() {
119 currentIteration++;
120
121
122
123
124 while(true) {
125
126 try {
127 for(V v1 : getGraph().getVertices()) {
128
129 calcRepulsion(v1);
130 }
131 break;
132 } catch(ConcurrentModificationException cme) {}
133 }
134
135
136
137
138 while(true) {
139 try {
140 for(E e : getGraph().getEdges()) {
141
142 calcAttraction(e);
143 }
144 break;
145 } catch(ConcurrentModificationException cme) {}
146 }
147
148
149 while(true) {
150 try {
151 for(V v : getGraph().getVertices()) {
152 if (isLocked(v)) continue;
153 calcPositions(v);
154 }
155 break;
156 } catch(ConcurrentModificationException cme) {}
157 }
158 cool();
159 }
160
161 public synchronized void calcPositions(V v) {
162
163 Vector3f fvd = frVertexData.get(v);
164 if(fvd == null) return;
165 Point3f xyd = transform(v);
166
167 double deltaLength = Math.max(EPSILON, fvd.length());
168
169
170
171 Vector3f newDisp = new Vector3f(fvd);
172 newDisp.scale((float)(Math.min(deltaLength, temperature)/deltaLength), fvd);
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189 xyd.add(newDisp);
190
191
192
193
194
195
196
197 double borderWidth = getSize().getRadius() / 50.0;
198 double min = -getSize().getRadius() + borderWidth;
199 double max = -min;
200
201 double[] min_pos = new double[3];
202 double[] max_pos = new double[3];
203 for (int i = 0; i < 3; i++)
204 {
205 min_pos[i] = min + Math.random() * borderWidth * 2;
206 max_pos[i] = max - Math.random() * borderWidth * 2;
207 }
208
209
210 xyd.set((float)Math.min(Math.max(xyd.getX(), min_pos[0]), max_pos[0]),
211 (float)Math.min(Math.max(xyd.getY(), min_pos[1]), max_pos[1]),
212 (float)Math.min(Math.max(xyd.getZ(), min_pos[2]), max_pos[2]));
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239 }
240
241 public void calcAttraction(E e) {
242 Pair<V> p = getGraph().getEndpoints(e);
243 V v1 = p.getFirst();
244 V v2 = p.getSecond();
245
246
247 Point3f p1 = transform(v1);
248 Point3f p2 = transform(v2);
249 if(p1 == null || p2 == null) return;
250
251
252
253
254 Vector3f delta = new Vector3f();
255 delta.negate(p2);
256 delta.add(p1);
257
258 double deltaLength = Math.max(EPSILON, delta.length());
259
260
261
262
263 double force = (deltaLength * deltaLength) / attraction_constant;
264
265 if (Double.isNaN(force)) { throw new IllegalArgumentException(
266 "Unexpected mathematical result in FRLayout:calcPositions [force]"); }
267
268 delta.scale((float)(force / deltaLength));
269
270 frVertexData.get(v2).add(delta);
271 delta.negate();
272 frVertexData.get(v1).add(delta);
273
274
275
276
277
278
279
280
281
282
283
284
285 }
286
287 public void calcRepulsion(V v1) {
288 Vector3f fvd1 = frVertexData.get(v1);
289
290 if(fvd1 == null) return;
291 fvd1.set(0, 0, 0);
292
293
294 try {
295 for(V v2 : getGraph().getVertices()) {
296
297
298 if (v1 != v2) {
299 Point3f p1 = transform(v1);
300 Point3f p2 = transform(v2);
301 if(p1 == null || p2 == null) continue;
302
303
304
305
306
307 Vector3f delta = new Vector3f();
308 delta.negate(p2);
309 delta.add(p1);
310
311 double deltaLength = Math.max(EPSILON, delta.length());
312
313
314
315
316 double force = (repulsion_constant * repulsion_constant) / deltaLength;
317
318 if (Double.isNaN(force)) { throw new RuntimeException(
319 "Unexpected mathematical result in FRLayout:calcPositions [repulsion]"); }
320
321 delta.scale((float)(force / deltaLength));
322 fvd1.add(delta);
323
324
325
326
327
328 }
329 }
330 } catch(ConcurrentModificationException cme) {
331 calcRepulsion(v1);
332 }
333 }
334
335 private void cool() {
336 temperature *= (1.0 - currentIteration / (double) mMaxIterations);
337 }
338
339 public void setMaxIterations(int maxIterations) {
340 mMaxIterations = maxIterations;
341 }
342
343
344
345
346
347
348
349
350 public boolean isIncremental() {
351 return true;
352 }
353
354
355
356
357
358 public boolean done() {
359 if (currentIteration > mMaxIterations) {
360 return true;
361 }
362 return false;
363 }
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401 }