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