All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.uci.ics.jung.algorithms.layout.FRLayout Maven / Gradle / Ivy

There is a newer version: 2.1.1
Show newest version
/*
 * Copyright (c) 2003, The JUNG Authors
 * All rights reserved.
 *
 * This software is open-source under the BSD license; see either "license.txt"
 * or https://github.com/jrtom/jung/blob/master/LICENSE for a description.
 */
package edu.uci.ics.jung.algorithms.layout;

import java.awt.Dimension;
import java.awt.geom.Point2D;
import java.util.ConcurrentModificationException;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import edu.uci.ics.jung.algorithms.layout.util.RandomLocationTransformer;
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 force-directed algorithm for node layout.
 * 
 * 

Behavior is determined by the following settable parameters: *

    *
  • attraction multiplier: how much edges try to keep their vertices together *
  • repulsion multiplier: how much vertices try to push each other apart *
  • maximum iterations: how many iterations this algorithm will use before stopping *
* Each of the first two defaults to 0.75; the maximum number of iterations defaults to 700. * * @see "Fruchterman and Reingold, 'Graph Drawing by Force-directed Placement'" * @see "http://i11www.ilkd.uni-karlsruhe.de/teaching/SS_04/visualisierung/papers/fruchterman91graph.pdf" * @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; protected LoadingCache frVertexData = CacheBuilder.newBuilder().build(new CacheLoader() { public FRVertexData load(V vertex) { return new FRVertexData(); } }); private double attraction_multiplier = 0.75; private double attraction_constant; private double repulsion_multiplier = 0.75; private double repulsion_constant; private double max_dimension; public FRLayout(Graph g) { super(g); } public FRLayout(Graph g, Dimension d) { super(g, new RandomLocationTransformer(d), d); initialize(); max_dimension = Math.max(d.height, d.width); } @Override public void setSize(Dimension size) { if(initialized == false) { setInitializer(new RandomLocationTransformer(size)); } super.setSize(size); max_dimension = Math.max(size.height, size.width); } 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(); Dimension d = getSize(); if(graph != null && d != null) { currentIteration = 0; temperature = d.getWidth() / 10; forceConstant = Math .sqrt(d.getHeight() * d.getWidth() / 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()) { 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(); } protected synchronized void calcPositions(V v) { FRVertexData fvd = getFRData(v); if(fvd == null) return; Point2D xyd = apply(v); double deltaLength = Math.max(EPSILON, fvd.norm()); double newXDisp = fvd.getX() / deltaLength * Math.min(deltaLength, temperature); if (Double.isNaN(newXDisp)) { throw new IllegalArgumentException( "Unexpected mathematical result in FRLayout:calcPositions [xdisp]"); } double newYDisp = fvd.getY() / deltaLength * Math.min(deltaLength, temperature); xyd.setLocation(xyd.getX()+newXDisp, xyd.getY()+newYDisp); double borderWidth = getSize().getWidth() / 50.0; double newXPos = xyd.getX(); if (newXPos < borderWidth) { newXPos = borderWidth + Math.random() * borderWidth * 2.0; } else if (newXPos > (getSize().getWidth() - borderWidth)) { newXPos = getSize().getWidth() - borderWidth - Math.random() * borderWidth * 2.0; } double newYPos = xyd.getY(); if (newYPos < borderWidth) { newYPos = borderWidth + Math.random() * borderWidth * 2.0; } else if (newYPos > (getSize().getHeight() - borderWidth)) { newYPos = getSize().getHeight() - borderWidth - Math.random() * borderWidth * 2.0; } xyd.setLocation(newXPos, newYPos); } protected void calcAttraction(E e) { Pair endpoints = getGraph().getEndpoints(e); V v1 = endpoints.getFirst(); V v2 = endpoints.getSecond(); boolean v1_locked = isLocked(v1); boolean v2_locked = isLocked(v2); if(v1_locked && v2_locked) { // both locked, do nothing return; } Point2D p1 = apply(v1); Point2D p2 = apply(v2); if(p1 == null || p2 == null) return; double xDelta = p1.getX() - p2.getX(); double yDelta = p1.getY() - p2.getY(); 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]"); } double dx = (xDelta / deltaLength) * force; double dy = (yDelta / deltaLength) * force; if(v1_locked == false) { FRVertexData fvd1 = getFRData(v1); fvd1.offset(-dx, -dy); } if(v2_locked == false) { FRVertexData fvd2 = getFRData(v2); fvd2.offset(dx, dy); } } protected void calcRepulsion(V v1) { FRVertexData fvd1 = getFRData(v1); if(fvd1 == null) return; fvd1.setLocation(0, 0); try { for(V v2 : getGraph().getVertices()) { // if (isLocked(v2)) continue; if (v1 != v2) { Point2D p1 = apply(v1); Point2D p2 = apply(v2); if(p1 == null || p2 == null) continue; double xDelta = p1.getX() - p2.getX(); double yDelta = p1.getY() - p2.getY(); double deltaLength = Math.max(EPSILON, Math .sqrt((xDelta * xDelta) + (yDelta * yDelta))); double force = (repulsion_constant * repulsion_constant) / deltaLength; if (Double.isNaN(force)) { throw new RuntimeException( "Unexpected mathematical result in FRLayout:calcPositions [repulsion]"); } fvd1.offset((xDelta / deltaLength) * force, (yDelta / deltaLength) * force); } } } catch(ConcurrentModificationException cme) { calcRepulsion(v1); } } private void cool() { temperature *= (1.0 - currentIteration / (double) mMaxIterations); } public void setMaxIterations(int maxIterations) { mMaxIterations = maxIterations; } protected FRVertexData getFRData(V v) { return frVertexData.getUnchecked(v); } /** * @return true */ public boolean isIncremental() { return true; } /** * @return true once the current iteration has passed the maximum count. */ public boolean done() { if (currentIteration > mMaxIterations || temperature < 1.0/max_dimension) { return true; } return false; } @SuppressWarnings("serial") protected static class FRVertexData extends Point2D.Double { protected void offset(double x, double y) { this.x += x; this.y += y; } protected double norm() { return Math.sqrt(x*x + y*y); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy