--- /dev/null
+/*
+* 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.layout;
+
+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 org.apache.commons.collections15.Factory;
+import org.apache.commons.collections15.map.LazyMap;
+
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implements a self-organizing map layout algorithm, based on Meyer's
+ * self-organizing graph methods.
+ *
+ * @author Yan Biao Boey
+ */
+public class ISOMLayout<V, E> extends AbstractLayout<V,E> implements IterativeContext {
+
+ Map<V, ISOMVertexData> isomVertexData =
+ LazyMap.decorate(new HashMap<V, ISOMVertexData>(),
+ new Factory<ISOMVertexData>() {
+ public ISOMVertexData create() {
+ return new ISOMVertexData();
+ }});
+
+ private int maxEpoch;
+ private int epoch;
+
+ private int radiusConstantTime;
+ private int radius;
+ private int minRadius;
+
+ private double adaption;
+ private double initialAdaption;
+ private double minAdaption;
+
+ protected GraphElementAccessor<V,E> elementAccessor =
+ new RadiusGraphElementAccessor<V,E>();
+
+ private double coolingFactor;
+
+ private List<V> queue = new ArrayList<V>();
+ private String status = null;
+
+ /**
+ * Returns the current number of epochs and execution status, as a string.
+ */
+ public String getStatus() {
+ return status;
+ }
+
+ /**
+ * Creates an <code>ISOMLayout</code> instance for the specified graph <code>g</code>.
+ * @param g
+ */
+ public ISOMLayout(Graph<V,E> g) {
+ super(g);
+ }
+
+ public void initialize() {
+
+ setInitializer(new RandomLocationTransformer<V>(getSize()));
+ maxEpoch = 2000;
+ epoch = 1;
+
+ radiusConstantTime = 100;
+ radius = 5;
+ minRadius = 1;
+
+ initialAdaption = 90.0D / 100.0D;
+ adaption = initialAdaption;
+ minAdaption = 0;
+
+ //factor = 0; //Will be set later on
+ coolingFactor = 2;
+
+ //temperature = 0.03;
+ //initialJumpRadius = 100;
+ //jumpRadius = initialJumpRadius;
+
+ //delay = 100;
+ }
+
+
+ /**
+ * Advances the current positions of the graph elements.
+ */
+ public void step() {
+ status = "epoch: " + epoch + "; ";
+ if (epoch < maxEpoch) {
+ adjust();
+ updateParameters();
+ status += " status: running";
+
+ } else {
+ status += "adaption: " + adaption + "; ";
+ status += "status: done";
+// done = true;
+ }
+ }
+
+ private synchronized void adjust() {
+ //Generate random position in graph space
+ Point2D tempXYD = new Point2D.Double();
+
+ // creates a new XY data location
+ tempXYD.setLocation(10 + Math.random() * getSize().getWidth(),
+ 10 + Math.random() * getSize().getHeight());
+
+ //Get closest vertex to random position
+ V winner = elementAccessor.getVertex(this, tempXYD.getX(), tempXYD.getY());
+
+ while(true) {
+ try {
+ for(V v : getGraph().getVertices()) {
+ ISOMVertexData ivd = getISOMVertexData(v);
+ ivd.distance = 0;
+ ivd.visited = false;
+ }
+ break;
+ } catch(ConcurrentModificationException cme) {}
+ }
+ adjustVertex(winner, tempXYD);
+ }
+
+ private synchronized void updateParameters() {
+ epoch++;
+ double factor = Math.exp(-1 * coolingFactor * (1.0 * epoch / maxEpoch));
+ adaption = Math.max(minAdaption, factor * initialAdaption);
+ //jumpRadius = (int) factor * jumpRadius;
+ //temperature = factor * temperature;
+ if ((radius > minRadius) && (epoch % radiusConstantTime == 0)) {
+ radius--;
+ }
+ }
+
+ private synchronized void adjustVertex(V v, Point2D tempXYD) {
+ queue.clear();
+ ISOMVertexData ivd = getISOMVertexData(v);
+ ivd.distance = 0;
+ ivd.visited = true;
+ queue.add(v);
+ V current;
+
+ while (!queue.isEmpty()) {
+ current = queue.remove(0);
+ ISOMVertexData currData = getISOMVertexData(current);
+ Point2D currXYData = transform(current);
+
+ double dx = tempXYD.getX() - currXYData.getX();
+ double dy = tempXYD.getY() - currXYData.getY();
+ double factor = adaption / Math.pow(2, currData.distance);
+
+ currXYData.setLocation(currXYData.getX()+(factor*dx), currXYData.getY()+(factor*dy));
+
+ if (currData.distance < radius) {
+ Collection<V> s = getGraph().getNeighbors(current);
+ while(true) {
+ try {
+ for(V child : s) {
+ ISOMVertexData childData = getISOMVertexData(child);
+ if (childData != null && !childData.visited) {
+ childData.visited = true;
+ childData.distance = currData.distance + 1;
+ queue.add(child);
+ }
+ }
+ break;
+ } catch(ConcurrentModificationException cme) {}
+ }
+ }
+ }
+ }
+
+ protected ISOMVertexData getISOMVertexData(V v) {
+ return isomVertexData.get(v);
+ }
+
+ /**
+ * This one is an incremental visualization.
+ * @return <code>true</code> is the layout algorithm is incremental, <code>false</code> otherwise
+ */
+ public boolean isIncremental() {
+ return true;
+ }
+
+ /**
+ * Returns <code>true</code> if the vertex positions are no longer being
+ * updated. Currently <code>ISOMLayout</code> stops updating vertex
+ * positions after a certain number of iterations have taken place.
+ * @return <code>true</code> if the vertex position updates have stopped,
+ * <code>false</code> otherwise
+ */
+ public boolean done() {
+ return epoch >= maxEpoch;
+ }
+
+ protected static class ISOMVertexData {
+ int distance;
+ boolean visited;
+
+ protected ISOMVertexData() {
+ distance = 0;
+ visited = false;
+ }
+ }
+
+ /**
+ * Resets the layout iteration count to 0, which allows the layout algorithm to
+ * continue updating vertex positions.
+ */
+ public void reset() {
+ epoch = 0;
+ }
+}
\ No newline at end of file