← Back to team overview

gephi.team team mailing list archive

[Merge] lp:~mathieu-jacomy/gephi/forceatlas2 into lp:gephi

 

Mathieu Bastian has proposed merging lp:~mathieu-jacomy/gephi/forceatlas2 into lp:gephi.

Requested reviews:
  Mathieu Bastian (mathieu.bastian)

For more details, see:
https://code.launchpad.net/~mathieu-jacomy/gephi/forceatlas2/+merge/63504

New awesome ForceAtlas2 algorithm
-- 
https://code.launchpad.net/~mathieu-jacomy/gephi/forceatlas2/+merge/63504
Your team Gephi Team is subscribed to branch lp:gephi.
=== added directory 'ForceAtlas2'
=== added file 'ForceAtlas2/build.xml'
--- ForceAtlas2/build.xml	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/build.xml	2011-06-05 20:50:57 +0000
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
+<!-- for some information on what you could do (e.g. targets to override). -->
+<!-- If you delete this file and reopen the project it will be recreated. -->
+<project name="org.webatlas.forceatlas2" default="netbeans" basedir=".">
+    <description>Builds, tests, and runs the project org.webatlas.forceatlas2.</description>
+    <import file="nbproject/build-impl.xml"/>
+</project>

=== added file 'ForceAtlas2/manifest.mf'
--- ForceAtlas2/manifest.mf	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/manifest.mf	2011-06-05 20:50:57 +0000
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+OpenIDE-Module: org.webatlas.forceatlas2
+OpenIDE-Module-Localizing-Bundle: org/webatlas/forceatlas2/Bundle.properties
+OpenIDE-Module-Specification-Version: 1.6
+

=== added directory 'ForceAtlas2/nbproject'
=== added file 'ForceAtlas2/nbproject/build-impl.xml'
--- ForceAtlas2/nbproject/build-impl.xml	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/nbproject/build-impl.xml	2011-06-05 20:50:57 +0000
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+*** GENERATED FROM project.xml - DO NOT EDIT  ***
+***         EDIT ../build.xml INSTEAD         ***
+-->
+<project name="org.webatlas.forceatlas2-impl" basedir="..">
+    <fail message="Please build using Ant 1.7.1 or higher.">
+        <condition>
+            <not>
+                <antversion atleast="1.7.1"/>
+            </not>
+        </condition>
+    </fail>
+    <property file="nbproject/private/suite-private.properties"/>
+    <property file="nbproject/suite.properties"/>
+    <fail unless="suite.dir">You must set 'suite.dir' to point to your containing module suite</fail>
+    <property file="${suite.dir}/nbproject/private/platform-private.properties"/>
+    <property file="${suite.dir}/nbproject/platform.properties"/>
+    <macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-project/2";>
+        <attribute name="name"/>
+        <attribute name="value"/>
+        <sequential>
+            <property name="@{name}" value="${@{value}}"/>
+        </sequential>
+    </macrodef>
+    <macrodef name="evalprops" uri="http://www.netbeans.org/ns/nb-module-project/2";>
+        <attribute name="property"/>
+        <attribute name="value"/>
+        <sequential>
+            <property name="@{property}" value="@{value}"/>
+        </sequential>
+    </macrodef>
+    <property file="${user.properties.file}"/>
+    <nbmproject2:property name="harness.dir" value="nbplatform.${nbplatform.active}.harness.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <nbmproject2:property name="nbplatform.active.dir" value="nbplatform.${nbplatform.active}.netbeans.dest.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <nbmproject2:evalprops property="cluster.path.evaluated" value="${cluster.path}" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
+    <fail message="Path to 'platform' cluster missing in $${cluster.path} property or using corrupt Netbeans Platform (missing harness).">
+        <condition>
+            <not>
+                <contains string="${cluster.path.evaluated}" substring="platform"/>
+            </not>
+        </condition>
+    </fail>
+    <import file="${harness.dir}/build.xml"/>
+</project>

=== added file 'ForceAtlas2/nbproject/genfiles.properties'
--- ForceAtlas2/nbproject/genfiles.properties	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/nbproject/genfiles.properties	2011-06-05 20:50:57 +0000
@@ -0,0 +1,8 @@
+build.xml.data.CRC32=0a8bc1d8
+build.xml.script.CRC32=7eaa9dd0
+build.xml.stylesheet.CRC32=a56c6a5b@1.45.1
+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
+nbproject/build-impl.xml.data.CRC32=0a8bc1d8
+nbproject/build-impl.xml.script.CRC32=eb5b8ec7
+nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.45.1

=== added file 'ForceAtlas2/nbproject/project.properties'
--- ForceAtlas2/nbproject/project.properties	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/nbproject/project.properties	2011-06-05 20:50:57 +0000
@@ -0,0 +1,2 @@
+javac.source=1.6
+javac.compilerargs=-Xlint -Xlint:-serial

=== added file 'ForceAtlas2/nbproject/project.xml'
--- ForceAtlas2/nbproject/project.xml	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/nbproject/project.xml	2011-06-05 20:50:57 +0000
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1";>
+    <type>org.netbeans.modules.apisupport.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/nb-module-project/3";>
+            <code-name-base>org.webatlas.forceatlas2</code-name-base>
+            <suite-component/>
+            <module-dependencies>
+                <dependency>
+                    <code-name-base>org.gephi.data.attributes.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>0.8</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.gephi.dynamic.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>0.8</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.gephi.graph.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>0.8</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.gephi.layout.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>0.8.0.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.gephi.project.api</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>0.8</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>8.14.1</specification-version>
+                    </run-dependency>
+                </dependency>
+                <dependency>
+                    <code-name-base>org.openide.util.lookup</code-name-base>
+                    <build-prerequisite/>
+                    <compile-dependency/>
+                    <run-dependency>
+                        <specification-version>8.6.1</specification-version>
+                    </run-dependency>
+                </dependency>
+            </module-dependencies>
+            <public-packages/>
+        </data>
+    </configuration>
+</project>

=== added file 'ForceAtlas2/nbproject/suite.properties'
--- ForceAtlas2/nbproject/suite.properties	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/nbproject/suite.properties	2011-06-05 20:50:57 +0000
@@ -0,0 +1,1 @@
+suite.dir=${basedir}/..

=== added directory 'ForceAtlas2/src'
=== added directory 'ForceAtlas2/src/org'
=== added directory 'ForceAtlas2/src/org/webatlas'
=== added directory 'ForceAtlas2/src/org/webatlas/forceatlas2'
=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/Bundle.properties'
--- ForceAtlas2/src/org/webatlas/forceatlas2/Bundle.properties	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/src/org/webatlas/forceatlas2/Bundle.properties	2011-06-05 20:50:57 +0000
@@ -0,0 +1,33 @@
+OpenIDE-Module-Display-Category=Plugin
+OpenIDE-Module-Long-Description=Quality Layout, used in SNA. \
+    A linear-attraction linear-repulsion model with few approximations (BarnesHut). \
+    Features an optimal speed vs. precision balance and degree-driven repulsion for a high readability. \
+    Works on medium-sized graphs: 10 to 10000 nodes.
+OpenIDE-Module-Name=ForceAtlas 2, Quality Layout
+OpenIDE-Module-Short-Description=Quality Layout
+
+ForceAtlas2.name=ForceAtlas 2
+ForceAtlas2.description=Quality layout: a linear-attraction linear-repulsion model with few approximations (BarnesHut). Speed automatically computed.
+
+ForceAtlas2.tuning=Tuning
+ForceAtlas2.behavior=Behavior Alternatives
+ForceAtlas2.performance=Performance
+
+ForceAtlas2.scalingRatio.name=Scaling
+ForceAtlas2.scalingRatio.desc=How much repulsion you want. More makes a more sparse graph.
+ForceAtlas2.gravity.name=Gravity
+ForceAtlas2.gravity.desc=Attracts nodes to the center. Prevents islands from drifting away.
+ForceAtlas2.distributedAttraction.name=Dissuade Hubs
+ForceAtlas2.distributedAttraction.desc=Distributes attraction along outbound edges. Hubs attract less and thus are pushed to the borders.
+ForceAtlas2.linLogMode.name=LinLog mode
+ForceAtlas2.linLogMode.desc=Switch ForceAtlas' model from lin-lin to lin-log (tribute to Andreas Noack). Makes clusters more tight.
+ForceAtlas2.adjustSizes.name=Prevent Overlap
+ForceAtlas2.adjustSizes.desc=Use only when spatialized. Should not be used with "Approximate Repulsion"
+ForceAtlas2.jitterTolerance.name=Tolerance (speed)
+ForceAtlas2.jitterTolerance.desc=How much swinging you allow. Above 1 discouraged. Lower gives less speed and more precision.
+ForceAtlas2.barnesHutOptimization.name=Approximate Repulsion
+ForceAtlas2.barnesHutOptimization.desc=Barnes Hut optimization: n\u00b2 complexity to n.ln(n) ; allows larger graphs.
+ForceAtlas2.barnesHutTheta.name=Approximation
+ForceAtlas2.barnesHutTheta.desc=Theta of the Barnes Hut optimization.
+ForceAtlas2.edgeWeightInfluence.name=Edge Weight Influence
+ForceAtlas2.edgeWeightInfluence.desc=How much influence you give to the edges weight. 0 is "no influence" and 1 is "normal".

=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/Bundle_fr.properties'
--- ForceAtlas2/src/org/webatlas/forceatlas2/Bundle_fr.properties	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/src/org/webatlas/forceatlas2/Bundle_fr.properties	2011-06-05 20:50:57 +0000
@@ -0,0 +1,33 @@
+OpenIDE-Module-Display-Category=Plugin
+OpenIDE-Module-Long-Description=Spatialisation qualitative, utilis\u00e9e dans l'analyse des r\u00e9seaux sociaux. \
+    Attraction et r\u00e9pulsion lin\u00e9aires avec quelques approximations (Barnes Hut)\
+    Comprend un \u00e9quilibrage dynamique de la vitesse contre la pr\u00e9cision, et une r\u00e9pulsion par degree qui rend le graphe plus lisible.\
+    Fonctionne sur des graphes de 10 \u00e0 10000 noeuds.
+OpenIDE-Module-Name=ForceAtlas 2, Spatialisation qualitative
+OpenIDE-Module-Short-Description=Spatialisation qualitative
+
+ForceAtlas2.name=ForceAtlas 2
+ForceAtlas2.description=Spatialisation qualitative: mod\u00e8le \u00e0 attraction et r\u00e9pulsion lin\u00e9aires avec quelques optimisations (Barnes Hut). Param\u00e9trage automatis\u00e9.
+
+ForceAtlas2.tuning=R\u00e9glages fins
+ForceAtlas2.behavior=Options de comportement
+ForceAtlas2.performance=Performances
+
+ForceAtlas2.scalingRatio.name=Dimensionnement
+ForceAtlas2.scalingRatio.desc=Quantit\u00e9 de r\u00e9pulsion, par rapport \u00e0 l'attraction. Rend le graphe plus \u00e9tal\u00e9.
+ForceAtlas2.gravity.name=Gravit\u00e9
+ForceAtlas2.gravity.desc=Attire les noeuds vers le centre. Emp\u00eache les \u00eelots de d\u00e9river \u00e0 l'infini.
+ForceAtlas2.distributedAttraction.name=Dissuader les Hubs
+ForceAtlas2.distributedAttraction.desc=Distribue l'attraction dans les liens sortants. Les Hubs attirent moins et sont donc repouss\u00e9s en p\u00e9riph\u00e9rie.
+ForceAtlas2.linLogMode.name=Mode LinLog
+ForceAtlas2.linLogMode.desc=Passer le mod\u00e8le de ForceAtlas de lin-lin \u00e0 lin-log (hommage \u00e0 Andreas Noack). Rend les concentrations de noeuds sont plus resserr\u00e9es.
+ForceAtlas2.adjustSizes.name=Emp\u00eacher Recouvrement
+ForceAtlas2.adjustSizes.desc=Utilisez lorsque le graphe a d\u00e9j\u00e0 converg\u00e9. Permet d'emp\u00eacher les noeuds de se recouvrir. Utilisation d\u00e9conseill\u00e9e avec l'approximation de la r\u00e9pulsion.
+ForceAtlas2.jitterTolerance.name=Tol\u00e9rance (vitesse)
+ForceAtlas2.jitterTolerance.desc=Quantit\u00e9 de vibration autoris\u00e9e (valeurs > 1 d\u00e9conseill\u00e9es). Baisser la valeur apporte plus de pr\u00e9cision et moins de vitesse.
+ForceAtlas2.barnesHutOptimization.name=Approximer R\u00e9pulsion
+ForceAtlas2.barnesHutOptimization.desc=L'optimisation de Barnes Hut: permet une r\u00e9duction de la complexit\u00e9 du calcul de n\u00b2 \u00e0 n.ln(n). Permet de spatialiser de plus gros graphes.
+ForceAtlas2.barnesHutTheta.name=Approximation
+ForceAtlas2.barnesHutTheta.desc=param\u00e8tre Theta de l'optimisation de Barnes Hut.
+ForceAtlas2.edgeWeightInfluence.name=Influence Poids des liens
+ForceAtlas2.edgeWeightInfluence.desc=L'influence du poides des liens. 0 c'est aucune influence, 1 c'est une influence normale et au-del\u00e0 \u00e7a accentue l'importance du poids des liens dans la spatialisation.

=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2.java'
--- ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2.java	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2.java	2011-06-05 20:50:57 +0000
@@ -0,0 +1,450 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy <Mathieu.Jacomy@xxxxxxxxx>
+Website : http://www.webatlas.fr
+
+You should have received a copy of the GNU Affero General Public License
+along with ForceAtlas 2.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package org.webatlas.forceatlas2;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.gephi.data.attributes.type.TimeInterval;
+import org.gephi.dynamic.DynamicUtilities;
+import org.gephi.dynamic.api.DynamicController;
+import org.gephi.dynamic.api.DynamicModel;
+import org.gephi.graph.api.Edge;
+import org.gephi.graph.api.GraphModel;
+import org.gephi.graph.api.HierarchicalGraph;
+import org.gephi.graph.api.Node;
+import org.gephi.graph.api.NodeData;
+import org.gephi.layout.spi.Layout;
+import org.gephi.layout.spi.LayoutBuilder;
+import org.gephi.layout.spi.LayoutProperty;
+import org.gephi.project.api.Workspace;
+import org.openide.util.Lookup;
+import org.openide.util.NbBundle;
+import org.webatlas.forceatlas2.ForceFactory.AttractionForce;
+import org.webatlas.forceatlas2.ForceFactory.RepulsionForce;
+
+/**
+ * ForceAtlas 2 Layout, manages each step of the computations.
+ * @author Mathieu Jacomy
+ */
+public class ForceAtlas2 implements Layout{
+    private GraphModel graphModel;
+    private HierarchicalGraph graph;
+    private ForceAtlas2Builder layoutBuilder;
+    private DynamicModel dynamicModel;
+
+    private double edgeWeightInfluence;
+    private double jitterTolerance;
+    private double scalingRatio;
+    private double gravity;
+    private double speed;
+    private boolean outboundAttractionDistribution;
+    private boolean adjustSizes;
+    private boolean barnesHutOptimize;
+    private double barnesHutTheta;
+    private boolean linLogMode;
+    private Region rootRegion;
+
+    double outboundAttCompensation = 1;
+
+    //Dynamic Weight
+    private TimeInterval timeInterval;
+
+    public ForceAtlas2(ForceAtlas2Builder layoutBuilder) {
+        this.layoutBuilder = layoutBuilder;
+    }
+
+    @Override
+    public void initAlgo() {
+        speed = 1.;
+
+        graph = graphModel.getHierarchicalGraphVisible();
+        this.timeInterval = DynamicUtilities.getVisibleInterval(dynamicModel);
+
+        graph.readLock();
+        Node[] nodes = graph.getNodes().toArray();
+
+        // Initialise layout data
+        for (Node n : nodes) {
+            if (n.getNodeData().getLayoutData() == null || !(n.getNodeData().getLayoutData() instanceof ForceAtlas2LayoutData)) {
+                ForceAtlas2LayoutData nLayout = new ForceAtlas2LayoutData();
+                n.getNodeData().setLayoutData(nLayout);
+            }
+            NodeData nData = n.getNodeData();
+            ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+            nLayout.mass = 1 + graph.getDegree(n);
+            nLayout.old_dx = 0;
+            nLayout.old_dy = 0;
+            nLayout.dx = 0;
+            nLayout.dy = 0;
+        }
+    }
+
+    @Override
+    public void goAlgo() {
+        // Initialize graph data
+        if (graphModel == null) {
+            return;
+        }
+        graph = graphModel.getHierarchicalGraphVisible();
+        this.timeInterval = DynamicUtilities.getVisibleInterval(dynamicModel);
+        
+        graph.readLock();
+        Node[] nodes = graph.getNodes().toArray();
+        Edge[] edges = graph.getEdgesAndMetaEdges().toArray();
+
+        // Initialise layout data
+        for (Node n : nodes) {
+            if (n.getNodeData().getLayoutData() == null || !(n.getNodeData().getLayoutData() instanceof ForceAtlas2LayoutData)) {
+                ForceAtlas2LayoutData nLayout = new ForceAtlas2LayoutData();
+                n.getNodeData().setLayoutData(nLayout);
+            }
+            NodeData nData = n.getNodeData();
+            ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+            nLayout.mass = 1 + graph.getDegree(n);
+            nLayout.old_dx = nLayout.dx;
+            nLayout.old_dy = nLayout.dy;
+            nLayout.dx = 0;
+            nLayout.dy = 0;
+        }
+        
+        // If Barnes Hut active, initialize root region
+        if(isBarnesHutOptimize()){
+            rootRegion = new Region(nodes);
+            rootRegion.buildSubRegions();
+        }
+
+        // If outboundAttractionDistribution active, compensate.
+        if(isOutboundAttractionDistribution()){
+            outboundAttCompensation = 0;
+            for(Node n : nodes){
+                NodeData nData = n.getNodeData();
+                ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+                outboundAttCompensation += nLayout.mass;
+            }
+            outboundAttCompensation /= nodes.length;
+        }
+
+        // Repulsion
+        RepulsionForce Repulsion = ForceFactory.builder.buildRepulsion(isAdjustSizes(), getScalingRatio());
+        if(isBarnesHutOptimize()){
+            for(Node n : nodes){
+                rootRegion.applyForce(n, Repulsion, getBarnesHutTheta());
+            }
+        } else {
+            for (int n1Index = 0; n1Index<nodes.length; n1Index++) {
+                Node n1 = nodes[n1Index];
+                for (int n2Index = 0; n2Index<n1Index; n2Index++) {
+                    Node n2 = nodes[n2Index];
+                    Repulsion.apply(n1, n2);
+                }
+            }
+        }
+
+        // Attraction
+        AttractionForce Attraction = ForceFactory.builder.buildAttraction(isLinLogMode(), isOutboundAttractionDistribution(), isAdjustSizes(), 1*((isOutboundAttractionDistribution())?(outboundAttCompensation):(1)));
+        for (Edge e : edges) {
+            Attraction.apply(e.getSource(), e.getTarget(), Math.pow(getWeight(e),getEdgeWeightInfluence()));
+        }
+        
+        // Gravity
+        for (Node n : nodes) {
+            Repulsion.apply(n, getGravity()/getScalingRatio());
+        }
+
+        // Auto adjust speed
+        double totalSwinging = 0d;  // How much irregular movement
+        double totalEffectiveTraction = 0d;  // Hom much useful movement
+        for (Node n : nodes) {
+            NodeData nData = n.getNodeData();
+            ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+            if (!nData.isFixed()) {
+                double swinging = Math.sqrt(Math.pow(nLayout.old_dx - nLayout.dx,2) + Math.pow(nLayout.old_dy - nLayout.dy,2));
+                totalSwinging += nLayout.mass * swinging;   // If the node has a burst change of direction, then it's not converging.
+                totalEffectiveTraction += nLayout.mass * 0.5 * Math.sqrt(Math.pow(nLayout.old_dx + nLayout.dx,2) + Math.pow(nLayout.old_dy + nLayout.dy,2));
+            }
+        }
+        // We want that swingingMovement < tolerance * convergenceMovement
+        double targetSpeed = getJitterTolerance() * getJitterTolerance() * totalEffectiveTraction / totalSwinging;
+
+        // But the speed shoudn't rise too much too quickly, since it would make the convergence drop dramatically.
+        double maxRise = 0.5;   // Max rise: 50%
+        speed = speed + Math.min(targetSpeed - speed, maxRise * speed);
+
+        // Apply forces
+        if(isAdjustSizes()){
+            // If nodes overlap prevention is active, it's not possible to trust the swinging mesure.
+            for (Node n : nodes) {
+                NodeData nData = n.getNodeData();
+                ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+                if (!nData.isFixed()) {
+
+                    // Adaptive auto-speed: the speed of each node is lowered
+                    // when the node swings.
+                    double swinging = Math.sqrt((nLayout.old_dx - nLayout.dx) * (nLayout.old_dx - nLayout.dx) + (nLayout.old_dy - nLayout.dy) * (nLayout.old_dy - nLayout.dy));
+                    double factor = 0.1 * speed / (1f + speed * Math.sqrt(swinging));
+
+                    double df = Math.sqrt(Math.pow(nLayout.dx, 2)+Math.pow(nLayout.dy, 2));
+                    factor = Math.min(factor*df, 10.)/df;
+
+                    double x = nData.x() + nLayout.dx*factor;
+                    double y = nData.y() + nLayout.dy*factor;
+
+                    nData.setX((float) x);
+                    nData.setY((float) y);
+                }
+            }
+        } else {
+            for (Node n : nodes) {
+                NodeData nData = n.getNodeData();
+                ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+                if (!nData.isFixed()) {
+                    
+                    // Adaptive auto-speed: the speed of each node is lowered
+                    // when the node swings.
+                    double swinging = Math.sqrt((nLayout.old_dx - nLayout.dx) * (nLayout.old_dx - nLayout.dx) + (nLayout.old_dy - nLayout.dy) * (nLayout.old_dy - nLayout.dy));
+                    //double factor = speed / (1f + Math.sqrt(speed * swinging));
+                    double factor = speed / (1f + speed * Math.sqrt(swinging));
+
+                    double x = nData.x() + nLayout.dx*factor;
+                    double y = nData.y() + nLayout.dy*factor;
+
+                    nData.setX((float) x);
+                    nData.setY((float) y);
+                }
+            }
+        }
+        graph.readUnlock();
+    }
+
+    @Override
+    public boolean canAlgo() {
+        return graphModel != null;
+    }
+
+    @Override
+    public void endAlgo() {
+        for (Node n : graph.getNodes()) {
+            n.getNodeData().setLayoutData(null);
+        }
+        graph.readUnlock();
+    }
+
+    @Override
+    public LayoutProperty[] getProperties() {
+        List<LayoutProperty> properties = new ArrayList<LayoutProperty>();
+        final String FORCEATLAS2_TUNING = NbBundle.getMessage(getClass(), "ForceAtlas2.tuning");
+        final String FORCEATLAS2_BEHAVIOR = NbBundle.getMessage(getClass(), "ForceAtlas2.behavior");
+        final String FORCEATLAS2_PERFORMANCE = NbBundle.getMessage(getClass(), "ForceAtlas2.performance");
+
+        try {
+            properties.add(LayoutProperty.createProperty(
+                    this, Double.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.scalingRatio.name"),
+                    FORCEATLAS2_TUNING,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.scalingRatio.desc"),
+                    "getScalingRatio", "setScalingRatio"));
+
+            properties.add(LayoutProperty.createProperty(
+                    this, Double.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.gravity.name"),
+                    FORCEATLAS2_TUNING,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.gravity.desc"),
+                    "getGravity", "setGravity"));
+
+            properties.add(LayoutProperty.createProperty(
+                    this, Boolean.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.distributedAttraction.name"),
+                    FORCEATLAS2_BEHAVIOR,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.distributedAttraction.desc"),
+                    "isOutboundAttractionDistribution", "setOutboundAttractionDistribution"));
+
+            properties.add(LayoutProperty.createProperty(
+                    this, Boolean.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.linLogMode.name"),
+                    FORCEATLAS2_BEHAVIOR,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.linLogMode.desc"),
+                    "isLinLogMode", "setLinLogMode"));
+
+            properties.add(LayoutProperty.createProperty(
+                    this, Boolean.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.adjustSizes.name"),
+                    FORCEATLAS2_BEHAVIOR,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.adjustSizes.desc"),
+                    "isAdjustSizes", "setAdjustSizes"));
+
+            properties.add(LayoutProperty.createProperty(
+                    this, Double.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.edgeWeightInfluence.name"),
+                    FORCEATLAS2_BEHAVIOR,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.edgeWeightInfluence.desc"),
+                    "getEdgeWeightInfluence", "setEdgeWeightInfluence"));
+
+            properties.add(LayoutProperty.createProperty(
+                    this, Double.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.jitterTolerance.name"),
+                    FORCEATLAS2_PERFORMANCE,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.jitterTolerance.desc"),
+                    "getJitterTolerance", "setJitterTolerance"));
+
+            properties.add(LayoutProperty.createProperty(
+                    this, Boolean.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutOptimization.name"),
+                    FORCEATLAS2_PERFORMANCE,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutOptimization.desc"),
+                    "isBarnesHutOptimize", "setBarnesHutOptimize"));
+
+            properties.add(LayoutProperty.createProperty(
+                    this, Double.class,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutTheta.name"),
+                    FORCEATLAS2_PERFORMANCE,
+                    NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutTheta.desc"),
+                    "getBarnesHutTheta", "setBarnesHutTheta"));
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return properties.toArray(new LayoutProperty[0]);
+    }
+
+    @Override
+    public void resetPropertiesValues() {
+        int nodesCount = 0;
+
+        if(graphModel != null){
+            nodesCount = graphModel.getHierarchicalGraphVisible().getNodeCount();
+        }
+        
+        // Tuning
+        if(nodesCount>=100){
+            setScalingRatio(2.0);
+        } else {
+            setScalingRatio(10.0);
+        }
+        setGravity(1.);
+
+        // Behavior
+        setOutboundAttractionDistribution(false);
+        setLinLogMode(false);
+        setAdjustSizes(false);
+        setEdgeWeightInfluence(1.);
+
+        // Performance
+        if(nodesCount>=50000){
+            setJitterTolerance(10d);
+        } else if(nodesCount>=5000){
+            setJitterTolerance(1d);
+        } else {
+            setJitterTolerance(0.1d);
+        }
+        if(nodesCount>=1000){
+            setBarnesHutOptimize(true);
+        } else {
+            setBarnesHutOptimize(false);
+        }
+        setBarnesHutTheta(1.2);
+    }
+
+    @Override
+    public LayoutBuilder getBuilder() {
+        return layoutBuilder;
+    }
+
+    @Override
+    public void setGraphModel(GraphModel graphModel) {
+        this.graphModel = graphModel;
+        Workspace workspace = graphModel.getWorkspace();
+        DynamicController dynamicController = Lookup.getDefault().lookup(DynamicController.class);
+        if (dynamicController != null && workspace != null) {
+            dynamicModel = dynamicController.getModel(workspace);
+        }
+        // Trick: reset here to take the profile of the graph in account for default values
+        resetPropertiesValues();
+    }
+
+    public Double getBarnesHutTheta() {
+        return barnesHutTheta;
+    }
+
+    public void setBarnesHutTheta(Double barnesHutTheta) {
+        this.barnesHutTheta = barnesHutTheta;
+    }
+
+    public Double getEdgeWeightInfluence() {
+        return edgeWeightInfluence;
+    }
+
+    public void setEdgeWeightInfluence(Double edgeWeightInfluence) {
+        this.edgeWeightInfluence = edgeWeightInfluence;
+    }
+
+    public Double getJitterTolerance() {
+        return jitterTolerance;
+    }
+
+    public void setJitterTolerance(Double jitterTolerance) {
+        this.jitterTolerance = jitterTolerance;
+    }
+
+    public Boolean isLinLogMode() {
+        return linLogMode;
+    }
+
+    public void setLinLogMode(Boolean linLogMode) {
+        this.linLogMode = linLogMode;
+    }
+
+    public Double getScalingRatio() {
+        return scalingRatio;
+    }
+
+    public void setScalingRatio(Double scalingRatio) {
+        this.scalingRatio = scalingRatio;
+    }
+
+    public Double getGravity() {
+        return gravity;
+    }
+
+    public void setGravity(Double gravity) {
+        this.gravity = gravity;
+    }
+
+    public Boolean isOutboundAttractionDistribution() {
+        return outboundAttractionDistribution;
+    }
+
+    public void setOutboundAttractionDistribution(Boolean outboundAttractionDistribution) {
+        this.outboundAttractionDistribution = outboundAttractionDistribution;
+    }
+
+    public Boolean isAdjustSizes() {
+        return adjustSizes;
+    }
+
+    public void setAdjustSizes(Boolean adjustSizes) {
+        this.adjustSizes = adjustSizes;
+    }
+
+    public Boolean isBarnesHutOptimize() {
+        return barnesHutOptimize;
+    }
+
+    public void setBarnesHutOptimize(Boolean barnesHutOptimize) {
+        this.barnesHutOptimize = barnesHutOptimize;
+    }
+    
+    private float getWeight(Edge edge) {
+        if(timeInterval!=null) {
+            return edge.getWeight(timeInterval.getLow(), timeInterval.getHigh());
+        } else {
+            return edge.getWeight();
+        }
+    }
+}

=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2Builder.java'
--- ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2Builder.java	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2Builder.java	2011-06-05 20:50:57 +0000
@@ -0,0 +1,75 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy <Mathieu.Jacomy@xxxxxxxxx>
+Website : http://www.webatlas.fr
+
+You should have received a copy of the GNU Affero General Public License
+along with ForceAtlas 2.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package org.webatlas.forceatlas2;
+
+import javax.swing.Icon;
+import javax.swing.JPanel;
+import org.gephi.layout.spi.Layout;
+import org.gephi.layout.spi.LayoutBuilder;
+import org.gephi.layout.spi.LayoutUI;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Layout Builder
+ * @author Mathieu Jacomy
+ */
+@ServiceProvider(service = LayoutBuilder.class)
+public class ForceAtlas2Builder implements LayoutBuilder{
+
+    private ForceAtlas2UI ui = new ForceAtlas2UI();
+
+    @Override
+    public String getName() {
+        return NbBundle.getMessage(ForceAtlas2.class, "ForceAtlas2.name");
+    }
+
+    @Override
+    public LayoutUI getUI() {
+        return ui;
+    }
+
+    @Override
+    public ForceAtlas2 buildLayout() {
+        ForceAtlas2 layout = new ForceAtlas2(this);
+        return layout;
+    }
+
+
+    private class ForceAtlas2UI implements LayoutUI{
+
+        @Override
+        public String getDescription() {
+            return NbBundle.getMessage(ForceAtlas2.class, "ForceAtlas2.description");
+        }
+
+        @Override
+        public Icon getIcon() {
+            return null;
+        }
+
+        @Override
+        public JPanel getSimplePanel(Layout layout) {
+            return null;
+        }
+
+        @Override
+        public int getQualityRank() {
+            return 4;
+        }
+
+        @Override
+        public int getSpeedRank() {
+            return 4;
+        }
+
+    }
+    
+}
\ No newline at end of file

=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2LayoutData.java'
--- ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2LayoutData.java	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2LayoutData.java	2011-06-05 20:50:57 +0000
@@ -0,0 +1,24 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy <Mathieu.Jacomy@xxxxxxxxx>
+Website : http://www.webatlas.fr
+
+You should have received a copy of the GNU Affero General Public License
+along with ForceAtlas 2.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package org.webatlas.forceatlas2;
+
+import org.gephi.graph.spi.LayoutData;
+
+/**
+ * Data stored in Nodes and used by ForceAtlas2
+ * @author Mathieu Jacomy
+ */
+public class ForceAtlas2LayoutData implements LayoutData{
+    //Data
+    public double dx = 0;
+    public double dy = 0;
+    public double old_dx = 0;
+    public double old_dy = 0;
+    public double mass = 1;
+}

=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/ForceFactory.java'
--- ForceAtlas2/src/org/webatlas/forceatlas2/ForceFactory.java	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/src/org/webatlas/forceatlas2/ForceFactory.java	2011-06-05 20:50:57 +0000
@@ -0,0 +1,510 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy <Mathieu.Jacomy@xxxxxxxxx>
+Website : http://www.webatlas.fr
+
+You should have received a copy of the GNU Affero General Public License
+along with ForceAtlas 2.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package org.webatlas.forceatlas2;
+
+import org.gephi.graph.api.Node;
+import org.gephi.graph.api.NodeData;
+
+/**
+ * Generates the forces on demand, here are all the formulas for attraction and repulsion.
+ * @author Mathieu Jacomy
+ */
+public class ForceFactory {
+    public static ForceFactory builder = new ForceFactory();
+
+    private ForceFactory(){};
+
+    public RepulsionForce buildRepulsion(boolean adjustBySize, double coefficient){
+        if(adjustBySize){
+            return new linRepulsion_antiCollision(coefficient);
+        } else {
+            return new linRepulsion(coefficient);
+        }
+    }
+
+    public AttractionForce buildAttraction(boolean logAttraction, boolean distributedAttraction, boolean adjustBySize, double coefficient){
+         if(adjustBySize){
+            if(logAttraction){
+                 if(distributedAttraction){
+                    return new logAttraction_degreeDistributed_antiCollision(coefficient);
+                } else {
+                    return new logAttraction_antiCollision(coefficient);
+                }
+            } else {
+                if(distributedAttraction){
+                    return new linAttraction_degreeDistributed_antiCollision(coefficient);
+                } else {
+                    return new linAttraction_antiCollision(coefficient);
+                }
+            }
+        } else {
+             if(logAttraction){
+                 if(distributedAttraction){
+                    return new logAttraction_degreeDistributed(coefficient);
+                } else {
+                    return new logAttraction(coefficient);
+                }
+            } else {
+                if(distributedAttraction){
+                    return new linAttraction_massDistributed(coefficient);
+                } else {
+                    return new linAttraction(coefficient);
+                }
+            }
+        }
+    }
+
+    public abstract class AttractionForce {
+        public abstract void apply(Node n1, Node n2, double e); // Model for node-node attraction (e is for edge weight if needed)
+    }
+
+    public abstract class RepulsionForce {
+        public abstract void apply(Node n1, Node n2);           // Model for node-node repulsion
+        public abstract void apply(Node n, Region r);           // Model for Barnes Hut approximation
+        public abstract void apply(Node n, double g);           // Model for gravitation (anti-repulsion)
+    }
+
+    /*
+     * Repulsion force: Linear
+     */
+    private class linRepulsion extends RepulsionForce{
+        private double coefficient;
+
+        public linRepulsion(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+            double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+            if (distance > 0) {
+                // NB: factor = force / distance
+                double factor = coefficient * n1Layout.mass * n2Layout.mass / distance / distance;
+
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+            }
+        }
+
+        @Override
+        public void apply(Node n, Region r) {
+            NodeData nData = n.getNodeData();
+            ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+
+            // Get the distance
+            double xDist = nData.x() - r.getMassCenterX();
+            double yDist = nData.y() - r.getMassCenterY();
+            double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+            if (distance > 0) {
+                // NB: factor = force / distance
+                double factor = coefficient * nLayout.mass * r.getMass() / distance / distance;
+
+                nLayout.dx += xDist * factor;
+                nLayout.dy += yDist * factor;
+            }
+        }
+
+        @Override
+        public void apply(Node n, double g) {
+            NodeData nData = n.getNodeData();
+            ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+
+            // Get the distance
+            double xDist = nData.x();
+            double yDist = nData.y();
+            double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+            if (distance > 0) {
+                // NB: factor = force / distance
+                double factor = coefficient * nLayout.mass * g / distance;
+
+                nLayout.dx -= xDist * factor;
+                nLayout.dy -= yDist * factor;
+            }
+        }
+    }
+
+    /*
+     * Repulsion force: Linear with Anti-collision
+     */
+    private class linRepulsion_antiCollision extends RepulsionForce{
+        private double coefficient;
+
+        public linRepulsion_antiCollision(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+            double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
+
+            if (distance > 0) {
+                // NB: factor = force / distance
+                double factor = coefficient * n1Layout.mass * n2Layout.mass / distance / distance;
+                
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+                
+            } else if(distance < 0){
+                double factor = 100 * coefficient * n1Layout.mass * n2Layout.mass;
+
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+            }
+        }
+
+        @Override
+        public void apply(Node n, Region r) {
+            NodeData nData = n.getNodeData();
+            ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+
+            // Get the distance
+            double xDist = nData.x() - r.getMassCenterX();
+            double yDist = nData.y() - r.getMassCenterY();
+            double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+            if (distance > 0) {
+                // NB: factor = force / distance
+                double factor = coefficient * nLayout.mass * r.getMass() / distance / distance;
+
+                nLayout.dx += xDist * factor;
+                nLayout.dy += yDist * factor;
+            } else if(distance < 0){
+                double factor = -coefficient * nLayout.mass * r.getMass() / distance;
+
+                nLayout.dx += xDist * factor;
+                nLayout.dy += yDist * factor;
+            }
+        }
+
+        @Override
+        public void apply(Node n, double g) {
+            NodeData nData = n.getNodeData();
+            ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+
+            // Get the distance
+            double xDist = nData.x();
+            double yDist = nData.y();
+            double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+            if (distance > 0) {
+                // NB: factor = force / distance
+                double factor = coefficient * nLayout.mass * g / distance;
+
+                nLayout.dx -= xDist * factor;
+                nLayout.dy -= yDist * factor;
+            }
+        }
+    }
+
+    /*
+     * Attraction force: Linear
+     */
+    private class linAttraction extends AttractionForce{
+        private double coefficient;
+
+        public linAttraction(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2, double e) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+
+            // NB: factor = force / distance
+            double factor = -coefficient * e;
+
+            n1Layout.dx += xDist * factor;
+            n1Layout.dy += yDist * factor;
+
+            n2Layout.dx -= xDist * factor;
+            n2Layout.dy -= yDist * factor;
+        }
+    }
+
+    /*
+     * Attraction force: Linear, distributed by mass (typically, degree)
+     */
+    private class linAttraction_massDistributed extends AttractionForce{
+        private double coefficient;
+
+        public linAttraction_massDistributed(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2, double e) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+
+            // NB: factor = force / distance
+            double factor = -coefficient * e / n1Layout.mass;
+
+            n1Layout.dx += xDist * factor;
+            n1Layout.dy += yDist * factor;
+
+            n2Layout.dx -= xDist * factor;
+            n2Layout.dy -= yDist * factor;
+        }
+    }
+
+    /*
+     * Attraction force: Logarithmic
+     */
+    private class logAttraction extends AttractionForce{
+        private double coefficient;
+
+        public logAttraction(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2, double e) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+            double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+            if (distance > 0) {
+
+                // NB: factor = force / distance
+                double factor = -coefficient * e * Math.log(1+distance) / distance;
+
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+            }
+        }
+    }
+
+    /*
+     * Attraction force: Linear, distributed by Degree
+     */
+    private class logAttraction_degreeDistributed extends AttractionForce{
+        private double coefficient;
+
+        public logAttraction_degreeDistributed(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2, double e) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+            double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+            if (distance > 0) {
+
+                // NB: factor = force / distance
+                double factor = -coefficient * e * Math.log(1+distance) / distance / n1Layout.mass;
+
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+            }
+        }
+    }
+    
+    /*
+     * Attraction force: Linear, with Anti-Collision
+     */
+    private class linAttraction_antiCollision extends AttractionForce{
+        private double coefficient;
+
+        public linAttraction_antiCollision(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2, double e) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+            double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
+
+            if(distance>0){
+                // NB: factor = force / distance
+                double factor = -coefficient * e;
+
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+            }
+        }
+    }
+
+    /*
+     * Attraction force: Linear, distributed by Degree, with Anti-Collision
+     */
+    private class linAttraction_degreeDistributed_antiCollision extends AttractionForce{
+        private double coefficient;
+
+        public linAttraction_degreeDistributed_antiCollision(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2, double e) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+            double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
+
+            if(distance>0){
+                // NB: factor = force / distance
+                double factor = -coefficient * e / n1Layout.mass;
+
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+            }
+        }
+    }
+
+    /*
+     * Attraction force: Logarithmic, with Anti-Collision
+     */
+    private class logAttraction_antiCollision extends AttractionForce{
+        private double coefficient;
+
+        public logAttraction_antiCollision(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2, double e) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+            double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
+
+            if(distance>0){
+
+                // NB: factor = force / distance
+                double factor = -coefficient * e * Math.log(1+distance) / distance;
+
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+            }
+        }
+    }
+
+    /*
+     * Attraction force: Linear, distributed by Degree, with Anti-Collision
+     */
+    private class logAttraction_degreeDistributed_antiCollision extends AttractionForce{
+        private double coefficient;
+
+        public logAttraction_degreeDistributed_antiCollision(double c){
+            coefficient = c;
+        }
+
+        @Override
+        public void apply(Node n1, Node n2, double e) {
+            NodeData n1Data = n1.getNodeData();
+            ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
+            NodeData n2Data = n2.getNodeData();
+            ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
+
+            // Get the distance
+            double xDist = n1Data.x() - n2Data.x();
+            double yDist = n1Data.y() - n2Data.y();
+            double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
+
+            if(distance>0){
+
+                // NB: factor = force / distance
+                double factor = -coefficient * e * Math.log(1+distance) / distance / n1Layout.mass;
+
+                n1Layout.dx += xDist * factor;
+                n1Layout.dy += yDist * factor;
+
+                n2Layout.dx -= xDist * factor;
+                n2Layout.dy -= yDist * factor;
+            }
+        }
+    }
+}

=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/Region.java'
--- ForceAtlas2/src/org/webatlas/forceatlas2/Region.java	1970-01-01 00:00:00 +0000
+++ ForceAtlas2/src/org/webatlas/forceatlas2/Region.java	2011-06-05 20:50:57 +0000
@@ -0,0 +1,197 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy <Mathieu.Jacomy@xxxxxxxxx>
+Website : http://www.webatlas.fr
+
+You should have received a copy of the GNU Affero General Public License
+along with ForceAtlas 2.  If not, see <http://www.gnu.org/licenses/>.
+*/
+package org.webatlas.forceatlas2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.gephi.graph.api.Node;
+import org.gephi.graph.api.NodeData;
+import org.webatlas.forceatlas2.ForceFactory.RepulsionForce;
+
+/**
+ * Barnes Hut optimization
+ * @author Mathieu Jacomy
+ */
+public class Region{
+
+    private double mass;
+
+    private double massCenterX;
+    private double massCenterY;
+    private double size;
+
+    private List<Node> nodes;
+    private List<Region> subregions = new ArrayList<Region>();
+
+    public Region(Node[] nodes){
+        this.nodes = new ArrayList<Node>();
+        this.nodes.addAll(Arrays.asList(nodes));
+        updateMassAndGeometry();
+    }
+
+    public Region(ArrayList<Node> nodes){
+        this.nodes = new ArrayList<Node>(nodes);
+        updateMassAndGeometry();
+    }
+
+    public void updateMassAndGeometry(){
+        if(nodes.size()>1){
+            // Compute Mass
+            mass = 0;
+            double massSumX = 0;
+            double massSumY = 0;
+            for(Node n : nodes){
+                NodeData nData = n.getNodeData();
+                ForceAtlas2LayoutData nLayout = nData.getLayoutData();
+                mass += nLayout.mass;
+                massSumX += nData.x() * nLayout.mass;
+                massSumY += nData.y() * nLayout.mass;
+            }
+            massCenterX = massSumX / mass;
+            massCenterY = massSumY / mass;
+
+            // Compute size
+            size = Double.MIN_VALUE;
+            for(Node n : nodes){
+                NodeData nData = n.getNodeData();
+                double distance = Math.sqrt((nData.x()-massCenterX)*(nData.x()-massCenterX) + (nData.y()-massCenterY)*(nData.y()-massCenterY));
+                size = Math.max(size, 2*distance);
+            }
+        }
+    }
+
+    public void buildSubRegions(){
+        if(nodes.size()>1){
+            ArrayList<Node> leftNodes = new ArrayList<Node>();
+            ArrayList<Node> rightNodes = new ArrayList<Node>();
+            for(Node n : nodes){
+                NodeData nData = n.getNodeData();
+                ArrayList<Node> nodesColumn = (nData.x()<massCenterX)?(leftNodes):(rightNodes);
+                nodesColumn.add(n);
+            }
+
+            ArrayList<Node> topleftNodes = new ArrayList<Node>();
+            ArrayList<Node> bottomleftNodes = new ArrayList<Node>();
+            for(Node n : leftNodes){
+                NodeData nData = n.getNodeData();
+                ArrayList<Node> nodesLine = (nData.y()<massCenterY)?(topleftNodes):(bottomleftNodes);
+                nodesLine.add(n);
+            }
+
+            ArrayList<Node> bottomrightNodes = new ArrayList<Node>();
+            ArrayList<Node> toprightNodes = new ArrayList<Node>();
+            for(Node n : rightNodes){
+                NodeData nData = n.getNodeData();
+                ArrayList<Node> nodesLine = (nData.y()<massCenterY)?(toprightNodes):(bottomrightNodes);
+                nodesLine.add(n);
+            }
+
+            if(topleftNodes.size()>0){
+                if(topleftNodes.size()<nodes.size()){
+                    Region subregion = new Region(topleftNodes);
+                    subregions.add(subregion);
+                } else {
+                    for(Node n : topleftNodes){
+                        ArrayList<Node> oneNodeList = new ArrayList<Node>();
+                        oneNodeList.add(n);
+                        Region subregion = new Region(oneNodeList);
+                        subregions.add(subregion);
+                    }
+                }
+            }
+            if(bottomleftNodes.size()>0){
+                if(bottomleftNodes.size()<nodes.size()){
+                    Region subregion = new Region(bottomleftNodes);
+                    subregions.add(subregion);
+                } else {
+                    for(Node n : bottomleftNodes){
+                        ArrayList<Node> oneNodeList = new ArrayList<Node>();
+                        oneNodeList.add(n);
+                        Region subregion = new Region(oneNodeList);
+                        subregions.add(subregion);
+                    }
+                }
+            }
+            if(bottomrightNodes.size()>0){
+                if(bottomrightNodes.size()<nodes.size()){
+                    Region subregion = new Region(bottomrightNodes);
+                    subregions.add(subregion);
+                } else {
+                    for(Node n : bottomrightNodes){
+                        ArrayList<Node> oneNodeList = new ArrayList<Node>();
+                        oneNodeList.add(n);
+                        Region subregion = new Region(oneNodeList);
+                        subregions.add(subregion);
+                    }
+                }
+            }
+            if(toprightNodes.size()>0){
+                if(toprightNodes.size()<nodes.size()){
+                    Region subregion = new Region(toprightNodes);
+                    subregions.add(subregion);
+                } else {
+                    for(Node n : toprightNodes){
+                        ArrayList<Node> oneNodeList = new ArrayList<Node>();
+                        oneNodeList.add(n);
+                        Region subregion = new Region(oneNodeList);
+                        subregions.add(subregion);
+                    }
+                }
+            }
+
+            for(Region subregion : subregions){
+                subregion.buildSubRegions();
+            }
+        }
+    }
+
+    public void applyForce(Node n, RepulsionForce Force, double theta){
+        NodeData nData = n.getNodeData();
+        if(nodes.size() < 2){
+            Node regionNode = nodes.get(0);
+            Force.apply(n, regionNode);
+        } else {
+            double distance = Math.sqrt((nData.x()-massCenterX)*(nData.x()-massCenterX) + (nData.y() - massCenterY)*(nData.y()-massCenterY));
+            if(distance * theta > size){
+                Force.apply(n, this);
+            } else {
+                for (Region subregion : subregions) {
+                    subregion.applyForce(n, Force, theta);
+                }
+            }
+        }
+    }
+
+    public double getMass() {
+        return mass;
+    }
+
+    public void setMass(double mass) {
+        this.mass = mass;
+    }
+
+    public double getMassCenterX() {
+        return massCenterX;
+    }
+
+    public void setMassCenterX(double massCenterX) {
+        this.massCenterX = massCenterX;
+    }
+
+    public double getMassCenterY() {
+        return massCenterY;
+    }
+
+    public void setMassCenterY(double massCenterY) {
+        this.massCenterY = massCenterY;
+    }
+
+    
+}

=== added directory 'ForceAtlas2/test'
=== added directory 'ForceAtlas2/test/unit'
=== added directory 'ForceAtlas2/test/unit/src'

Follow ups