Pages are now grouped per group they appear in on in the header (where possible) Utilities used by multiple pages in the project are grouped in folders such as services/pipes/etc
106 lines
3.4 KiB
TypeScript
106 lines
3.4 KiB
TypeScript
import { Injectable, ElementRef } from '@angular/core';
|
|
import * as THREE from 'three';
|
|
import { RendererService } from './renderer.service';
|
|
import { IntersectionPlaneService } from './intersection-plane.service';
|
|
import { ParticleManagerService } from './particle-manager.service';
|
|
|
|
/**
|
|
* Service responsible for handling user input interactions
|
|
*/
|
|
@Injectable({
|
|
providedIn: 'root'
|
|
})
|
|
export class InputHandlerService {
|
|
private raycaster = new THREE.Raycaster();
|
|
private mouse = new THREE.Vector2();
|
|
private isDragging = false;
|
|
private mouseDownTime = 0;
|
|
|
|
constructor(
|
|
private rendererService: RendererService,
|
|
private intersectionPlaneService: IntersectionPlaneService,
|
|
private particleManagerService: ParticleManagerService
|
|
) {}
|
|
|
|
/**
|
|
* Initializes input event listeners
|
|
*/
|
|
initializeInputHandlers(rendererElement: HTMLElement): void {
|
|
rendererElement.addEventListener('mousedown', this.onMouseDown.bind(this));
|
|
rendererElement.addEventListener('mouseup', this.onMouseUp.bind(this));
|
|
rendererElement.addEventListener('mousemove', this.onMouseMove.bind(this));
|
|
window.addEventListener('resize', this.onWindowResize.bind(this));
|
|
}
|
|
|
|
/**
|
|
* Handles mouse down event
|
|
*/
|
|
private onMouseDown(event: MouseEvent): void {
|
|
this.isDragging = false;
|
|
this.mouseDownTime = Date.now();
|
|
}
|
|
|
|
/**
|
|
* Handles mouse up event
|
|
*/
|
|
private onMouseUp(event: MouseEvent): void {
|
|
// If mouse was down for less than 200ms and didn't move much, consider it a click, not a drag
|
|
if (Date.now() - this.mouseDownTime < 200 && !this.isDragging) {
|
|
this.handlePlaneClick(event);
|
|
}
|
|
this.isDragging = false;
|
|
}
|
|
|
|
/**
|
|
* Handles mouse move event
|
|
*/
|
|
private onMouseMove(event: MouseEvent): void {
|
|
// If mouse moves while button is pressed, it's a drag
|
|
if (event.buttons > 0) {
|
|
this.isDragging = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles mouse click on the plane
|
|
*/
|
|
private handlePlaneClick(event: MouseEvent): void {
|
|
// Calculate mouse position in normalized device coordinates
|
|
const rect = this.rendererService.renderer.domElement.getBoundingClientRect();
|
|
this.mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
|
|
this.mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
|
|
|
|
// Update the picking ray with the camera and mouse position
|
|
this.raycaster.setFromCamera(this.mouse, this.rendererService.camera);
|
|
|
|
// Calculate objects intersecting the picking ray
|
|
const intersects = this.raycaster.intersectObject(this.intersectionPlaneService.getIntersectionPlane());
|
|
|
|
if (intersects.length > 0) {
|
|
const point = intersects[0].point;
|
|
this.particleManagerService.addParticle(point.x, point.y, point.z);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles window resize event
|
|
*/
|
|
private onWindowResize(): void {
|
|
// This is delegated to the renderer service
|
|
const container = this.rendererService.renderer.domElement.parentElement;
|
|
if (container) {
|
|
this.rendererService.onWindowResize(new ElementRef(container));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes event listeners
|
|
*/
|
|
cleanup(rendererElement: HTMLElement): void {
|
|
rendererElement.removeEventListener('mousedown', this.onMouseDown.bind(this));
|
|
rendererElement.removeEventListener('mouseup', this.onMouseUp.bind(this));
|
|
rendererElement.removeEventListener('mousemove', this.onMouseMove.bind(this));
|
|
window.removeEventListener('resize', this.onWindowResize.bind(this));
|
|
}
|
|
}
|