From 3f21a875df46593b1eb4510288732510252c21db Mon Sep 17 00:00:00 2001 From: Pitchaya Boonsarngsuk <2285135b@student.gla.ac.uk> Date: Wed, 31 Jan 2018 16:01:54 +0000 Subject: [PATCH] Refactor and comment hybrid simulation --- .../neighbourSampling.js | 1 - src/hybridSimulation.js | 127 +++++++++++------- 2 files changed, 76 insertions(+), 52 deletions(-) diff --git a/examples/js/src/neighbourSampling-papaparsing/neighbourSampling.js b/examples/js/src/neighbourSampling-papaparsing/neighbourSampling.js index 2f9a652..58a59ca 100644 --- a/examples/js/src/neighbourSampling-papaparsing/neighbourSampling.js +++ b/examples/js/src/neighbourSampling-papaparsing/neighbourSampling.js @@ -25,6 +25,5 @@ function startNeighbourSamplingSimulation() { .on("end", ended) .force(forceName, force); // Restart the simulation. - console.log(simulation.force(forceName).neighbourSize(), simulation.force(forceName).sampleSize()); simulation.restart(); } diff --git a/src/hybridSimulation.js b/src/hybridSimulation.js index 8861d05..4a82ca5 100644 --- a/src/hybridSimulation.js +++ b/src/hybridSimulation.js @@ -4,13 +4,33 @@ import interpBruteForce from "./interpolation/interpBruteForce"; import interpolationPivots from "./interpolation/interpolationPivots"; 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) { var SAMPLE_ITERATIONS = 300, FULL_ITERATIONS = 20, interpDistanceFn, PIVOTS = false, - NUMPIVOTS = 3, + NUM_PIVOTS = 3, INTERP_FINE_ITS = 20, sample = [], remainder = [], @@ -18,7 +38,7 @@ export default function (sim, forceS, forceF) { forceSample = forceS, forceFull = forceF, event = d3.dispatch("sampleTick", "fullTick", "startInterp", "end"), - toInit = true, + initAlready = false, nodes, alreadyRanIterations, hybrid; @@ -26,36 +46,32 @@ export default function (sim, forceS, forceF) { if(simulation != undefined) initSimulation(); if(forceS != undefined || forceF != undefined) initForces(); + // Performed on first run function initialize() { - toInit = false; + initAlready = true; console.log("Initializing Hybrid"); alreadyRanIterations = 0; simulation - .on("tick", function () { - event.call("sampleTick"); - console.log("InternalTick"); - alreadyRanIterations++; - if(alreadyRanIterations >= SAMPLE_ITERATIONS){ - ended(); - } - }) - .on("end", ended) + .on("tick", sampleTick) + .on("end", sampleEnded) .nodes(sample) .force("Sample force", forceSample); console.log("Initialized Hybrid"); } function initForces(){ - if (typeof forceSample.stableVelocity == 'function' && - typeof forceSample.stableVeloHandler == 'function') { + if (forceSample.stableVelocity && forceSample.stableVeloHandler) { forceSample - .stableVelocity(0.000001) - .stableVeloHandler(ended); + .stableVelocity(0.000001) //TODO + .stableVeloHandler(sampleEnded); } - if(interpDistanceFn === undefined && - typeof forceFull.distance == 'function') { - interpDistanceFn = forceFull.distance(); + // Set default value for interpDistanceFn if not been specified yet + if(interpDistanceFn === undefined) { + if(forceFull.distance == 'function') + interpDistanceFn = forceFull.distance(); + else + interpDistanceFn = constant(300); } } @@ -71,55 +87,64 @@ export default function (sim, forceS, forceF) { remainder = sets.remainder; } - function ended() { - console.log("Ended sample simulation"); + // Sample simulation ticked 1 frame, keep track of number of iterations here. + 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"); simulation.stop(); 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].vy=0; } - alert('About to interpolate'); + + let p1 = performance.now(); if (PIVOTS) { - interpolationPivots(sample, remainder, NUMPIVOTS, distanceFn, INTERP_FINE_ITS); + interpolationPivots(sample, remainder, NUM_PIVOTS, interpDistanceFn, INTERP_FINE_ITS); } 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"); - alert('About to Full run'); - if (FULL_ITERATIONS==0) { + alreadyRanIterations = 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"); return; } - fullSimulation = d3.forceSimulation() - .stop() - .alphaDecay(1 - Math.pow(0.001, 1 / FULL_ITERATIONS)); - - - 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(); + simulation + .on("tick", fullTick) + .force("Full force", forceFull) + .restart(); } return hybrid = { restart: function () { - if(toInit) initialize(); + if(!initAlready) initialize(); simulation.restart(); return hybrid; }, @@ -134,7 +159,7 @@ export default function (sim, forceS, forceF) { }, numPivots: function (_) { - return arguments.length ? (NUMPIVOTS = +_, hybrid) : NUMPIVOTS; + return arguments.length ? (NUM_PIVOTS = +_, hybrid) : NUM_PIVOTS; }, sampleIterations: function (_) {