@thi.ng/distance
Version:
N-dimensional distance metrics & K-nearest neighborhoods for point queries
101 lines (100 loc) • 2.68 kB
JavaScript
import { assert } from "@thi.ng/errors/assert";
import { Heap } from "@thi.ng/heaps/heap";
import { clamp0 } from "@thi.ng/math/interval";
import { DIST_SQ, DIST_SQ1, DIST_SQ2, DIST_SQ3 } from "./squared.js";
class KNearest {
constructor(dist, target, k, radius = Infinity, sorted = false) {
this.dist = dist;
this.target = target;
this.k = k;
this.radius = radius;
this.sorted = sorted;
this.radius = clamp0(radius);
this.setK(k);
}
_currR;
_heap = new Heap(null, {
compare: (a, b) => b[0] - a[0]
});
reset() {
this._currR = this.dist.to(this.radius);
this._heap.clear();
return this;
}
/**
* Resets search/reference position.
*
* @param target
*/
setTarget(target) {
this.target = target;
}
/**
* Resets max. search/query radius and clears current results.
*
* @param r
*/
setRadius(r) {
this.radius = clamp0(r);
this.reset();
}
/**
* Resets `k-nearest` limit and clears current results.
*
* @param k
*/
setK(k) {
assert(k > 0, `invalid k (must be > 0)`);
this.k = k;
this.reset();
}
/**
* Returns an array of current nearest neighbor result tuples (each `[dist,
* val]`). The array will contain at most `k` items and if the `sorted` ctor
* arg was true, will be sorted by distance.
*
* @remarks
* Use {@link KNearest.values} to obtain result values **without** their distance
* metrics.
*/
deref() {
return this.sorted ? this._heap.max() : this._heap.values;
}
/**
* Similar to {@link KNearest.deref}, but returns array of result values **without**
* their distance metrics.
*/
values() {
return this.deref().map((x) => x[1]);
}
includesDistance(d, eucledian = true) {
return (eucledian ? this.dist.to(d) : d) <= this._currR;
}
includesPosition(pos) {
return this.dist.metric(this.target, pos) < this._currR;
}
consider(pos, val) {
const d = this.dist.metric(this.target, pos);
if (d <= this._currR) {
const heap = this._heap;
if (heap.length === this.k) {
heap.pushPop([d, val]);
this._currR = heap.peek()[0];
} else {
heap.push([d, val]);
}
}
return d;
}
}
const knearest = (p, k, r, dist = DIST_SQ, sorted) => new KNearest(dist, p, k, r, sorted);
const knearest2 = (p, k, r, dist = DIST_SQ2, sorted) => new KNearest(dist, p, k, r, sorted);
const knearest3 = (p, k, r, dist = DIST_SQ3, sorted) => new KNearest(dist, p, k, r, sorted);
const knearestN = (p, k, r, dist = DIST_SQ1, sorted) => new KNearest(dist, p, k, r, sorted);
export {
KNearest,
knearest,
knearest2,
knearest3,
knearestN
};