Separate a Link Force with optimization
This commit is contained in:
@@ -41,7 +41,8 @@ var nodes, // as in Data points
|
|||||||
fileName = "",
|
fileName = "",
|
||||||
selectedData,
|
selectedData,
|
||||||
clickedIndex = -1,
|
clickedIndex = -1,
|
||||||
paused = false;
|
paused = false,
|
||||||
|
alreadyRanIterations;
|
||||||
|
|
||||||
// Default parameters
|
// Default parameters
|
||||||
var MULTIPLIER = 50,
|
var MULTIPLIER = 50,
|
||||||
@@ -119,10 +120,16 @@ function processData(data, error) {
|
|||||||
props.pop(); //Hide Iris index / last column from distance function
|
props.pop(); //Hide Iris index / last column from distance function
|
||||||
|
|
||||||
//Put the nodes in random starting positions
|
//Put the nodes in random starting positions
|
||||||
|
//TODO Change this back
|
||||||
|
nodes.forEach(function (d) {
|
||||||
|
d.x = 0;
|
||||||
|
d.y = 0;
|
||||||
|
});
|
||||||
|
/*
|
||||||
nodes.forEach(function (d) {
|
nodes.forEach(function (d) {
|
||||||
d.x = (Math.random()-0.5) * 100000;
|
d.x = (Math.random()-0.5) * 100000;
|
||||||
d.y = (Math.random()-0.5) * 100000;
|
d.y = (Math.random()-0.5) * 100000;
|
||||||
});
|
});*/
|
||||||
|
|
||||||
addNodesToDOM(nodes);
|
addNodesToDOM(nodes);
|
||||||
|
|
||||||
@@ -180,6 +187,8 @@ function addNodesToDOM(data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ticked() {
|
function ticked() {
|
||||||
|
console.log("ticked");
|
||||||
|
alreadyRanIterations++;
|
||||||
// If rendering is selected, then draw at every iteration.
|
// If rendering is selected, then draw at every iteration.
|
||||||
if (rendering === true) {
|
if (rendering === true) {
|
||||||
node // Each sub-circle in the SVG, update cx and cy
|
node // Each sub-circle in the SVG, update cx and cy
|
||||||
@@ -194,9 +203,14 @@ function ticked() {
|
|||||||
if (springForce) {
|
if (springForce) {
|
||||||
intercom.emit("passedData", simulation.force(forceName).distributionData());
|
intercom.emit("passedData", simulation.force(forceName).distributionData());
|
||||||
}
|
}
|
||||||
|
if(alreadyRanIterations == ITERATIONS) {
|
||||||
|
simulation.stop();
|
||||||
|
ended();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function ended() {
|
function ended() {
|
||||||
|
console.log("ended");
|
||||||
if (rendering !== true) { // Never drawn anything before? Now it's time.
|
if (rendering !== true) { // Never drawn anything before? Now it's time.
|
||||||
node
|
node
|
||||||
.attr("cx", function (d) {
|
.attr("cx", function (d) {
|
||||||
|
|||||||
@@ -1,40 +1,45 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the link force algorithm and start simulation.
|
* Initialize the link force algorithm and start simulation.
|
||||||
*/
|
*/
|
||||||
function startLinkSimulation() {
|
function startLinkSimulation() {
|
||||||
console.log("startLinkSimulation")
|
console.log("startLinkSimulation")
|
||||||
springForce = false;
|
springForce = false;
|
||||||
|
alreadyRanIterations = 0;
|
||||||
simulation.stop();
|
simulation.stop();
|
||||||
p1 = performance.now();
|
p1 = performance.now();
|
||||||
let links = [];
|
let links = [];
|
||||||
|
|
||||||
// Initialize link array.
|
// Initialize link array.
|
||||||
nodes = simulation.nodes();
|
nodes = simulation.nodes();
|
||||||
for (i = 0; i < nodes.length; i++) {
|
for (i = nodes.length-1; i >= 1; i--) {
|
||||||
for (j = 0; j < i; j++) {
|
for (j = i-1; j >= 0; j--) {
|
||||||
if (i !== j) {
|
|
||||||
links.push({
|
links.push({
|
||||||
source: nodes[i],
|
source: nodes[i],
|
||||||
target: nodes[j],
|
target: nodes[j],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Add the links to the simulation.
|
/* Add force
|
||||||
simulation.force(forceName, d3.forceLink().links(links));
|
* Please add the distance function before feeding the force
|
||||||
|
* it to the simulation or adding links.
|
||||||
simulation
|
*
|
||||||
.alphaDecay(1 - Math.pow(0.001, 1 / ITERATIONS))
|
* On setting the distance fn and being initialized by the simulation, the
|
||||||
//.velocityDecay(0.8)
|
* force will pre-calculate high-dimensional distances of every link and
|
||||||
.force(forceName)
|
* store that as cache.
|
||||||
// The distance function that will be used to calculate distances
|
* Adding distance fn before links means that the first pre-calculation will
|
||||||
// between nodes.
|
* calculate noting as there was no link.
|
||||||
|
* The full pre-calculation will then occur once when the force is being
|
||||||
|
* initialized by the simulation.
|
||||||
|
*/
|
||||||
|
simulation.force(forceName,
|
||||||
|
d3.forceLinkOptimized()
|
||||||
.distance(function (n) {
|
.distance(function (n) {
|
||||||
return distanceFunction(n.source, n.target, props, norm);
|
return distanceFunction(n.source, n.target, props, norm);
|
||||||
})
|
})
|
||||||
// Set the parameter for the algorithm (optional).
|
.links(links)
|
||||||
// Restart the simulation.
|
).alphaDecay(0)
|
||||||
simulation.alpha(1).restart();
|
//.velocityDecay(0.8)
|
||||||
|
.alpha(1)
|
||||||
|
.restart();
|
||||||
}
|
}
|
||||||
|
|||||||
36
index.js
36
index.js
@@ -1,28 +1,20 @@
|
|||||||
/*export {
|
/*
|
||||||
default as forceNeighbourSampling
|
export {default as forceNeighbourSampling}
|
||||||
}
|
|
||||||
from "./src/neighbourSampling";
|
from "./src/neighbourSampling";
|
||||||
export {
|
export {default as forceNeighbourSamplingPre}
|
||||||
default as forceNeighbourSamplingPre
|
from "./src/neighbourSamplingPre";
|
||||||
}
|
*/
|
||||||
from "./src/neighbourSamplingPre";*/
|
export {default as forceNeighbourSamplingDistance}
|
||||||
export {
|
|
||||||
default as forceNeighbourSamplingDistance
|
|
||||||
}
|
|
||||||
from "./src/neighbourSamplingDistance";
|
from "./src/neighbourSamplingDistance";
|
||||||
export {
|
|
||||||
default as forceBarnesHut
|
export { default as forceBarnesHut}
|
||||||
}
|
|
||||||
from "./src/barnesHut";
|
from "./src/barnesHut";
|
||||||
export {
|
|
||||||
default as tSNE
|
export { default as tSNE}
|
||||||
}
|
|
||||||
from "./src/t-sne";
|
from "./src/t-sne";
|
||||||
export {
|
|
||||||
default as forceLink
|
export { default as forceLinkOptimized}
|
||||||
}
|
|
||||||
from "./src/link";
|
from "./src/link";
|
||||||
export {
|
|
||||||
default as hybridSimulation
|
export { default as hybridSimulation}
|
||||||
}
|
|
||||||
from "./src/hybridSimulation";
|
from "./src/hybridSimulation";
|
||||||
|
|||||||
97
src/link.js
97
src/link.js
@@ -1,100 +1,53 @@
|
|||||||
import constant from "./constant";
|
import constant from "./constant";
|
||||||
import jiggle from "./jiggle";
|
import jiggle from "./jiggle";
|
||||||
import {map} from "d3-collection";
|
|
||||||
import {getStress} from "./stress";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extended link force algorithm to include the stress metric for
|
* Modified link force algorithm
|
||||||
* comparisons between the different algorithms.
|
* - ignore alpha
|
||||||
* Everything else is the same as in D3 force module.
|
* - removed location prediction, and bias
|
||||||
|
* - modified strength calculation
|
||||||
|
* - removed other unused functions
|
||||||
|
* Making it more suitable for the spring model.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function index(d, i) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
function find(nodeById, nodeId) {
|
|
||||||
var node = nodeById.get(nodeId);
|
|
||||||
if (!node) throw new Error("missing: " + nodeId);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function(links) {
|
export default function(links) {
|
||||||
var id = index,
|
var dataSizeFactor,
|
||||||
strength = defaultStrength,
|
|
||||||
strengths,
|
|
||||||
distance = constant(30),
|
distance = constant(30),
|
||||||
distances,
|
distances = [],
|
||||||
nodes,
|
nodes,
|
||||||
count,
|
|
||||||
bias,
|
|
||||||
iterations = 1;
|
iterations = 1;
|
||||||
|
|
||||||
if (links == null) links = [];
|
if (links == null) links = [];
|
||||||
|
|
||||||
function defaultStrength(link) {
|
function force() {
|
||||||
return 1 / Math.min(count[link.source.index], count[link.target.index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function force(alpha) {
|
|
||||||
for (var k = 0, n = links.length; k < iterations; ++k) { // Each iteration in a tick
|
for (var k = 0, n = links.length; k < iterations; ++k) { // Each iteration in a tick
|
||||||
for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) { // For each link
|
for (var i = 0, link, source, target, x, y, l, b; i < n; ++i) { // For each link
|
||||||
link = links[i];
|
link = links[i];
|
||||||
|
// jiggle so it wont divide / multiply by zero after this
|
||||||
source = link.source;
|
source = link.source;
|
||||||
target = link.target;
|
target = link.target;
|
||||||
x = target.x + target.vx - source.x - source.vx || jiggle();
|
x = target.x - source.x || jiggle();
|
||||||
y = target.y + target.vy - source.y - source.vy || jiggle();
|
y = target.y - source.y || jiggle();
|
||||||
l = Math.sqrt(x * x + y * y);
|
l = Math.sqrt(x * x + y * y);
|
||||||
l = (l - distances[i]) / l * alpha * strengths[i];
|
l = (l - distances[i]) / l * dataSizeFactor;
|
||||||
x *= l, y *= l;
|
x *= l, y *= l;
|
||||||
target.vx -= x * (b = bias[i]);
|
target.vx -= x;
|
||||||
target.vy -= y * b;
|
target.vy -= y;
|
||||||
source.vx += x * (b = 1 - b);
|
source.vx += x;
|
||||||
source.vy += y * b;
|
source.vy += y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initialize() {
|
function initialize() {
|
||||||
if (!nodes) return;
|
if (!nodes) return;
|
||||||
|
console.log("CUSTOM LINK FORCE");
|
||||||
var i,
|
dataSizeFactor = 0.5/(nodes.length-1);
|
||||||
n = nodes.length,
|
initializeDistance();
|
||||||
m = links.length,
|
|
||||||
nodeById = map(nodes, id),
|
|
||||||
link;
|
|
||||||
|
|
||||||
for (i = 0, count = new Array(n); i < n; ++i) {
|
|
||||||
count[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < m; ++i) {
|
|
||||||
link = links[i];
|
|
||||||
link.index = i;
|
|
||||||
//if (typeof link.source !== "object") link.source = find(nodeById, link.source);
|
|
||||||
//if (typeof link.target !== "object") link.target = find(nodeById, link.target);
|
|
||||||
++count[link.source.index];
|
|
||||||
++count[link.target.index];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0, bias = new Array(m); i < m; ++i) {
|
|
||||||
link = links[i];
|
|
||||||
bias[i] = count[link.source.index] / (count[link.source.index] + count[link.target.index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
strengths = new Array(m), initializeStrength();
|
|
||||||
distances = new Array(m), initializeDistance();
|
|
||||||
}
|
|
||||||
|
|
||||||
function initializeStrength() {
|
|
||||||
if (!nodes) return;
|
|
||||||
|
|
||||||
for (var i = 0, n = links.length; i < n; ++i) {
|
|
||||||
strengths[i] = +strength(links[i], i, links);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initializeDistance() {
|
function initializeDistance() {
|
||||||
|
console.log("INIT DISTANCE", links.length);
|
||||||
if (!nodes) return;
|
if (!nodes) return;
|
||||||
|
|
||||||
for (var i = 0, n = links.length; i < n; ++i) {
|
for (var i = 0, n = links.length; i < n; ++i) {
|
||||||
@@ -119,17 +72,9 @@ export default function(links) {
|
|||||||
return arguments.length ? (iterations = +_, force) : iterations;
|
return arguments.length ? (iterations = +_, force) : iterations;
|
||||||
};
|
};
|
||||||
|
|
||||||
force.strength = function(_) {
|
|
||||||
return arguments.length ? (strength = typeof _ === "function" ? _ : constant(+_), initializeStrength(), force) : strength;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.distance = function(_) {
|
force.distance = function(_) {
|
||||||
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), initializeDistance(), force) : distance;
|
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), initializeDistance(), force) : distance;
|
||||||
};
|
};
|
||||||
|
|
||||||
force.stress = function() {
|
|
||||||
return getStress(nodes, function(s,t){return distance({source: s, target: t});});
|
|
||||||
}
|
|
||||||
|
|
||||||
return force;
|
return force;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user