From 8be78196dd3ca962f73263cd6d0114e110332b83 Mon Sep 17 00:00:00 2001 From: Smoren Date: Sat, 13 Jul 2024 21:38:31 +0300 Subject: [PATCH] Grabbing atoms functionality implemented. --- src/lib/drawer/2d.ts | 45 ++++++++++++++++++++++++++++---- src/lib/drawer/3d.ts | 1 + src/lib/drawer/utils.ts | 48 +++++++++++++++++++++++++++++++--- src/lib/simulation.ts | 57 +++++++++++++++++++++++++++++------------ src/lib/types/drawer.ts | 4 +++ 5 files changed, 131 insertions(+), 24 deletions(-) diff --git a/src/lib/drawer/2d.ts b/src/lib/drawer/2d.ts index 59eb86a..b4ea4c9 100644 --- a/src/lib/drawer/2d.ts +++ b/src/lib/drawer/2d.ts @@ -175,7 +175,7 @@ export class Drawer2d implements DrawerInterface { const coords = createVector( transposeCoordsBackward([event.offsetX, event.offsetY], this.viewConfig.offset, this.viewConfig.scale), ); - this.eventManager.triggerClick({ coords, extraKey: keyDown }); + this.eventManager.triggerClick({ coords, extraKey: keyDown, ctrlKey: event.ctrlKey }); console.log(keyDown, coords); }); @@ -208,22 +208,57 @@ export class Drawer2d implements DrawerInterface { }); const mouseDownHandler = (event: MouseEvent | TouchEvent) => { - mouseDownVector = (event instanceof MouseEvent) + const coords = (event instanceof MouseEvent) ? createVector([event.offsetX, event.offsetY]) : createVector([event.touches[0].clientX, event.touches[0].clientY]); document.body.style.cursor = 'grabbing'; + + try { + this.eventManager.triggerMouseDown({ + coords: transposeCoordsBackward(coords, this.viewConfig.offset, this.viewConfig.scale), + extraKey: keyDown, + ctrlKey: event.ctrlKey, + }); + } catch (e) { + return; + } + + mouseDownVector = coords; }; const mouseUpHandler = (event: MouseEvent | TouchEvent) => { + const coords = (event instanceof MouseEvent) + ? createVector([event.offsetX, event.offsetY]) + : createVector([event.touches[0].clientX, event.touches[0].clientY]); mouseDownVector = undefined; document.body.style.cursor = 'auto'; + + this.eventManager.triggerMouseUp({ + coords: transposeCoordsBackward(coords, this.viewConfig.offset, this.viewConfig.scale), + extraKey: keyDown, + ctrlKey: event.ctrlKey, + }); }; const mouseMoveHandler = (event: MouseEvent | TouchEvent) => { - if (mouseDownVector === undefined) { - return; - } const coords = (event instanceof MouseEvent) ? createVector([event.offsetX, event.offsetY]) : createVector([event.touches[0].clientX, event.touches[0].clientY]); + + this.eventManager.triggerMouseMove({ + coords: transposeCoordsBackward(coords, this.viewConfig.offset, this.viewConfig.scale), + extraKey: keyDown, + ctrlKey: event.ctrlKey, + }); + + if (mouseDownVector === undefined) { + return; + } + + this.eventManager.triggerMouseGrab({ + coords: transposeCoordsBackward(coords, this.viewConfig.offset, this.viewConfig.scale), + extraKey: keyDown, + ctrlKey: event.ctrlKey, + }); + const diff = coords.clone().sub(mouseDownVector); this.viewConfig.offset[0] += diff[0]; diff --git a/src/lib/drawer/3d.ts b/src/lib/drawer/3d.ts index e297f53..805cae6 100644 --- a/src/lib/drawer/3d.ts +++ b/src/lib/drawer/3d.ts @@ -281,6 +281,7 @@ export class Drawer3d implements DrawerInterface { this.eventManager.triggerClick({ coords: [pos.x, pos.y, pos.z], extraKey: keyDown, + ctrlKey: event.ctrlKey, }); } }; diff --git a/src/lib/drawer/utils.ts b/src/lib/drawer/utils.ts index bd36f6e..bdb9714 100644 --- a/src/lib/drawer/utils.ts +++ b/src/lib/drawer/utils.ts @@ -1,19 +1,28 @@ import type { EventManagerInterface, MouseEventData, MouseEventListenerCallback } from '../types/drawer'; +import type { NumericVector } from "@/lib/math/types"; type ListenersStorage = { onClick: MouseEventListenerCallback[]; onMouseDown: MouseEventListenerCallback[]; onMouseMove: MouseEventListenerCallback[]; + onMouseGrab: MouseEventListenerCallback[]; onMouseUp: MouseEventListenerCallback[]; }; +export class PreventException extends Error { +} + export class EventManager implements EventManagerInterface { private listeners: ListenersStorage = { onClick: [], onMouseDown: [], onMouseMove: [], + onMouseGrab: [], onMouseUp: [], }; + private lastPoint: NumericVector | undefined; + private mouseGrabTick: ReturnType | undefined; + onClick(callback: MouseEventListenerCallback): EventManagerInterface { return this.add('onClick', callback); } @@ -26,6 +35,10 @@ export class EventManager implements EventManagerInterface { return this.add('onMouseMove', callback); } + onMouseGrab(callback: MouseEventListenerCallback): EventManagerInterface { + return this.add('onMouseGrab', callback); + } + onMouseUp(callback: MouseEventListenerCallback): EventManagerInterface { return this.add('onMouseUp', callback); } @@ -35,6 +48,7 @@ export class EventManager implements EventManagerInterface { } triggerMouseDown(event: MouseEventData): void { + this.startMouseGrabTick(); this.trigger('onMouseDown', event); } @@ -42,7 +56,12 @@ export class EventManager implements EventManagerInterface { this.trigger('onMouseMove', event); } + triggerMouseGrab(event: MouseEventData): void { + this.trigger('onMouseGrab', event); + } + triggerMouseUp(event: MouseEventData): void { + this.stopMouseGrabTick(); this.trigger('onMouseUp', event); } @@ -51,9 +70,32 @@ export class EventManager implements EventManagerInterface { return this; } - private trigger(type: keyof ListenersStorage, event: MouseEventData): void { - for (const callback of this.listeners[type]) { - callback(event); + private trigger(type: keyof ListenersStorage, event: MouseEventData, throwException: boolean = true): void { + this.lastPoint = event.coords; + try { + for (const callback of this.listeners[type]) { + callback(event); + } + } catch (e) { + if (throwException) { + throw e; + } + } + } + + private startMouseGrabTick() { + this.mouseGrabTick = setInterval(() => { + this.trigger('onMouseGrab', { + coords: this.lastPoint as NumericVector, + extraKey: undefined, + ctrlKey: false, + }, false); + }, 30); + } + + private stopMouseGrabTick() { + if (this.mouseGrabTick) { + clearInterval(this.mouseGrabTick); } } } diff --git a/src/lib/simulation.ts b/src/lib/simulation.ts index 05acad6..f831f50 100644 --- a/src/lib/simulation.ts +++ b/src/lib/simulation.ts @@ -13,6 +13,8 @@ import { SummaryManager } from './analysis/summary'; import type { NumericVector } from './math/types'; import type { Compound } from './types/analysis'; import { CompoundsCollector } from './analysis/compounds'; +import { PreventException } from "@/lib/drawer/utils"; +import { toVector } from "@/lib/math"; export class Simulation implements SimulationInterface { readonly config: SimulationConfig; @@ -42,22 +44,7 @@ export class Simulation implements SimulationInterface { this.clusterManager = new ClusterManager(this.config.worldConfig.MAX_INTERACTION_RADIUS); this.runningState = new RunningState(); - this.drawer.eventManager.onClick((event) => { - const foundAtom = this.clusterManager.findAtomByCoords( - event.coords, - this.config.typesConfig.RADIUS, - this.config.worldConfig.ATOM_RADIUS, - ); - if (foundAtom) { - console.log('ATOM FOUND', foundAtom); - } - - if (event.extraKey === undefined || event.extraKey > this.config.typesConfig.FREQUENCIES.length) { - return; - } - console.log('atom added'); - this._atoms.push(createAtom(event.extraKey-1, event.coords)); - }); + this.initEventHandlers(); } get atoms(): AtomInterface[] { @@ -217,4 +204,42 @@ export class Simulation implements SimulationInterface { this.runningState.confirmStop(); } } + + private initEventHandlers(): void { + let grabbedAtom: AtomInterface | undefined = undefined; + + this.drawer.eventManager.onClick((event) => { + if (event.extraKey === undefined || event.extraKey > this.config.typesConfig.FREQUENCIES.length) { + return; + } + console.log('atom added'); + this._atoms.push(createAtom(event.extraKey-1, event.coords)); + }); + + this.drawer.eventManager.onMouseDown((event) => { + if (!event.ctrlKey) { + return; + } + grabbedAtom = this.clusterManager.findAtomByCoords( + event.coords, + this.config.typesConfig.RADIUS, + this.config.worldConfig.ATOM_RADIUS*2, + ); + if (grabbedAtom) { + console.log('ATOM FOUND', grabbedAtom); + } + throw new PreventException(); + }); + + this.drawer.eventManager.onMouseGrab((event) => { + if (grabbedAtom) { + const speed = toVector(event.coords).sub(grabbedAtom.position).mul(0.2); + grabbedAtom.speed.set(speed); + } + }); + + this.drawer.eventManager.onMouseUp((event) => { + grabbedAtom = undefined; + }); + } } diff --git a/src/lib/types/drawer.ts b/src/lib/types/drawer.ts index 0236378..3815f07 100644 --- a/src/lib/types/drawer.ts +++ b/src/lib/types/drawer.ts @@ -11,7 +11,9 @@ export interface ViewConfigInterface { export type MouseEventData = { coords: NumericVector; extraKey: number | undefined; + ctrlKey: boolean; } + export type MouseEventListenerCallback = (event: MouseEventData) => void; export interface DrawerInterface { @@ -24,11 +26,13 @@ export interface EventManagerInterface { onClick(callback: MouseEventListenerCallback): EventManagerInterface; onMouseDown(callback: MouseEventListenerCallback): EventManagerInterface; onMouseMove(callback: MouseEventListenerCallback): EventManagerInterface; + onMouseGrab(callback: MouseEventListenerCallback): EventManagerInterface; onMouseUp(callback: MouseEventListenerCallback): EventManagerInterface; triggerClick(event: MouseEventData): void; triggerMouseDown(event: MouseEventData): void; triggerMouseMove(event: MouseEventData): void; + triggerMouseGrab(event: MouseEventData): void; triggerMouseUp(event: MouseEventData): void; }