diff --git a/src/hybridSimulation.js b/src/hybridSimulation.js index 4a82ca5..09e0ced 100644 --- a/src/hybridSimulation.js +++ b/src/hybridSimulation.js @@ -108,9 +108,9 @@ export default function (sim, forceS, forceF) { function sampleEnded() { event.call("startInterp"); - simulation.stop(); simulation.force("Sample force", null); + // Reset velocity of all nodes for (let i=sample.length-1; i>=0; i--){ sample[i].vx=0; sample[i].vy=0; diff --git a/src/interpolation/interpBruteForce.js b/src/interpolation/interpBruteForce.js index e887460..8bc353e 100644 --- a/src/interpolation/interpBruteForce.js +++ b/src/interpolation/interpBruteForce.js @@ -2,10 +2,14 @@ import {pointOnCircle, takeSampleFrom} from "./helpers"; import {placeNearToNearestNeighbour} from "./interpCommon"; /** - * Perform interpolation where the nearest neighbour is found by brute-force. + * Perform interpolation where the "parent" node is found by brute-force. + * A "parent" of a node to be interpolated is a node whose position in 2D space + * is already known and have the least high-dimensional distance to the node in + * question. * For each point to be interpolated: - * - Phase 1: find a nearest beighbour by comparing high-d distance against - each point already in the graph. + * - Phase 1: find the "parent" by comparing high-d distance against every + points already plotted on the graph. + this is essentially a nearest neighbour finding problem. * - Phase 2 and 3 are passed onto placeNearToNearestNeighbour * @param {list} sampleSet - nodes already plotted on the 2D graph * @param {list} remainderSet - nodes to be interpolated onto the graph diff --git a/src/interpolation/interpCommon.js b/src/interpolation/interpCommon.js index 70bd7fb..2beb10b 100644 --- a/src/interpolation/interpCommon.js +++ b/src/interpolation/interpCommon.js @@ -1,6 +1,29 @@ import {pointOnCircle, sumDistError} from "./helpers"; import jiggle from "../jiggle"; +/** + * Phase 2 and 3 of each node to be interpolated. + * After a parent and high-dimensional distance to parent is found, imagine a + * circle around the parent with the distance as radius. + * Phase 2: Determine the quadrant of the circle the node is likely to be in. + * Then, perform a binary search on the quardrant to find the best + * position on the quadrant and place the node there. + * The sum of ( difference between high-low-d distances between the + * node and other sampled nodes ) is used to determine the prefered + * position (lower is better). + * Phase 3: A subset of points whose 2D position is known are selected. + For several iterations: + * Calculate the total force between the node and other nodes in the subset + * Apply the force to the node's position + * @param {object} node - the node to be interpolated + * @param {object} nearNeighbour - the parent of the node + * @param {number} radius - high-dimensional distance to the parent + * @param {list} sampleSubset - subset for phase 3, list of nodes + * @param {list} realDistances - high-dimensional distances from the node to each + of the node in sampleSubset, list of numbers + index must correspond to sampleSubset + * @param {Integer} endingIts - Number of iterations for phase 3 + */ export function placeNearToNearestNeighbour(node, nearNeighbour, radius, sampleSubset, realDistances, endingIts) { let dist0 = sumDistError(pointOnCircle(nearNeighbour.x, nearNeighbour.y, 0, radius), sampleSubset, realDistances), @@ -39,6 +62,7 @@ export function placeNearToNearestNeighbour(node, nearNeighbour, radius, sampleS } } + // Determine the angle let angle = binarySearchMin(lowBound, highBound, function(angle){ return sumDistError(pointOnCircle(nearNeighbour.x, nearNeighbour.y, angle, radius), sampleSubset, realDistances); @@ -47,6 +71,7 @@ export function placeNearToNearestNeighbour(node, nearNeighbour, radius, sampleS node.x = newPoint.x; node.y = newPoint.y; + // Phase 3 let multiplier = 1/sampleSubset.length, sumForces; @@ -60,16 +85,15 @@ export function placeNearToNearestNeighbour(node, nearNeighbour, radius, sampleS function sumForcesToSample(node, samples, sampleCache) { let nodeVx = 0, nodeVy = 0, - x, y, l; + x, y, l, i, sample; - for (let i = samples.length-1; i >=0 ; i--) { - let sample = samples[i]; + for (i = samples.length-1; i >=0 ; i--) { + sample = samples[i]; // jiggle so l won't be zero and divide by zero error after this x = node.x - sample.x || jiggle(); y = node.y - sample.y || jiggle(); l = Math.sqrt(x * x + y * y); - l = (l - sampleCache[i]) / l; x *= l, y *= l; nodeVx -= x; diff --git a/src/interpolation/interpolationPivots.js b/src/interpolation/interpolationPivots.js index f1de9eb..3f3a059 100644 --- a/src/interpolation/interpolationPivots.js +++ b/src/interpolation/interpolationPivots.js @@ -2,10 +2,10 @@ import {pointOnCircle, takeSampleFrom} from "./helpers"; import {placeNearToNearestNeighbour} from "./interpCommon"; /** - * Perform interpolation where the near neighbour is estimated by pivot-based searching. + * Perform interpolation where the "parent" node is is estimated by pivot-based searching. * - Pre-processing: assign random samples as pivots, * put the others in each pivot's bucket. - * ie. non-pivot sample X may be in + * ie. a non-pivot sample X may be in * - bucket 3 of pivot A, * - bucket 1 of pivot B, * - bucket 5 of pivot C, @@ -13,7 +13,8 @@ import {placeNearToNearestNeighbour} from "./interpCommon"; * For each point to be interpolated: * - Phase 1: for each pivot: compare distance against the pivot * compare against other points in the same bucket of that pivot - * note down the nearest neighbour found + * note down the parent found + * this is essentially a near neighbour estimation problem. * - Phase 2 and 3 are passed onto placeNearToNearestNeighbour * @param {list} sampleSet - nodes already plotted on the 2D graph * @param {list} remainderSet - nodes to be interpolated onto the graph