edu.uci.ics.jung.algorithms.layout3d.FRLayout.java Source code

Java tutorial

Introduction

Here is the source code for edu.uci.ics.jung.algorithms.layout3d.FRLayout.java

Source

/*
 * 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<V, E> extends AbstractLayout<V, E> implements IterativeContext {

    private double forceConstant;

    private double temperature;

    private int currentIteration;

    private int mMaxIterations = 700;

    //    private Map<V, FRVertexData> frVertexData = 
    //       LazyMap.decorate(new HashMap<V,FRVertexData>(), new Factory<FRVertexData>() {
    //          public FRVertexData create() {
    //             return new FRVertexData();
    //          }});

    private Map<V, Vector3f> frVertexData = LazyMap.decorate(new HashMap<V, Vector3f>(), new Factory<Vector3f>() {
        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<V, E> g) {
        super(g);
    }

    public FRLayout(Graph<V, E> g, BoundingSphere d) {
        super(g, new RandomLocationTransformer<V>(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<V>(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<V, E> 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<V> 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,
     * <tt>MAX_ITERATIONS</tt>.
     */
    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));
    //        }
    //     }
}