diff --git a/modules/LayoutPluginExample/src/main/resources/org/gephi/plugins/example/layout/Bundle.properties b/modules/LayoutPluginExample/src/main/resources/org/gephi/plugins/example/layout/Bundle.properties
index 26f6990..f5c97d7 100644
--- a/modules/LayoutPluginExample/src/main/resources/org/gephi/plugins/example/layout/Bundle.properties
+++ b/modules/LayoutPluginExample/src/main/resources/org/gephi/plugins/example/layout/Bundle.properties
@@ -1,4 +1,4 @@
-OpenIDE-Module-Display-Category=Plugin
+OpenIDE-Module-Display-Category=Layout
OpenIDE-Module-Long-Description=\
Example of Layout Plugins:\n
\n- Grid Layout: Place all nodes in a simple grid. \
Users can configure the size of the area and the speed.\
diff --git a/modules/NodeWeight/README.md b/modules/NodeWeight/README.md
new file mode 100644
index 0000000..a1c45c3
--- /dev/null
+++ b/modules/NodeWeight/README.md
@@ -0,0 +1,31 @@
+# User Guide for Node Weight Plugin
+
+1. Launch Gephi (to get started with Gephi visit https://gephi.org or https://github.com/gephi/gephi)
+2. Install Node Weight plugin from Plugin menu in Gephi
+3. Import data or a .gexf file
+ * Dynamic data should be in string form like [Timestamp: data], [timestamp: data].
+ e.g. : “[2018-07-21T00:00:00.000Z, 0.21100000000000002]; [2018-07-21T00:30:00.000Z, 0.429]; [2018-07-21T03:00:00.000Z, 0.525]; [2018-07-21T06:00:00.000Z, 0.465]; [2018-07-21T12:00:00.000Z, 0.545]”
+ * Edges should have an ID in ‘Source’ and an ID in ‘Target’ that corresponds to the correct node IDs
+ * To use the Node Weight plugin functionality, you should have data in the nodes table in a column entitled ‘weight’ or ‘weight_dynamic’ (either will accept dynamic and non dynamic data)
+4. In Data Observatory, designate dynamic data columns as appropriate with your data.
+5. Go to Overview
+6. In the Layout tab select ‘Node Weight’ from the drop-down list
+7. Important fields for the Node Layout are:
+ * Node Weight Influence: Adjust scaling on node weight attraction. More creates larger distance between nodes.
+ * Opacity mode: Requires data scaled to its standard deviation. Opacity changes according to weight (in preview / export), turn on per-node opacity in preview tab to activate.
+ * Heat Map: Checkbox to turn on heat map based on normalized node weights. Requires data scaled to its standard deviation.
+ * Change Heat Map: Checkbox to turn on heat map to show change of normalized node weights between time slices in dynamic data. Red-green.
+ * Alternative Change Heat Map: Checkbox to turn on heat map to show change of normalized node weights between time slices in dynamic data Requires data scaled to its standard deviation. Blue-yellow.
+ * Gravity: Attracts nodes to the center. Prevents islands from drifting away. If negative weight averages in your network, minimum gravity set to avoid nodes floating out of graph space.
+ * Stronger Gravity: A stronger gravity law. Highly recommended to keep nodes in graph space if there are negative weights.
+ * Scaling: Because of the stronger gravity anchoring nodes with negative weights, you will probably want to turn this up.
+
+8. Experiment with features. Select one of the heat maps. Select opacity. Change the Node Weight influence.
+ Dynamic-specific
+ A. Turn on the Timeline feature: Select Timeline from the Window drop-down menu. Go to the bottom of the Gephi interface and click ‘Enable Timeline’
+ B. Adjust the intervals by sliding the edges of the translucent blue bar.
+ C. Drag your interval to the 1 marker and press the play button or manually drag the interval around. The interval for which you have data must be completed: e.g. if you want data for hour 3, cover the whole of 3 with an hour-long interval or with a smaller time-slice go to the next hour area not covered by your data. Mouse over the interval for a tooltip telling the duration and bounds of your interval. For the change heat maps, offset your interval an hour (or whatever other unit your timeline is using) later to see the change.
+
+
+
+
diff --git a/modules/NodeWeight/pom.xml b/modules/NodeWeight/pom.xml
new file mode 100644
index 0000000..cf64804
--- /dev/null
+++ b/modules/NodeWeight/pom.xml
@@ -0,0 +1,106 @@
+
+
+ 4.0.0
+
+ gephi-plugin-parent
+ org.gephi
+ 0.9.0
+
+
+ imperial
+ node-weight
+ 1.0.0
+ nbm
+
+ Node Weight
+
+
+
+ org.netbeans.api
+ org-openide-util-lookup
+
+
+ org.netbeans.api
+ org-openide-dialogs
+ jar
+ RELEASE81
+
+
+
+ org.gephi
+ graph-api
+
+
+ org.gephi
+ layout-api
+
+
+ org.gephi
+ ui-propertyeditor
+
+
+
+
+
+
+ org.gephi
+ project-api
+
+
+ org.gephi
+ core-library-wrapper
+
+
+ org.gephi
+ utils-longtask
+
+
+
+ org.netbeans.api
+ org-openide-util
+
+
+ org.netbeans.api
+ org-openide-nodes
+
+
+
+
+
+
+ org.codehaus.mojo
+ nbm-maven-plugin
+
+ Apache 2.0
+ Julianne Joswiak
+ jj2816@imperial.ac.uk
+
+ http://github.com/gephi/gephi-plugins-bootcamp
+ $homepage_url
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/AbstractLayout.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/AbstractLayout.java
new file mode 100644
index 0000000..c4c2ed5
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/AbstractLayout.java
@@ -0,0 +1,104 @@
+/*
+ Copyright 2008-2010 Gephi
+ Authors : Helder Suzuki
+ Website : http://www.gephi.org
+ This file is part of Gephi.
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ Copyright 2011 Gephi Consortium. All rights reserved.
+ The contents of this file are subject to the terms of either the GNU
+ General Public License Version 3 only ("GPL") or the Common
+ Development and Distribution License("CDDL") (collectively, the
+ "License"). You may not use this file except in compliance with the
+ License. You can obtain a copy of the License at
+ http://gephi.org/about/legal/license-notice/
+ or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+ specific language governing permissions and limitations under the
+ License. When distributing the software, include this License Header
+ Notice in each file and include the License files at
+ /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+ License Header, with the fields enclosed by brackets [] replaced by
+ your own identifying information:
+ "Portions Copyrighted [year] [name of copyright owner]"
+ If you wish your version of this file to be governed by only the CDDL
+ or only the GPL Version 3, indicate your decision by adding
+ "[Contributor] elects to include this software in this distribution
+ under the [CDDL or GPL Version 3] license." If you do not indicate a
+ single choice of license, a recipient has the option to distribute
+ your version of this file under either the CDDL, the GPL Version 3 or
+ to extend the choice of license to its licensees as provided above.
+ However, if you add GPL Version 3 code and therefore, elected the GPL
+ Version 3 license, then the option applies only if the new code is
+ made subject to such option by the copyright holder.
+ Contributor(s):
+ Portions Copyrighted 2011 Gephi Consortium.
+ */
+package org.gephi.layout.plugins.layout.NodeWeight;
+
+import org.gephi.graph.api.Graph;
+import org.gephi.graph.api.GraphModel;
+import org.gephi.graph.api.Node;
+import org.gephi.graph.api.NodeIterable;
+import org.gephi.layout.spi.Layout;
+import org.gephi.layout.spi.LayoutBuilder;
+
+/**
+ * Base class for layout algorithms.
+ *
+ * @author Helder Suzuki
+ */
+public abstract class AbstractLayout implements Layout {
+
+ private final LayoutBuilder layoutBuilder;
+ protected GraphModel graphModel;
+ private boolean converged;
+
+ public AbstractLayout(LayoutBuilder layoutBuilder) {
+ this.layoutBuilder = layoutBuilder;
+ }
+
+ @Override
+ public LayoutBuilder getBuilder() {
+ return layoutBuilder;
+ }
+
+ @Override
+ public void setGraphModel(GraphModel graphModel) {
+ this.graphModel = graphModel;
+ }
+
+ @Override
+ public boolean canAlgo() {
+ return !isConverged() && graphModel != null;
+ }
+
+ public void setConverged(boolean converged) {
+ this.converged = converged;
+ }
+
+ public boolean isConverged() {
+ return converged;
+ }
+
+ /**
+ * See https://github.com/gephi/gephi/issues/603 Nodes position to NaN on applied layout
+ *
+ * @param graphModel
+ */
+ public static void ensureSafeLayoutNodePositions(GraphModel graphModel) {
+ Graph graph = graphModel.getGraph();
+ NodeIterable nodesIterable = graph.getNodes();
+ for (Node node : nodesIterable) {
+ if (node.x() != 0 || node.y() != 0) {
+ nodesIterable.doBreak();
+ return;
+ }
+ }
+
+ //All at 0.0, init some random positions
+ nodesIterable = graph.getNodes();
+ for (Node node : nodesIterable) {
+ node.setX((float) ((0.01 + Math.random()) * 1000) - 500);
+ node.setY((float) ((0.01 + Math.random()) * 1000) - 500);
+ }
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2.java
new file mode 100644
index 0000000..9ef7487
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2.java
@@ -0,0 +1,945 @@
+/*
+ Copyright 2008-2011 Gephi
+ Authors : Mathieu Jacomy
+ Website : http://www.gephi.org
+
+ This file is part of Gephi.
+
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+ Copyright 2011 Gephi Consortium. All rights reserved.
+
+ The contents of this file are subject to the terms of either the GNU
+ General Public License Version 3 only ("GPL") or the Common
+ Development and Distribution License("CDDL") (collectively, the
+ "License"). You may not use this file except in compliance with the
+ License. You can obtain a copy of the License at
+ http://gephi.org/about/legal/license-notice/
+ or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+ specific language governing permissions and limitations under the
+ License. When distributing the software, include this License Header
+ Notice in each file and include the License files at
+ /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+ License Header, with the fields enclosed by brackets [] replaced by
+ your own identifying information:
+ "Portions Copyrighted [year] [name of copyright owner]"
+
+ If you wish your version of this file to be governed by only the CDDL
+ or only the GPL Version 3, indicate your decision by adding
+ "[Contributor] elects to include this software in this distribution
+ under the [CDDL or GPL Version 3] license." If you do not indicate a
+ single choice of license, a recipient has the option to distribute
+ your version of this file under either the CDDL, the GPL Version 3 or
+ to extend the choice of license to its licensees as provided above.
+ However, if you add GPL Version 3 code and therefore, elected the GPL
+ Version 3 license, then the option applies only if the new code is
+ made subject to such option by the copyright holder.
+
+ Contributor(s):
+
+ Portions Copyrighted 2011 Gephi Consortium.
+ */
+
+// all code attributable to Julianne Joswiak lies between tags: //jj2018 and //
+
+package org.gephi.layout.plugins.layout.NodeWeight;
+
+import java.awt.Color;
+
+
+import org.gephi.graph.api.Edge;
+import org.gephi.graph.api.Element;
+import org.gephi.graph.api.Graph;
+import org.gephi.graph.api.GraphModel;
+import org.gephi.graph.api.Node;
+import org.gephi.graph.api.Table;
+import org.gephi.graph.api.Estimator;
+import org.gephi.graph.api.Interval;
+import org.gephi.graph.api.types.IntervalMap;
+import org.gephi.graph.api.types.TimeMap;
+import org.gephi.graph.api.types.TimestampMap;
+import org.gephi.graph.api.types.TimestampDoubleMap;
+import org.gephi.graph.api.Interval;
+
+import org.gephi.layout.plugins.layout.NodeWeight.HeatmapFeatures;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.AttractionForce;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.RepulsionForce;
+
+
+import org.gephi.layout.spi.Layout;
+import org.gephi.layout.spi.LayoutBuilder;
+import org.gephi.layout.spi.LayoutProperty;
+
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Arrays;
+
+
+
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+import org.openide.NotifyDescriptor;
+import org.openide.DialogDisplayer;
+
+import org.gephi.layout.plugins.layout.NodeWeight.AbstractLayout;
+
+
+
+/**
+ * Node Weight Influence added to ForceAtlas 2 Layout, manages each step of the computations.
+ *
+ * @author Mathieu Jacomy
+ */
+public class ForceAtlas2 implements Layout {
+
+ private GraphModel graphModel;
+ private Graph graph;
+ private final ForceAtlas2Builder layoutBuilder;
+ private double edgeWeightInfluence;
+ private double jitterTolerance;
+ private double scalingRatio;
+ private double gravity;
+ private double speed;
+ private double speedEfficiency;
+ private boolean outboundAttractionDistribution;
+ private boolean adjustSizes;
+ private boolean barnesHutOptimize;
+ private double barnesHutTheta;
+ private boolean linLogMode;
+ private boolean strongGravityMode;
+ private boolean heatMapChangeMode;
+ private boolean heatMapChangeModeCB;
+ private boolean heatMapMode;
+ private boolean opacityMode;
+ private int threadCount;
+ private int currentThreadCount;
+ private Region rootRegion;
+ double outboundAttCompensation = 1;
+ private ExecutorService pool;
+ private double minWeight;
+ private double maxWeight;
+ private String nodeWeightColumnName;
+ private double nodeWeightScaling;
+
+
+
+ //by jj2018
+ private void setNodeWeightColumnName(Table table) {
+ if (table.hasColumn("weight")) {
+ this.nodeWeightColumnName = "weight";
+ }
+ if (table.hasColumn("weight_dynamic")) {
+ this.nodeWeightColumnName = "weight_dynamic";
+ }
+ }
+ //
+
+
+ public ForceAtlas2(ForceAtlas2Builder layoutBuilder) {
+ this.layoutBuilder = layoutBuilder;
+ this.threadCount = Math.min(4, Math.max(1, Runtime.getRuntime().availableProcessors() - 1));
+ }
+
+ @Override
+ public void initAlgo() {
+ AbstractLayout.ensureSafeLayoutNodePositions(graphModel);
+
+ speed = 1.;
+ speedEfficiency = 1.;
+
+
+ Table table = graphModel.getNodeTable();
+
+ //by jj2018
+ if (!(table.hasColumn("weight") || table.hasColumn("weight_dynamic"))) {
+ table.addColumn("weight", Double.class);
+ }
+
+ setNodeWeightColumnName(table);
+
+ if (!(table.getColumn(nodeWeightColumnName).getTypeClass()==Double.class || (table.getColumn(nodeWeightColumnName).getTypeClass()==TimestampDoubleMap.class))) {
+
+ NotifyDescriptor d = new NotifyDescriptor.Message("The Node Weight plugin requires types\n Double or TimestampDoubleMap", NotifyDescriptor.INFORMATION_MESSAGE);
+ DialogDisplayer.getDefault().notify(d);
+
+
+ }
+ //
+
+
+
+ graph = graphModel.getGraphVisible();
+
+ graph.readLock();
+ try {
+ Node[] nodes = graph.getNodes().toArray();
+
+ // Initialise layout data
+ for (Node n : nodes) {
+ if (n.getLayoutData() == null || !(n.getLayoutData() instanceof ForceAtlas2LayoutData)) {
+ ForceAtlas2LayoutData nLayout = new ForceAtlas2LayoutData();
+ n.setLayoutData(nLayout);
+ }
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+ nLayout.mass = 1 + graph.getDegree(n);
+ nLayout.old_dx = 0;
+ nLayout.old_dy = 0;
+ nLayout.dx = 0;
+ nLayout.dy = 0;
+ }
+
+
+
+
+ pool = Executors.newFixedThreadPool(threadCount);
+ currentThreadCount = threadCount;
+ } finally {
+ graph.readUnlockAll();
+ }
+
+}
+
+
+
+
+ private double getEdgeWeight(Edge edge, boolean isDynamicWeight, Interval interval) {
+ if (isDynamicWeight) {
+ return edge.getWeight(interval);
+ } else {
+ return edge.getWeight();
+ }
+ }
+ // jj2018
+ // hasNodeWeightStop refers to 'has node weight.' (full stop)
+ private boolean hasNodeWeightStop(Node node) {
+ if (graphModel.getNodeTable().hasColumn("weight") && node.getAttribute("weight") != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ private boolean hasNodeWeightDynamic(Node node) {
+ if (graphModel.getNodeTable().hasColumn("weight_dynamic") && node.getAttribute("weight_dynamic") != null) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ private boolean hasNodeWeight(Node node) {
+ if (hasNodeWeightStop(node) || hasNodeWeightDynamic(node)){
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private double getNodeWeightChange(Node node, boolean isDynamicNodeWeight, Interval interval, Double weight) {
+ if (interval.getLow() != Interval.INFINITY_INTERVAL.getLow()) {
+ TimestampMap map = (TimestampMap) node.getAttribute(this.nodeWeightColumnName);
+ Interval prev_interval = new Interval (Interval.INFINITY_INTERVAL.getLow(), interval.getLow());
+ Double last_value = (Double) map.get(prev_interval, Estimator.LAST);
+ Double change = weight - last_value;
+ return change;
+ }
+ return 0.0;
+ }
+
+ private double getNodeWeight(Node node, boolean isDynamicNodeWeight, Interval interval) {
+
+ if (isDynamicNodeWeight) {
+ // node_weight = (Double) n.getAttribute("gravity_x");
+ TimestampMap map = (TimestampMap) node.getAttribute(this.nodeWeightColumnName);
+ // Estimator estimator = (Estimator) AVERAGE;
+
+ Double value = (Double) map.get(interval, Estimator.AVERAGE);
+ if (value == null) {
+ Interval prev_interval = new Interval (Interval.INFINITY_INTERVAL.getLow(), interval.getLow());
+ value = (Double) map.get(prev_interval, Estimator.LAST);
+ if (value == null) {
+ value = 0.0;
+ }
+ }
+ return value;
+ } else {
+ return (Double) node.getAttribute(this.nodeWeightColumnName);
+ }
+ }
+ //
+
+
+
+
+ @Override
+ public void goAlgo() {
+ // Initialize graph data
+ if (graphModel == null) {
+ return;
+ }
+
+ graph = graphModel.getGraphVisible();
+
+ graph.readLock();
+ boolean isDynamicWeight = graphModel.getEdgeTable().getColumn("weight").isDynamic();
+
+ //by jj2018
+ boolean isDynamicNodeWeight = graphModel.getNodeTable().getColumn(this.nodeWeightColumnName).isDynamic();
+ //
+
+ Interval interval = graph.getView().getTimeInterval();
+
+ try {
+ Node[] nodes = graph.getNodes().toArray();
+ Edge[] edges = graph.getEdges().toArray();
+
+ //by jj2018
+ //minimums of node averages and of edges
+ Double min_avg_wt_n = 0.0;
+ Double min_wt_e = 0.0;
+ //
+
+ for (Edge e : edges) {
+
+ //by jj2018
+ // calculate minimum of edge weights
+ if (getEdgeWeightInfluence() != 0){
+ Double edge_wt = e.getWeight();
+ if (edge_wt < min_wt_e) {
+ min_wt_e = edge_wt;
+ }
+ }
+
+
+ // calculate minimum of node weight averages
+ Node a = e.getSource();
+ Node b = e.getTarget();
+ if (hasNodeWeight(a) && hasNodeWeight(b)){
+ Double wta = getNodeWeight(a, isDynamicNodeWeight, interval);
+ Double wtb = getNodeWeight(b, isDynamicNodeWeight, interval);
+ Double avg_wt_n = (wta + wtb)/2;
+ if (avg_wt_n < min_avg_wt_n) {
+ min_avg_wt_n = avg_wt_n;
+ }
+ }
+
+ // anchor nodes: counteract weight negativity;
+ // negative attraction acts as repulsion and nodes can be pushed out of bounds
+
+ // account for minimum node weight average value
+ Double potential_gravity_e = 2*(-(min_wt_e) + 10);
+ if (min_wt_e < 0.0 && getGravity() < potential_gravity_e) {
+ setGravity(potential_gravity_e);
+ }
+ // account for minimum edge weight average value
+ Double potential_gravity_n = -(min_avg_wt_n) + 10;
+ if (min_avg_wt_n < 0.0 && getGravity() < potential_gravity_n) {
+ setGravity(potential_gravity_n);
+ }
+
+ // combine the adjusted gravity for if both edges and nodes have
+ // significant negatives
+ if (min_wt_e < 0 && min_avg_wt_n < 0){
+ Double potential_gravity_combined = (potential_gravity_n + potential_gravity_e);
+ if (getGravity() < potential_gravity_combined) {
+ setGravity(potential_gravity_combined);
+ }
+ }
+
+
+ }
+ //
+
+
+ // Initialise layout data
+ for (Node n : nodes) {
+ if (n.getLayoutData() == null || !(n.getLayoutData() instanceof ForceAtlas2LayoutData)) {
+ ForceAtlas2LayoutData nLayout = new ForceAtlas2LayoutData();
+ n.setLayoutData(nLayout);
+ }
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+ nLayout.mass = 1 + graph.getDegree(n);
+ nLayout.old_dx = nLayout.dx;
+ nLayout.old_dy = nLayout.dy;
+ nLayout.dx = 0;
+ nLayout.dy = 0;
+ }
+
+ // If Barnes Hut active, initialize root region
+ if (isBarnesHutOptimize()) {
+ rootRegion = new Region(nodes);
+ rootRegion.buildSubRegions();
+ }
+
+ // If outboundAttractionDistribution active, compensate.
+ if (isOutboundAttractionDistribution()) {
+ outboundAttCompensation = 0;
+ for (Node n : nodes) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+ outboundAttCompensation += nLayout.mass;
+ }
+ outboundAttCompensation /= nodes.length;
+ }
+
+ // Repulsion (and gravity)
+ // NB: Muti-threaded
+ RepulsionForce Repulsion = ForceFactory.builder.buildRepulsion(isAdjustSizes(), getScalingRatio());
+
+ int taskCount = 8 * currentThreadCount; // The threadPool Executor Service will manage the fetching of tasks and threads.
+ // We make more tasks than threads because some tasks may need more time to compute.
+ ArrayList threads = new ArrayList();
+ for (int t = taskCount; t > 0; t--) {
+ int from = (int) Math.floor(nodes.length * (t - 1) / taskCount);
+ int to = (int) Math.floor(nodes.length * t / taskCount);
+ Future future = pool.submit(new NodesThread(nodes, from, to, isBarnesHutOptimize(), getBarnesHutTheta(), getGravity(), (isStrongGravityMode()) ? (ForceFactory.builder.getStrongGravity(getScalingRatio())) : (Repulsion), getScalingRatio(), rootRegion, Repulsion));
+ threads.add(future);
+ }
+ for (Future future : threads) {
+ try {
+ future.get();
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to layout " + this.getClass().getSimpleName() + ".", e);
+ }
+ }
+
+ // Attraction
+ // Edge Weight
+ AttractionForce Attraction = ForceFactory.builder.buildAttraction(isLinLogMode(), isOutboundAttractionDistribution(), isAdjustSizes(), 1 * ((isOutboundAttractionDistribution()) ? (outboundAttCompensation) : (1)));
+ if (getEdgeWeightInfluence() == 0) {
+ for (Edge e : edges) {
+ Attraction.apply(e.getSource(), e.getTarget(), 1);
+ }
+ } else if (getEdgeWeightInfluence() == 1) {
+ for (Edge e : edges) {
+ Attraction.apply(e.getSource(), e.getTarget(), getEdgeWeight(e, isDynamicWeight, interval));
+ }
+ } else {
+ for (Edge e : edges) {
+ Attraction.apply(e.getSource(), e.getTarget(), Math.pow(getEdgeWeight(e, isDynamicWeight, interval), getEdgeWeightInfluence()));
+ }
+ }
+
+ //by jj2018
+ // Node Weight
+ AttractionForce NodeAttraction = ForceFactory.builder.buildAttraction(isLinLogMode(), isOutboundAttractionDistribution(), isAdjustSizes(), 1 * ((isOutboundAttractionDistribution()) ? (outboundAttCompensation) : (1)));
+
+
+ for (Edge e : edges) {
+ Node node_a = e.getSource();
+ Node node_b = e.getTarget();
+
+
+ if (hasNodeWeight(node_a) && hasNodeWeight(node_b)) {
+ Double wt_a = 0.0;
+ Double wt_b = 0.0;
+ wt_a = getNodeWeight(node_a, isDynamicNodeWeight, interval);
+ wt_b = getNodeWeight(node_b, isDynamicNodeWeight, interval);
+
+
+
+ // red-blue heat map mode
+ if (isHeatMapMode()) {
+
+ HeatmapFeatures.adjustColorWeight(node_a, wt_a);
+ HeatmapFeatures.adjustColorWeight(node_b, wt_b);
+ }
+
+ // red-green change-oriented heat map mode
+ if (isHeatMapChangeMode() && isDynamicNodeWeight) {
+
+
+ Double change_a = getNodeWeightChange(node_a, isDynamicNodeWeight, interval, wt_a);
+ Double change_b = getNodeWeightChange(node_b, isDynamicNodeWeight, interval, wt_b);
+ HeatmapFeatures.adjustColorChange(node_a, change_a);
+ HeatmapFeatures.adjustColorChange(node_b, change_b);
+
+
+ }
+ // blue-yellow change-oriented heat map mode
+ if (isHeatMapChangeModeCB() && isDynamicNodeWeight) {
+
+ Double change_a = getNodeWeightChange(node_a, isDynamicNodeWeight, interval, wt_a);
+ Double change_b = getNodeWeightChange(node_b, isDynamicNodeWeight, interval, wt_b);
+ HeatmapFeatures.adjustColorblindChange(node_a, change_a);
+ HeatmapFeatures.adjustColorblindChange(node_b, change_b);
+
+
+ }
+
+ // Opacity applied last so both can be used
+ if (isOpacityMode()){
+ HeatmapFeatures.adjustOpacity(node_a, wt_a);
+ HeatmapFeatures.adjustOpacity(node_b, wt_b);
+ }
+
+
+
+ // Node forces applied
+ NodeAttraction.apply(node_a, node_b, (getNodeWeightScaling()*(wt_a + wt_b)/2)); }
+
+ }
+ //
+
+
+
+ if (getEdgeWeightInfluence() == 0) {
+ for (Edge e : edges) {
+ Attraction.apply(e.getSource(), e.getTarget(), 1);
+ }
+ } else if (getEdgeWeightInfluence() == 1) {
+ for (Edge e : edges) {
+ Attraction.apply(e.getSource(), e.getTarget(), getEdgeWeight(e, isDynamicWeight, interval));
+ }
+ } else {
+ for (Edge e : edges) {
+ Attraction.apply(e.getSource(), e.getTarget(), Math.pow(getEdgeWeight(e, isDynamicWeight, interval), getEdgeWeightInfluence()));
+ }
+ }
+
+ // Auto adjust speed
+ double totalSwinging = 0d; // How much irregular movement
+ double totalEffectiveTraction = 0d; // Hom much useful movement
+ for (Node n : nodes) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+ if (!n.isFixed()) {
+ double swinging = Math.sqrt(Math.pow(nLayout.old_dx - nLayout.dx, 2) + Math.pow(nLayout.old_dy - nLayout.dy, 2));
+ totalSwinging += nLayout.mass * swinging; // If the node has a burst change of direction, then it's not converging.
+ totalEffectiveTraction += nLayout.mass * 0.5 * Math.sqrt(Math.pow(nLayout.old_dx + nLayout.dx, 2) + Math.pow(nLayout.old_dy + nLayout.dy, 2));
+ }
+ }
+ // We want that swingingMovement < tolerance * convergenceMovement
+
+ // Optimize jitter tolerance
+ // The 'right' jitter tolerance for this network. Bigger networks need more tolerance. Denser networks need less tolerance. Totally empiric.
+ double estimatedOptimalJitterTolerance = 0.05 * Math.sqrt(nodes.length);
+ double minJT = Math.sqrt(estimatedOptimalJitterTolerance);
+ double maxJT = 10;
+ double jt = jitterTolerance * Math.max(minJT, Math.min(maxJT, estimatedOptimalJitterTolerance * totalEffectiveTraction / Math.pow(nodes.length, 2)));
+
+ double minSpeedEfficiency = 0.05;
+
+ // Protection against erratic behavior
+ if (totalSwinging / totalEffectiveTraction > 2.0) {
+ if (speedEfficiency > minSpeedEfficiency) {
+ speedEfficiency *= 0.5;
+ }
+ jt = Math.max(jt, jitterTolerance);
+ }
+
+ double targetSpeed = jt * speedEfficiency * totalEffectiveTraction / totalSwinging;
+
+ // Speed efficiency is how the speed really corresponds to the swinging vs. convergence tradeoff
+ // We adjust it slowly and carefully
+ if (totalSwinging > jt * totalEffectiveTraction) {
+ if (speedEfficiency > minSpeedEfficiency) {
+ speedEfficiency *= 0.7;
+ }
+ } else if (speed < 1000) {
+ speedEfficiency *= 1.3;
+ }
+
+ // But the speed shoudn't rise too much too quickly, since it would make the convergence drop dramatically.
+ double maxRise = 0.5; // Max rise: 50%
+ speed = speed + Math.min(targetSpeed - speed, maxRise * speed);
+
+ // Apply forces
+ if (isAdjustSizes()) {
+ // If nodes overlap prevention is active, it's not possible to trust the swinging mesure.
+ for (Node n : nodes) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+ if (!n.isFixed()) {
+
+ // Adaptive auto-speed: the speed of each node is lowered
+ // when the node swings.
+ double swinging = nLayout.mass * Math.sqrt((nLayout.old_dx - nLayout.dx) * (nLayout.old_dx - nLayout.dx) + (nLayout.old_dy - nLayout.dy) * (nLayout.old_dy - nLayout.dy));
+ double factor = 0.1 * speed / (1f + Math.sqrt(speed * swinging));
+
+ double df = Math.sqrt(Math.pow(nLayout.dx, 2) + Math.pow(nLayout.dy, 2));
+ factor = Math.min(factor * df, 10.) / df;
+
+ double x = n.x() + nLayout.dx * factor;
+ double y = n.y() + nLayout.dy * factor;
+
+ n.setX((float) x);
+ n.setY((float) y);
+ }
+ }
+ } else {
+ for (Node n : nodes) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+ if (!n.isFixed()) {
+
+ // Adaptive auto-speed: the speed of each node is lowered
+ // when the node swings.
+ double swinging = nLayout.mass * Math.sqrt((nLayout.old_dx - nLayout.dx) * (nLayout.old_dx - nLayout.dx) + (nLayout.old_dy - nLayout.dy) * (nLayout.old_dy - nLayout.dy));
+ //double factor = speed / (1f + Math.sqrt(speed * swinging));
+ double factor = speed / (1f + Math.sqrt(speed * swinging));
+
+ double x = n.x() + nLayout.dx * factor;
+ double y = n.y() + nLayout.dy * factor;
+
+ n.setX((float) x);
+ n.setY((float) y);
+ }
+ }
+ }
+ } finally {
+ graph.readUnlockAll();
+ }
+ }
+
+
+ @Override
+ public boolean canAlgo() {
+ return graphModel != null;
+ }
+
+ @Override
+ public void endAlgo() {
+ graph.readLock();
+ try {
+ for (Node n : graph.getNodes()) {
+ n.setLayoutData(null);
+ }
+ pool.shutdown();
+ } finally {
+ graph.readUnlockAll();
+ }
+ }
+
+
+
+ @Override
+ public LayoutProperty[] getProperties() {
+ List properties = new ArrayList();
+ final String FORCEATLAS2_TUNING = NbBundle.getMessage(getClass(), "ForceAtlas2.tuning");
+ final String FORCEATLAS2_BEHAVIOR = NbBundle.getMessage(getClass(), "ForceAtlas2.behavior");
+ final String FORCEATLAS2_PERFORMANCE = NbBundle.getMessage(getClass(), "ForceAtlas2.performance");
+ final String FORCEATLAS2_THREADS = NbBundle.getMessage(getClass(), "ForceAtlas2.threads");
+
+ try {
+ properties.add(LayoutProperty.createProperty(
+ this, Double.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.scalingRatio.name"),
+ FORCEATLAS2_TUNING,
+ "ForceAtlas2.scalingRatio.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.scalingRatio.desc"),
+ "getScalingRatio", "setScalingRatio"));
+ properties.add(LayoutProperty.createProperty(
+ this, Double.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.nodeWeightScaling.name"),
+ FORCEATLAS2_TUNING,
+ "ForceAtlas2.nodeWeightScaling.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.nodeWeightScaling.desc"),
+ "getNodeWeightScaling", "setNodeWeightScaling"));
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.strongGravityMode.name"),
+ FORCEATLAS2_TUNING,
+ "ForceAtlas2.strongGravityMode.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.strongGravityMode.desc"),
+ "isStrongGravityMode", "setStrongGravityMode"));
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.opacityMode.name"),
+ FORCEATLAS2_TUNING,
+ "ForceAtlas2.opacityMode.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.opacityMode.desc"),
+ "isOpacityMode", "setOpacityMode"));
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.heatMapMode.name"),
+ FORCEATLAS2_TUNING,
+ "ForceAtlas2.heatMapMode.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.heatMapMode.desc"),
+ "isHeatMapMode", "setHeatMapMode"));
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.heatMapChangeMode.name"),
+ FORCEATLAS2_TUNING,
+ "ForceAtlas2.heatMapChangeMode.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.heatMapChangeMode.desc"),
+ "isHeatMapChangeMode", "setHeatMapChangeMode"));
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.heatMapChangeModeCB.name"),
+ FORCEATLAS2_TUNING,
+ "ForceAtlas2.heatMapChangeModeCB.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.heatMapChangeModeCB.desc"),
+ "isHeatMapChangeModeCB", "setHeatMapChangeModeCB"));
+ properties.add(LayoutProperty.createProperty(
+ this, Double.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.gravity.name"),
+ FORCEATLAS2_TUNING,
+ "ForceAtlas2.gravity.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.gravity.desc"),
+ "getGravity", "setGravity"));
+
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.distributedAttraction.name"),
+ FORCEATLAS2_BEHAVIOR,
+ "ForceAtlas2.distributedAttraction.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.distributedAttraction.desc"),
+ "isOutboundAttractionDistribution", "setOutboundAttractionDistribution"));
+
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.linLogMode.name"),
+ FORCEATLAS2_BEHAVIOR,
+ "ForceAtlas2.linLogMode.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.linLogMode.desc"),
+ "isLinLogMode", "setLinLogMode"));
+
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.adjustSizes.name"),
+ FORCEATLAS2_BEHAVIOR,
+ "ForceAtlas2.adjustSizes.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.adjustSizes.desc"),
+ "isAdjustSizes", "setAdjustSizes"));
+
+ properties.add(LayoutProperty.createProperty(
+ this, Double.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.edgeWeightInfluence.name"),
+ FORCEATLAS2_BEHAVIOR,
+ "ForceAtlas2.edgeWeightInfluence.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.edgeWeightInfluence.desc"),
+ "getEdgeWeightInfluence", "setEdgeWeightInfluence"));
+
+ properties.add(LayoutProperty.createProperty(
+ this, Double.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.jitterTolerance.name"),
+ FORCEATLAS2_PERFORMANCE,
+ "ForceAtlas2.jitterTolerance.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.jitterTolerance.desc"),
+ "getJitterTolerance", "setJitterTolerance"));
+
+ properties.add(LayoutProperty.createProperty(
+ this, Boolean.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutOptimization.name"),
+ FORCEATLAS2_PERFORMANCE,
+ "ForceAtlas2.barnesHutOptimization.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutOptimization.desc"),
+ "isBarnesHutOptimize", "setBarnesHutOptimize"));
+
+ properties.add(LayoutProperty.createProperty(
+ this, Double.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutTheta.name"),
+ FORCEATLAS2_PERFORMANCE,
+ "ForceAtlas2.barnesHutTheta.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.barnesHutTheta.desc"),
+ "getBarnesHutTheta", "setBarnesHutTheta"));
+
+ properties.add(LayoutProperty.createProperty(
+ this, Integer.class,
+ NbBundle.getMessage(getClass(), "ForceAtlas2.threads.name"),
+ FORCEATLAS2_THREADS,
+ "ForceAtlas2.threads.name",
+ NbBundle.getMessage(getClass(), "ForceAtlas2.threads.desc"),
+ "getThreadsCount", "setThreadsCount"));
+
+ } catch (Exception e) {
+ Exceptions.printStackTrace(e);
+ }
+
+ return properties.toArray(new LayoutProperty[0]);
+ }
+
+ @Override
+ public void resetPropertiesValues() {
+ int nodesCount = 0;
+
+ if (graphModel != null) {
+ nodesCount = graphModel.getGraphVisible().getNodeCount();
+ }
+
+ // Tuning
+ if (nodesCount >= 100) {
+ setScalingRatio(2.0);
+ } else {
+ setScalingRatio(10.0);
+ }
+ // strong gravity a default to counteract runaway nodes
+ //by jj2018
+ setStrongGravityMode(true);
+ setNodeWeightScaling(1.);
+ setHeatMapMode(false);
+ setHeatMapChangeMode(false);
+ setHeatMapChangeModeCB(false);
+ setOpacityMode(false);
+ //
+ setGravity(1.);
+
+
+
+ // Behavior
+ setOutboundAttractionDistribution(false);
+ setLinLogMode(false);
+ setAdjustSizes(false);
+ setEdgeWeightInfluence(1.);
+
+ // Performance
+ setJitterTolerance(1d);
+ if (nodesCount >= 1000) {
+ setBarnesHutOptimize(true);
+ } else {
+ setBarnesHutOptimize(false);
+ }
+ setBarnesHutTheta(1.2);
+ setThreadsCount(Math.max(1, Runtime.getRuntime().availableProcessors() - 1));
+ }
+
+
+
+ @Override
+ public LayoutBuilder getBuilder() {
+ return layoutBuilder;
+ }
+
+ @Override
+ public void setGraphModel(GraphModel graphModel) {
+ this.graphModel = graphModel;
+ // Trick: reset here to take the profile of the graph in account for default values
+ resetPropertiesValues();
+ }
+
+ public Double getBarnesHutTheta() {
+ return barnesHutTheta;
+ }
+
+ public void setBarnesHutTheta(Double barnesHutTheta) {
+ this.barnesHutTheta = barnesHutTheta;
+ }
+
+ public Double getEdgeWeightInfluence() {
+ return edgeWeightInfluence;
+ }
+
+ public void setEdgeWeightInfluence(Double edgeWeightInfluence) {
+ this.edgeWeightInfluence = edgeWeightInfluence;
+ }
+
+ public Double getJitterTolerance() {
+ return jitterTolerance;
+ }
+
+ public void setJitterTolerance(Double jitterTolerance) {
+ this.jitterTolerance = jitterTolerance;
+ }
+
+ public Boolean isLinLogMode() {
+ return linLogMode;
+ }
+
+ public void setLinLogMode(Boolean linLogMode) {
+ this.linLogMode = linLogMode;
+ }
+
+ public Double getScalingRatio() {
+ return scalingRatio;
+ }
+
+ public void setScalingRatio(Double scalingRatio) {
+ this.scalingRatio = scalingRatio;
+ }
+ //by jj2018
+ public Boolean isStrongGravityMode() {
+ return strongGravityMode;
+ }
+
+ public void setStrongGravityMode(Boolean strongGravityMode) {
+ this.strongGravityMode = strongGravityMode;
+ }
+
+ public Boolean isOpacityMode() {
+ return opacityMode;
+ }
+
+ public void setOpacityMode(Boolean opacityMode) {
+ this.opacityMode = opacityMode;
+ }
+
+ public Boolean isHeatMapMode() {
+ return heatMapMode;
+ }
+
+ public void setHeatMapMode(Boolean heatMapMode) {
+ this.heatMapMode = heatMapMode;
+ }
+
+ public Boolean isHeatMapChangeMode() {
+ return heatMapChangeMode;
+ }
+
+ public void setHeatMapChangeMode(Boolean heatMapChangeMode) {
+ this.heatMapChangeMode = heatMapChangeMode;
+ }
+
+ public Boolean isHeatMapChangeModeCB() {
+ return heatMapChangeModeCB;
+ }
+
+ public void setHeatMapChangeModeCB(Boolean heatMapChangeModeCB) {
+ this.heatMapChangeModeCB = heatMapChangeModeCB;
+ }
+
+ public Double getNodeWeightScaling() {
+ return nodeWeightScaling;
+ }
+
+ public void setNodeWeightScaling(Double factor) {
+ this.nodeWeightScaling = factor;
+ }
+
+ //
+
+ public Double getGravity() {
+ return gravity;
+ }
+
+ public void setGravity(Double gravity) {
+ this.gravity = gravity;
+ }
+
+ public Integer getThreadsCount() {
+ return threadCount;
+ }
+
+ public void setThreadsCount(Integer threadCount) {
+ this.threadCount = Math.max(1, threadCount);
+ }
+
+ public Boolean isOutboundAttractionDistribution() {
+ return outboundAttractionDistribution;
+ }
+
+ public void setOutboundAttractionDistribution(Boolean outboundAttractionDistribution) {
+ this.outboundAttractionDistribution = outboundAttractionDistribution;
+ }
+
+ public Boolean isAdjustSizes() {
+ return adjustSizes;
+ }
+
+ public void setAdjustSizes(Boolean adjustSizes) {
+ this.adjustSizes = adjustSizes;
+ }
+
+ public Boolean isBarnesHutOptimize() {
+ return barnesHutOptimize;
+ }
+
+ public void setBarnesHutOptimize(Boolean barnesHutOptimize) {
+ this.barnesHutOptimize = barnesHutOptimize;
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2Builder.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2Builder.java
new file mode 100644
index 0000000..cd8ab06
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2Builder.java
@@ -0,0 +1,105 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2011 Gephi Consortium. All rights reserved.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 3 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://gephi.org/about/legal/license-notice/
+or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+specific language governing permissions and limitations under the
+License. When distributing the software, include this License Header
+Notice in each file and include the License files at
+/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 3, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 3] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 3 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 3 code and therefore, elected the GPL
+Version 3 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+
+Contributor(s):
+
+Portions Copyrighted 2011 Gephi Consortium.
+ */
+package org.gephi.layout.plugins.layout.NodeWeight;
+// package org.gephi.layout.plugin.NodeWeight;
+
+import javax.swing.Icon;
+import javax.swing.JPanel;
+import org.gephi.layout.spi.Layout;
+import org.gephi.layout.spi.LayoutBuilder;
+import org.gephi.layout.spi.LayoutUI;
+import org.openide.util.NbBundle;
+import org.openide.util.lookup.ServiceProvider;
+
+/**
+ * Layout Builder
+ * @author Mathieu Jacomy
+ */
+@ServiceProvider(service = LayoutBuilder.class)
+public class ForceAtlas2Builder implements LayoutBuilder {
+
+ private ForceAtlas2UI ui = new ForceAtlas2UI();
+
+ @Override
+ public String getName() {
+ return NbBundle.getMessage(ForceAtlas2.class, "NodeWeight.name");
+ }
+
+ @Override
+ public LayoutUI getUI() {
+ return ui;
+ }
+
+ @Override
+ public ForceAtlas2 buildLayout() {
+ ForceAtlas2 layout = new ForceAtlas2(this);
+ return layout;
+ }
+
+ private class ForceAtlas2UI implements LayoutUI {
+
+ @Override
+ public String getDescription() {
+ return NbBundle.getMessage(ForceAtlas2.class, "NodeWeight.description");
+ }
+
+ @Override
+ public Icon getIcon() {
+ return null;
+ }
+
+ @Override
+ public JPanel getSimplePanel(Layout layout) {
+ return null;
+ }
+
+ @Override
+ public int getQualityRank() {
+ return 4;
+ }
+
+ @Override
+ public int getSpeedRank() {
+ return 4;
+ }
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2LayoutData.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2LayoutData.java
new file mode 100644
index 0000000..ddeb8c7
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceAtlas2LayoutData.java
@@ -0,0 +1,61 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2011 Gephi Consortium. All rights reserved.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 3 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://gephi.org/about/legal/license-notice/
+or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+specific language governing permissions and limitations under the
+License. When distributing the software, include this License Header
+Notice in each file and include the License files at
+/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 3, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 3] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 3 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 3 code and therefore, elected the GPL
+Version 3 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+
+Contributor(s):
+
+Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+import org.gephi.graph.spi.LayoutData;
+
+/**
+ * Data stored in Nodes and used by ForceAtlas2
+ * @author Mathieu Jacomy
+ */
+public class ForceAtlas2LayoutData implements LayoutData {
+ //Data
+
+
+ public double dx = 0;
+ public double dy = 0;
+ public double old_dx = 0;
+ public double old_dy = 0;
+ public double mass = 1;
+ // weight
+ public double weight = 0;
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceFactory.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceFactory.java
new file mode 100644
index 0000000..7a9639e
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/ForceFactory.java
@@ -0,0 +1,577 @@
+/*
+ Copyright 2008-2011 Gephi
+ Authors : Mathieu Jacomy
+ Website : http://www.gephi.org
+
+ This file is part of Gephi.
+
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+ Copyright 2011 Gephi Consortium. All rights reserved.
+
+ The contents of this file are subject to the terms of either the GNU
+ General Public License Version 3 only ("GPL") or the Common
+ Development and Distribution License("CDDL") (collectively, the
+ "License"). You may not use this file except in compliance with the
+ License. You can obtain a copy of the License at
+ http://gephi.org/about/legal/license-notice/
+ or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+ specific language governing permissions and limitations under the
+ License. When distributing the software, include this License Header
+ Notice in each file and include the License files at
+ /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+ License Header, with the fields enclosed by brackets [] replaced by
+ your own identifying information:
+ "Portions Copyrighted [year] [name of copyright owner]"
+
+ If you wish your version of this file to be governed by only the CDDL
+ or only the GPL Version 3, indicate your decision by adding
+ "[Contributor] elects to include this software in this distribution
+ under the [CDDL or GPL Version 3] license." If you do not indicate a
+ single choice of license, a recipient has the option to distribute
+ your version of this file under either the CDDL, the GPL Version 3 or
+ to extend the choice of license to its licensees as provided above.
+ However, if you add GPL Version 3 code and therefore, elected the GPL
+ Version 3 license, then the option applies only if the new code is
+ made subject to such option by the copyright holder.
+
+ Contributor(s):
+
+ Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+import org.gephi.graph.api.Node;
+
+/**
+ * Generates the forces on demand, here are all the formulas for attraction and
+ * repulsion.
+ *
+ * @author Mathieu Jacomy
+ */
+public class ForceFactory {
+
+ public static ForceFactory builder = new ForceFactory();
+
+ private ForceFactory() {
+ }
+
+ public RepulsionForce buildRepulsion(boolean adjustBySize, double coefficient) {
+ if (adjustBySize) {
+ return new linRepulsion_antiCollision(coefficient);
+ } else {
+ return new linRepulsion(coefficient);
+ }
+ }
+
+ public RepulsionForce getStrongGravity(double coefficient) {
+ return new strongGravity(coefficient);
+ }
+
+ public AttractionForce buildAttraction(boolean logAttraction, boolean distributedAttraction, boolean adjustBySize, double coefficient) {
+ if (adjustBySize) {
+ if (logAttraction) {
+ if (distributedAttraction) {
+ return new logAttraction_degreeDistributed_antiCollision(coefficient);
+ } else {
+ return new logAttraction_antiCollision(coefficient);
+ }
+ } else {
+ if (distributedAttraction) {
+ return new linAttraction_degreeDistributed_antiCollision(coefficient);
+ } else {
+ return new linAttraction_antiCollision(coefficient);
+ }
+ }
+ } else {
+ if (logAttraction) {
+ if (distributedAttraction) {
+ return new logAttraction_degreeDistributed(coefficient);
+ } else {
+ return new logAttraction(coefficient);
+ }
+ } else {
+ if (distributedAttraction) {
+ return new linAttraction_massDistributed(coefficient);
+ } else {
+ return new linAttraction(coefficient);
+ }
+ }
+ }
+ }
+
+ public abstract class AttractionForce {
+
+ public abstract void apply(Node n1, Node n2, double e); // Model for node-node attraction (e is for edge weight if needed)
+ }
+
+ public abstract class RepulsionForce {
+
+ public abstract void apply(Node n1, Node n2); // Model for node-node repulsion
+
+ public abstract void apply(Node n, Region r); // Model for Barnes Hut approximation
+
+ public abstract void apply(Node n, double g); // Model for gravitation (anti-repulsion)
+ }
+
+ /*
+ * Repulsion force: Linear
+ */
+ private class linRepulsion extends RepulsionForce {
+
+ private double coefficient;
+
+ public linRepulsion(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = coefficient * n1Layout.mass * n2Layout.mass / distance / distance;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+
+ @Override
+ public void apply(Node n, Region r) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+
+ // Get the distance
+ double xDist = n.x() - r.getMassCenterX();
+ double yDist = n.y() - r.getMassCenterY();
+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = coefficient * nLayout.mass * r.getMass() / distance / distance;
+
+ nLayout.dx += xDist * factor;
+ nLayout.dy += yDist * factor;
+ }
+ }
+
+ @Override
+ public void apply(Node n, double g) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+
+ // Get the distance
+ double xDist = n.x();
+ double yDist = n.y();
+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = coefficient * nLayout.mass * g / distance;
+
+ nLayout.dx -= xDist * factor;
+ nLayout.dy -= yDist * factor;
+ }
+ }
+ }
+
+ /*
+ * Repulsion force: Strong Gravity (as a Repulsion Force because it is easier)
+ */
+ private class linRepulsion_antiCollision extends RepulsionForce {
+
+ private double coefficient;
+
+ public linRepulsion_antiCollision(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1.size() - n2.size();
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = coefficient * n1Layout.mass * n2Layout.mass / distance / distance;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+
+ } else if (distance < 0) {
+ double factor = 100 * coefficient * n1Layout.mass * n2Layout.mass;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+
+ @Override
+ public void apply(Node n, Region r) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+
+ // Get the distance
+ double xDist = n.x() - r.getMassCenterX();
+ double yDist = n.y() - r.getMassCenterY();
+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = coefficient * nLayout.mass * r.getMass() / distance / distance;
+
+ nLayout.dx += xDist * factor;
+ nLayout.dy += yDist * factor;
+ } else if (distance < 0) {
+ double factor = -coefficient * nLayout.mass * r.getMass() / distance;
+
+ nLayout.dx += xDist * factor;
+ nLayout.dy += yDist * factor;
+ }
+ }
+
+ @Override
+ public void apply(Node n, double g) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+
+ // Get the distance
+ double xDist = n.x();
+ double yDist = n.y();
+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = coefficient * nLayout.mass * g / distance;
+
+ nLayout.dx -= xDist * factor;
+ nLayout.dy -= yDist * factor;
+ }
+ }
+ }
+
+ private class strongGravity extends RepulsionForce {
+
+ private double coefficient;
+
+ public strongGravity(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2) {
+ // Not Relevant
+ }
+
+ @Override
+ public void apply(Node n, Region r) {
+ // Not Relevant
+ }
+
+ @Override
+ public void apply(Node n, double g) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+
+ // Get the distance
+ double xDist = n.x();
+ double yDist = n.y();
+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = coefficient * nLayout.mass * g;
+
+ nLayout.dx -= xDist * factor;
+ nLayout.dy -= yDist * factor;
+ }
+ }
+ }
+
+ /*
+ * Attraction force: Linear
+ */
+ private class linAttraction extends AttractionForce {
+
+ private double coefficient;
+
+ public linAttraction(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2, double e) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+
+ // NB: factor = force / distance
+ double factor = -coefficient * e;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+
+ /*
+ * Attraction force: Linear, distributed by mass (typically, degree)
+ */
+ private class linAttraction_massDistributed extends AttractionForce {
+
+ private double coefficient;
+
+ public linAttraction_massDistributed(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2, double e) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+
+ // NB: factor = force / distance
+ double factor = -coefficient * e / n1Layout.mass;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+
+ /*
+ * Attraction force: Logarithmic
+ */
+ private class logAttraction extends AttractionForce {
+
+ private double coefficient;
+
+ public logAttraction(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2, double e) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+ if (distance > 0) {
+
+ // NB: factor = force / distance
+ double factor = -coefficient * e * Math.log(1 + distance) / distance;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+ }
+
+ /*
+ * Attraction force: Linear, distributed by Degree
+ */
+ private class logAttraction_degreeDistributed extends AttractionForce {
+
+ private double coefficient;
+
+ public logAttraction_degreeDistributed(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2, double e) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+ double distance = (float) Math.sqrt(xDist * xDist + yDist * yDist);
+
+ if (distance > 0) {
+
+ // NB: factor = force / distance
+ double factor = -coefficient * e * Math.log(1 + distance) / distance / n1Layout.mass;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+ }
+
+ /*
+ * Attraction force: Linear, with Anti-Collision
+ */
+ private class linAttraction_antiCollision extends AttractionForce {
+
+ private double coefficient;
+
+ public linAttraction_antiCollision(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2, double e) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1.size() - n2.size();
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = -coefficient * e;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+ }
+
+ /*
+ * Attraction force: Linear, distributed by Degree, with Anti-Collision
+ */
+ private class linAttraction_degreeDistributed_antiCollision extends AttractionForce {
+
+ private double coefficient;
+
+ public linAttraction_degreeDistributed_antiCollision(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2, double e) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1.size() - n2.size();
+
+ if (distance > 0) {
+ // NB: factor = force / distance
+ double factor = -coefficient * e / n1Layout.mass;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+ }
+
+ /*
+ * Attraction force: Logarithmic, with Anti-Collision
+ */
+ private class logAttraction_antiCollision extends AttractionForce {
+
+ private double coefficient;
+
+ public logAttraction_antiCollision(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2, double e) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1.size() - n2.size();
+
+ if (distance > 0) {
+
+ // NB: factor = force / distance
+ double factor = -coefficient * e * Math.log(1 + distance) / distance;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+ }
+
+ /*
+ * Attraction force: Linear, distributed by Degree, with Anti-Collision
+ */
+ private class logAttraction_degreeDistributed_antiCollision extends AttractionForce {
+
+ private double coefficient;
+
+ public logAttraction_degreeDistributed_antiCollision(double c) {
+ coefficient = c;
+ }
+
+ @Override
+ public void apply(Node n1, Node n2, double e) {
+ ForceAtlas2LayoutData n1Layout = n1.getLayoutData();
+ ForceAtlas2LayoutData n2Layout = n2.getLayoutData();
+
+ // Get the distance
+ double xDist = n1.x() - n2.x();
+ double yDist = n1.y() - n2.y();
+ double distance = Math.sqrt(xDist * xDist + yDist * yDist) - n1.size() - n2.size();
+
+ if (distance > 0) {
+
+ // NB: factor = force / distance
+ double factor = -coefficient * e * Math.log(1 + distance) / distance / n1Layout.mass;
+
+ n1Layout.dx += xDist * factor;
+ n1Layout.dy += yDist * factor;
+
+ n2Layout.dx -= xDist * factor;
+ n2Layout.dy -= yDist * factor;
+ }
+ }
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/HeatmapFeatures.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/HeatmapFeatures.java
new file mode 100644
index 0000000..f5f5be2
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/HeatmapFeatures.java
@@ -0,0 +1,202 @@
+
+// Appearance features for node weight plugin
+// Submitted in partial fulfillment for the Imperial Computing MSc
+// Authors : Julianne Joswiak
+
+package org.gephi.layout.plugins.layout.NodeWeight;
+
+import java.awt.Color;
+
+
+import org.gephi.graph.api.Edge;
+import org.gephi.graph.api.Element;
+import org.gephi.graph.api.Graph;
+import org.gephi.graph.api.GraphModel;
+import org.gephi.graph.api.Node;
+import org.gephi.graph.api.Table;
+import org.gephi.graph.api.Estimator;
+import org.gephi.graph.api.Interval;
+import org.gephi.graph.api.types.IntervalMap;
+import org.gephi.graph.api.types.TimeMap;
+import org.gephi.graph.api.types.TimestampMap;
+import org.gephi.graph.api.types.TimestampDoubleMap;
+import org.gephi.graph.api.Interval;
+
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.AttractionForce;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.RepulsionForce;
+
+
+import org.gephi.layout.spi.Layout;
+import org.gephi.layout.spi.LayoutBuilder;
+import org.gephi.layout.spi.LayoutProperty;
+
+
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.Comparator;
+import java.util.Map;
+import java.util.Arrays;
+
+
+
+import org.openide.util.Exceptions;
+import org.openide.util.NbBundle;
+import org.openide.NotifyDescriptor;
+import org.openide.DialogDisplayer;
+
+import org.gephi.layout.plugins.layout.NodeWeight.AbstractLayout;
+
+
+
+
+
+public class HeatmapFeatures {
+
+ private static Color colorBlender(Color a, Color b, double proportion){
+ float propA = (float) proportion;
+ float propB = 1.0f - propA;
+
+ float rgbA[] = new float[3];
+ float rgbB[] = new float[3];
+
+ a.getColorComponents(rgbA);
+ b.getColorComponents(rgbB);
+
+ Color blend_color = new Color (rgbA[0] * propA + rgbB[0] * propB,
+ rgbA[1] * propA + rgbB[1] * propB, rgbA[2] * propA + rgbB[2] * propB);
+
+ return blend_color;
+ };
+
+ private static void adjustColor(Node node, Double value, Color lo, Color md, Color hi){
+
+ Color low = lo;
+ Color mid = md;
+ Color high = hi;
+
+ if (value >= -3.0 && value < -2.5) {
+ node.setColor(low);
+ } if (value >= -2.5 && value < -2.0) {
+ node.setColor(colorBlender(low, mid, (1-0.16667)));
+ } if (value >= -2.0 && value < -1.5) {
+ node.setColor(colorBlender(low, mid, (1-(2*0.16667))));
+ } if (value >= -1.5 && value < -1.0) {
+ node.setColor(colorBlender(low, mid, (1-(3*0.16667))));
+ } if (value >= -1.0 && value < -0.5) {
+ node.setColor(colorBlender(low, mid, (1-(4*0.16667))));
+ } if (value >= -0.5 && value < 0) {
+ node.setColor(colorBlender(low, mid, (1-(5*0.16667))));
+ }
+
+ if (value == 0.0) {
+ node.setColor(mid);
+ }
+
+ if (value > 0.0 && value < 0.5) {
+ node.setColor(colorBlender(mid, high, (1-0.16667)));
+ } if (value >= 0.5 && value < 1.0) {
+ node.setColor(colorBlender(mid, high, (1-(2*0.16667))));
+ } if (value >= 1.0 && value < 1.5) {
+ node.setColor(colorBlender(mid, high, (1-(3*0.16667))));
+ } if (value >= 1.5 && value < 2.0) {
+ node.setColor(colorBlender(mid, high, (1-(4*0.16667))));
+ } if (value >= 2.0 && value < 2.5) {
+ node.setColor(colorBlender(mid, high, (1-(5*0.16667))));
+ } if (value >= 2.5 && value < 3) {
+ node.setColor(high);
+ }
+
+ if (value < -3.0) {
+ node.setColor(Color.gray);
+ } if (value > 3.0) {
+ node.setColor(Color.black);
+ }
+ };
+
+ protected static void adjustColorWeight(Node node, Double weight){
+ Color blue = new Color(0f, 51/255f, 153/255f);
+ Color white = Color.white;
+ Color red = new Color(153/255f, 0f, 0f);
+
+ adjustColor(node, weight, blue, white, red);
+ };
+
+ protected static void adjustColorChange(Node node, Double diff){
+ Color red = new Color(204/255f, 0f, 0f);
+ Color white = Color.white;
+ Color green = new Color(0f, 204/255f, 0f);
+
+ if (diff != 0.0) {
+ adjustColor(node, diff, red, white, green);
+ }
+ };
+
+ protected static void adjustColorblindChange(Node node, Double diff){
+ Color yellow = new Color(255/255f, 255/255f, 0f);
+ Color white = Color.white;
+ Color blue = new Color(0f, 0f, 255/255f);
+
+ if (diff != 0.0) {
+ adjustColor(node, diff, yellow, white, blue);
+ }
+ };
+
+
+ //Converts the components of a color, as specified by the HSB model, to an equivalent set of values for the default RGB model.
+
+ protected static void adjustOpacity(Node node, Double weight){
+ if (weight >= -3.0 && weight < -2.5) {
+ node.setAlpha(0.0833f*1f);
+
+ } if (weight >= -2.5 && weight < -2.0) {
+ node.setAlpha(0.0833f*2f);
+
+ } if (weight >= -2.0 && weight < -1.5) {
+ node.setAlpha(0.0833f*3f);
+
+ } if (weight >= -1.5 && weight < -1.0) {
+ node.setAlpha(0.0833f*4f);
+
+ } if (weight >= -1.0 && weight < -0.5) {
+ node.setAlpha(0.0833f*5f);
+
+ } if (weight >= -0.5 && weight < 0) {
+ node.setAlpha(0.0833f*6f);
+
+ } if (weight >= 0.0 && weight < 0.5) {
+ node.setAlpha(0.0833f*7f);
+
+ } if (weight >= 0.5 && weight < 1.0) {
+ node.setAlpha(0.0833f*8f);
+
+ } if (weight >= 1.0 && weight < 1.5) {
+ node.setAlpha(0.0833f*9f);
+
+ } if (weight >= 1.5 && weight < 2.0) {
+ node.setAlpha(0.0833f*10f);
+
+ } if (weight >= 2.0 && weight < 2.5) {
+ node.setAlpha(0.0833f*11f);
+
+ } if (weight >= 2.5 && weight <= 3) {
+ node.setAlpha(0.0833f*12f);
+ }
+
+ if (weight < -3) {
+ node.setColor(Color.white);
+ }
+ if (weight > 3) {
+ node.setColor(Color.black);
+ }
+ };
+
+}
+
+
+
+
+
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/NodesThread.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/NodesThread.java
new file mode 100644
index 0000000..170b0b8
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/NodesThread.java
@@ -0,0 +1,102 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2011 Gephi Consortium. All rights reserved.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 3 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://gephi.org/about/legal/license-notice/
+or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+specific language governing permissions and limitations under the
+License. When distributing the software, include this License Header
+Notice in each file and include the License files at
+/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 3, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 3] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 3 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 3 code and therefore, elected the GPL
+Version 3 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+
+Contributor(s):
+
+Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+import org.gephi.graph.api.Node;
+// import org.gephi.layout.plugin.NodeWeight.ForceFactory.RepulsionForce;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.RepulsionForce;
+
+/**
+ *
+ * @author Mathieu Jacomy
+ */
+public class NodesThread implements Runnable {
+
+ private Node[] nodes;
+ private int from;
+ private int to;
+ private Region rootRegion;
+ private boolean barnesHutOptimize;
+ private RepulsionForce Repulsion;
+ private double barnesHutTheta;
+ private double gravity;
+ private RepulsionForce GravityForce;
+ private double scaling;
+
+ public NodesThread(Node[] nodes, int from, int to, boolean barnesHutOptimize, double barnesHutTheta, double gravity, RepulsionForce GravityForce, double scaling, Region rootRegion, RepulsionForce Repulsion) {
+ this.nodes = nodes;
+ this.from = from;
+ this.to = to;
+ this.rootRegion = rootRegion;
+ this.barnesHutOptimize = barnesHutOptimize;
+ this.Repulsion = Repulsion;
+ this.barnesHutTheta = barnesHutTheta;
+ this.gravity = gravity;
+ this.GravityForce = GravityForce;
+ this.scaling = scaling;
+ }
+
+ @Override
+ public void run() {
+ // Repulsion
+ if (barnesHutOptimize) {
+ for (int nIndex = from; nIndex < to; nIndex++) {
+ Node n = nodes[nIndex];
+ rootRegion.applyForce(n, Repulsion, barnesHutTheta);
+ }
+ } else {
+ for (int n1Index = from; n1Index < to; n1Index++) {
+ Node n1 = nodes[n1Index];
+ for (int n2Index = 0; n2Index < n1Index; n2Index++) {
+ Node n2 = nodes[n2Index];
+ Repulsion.apply(n1, n2);
+ }
+ }
+ }
+
+ // Gravity
+ for (int nIndex = from; nIndex < to; nIndex++) {
+ Node n = nodes[nIndex];
+ GravityForce.apply(n, gravity / scaling);
+ }
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/Operation.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/Operation.java
new file mode 100644
index 0000000..771b0a6
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/Operation.java
@@ -0,0 +1,51 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2011 Gephi Consortium. All rights reserved.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 3 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://gephi.org/about/legal/license-notice/
+or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+specific language governing permissions and limitations under the
+License. When distributing the software, include this License Header
+Notice in each file and include the License files at
+/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 3, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 3] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 3 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 3 code and therefore, elected the GPL
+Version 3 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+
+Contributor(s):
+
+Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+/**
+ *
+ * @author Mathieu Jacomy
+ */
+public abstract class Operation {
+
+ public abstract void execute();
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeNodeAttract.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeNodeAttract.java
new file mode 100644
index 0000000..5e8bcea
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeNodeAttract.java
@@ -0,0 +1,70 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2011 Gephi Consortium. All rights reserved.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 3 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://gephi.org/about/legal/license-notice/
+or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+specific language governing permissions and limitations under the
+License. When distributing the software, include this License Header
+Notice in each file and include the License files at
+/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 3, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 3] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 3 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 3 code and therefore, elected the GPL
+Version 3 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+
+Contributor(s):
+
+Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+import org.gephi.graph.api.Node;
+// import org.gephi.layout.plugin.NodeWeight.ForceFactory.AttractionForce;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.AttractionForce;
+
+/**
+ *
+ * @author Mathieu Jacomy
+ */
+public class OperationNodeNodeAttract extends Operation {
+
+ private final Node n1;
+ private final Node n2;
+ private final AttractionForce f;
+ private final double coefficient;
+
+ public OperationNodeNodeAttract(Node n1, Node n2, AttractionForce f, double coefficient) {
+ this.n1 = n1;
+ this.n2 = n2;
+ this.f = f;
+ this.coefficient = coefficient;
+ }
+
+ @Override
+ public void execute() {
+ f.apply(n1, n2, coefficient);
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeNodeRepulse.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeNodeRepulse.java
new file mode 100644
index 0000000..4f78bd8
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeNodeRepulse.java
@@ -0,0 +1,68 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2011 Gephi Consortium. All rights reserved.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 3 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://gephi.org/about/legal/license-notice/
+or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+specific language governing permissions and limitations under the
+License. When distributing the software, include this License Header
+Notice in each file and include the License files at
+/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 3, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 3] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 3 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 3 code and therefore, elected the GPL
+Version 3 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+
+Contributor(s):
+
+Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+import org.gephi.graph.api.Node;
+// import org.gephi.layout.plugin.NodeWeight.ForceFactory.RepulsionForce;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.RepulsionForce;
+
+/**
+ *
+ * @author Mathieu Jacomy
+ */
+public class OperationNodeNodeRepulse extends Operation {
+
+ private final Node n1;
+ private final Node n2;
+ private final RepulsionForce f;
+
+ public OperationNodeNodeRepulse(Node n1, Node n2, RepulsionForce f) {
+ this.n1 = n1;
+ this.n2 = n2;
+ this.f = f;
+ }
+
+ @Override
+ public void execute() {
+ f.apply(n1, n2);
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeRegionRepulse.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeRegionRepulse.java
new file mode 100644
index 0000000..76ba361
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeRegionRepulse.java
@@ -0,0 +1,70 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2011 Gephi Consortium. All rights reserved.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 3 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://gephi.org/about/legal/license-notice/
+or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+specific language governing permissions and limitations under the
+License. When distributing the software, include this License Header
+Notice in each file and include the License files at
+/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 3, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 3] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 3 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 3 code and therefore, elected the GPL
+Version 3 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+
+Contributor(s):
+
+Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+import org.gephi.graph.api.Node;
+// import org.gephi.layout.plugin.NodeWeight.ForceFactory.RepulsionForce;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.RepulsionForce;
+
+/**
+ *
+ * @author Mathieu Jacomy
+ */
+public class OperationNodeRegionRepulse extends Operation {
+
+ private final Node n;
+ private final Region r;
+ private final RepulsionForce f;
+ private final double theta;
+
+ public OperationNodeRegionRepulse(Node n, Region r, RepulsionForce f, double theta) {
+ this.n = n;
+ this.f = f;
+ this.r = r;
+ this.theta = theta;
+ }
+
+ @Override
+ public void execute() {
+ r.applyForce(n, f, theta);
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeRepulse.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeRepulse.java
new file mode 100644
index 0000000..cf5fd1e
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/OperationNodeRepulse.java
@@ -0,0 +1,68 @@
+/*
+Copyright 2008-2011 Gephi
+Authors : Mathieu Jacomy
+Website : http://www.gephi.org
+
+This file is part of Gephi.
+
+DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+Copyright 2011 Gephi Consortium. All rights reserved.
+
+The contents of this file are subject to the terms of either the GNU
+General Public License Version 3 only ("GPL") or the Common
+Development and Distribution License("CDDL") (collectively, the
+"License"). You may not use this file except in compliance with the
+License. You can obtain a copy of the License at
+http://gephi.org/about/legal/license-notice/
+or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+specific language governing permissions and limitations under the
+License. When distributing the software, include this License Header
+Notice in each file and include the License files at
+/cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+License Header, with the fields enclosed by brackets [] replaced by
+your own identifying information:
+"Portions Copyrighted [year] [name of copyright owner]"
+
+If you wish your version of this file to be governed by only the CDDL
+or only the GPL Version 3, indicate your decision by adding
+"[Contributor] elects to include this software in this distribution
+under the [CDDL or GPL Version 3] license." If you do not indicate a
+single choice of license, a recipient has the option to distribute
+your version of this file under either the CDDL, the GPL Version 3 or
+to extend the choice of license to its licensees as provided above.
+However, if you add GPL Version 3 code and therefore, elected the GPL
+Version 3 license, then the option applies only if the new code is
+made subject to such option by the copyright holder.
+
+Contributor(s):
+
+Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+import org.gephi.graph.api.Node;
+// import org.gephi.layout.plugin.NodeWeight.ForceFactory.RepulsionForce;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.RepulsionForce;
+
+/**
+ *
+ * @author Mathieu Jacomy
+ */
+public class OperationNodeRepulse extends Operation {
+
+ private Node n;
+ private RepulsionForce f;
+ private double coefficient;
+
+ public OperationNodeRepulse(Node n, RepulsionForce f, double coefficient) {
+ this.n = n;
+ this.f = f;
+ this.coefficient = coefficient;
+ }
+
+ @Override
+ public void execute() {
+ f.apply(n, coefficient);
+ }
+}
diff --git a/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/Region.java b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/Region.java
new file mode 100644
index 0000000..1277929
--- /dev/null
+++ b/modules/NodeWeight/src/main/java/org/gephi/layout/plugins/layout/NodeWeight/Region.java
@@ -0,0 +1,222 @@
+/*
+ Copyright 2008-2011 Gephi
+ Authors : Mathieu Jacomy
+ Website : http://www.gephi.org
+
+ This file is part of Gephi.
+
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+ Copyright 2011 Gephi Consortium. All rights reserved.
+
+ The contents of this file are subject to the terms of either the GNU
+ General Public License Version 3 only ("GPL") or the Common
+ Development and Distribution License("CDDL") (collectively, the
+ "License"). You may not use this file except in compliance with the
+ License. You can obtain a copy of the License at
+ http://gephi.org/about/legal/license-notice/
+ or /cddl-1.0.txt and /gpl-3.0.txt. See the License for the
+ specific language governing permissions and limitations under the
+ License. When distributing the software, include this License Header
+ Notice in each file and include the License files at
+ /cddl-1.0.txt and /gpl-3.0.txt. If applicable, add the following below the
+ License Header, with the fields enclosed by brackets [] replaced by
+ your own identifying information:
+ "Portions Copyrighted [year] [name of copyright owner]"
+
+ If you wish your version of this file to be governed by only the CDDL
+ or only the GPL Version 3, indicate your decision by adding
+ "[Contributor] elects to include this software in this distribution
+ under the [CDDL or GPL Version 3] license." If you do not indicate a
+ single choice of license, a recipient has the option to distribute
+ your version of this file under either the CDDL, the GPL Version 3 or
+ to extend the choice of license to its licensees as provided above.
+ However, if you add GPL Version 3 code and therefore, elected the GPL
+ Version 3 license, then the option applies only if the new code is
+ made subject to such option by the copyright holder.
+
+ Contributor(s):
+
+ Portions Copyrighted 2011 Gephi Consortium.
+ */
+// package org.gephi.layout.plugin.NodeWeight;
+package org.gephi.layout.plugins.layout.NodeWeight;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.gephi.graph.api.Node;
+// import org.gephi.layout.plugin.NodeWeight.ForceFactory.RepulsionForce;
+import org.gephi.layout.plugins.layout.NodeWeight.ForceFactory.RepulsionForce;
+
+/**
+ * Barnes Hut optimization
+ *
+ * @author Mathieu Jacomy
+ */
+public class Region {
+
+ private double mass;
+ private double massCenterX;
+ private double massCenterY;
+ private double size;
+ private final List nodes;
+ private final List subregions = new ArrayList();
+
+ public Region(Node[] nodes) {
+ this.nodes = new ArrayList();
+ this.nodes.addAll(Arrays.asList(nodes));
+ updateMassAndGeometry();
+ }
+
+ public Region(ArrayList nodes) {
+ this.nodes = new ArrayList(nodes);
+ updateMassAndGeometry();
+ }
+
+ private void updateMassAndGeometry() {
+ if (nodes.size() > 1) {
+ // Compute Mass
+ mass = 0;
+ double massSumX = 0;
+ double massSumY = 0;
+ for (Node n : nodes) {
+ ForceAtlas2LayoutData nLayout = n.getLayoutData();
+ mass += nLayout.mass;
+ massSumX += n.x() * nLayout.mass;
+ massSumY += n.y() * nLayout.mass;
+ }
+ massCenterX = massSumX / mass;
+ massCenterY = massSumY / mass;
+
+ // Compute size
+ size = Double.MIN_VALUE;
+ for (Node n : nodes) {
+ double distance = Math.sqrt((n.x() - massCenterX) * (n.x() - massCenterX) + (n.y() - massCenterY) * (n.y() - massCenterY));
+ size = Math.max(size, 2 * distance);
+ }
+ }
+ }
+
+ public synchronized void buildSubRegions() {
+ if (nodes.size() > 1) {
+ ArrayList leftNodes = new ArrayList();
+ ArrayList rightNodes = new ArrayList();
+ for (Node n : nodes) {
+ ArrayList nodesColumn = (n.x() < massCenterX) ? (leftNodes) : (rightNodes);
+ nodesColumn.add(n);
+ }
+
+ ArrayList topleftNodes = new ArrayList();
+ ArrayList bottomleftNodes = new ArrayList();
+ for (Node n : leftNodes) {
+ ArrayList nodesLine = (n.y() < massCenterY) ? (topleftNodes) : (bottomleftNodes);
+ nodesLine.add(n);
+ }
+
+ ArrayList bottomrightNodes = new ArrayList();
+ ArrayList toprightNodes = new ArrayList();
+ for (Node n : rightNodes) {
+ ArrayList nodesLine = (n.y() < massCenterY) ? (toprightNodes) : (bottomrightNodes);
+ nodesLine.add(n);
+ }
+
+ if (topleftNodes.size() > 0) {
+ if (topleftNodes.size() < nodes.size()) {
+ Region subregion = new Region(topleftNodes);
+ subregions.add(subregion);
+ } else {
+ for (Node n : topleftNodes) {
+ ArrayList oneNodeList = new ArrayList();
+ oneNodeList.add(n);
+ Region subregion = new Region(oneNodeList);
+ subregions.add(subregion);
+ }
+ }
+ }
+ if (bottomleftNodes.size() > 0) {
+ if (bottomleftNodes.size() < nodes.size()) {
+ Region subregion = new Region(bottomleftNodes);
+ subregions.add(subregion);
+ } else {
+ for (Node n : bottomleftNodes) {
+ ArrayList oneNodeList = new ArrayList();
+ oneNodeList.add(n);
+ Region subregion = new Region(oneNodeList);
+ subregions.add(subregion);
+ }
+ }
+ }
+ if (bottomrightNodes.size() > 0) {
+ if (bottomrightNodes.size() < nodes.size()) {
+ Region subregion = new Region(bottomrightNodes);
+ subregions.add(subregion);
+ } else {
+ for (Node n : bottomrightNodes) {
+ ArrayList oneNodeList = new ArrayList();
+ oneNodeList.add(n);
+ Region subregion = new Region(oneNodeList);
+ subregions.add(subregion);
+ }
+ }
+ }
+ if (toprightNodes.size() > 0) {
+ if (toprightNodes.size() < nodes.size()) {
+ Region subregion = new Region(toprightNodes);
+ subregions.add(subregion);
+ } else {
+ for (Node n : toprightNodes) {
+ ArrayList oneNodeList = new ArrayList();
+ oneNodeList.add(n);
+ Region subregion = new Region(oneNodeList);
+ subregions.add(subregion);
+ }
+ }
+ }
+
+ for (Region subregion : subregions) {
+ subregion.buildSubRegions();
+ }
+ }
+ }
+
+ public void applyForce(Node n, RepulsionForce Force, double theta) {
+ if (nodes.size() < 2) {
+ Node regionNode = nodes.get(0);
+ Force.apply(n, regionNode);
+ } else {
+ double distance = Math.sqrt((n.x() - massCenterX) * (n.x() - massCenterX) + (n.y() - massCenterY) * (n.y() - massCenterY));
+ if (distance * theta > size) {
+ Force.apply(n, this);
+ } else {
+ for (Region subregion : subregions) {
+ subregion.applyForce(n, Force, theta);
+ }
+ }
+ }
+ }
+
+ public double getMass() {
+ return mass;
+ }
+
+ public void setMass(double mass) {
+ this.mass = mass;
+ }
+
+ public double getMassCenterX() {
+ return massCenterX;
+ }
+
+ public void setMassCenterX(double massCenterX) {
+ this.massCenterX = massCenterX;
+ }
+
+ public double getMassCenterY() {
+ return massCenterY;
+ }
+
+ public void setMassCenterY(double massCenterY) {
+ this.massCenterY = massCenterY;
+ }
+}
diff --git a/modules/NodeWeight/src/main/nbm/manifest.mf b/modules/NodeWeight/src/main/nbm/manifest.mf
new file mode 100644
index 0000000..9e84c49
--- /dev/null
+++ b/modules/NodeWeight/src/main/nbm/manifest.mf
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+OpenIDE-Module-Name: Node Weight
+OpenIDE-Module-Display-Category: Layout
+OpenIDE-Module-Short-Description: adds node to FA2
+OpenIDE-Module-Long-Description: see above
diff --git a/modules/NodeWeight/src/main/resources/org/gephi/layout/plugins/layout/NodeWeight/Bundle.properties b/modules/NodeWeight/src/main/resources/org/gephi/layout/plugins/layout/NodeWeight/Bundle.properties
new file mode 100644
index 0000000..b7474b5
--- /dev/null
+++ b/modules/NodeWeight/src/main/resources/org/gephi/layout/plugins/layout/NodeWeight/Bundle.properties
@@ -0,0 +1,63 @@
+#OpenIDE-Module-Display-Category=Layout
+#OpenIDE-Module-Long-Description=\
+# Example of Layout Plugins:\n
\n- Grid Layout: Place all nodes in a simple grid. \
+# Users can configure the size of the area and the speed.\
+#
\n- Sorted Grid Layout: Same example as Grid Layout but users can sort nodes with an attribute column.\
+#
\n
+#OpenIDE-Module-Name=NodeWeight
+#OpenIDE-Module-Short-Description=add node weight to FA2 algorithm
+
+
+
+
+NodeWeight.name=Node Weight
+NodeWeight.description=Force Atlas 2 with node weight influence and appearance features.
+
+ForceAtlas2.tuning=Tuning
+ForceAtlas2.behavior=Behavior Alternatives
+ForceAtlas2.performance=Performance
+ForceAtlas2.threads=Threads
+
+# jj2018
+ForceAtlas2.opacityMode.name=Opacity mode
+ForceAtlas2.opacityMode.desc=Requires data scaled to its standard deviation. Opacity changes according to weight (in preview / export), turn on per-node opacity in preview tab to activate.
+
+ForceAtlas2.heatMapMode.name=Heat Map
+ForceAtlas2.heatMapMode.desc=Turn on heat map based on normalized node weights. Requires data scaled to its standard deviation.
+
+ForceAtlas2.heatMapChangeMode.name=Change Heat Map
+ForceAtlas2.heatMapChangeMode.desc=Turn on heat map to show change of normalized node weights between time slices in dynamic data.
+
+ForceAtlas2.heatMapChangeModeCB.name=Alternative Change Heat Map (Colorblind accessible)
+ForceAtlas2.heatMapChangeModeCB.desc=Turn on heat map to show change of normalized node weights between time slices in dynamic data Requires data scaled to its standard deviation.
+
+ForceAtlas2.nodeWeightScaling.name=Node Weight Influence
+ForceAtlas2.nodeWeightScaling.desc=Adjust scaling on node weight attraction. More creates larger distance between nodes.
+#
+
+
+ForceAtlas2.scalingRatio.name=Scaling
+ForceAtlas2.scalingRatio.desc=How much repulsion you want. More makes a more sparse graph.
+
+# annotations by jj2018 on stronger gravity description
+ForceAtlas2.gravity.name=Gravity
+ForceAtlas2.gravity.desc=Attracts nodes to the center. Prevents islands from drifting away. If negative weight averages in your network, minimum gravity set to avoid nodes floating out of graph space.
+ForceAtlas2.strongGravityMode.name=Stronger Gravity
+ForceAtlas2.strongGravityMode.desc=A stronger gravity law. Highly recommended to keep nodes in graph space.
+
+ForceAtlas2.distributedAttraction.name=Dissuade Hubs
+ForceAtlas2.distributedAttraction.desc=Distributes attraction along outbound edges. Hubs attract less and thus are pushed to the borders.
+ForceAtlas2.linLogMode.name=LinLog mode
+ForceAtlas2.linLogMode.desc=Switch ForceAtlas' model from lin-lin to lin-log (tribute to Andreas Noack). Makes clusters more tight.
+ForceAtlas2.adjustSizes.name=Prevent Overlap
+ForceAtlas2.adjustSizes.desc=Use only when spatialized. Should not be used with "Approximate Repulsion"
+ForceAtlas2.jitterTolerance.name=Tolerance (speed)
+ForceAtlas2.jitterTolerance.desc=How much swinging you allow. Above 1 discouraged. Lower gives less speed and more precision.
+ForceAtlas2.barnesHutOptimization.name=Approximate Repulsion
+ForceAtlas2.barnesHutOptimization.desc=Barnes Hut optimization: n\u00b2 complexity to n.ln(n) ; allows larger graphs.
+ForceAtlas2.barnesHutTheta.name=Approximation
+ForceAtlas2.barnesHutTheta.desc=Theta of the Barnes Hut optimization.
+ForceAtlas2.edgeWeightInfluence.name=Edge Weight Influence
+ForceAtlas2.edgeWeightInfluence.desc=How much influence you give to the edges weight. 0 is "no influence" and 1 is "normal".
+ForceAtlas2.threads.name=Threads number
+ForceAtlas2.threads.desc=More threads means more speed if your cores can handle it.
diff --git a/pom.xml b/pom.xml
index f13a911..e0ac83d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,15 +4,15 @@
org.gephi
gephi-plugins
- 0.9.0
+ 0.9.2
pom
gephi-plugins
-
+
- modules/PluginsSubMenuExample
+
+
+
+ modules/NodeWeight
-
+
- 0.9.0
+ 0.9.2
${project.build.directory}/plugins_clusters
github
-
+
scm:git:git://github.com/gephi/gephi-plugins.git
@@ -53,7 +55,7 @@
-
+
@@ -76,7 +78,7 @@
tar.gz
-
+
@@ -109,7 +111,7 @@
-
+
@@ -126,7 +128,7 @@
-
+
org.apache.maven.plugins
@@ -148,7 +150,7 @@
-
+
org.codehaus.mojo
@@ -167,7 +169,7 @@
-
+