169 lines
5.3 KiB
JavaScript
169 lines
5.3 KiB
JavaScript
import { dispatch } from "d3-dispatch";
|
|
import constant from "./constant";
|
|
import interpolation from "./interpolation";
|
|
import interpolationPivots from "./interpolationPivots";
|
|
import neighbourSamplingDistance from "./neighbourSamplingDistance";
|
|
|
|
export default function (nodes, config) {
|
|
|
|
var hybrid,
|
|
fullSimulation,
|
|
SAMPLE_ITERATIONS = config.hasOwnProperty("iteration") ? config.iteration : 300,
|
|
neighbourSize = config.hasOwnProperty("neighbourSize") ? config.neighbourSize : 6,
|
|
sampleSize = config.hasOwnProperty("sampleSize") ? config.sampleSize : 3,
|
|
distanceRange = config.hasOwnProperty("distanceRange") ? config.distanceRange : 10,
|
|
FULL_ITERATIONS = config.hasOwnProperty("fullIterations") ? config.fullIterations : 20,
|
|
FullneighbourSize = config.hasOwnProperty("fullNeighbourSize") ? config.fullNeighbourSize : 6,
|
|
FullsampleSize = config.hasOwnProperty("fullSampleSize") ? config.fullSampleSize : 3,
|
|
FulldistanceRange = config.hasOwnProperty("fullDistanceRange") ? config.fullDistanceRange : 10,
|
|
distanceFn = config.hasOwnProperty("distanceFn") ? config.distanceFn : constant(300),
|
|
PIVOTS = config.hasOwnProperty("pivots") ? config.pivots : false,
|
|
NUMPIVOTS = config.hasOwnProperty("numPivots") ? config.numPivots : 3,
|
|
event = d3.dispatch("sampleTick", "fullTick", "startFull", "end");
|
|
|
|
var sets = sampleFromNodes(nodes, Math.sqrt(nodes.length));
|
|
var sample = sets.sample;
|
|
var remainder = sets.remainder;
|
|
var sampleSubset = sampleFromNodes(sample, Math.sqrt(sample.length)).sample;
|
|
|
|
var sampleSimulation = d3.forceSimulation(sample)
|
|
.stop()
|
|
.alphaDecay(1 - Math.pow(0.001, 1 / SAMPLE_ITERATIONS))
|
|
.on("tick", function () {
|
|
event.call("sampleTick", sampleSimulation);
|
|
})
|
|
.on("end", ended);
|
|
|
|
sampleSimulation.force("forces", neighbourSamplingDistance()
|
|
.neighbourSize(neighbourSize)
|
|
.sampleSize(sampleSize)
|
|
.distanceRange(distanceRange)
|
|
.distance(distanceFn)
|
|
);
|
|
sampleSimulation.alpha(1).restart();
|
|
|
|
function ended() {
|
|
event.call("startFull");
|
|
console.log("Ended sample simulation");
|
|
/*
|
|
if (PIVOTS) {
|
|
interpolationPivots(sample, remainder, interpSubset, NUMPIVOTS, distance);
|
|
} else {
|
|
interpolation(sample, remainder, interpSubset, distance);
|
|
}
|
|
*/
|
|
event.call("fullTick");
|
|
alert('About to Full run');
|
|
if (FULL_ITERATIONS==0) {
|
|
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)
|
|
)
|
|
.on("tick", function () {
|
|
event.call("fullTick", fullSimulation);
|
|
})
|
|
.on("end", function () {
|
|
event.call("end", fullSimulation);
|
|
})
|
|
.nodes(nodes)
|
|
.alpha(1).restart();
|
|
}
|
|
|
|
return hybrid = {
|
|
distance: function (_) {
|
|
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), hybrid) : distance;
|
|
},
|
|
|
|
stop: function () {
|
|
if (typeof sampleSimulation !== 'undefined') {
|
|
sampleSimulation.stop();
|
|
}
|
|
if (typeof fullSimulation !== 'undefined') {
|
|
fullSimulation.stop();
|
|
}
|
|
return hybrid;
|
|
},
|
|
|
|
pivots: function (_) {
|
|
return arguments.length ? (PIVOTS = _, hybrid) : PIVOTS;
|
|
},
|
|
|
|
numPivots: function (_) {
|
|
return arguments.length ? (NUMPIVOTS = +_, hybrid) : NUMPIVOTS;
|
|
},
|
|
|
|
multiplier: function (_) {
|
|
return arguments.length ? (MULTIPLIER = +_, hybrid) : MULTIPLIER;
|
|
},
|
|
|
|
sampleIterations: function (_) {
|
|
return arguments.length ? (SAMPLE_ITERATIONS = +_, hybrid) : SAMPLE_ITERATIONS;
|
|
},
|
|
|
|
fullIterations: function (_) {
|
|
return arguments.length ? (FULL_ITERATIONS = +_, hybrid) : FULL_ITERATIONS;
|
|
},
|
|
|
|
neighbourSize: function (_) {
|
|
return arguments.length ? (neighbourSize = +_, hybrid) : neighbourSize;
|
|
},
|
|
|
|
sampleSize: function (_) {
|
|
return arguments.length ? (sampleSize = +_, hybrid) : sampleSize;
|
|
},
|
|
|
|
on: function (name, _) {
|
|
return arguments.length > 1 ? (event.on(name, _), hybrid) : event.on(name);
|
|
},
|
|
|
|
sample: function (_) {
|
|
return arguments.length ? (sample = _, hybrid) : sample;
|
|
},
|
|
|
|
remainder: function (_) {
|
|
return arguments.length ? (remainder = _, hybrid) : remainder;
|
|
},
|
|
|
|
stress: function () {
|
|
return fullSimulation.force("neighbourSampling").stress();
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
|
|
function sampleFromNodes(nodes, size) {
|
|
let randElements = [],
|
|
max = nodes.length;
|
|
|
|
for (var i = 0; i < size; ++i) {
|
|
var rand = nodes[Math.floor((Math.random() * max))];
|
|
// If the rand is already in random list or in exclude list
|
|
// ignore it and get a new value.
|
|
while (randElements.includes(rand)) {
|
|
rand = nodes[Math.floor((Math.random() * max))];
|
|
}
|
|
randElements.push(rand);
|
|
}
|
|
|
|
var remainder = nodes.filter(function (node) {
|
|
return !randElements.includes(node);
|
|
});
|
|
|
|
return {
|
|
sample: randElements,
|
|
remainder: remainder
|
|
};
|
|
}
|