From ecd9b3d8248c18a8d7b1f6a3f71bd2be350c526a Mon Sep 17 00:00:00 2001 From: Teriuihi Date: Sun, 22 Jun 2025 19:23:54 +0200 Subject: [PATCH] Modularize renderer and plane control functionality into `RenderContainerComponent` --- .../render-container.component.html | 41 +++++++ .../render-container.component.scss | 46 ++++++++ .../render-container.component.spec.ts | 23 ++++ .../render-container.component.ts | 101 ++++++++++++++++++ .../app/particles/particles.component.html | 41 +------ .../app/particles/particles.component.scss | 47 -------- .../src/app/particles/particles.component.ts | 90 +--------------- 7 files changed, 217 insertions(+), 172 deletions(-) create mode 100644 frontend/src/app/particles/components/render-container/render-container.component.html create mode 100644 frontend/src/app/particles/components/render-container/render-container.component.scss create mode 100644 frontend/src/app/particles/components/render-container/render-container.component.spec.ts create mode 100644 frontend/src/app/particles/components/render-container/render-container.component.ts diff --git a/frontend/src/app/particles/components/render-container/render-container.component.html b/frontend/src/app/particles/components/render-container/render-container.component.html new file mode 100644 index 0000000..5f8c668 --- /dev/null +++ b/frontend/src/app/particles/components/render-container/render-container.component.html @@ -0,0 +1,41 @@ +
+
+ + +
+ + + + + + +
+
+
diff --git a/frontend/src/app/particles/components/render-container/render-container.component.scss b/frontend/src/app/particles/components/render-container/render-container.component.scss new file mode 100644 index 0000000..0806eda --- /dev/null +++ b/frontend/src/app/particles/components/render-container/render-container.component.scss @@ -0,0 +1,46 @@ +.renderer-container { + height: 1000px; + border: 1px solid #ccc; + border-radius: 4px; + overflow: hidden; + background-color: #f0f0f0; + display: flex; + justify-content: center; + align-items: center; + position: relative; /* Added for absolute positioning of overlay */ +} + +.plane-controls-overlay { + position: absolute; + top: 20px; + right: 20px; + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 10px; + z-index: 10; +} + +.plane-orientation-buttons { + display: grid; + grid-template-columns: repeat(2, 1fr); + grid-template-rows: repeat(3, 1fr); + gap: 10px; + margin-top: 10px; +} + +.plane-orientation-buttons button { + opacity: 0.7; + transition: opacity 0.2s, transform 0.2s; +} + +.plane-orientation-buttons button:hover { + opacity: 1; + transform: scale(1.1); +} + +.plane-orientation-buttons button.active { + opacity: 1; + transform: scale(1.1); + box-shadow: 0 0 10px rgba(255, 255, 255, 0.5); +} diff --git a/frontend/src/app/particles/components/render-container/render-container.component.spec.ts b/frontend/src/app/particles/components/render-container/render-container.component.spec.ts new file mode 100644 index 0000000..191e8d3 --- /dev/null +++ b/frontend/src/app/particles/components/render-container/render-container.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RenderContainerComponent } from './render-container.component'; + +describe('RenderContainerComponent', () => { + let component: RenderContainerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RenderContainerComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(RenderContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/particles/components/render-container/render-container.component.ts b/frontend/src/app/particles/components/render-container/render-container.component.ts new file mode 100644 index 0000000..6fd5913 --- /dev/null +++ b/frontend/src/app/particles/components/render-container/render-container.component.ts @@ -0,0 +1,101 @@ +import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild} from '@angular/core'; +import {MatMiniFabButton} from '@angular/material/button'; +import {NgIf} from '@angular/common'; +import {IntersectionPlaneService, PlaneOrientation} from '../../services/intersection-plane.service'; +import {MatIcon} from '@angular/material/icon'; +import {MatTooltip} from '@angular/material/tooltip'; +import {RendererService} from '../../services/renderer.service'; +import {PlayerModelService} from '../../services/player-model.service'; +import {InputHandlerService} from '../../services/input-handler.service'; + +@Component({ + selector: 'app-render-container', + imports: [ + MatIcon, + MatMiniFabButton, + MatTooltip, + NgIf + ], + templateUrl: './render-container.component.html', + styleUrl: './render-container.component.scss' +}) +export class RenderContainerComponent implements AfterViewInit, OnDestroy { + @ViewChild('rendererContainer') rendererContainer!: ElementRef; + + constructor( + private intersectionPlaneService: IntersectionPlaneService, + private playerModelService: PlayerModelService, + private inputHandlerService: InputHandlerService, + private rendererService: RendererService, + ) { + } + + ngAfterViewInit(): void { + this.initializeScene(); + this.animate(); + } + + /** + * Clean up resources when component is destroyed + */ + ngOnDestroy(): void { + if (this.rendererService.renderer) { + this.inputHandlerService.cleanup(this.rendererService.renderer.domElement); + } + } + + /** + * Initialize the 3D scene and all related components + */ + private initializeScene(): void { + this.rendererService.initializeRenderer(this.rendererContainer); + this.playerModelService.createPlayerModel(); + this.intersectionPlaneService.createIntersectionPlane(); + this.inputHandlerService.initializeInputHandlers(this.rendererService.renderer.domElement); + } + + /** + * Animation loop + */ + private animate(): void { + requestAnimationFrame(this.animate.bind(this)); + this.intersectionPlaneService.updatePlaneOrientation(this.rendererService.camera); + this.rendererService.render(); + } + + /** + * Get whether the plane is locked + */ + public get isPlaneLocked(): boolean { + return this.intersectionPlaneService.isPlaneLocked(); + } + + /** + * Toggle the plane locked state + */ + public togglePlaneLock(): void { + const newLockedState = !this.isPlaneLocked; + this.intersectionPlaneService.setPlaneLocked(newLockedState); + } + + /** + * Get the current plane orientation + */ + public get currentPlaneOrientation(): PlaneOrientation { + return this.intersectionPlaneService.getCurrentOrientation(); + } + + /** + * Set the plane orientation + */ + public setPlaneOrientation(orientation: PlaneOrientation): void { + this.intersectionPlaneService.setPlaneOrientation(orientation); + } + + /** + * Get all available plane orientations + */ + public get planeOrientations(): typeof PlaneOrientation { + return PlaneOrientation; + } +} diff --git a/frontend/src/app/particles/particles.component.html b/frontend/src/app/particles/particles.component.html index 704d7f1..17579b5 100644 --- a/frontend/src/app/particles/particles.component.html +++ b/frontend/src/app/particles/particles.component.html @@ -14,46 +14,7 @@
-
-
- - -
- - - - - - -
-
-
+
diff --git a/frontend/src/app/particles/particles.component.scss b/frontend/src/app/particles/particles.component.scss index 4892087..f6b738c 100644 --- a/frontend/src/app/particles/particles.component.scss +++ b/frontend/src/app/particles/particles.component.scss @@ -4,53 +4,6 @@ display: flex; } -.renderer-container { - height: 1000px; - border: 1px solid #ccc; - border-radius: 4px; - overflow: hidden; - background-color: #f0f0f0; - display: flex; - justify-content: center; - align-items: center; - position: relative; /* Added for absolute positioning of overlay */ -} - -.plane-controls-overlay { - position: absolute; - top: 20px; - right: 20px; - display: flex; - flex-direction: column; - align-items: flex-end; - gap: 10px; - z-index: 10; -} - -.plane-orientation-buttons { - display: grid; - grid-template-columns: repeat(2, 1fr); - grid-template-rows: repeat(3, 1fr); - gap: 10px; - margin-top: 10px; -} - -.plane-orientation-buttons button { - opacity: 0.7; - transition: opacity 0.2s, transform 0.2s; -} - -.plane-orientation-buttons button:hover { - opacity: 1; - transform: scale(1.1); -} - -.plane-orientation-buttons button.active { - opacity: 1; - transform: scale(1.1); - box-shadow: 0 0 10px rgba(255, 255, 255, 0.5); -} - .side-column { flex: 1; flex-direction: column; diff --git a/frontend/src/app/particles/particles.component.ts b/frontend/src/app/particles/particles.component.ts index 1b38b13..f919d9a 100644 --- a/frontend/src/app/particles/particles.component.ts +++ b/frontend/src/app/particles/particles.component.ts @@ -1,4 +1,4 @@ -import {AfterViewInit, Component, ElementRef, OnDestroy, ViewChild} from '@angular/core'; +import {Component, ElementRef, ViewChild} from '@angular/core'; import {CommonModule} from '@angular/common'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {MatButtonModule} from '@angular/material/button'; @@ -13,18 +13,15 @@ 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, PlaneOrientation} from './services/intersection-plane.service'; +import {IntersectionPlaneService} from './services/intersection-plane.service'; import {ParticleManagerService} from './services/particle-manager.service'; -import {InputHandlerService} from './services/input-handler.service'; // Models import {PropertiesComponent} from './components/properties/properties.component'; import {ParticleComponent} from './components/particle/particle.component'; import {FramesComponent} from './components/frames/frames.component'; import {MatSnackBar} from '@angular/material/snack-bar'; -import {MatTooltip} from '@angular/material/tooltip'; +import {RenderContainerComponent} from './components/render-container/render-container.component'; @Component({ selector: 'app-particles', @@ -46,52 +43,21 @@ import {MatTooltip} from '@angular/material/tooltip'; PropertiesComponent, ParticleComponent, FramesComponent, - MatTooltip, + RenderContainerComponent, ], templateUrl: './particles.component.html', styleUrl: './particles.component.scss' }) -export class ParticlesComponent implements AfterViewInit, OnDestroy { - @ViewChild('rendererContainer') rendererContainer!: ElementRef; +export class ParticlesComponent { @ViewChild('planeSlider') planeSlider!: ElementRef; constructor( - private rendererService: RendererService, - private playerModelService: PlayerModelService, private intersectionPlaneService: IntersectionPlaneService, private particleManagerService: ParticleManagerService, - private inputHandlerService: InputHandlerService, private matSnackBar: MatSnackBar, ) { } - /** - * Initialize Three.js scene after view is initialized - */ - ngAfterViewInit(): void { - this.initializeScene(); - this.animate(); - } - - /** - * Clean up resources when component is destroyed - */ - ngOnDestroy(): void { - if (this.rendererService.renderer) { - this.inputHandlerService.cleanup(this.rendererService.renderer.domElement); - } - } - - /** - * Initialize the 3D scene and all related components - */ - private initializeScene(): void { - this.rendererService.initializeRenderer(this.rendererContainer); - this.playerModelService.createPlayerModel(); - this.intersectionPlaneService.createIntersectionPlane(); - this.inputHandlerService.initializeInputHandlers(this.rendererService.renderer.domElement); - } - /** * Update plane position based on slider */ @@ -120,16 +86,6 @@ export class ParticlesComponent implements AfterViewInit, OnDestroy { return this.intersectionPlaneService.getMinOffset(); } - /** - * Animation loop - */ - private animate(): void { - requestAnimationFrame(this.animate.bind(this)); - this.intersectionPlaneService.updatePlaneOrientation(this.rendererService.camera); - this.rendererService.render(); - } - - /** * Generate JSON output */ @@ -142,40 +98,4 @@ export class ParticlesComponent implements AfterViewInit, OnDestroy { this.matSnackBar.open('Copied to clipboard', '', {duration: 2000}) }); } - - /** - * Get whether the plane is locked - */ - public get isPlaneLocked(): boolean { - return this.intersectionPlaneService.isPlaneLocked(); - } - - /** - * Toggle the plane locked state - */ - public togglePlaneLock(): void { - const newLockedState = !this.isPlaneLocked; - this.intersectionPlaneService.setPlaneLocked(newLockedState); - } - - /** - * Get the current plane orientation - */ - public get currentPlaneOrientation(): PlaneOrientation { - return this.intersectionPlaneService.getCurrentOrientation(); - } - - /** - * Set the plane orientation - */ - public setPlaneOrientation(orientation: PlaneOrientation): void { - this.intersectionPlaneService.setPlaneOrientation(orientation); - } - - /** - * Get all available plane orientations - */ - public get planeOrientations(): typeof PlaneOrientation { - return PlaneOrientation; - } }