import { Raycaster, Vector3 } from "three" class Draggable{ constructor(engine, defaultDistance){ const objects = []; const raycaster = new Raycaster(); let v = new Vector3(), cv = new Vector3(); let dragging = null; this.add = function(object, dragZone, fn, distance){ objects.push(object); object._draggable = {fn, dragZone, distance: (distance || defaultDistance) * engine.scale} } this.remove = function(object){ delete object._draggable; objects.splice(objects.indexOf(object), 1); } this.handleController = function(controller, action){ raycaster.setFromXRController(controller); this.handle(action); } this.handleMouse = function (mouse, action) { raycaster.setFromCamera(mouse, engine.camera); this.handle(action); }; this.handle = function(action){ if (['start', 'selectstart'].includes(action)){ let forExecute = []; engine.cameraWorld.getWorldPosition(cv); objects.filter(o=>{ do { if (o.__active === false || o.visible === false) return false; o = o.parent; } while (o); return true; }).forEach(o=>{ o.getWorldPosition(v); if (cv.distanceTo(v) <= o._draggable.distance){ const intersects = raycaster.intersectObject(o); if (intersects[0]) forExecute.push({o, i:intersects[0]}) } }); if (forExecute[0]) { let s = forExecute.sort((a,b)=>a.i.distance-b.i.distance)[0]; s.o._draggable.fn.start && s.o._draggable.fn.start(s.i); dragging = s; dragging.zone = raycaster.intersectObject(s.o._draggable.dragZone)[0]; } }else if (['end', 'selectend'].includes(action) && dragging){ dragging.o._draggable.fn.end && dragging.o._draggable.fn.end(dragging); dragging = null; }else if(['drag', 'move'].includes(action) && dragging){ const intersect = raycaster.intersectObject(dragging.o._draggable.dragZone)[0]; if (intersect?.uv && dragging.zone?.uv){ dragging.o.position.x += -4*(dragging.zone.uv.x - intersect.uv.x); dragging.o.position.y += 4*(dragging.zone.uv.y - intersect.uv.y); dragging.o._draggable.fn.drag && dragging.o._draggable.fn.drag(dragging); dragging.zone = intersect; } } } } } export { Draggable }