import * as THREE from "three"; import {getNormalizedPointer} from "../utils/common.js"; import {STATES} from "../config/constants.js"; export class InteractionController { constructor(main) { this.main = main; this.raycaster = new THREE.Raycaster(); this.pointer = new THREE.Vector2(); this.intersectionPoint = new THREE.Vector3(); this.distanceToCamera = 0; this.onPointerDown = this.onPointerDown.bind(this); this.onPointerMove = this.onPointerMove.bind(this); this.onPointerUp = this.onPointerUp.bind(this); this.init(); } init() { const {app} = this.main; app.renderer.domElement.addEventListener("pointerdown", this.onPointerDown); app.renderer.domElement.addEventListener("pointermove", this.onPointerMove); app.renderer.domElement.addEventListener("pointerup", this.onPointerUp); } onPointerDown(event) { const {app, pixelVoxelMesh, stateController, sequence, physics, params, gridInfo, container} = this.main; if (event.target !== app.domElement || !pixelVoxelMesh) return; const ndc = getNormalizedPointer(event, container.clientWidth, container.clientHeight); this.pointer.set(ndc.x, ndc.y); this.raycaster.setFromCamera(this.pointer, app.camera); const intersects = this.raycaster.intersectObject(pixelVoxelMesh.mesh); if (intersects.length > 0) { const {instanceId, point} = intersects[0]; if (stateController.state === STATES.DEFAULT && params.manualMode) { const u = (instanceId % gridInfo.cols) / gridInfo.cols; const v = Math.floor(instanceId / gridInfo.cols) / gridInfo.rows; sequence.startCollapse(u, v); return; } if (stateController.state === STATES.COLLAPSED) { const success = physics.startDrag(point, instanceId); if (success) { app.controls.enabled = false; this.distanceToCamera = app.camera.position.distanceTo(point); } } } } onPointerMove(event) { const {app, physics, container} = this.main; if (physics.joint) { const ndc = getNormalizedPointer(event, container.clientWidth, container.clientHeight); this.pointer.set(ndc.x, ndc.y); this.raycaster.setFromCamera(this.pointer, app.camera); const targetPos = this.raycaster.ray.at(this.distanceToCamera, this.intersectionPoint); physics.moveDrag(targetPos); } } onPointerUp() { const {app, physics} = this.main; if (physics.joint) { physics.endDrag(); } app.controls.enabled = true; } dispose() { const {app} = this.main; app.renderer.domElement.removeEventListener("pointerdown", this.onPointerDown); app.renderer.domElement.removeEventListener("pointermove", this.onPointerMove); app.renderer.domElement.removeEventListener("pointerup", this.onPointerUp); } }