import {AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core'; import {CommonModule} from '@angular/common'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {MatButtonModule} from '@angular/material/button'; import {MatInputModule} from '@angular/material/input'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatSelectModule} from '@angular/material/select'; import {MatSliderModule} from '@angular/material/slider'; import {MatCheckboxModule} from '@angular/material/checkbox'; import {MatTabsModule} from '@angular/material/tabs'; import {MatCardModule} from '@angular/material/card'; import {MatIconModule} from '@angular/material/icon'; import {HeaderComponent} from '../header/header.component'; // Services import {RendererService} from './services/renderer.service'; import {PlayerModelService} from './services/player-model.service'; import {IntersectionPlaneService} from './services/intersection-plane.service'; import {ParticleManagerService} from './services/particle-manager.service'; import {InputHandlerService} from './services/input-handler.service'; import {FrameManagerService} from './services/frame-manager.service'; // Models import {ParticleData, ParticleType} from './models/particle.model'; @Component({ selector: 'app-particles', standalone: true, imports: [ CommonModule, FormsModule, ReactiveFormsModule, MatButtonModule, MatInputModule, MatFormFieldModule, MatSelectModule, MatSliderModule, MatCheckboxModule, MatTabsModule, MatCardModule, MatIconModule, HeaderComponent ], templateUrl: './particles.component.html', styleUrl: './particles.component.scss' }) export class ParticlesComponent implements OnInit, AfterViewInit, OnDestroy { @ViewChild('rendererContainer') rendererContainer!: ElementRef; @ViewChild('planeSlider') planeSlider!: ElementRef; // UI state public particleTypes = Object.values(ParticleType); constructor( private rendererService: RendererService, private playerModelService: PlayerModelService, private intersectionPlaneService: IntersectionPlaneService, private particleManagerService: ParticleManagerService, private inputHandlerService: InputHandlerService, private frameManagerService: FrameManagerService ) { } /** * Initialize component */ ngOnInit(): void { // No initialization needed here } /** * Initialize Three.js scene after view is initialized */ ngAfterViewInit(): void { this.initializeScene(); this.animate(); } /** * Clean up resources when component is destroyed */ ngOnDestroy(): void { // Clean up event listeners if (this.rendererService.renderer) { this.inputHandlerService.cleanup(this.rendererService.renderer.domElement); } } /** * Initialize the 3D scene and all related components */ private initializeScene(): void { // Initialize renderer this.rendererService.initializeRenderer(this.rendererContainer); // Create player model this.playerModelService.createPlayerModel(); // Create intersection plane this.intersectionPlaneService.createIntersectionPlane(); // Initialize input handlers this.inputHandlerService.initializeInputHandlers(this.rendererService.renderer.domElement); } /** * Update plane position based on slider */ public updatePlanePosition(event: Event): void { const slider = event.target as HTMLInputElement; const value = Number(slider.value); this.intersectionPlaneService.updatePlanePosition(value); } /** * Get the current plane position */ public get planePosition(): number { return this.intersectionPlaneService.getPlanePosition(); } public set planePosition(newPlanePosition: number) { this.intersectionPlaneService.updatePlanePosition(newPlanePosition); } public get maxOffset(): number { return this.intersectionPlaneService.getMaxOffset(); } public get minOffset(): number { return this.intersectionPlaneService.getMinOffset(); } /** * Get the selected color */ public get selectedColor(): string { return this.particleManagerService.getSelectedColor(); } /** * Set the selected color */ public set selectedColor(color: string) { this.particleManagerService.setSelectedColor(color); } /** * Get the particle data */ public get particleData(): ParticleData { return this.particleManagerService.getParticleData(); } /** * Get the current frame */ public get currentFrame(): string { return this.particleManagerService.getCurrentFrame(); } /** * Get all frames */ public get frames(): string[] { return this.particleManagerService.getFrames(); } /** * Animation loop */ private animate(): void { requestAnimationFrame(this.animate.bind(this)); // Update plane orientation based on camera position this.intersectionPlaneService.updatePlaneOrientation(this.rendererService.camera); // Render the scene this.rendererService.render(); } /** * Add a new frame */ public addFrame(): void { this.frameManagerService.addFrame(); } /** * Switch to a different frame */ public switchFrame(frameId: string): void { this.frameManagerService.switchFrame(frameId); } /** * Generate JSON output */ public generateJson(): string { return this.particleManagerService.generateJson(); } /** * Remove a particle */ public removeParticle(frameId: string, index: number): void { this.particleManagerService.removeParticle(frameId, index); } /** * Remove a frame */ public removeFrame(frameId: string): void { this.frameManagerService.removeFrame(frameId); } }