Merge lp:~mathieu-jacomy/gephi/forceatlas2 into lp:~gephi.team/gephi/0.8

Proposed by Mathieu Bastian
Status: Merged
Merged at revision: 2240
Proposed branch: lp:~mathieu-jacomy/gephi/forceatlas2
Merge into: lp:~gephi.team/gephi/0.8
Diff against target: 1540 lines (+1460/-0)
14 files modified
ForceAtlas2/build.xml (+8/-0)
ForceAtlas2/manifest.mf (+5/-0)
ForceAtlas2/nbproject/build-impl.xml (+45/-0)
ForceAtlas2/nbproject/genfiles.properties (+8/-0)
ForceAtlas2/nbproject/project.properties (+2/-0)
ForceAtlas2/nbproject/project.xml (+69/-0)
ForceAtlas2/nbproject/suite.properties (+1/-0)
ForceAtlas2/src/org/webatlas/forceatlas2/Bundle.properties (+33/-0)
ForceAtlas2/src/org/webatlas/forceatlas2/Bundle_fr.properties (+33/-0)
ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2.java (+450/-0)
ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2Builder.java (+75/-0)
ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2LayoutData.java (+24/-0)
ForceAtlas2/src/org/webatlas/forceatlas2/ForceFactory.java (+510/-0)
ForceAtlas2/src/org/webatlas/forceatlas2/Region.java (+197/-0)
To merge this branch: bzr merge lp:~mathieu-jacomy/gephi/forceatlas2
Reviewer Review Type Date Requested Status
Mathieu Bastian Approve
Review via email: mp+63504@code.launchpad.net

Description of the change

New awesome ForceAtlas2 algorithm

To post a comment you must log in.
Revision history for this message
Mathieu Bastian (mathieu.bastian) wrote :

The algorithm classes will be moved to the LayoutPlugin module

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'ForceAtlas2'
2=== added file 'ForceAtlas2/build.xml'
3--- ForceAtlas2/build.xml 1970-01-01 00:00:00 +0000
4+++ ForceAtlas2/build.xml 2011-06-05 20:50:57 +0000
5@@ -0,0 +1,8 @@
6+<?xml version="1.0" encoding="UTF-8"?>
7+<!-- You may freely edit this file. See harness/README in the NetBeans platform -->
8+<!-- for some information on what you could do (e.g. targets to override). -->
9+<!-- If you delete this file and reopen the project it will be recreated. -->
10+<project name="org.webatlas.forceatlas2" default="netbeans" basedir=".">
11+ <description>Builds, tests, and runs the project org.webatlas.forceatlas2.</description>
12+ <import file="nbproject/build-impl.xml"/>
13+</project>
14
15=== added file 'ForceAtlas2/manifest.mf'
16--- ForceAtlas2/manifest.mf 1970-01-01 00:00:00 +0000
17+++ ForceAtlas2/manifest.mf 2011-06-05 20:50:57 +0000
18@@ -0,0 +1,5 @@
19+Manifest-Version: 1.0
20+OpenIDE-Module: org.webatlas.forceatlas2
21+OpenIDE-Module-Localizing-Bundle: org/webatlas/forceatlas2/Bundle.properties
22+OpenIDE-Module-Specification-Version: 1.6
23+
24
25=== added directory 'ForceAtlas2/nbproject'
26=== added file 'ForceAtlas2/nbproject/build-impl.xml'
27--- ForceAtlas2/nbproject/build-impl.xml 1970-01-01 00:00:00 +0000
28+++ ForceAtlas2/nbproject/build-impl.xml 2011-06-05 20:50:57 +0000
29@@ -0,0 +1,45 @@
30+<?xml version="1.0" encoding="UTF-8"?>
31+<!--
32+*** GENERATED FROM project.xml - DO NOT EDIT ***
33+*** EDIT ../build.xml INSTEAD ***
34+-->
35+<project name="org.webatlas.forceatlas2-impl" basedir="..">
36+ <fail message="Please build using Ant 1.7.1 or higher.">
37+ <condition>
38+ <not>
39+ <antversion atleast="1.7.1"/>
40+ </not>
41+ </condition>
42+ </fail>
43+ <property file="nbproject/private/suite-private.properties"/>
44+ <property file="nbproject/suite.properties"/>
45+ <fail unless="suite.dir">You must set 'suite.dir' to point to your containing module suite</fail>
46+ <property file="${suite.dir}/nbproject/private/platform-private.properties"/>
47+ <property file="${suite.dir}/nbproject/platform.properties"/>
48+ <macrodef name="property" uri="http://www.netbeans.org/ns/nb-module-project/2">
49+ <attribute name="name"/>
50+ <attribute name="value"/>
51+ <sequential>
52+ <property name="@{name}" value="${@{value}}"/>
53+ </sequential>
54+ </macrodef>
55+ <macrodef name="evalprops" uri="http://www.netbeans.org/ns/nb-module-project/2">
56+ <attribute name="property"/>
57+ <attribute name="value"/>
58+ <sequential>
59+ <property name="@{property}" value="@{value}"/>
60+ </sequential>
61+ </macrodef>
62+ <property file="${user.properties.file}"/>
63+ <nbmproject2:property name="harness.dir" value="nbplatform.${nbplatform.active}.harness.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
64+ <nbmproject2:property name="nbplatform.active.dir" value="nbplatform.${nbplatform.active}.netbeans.dest.dir" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
65+ <nbmproject2:evalprops property="cluster.path.evaluated" value="${cluster.path}" xmlns:nbmproject2="http://www.netbeans.org/ns/nb-module-project/2"/>
66+ <fail message="Path to 'platform' cluster missing in $${cluster.path} property or using corrupt Netbeans Platform (missing harness).">
67+ <condition>
68+ <not>
69+ <contains string="${cluster.path.evaluated}" substring="platform"/>
70+ </not>
71+ </condition>
72+ </fail>
73+ <import file="${harness.dir}/build.xml"/>
74+</project>
75
76=== added file 'ForceAtlas2/nbproject/genfiles.properties'
77--- ForceAtlas2/nbproject/genfiles.properties 1970-01-01 00:00:00 +0000
78+++ ForceAtlas2/nbproject/genfiles.properties 2011-06-05 20:50:57 +0000
79@@ -0,0 +1,8 @@
80+build.xml.data.CRC32=0a8bc1d8
81+build.xml.script.CRC32=7eaa9dd0
82+build.xml.stylesheet.CRC32=a56c6a5b@1.45.1
83+# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
84+# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
85+nbproject/build-impl.xml.data.CRC32=0a8bc1d8
86+nbproject/build-impl.xml.script.CRC32=eb5b8ec7
87+nbproject/build-impl.xml.stylesheet.CRC32=238281d1@1.45.1
88
89=== added file 'ForceAtlas2/nbproject/project.properties'
90--- ForceAtlas2/nbproject/project.properties 1970-01-01 00:00:00 +0000
91+++ ForceAtlas2/nbproject/project.properties 2011-06-05 20:50:57 +0000
92@@ -0,0 +1,2 @@
93+javac.source=1.6
94+javac.compilerargs=-Xlint -Xlint:-serial
95
96=== added file 'ForceAtlas2/nbproject/project.xml'
97--- ForceAtlas2/nbproject/project.xml 1970-01-01 00:00:00 +0000
98+++ ForceAtlas2/nbproject/project.xml 2011-06-05 20:50:57 +0000
99@@ -0,0 +1,69 @@
100+<?xml version="1.0" encoding="UTF-8"?>
101+<project xmlns="http://www.netbeans.org/ns/project/1">
102+ <type>org.netbeans.modules.apisupport.project</type>
103+ <configuration>
104+ <data xmlns="http://www.netbeans.org/ns/nb-module-project/3">
105+ <code-name-base>org.webatlas.forceatlas2</code-name-base>
106+ <suite-component/>
107+ <module-dependencies>
108+ <dependency>
109+ <code-name-base>org.gephi.data.attributes.api</code-name-base>
110+ <build-prerequisite/>
111+ <compile-dependency/>
112+ <run-dependency>
113+ <specification-version>0.8</specification-version>
114+ </run-dependency>
115+ </dependency>
116+ <dependency>
117+ <code-name-base>org.gephi.dynamic.api</code-name-base>
118+ <build-prerequisite/>
119+ <compile-dependency/>
120+ <run-dependency>
121+ <specification-version>0.8</specification-version>
122+ </run-dependency>
123+ </dependency>
124+ <dependency>
125+ <code-name-base>org.gephi.graph.api</code-name-base>
126+ <build-prerequisite/>
127+ <compile-dependency/>
128+ <run-dependency>
129+ <specification-version>0.8</specification-version>
130+ </run-dependency>
131+ </dependency>
132+ <dependency>
133+ <code-name-base>org.gephi.layout.api</code-name-base>
134+ <build-prerequisite/>
135+ <compile-dependency/>
136+ <run-dependency>
137+ <specification-version>0.8.0.1</specification-version>
138+ </run-dependency>
139+ </dependency>
140+ <dependency>
141+ <code-name-base>org.gephi.project.api</code-name-base>
142+ <build-prerequisite/>
143+ <compile-dependency/>
144+ <run-dependency>
145+ <specification-version>0.8</specification-version>
146+ </run-dependency>
147+ </dependency>
148+ <dependency>
149+ <code-name-base>org.openide.util</code-name-base>
150+ <build-prerequisite/>
151+ <compile-dependency/>
152+ <run-dependency>
153+ <specification-version>8.14.1</specification-version>
154+ </run-dependency>
155+ </dependency>
156+ <dependency>
157+ <code-name-base>org.openide.util.lookup</code-name-base>
158+ <build-prerequisite/>
159+ <compile-dependency/>
160+ <run-dependency>
161+ <specification-version>8.6.1</specification-version>
162+ </run-dependency>
163+ </dependency>
164+ </module-dependencies>
165+ <public-packages/>
166+ </data>
167+ </configuration>
168+</project>
169
170=== added file 'ForceAtlas2/nbproject/suite.properties'
171--- ForceAtlas2/nbproject/suite.properties 1970-01-01 00:00:00 +0000
172+++ ForceAtlas2/nbproject/suite.properties 2011-06-05 20:50:57 +0000
173@@ -0,0 +1,1 @@
174+suite.dir=${basedir}/..
175
176=== added directory 'ForceAtlas2/src'
177=== added directory 'ForceAtlas2/src/org'
178=== added directory 'ForceAtlas2/src/org/webatlas'
179=== added directory 'ForceAtlas2/src/org/webatlas/forceatlas2'
180=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/Bundle.properties'
181--- ForceAtlas2/src/org/webatlas/forceatlas2/Bundle.properties 1970-01-01 00:00:00 +0000
182+++ ForceAtlas2/src/org/webatlas/forceatlas2/Bundle.properties 2011-06-05 20:50:57 +0000
183@@ -0,0 +1,33 @@
184+OpenIDE-Module-Display-Category=Plugin
185+OpenIDE-Module-Long-Description=Quality Layout, used in SNA. \
186+ A linear-attraction linear-repulsion model with few approximations (BarnesHut). \
187+ Features an optimal speed vs. precision balance and degree-driven repulsion for a high readability. \
188+ Works on medium-sized graphs: 10 to 10000 nodes.
189+OpenIDE-Module-Name=ForceAtlas 2, Quality Layout
190+OpenIDE-Module-Short-Description=Quality Layout
191+
192+ForceAtlas2.name=ForceAtlas 2
193+ForceAtlas2.description=Quality layout: a linear-attraction linear-repulsion model with few approximations (BarnesHut). Speed automatically computed.
194+
195+ForceAtlas2.tuning=Tuning
196+ForceAtlas2.behavior=Behavior Alternatives
197+ForceAtlas2.performance=Performance
198+
199+ForceAtlas2.scalingRatio.name=Scaling
200+ForceAtlas2.scalingRatio.desc=How much repulsion you want. More makes a more sparse graph.
201+ForceAtlas2.gravity.name=Gravity
202+ForceAtlas2.gravity.desc=Attracts nodes to the center. Prevents islands from drifting away.
203+ForceAtlas2.distributedAttraction.name=Dissuade Hubs
204+ForceAtlas2.distributedAttraction.desc=Distributes attraction along outbound edges. Hubs attract less and thus are pushed to the borders.
205+ForceAtlas2.linLogMode.name=LinLog mode
206+ForceAtlas2.linLogMode.desc=Switch ForceAtlas' model from lin-lin to lin-log (tribute to Andreas Noack). Makes clusters more tight.
207+ForceAtlas2.adjustSizes.name=Prevent Overlap
208+ForceAtlas2.adjustSizes.desc=Use only when spatialized. Should not be used with "Approximate Repulsion"
209+ForceAtlas2.jitterTolerance.name=Tolerance (speed)
210+ForceAtlas2.jitterTolerance.desc=How much swinging you allow. Above 1 discouraged. Lower gives less speed and more precision.
211+ForceAtlas2.barnesHutOptimization.name=Approximate Repulsion
212+ForceAtlas2.barnesHutOptimization.desc=Barnes Hut optimization: n\u00b2 complexity to n.ln(n) ; allows larger graphs.
213+ForceAtlas2.barnesHutTheta.name=Approximation
214+ForceAtlas2.barnesHutTheta.desc=Theta of the Barnes Hut optimization.
215+ForceAtlas2.edgeWeightInfluence.name=Edge Weight Influence
216+ForceAtlas2.edgeWeightInfluence.desc=How much influence you give to the edges weight. 0 is "no influence" and 1 is "normal".
217
218=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/Bundle_fr.properties'
219--- ForceAtlas2/src/org/webatlas/forceatlas2/Bundle_fr.properties 1970-01-01 00:00:00 +0000
220+++ ForceAtlas2/src/org/webatlas/forceatlas2/Bundle_fr.properties 2011-06-05 20:50:57 +0000
221@@ -0,0 +1,33 @@
222+OpenIDE-Module-Display-Category=Plugin
223+OpenIDE-Module-Long-Description=Spatialisation qualitative, utilis\u00e9e dans l'analyse des r\u00e9seaux sociaux. \
224+ Attraction et r\u00e9pulsion lin\u00e9aires avec quelques approximations (Barnes Hut)\
225+ Comprend un \u00e9quilibrage dynamique de la vitesse contre la pr\u00e9cision, et une r\u00e9pulsion par degree qui rend le graphe plus lisible.\
226+ Fonctionne sur des graphes de 10 \u00e0 10000 noeuds.
227+OpenIDE-Module-Name=ForceAtlas 2, Spatialisation qualitative
228+OpenIDE-Module-Short-Description=Spatialisation qualitative
229+
230+ForceAtlas2.name=ForceAtlas 2
231+ForceAtlas2.description=Spatialisation qualitative: mod\u00e8le \u00e0 attraction et r\u00e9pulsion lin\u00e9aires avec quelques optimisations (Barnes Hut). Param\u00e9trage automatis\u00e9.
232+
233+ForceAtlas2.tuning=R\u00e9glages fins
234+ForceAtlas2.behavior=Options de comportement
235+ForceAtlas2.performance=Performances
236+
237+ForceAtlas2.scalingRatio.name=Dimensionnement
238+ForceAtlas2.scalingRatio.desc=Quantit\u00e9 de r\u00e9pulsion, par rapport \u00e0 l'attraction. Rend le graphe plus \u00e9tal\u00e9.
239+ForceAtlas2.gravity.name=Gravit\u00e9
240+ForceAtlas2.gravity.desc=Attire les noeuds vers le centre. Emp\u00eache les \u00eelots de d\u00e9river \u00e0 l'infini.
241+ForceAtlas2.distributedAttraction.name=Dissuader les Hubs
242+ForceAtlas2.distributedAttraction.desc=Distribue l'attraction dans les liens sortants. Les Hubs attirent moins et sont donc repouss\u00e9s en p\u00e9riph\u00e9rie.
243+ForceAtlas2.linLogMode.name=Mode LinLog
244+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.
245+ForceAtlas2.adjustSizes.name=Emp\u00eacher Recouvrement
246+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.
247+ForceAtlas2.jitterTolerance.name=Tol\u00e9rance (vitesse)
248+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.
249+ForceAtlas2.barnesHutOptimization.name=Approximer R\u00e9pulsion
250+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.
251+ForceAtlas2.barnesHutTheta.name=Approximation
252+ForceAtlas2.barnesHutTheta.desc=param\u00e8tre Theta de l'optimisation de Barnes Hut.
253+ForceAtlas2.edgeWeightInfluence.name=Influence Poids des liens
254+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.
255
256=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2.java'
257--- ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2.java 1970-01-01 00:00:00 +0000
258+++ ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2.java 2011-06-05 20:50:57 +0000
259@@ -0,0 +1,450 @@
260+/*
261+Copyright 2008-2011 Gephi
262+Authors : Mathieu Jacomy <Mathieu.Jacomy@gmail.com>
263+Website : http://www.webatlas.fr
264+
265+You should have received a copy of the GNU Affero General Public License
266+along with ForceAtlas 2. If not, see <http://www.gnu.org/licenses/>.
267+*/
268+package org.webatlas.forceatlas2;
269+
270+import java.util.ArrayList;
271+import java.util.List;
272+import org.gephi.data.attributes.type.TimeInterval;
273+import org.gephi.dynamic.DynamicUtilities;
274+import org.gephi.dynamic.api.DynamicController;
275+import org.gephi.dynamic.api.DynamicModel;
276+import org.gephi.graph.api.Edge;
277+import org.gephi.graph.api.GraphModel;
278+import org.gephi.graph.api.HierarchicalGraph;
279+import org.gephi.graph.api.Node;
280+import org.gephi.graph.api.NodeData;
281+import org.gephi.layout.spi.Layout;
282+import org.gephi.layout.spi.LayoutBuilder;
283+import org.gephi.layout.spi.LayoutProperty;
284+import org.gephi.project.api.Workspace;
285+import org.openide.util.Lookup;
286+import org.openide.util.NbBundle;
287+import org.webatlas.forceatlas2.ForceFactory.AttractionForce;
288+import org.webatlas.forceatlas2.ForceFactory.RepulsionForce;
289+
290+/**
291+ * ForceAtlas 2 Layout, manages each step of the computations.
292+ * @author Mathieu Jacomy
293+ */
294+public class ForceAtlas2 implements Layout{
295+ private GraphModel graphModel;
296+ private HierarchicalGraph graph;
297+ private ForceAtlas2Builder layoutBuilder;
298+ private DynamicModel dynamicModel;
299+
300+ private double edgeWeightInfluence;
301+ private double jitterTolerance;
302+ private double scalingRatio;
303+ private double gravity;
304+ private double speed;
305+ private boolean outboundAttractionDistribution;
306+ private boolean adjustSizes;
307+ private boolean barnesHutOptimize;
308+ private double barnesHutTheta;
309+ private boolean linLogMode;
310+ private Region rootRegion;
311+
312+ double outboundAttCompensation = 1;
313+
314+ //Dynamic Weight
315+ private TimeInterval timeInterval;
316+
317+ public ForceAtlas2(ForceAtlas2Builder layoutBuilder) {
318+ this.layoutBuilder = layoutBuilder;
319+ }
320+
321+ @Override
322+ public void initAlgo() {
323+ speed = 1.;
324+
325+ graph = graphModel.getHierarchicalGraphVisible();
326+ this.timeInterval = DynamicUtilities.getVisibleInterval(dynamicModel);
327+
328+ graph.readLock();
329+ Node[] nodes = graph.getNodes().toArray();
330+
331+ // Initialise layout data
332+ for (Node n : nodes) {
333+ if (n.getNodeData().getLayoutData() == null || !(n.getNodeData().getLayoutData() instanceof ForceAtlas2LayoutData)) {
334+ ForceAtlas2LayoutData nLayout = new ForceAtlas2LayoutData();
335+ n.getNodeData().setLayoutData(nLayout);
336+ }
337+ NodeData nData = n.getNodeData();
338+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
339+ nLayout.mass = 1 + graph.getDegree(n);
340+ nLayout.old_dx = 0;
341+ nLayout.old_dy = 0;
342+ nLayout.dx = 0;
343+ nLayout.dy = 0;
344+ }
345+ }
346+
347+ @Override
348+ public void goAlgo() {
349+ // Initialize graph data
350+ if (graphModel == null) {
351+ return;
352+ }
353+ graph = graphModel.getHierarchicalGraphVisible();
354+ this.timeInterval = DynamicUtilities.getVisibleInterval(dynamicModel);
355+
356+ graph.readLock();
357+ Node[] nodes = graph.getNodes().toArray();
358+ Edge[] edges = graph.getEdgesAndMetaEdges().toArray();
359+
360+ // Initialise layout data
361+ for (Node n : nodes) {
362+ if (n.getNodeData().getLayoutData() == null || !(n.getNodeData().getLayoutData() instanceof ForceAtlas2LayoutData)) {
363+ ForceAtlas2LayoutData nLayout = new ForceAtlas2LayoutData();
364+ n.getNodeData().setLayoutData(nLayout);
365+ }
366+ NodeData nData = n.getNodeData();
367+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
368+ nLayout.mass = 1 + graph.getDegree(n);
369+ nLayout.old_dx = nLayout.dx;
370+ nLayout.old_dy = nLayout.dy;
371+ nLayout.dx = 0;
372+ nLayout.dy = 0;
373+ }
374+
375+ // If Barnes Hut active, initialize root region
376+ if(isBarnesHutOptimize()){
377+ rootRegion = new Region(nodes);
378+ rootRegion.buildSubRegions();
379+ }
380+
381+ // If outboundAttractionDistribution active, compensate.
382+ if(isOutboundAttractionDistribution()){
383+ outboundAttCompensation = 0;
384+ for(Node n : nodes){
385+ NodeData nData = n.getNodeData();
386+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
387+ outboundAttCompensation += nLayout.mass;
388+ }
389+ outboundAttCompensation /= nodes.length;
390+ }
391+
392+ // Repulsion
393+ RepulsionForce Repulsion = ForceFactory.builder.buildRepulsion(isAdjustSizes(), getScalingRatio());
394+ if(isBarnesHutOptimize()){
395+ for(Node n : nodes){
396+ rootRegion.applyForce(n, Repulsion, getBarnesHutTheta());
397+ }
398+ } else {
399+ for (int n1Index = 0; n1Index<nodes.length; n1Index++) {
400+ Node n1 = nodes[n1Index];
401+ for (int n2Index = 0; n2Index<n1Index; n2Index++) {
402+ Node n2 = nodes[n2Index];
403+ Repulsion.apply(n1, n2);
404+ }
405+ }
406+ }
407+
408+ // Attraction
409+ AttractionForce Attraction = ForceFactory.builder.buildAttraction(isLinLogMode(), isOutboundAttractionDistribution(), isAdjustSizes(), 1*((isOutboundAttractionDistribution())?(outboundAttCompensation):(1)));
410+ for (Edge e : edges) {
411+ Attraction.apply(e.getSource(), e.getTarget(), Math.pow(getWeight(e),getEdgeWeightInfluence()));
412+ }
413+
414+ // Gravity
415+ for (Node n : nodes) {
416+ Repulsion.apply(n, getGravity()/getScalingRatio());
417+ }
418+
419+ // Auto adjust speed
420+ double totalSwinging = 0d; // How much irregular movement
421+ double totalEffectiveTraction = 0d; // Hom much useful movement
422+ for (Node n : nodes) {
423+ NodeData nData = n.getNodeData();
424+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
425+ if (!nData.isFixed()) {
426+ double swinging = Math.sqrt(Math.pow(nLayout.old_dx - nLayout.dx,2) + Math.pow(nLayout.old_dy - nLayout.dy,2));
427+ totalSwinging += nLayout.mass * swinging; // If the node has a burst change of direction, then it's not converging.
428+ totalEffectiveTraction += nLayout.mass * 0.5 * Math.sqrt(Math.pow(nLayout.old_dx + nLayout.dx,2) + Math.pow(nLayout.old_dy + nLayout.dy,2));
429+ }
430+ }
431+ // We want that swingingMovement < tolerance * convergenceMovement
432+ double targetSpeed = getJitterTolerance() * getJitterTolerance() * totalEffectiveTraction / totalSwinging;
433+
434+ // But the speed shoudn't rise too much too quickly, since it would make the convergence drop dramatically.
435+ double maxRise = 0.5; // Max rise: 50%
436+ speed = speed + Math.min(targetSpeed - speed, maxRise * speed);
437+
438+ // Apply forces
439+ if(isAdjustSizes()){
440+ // If nodes overlap prevention is active, it's not possible to trust the swinging mesure.
441+ for (Node n : nodes) {
442+ NodeData nData = n.getNodeData();
443+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
444+ if (!nData.isFixed()) {
445+
446+ // Adaptive auto-speed: the speed of each node is lowered
447+ // when the node swings.
448+ double swinging = Math.sqrt((nLayout.old_dx - nLayout.dx) * (nLayout.old_dx - nLayout.dx) + (nLayout.old_dy - nLayout.dy) * (nLayout.old_dy - nLayout.dy));
449+ double factor = 0.1 * speed / (1f + speed * Math.sqrt(swinging));
450+
451+ double df = Math.sqrt(Math.pow(nLayout.dx, 2)+Math.pow(nLayout.dy, 2));
452+ factor = Math.min(factor*df, 10.)/df;
453+
454+ double x = nData.x() + nLayout.dx*factor;
455+ double y = nData.y() + nLayout.dy*factor;
456+
457+ nData.setX((float) x);
458+ nData.setY((float) y);
459+ }
460+ }
461+ } else {
462+ for (Node n : nodes) {
463+ NodeData nData = n.getNodeData();
464+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
465+ if (!nData.isFixed()) {
466+
467+ // Adaptive auto-speed: the speed of each node is lowered
468+ // when the node swings.
469+ double swinging = Math.sqrt((nLayout.old_dx - nLayout.dx) * (nLayout.old_dx - nLayout.dx) + (nLayout.old_dy - nLayout.dy) * (nLayout.old_dy - nLayout.dy));
470+ //double factor = speed / (1f + Math.sqrt(speed * swinging));
471+ double factor = speed / (1f + speed * Math.sqrt(swinging));
472+
473+ double x = nData.x() + nLayout.dx*factor;
474+ double y = nData.y() + nLayout.dy*factor;
475+
476+ nData.setX((float) x);
477+ nData.setY((float) y);
478+ }
479+ }
480+ }
481+ graph.readUnlock();
482+ }
483+
484+ @Override
485+ public boolean canAlgo() {
486+ return graphModel != null;
487+ }
488+
489+ @Override
490+ public void endAlgo() {
491+ for (Node n : graph.getNodes()) {
492+ n.getNodeData().setLayoutData(null);
493+ }
494+ graph.readUnlock();
495+ }
496+
497+ @Override
498+ public LayoutProperty[] getProperties() {
499+ List<LayoutProperty> properties = new ArrayList<LayoutProperty>();
500+ final String FORCEATLAS2_TUNING = NbBundle.getMessage(getClass(), "ForceAtlas2.tuning");
501+ final String FORCEATLAS2_BEHAVIOR = NbBundle.getMessage(getClass(), "ForceAtlas2.behavior");
502+ final String FORCEATLAS2_PERFORMANCE = NbBundle.getMessage(getClass(), "ForceAtlas2.performance");
503+
504+ try {
505+ properties.add(LayoutProperty.createProperty(
506+ this, Double.class,
507+ NbBundle.getMessage(getClass(), "ForceAtlas2.scalingRatio.name"),
508+ FORCEATLAS2_TUNING,
509+ NbBundle.getMessage(getClass(), "ForceAtlas2.scalingRatio.desc"),
510+ "getScalingRatio", "setScalingRatio"));
511+
512+ properties.add(LayoutProperty.createProperty(
513+ this, Double.class,
514+ NbBundle.getMessage(getClass(), "ForceAtlas2.gravity.name"),
515+ FORCEATLAS2_TUNING,
516+ NbBundle.getMessage(getClass(), "ForceAtlas2.gravity.desc"),
517+ "getGravity", "setGravity"));
518+
519+ properties.add(LayoutProperty.createProperty(
520+ this, Boolean.class,
521+ NbBundle.getMessage(getClass(), "ForceAtlas2.distributedAttraction.name"),
522+ FORCEATLAS2_BEHAVIOR,
523+ NbBundle.getMessage(getClass(), "ForceAtlas2.distributedAttraction.desc"),
524+ "isOutboundAttractionDistribution", "setOutboundAttractionDistribution"));
525+
526+ properties.add(LayoutProperty.createProperty(
527+ this, Boolean.class,
528+ NbBundle.getMessage(getClass(), "ForceAtlas2.linLogMode.name"),
529+ FORCEATLAS2_BEHAVIOR,
530+ NbBundle.getMessage(getClass(), "ForceAtlas2.linLogMode.desc"),
531+ "isLinLogMode", "setLinLogMode"));
532+
533+ properties.add(LayoutProperty.createProperty(
534+ this, Boolean.class,
535+ NbBundle.getMessage(getClass(), "ForceAtlas2.adjustSizes.name"),
536+ FORCEATLAS2_BEHAVIOR,
537+ NbBundle.getMessage(getClass(), "ForceAtlas2.adjustSizes.desc"),
538+ "isAdjustSizes", "setAdjustSizes"));
539+
540+ properties.add(LayoutProperty.createProperty(
541+ this, Double.class,
542+ NbBundle.getMessage(getClass(), "ForceAtlas2.edgeWeightInfluence.name"),
543+ FORCEATLAS2_BEHAVIOR,
544+ NbBundle.getMessage(getClass(), "ForceAtlas2.edgeWeightInfluence.desc"),
545+ "getEdgeWeightInfluence", "setEdgeWeightInfluence"));
546+
547+ properties.add(LayoutProperty.createProperty(
548+ this, Double.class,
549+ NbBundle.getMessage(getClass(), "ForceAtlas2.jitterTolerance.name"),
550+ FORCEATLAS2_PERFORMANCE,
551+ NbBundle.getMessage(getClass(), "ForceAtlas2.jitterTolerance.desc"),
552+ "getJitterTolerance", "setJitterTolerance"));
553+
554+ properties.add(LayoutProperty.createProperty(
555+ this, Boolean.class,
556+ NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutOptimization.name"),
557+ FORCEATLAS2_PERFORMANCE,
558+ NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutOptimization.desc"),
559+ "isBarnesHutOptimize", "setBarnesHutOptimize"));
560+
561+ properties.add(LayoutProperty.createProperty(
562+ this, Double.class,
563+ NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutTheta.name"),
564+ FORCEATLAS2_PERFORMANCE,
565+ NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutTheta.desc"),
566+ "getBarnesHutTheta", "setBarnesHutTheta"));
567+
568+ } catch (Exception e) {
569+ e.printStackTrace();
570+ }
571+
572+ return properties.toArray(new LayoutProperty[0]);
573+ }
574+
575+ @Override
576+ public void resetPropertiesValues() {
577+ int nodesCount = 0;
578+
579+ if(graphModel != null){
580+ nodesCount = graphModel.getHierarchicalGraphVisible().getNodeCount();
581+ }
582+
583+ // Tuning
584+ if(nodesCount>=100){
585+ setScalingRatio(2.0);
586+ } else {
587+ setScalingRatio(10.0);
588+ }
589+ setGravity(1.);
590+
591+ // Behavior
592+ setOutboundAttractionDistribution(false);
593+ setLinLogMode(false);
594+ setAdjustSizes(false);
595+ setEdgeWeightInfluence(1.);
596+
597+ // Performance
598+ if(nodesCount>=50000){
599+ setJitterTolerance(10d);
600+ } else if(nodesCount>=5000){
601+ setJitterTolerance(1d);
602+ } else {
603+ setJitterTolerance(0.1d);
604+ }
605+ if(nodesCount>=1000){
606+ setBarnesHutOptimize(true);
607+ } else {
608+ setBarnesHutOptimize(false);
609+ }
610+ setBarnesHutTheta(1.2);
611+ }
612+
613+ @Override
614+ public LayoutBuilder getBuilder() {
615+ return layoutBuilder;
616+ }
617+
618+ @Override
619+ public void setGraphModel(GraphModel graphModel) {
620+ this.graphModel = graphModel;
621+ Workspace workspace = graphModel.getWorkspace();
622+ DynamicController dynamicController = Lookup.getDefault().lookup(DynamicController.class);
623+ if (dynamicController != null && workspace != null) {
624+ dynamicModel = dynamicController.getModel(workspace);
625+ }
626+ // Trick: reset here to take the profile of the graph in account for default values
627+ resetPropertiesValues();
628+ }
629+
630+ public Double getBarnesHutTheta() {
631+ return barnesHutTheta;
632+ }
633+
634+ public void setBarnesHutTheta(Double barnesHutTheta) {
635+ this.barnesHutTheta = barnesHutTheta;
636+ }
637+
638+ public Double getEdgeWeightInfluence() {
639+ return edgeWeightInfluence;
640+ }
641+
642+ public void setEdgeWeightInfluence(Double edgeWeightInfluence) {
643+ this.edgeWeightInfluence = edgeWeightInfluence;
644+ }
645+
646+ public Double getJitterTolerance() {
647+ return jitterTolerance;
648+ }
649+
650+ public void setJitterTolerance(Double jitterTolerance) {
651+ this.jitterTolerance = jitterTolerance;
652+ }
653+
654+ public Boolean isLinLogMode() {
655+ return linLogMode;
656+ }
657+
658+ public void setLinLogMode(Boolean linLogMode) {
659+ this.linLogMode = linLogMode;
660+ }
661+
662+ public Double getScalingRatio() {
663+ return scalingRatio;
664+ }
665+
666+ public void setScalingRatio(Double scalingRatio) {
667+ this.scalingRatio = scalingRatio;
668+ }
669+
670+ public Double getGravity() {
671+ return gravity;
672+ }
673+
674+ public void setGravity(Double gravity) {
675+ this.gravity = gravity;
676+ }
677+
678+ public Boolean isOutboundAttractionDistribution() {
679+ return outboundAttractionDistribution;
680+ }
681+
682+ public void setOutboundAttractionDistribution(Boolean outboundAttractionDistribution) {
683+ this.outboundAttractionDistribution = outboundAttractionDistribution;
684+ }
685+
686+ public Boolean isAdjustSizes() {
687+ return adjustSizes;
688+ }
689+
690+ public void setAdjustSizes(Boolean adjustSizes) {
691+ this.adjustSizes = adjustSizes;
692+ }
693+
694+ public Boolean isBarnesHutOptimize() {
695+ return barnesHutOptimize;
696+ }
697+
698+ public void setBarnesHutOptimize(Boolean barnesHutOptimize) {
699+ this.barnesHutOptimize = barnesHutOptimize;
700+ }
701+
702+ private float getWeight(Edge edge) {
703+ if(timeInterval!=null) {
704+ return edge.getWeight(timeInterval.getLow(), timeInterval.getHigh());
705+ } else {
706+ return edge.getWeight();
707+ }
708+ }
709+}
710
711=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2Builder.java'
712--- ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2Builder.java 1970-01-01 00:00:00 +0000
713+++ ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2Builder.java 2011-06-05 20:50:57 +0000
714@@ -0,0 +1,75 @@
715+/*
716+Copyright 2008-2011 Gephi
717+Authors : Mathieu Jacomy <Mathieu.Jacomy@gmail.com>
718+Website : http://www.webatlas.fr
719+
720+You should have received a copy of the GNU Affero General Public License
721+along with ForceAtlas 2. If not, see <http://www.gnu.org/licenses/>.
722+*/
723+
724+package org.webatlas.forceatlas2;
725+
726+import javax.swing.Icon;
727+import javax.swing.JPanel;
728+import org.gephi.layout.spi.Layout;
729+import org.gephi.layout.spi.LayoutBuilder;
730+import org.gephi.layout.spi.LayoutUI;
731+import org.openide.util.NbBundle;
732+import org.openide.util.lookup.ServiceProvider;
733+
734+/**
735+ * Layout Builder
736+ * @author Mathieu Jacomy
737+ */
738+@ServiceProvider(service = LayoutBuilder.class)
739+public class ForceAtlas2Builder implements LayoutBuilder{
740+
741+ private ForceAtlas2UI ui = new ForceAtlas2UI();
742+
743+ @Override
744+ public String getName() {
745+ return NbBundle.getMessage(ForceAtlas2.class, "ForceAtlas2.name");
746+ }
747+
748+ @Override
749+ public LayoutUI getUI() {
750+ return ui;
751+ }
752+
753+ @Override
754+ public ForceAtlas2 buildLayout() {
755+ ForceAtlas2 layout = new ForceAtlas2(this);
756+ return layout;
757+ }
758+
759+
760+ private class ForceAtlas2UI implements LayoutUI{
761+
762+ @Override
763+ public String getDescription() {
764+ return NbBundle.getMessage(ForceAtlas2.class, "ForceAtlas2.description");
765+ }
766+
767+ @Override
768+ public Icon getIcon() {
769+ return null;
770+ }
771+
772+ @Override
773+ public JPanel getSimplePanel(Layout layout) {
774+ return null;
775+ }
776+
777+ @Override
778+ public int getQualityRank() {
779+ return 4;
780+ }
781+
782+ @Override
783+ public int getSpeedRank() {
784+ return 4;
785+ }
786+
787+ }
788+
789+}
790\ No newline at end of file
791
792=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2LayoutData.java'
793--- ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2LayoutData.java 1970-01-01 00:00:00 +0000
794+++ ForceAtlas2/src/org/webatlas/forceatlas2/ForceAtlas2LayoutData.java 2011-06-05 20:50:57 +0000
795@@ -0,0 +1,24 @@
796+/*
797+Copyright 2008-2011 Gephi
798+Authors : Mathieu Jacomy <Mathieu.Jacomy@gmail.com>
799+Website : http://www.webatlas.fr
800+
801+You should have received a copy of the GNU Affero General Public License
802+along with ForceAtlas 2. If not, see <http://www.gnu.org/licenses/>.
803+*/
804+package org.webatlas.forceatlas2;
805+
806+import org.gephi.graph.spi.LayoutData;
807+
808+/**
809+ * Data stored in Nodes and used by ForceAtlas2
810+ * @author Mathieu Jacomy
811+ */
812+public class ForceAtlas2LayoutData implements LayoutData{
813+ //Data
814+ public double dx = 0;
815+ public double dy = 0;
816+ public double old_dx = 0;
817+ public double old_dy = 0;
818+ public double mass = 1;
819+}
820
821=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/ForceFactory.java'
822--- ForceAtlas2/src/org/webatlas/forceatlas2/ForceFactory.java 1970-01-01 00:00:00 +0000
823+++ ForceAtlas2/src/org/webatlas/forceatlas2/ForceFactory.java 2011-06-05 20:50:57 +0000
824@@ -0,0 +1,510 @@
825+/*
826+Copyright 2008-2011 Gephi
827+Authors : Mathieu Jacomy <Mathieu.Jacomy@gmail.com>
828+Website : http://www.webatlas.fr
829+
830+You should have received a copy of the GNU Affero General Public License
831+along with ForceAtlas 2. If not, see <http://www.gnu.org/licenses/>.
832+*/
833+package org.webatlas.forceatlas2;
834+
835+import org.gephi.graph.api.Node;
836+import org.gephi.graph.api.NodeData;
837+
838+/**
839+ * Generates the forces on demand, here are all the formulas for attraction and repulsion.
840+ * @author Mathieu Jacomy
841+ */
842+public class ForceFactory {
843+ public static ForceFactory builder = new ForceFactory();
844+
845+ private ForceFactory(){};
846+
847+ public RepulsionForce buildRepulsion(boolean adjustBySize, double coefficient){
848+ if(adjustBySize){
849+ return new linRepulsion_antiCollision(coefficient);
850+ } else {
851+ return new linRepulsion(coefficient);
852+ }
853+ }
854+
855+ public AttractionForce buildAttraction(boolean logAttraction, boolean distributedAttraction, boolean adjustBySize, double coefficient){
856+ if(adjustBySize){
857+ if(logAttraction){
858+ if(distributedAttraction){
859+ return new logAttraction_degreeDistributed_antiCollision(coefficient);
860+ } else {
861+ return new logAttraction_antiCollision(coefficient);
862+ }
863+ } else {
864+ if(distributedAttraction){
865+ return new linAttraction_degreeDistributed_antiCollision(coefficient);
866+ } else {
867+ return new linAttraction_antiCollision(coefficient);
868+ }
869+ }
870+ } else {
871+ if(logAttraction){
872+ if(distributedAttraction){
873+ return new logAttraction_degreeDistributed(coefficient);
874+ } else {
875+ return new logAttraction(coefficient);
876+ }
877+ } else {
878+ if(distributedAttraction){
879+ return new linAttraction_massDistributed(coefficient);
880+ } else {
881+ return new linAttraction(coefficient);
882+ }
883+ }
884+ }
885+ }
886+
887+ public abstract class AttractionForce {
888+ public abstract void apply(Node n1, Node n2, double e); // Model for node-node attraction (e is for edge weight if needed)
889+ }
890+
891+ public abstract class RepulsionForce {
892+ public abstract void apply(Node n1, Node n2); // Model for node-node repulsion
893+ public abstract void apply(Node n, Region r); // Model for Barnes Hut approximation
894+ public abstract void apply(Node n, double g); // Model for gravitation (anti-repulsion)
895+ }
896+
897+ /*
898+ * Repulsion force: Linear
899+ */
900+ private class linRepulsion extends RepulsionForce{
901+ private double coefficient;
902+
903+ public linRepulsion(double c){
904+ coefficient = c;
905+ }
906+
907+ @Override
908+ public void apply(Node n1, Node n2) {
909+ NodeData n1Data = n1.getNodeData();
910+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
911+ NodeData n2Data = n2.getNodeData();
912+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
913+
914+ // Get the distance
915+ double xDist = n1Data.x() - n2Data.x();
916+ double yDist = n1Data.y() - n2Data.y();
917+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
918+
919+ if (distance > 0) {
920+ // NB: factor = force / distance
921+ double factor = coefficient * n1Layout.mass * n2Layout.mass / distance / distance;
922+
923+ n1Layout.dx += xDist * factor;
924+ n1Layout.dy += yDist * factor;
925+
926+ n2Layout.dx -= xDist * factor;
927+ n2Layout.dy -= yDist * factor;
928+ }
929+ }
930+
931+ @Override
932+ public void apply(Node n, Region r) {
933+ NodeData nData = n.getNodeData();
934+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
935+
936+ // Get the distance
937+ double xDist = nData.x() - r.getMassCenterX();
938+ double yDist = nData.y() - r.getMassCenterY();
939+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
940+
941+ if (distance > 0) {
942+ // NB: factor = force / distance
943+ double factor = coefficient * nLayout.mass * r.getMass() / distance / distance;
944+
945+ nLayout.dx += xDist * factor;
946+ nLayout.dy += yDist * factor;
947+ }
948+ }
949+
950+ @Override
951+ public void apply(Node n, double g) {
952+ NodeData nData = n.getNodeData();
953+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
954+
955+ // Get the distance
956+ double xDist = nData.x();
957+ double yDist = nData.y();
958+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
959+
960+ if (distance > 0) {
961+ // NB: factor = force / distance
962+ double factor = coefficient * nLayout.mass * g / distance;
963+
964+ nLayout.dx -= xDist * factor;
965+ nLayout.dy -= yDist * factor;
966+ }
967+ }
968+ }
969+
970+ /*
971+ * Repulsion force: Linear with Anti-collision
972+ */
973+ private class linRepulsion_antiCollision extends RepulsionForce{
974+ private double coefficient;
975+
976+ public linRepulsion_antiCollision(double c){
977+ coefficient = c;
978+ }
979+
980+ @Override
981+ public void apply(Node n1, Node n2) {
982+ NodeData n1Data = n1.getNodeData();
983+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
984+ NodeData n2Data = n2.getNodeData();
985+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
986+
987+ // Get the distance
988+ double xDist = n1Data.x() - n2Data.x();
989+ double yDist = n1Data.y() - n2Data.y();
990+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
991+
992+ if (distance > 0) {
993+ // NB: factor = force / distance
994+ double factor = coefficient * n1Layout.mass * n2Layout.mass / distance / distance;
995+
996+ n1Layout.dx += xDist * factor;
997+ n1Layout.dy += yDist * factor;
998+
999+ n2Layout.dx -= xDist * factor;
1000+ n2Layout.dy -= yDist * factor;
1001+
1002+ } else if(distance < 0){
1003+ double factor = 100 * coefficient * n1Layout.mass * n2Layout.mass;
1004+
1005+ n1Layout.dx += xDist * factor;
1006+ n1Layout.dy += yDist * factor;
1007+
1008+ n2Layout.dx -= xDist * factor;
1009+ n2Layout.dy -= yDist * factor;
1010+ }
1011+ }
1012+
1013+ @Override
1014+ public void apply(Node n, Region r) {
1015+ NodeData nData = n.getNodeData();
1016+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
1017+
1018+ // Get the distance
1019+ double xDist = nData.x() - r.getMassCenterX();
1020+ double yDist = nData.y() - r.getMassCenterY();
1021+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
1022+
1023+ if (distance > 0) {
1024+ // NB: factor = force / distance
1025+ double factor = coefficient * nLayout.mass * r.getMass() / distance / distance;
1026+
1027+ nLayout.dx += xDist * factor;
1028+ nLayout.dy += yDist * factor;
1029+ } else if(distance < 0){
1030+ double factor = -coefficient * nLayout.mass * r.getMass() / distance;
1031+
1032+ nLayout.dx += xDist * factor;
1033+ nLayout.dy += yDist * factor;
1034+ }
1035+ }
1036+
1037+ @Override
1038+ public void apply(Node n, double g) {
1039+ NodeData nData = n.getNodeData();
1040+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
1041+
1042+ // Get the distance
1043+ double xDist = nData.x();
1044+ double yDist = nData.y();
1045+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
1046+
1047+ if (distance > 0) {
1048+ // NB: factor = force / distance
1049+ double factor = coefficient * nLayout.mass * g / distance;
1050+
1051+ nLayout.dx -= xDist * factor;
1052+ nLayout.dy -= yDist * factor;
1053+ }
1054+ }
1055+ }
1056+
1057+ /*
1058+ * Attraction force: Linear
1059+ */
1060+ private class linAttraction extends AttractionForce{
1061+ private double coefficient;
1062+
1063+ public linAttraction(double c){
1064+ coefficient = c;
1065+ }
1066+
1067+ @Override
1068+ public void apply(Node n1, Node n2, double e) {
1069+ NodeData n1Data = n1.getNodeData();
1070+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
1071+ NodeData n2Data = n2.getNodeData();
1072+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
1073+
1074+ // Get the distance
1075+ double xDist = n1Data.x() - n2Data.x();
1076+ double yDist = n1Data.y() - n2Data.y();
1077+
1078+ // NB: factor = force / distance
1079+ double factor = -coefficient * e;
1080+
1081+ n1Layout.dx += xDist * factor;
1082+ n1Layout.dy += yDist * factor;
1083+
1084+ n2Layout.dx -= xDist * factor;
1085+ n2Layout.dy -= yDist * factor;
1086+ }
1087+ }
1088+
1089+ /*
1090+ * Attraction force: Linear, distributed by mass (typically, degree)
1091+ */
1092+ private class linAttraction_massDistributed extends AttractionForce{
1093+ private double coefficient;
1094+
1095+ public linAttraction_massDistributed(double c){
1096+ coefficient = c;
1097+ }
1098+
1099+ @Override
1100+ public void apply(Node n1, Node n2, double e) {
1101+ NodeData n1Data = n1.getNodeData();
1102+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
1103+ NodeData n2Data = n2.getNodeData();
1104+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
1105+
1106+ // Get the distance
1107+ double xDist = n1Data.x() - n2Data.x();
1108+ double yDist = n1Data.y() - n2Data.y();
1109+
1110+ // NB: factor = force / distance
1111+ double factor = -coefficient * e / n1Layout.mass;
1112+
1113+ n1Layout.dx += xDist * factor;
1114+ n1Layout.dy += yDist * factor;
1115+
1116+ n2Layout.dx -= xDist * factor;
1117+ n2Layout.dy -= yDist * factor;
1118+ }
1119+ }
1120+
1121+ /*
1122+ * Attraction force: Logarithmic
1123+ */
1124+ private class logAttraction extends AttractionForce{
1125+ private double coefficient;
1126+
1127+ public logAttraction(double c){
1128+ coefficient = c;
1129+ }
1130+
1131+ @Override
1132+ public void apply(Node n1, Node n2, double e) {
1133+ NodeData n1Data = n1.getNodeData();
1134+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
1135+ NodeData n2Data = n2.getNodeData();
1136+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
1137+
1138+ // Get the distance
1139+ double xDist = n1Data.x() - n2Data.x();
1140+ double yDist = n1Data.y() - n2Data.y();
1141+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
1142+
1143+ if (distance > 0) {
1144+
1145+ // NB: factor = force / distance
1146+ double factor = -coefficient * e * Math.log(1+distance) / distance;
1147+
1148+ n1Layout.dx += xDist * factor;
1149+ n1Layout.dy += yDist * factor;
1150+
1151+ n2Layout.dx -= xDist * factor;
1152+ n2Layout.dy -= yDist * factor;
1153+ }
1154+ }
1155+ }
1156+
1157+ /*
1158+ * Attraction force: Linear, distributed by Degree
1159+ */
1160+ private class logAttraction_degreeDistributed extends AttractionForce{
1161+ private double coefficient;
1162+
1163+ public logAttraction_degreeDistributed(double c){
1164+ coefficient = c;
1165+ }
1166+
1167+ @Override
1168+ public void apply(Node n1, Node n2, double e) {
1169+ NodeData n1Data = n1.getNodeData();
1170+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
1171+ NodeData n2Data = n2.getNodeData();
1172+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
1173+
1174+ // Get the distance
1175+ double xDist = n1Data.x() - n2Data.x();
1176+ double yDist = n1Data.y() - n2Data.y();
1177+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
1178+
1179+ if (distance > 0) {
1180+
1181+ // NB: factor = force / distance
1182+ double factor = -coefficient * e * Math.log(1+distance) / distance / n1Layout.mass;
1183+
1184+ n1Layout.dx += xDist * factor;
1185+ n1Layout.dy += yDist * factor;
1186+
1187+ n2Layout.dx -= xDist * factor;
1188+ n2Layout.dy -= yDist * factor;
1189+ }
1190+ }
1191+ }
1192+
1193+ /*
1194+ * Attraction force: Linear, with Anti-Collision
1195+ */
1196+ private class linAttraction_antiCollision extends AttractionForce{
1197+ private double coefficient;
1198+
1199+ public linAttraction_antiCollision(double c){
1200+ coefficient = c;
1201+ }
1202+
1203+ @Override
1204+ public void apply(Node n1, Node n2, double e) {
1205+ NodeData n1Data = n1.getNodeData();
1206+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
1207+ NodeData n2Data = n2.getNodeData();
1208+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
1209+
1210+ // Get the distance
1211+ double xDist = n1Data.x() - n2Data.x();
1212+ double yDist = n1Data.y() - n2Data.y();
1213+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
1214+
1215+ if(distance>0){
1216+ // NB: factor = force / distance
1217+ double factor = -coefficient * e;
1218+
1219+ n1Layout.dx += xDist * factor;
1220+ n1Layout.dy += yDist * factor;
1221+
1222+ n2Layout.dx -= xDist * factor;
1223+ n2Layout.dy -= yDist * factor;
1224+ }
1225+ }
1226+ }
1227+
1228+ /*
1229+ * Attraction force: Linear, distributed by Degree, with Anti-Collision
1230+ */
1231+ private class linAttraction_degreeDistributed_antiCollision extends AttractionForce{
1232+ private double coefficient;
1233+
1234+ public linAttraction_degreeDistributed_antiCollision(double c){
1235+ coefficient = c;
1236+ }
1237+
1238+ @Override
1239+ public void apply(Node n1, Node n2, double e) {
1240+ NodeData n1Data = n1.getNodeData();
1241+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
1242+ NodeData n2Data = n2.getNodeData();
1243+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
1244+
1245+ // Get the distance
1246+ double xDist = n1Data.x() - n2Data.x();
1247+ double yDist = n1Data.y() - n2Data.y();
1248+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
1249+
1250+ if(distance>0){
1251+ // NB: factor = force / distance
1252+ double factor = -coefficient * e / n1Layout.mass;
1253+
1254+ n1Layout.dx += xDist * factor;
1255+ n1Layout.dy += yDist * factor;
1256+
1257+ n2Layout.dx -= xDist * factor;
1258+ n2Layout.dy -= yDist * factor;
1259+ }
1260+ }
1261+ }
1262+
1263+ /*
1264+ * Attraction force: Logarithmic, with Anti-Collision
1265+ */
1266+ private class logAttraction_antiCollision extends AttractionForce{
1267+ private double coefficient;
1268+
1269+ public logAttraction_antiCollision(double c){
1270+ coefficient = c;
1271+ }
1272+
1273+ @Override
1274+ public void apply(Node n1, Node n2, double e) {
1275+ NodeData n1Data = n1.getNodeData();
1276+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
1277+ NodeData n2Data = n2.getNodeData();
1278+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
1279+
1280+ // Get the distance
1281+ double xDist = n1Data.x() - n2Data.x();
1282+ double yDist = n1Data.y() - n2Data.y();
1283+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
1284+
1285+ if(distance>0){
1286+
1287+ // NB: factor = force / distance
1288+ double factor = -coefficient * e * Math.log(1+distance) / distance;
1289+
1290+ n1Layout.dx += xDist * factor;
1291+ n1Layout.dy += yDist * factor;
1292+
1293+ n2Layout.dx -= xDist * factor;
1294+ n2Layout.dy -= yDist * factor;
1295+ }
1296+ }
1297+ }
1298+
1299+ /*
1300+ * Attraction force: Linear, distributed by Degree, with Anti-Collision
1301+ */
1302+ private class logAttraction_degreeDistributed_antiCollision extends AttractionForce{
1303+ private double coefficient;
1304+
1305+ public logAttraction_degreeDistributed_antiCollision(double c){
1306+ coefficient = c;
1307+ }
1308+
1309+ @Override
1310+ public void apply(Node n1, Node n2, double e) {
1311+ NodeData n1Data = n1.getNodeData();
1312+ ForceAtlas2LayoutData n1Layout = n1Data.getLayoutData();
1313+ NodeData n2Data = n2.getNodeData();
1314+ ForceAtlas2LayoutData n2Layout = n2Data.getLayoutData();
1315+
1316+ // Get the distance
1317+ double xDist = n1Data.x() - n2Data.x();
1318+ double yDist = n1Data.y() - n2Data.y();
1319+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1Data.getSize() - n2Data.getSize();
1320+
1321+ if(distance>0){
1322+
1323+ // NB: factor = force / distance
1324+ double factor = -coefficient * e * Math.log(1+distance) / distance / n1Layout.mass;
1325+
1326+ n1Layout.dx += xDist * factor;
1327+ n1Layout.dy += yDist * factor;
1328+
1329+ n2Layout.dx -= xDist * factor;
1330+ n2Layout.dy -= yDist * factor;
1331+ }
1332+ }
1333+ }
1334+}
1335
1336=== added file 'ForceAtlas2/src/org/webatlas/forceatlas2/Region.java'
1337--- ForceAtlas2/src/org/webatlas/forceatlas2/Region.java 1970-01-01 00:00:00 +0000
1338+++ ForceAtlas2/src/org/webatlas/forceatlas2/Region.java 2011-06-05 20:50:57 +0000
1339@@ -0,0 +1,197 @@
1340+/*
1341+Copyright 2008-2011 Gephi
1342+Authors : Mathieu Jacomy <Mathieu.Jacomy@gmail.com>
1343+Website : http://www.webatlas.fr
1344+
1345+You should have received a copy of the GNU Affero General Public License
1346+along with ForceAtlas 2. If not, see <http://www.gnu.org/licenses/>.
1347+*/
1348+package org.webatlas.forceatlas2;
1349+
1350+import java.util.ArrayList;
1351+import java.util.Arrays;
1352+import java.util.List;
1353+import org.gephi.graph.api.Node;
1354+import org.gephi.graph.api.NodeData;
1355+import org.webatlas.forceatlas2.ForceFactory.RepulsionForce;
1356+
1357+/**
1358+ * Barnes Hut optimization
1359+ * @author Mathieu Jacomy
1360+ */
1361+public class Region{
1362+
1363+ private double mass;
1364+
1365+ private double massCenterX;
1366+ private double massCenterY;
1367+ private double size;
1368+
1369+ private List<Node> nodes;
1370+ private List<Region> subregions = new ArrayList<Region>();
1371+
1372+ public Region(Node[] nodes){
1373+ this.nodes = new ArrayList<Node>();
1374+ this.nodes.addAll(Arrays.asList(nodes));
1375+ updateMassAndGeometry();
1376+ }
1377+
1378+ public Region(ArrayList<Node> nodes){
1379+ this.nodes = new ArrayList<Node>(nodes);
1380+ updateMassAndGeometry();
1381+ }
1382+
1383+ public void updateMassAndGeometry(){
1384+ if(nodes.size()>1){
1385+ // Compute Mass
1386+ mass = 0;
1387+ double massSumX = 0;
1388+ double massSumY = 0;
1389+ for(Node n : nodes){
1390+ NodeData nData = n.getNodeData();
1391+ ForceAtlas2LayoutData nLayout = nData.getLayoutData();
1392+ mass += nLayout.mass;
1393+ massSumX += nData.x() * nLayout.mass;
1394+ massSumY += nData.y() * nLayout.mass;
1395+ }
1396+ massCenterX = massSumX / mass;
1397+ massCenterY = massSumY / mass;
1398+
1399+ // Compute size
1400+ size = Double.MIN_VALUE;
1401+ for(Node n : nodes){
1402+ NodeData nData = n.getNodeData();
1403+ double distance = Math.sqrt((nData.x()-massCenterX)*(nData.x()-massCenterX) + (nData.y()-massCenterY)*(nData.y()-massCenterY));
1404+ size = Math.max(size, 2*distance);
1405+ }
1406+ }
1407+ }
1408+
1409+ public void buildSubRegions(){
1410+ if(nodes.size()>1){
1411+ ArrayList<Node> leftNodes = new ArrayList<Node>();
1412+ ArrayList<Node> rightNodes = new ArrayList<Node>();
1413+ for(Node n : nodes){
1414+ NodeData nData = n.getNodeData();
1415+ ArrayList<Node> nodesColumn = (nData.x()<massCenterX)?(leftNodes):(rightNodes);
1416+ nodesColumn.add(n);
1417+ }
1418+
1419+ ArrayList<Node> topleftNodes = new ArrayList<Node>();
1420+ ArrayList<Node> bottomleftNodes = new ArrayList<Node>();
1421+ for(Node n : leftNodes){
1422+ NodeData nData = n.getNodeData();
1423+ ArrayList<Node> nodesLine = (nData.y()<massCenterY)?(topleftNodes):(bottomleftNodes);
1424+ nodesLine.add(n);
1425+ }
1426+
1427+ ArrayList<Node> bottomrightNodes = new ArrayList<Node>();
1428+ ArrayList<Node> toprightNodes = new ArrayList<Node>();
1429+ for(Node n : rightNodes){
1430+ NodeData nData = n.getNodeData();
1431+ ArrayList<Node> nodesLine = (nData.y()<massCenterY)?(toprightNodes):(bottomrightNodes);
1432+ nodesLine.add(n);
1433+ }
1434+
1435+ if(topleftNodes.size()>0){
1436+ if(topleftNodes.size()<nodes.size()){
1437+ Region subregion = new Region(topleftNodes);
1438+ subregions.add(subregion);
1439+ } else {
1440+ for(Node n : topleftNodes){
1441+ ArrayList<Node> oneNodeList = new ArrayList<Node>();
1442+ oneNodeList.add(n);
1443+ Region subregion = new Region(oneNodeList);
1444+ subregions.add(subregion);
1445+ }
1446+ }
1447+ }
1448+ if(bottomleftNodes.size()>0){
1449+ if(bottomleftNodes.size()<nodes.size()){
1450+ Region subregion = new Region(bottomleftNodes);
1451+ subregions.add(subregion);
1452+ } else {
1453+ for(Node n : bottomleftNodes){
1454+ ArrayList<Node> oneNodeList = new ArrayList<Node>();
1455+ oneNodeList.add(n);
1456+ Region subregion = new Region(oneNodeList);
1457+ subregions.add(subregion);
1458+ }
1459+ }
1460+ }
1461+ if(bottomrightNodes.size()>0){
1462+ if(bottomrightNodes.size()<nodes.size()){
1463+ Region subregion = new Region(bottomrightNodes);
1464+ subregions.add(subregion);
1465+ } else {
1466+ for(Node n : bottomrightNodes){
1467+ ArrayList<Node> oneNodeList = new ArrayList<Node>();
1468+ oneNodeList.add(n);
1469+ Region subregion = new Region(oneNodeList);
1470+ subregions.add(subregion);
1471+ }
1472+ }
1473+ }
1474+ if(toprightNodes.size()>0){
1475+ if(toprightNodes.size()<nodes.size()){
1476+ Region subregion = new Region(toprightNodes);
1477+ subregions.add(subregion);
1478+ } else {
1479+ for(Node n : toprightNodes){
1480+ ArrayList<Node> oneNodeList = new ArrayList<Node>();
1481+ oneNodeList.add(n);
1482+ Region subregion = new Region(oneNodeList);
1483+ subregions.add(subregion);
1484+ }
1485+ }
1486+ }
1487+
1488+ for(Region subregion : subregions){
1489+ subregion.buildSubRegions();
1490+ }
1491+ }
1492+ }
1493+
1494+ public void applyForce(Node n, RepulsionForce Force, double theta){
1495+ NodeData nData = n.getNodeData();
1496+ if(nodes.size() < 2){
1497+ Node regionNode = nodes.get(0);
1498+ Force.apply(n, regionNode);
1499+ } else {
1500+ double distance = Math.sqrt((nData.x()-massCenterX)*(nData.x()-massCenterX) + (nData.y() - massCenterY)*(nData.y()-massCenterY));
1501+ if(distance * theta > size){
1502+ Force.apply(n, this);
1503+ } else {
1504+ for (Region subregion : subregions) {
1505+ subregion.applyForce(n, Force, theta);
1506+ }
1507+ }
1508+ }
1509+ }
1510+
1511+ public double getMass() {
1512+ return mass;
1513+ }
1514+
1515+ public void setMass(double mass) {
1516+ this.mass = mass;
1517+ }
1518+
1519+ public double getMassCenterX() {
1520+ return massCenterX;
1521+ }
1522+
1523+ public void setMassCenterX(double massCenterX) {
1524+ this.massCenterX = massCenterX;
1525+ }
1526+
1527+ public double getMassCenterY() {
1528+ return massCenterY;
1529+ }
1530+
1531+ public void setMassCenterY(double massCenterY) {
1532+ this.massCenterY = massCenterY;
1533+ }
1534+
1535+
1536+}
1537
1538=== added directory 'ForceAtlas2/test'
1539=== added directory 'ForceAtlas2/test/unit'
1540=== added directory 'ForceAtlas2/test/unit/src'

Subscribers

People subscribed via source and target branches