CAD/client/src/helpers/hitTest.ts

68 lines
2.2 KiB
TypeScript

import * as THREE from 'three';
import type { SceneHelper } from './sceneHelper';
import './bvh';
export type HitTest = {
objects: THREE.Intersection<THREE.Object3D>[];
}
export type HitTestRaycasterOptions = {
cameraPixelSize: THREE.Vector2Like;
tolerancePixels: number;
}
export type HitTestOptions = HitTestRaycasterOptions & {
}
export class HitTestFactory {
private static raycasters: [THREE.Vector2, THREE.Raycaster][] = Array(9).fill(0).map(() => [new THREE.Vector2(), new THREE.Raycaster()]);
private static setupRaycasters(cursor: THREE.Vector2, camera: THREE.PerspectiveCamera, options: HitTestRaycasterOptions) {
this.raycasters[0][0].copy(cursor);
this.raycasters[0][1].setFromCamera(cursor, camera);
const count = HitTestFactory.raycasters.length - 1;
const step = Math.PI * 2 / count;
for (let angle = 0, idx = 0; idx < count; angle += step, idx++) {
const pos = {
x: Math.cos(angle) * options.tolerancePixels * options.cameraPixelSize.x,
y: Math.sin(angle) * options.tolerancePixels * options.cameraPixelSize.y,
};
const v = HitTestFactory.raycasters[idx + 1][0];
v.copy(cursor).add(pos);
HitTestFactory.raycasters[idx + 1][1].setFromCamera(v, camera);
}
}
public static getRaycasterPosition(index: number): THREE.Vector2 {
return HitTestFactory.raycasters[index][0];
}
public static get raycasterCount(): number {
return HitTestFactory.raycasters.length;
}
public static hitTest(scene: SceneHelper, cursor: THREE.Vector2, camera: THREE.PerspectiveCamera, options: HitTestOptions): HitTest {
HitTestFactory.setupRaycasters(cursor, camera, options);
const objects: THREE.Object3D[] = scene.objects;
const hitTest: Record<string, THREE.Intersection<THREE.Object3D>> = {};
HitTestFactory.raycasters.forEach((raycaster) => {
const hits = raycaster[1].intersectObjects(objects);
for (const hit of hits) {
hitTest[hit.object.uuid] = hit;
}
});
return {
objects: Object.values(hitTest),
}
}
}