@gravity-ui/graph
Version:
Modern graph editor component
104 lines (103 loc) • 3.09 kB
JavaScript
import debounce from "lodash/debounce";
import RBush from "rbush";
import { Emitter } from "../utils/Emitter";
export class HitTest extends Emitter {
constructor() {
super(...arguments);
this.tree = new RBush(16);
this.empty = true;
this.scheduledItems = new Set();
this.scheduleLoad = debounce(() => {
this.tree.load(Array.from(this.scheduledItems));
this.scheduledItems.clear();
this.emitUpdate();
}, 50);
this.emitUpdate = debounce(() => {
this.emit("update", this);
}, 50);
}
load(items) {
this.tree.load(items);
}
clear() {
this.scheduledItems.clear();
this.tree.clear();
}
add(item, force = false) {
this.scheduledItems.add(item);
this.scheduleLoad();
if (force) {
this.scheduleLoad.flush();
}
}
remove(item, silent = false) {
this.scheduledItems.delete(item);
this.tree.remove(item);
if (!silent) {
this.emitUpdate();
}
}
testPoint(point, pixelRatio) {
return this.testHitBox({
minX: point.x - 1,
minY: point.y - 1,
maxX: point.x + 1,
maxY: point.y + 1,
x: point.origPoint?.x * pixelRatio,
y: point.origPoint?.y * pixelRatio,
});
}
testBox(item) {
return this.tree.search(item).map((hitBox) => hitBox.item);
}
testHitBox(item) {
const hitBoxes = this.tree.search(item);
const result = [];
for (let i = 0; i < hitBoxes.length; i++) {
if (hitBoxes[i].item.onHitBox(item)) {
result.push(hitBoxes[i].item);
}
}
const res = result.sort((a, b) => {
const aZIndex = typeof a.zIndex === "number" ? a.zIndex : -1;
const bZIndex = typeof b.zIndex === "number" ? b.zIndex : -1;
if (aZIndex !== bZIndex) {
return bZIndex - aZIndex;
}
return 0;
});
return res;
}
}
export class HitBox {
constructor(item, hitTest) {
this.item = item;
this.hitTest = hitTest;
this.destroyed = false;
this.update = (minX, minY, maxX, maxY, force) => {
if (this.destroyed)
return;
if (minX === this.minX && minY === this.minY && maxX === this.maxX && maxY === this.maxY && !force)
return;
if (this.minX !== undefined) {
this.hitTest.remove(this, true);
}
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
this.rect = [this.minX, this.minY, this.maxX - this.minX, this.maxY - this.minY];
this.hitTest.add(this, Boolean(force));
};
}
getRect() {
return this.rect;
}
remove() {
this.hitTest.remove(this);
}
destroy() {
this.destroyed = true;
this.hitTest.remove(this);
}
}