0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-08 16:00:19 -05:00

Fix kdtree implementation.

This commit is contained in:
Andrey Antukh 2016-04-11 17:29:33 +03:00
parent c02e144012
commit 94fc4d2b88
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
2 changed files with 162 additions and 29 deletions

View file

@ -8,15 +8,15 @@
* @author Mircea Pricop <pricop@ubilabs.net>, 2012
* @author Martin Kleppe <kleppe@ubilabs.net>, 2012
* @author Ubilabs http://ubilabs.net, 2012
* @license MIT License <http://www.opensource.org/licenses/mit-license.php>
* @license MIT License <https://opensource.org/licenses/MIT>
*/
goog.provide("kdtree");
goog.provide("kdtree.KDTree");
goog.provide("kdtree.core");
goog.provide("kdtree.core.KDTree");
goog.require('goog.array');
goog.require('goog.structs.Heap');
goog.require('goog.asserts');
goog.require("kdtree.heap");
goog.require("goog.array");
goog.require("goog.asserts");
goog.scope(function() {
"use strict";
@ -35,6 +35,10 @@ goog.scope(function() {
}
}
function precision(v) {
return parseFloat(v.toFixed(6));
}
function buildTree(points, depth, parent, dimensions) {
const dim = depth % dimensions;
@ -184,7 +188,6 @@ goog.scope(function() {
node.left = null;
node.obj = nextObj;
}
}
nearest(point, maxNodes) {
@ -192,29 +195,19 @@ goog.scope(function() {
maxNodes = 1;
}
let best = new goog.structs.Heap();
let best = new kdtree.heap.MinHeap((x, y) => {
let res = x[1] - y[1];
return res;
});
const nearestSearch = (node) => {
let bestChild;
const distance = this.metric(point, node.obj);
const dimension = node.dimension;
if (best.getCount() < maxNodes || distance < best.peek()[1]) {
best.insert(-distance, [node.obj, distance]);
if (best.getCount() > maxNodes) {
best.remove();
}
}
let distance = precision(this.metric(point, node.obj));
if (best.isEmpty()) {
best.insert(-distance, [node.obj, distance]);
best.insert([node.obj, distance]);
} else {
if (distance < best.peek()[1]) {
best.insert(-distance, [node.obj, distance]);
if (best.getCount() > maxNodes) {
best.remove();
}
best.insert([node.obj, distance]);
}
}
@ -222,12 +215,13 @@ goog.scope(function() {
return;
}
let bestChild = null;
if (node.right === null) {
bestChild = node.left;
} else if (node.left === null) {
bestChild = node.right;
} else {
if (point[dimension] < node.obj[dimension]) {
if (point[node.dimension] < node.obj[node.dimension]) {
bestChild = node.left;
} else {
bestChild = node.right;
@ -235,14 +229,41 @@ goog.scope(function() {
}
nearestSearch(bestChild);
let candidate = [null, null];
for (let i = 0; i < this.dimensions; i += 1) {
if (i === node.dimension) {
candidate[i] = point[i];
} else {
candidate[i] = node.obj[i];
}
}
distance = Math.abs(this.metric(candidate, node.obj));
if (best.size < maxNodes || distance < best.peek()[1]) {
let otherChild;
if (bestChild === node.left) {
otherChild = node.right;
} else {
otherChild = node.left;
}
if (otherChild !== null) {
nearestSearch(otherChild);
}
}
}
if(this.root) {
nearestSearch(this.root);
}
result = best.getValues();
result.sort((x) => x[1]);
const result = [];
for (let i=0; i < (Math.min(maxNodes, best.size)); i++) {
result.push(best.removeHead());
}
return result;
}
@ -274,8 +295,8 @@ goog.scope(function() {
};
// Types
kdtree.KDTree = KDTree;
kdtree.core.KDTree = KDTree;
// Factory functions
kdtree.create2d = create2d;
kdtree.core.create2d = create2d;
});

112
vendor/kdtree/heap.js vendored Normal file
View file

@ -0,0 +1,112 @@
/**
* kdtree
*
* Is a modified and google closure adapted kdtree implementation
* of https://github.com/ubilabs/kd-tree-javascript.
*
* @author Andrey Antukh <niwi@niwi.nz>, 2016
* @license MIT License <https://opensource.org/licenses/MIT>
*/
goog.provide("kdtree.heap");
goog.provide("kdtree.heap.MinHeap");
goog.scope(function() {
"use strict";
const compare = (x,y) => x-y;
class MinHeap {
constructor(cmp) {
this.cmp = cmp || compare;
this.heap = [];
this.size = 0;
}
insert(item) {
const heap = this.heap;
let index = this.size++;
let parent = (index-1)>>1;
heap[index] = item;
while ((index > 0) && this.cmp(heap[parent], item) > 0) {
const tmp = heap[parent];
heap[parent] = heap[index];
heap[index] = tmp;
index = parent;
parent = (index-1)>>1;
}
}
isEmpty() {
return this.size === 0;
}
peek() {
return this.heap[0];
}
removeHead() {
const heap = this.heap;
const cmp = this.cmp;
if (this.size === 0) {
return null;
}
const head = heap[0];
this._bubble(0);
return head;
}
remove(item) {
const heap = this.heap;
for (let i = 0; i < this.size; ++i) {
if (heap[i] === item) {
this._bubble(i);
return true;
}
}
return false;
}
_bubble(index) {
const heap = this.heap;
const cmp = this.cmp;
heap[index] = heap[--this.size];
heap[this.size] = null;
while (true) {
const leftIndex = (index<<1)+1;
const rightIndex = (index<<1)+2;
let minIndex = index;
if (leftIndex < this.size && cmp(heap[leftIndex], heap[minIndex]) < 0) {
minIndex = leftIndex;
}
if (rightIndex < this.size && cmp(heap[rightIndex], heap[minIndex]) < 0) {
minIndex = rightIndex;
}
if (minIndex !== index) {
const tmp = heap[index];
heap[index] = heap[minIndex];
heap[minIndex] = tmp;
index = minIndex;
} else {
break;
}
}
}
}
kdtree.heap.MinHeap = MinHeap;
});