Refactor and comment hybrid simulation

This commit is contained in:
Pitchaya Boonsarngsuk
2018-01-31 16:01:54 +00:00
parent e700df5059
commit 3f21a875df
2 changed files with 76 additions and 52 deletions

View File

@@ -25,6 +25,5 @@ function startNeighbourSamplingSimulation() {
.on("end", ended) .on("end", ended)
.force(forceName, force); .force(forceName, force);
// Restart the simulation. // Restart the simulation.
console.log(simulation.force(forceName).neighbourSize(), simulation.force(forceName).sampleSize());
simulation.restart(); simulation.restart();
} }

View File

@@ -4,13 +4,33 @@ import interpBruteForce from "./interpolation/interpBruteForce";
import interpolationPivots from "./interpolation/interpolationPivots"; import interpolationPivots from "./interpolation/interpolationPivots";
import {takeSampleFrom} from "./interpolation/helpers"; import {takeSampleFrom} from "./interpolation/helpers";
/**
* An implementation of Chalmers, Morrison, and Ross' 2002 hybrid layout
* algorithm with an option to use the 2003 pivot-based near neighbour searching
* method.
* It performs the 1996 neighbour sampling spring simulation model, on a
* "sample set", sqrt(n) samples of the data.
* Other data points are then interpolated into the model.
* Finally, another spring simulation may be performed on the entire dataset to
* clean up the model.
* @param {object} sim - D3 Simulation object
* @param {object} forceS - Pre-configured D3 force object for the sample set.
The ending condition will be re-configured.
Neighbour sampling force is expected, but other D3
forces may also work.
* @param {object} forceF - Pre-configured D3 force object for the simultion ran
on the entire dataset at the end.
Neighbour sampling force is expected, but other D3
forces may also work.
The force should not have any ending condition.
*/
export default function (sim, forceS, forceF) { export default function (sim, forceS, forceF) {
var var
SAMPLE_ITERATIONS = 300, SAMPLE_ITERATIONS = 300,
FULL_ITERATIONS = 20, FULL_ITERATIONS = 20,
interpDistanceFn, interpDistanceFn,
PIVOTS = false, PIVOTS = false,
NUMPIVOTS = 3, NUM_PIVOTS = 3,
INTERP_FINE_ITS = 20, INTERP_FINE_ITS = 20,
sample = [], sample = [],
remainder = [], remainder = [],
@@ -18,7 +38,7 @@ export default function (sim, forceS, forceF) {
forceSample = forceS, forceSample = forceS,
forceFull = forceF, forceFull = forceF,
event = d3.dispatch("sampleTick", "fullTick", "startInterp", "end"), event = d3.dispatch("sampleTick", "fullTick", "startInterp", "end"),
toInit = true, initAlready = false,
nodes, nodes,
alreadyRanIterations, alreadyRanIterations,
hybrid; hybrid;
@@ -26,36 +46,32 @@ export default function (sim, forceS, forceF) {
if(simulation != undefined) initSimulation(); if(simulation != undefined) initSimulation();
if(forceS != undefined || forceF != undefined) initForces(); if(forceS != undefined || forceF != undefined) initForces();
// Performed on first run
function initialize() { function initialize() {
toInit = false; initAlready = true;
console.log("Initializing Hybrid"); console.log("Initializing Hybrid");
alreadyRanIterations = 0; alreadyRanIterations = 0;
simulation simulation
.on("tick", function () { .on("tick", sampleTick)
event.call("sampleTick"); .on("end", sampleEnded)
console.log("InternalTick");
alreadyRanIterations++;
if(alreadyRanIterations >= SAMPLE_ITERATIONS){
ended();
}
})
.on("end", ended)
.nodes(sample) .nodes(sample)
.force("Sample force", forceSample); .force("Sample force", forceSample);
console.log("Initialized Hybrid"); console.log("Initialized Hybrid");
} }
function initForces(){ function initForces(){
if (typeof forceSample.stableVelocity == 'function' && if (forceSample.stableVelocity && forceSample.stableVeloHandler) {
typeof forceSample.stableVeloHandler == 'function') {
forceSample forceSample
.stableVelocity(0.000001) .stableVelocity(0.000001) //TODO
.stableVeloHandler(ended); .stableVeloHandler(sampleEnded);
} }
if(interpDistanceFn === undefined && // Set default value for interpDistanceFn if not been specified yet
typeof forceFull.distance == 'function') { if(interpDistanceFn === undefined) {
interpDistanceFn = forceFull.distance(); if(forceFull.distance == 'function')
interpDistanceFn = forceFull.distance();
else
interpDistanceFn = constant(300);
} }
} }
@@ -71,55 +87,64 @@ export default function (sim, forceS, forceF) {
remainder = sets.remainder; remainder = sets.remainder;
} }
function ended() { // Sample simulation ticked 1 frame, keep track of number of iterations here.
console.log("Ended sample simulation"); function sampleTick() {
event.call("sampleTick");
if(++alreadyRanIterations >= SAMPLE_ITERATIONS){
sampleEnded();
}
}
// Full simulation ticked 1 frame, keep track of number of iterations here.
function fullTick() {
event.call("fullTick");
if(++alreadyRanIterations >= FULL_ITERATIONS){
simulation.stop();
initAlready = false;
simulation.force("Full force", null);
event.call("end");
}
}
function sampleEnded() {
event.call("startInterp"); event.call("startInterp");
simulation.stop(); simulation.stop();
simulation.force("Sample force", null); simulation.force("Sample force", null);
for (let i=sample.size-1; i>=0; i--){ for (let i=sample.length-1; i>=0; i--){
sample[i].vx=0; sample[i].vx=0;
sample[i].vy=0; sample[i].vy=0;
} }
alert('About to interpolate');
let p1 = performance.now();
if (PIVOTS) { if (PIVOTS) {
interpolationPivots(sample, remainder, NUMPIVOTS, distanceFn, INTERP_FINE_ITS); interpolationPivots(sample, remainder, NUM_PIVOTS, interpDistanceFn, INTERP_FINE_ITS);
} else { } else {
interpBruteForce(sample, remainder, distanceFn, INTERP_FINE_ITS); interpBruteForce(sample, remainder, interpDistanceFn, INTERP_FINE_ITS);
} }
let p2 = performance.now();
console.log("Interpolation time: " + (p2 - p1));
event.call("fullTick"); event.call("fullTick");
alert('About to Full run'); alreadyRanIterations = 0;
if (FULL_ITERATIONS==0) { simulation
.on("tick", null)
.on("end", null) // The ending condition should be iterations count
.nodes(nodes);
if (FULL_ITERATIONS==0 || forceF === undefined || forceF === null) {
event.call("end"); event.call("end");
return; return;
} }
fullSimulation = d3.forceSimulation() simulation
.stop() .on("tick", fullTick)
.alphaDecay(1 - Math.pow(0.001, 1 / FULL_ITERATIONS)); .force("Full force", forceFull)
.restart();
fullSimulation
.force("neighbourSampling", neighbourSamplingDistance()
.neighbourSize(FullneighbourSize)
.sampleSize(FullsampleSize)
.distanceRange(FulldistanceRange)
.distance(distanceFn)
.stableVelocity(0.004)
.stableVeloHandler(function(){fullSimulation.stop(); event.call("end");})
)
.on("tick", function () {
event.call("fullTick", fullSimulation);
})
.on("end", function () {
event.call("end", fullSimulation);
})
.nodes(nodes)
.alpha(1).restart();
} }
return hybrid = { return hybrid = {
restart: function () { restart: function () {
if(toInit) initialize(); if(!initAlready) initialize();
simulation.restart(); simulation.restart();
return hybrid; return hybrid;
}, },
@@ -134,7 +159,7 @@ export default function (sim, forceS, forceF) {
}, },
numPivots: function (_) { numPivots: function (_) {
return arguments.length ? (NUMPIVOTS = +_, hybrid) : NUMPIVOTS; return arguments.length ? (NUM_PIVOTS = +_, hybrid) : NUM_PIVOTS;
}, },
sampleIterations: function (_) { sampleIterations: function (_) {