org.bigwiv.blastgraph.gui.graphvisualization.EWLayout.java Source code

Java tutorial

Introduction

Here is the source code for org.bigwiv.blastgraph.gui.graphvisualization.EWLayout.java

Source

/*
 * BlastGraph: a comparative genomics tool
 * Copyright (C) 2013  Yanbo Ye (yeyanbo289@gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package org.bigwiv.blastgraph.gui.graphvisualization;

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

import org.apache.commons.collections15.Factory;
import org.apache.commons.collections15.map.LazyMap;
import org.bigwiv.blastgraph.BlastGraph;
import org.bigwiv.blastgraph.HitVertex;
import org.bigwiv.blastgraph.ValueEdge;

import edu.uci.ics.jung.algorithms.layout.AbstractLayout;
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;

/**
 * Edge weighted FRLayout
 * Based on original FRLayout
 * 
 * @author yeyanbo
 *
 * @param <V>
 * @param <E>
 */
public class EWLayout<V, E> extends AbstractLayout<V, E> implements IterativeContext {

    private double forceConstant;

    private double temperature;

    private int currentIteration;

    private int mMaxIterations = 700;

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

    private double attraction_multiplier = 0.75;

    private double attraction_constant;

    private double repulsion_multiplier = 0.75;

    private double repulsion_constant;

    private double max_dimension;

    private double minEvalue = Double.POSITIVE_INFINITY;

    private double maxEvalue = 0;

    public EWLayout(Graph<V, E> g) {
        super(g);
        findMinMax();
    }

    public EWLayout(Graph<V, E> g, Dimension d) {
        super((Graph<V, E>) g, new RandomLocationTransformer<V>(d), d);
        findMinMax();
        initialize();
        max_dimension = Math.max(d.height, d.width);
    }

    private void findMinMax() {
        BlastGraph<HitVertex, ValueEdge> graph = (BlastGraph<HitVertex, ValueEdge>) getGraph();
        // find min and max evalue
        double evalue;
        for (ValueEdge e : graph.getEdges()) {
            evalue = e.getExpectValue();
            minEvalue = minEvalue > evalue ? evalue : minEvalue;
            maxEvalue = maxEvalue < evalue ? evalue : maxEvalue;
        }
        // solve log zero problem
        minEvalue = (minEvalue == 0) ? 1E-200 : minEvalue;

    }

    @Override
    public void setSize(Dimension size) {
        if (initialized == false) {
            setInitializer(new RandomLocationTransformer<V>(size));
        }
        super.setSize(size);
        max_dimension = Math.max(size.height, size.width);
    }

    /**
     * Sets the attraction multiplier.
     */
    public void setAttractionMultiplier(double attraction) {
        this.attraction_multiplier = attraction;
    }

    /**
     * Sets the repulsion multiplier.
     */
    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();
        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) {
        VertexData fvd = getData(v);
        if (fvd == null)
            return;
        Point2D xyd = transform(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) {
        ValueEdge edge = (ValueEdge) e;

        double evalue = edge.getExpectValue();
        double ew; // edge weight

        if (evalue <= 0) {
            ew = 1;
        } else {
            ew = (Math.log10(maxEvalue) - Math.log10(evalue)) / (Math.log10(maxEvalue) - Math.log10(minEvalue));
        }

        Pair<V> 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 = transform(v1);
        Point2D p2 = transform(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)));

        // force by edge weight
        double force = (deltaLength * deltaLength) * ew / 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) {
            VertexData fvd1 = getData(v1);
            fvd1.offset(-dx, -dy);
        }
        if (v2_locked == false) {
            VertexData fvd2 = getData(v2);
            fvd2.offset(dx, dy);
        }
    }

    protected void calcRepulsion(V v1) {
        VertexData fvd1 = getData(v1);
        if (fvd1 == null)
            return;
        fvd1.setLocation(0, 0);

        try {
            for (V v2 : getGraph().getVertices()) {

                // if (isLocked(v2)) continue;
                if (v1 != v2) {
                    Point2D p1 = transform(v1);
                    Point2D p2 = transform(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);
    }

    /**
     * Sets the maximum number of iterations.
     */
    public void setMaxIterations(int maxIterations) {
        mMaxIterations = maxIterations;
    }

    protected VertexData getData(V v) {
        return vertexData.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 || temperature < 1.0 / max_dimension) {
            return true;
        }
        return false;
    }

    protected static class VertexData 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);
        }
    }
}