function doInterpolationPivots(sampleSet, remainderSet, interpSubset, properties) { var distance = calculateDistancePoker; // var distance = calculateEuclideanDistance; // Pivot based parent finding var numBuckets = Math.floor(Math.sqrt(sampleSet.length)), // numPivots = Math.floor(Math.sqrt(sampleSet.length)), numPivots = 3, parents = [], maxDists = [], bucketWidths = [], pivotsBuckets = []; console.log("Parents, pivots=", numPivots); var pivots = createRandomSample(sampleSet.concat(remainderSet), sampleSet.length, numPivots); for (var i = 0; i < numPivots; i++) { pivotsBuckets[i] = []; for (var j = 0; j < numBuckets; j++) { pivotsBuckets[i][j] = []; } } // Pre-processing var fullDists = [] for (var i = 0; i < sampleSet.length; i++) { fullDists[i] = []; } 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, properties); // console.log(dist, c1, c2); if (dist > maxDist) { maxDist = dist; } fullDists[i][j] = dist; } else { fullDists[i][j] = 0.0001; } } maxDists.push(maxDist); bucketWidths.push(maxDist / numBuckets); } // console.log(fullDists); 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]); } } for (var i = 0; i < remainderSet.length; i++) { var node = remainderSet[i], minNode = sampleSet[0], minDist = 0, sampleCache = []; // Pivot based parent search 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, properties); 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, properties); if (dist <= clDist) { clDist = dist; minNode = bucketContents[w]; } } } } } for (var k = 0; k < interpSubset.length; k++) { sampleCache[k] = distance(node, interpSubset[k], properties); } var radius = distance(node, minNode, properties); 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 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; }