2025-06-22 15:26:10 +00:00
|
|
|
import {AfterViewInit, Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
|
2025-06-21 22:40:16 +00:00
|
|
|
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';
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
// 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';
|
2025-06-21 22:40:16 +00:00
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
// Models
|
|
|
|
|
import { ParticleType, ParticleData } from './models/particle.model';
|
2025-06-21 22:40:16 +00:00
|
|
|
|
|
|
|
|
@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'
|
|
|
|
|
})
|
2025-06-22 15:26:10 +00:00
|
|
|
export class ParticlesComponent implements OnInit, AfterViewInit, OnDestroy {
|
2025-06-21 22:40:16 +00:00
|
|
|
@ViewChild('rendererContainer') rendererContainer!: ElementRef;
|
|
|
|
|
@ViewChild('planeSlider') planeSlider!: ElementRef;
|
|
|
|
|
|
|
|
|
|
// UI state
|
|
|
|
|
public particleTypes = Object.values(ParticleType);
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
constructor(
|
|
|
|
|
private rendererService: RendererService,
|
|
|
|
|
private playerModelService: PlayerModelService,
|
|
|
|
|
private intersectionPlaneService: IntersectionPlaneService,
|
|
|
|
|
private particleManagerService: ParticleManagerService,
|
|
|
|
|
private inputHandlerService: InputHandlerService,
|
|
|
|
|
private frameManagerService: FrameManagerService
|
|
|
|
|
) {
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Initialize component
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
ngOnInit(): void {
|
2025-06-22 15:26:10 +00:00
|
|
|
// No initialization needed here
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Initialize Three.js scene after view is initialized
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
ngAfterViewInit(): void {
|
2025-06-22 15:26:10 +00:00
|
|
|
this.initializeScene();
|
2025-06-21 22:40:16 +00:00
|
|
|
this.animate();
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Clean up resources when component is destroyed
|
|
|
|
|
*/
|
|
|
|
|
ngOnDestroy(): void {
|
|
|
|
|
// Clean up event listeners
|
|
|
|
|
if (this.rendererService.renderer) {
|
|
|
|
|
this.inputHandlerService.cleanup(this.rendererService.renderer.domElement);
|
|
|
|
|
}
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Initialize the 3D scene and all related components
|
|
|
|
|
*/
|
|
|
|
|
private initializeScene(): void {
|
|
|
|
|
// Initialize renderer
|
|
|
|
|
this.rendererService.initializeRenderer(this.rendererContainer);
|
2025-06-21 22:40:16 +00:00
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
// Create player model
|
|
|
|
|
this.playerModelService.createPlayerModel();
|
2025-06-21 22:40:16 +00:00
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
// Create intersection plane
|
|
|
|
|
this.intersectionPlaneService.createIntersectionPlane();
|
2025-06-21 22:40:16 +00:00
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
// Initialize input handlers
|
|
|
|
|
this.inputHandlerService.initializeInputHandlers(this.rendererService.renderer.domElement);
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Update plane position based on slider
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
public updatePlanePosition(event: Event): void {
|
|
|
|
|
const slider = event.target as HTMLInputElement;
|
2025-06-22 15:26:10 +00:00
|
|
|
const value = Number(slider.value);
|
|
|
|
|
this.intersectionPlaneService.updatePlanePosition(value);
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Get the current plane position
|
|
|
|
|
*/
|
|
|
|
|
public get planePosition(): number {
|
|
|
|
|
return this.intersectionPlaneService.getPlanePosition();
|
2025-06-21 22:55:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Get the selected color
|
|
|
|
|
*/
|
|
|
|
|
public get selectedColor(): string {
|
|
|
|
|
return this.particleManagerService.getSelectedColor();
|
2025-06-21 22:55:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Set the selected color
|
|
|
|
|
*/
|
|
|
|
|
public set selectedColor(color: string) {
|
|
|
|
|
this.particleManagerService.setSelectedColor(color);
|
2025-06-21 22:55:38 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Get the particle data
|
|
|
|
|
*/
|
|
|
|
|
public get particleData(): ParticleData {
|
|
|
|
|
return this.particleManagerService.getParticleData();
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Get the current frame
|
|
|
|
|
*/
|
|
|
|
|
public get currentFrame(): string {
|
|
|
|
|
return this.particleManagerService.getCurrentFrame();
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Get all frames
|
|
|
|
|
*/
|
|
|
|
|
public get frames(): string[] {
|
|
|
|
|
return this.particleManagerService.getFrames();
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Animation loop
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
private animate(): void {
|
|
|
|
|
requestAnimationFrame(this.animate.bind(this));
|
|
|
|
|
|
2025-06-21 22:55:38 +00:00
|
|
|
// Update plane orientation based on camera position
|
2025-06-22 15:26:10 +00:00
|
|
|
this.intersectionPlaneService.updatePlaneOrientation(this.rendererService.camera);
|
2025-06-21 22:40:16 +00:00
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
// Render the scene
|
|
|
|
|
this.rendererService.render();
|
2025-06-21 23:03:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Add a new frame
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
public addFrame(): void {
|
2025-06-22 15:26:10 +00:00
|
|
|
this.frameManagerService.addFrame();
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Switch to a different frame
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
public switchFrame(frameId: string): void {
|
2025-06-22 15:26:10 +00:00
|
|
|
this.frameManagerService.switchFrame(frameId);
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Generate JSON output
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
public generateJson(): string {
|
2025-06-22 15:26:10 +00:00
|
|
|
return this.particleManagerService.generateJson();
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Remove a particle
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
public removeParticle(frameId: string, index: number): void {
|
2025-06-22 15:26:10 +00:00
|
|
|
this.particleManagerService.removeParticle(frameId, index);
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-22 15:26:10 +00:00
|
|
|
/**
|
|
|
|
|
* Remove a frame
|
|
|
|
|
*/
|
2025-06-21 22:40:16 +00:00
|
|
|
public removeFrame(frameId: string): void {
|
2025-06-22 15:26:10 +00:00
|
|
|
this.frameManagerService.removeFrame(frameId);
|
2025-06-21 22:40:16 +00:00
|
|
|
}
|
|
|
|
|
}
|