Neighbor stop on little velocity changes

This commit is contained in:
Pitchaya Boonsarngsuk
2018-01-17 09:55:43 +00:00
parent 7b6b5e46c6
commit 5ece153651
3 changed files with 97 additions and 77 deletions

View File

@@ -166,9 +166,9 @@ function processData(data, error) {
.nodes(nodes) .nodes(nodes)
.on("tick", ticked) .on("tick", ticked)
.on("end", ended); .on("end", ended);
};
function ticked() {
function ticked() {
// 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
@@ -183,9 +183,9 @@ function processData(data, error) {
if (springForce) { if (springForce) {
intercom.emit("passedData", simulation.force(forceName).distributionData()); intercom.emit("passedData", simulation.force(forceName).distributionData());
} }
} }
function ended() { function 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) {
@@ -208,9 +208,7 @@ function processData(data, error) {
p1 = 0; p1 = 0;
p2 = 0; p2 = 0;
} }
} }
};
function brushEnded() { function brushEnded() {
var s = d3.event.selection, var s = d3.event.selection,
@@ -281,7 +279,10 @@ function startNeighbourSamplingSimulation() {
// between nodes. // between nodes.
.distance(function (s, t) { .distance(function (s, t) {
return distanceFunction(s, t, props, norm) * MULTIPLIER; return distanceFunction(s, t, props, norm) * MULTIPLIER;
})); })
.stableVelocity(1.2 * MULTIPLIER)
.stableVeloHandler( function(){simulation.stop(); ended();} )
);
// Restart the simulation. // Restart the simulation.
console.log(simulation.force(forceName).neighbourSize(), simulation.force(forceName).sampleSize()); console.log(simulation.force(forceName).neighbourSize(), simulation.force(forceName).sampleSize());
simulation.alpha(1).restart(); simulation.alpha(1).restart();
@@ -310,7 +311,7 @@ function startHybridSimulation() {
pivots: PIVOTS, pivots: PIVOTS,
numPivots: NUM_PIVOTS numPivots: NUM_PIVOTS
}; };
.sampleSize(SAMPLE_SIZE); console.log(configuration);
hybridSimulation = d3.hybridSimulation(nodes, configuration); hybridSimulation = d3.hybridSimulation(nodes, configuration);
let sample = hybridSimulation.sample(); let sample = hybridSimulation.sample();

View File

@@ -34,13 +34,16 @@ export default function (nodes, config) {
}) })
.on("end", ended); .on("end", ended);
sampleSimulation.force("forces", neighbourSamplingDistance() sampleSimulation
.force("forces", neighbourSamplingDistance()
.neighbourSize(neighbourSize) .neighbourSize(neighbourSize)
.sampleSize(sampleSize) .sampleSize(sampleSize)
.distanceRange(distanceRange) .distanceRange(distanceRange)
.distance(distanceFn) .distance(distanceFn)
); .stableVelocity(60)
sampleSimulation.alpha(1).restart(); .stableVeloHandler(function(){sampleSimulation.stop(); ended();})
)
.alpha(1).restart();
function ended() { function ended() {
event.call("startFull"); event.call("startFull");

View File

@@ -29,7 +29,9 @@ export default function () {
//freeness = 0.85, //freeness = 0.85,
//springForce = 0.7, //springForce = 0.7,
//dampingFactor = 0.3, //dampingFactor = 0.3,
velocity; velocity,
stableVelocity = 0,
stableVeloHandler = null;
/** /**
* Calculates the forces at each iteration between the node and the * Calculates the forces at each iteration between the node and the
@@ -39,21 +41,28 @@ export default function () {
*/ */
function force(alpha) { function force(alpha) {
velocity = 0; velocity = 0;
for (var i = 0, n = nodes.length; i < n; ++i) { for (let i = 0, n = nodes.length; i < n; ++i) {
// Randomize the samples for every node. // Randomize the samples for every node.
samples[i] = randomizeSample(i); samples[i] = randomizeSample(i);
// Calculate the forces between node and its neighbours. // Calculate the forces between node and its neighbours.
for (var [keyN, valueN] of neighbours[i]) { for (let [keyN, valueN] of neighbours[i]) {
setVelocity(i, keyN, valueN, alpha); setVelocity(i, keyN, valueN, alpha);
} }
// Calculate the forces between node and its sample set. // Calculate the forces between node and its sample set.
for (var [keyS, valueS] of samples[i]) { for (let [keyS, valueS] of samples[i]) {
setVelocity(i, keyS, valueS, alpha); setVelocity(i, keyS, valueS, alpha);
} }
// Check if there are a better neighbours in a sample array // Check if there are a better neighbours in a sample array
// for each node. // for each node.
findNewNeighbours(i); findNewNeighbours(i);
} }
velocity /= nodes.length*alpha;
// Now total velocity change per node, alpha not considered
if(Math.abs(velocity)<stableVelocity && stableVeloHandler!== null){
console.log("Neighbour sampling is now", velocity, ", calling stableVeloHandler().")
stableVeloHandler();
}
//else console.log(velocity);
} }
/** /**
@@ -65,7 +74,7 @@ export default function () {
* @param {number} alpha - controls the speed of simulation. * @param {number} alpha - controls the speed of simulation.
*/ */
function setVelocity(sourceId, targetId, dist, alpha) { function setVelocity(sourceId, targetId, dist, alpha) {
var source, target, x, y, l; let source, target, x, y, l;
source = nodes[sourceId], target = nodes[targetId]; source = nodes[sourceId], target = nodes[targetId];
// If x or y coordinates not defined, add some randomness. // If x or y coordinates not defined, add some randomness.
x = target.x + target.vx - source.x - source.vx || jiggle(); x = target.x + target.vx - source.x - source.vx || jiggle();
@@ -73,12 +82,12 @@ export default function () {
l = Math.sqrt(x * x + y * y); l = Math.sqrt(x * x + y * y);
l = (l - dist) / l * alpha; l = (l - dist) / l * alpha;
x *= l, y *= l; x *= l, y *= l;
velocity += x + y; velocity += Math.abs(x) + Math.abs(y);
// Set the calculated velocites for both nodes. // Set the calculated velocites for both nodes.
target.vx -= x; target.vx -= x*0.5;
target.vy -= y; target.vy -= y*0.5;
source.vx += x; source.vx += x*0.5;
source.vy += y; source.vy += y*0.5;
} }
/** /**
@@ -89,11 +98,11 @@ export default function () {
// Initialize for each node a neighbour and sample arrays // Initialize for each node a neighbour and sample arrays
// with random values. // with random values.
for (var i = 0, n = nodes.length; i < n; ++i) { for (let i = 0, n = nodes.length; i < n; ++i) {
var exclude = []; // Array that keeps the indices of nodes to ignore. let exclude = []; // Array that keeps the indices of nodes to ignore.
exclude.push(i); exclude.push(i);
var neighbs = createRandomNeighbours(i, exclude, n, neighbourSize); let neighbs = createRandomNeighbours(i, exclude, n, neighbourSize);
// Sort the neighbour set by the distances. // Sort the neighbour set by the distances.
neighbs = new Map([...neighbs.entries()].sort(sortDistances)); neighbs = new Map([...neighbs.entries()].sort(sortDistances));
neighbours[i] = neighbs; neighbours[i] = neighbs;
@@ -127,17 +136,17 @@ export default function () {
* data set. * data set.
*/ */
function createRandomNeighbours(index, exclude, max, size) { function createRandomNeighbours(index, exclude, max, size) {
var randElements = new Map(); let randElements = new Map();
var triedElements = 0; let triedElements = 0;
while ((randElements.size < size) && (randElements.size + exclude.length + triedElements < nodes.length)) { while ((randElements.size < size) && (randElements.size + exclude.length + triedElements < nodes.length)) {
var rand = Math.floor((Math.random() * max)); let rand = Math.floor((Math.random() * max));
// If the rand is already in random list or in exclude list // If the rand is already in random list or in exclude list
// ignore it and get a new value. // ignore it and get a new value.
while (randElements.has(rand) || exclude.includes(rand)) { while (randElements.has(rand) || exclude.includes(rand)) {
rand = Math.floor((Math.random() * max)); rand = Math.floor((Math.random() * max));
} }
var dist = +distance(nodes[index], nodes[rand]); let dist = +distance(nodes[index], nodes[rand]);
if (dist <= distanceRange) { if (dist <= distanceRange) {
randElements.set(rand, dist); randElements.set(rand, dist);
} else { } else {
@@ -150,14 +159,14 @@ export default function () {
function createRandomSample(index, exclude, max, size) { function createRandomSample(index, exclude, max, size) {
var randElements = new Map(); let randElements = new Map();
for (var i = 0; i < size; ++i) { for (let i = 0; i < size; ++i) {
// Stop when no new elements can be found. // Stop when no new elements can be found.
if (randElements.size + exclude.length >= nodes.length) { if (randElements.size + exclude.length >= nodes.length) {
break; break;
} }
var rand = Math.floor((Math.random() * max)); let rand = Math.floor((Math.random() * max));
// If the rand is already in random list or in exclude list // If the rand is already in random list or in exclude list
// ignore it and get a new value. // ignore it and get a new value.
while (randElements.has(rand) || exclude.includes(rand)) { while (randElements.has(rand) || exclude.includes(rand)) {
@@ -176,7 +185,7 @@ export default function () {
*/ */
function randomizeSample(index) { function randomizeSample(index) {
// Ignore the current neighbours of the node and itself. // Ignore the current neighbours of the node and itself.
var exclude = [index]; let exclude = [index];
exclude = exclude.concat(Array.from(neighbours[index].keys())); exclude = exclude.concat(Array.from(neighbours[index].keys()));
return createRandomSample(index, exclude, nodes.length, sampleSize); return createRandomSample(index, exclude, nodes.length, sampleSize);
} }
@@ -188,11 +197,11 @@ export default function () {
* @param {number} index - index of current node. * @param {number} index - index of current node.
*/ */
function findNewNeighbours(index) { function findNewNeighbours(index) {
var sample = samples[index]; let sample = samples[index];
if (neighbours[index].size > 0) { if (neighbours[index].size > 0) {
for (var [key, value] of sample) { for (let [key, value] of sample) {
var neighbMax = neighbours[index].entries().next().value; let neighbMax = neighbours[index].entries().next().value;
// Check if a value from sample could be a better neighbour // Check if a value from sample could be a better neighbour
// if so, replace it. // if so, replace it.
@@ -212,9 +221,9 @@ export default function () {
* @return {number} - stress of the layout. * @return {number} - stress of the layout.
*/ */
function getStress() { function getStress() {
var totalDiffSq = 0, totalHighDistSq = 0; let totalDiffSq = 0, totalHighDistSq = 0;
for (var i = 0, source, target, realDist, highDist; i < nodes.length; i++) { for (let i = 0, source, target, realDist, highDist; i < nodes.length; i++) {
for (var j = 0; j < nodes.length; j++) { for (let j = 0; j < nodes.length; j++) {
if (i !== j) { if (i !== j) {
source = nodes[i], target = nodes[j]; source = nodes[i], target = nodes[j];
realDist = Math.hypot(target.x - source.x, target.y - source.y); realDist = Math.hypot(target.x - source.x, target.y - source.y);
@@ -238,8 +247,8 @@ export default function () {
function getDistributionData() { function getDistributionData() {
var d = []; let d = [];
for (var i = 0; i < nodes.length; i++) { for (let i = 0; i < nodes.length; i++) {
d.push({ "index": i, "size": neighbours[i].size }); d.push({ "index": i, "size": neighbours[i].size });
} }
return { "maxSize": neighbourSize, "l": nodes.length, "distribution": d }; return { "maxSize": neighbourSize, "l": nodes.length, "distribution": d };
@@ -292,5 +301,12 @@ export default function () {
return getDistributionData(); return getDistributionData();
}; };
force.stableVeloHandler = function (_) {
return arguments.length ? (stableVeloHandler = _, force) : stableVeloHandler;
};
force.stableVelocity = function (_) {
return arguments.length ? (stableVelocity = _, force) : stableVelocity;
};
return force; return force;
} }