edu.uci.ics.jung.algorithms.layout3d.FRLayout Maven / Gradle / Ivy
/*
* Copyright (c) 2003, the JUNG Project and the Regents of the University of
* California All rights reserved.
*
* This software is open-source under the BSD license; see either "license.txt"
* or http://jung.sourceforge.net/license.txt for a description.
*/
package edu.uci.ics.jung.algorithms.layout3d;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
import javax.media.j3d.BoundingSphere;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;
import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.map.LazyMap;
import edu.uci.ics.jung.algorithms.util.IterativeContext;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.util.Pair;
/**
* Implements the Fruchterman-Reingold algorithm for node layout.
*
* @author Scott White, Yan-Biao Boey, Danyel Fisher
*/
public class FRLayout extends AbstractLayout implements IterativeContext {
private double forceConstant;
private double temperature;
private int currentIteration;
private int mMaxIterations = 700;
// private Map frVertexData =
// LazyMap.decorate(new HashMap(), new Factory() {
// public FRVertexData create() {
// return new FRVertexData();
// }});
private Map frVertexData =
LazyMap.decorate(new HashMap(), new Factory() {
public Vector3f create() {
return new Vector3f();
}});
private double attraction_multiplier = 0.75;
private double attraction_constant;
private double repulsion_multiplier = 0.75;
private double repulsion_constant;
public FRLayout(Graph g) {
super(g);
}
public FRLayout(Graph g, BoundingSphere d) {
super(g, new RandomLocationTransformer(d), d);
initialize();
}
/* (non-Javadoc)
* @see edu.uci.ics.jung.visualization.layout.AbstractLayout#setSize(java.awt.Dimension)
*/
@Override
public void setSize(BoundingSphere size) {
setInitializer(new RandomLocationTransformer(size));
super.setSize(size);
}
public void setAttractionMultiplier(double attraction) {
this.attraction_multiplier = attraction;
}
public void setRepulsionMultiplier(double repulsion) {
this.repulsion_multiplier = repulsion;
}
public void reset() {
doInit();
}
public void initialize() {
doInit();
}
private void doInit() {
Graph graph = getGraph();
BoundingSphere d = getSize();
if(graph != null) {//&& d != null) {
currentIteration = 0;
temperature = d.getRadius() / 10;
forceConstant =
Math
.sqrt(d.getRadius()
* d.getRadius() * d.getRadius()
/ graph.getVertexCount());
attraction_constant = attraction_multiplier * forceConstant;
repulsion_constant = repulsion_multiplier * forceConstant;
}
}
private double EPSILON = 0.000001D;
/**
* Moves the iteration forward one notch, calculation attraction and
* repulsion between vertices and edges and cooling the temperature.
*/
public synchronized void step() {
currentIteration++;
/**
* Calculate repulsion
*/
while(true) {
try {
for(V v1 : getGraph().getVertices()) {
// if (isLocked(v1)) continue;
calcRepulsion(v1);
}
break;
} catch(ConcurrentModificationException cme) {}
}
/**
* Calculate attraction
*/
while(true) {
try {
for(E e : getGraph().getEdges()) {
calcAttraction(e);
}
break;
} catch(ConcurrentModificationException cme) {}
}
while(true) {
try {
for(V v : getGraph().getVertices()) {
if (isLocked(v)) continue;
calcPositions(v);
}
break;
} catch(ConcurrentModificationException cme) {}
}
cool();
}
public synchronized void calcPositions(V v) {
// FRVertexData fvd = getFRData(v);
Vector3f fvd = frVertexData.get(v);
if(fvd == null) return;
Point3f xyd = transform(v);
double deltaLength = Math.max(EPSILON, fvd.length());
// double deltaLength = Math.max(EPSILON, Math.sqrt(fvd.disp
// .dot(fvd.disp)));
Vector3f newDisp = new Vector3f(fvd);
newDisp.scale((float)(Math.min(deltaLength, temperature)/deltaLength), fvd);
// double newXDisp = fvd.getXDisp() / deltaLength
// * Math.min(deltaLength, temperature);
// if (Double.isNaN(newXDisp)) {
// throw new IllegalArgumentException(
// "Unexpected mathematical result in FRLayout:calcPositions [xdisp]"); }
//
// double newYDisp = fvd.getYDisp() / deltaLength
// * Math.min(deltaLength, temperature);
//
//
// double newZDisp = fvd.getZDisp() / deltaLength
// * Math.min(deltaLength, temperature);
// System.err.println("deltaLength = "+deltaLength);
// System.err.println(v+" was set to "+xyd);
xyd.add(newDisp);
// xyd.set((float)(xyd.getX()+newXDisp),
// (float)(xyd.getY()+newYDisp),
// (float)(xyd.getZ()+newZDisp));
// System.err.println("newXDisp="+newXDisp+",newYDisp="+newYDisp+",newZDisp="+newZDisp);
// System.err.println(v+" set to "+xyd);
double borderWidth = getSize().getRadius() / 50.0;
double min = -getSize().getRadius() + borderWidth;
double max = -min;
double[] min_pos = new double[3];
double[] max_pos = new double[3];
for (int i = 0; i < 3; i++)
{
min_pos[i] = min + Math.random() * borderWidth * 2;
max_pos[i] = max - Math.random() * borderWidth * 2;
}
xyd.set((float)Math.min(Math.max(xyd.getX(), min_pos[0]), max_pos[0]),
(float)Math.min(Math.max(xyd.getY(), min_pos[1]), max_pos[1]),
(float)Math.min(Math.max(xyd.getZ(), min_pos[2]), max_pos[2]));
// double newXPos = xyd.getX();
// if (newXPos < min) {
// newXPos = min + Math.random() * borderWidth * 2.0;
// } else if (newXPos > max) {
// newXPos = max - Math.random()
// * borderWidth * 2.0;
// }
//
// double newYPos = xyd.getY();
// if (newYPos < min) {
// newYPos = min + Math.random() * borderWidth * 2.0;
// } else if (newYPos > max) {
// newYPos = max
// - Math.random() * borderWidth * 2.0;
// }
//
// double newZPos = xyd.getZ();
// if (newZPos < min) {
// newZPos = min + Math.random() * borderWidth * 2.0;
// } else if (newZPos > max) {
// newZPos = max
// - Math.random() * borderWidth * 2.0;
// }
//
// xyd.set((float)newXPos, (float)newYPos, (float)newZPos);
}
public void calcAttraction(E e) {
Pair p = getGraph().getEndpoints(e);
V v1 = p.getFirst();
V v2 = p.getSecond();
// V v1 = getGraph().getIncidentVertices(e).iterator().next();
// V v2 = getGraph().getOpposite(v1, e);
Point3f p1 = transform(v1);
Point3f p2 = transform(v2);
if(p1 == null || p2 == null) return;
// double xDelta = p1.getX() - p2.getX();
// double yDelta = p1.getY() - p2.getY();
// double zDelta = p1.getZ() - p2.getZ();
Vector3f delta = new Vector3f();
delta.negate(p2);
delta.add(p1);
double deltaLength = Math.max(EPSILON, delta.length());
// double deltaLength = Math.max(EPSILON, Math.sqrt((xDelta * xDelta)
// + (yDelta * yDelta)));
double force = (deltaLength * deltaLength) / attraction_constant;
if (Double.isNaN(force)) { throw new IllegalArgumentException(
"Unexpected mathematical result in FRLayout:calcPositions [force]"); }
delta.scale((float)(force / deltaLength));
frVertexData.get(v2).add(delta);
delta.negate();
frVertexData.get(v1).add(delta);
// FRVertexData fvd1 = getFRData(v1);
// FRVertexData fvd2 = getFRData(v2);
//
// fvd1.decrementDisp(
// (float)((xDelta / deltaLength) * force),
// (float)((yDelta / deltaLength) * force),
// (float)((zDelta / deltaLength) * force));
// fvd2.incrementDisp(
// (float)((xDelta / deltaLength) * force),
// (float)((yDelta / deltaLength) * force),
// (float)((zDelta / deltaLength) * force));
}
public void calcRepulsion(V v1) {
Vector3f fvd1 = frVertexData.get(v1);
// FRVertexData fvd1 = getFRData(v1);
if(fvd1 == null) return;
fvd1.set(0, 0, 0);
// fvd1.setDisp(0, 0, 0);
try {
for(V v2 : getGraph().getVertices()) {
// if (isLocked(v2)) continue;
if (v1 != v2) {
Point3f p1 = transform(v1);
Point3f p2 = transform(v2);
if(p1 == null || p2 == null) continue;
// double xDelta = p1.getX() - p2.getX();
// double yDelta = p1.getY() - p2.getY();
// double zDelta = p1.getZ() - p2.getZ();
Vector3f delta = new Vector3f();
delta.negate(p2);
delta.add(p1);
double deltaLength = Math.max(EPSILON, delta.length());
// double deltaLength = Math.max(EPSILON, Math
// .sqrt((xDelta * xDelta) + (yDelta * yDelta) + (zDelta * zDelta)));
double force = (repulsion_constant * repulsion_constant) / deltaLength;
if (Double.isNaN(force)) { throw new RuntimeException(
"Unexpected mathematical result in FRLayout:calcPositions [repulsion]"); }
delta.scale((float)(force / deltaLength));
fvd1.add(delta);
// fvd1.incrementDisp(
// (float)((xDelta / deltaLength) * force),
// (float)((yDelta / deltaLength) * force),
// (float)((zDelta / deltaLength) * force));
}
}
} catch(ConcurrentModificationException cme) {
calcRepulsion(v1);
}
}
private void cool() {
temperature *= (1.0 - currentIteration / (double) mMaxIterations);
}
public void setMaxIterations(int maxIterations) {
mMaxIterations = maxIterations;
}
// public FRVertexData getFRData(V v) {
// return frVertexData.get(v);
// }
/**
* This one is an incremental visualization.
*/
public boolean isIncremental() {
return true;
}
/**
* Returns true once the current iteration has passed the maximum count,
* MAX_ITERATIONS.
*/
public boolean done() {
if (currentIteration > mMaxIterations) {
return true;
}
return false;
}
// public static class FRVertexData {
//
// private Vector3f disp;
//
// public FRVertexData() {
// initialize();
// }
//
// public void initialize() {
// disp = new Vector3f();
// }
//
// public double getXDisp() {
// return disp.getX();
// }
//
// public double getYDisp() {
// return disp.getY();
// }
//
// public double getZDisp() {
// return disp.getZ();
// }
//
// public void setDisp(float x, float y, float z) {
// disp.set(x,y,z);
// }
//
// public void incrementDisp(float x, float y, float z) {
// disp.add(new Vector3f(x,y,z));
// }
//
// public void decrementDisp(float x, float y, float z) {
// disp.sub(new Vector3f(x,y,x));
// }
// }
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy