From df652ffec6937fcd7edacae128f30374f638e7c1 Mon Sep 17 00:00:00 2001 From: Pitchaya Boonsarngsuk <2285135b@student.gla.ac.uk> Date: Wed, 24 Jan 2018 08:51:09 +0000 Subject: [PATCH] Change velocity-difference threshold calculation method and misc small non-functional changes (reverted from commit bb18055c6648f73076d3d8a0ffdb49f72e0d159a) --- src/interpolation/interpolationPivots.js | 325 ++++++++++++++++++----- 1 file changed, 257 insertions(+), 68 deletions(-) diff --git a/src/interpolation/interpolationPivots.js b/src/interpolation/interpolationPivots.js index 0ccd076..ea6cc53 100644 --- a/src/interpolation/interpolationPivots.js +++ b/src/interpolation/interpolationPivots.js @@ -1,98 +1,287 @@ -import {pointOnCircle, takeSampleFrom} from "./helpers"; -import {placeNearToNearestNeighbour} from "./interpCommon"; +export default function(sampleSet, remainderSet, interpSubset, nPivots, distanceFunction) { + var distance = distanceFunction; -export default function(sampleSet, remainderSet, numPivots, distanceFn) { // Pivot based parent finding - let numBuckets = Math.floor(Math.sqrt(sampleSet.length)); - let pivots = takeSampleFrom(sampleSet, numPivots); - console.log("Pivots", pivots); - // Common temporary variables - let i, j, pivot, sample, bucketWidth; // Temp var, declared seperately to avoid GC - let pivotsBuckets = []; // [ For each Pivot:[For each bucket:[each point in bucket]] ] + var numBuckets = Math.floor(Math.sqrt(sampleSet.length)), + numPivots = nPivots, + parents = [], + maxDists = [], + bucketWidths = [], + pivotsBuckets = []; - for (i = 0; i < numPivots; i++) { + console.log("Parents, pivots=", numPivots); + + var pivots = createRandomSample(sampleSet.concat(remainderSet), sampleSet.length, numPivots); + + for (var i = 0; i < numPivots; i++) { pivotsBuckets[i] = []; - for (j = 0; j < numBuckets; j++) { + for (var j = 0; j < numBuckets; j++) { pivotsBuckets[i][j] = []; } } - // Pre-calculate distance between each sample to each pivot - let distCache = [], // [ For each Sample:[For each Pivot: distance] ] - bucketWidths = []; // [ For each Pivot: width of each bucket ] + // Pre-processing + var fullDists = [] + for (var i = 0; i < sampleSet.length; i++) { + fullDists[i] = []; + } - for (i = 0; i < sampleSet.length; i++) - distCache[i] = []; - - for (j = 0; j < numPivots; j++) { - pivot = pivots[j], - maxDist = -1; - - for (i = 0; i < sampleSet.length; i++) { - sample = sampleSet[i]; - if (pivot !== sample) { - distCache[i][j] = distanceFn(pivot, sample); - if (distCache[i][j] > maxDist) - maxDist = distCache[i][j]; + for (var j = 0, maxDist = -1; j < numPivots; j++) { + var c1 = pivots[j]; + for (var i = 0; i < sampleSet.length; i++) { + var c2 = sampleSet[i]; + if (c1 !== c2) { + var dist = distance(c1, c2, props, norm); + // console.log(dist, c1, c2); + if (dist > maxDist) { + maxDist = dist; + } + fullDists[i][j] = dist; } else { - distCache[i][j] = 0; + fullDists[i][j] = 0.0001; } } - + maxDists.push(maxDist); bucketWidths.push(maxDist / numBuckets); } - // console.log(distCaches); + // console.log(fullDists); - // Put samples (pivot included) into buckets - for (j = 0; j < numPivots; j++) { - bucketWidth = bucketWidths[j]; - for (i = 0; i < sampleSet.length; i++) { - sample = sampleSet[i]; - pivotsBuckets[j][Math.floor(distCache[i][j] / bucketWidth)].push(sample); + for (var j = 0; j < numPivots; j++) { + var bucketWidth = bucketWidths[j]; + for (var i = 0; i < sampleSet.length; i++) { + var tmp = pivotsBuckets[j][Math.floor((fullDists[i][j] - 0.0001) / bucketWidth)]; + // pivotsBuckets[j][Math.floor((fullDists[i][j] - 0.0001) / bucketWidth)].push(sampleSet[i]); + // console.log(tmp, i, j, bucketWidth, Math.floor((fullDists[i][j] - 0.0001) / bucketWidth)); + tmp.push(sampleSet[i]); } } - - //Plot each of the remainder nodes - for (let node of remainderSet) { - let sampleSubsetDistanceCache = [], - sampleSubset = takeSampleFrom(sampleSet, Math.sqrt(sampleSet.length)).sample; + for (var i = 0; i < remainderSet.length; i++) { + var node = remainderSet[i], + minNode = sampleSet[0], + minDist = 0, + sampleCache = []; // Pivot based parent search - for (let p = 0; p < numPivots; p++) { - let pivot = pivots[p]; - let bucketWidth = bucketWidths[p]; - let dist = distanceFn(node, pivot); - let index = sampleSubset.indexOf(pivot); - if (index !== -1) - sampleSubsetDistanceCache[index] = dist; - - let bucketNumber = Math.floor(dist / bucketWidth); - if (bucketNumber >= numBuckets) { - bucketNumber = numBuckets - 1; - } else if (bucketNumber < 0) { // Should never be negative anyway - bucketNumber = 0; - } - - let clDist, minNode; - for (let candidateNode of pivotsBuckets[p][bucketNumber]) { - - dist = distanceFn(candidateNode, node); - if (candidateNode <= clDist) { - clDist = candidateNode; - minNode = bucketContents[w]; + var node = remainderSet[i]; + var clDist = Number.MAX_VALUE; + for (var p = 0; p < numPivots; p++) { + var comp = pivots[p]; + var bucketWidth = bucketWidths[p]; + if (node !== comp) { + var dist = distance(node, comp, props, norm); + var bNum = Math.floor((dist - 0.0001) / bucketWidth); + if (bNum >= numBuckets) { + bNum = numBuckets - 1; + } else if (bNum < 0) { + bNum = 0; + } + var bucketContents = pivotsBuckets[p][bNum]; + for (var w = 0; w < bucketContents.length; w++) { + var c1 = bucketContents[w]; + if (c1 != node) { + dist = distance(c1, node, props, norm); + if (dist <= clDist) { + clDist = dist; + minNode = bucketContents[w]; + } + } } } } - for (let k = 0; k < sampleSubset.length; k++) { - if (sampleSubsetDistanceCache[k] !== undefined) - sampleSubsetDistanceCache[k] = distanceFn(node, sampleSubset[k]); + + for (var k = 0; k < interpSubset.length; k++) { + sampleCache[k] = distance(node, interpSubset[k], props, norm); } - let radius = distanceFn(node, minNode); - placeNearToNearestNeighbour(node, minNode, radius, sampleSubset, sampleSubsetDistanceCache); + var radius = distance(node, minNode, props, norm); + placeNearToNearestNeighbour(node, minNode, interpSubset, sampleCache, radius); } } + + +function placeNearToNearestNeighbour(node, minNode, sample, sampleCache, radius) { + var + dist0 = 0.0, + dist90 = 0.0, + dist180 = 0.0, + dist270 = 0.0, + lowBound = 0.0, + highBound = 0.0; + + dist0 = sumDistToSample(node, centerPoint(0, radius, minNode.x, minNode.y), sample, sampleCache); + dist90 = sumDistToSample(node, centerPoint(90, radius, minNode.x, minNode.y), sample, sampleCache); + dist180 = sumDistToSample(node, centerPoint(180, radius, minNode.x, minNode.y), sample, sampleCache); + dist270 = sumDistToSample(node, centerPoint(270, radius, minNode.x, minNode.y), sample, sampleCache); + + // console.log(dist0, dist90, dist180, dist270); + + // Determine the closest quadrant + + if (dist0 == dist180) { + if (dist90 > dist270) + lowBound = highBound = 270; + else + lowBound = highBound = 90; + + } else if (dist90 == dist270) { + if (dist0 > dist180) + lowBound = highBound = 180; + else + lowBound = highBound = 0; + } else if (dist0 > dist180) { + if (dist90 > dist270) { + lowBound = 180; + highBound = 270; + } else { + lowBound = 90; + highBound = 180; + } + } else { + if (dist90 > dist270) { + lowBound = 270; + highBound = 360; + } else { + lowBound = 0; + highBound = 90; + } + } + + var angle = binarySearch(lowBound, highBound, minNode.x, minNode.y, radius, node, sample, sampleCache); + var newPoint = centerPoint(angle, radius, minNode.x, minNode.y); + + // console.log(newPoint); + node.x = newPoint.x; + node.y = newPoint.y; + + // for (var i = 0; i < 20; i++) { + // var forces = sumForcesToSample(node, sample, sampleCache); + // // console.log(forces); + // node.x += forces.x; + // node.y += forces.y; + // } + +} + + +function centerPoint(angle, radius, posX, posY) { + var x = posX + Math.cos(toRadians(angle) * radius); + var y = posY + Math.sin(toRadians(angle) * radius); + + return { + x: x, + y: y + }; +} + +function toRadians(degrees) { + return degrees * (Math.PI / 180); +} + +function sumDistToSample(node, point, sample, sampleCache) { + var total = 0.0; + // console.log(total, sample); + + for (var i = 0; i < sample.length; i++) { + var s = sample[i]; + var realDist = Math.hypot(s.x - point.x, s.y - point.y); + var desDist = sampleCache[i]; + total += Math.abs(realDist - desDist); + } + + return total; +} + + +function sumForcesToSample(node, sample, sampleCache) { + var x = 0, + y = 0, + // len = 0, + dist = 0, + force, + SPRING_FORCE = 0.7; + + for (var i = 0, unitX, unitY; i < sample.length; i++) { + var s = sample[i]; + if (s !== node) { + unitX = s.x - node.x; + unitY = s.y - node.y; + + // Normalize coordinates + len = Math.sqrt(unitX * unitX + unitY * unitY); + unitX /= len; + unitY /= len; + + console.log(unitX, unitY); + + var realDist = Math.sqrt(unitX * unitX + unitY * unitY); + var desDist = sampleCache[i]; + dist += realDist - desDist; + force = (SPRING_FORCE * dist); + + x += unitX * force; + y += unitY * force; + } + + x *= (1.0 / sample.length); + y *= (1.0 / sample.length); + + return { + x: x, + y: y + }; + } +} + +function createRandomSample(nodes, max, size) { + var randElements = []; + + for (var i = 0; i < size; ++i) { + // Stop when no new elements can be found. + if (randElements.size >= nodes.length) { + break; + } + var rand = 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 = Math.floor((Math.random() * max)); + } + randElements.push(nodes[rand]); + } + + return randElements; +} + + +function binarySearch(lb, hb, x, y, r, node, sample, sampleCache) { + while (lb <= hb) { + var mid = Math.round((lb + hb) / 2); + + if ((mid === lb) || (mid === hb)) { + if (sumDistToSample(node, centerPoint(lb, r, x, y), sample, sampleCache) >= + sumDistToSample(node, centerPoint(hb, r, x, y), sample, sampleCache)) { + return hb; + } else { + return lb; + } + } else { + var distMidLeft = sumDistToSample(node, centerPoint(mid + 1, r, x, y), sample, sampleCache); + var distMidRight = sumDistToSample(node, centerPoint(mid - 1, r, x, y), sample, sampleCache); + var distMid = sumDistToSample(node, centerPoint(mid, r, x, y), sample, sampleCache); + + if (distMid > distMidLeft) { + lb = mid + 1; + } else if (distMid > distMidRight) { + hb = mid - 1; + } else { + return mid; + } + } + } + + return -1; +}