Skip to content

Commit

Permalink
Updated BinaryHeap as well.
Browse files Browse the repository at this point in the history
  • Loading branch information
PoneyClairDeLune committed Jan 25, 2025
1 parent 06cf360 commit 02cc0c0
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 24 deletions.
2 changes: 1 addition & 1 deletion dist/kd-tree.mjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"use strict";let g=class{left=null;right=null;obj;parent;dimension;constructor(t,e,i){let l=this;l.obj=t,l.parent=i,l.dimension=e}};function a(h){this.content=[],this.scoreFunction=h}a.prototype={push:function(h){this.content.push(h),this.bubbleUp(this.content.length-1)},pop:function(){let h=this.content[0],t=this.content.pop();return this.content.length>0&&(this.content[0]=t,this.sinkDown(0)),h},peek:function(){return this.content[0]},remove:function(h){for(let t=this.content.length,e=0;e<t;e++)if(this.content[e]==h){let i=this.content.pop();e!=t-1&&(this.content[e]=i,this.scoreFunction(i)<this.scoreFunction(h)?this.bubbleUp(e):this.sinkDown(e));return}throw new Error("Node not found.")},size:function(){return this.content.length},bubbleUp:function(h){for(let t=this.content[h];h>0;){let e=Math.floor((h+1)/2)-1,i=this.content[e];if(this.scoreFunction(t)<this.scoreFunction(i))this.content[e]=t,this.content[h]=i,h=e;else break}},sinkDown:function(h){for(let t=this.content.length,e=this.content[h],i=this.scoreFunction(e);;){let l=(h+1)*2,n=l-1,r=null;if(n<t){let u=this.content[n],s=this.scoreFunction(u);s<i&&(r=n)}if(l<t){let c=this.content[l],o=this.scoreFunction(c);o<(r==null?i:s)&&(r=l)}if(r!=null)this.content[h]=this.content[r],this.content[r]=e,h=r;else break}}};let j=class{#t;#i;root;get dimensions(){return this.#t}get metric(){return this.#i}#l(t,e,i){let l=this,n=e%l.#t.length;if(t.length===0)return null;if(t.length===1)return new g(t[0],n,i);t.sort((s,c)=>s[l.#t[n]]-c[l.#t[n]]);let r=t.length>>1,u=new g(t[r],n,i);return u.left=l.#l(t.slice(0,r),e+1,u),u.right=l.#l(t.slice(r+1),e+1,u),u}#n(t){let e=this;t.left&&(t.left.parent=t,e.#n(t.left)),t.right&&(t.right.parent=t,e.#n(t.right))}#g(t){this.root=t,this.#n(this.root)}#r(t,e,i){if(t===null)return e;let l=dimensions[t.dimension];return i[l]<t.obj[l]?upThis.#r(t.left,t,i):upThis.#r(t.right,t,i)}#h(t,e){let i=this;if(t===null)return null;if(t.obj===e)return t;let l=i.#t[t.dimension];return e[l]<t.obj[l]?i.#h(t.left,e):i.#h(t.right,e)}#e(t,e){let i=this;if(t===null)return null;let l=i.#t[e];if(t.dimension===e)return t.left!=null?i.#e(t.left,e):t;let n=t.obj[l],r=i.#e(t.left,e),u=i.#e(t.right,e),s=t;return r!=null&&r.obj[l]<n&&(s=r),u!=null&&u.obj[l]<s.obj[l]&&(s=u),s}#u(t){let e=this;if(t.left===null&&t.right===null){if(t.parent===null){e.root=null;return}let i=e.#t[t.parent.dimension];t.obj[i]<t.parent.obj[i]?t.parent.left=null:t.parent.right=null;return}if(t.right==null){let i=e.#e(t.left,t.dimension),l=i.obj;e.#u(i),t.right=t.left,t.left=null,t.obj=l}else{let i=e.#e(t.right,t.dimension),l=i.obj;e.#u(i),t.obj=l}}#c(t,e,i,l){i.push([t,e]),i.size()>l&&i.pop()}#s(t,e,i,l){let n=this,r=n.#t[t.dimension],u=n.#i(e,t.obj),s=[];for(let f=0;f<n.#t.length;f++)f===t.dimension?s[n.#t[f]]=e[n.#t[f]]:s[n.#t[f]]=t.obj[n.#t[f]];let c=n.#i(s,t.obj);if(t.right===null&&t.left===null){(i.size()<l||u<i.peek()[1])&&n.#c(t,u,i,l);return}let o;if(t.right===null?o=t.left:t.left===null?o=t.right:e[r]<t.obj[r]?o=t.left:o=t.right,n.#s(o,e,i,l),(i.size()<l||u<i.peek()[1])&&n.#c(t,u,i,l),i.size()<l||Math.abs(c)<i.peek()[1]){let f;o===t.left?f=t.right:f=t.left,f!=null&&n.#s(f,e,i,l)}}#f(t){return t==null?0:Math.max(this.#f(t.left),this.#f(t.right))+1}#o(t){return t==null?0:this.#o(t.left)+this.#o(t.right)+1}toJSON(t){let e=this;t||(t=e.root);let i=new g(t.obj,t.dimension,null);return t.left&&(i.left=e.toJSON(t.left)),t.right&&(i.right=e.toJSON(t.right)),i}insert(t){let e=this,i=e.#r(e.root,null,t);if(i===null){e.root=new g(t,0,null);return}let l=new g(t,(i.dimension+1)%e.#t.length,i),n=e.#t[i.dimension];t[n]<i.obj[n]?i.left=l:i.right=l}remove(t){let e=this,i=e.#h(e.root,t);i!==null&&e.#u(i)}nearest(t,e,i){let l=new a(r=>-r[1]);if(i)for(let r=0;r<e;r++)l.push([null,i]);this.root&&this.#s(this.root,t,l,e);let n=[];for(let r=0;r<Math.min(e,l.content.length);r++)l.content[r][0]&&n.push([l.content[r][0].obj,l.content[r][1]]);return n}balanceFactor(){let t=this;return t.#f(t.root)/Math.log(t.#o(t.root))/Math.LN2}constructor(t,e,i){let l=this;l.#t=i,l.#i=e,Array.isArray(t)?l.root=l.#l(t,0,null):l.#g(t,e,i)}};export{j as KDTree,a as KDTreeBinaryHeap};
function g(h){this.content=[],this.scoreFunction=h}g.prototype={push:function(h){this.content.push(h),this.bubbleUp(this.content.length-1)},pop:function(){let h=this.content[0],t=this.content.pop();return this.content.length>0&&(this.content[0]=t,this.sinkDown(0)),h},peek:function(){return this.content[0]},remove:function(h){for(let t=this.content.length,e=0;e<t;e++)if(this.content[e]==h){let n=this.content.pop();e!=t-1&&(this.content[e]=n,this.scoreFunction(n)<this.scoreFunction(h)?this.bubbleUp(e):this.sinkDown(e));return}throw new Error("Node not found.")},size:function(){return this.content.length},bubbleUp:function(h){for(let t=this.content[h];h>0;){let e=Math.floor((h+1)/2)-1,n=this.content[e];if(this.scoreFunction(t)<this.scoreFunction(n))this.content[e]=t,this.content[h]=n,h=e;else break}},sinkDown:function(h){for(let t=this.content.length,e=this.content[h],n=this.scoreFunction(e);;){let i=(h+1)*2,l=i-1,r=null;if(l<t){let u=this.content[l],s=this.scoreFunction(u);s<n&&(r=l)}if(i<t){let f=this.content[i],c=this.scoreFunction(f);c<(r==null?n:s)&&(r=i)}if(r!=null)this.content[h]=this.content[r],this.content[r]=e,h=r;else break}}};let a=class{left=null;right=null;obj;parent;dimension;constructor(t,e,n){let i=this;i.obj=t,i.parent=n,i.dimension=e}},p=class{#t;#n;root;get dimensions(){return this.#t}get metric(){return this.#n}#i(t,e,n){let i=this,l=e%i.#t.length;if(t.length===0)return null;if(t.length===1)return new a(t[0],l,n);t.sort((s,f)=>s[i.#t[l]]-f[i.#t[l]]);let r=t.length>>1,u=new a(t[r],l,n);return u.left=i.#i(t.slice(0,r),e+1,u),u.right=i.#i(t.slice(r+1),e+1,u),u}#l(t){let e=this;t.left&&(t.left.parent=t,e.#l(t.left)),t.right&&(t.right.parent=t,e.#l(t.right))}#a(t){this.root=t,this.#l(this.root)}#r(t,e,n){if(t===null)return e;let i=dimensions[t.dimension];return n[i]<t.obj[i]?upThis.#r(t.left,t,n):upThis.#r(t.right,t,n)}#h(t,e){let n=this;if(t===null)return null;if(t.obj===e)return t;let i=n.#t[t.dimension];return e[i]<t.obj[i]?n.#h(t.left,e):n.#h(t.right,e)}#e(t,e){let n=this;if(t===null)return null;let i=n.#t[e];if(t.dimension===e)return t.left!=null?n.#e(t.left,e):t;let l=t.obj[i],r=n.#e(t.left,e),u=n.#e(t.right,e),s=t;return r!=null&&r.obj[i]<l&&(s=r),u!=null&&u.obj[i]<s.obj[i]&&(s=u),s}#u(t){let e=this;if(t.left===null&&t.right===null){if(t.parent===null){e.root=null;return}let n=e.#t[t.parent.dimension];t.obj[n]<t.parent.obj[n]?t.parent.left=null:t.parent.right=null;return}if(t.right==null){let n=e.#e(t.left,t.dimension),i=n.obj;e.#u(n),t.right=t.left,t.left=null,t.obj=i}else{let n=e.#e(t.right,t.dimension),i=n.obj;e.#u(n),t.obj=i}}#f(t,e,n,i){n.push([t,e]),n.size()>i&&n.pop()}#s(t,e,n,i){let l=this,r=l.#t[t.dimension],u=l.#n(e,t.obj),s=[];for(let o=0;o<l.#t.length;o++)o===t.dimension?s[l.#t[o]]=e[l.#t[o]]:s[l.#t[o]]=t.obj[l.#t[o]];let f=l.#n(s,t.obj);if(t.right===null&&t.left===null){(n.size()<i||u<n.peek()[1])&&l.#f(t,u,n,i);return}let c;if(t.right===null?c=t.left:t.left===null?c=t.right:e[r]<t.obj[r]?c=t.left:c=t.right,l.#s(c,e,n,i),(n.size()<i||u<n.peek()[1])&&l.#f(t,u,n,i),n.size()<i||Math.abs(f)<n.peek()[1]){let o;c===t.left?o=t.right:o=t.left,o!=null&&l.#s(o,e,n,i)}}#o(t){return t==null?0:Math.max(this.#o(t.left),this.#o(t.right))+1}#c(t){return t==null?0:this.#c(t.left)+this.#c(t.right)+1}toJSON(t){let e=this;t||(t=e.root);let n=new a(t.obj,t.dimension,null);return t.left&&(n.left=e.toJSON(t.left)),t.right&&(n.right=e.toJSON(t.right)),n}insert(t){let e=this,n=e.#r(e.root,null,t);if(n===null){e.root=new a(t,0,null);return}let i=new a(t,(n.dimension+1)%e.#t.length,n),l=e.#t[n.dimension];t[l]<n.obj[l]?n.left=i:n.right=i}remove(t){let e=this,n=e.#h(e.root,t);n!==null&&e.#u(n)}nearest(t,e,n){let i=new g(r=>-r[1]);if(n)for(let r=0;r<e;r++)i.push([null,n]);this.root&&this.#s(this.root,t,i,e);let l=[];for(let r=0;r<Math.min(e,i.content.length);r++)i.content[r][0]&&l.push([i.content[r][0].obj,i.content[r][1]]);return l}balanceFactor(){let t=this;return t.#o(t.root)/Math.log(t.#c(t.root))/Math.LN2}constructor(t,e,n){let i=this;i.#t=n,i.#n=e,Array.isArray(t)?i.root=i.#i(t,0,null):i.#a(t,e,n)}};export{g as KDBinaryHeap,p as KDTree};
/**
* k-d Tree JavaScript - v1.1
*
Expand Down
136 changes: 114 additions & 22 deletions src/kd-tree/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,7 @@
* @license MIT License <http://www.opensource.org/licenses/mit-license.php>
*/

/*
WARNING!
The current code haven't received any rewrite yet.
*/

let TreeNode = class TreeNode {
left = null;
right = null;
obj;
parent;
dimension;
constructor(object, dimension, parent) {
let upThis = this;
upThis.obj = object;
upThis.parent = parent;
upThis.dimension = dimension;
};
};

// Binary heap implementation from:
// http://eloquentjavascript.net/appendix2.html
// Binary heap implementation from http://eloquentjavascript.net/appendix2.html

function BinaryHeap(scoreFunction){
this.content = [];
Expand Down Expand Up @@ -158,6 +138,118 @@ BinaryHeap.prototype = {
}
};

// Binary heap implementation from http://eloquentjavascript.net/appendix2.html
// Rewritten to use the modern syntax

let KDBinaryHeap = class KDBinaryHeap {
content = [];
scoreFunction;
push(element) {
let upThis = this;
upThis.content.push(element);
upThis.bubbleUp(upThis.content.length - 1);
};
pop() {
let upThis = this;
let result = upThis.content[0];
let end = upThis.content.pop();
if (upThis.content.length > 0) {
upThis.content[0] = end;
upThis.sinkDown(0);
};
return result;
};
peek() {
return this.content[0];
};
remove(node) {
let upThis = this;
let length = upThis.content.length;
for (let i = 0; i < length; i ++) {
if (upThis.content[i] == node) {
let end = upThis.content.pop();
if (i != length - 1) {
upThis.content[i] = end;
if (upThis.scoreFunction(end) < upThis.scoreFunction(node)) {
upThis.bubbleUp(i);
} else {
upThis.sinkDown(i);
};
};
return;
};
};
throw(new Error(`The specified node was not found.`));
};
size() {
return this.content.length;
};
bubbleUp(n) {
let upThis = this;
let element = upThis.content[n];
while (n > 0) {
let parentN = ((n + 1) >> 1) - 1,
parent = upThis.content[parentN];
if (upThis.scoreFunction(element) < upThis.scoreFunction(parent)) {
upThis.content[parentN] = element;
upThis.content[n] = parent;
n = parentN;
} else {
break;
};
};
};
sinkDown(n) {
let upThis = this;
let length = upThis.content.length,
element = upThis.content[n],
score = upThis.scoreFunction(element);
while (true) {
let child2N = (n + 1) << 1,
child1N = child2N - 1;
let swap = null;
if (child1N < length) {
let child1 = upThis.content[child1N],
child1Score = upThis.scoreFunction(child1);
if (child1Score < score) {
swap = child1N;
};
};
if (child2N < length) {
let child2 = upThis.content[child2N],
child2Score = upThis.scoreFunction(child2);
if (child2Score < score) {
swap = child2N;
};
};
if (swap != null) {
upThis.content[n] = upThis.content[swap];
upThis.content[swap] = element;
n = swap;
} else {
break;
};
};
};
constructor(scoreFunction) {
this.scoreFunction = scoreFunction;
};
};

let TreeNode = class TreeNode {
left = null;
right = null;
obj;
parent;
dimension;
constructor(object, dimension, parent) {
let upThis = this;
upThis.obj = object;
upThis.parent = parent;
upThis.dimension = dimension;
};
};

let KDTree = class KDTree {
#dimensions;
#metricFunction;
Expand Down Expand Up @@ -429,5 +521,5 @@ let KDTree = class KDTree {

export {
KDTree,
BinaryHeap as KDTreeBinaryHeap
BinaryHeap as KDBinaryHeap
};
2 changes: 1 addition & 1 deletion src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ setInterval(async () => {
let pickedResult = self.colourTree?.nearest(colourValue, 1, 65025)[0];
colourDisplay[4].innerText = `${performance.now() - startTime}`.substring(0, 4);
if (pickedResult[0]) {
colourDisplay[3].innerText = `${Math.sqrt(pickedResult[1])}`.substring(0, 3);
colourDisplay[3].innerText = `${Math.sqrt(pickedResult[1])}`.substring(0, 4);
colourBoxNearest.style.backgroundColor = `rgb(${pickedResult[0].join(", ")})`;
};
}, 40);
Expand Down

0 comments on commit 02cc0c0

Please sign in to comment.