diff --git a/frontend/src/app/particles/particles.component.html b/frontend/src/app/particles/particles.component.html index 8daf127..704d7f1 100644 --- a/frontend/src/app/particles/particles.component.html +++ b/frontend/src/app/particles/particles.component.html @@ -15,6 +15,44 @@
+
+ + +
+ + + + + + +
+
diff --git a/frontend/src/app/particles/particles.component.scss b/frontend/src/app/particles/particles.component.scss index cf8954f..4892087 100644 --- a/frontend/src/app/particles/particles.component.scss +++ b/frontend/src/app/particles/particles.component.scss @@ -13,6 +13,42 @@ 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 { diff --git a/frontend/src/app/particles/particles.component.ts b/frontend/src/app/particles/particles.component.ts index aff870f..1b38b13 100644 --- a/frontend/src/app/particles/particles.component.ts +++ b/frontend/src/app/particles/particles.component.ts @@ -15,7 +15,7 @@ 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 {IntersectionPlaneService, PlaneOrientation} from './services/intersection-plane.service'; import {ParticleManagerService} from './services/particle-manager.service'; import {InputHandlerService} from './services/input-handler.service'; @@ -24,6 +24,7 @@ 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'; @Component({ selector: 'app-particles', @@ -45,6 +46,7 @@ import {MatSnackBar} from '@angular/material/snack-bar'; PropertiesComponent, ParticleComponent, FramesComponent, + MatTooltip, ], templateUrl: './particles.component.html', styleUrl: './particles.component.scss' @@ -140,4 +142,40 @@ 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; + } } diff --git a/frontend/src/app/particles/services/intersection-plane.service.ts b/frontend/src/app/particles/services/intersection-plane.service.ts index 16d9646..9832f4c 100644 --- a/frontend/src/app/particles/services/intersection-plane.service.ts +++ b/frontend/src/app/particles/services/intersection-plane.service.ts @@ -5,7 +5,7 @@ import {RendererService} from './renderer.service'; /** * Represents the possible orientations of the intersection plane */ -enum PlaneOrientation { +export enum PlaneOrientation { VERTICAL_ABOVE, VERTICAL_BELOW, HORIZONTAL_FRONT, @@ -24,6 +24,7 @@ export class IntersectionPlaneService { private intersectionPlane!: THREE.Mesh; private planePosition: number = 0; // Position in 1/16th of a block private currentOrientation: PlaneOrientation = PlaneOrientation.HORIZONTAL_FRONT; + private planeLocked: boolean = false; constructor(private rendererService: RendererService) { } @@ -76,7 +77,6 @@ export class IntersectionPlaneService { // Determine which quadrant the camera is in with a 45-degree offset const quadrant = Math.floor((cameraAngle + Math.PI + Math.PI / 4) / (Math.PI / 2)) % 4; - // Return the appropriate orientation based on quadrant switch (quadrant) { case 0: return PlaneOrientation.HORIZONTAL_FRONT; @@ -98,42 +98,11 @@ export class IntersectionPlaneService { public updatePlaneOrientation(camera: THREE.Camera): void { if (!this.intersectionPlane) return; - this.currentOrientation = this.determinePlaneOrientation(camera); - - // Apply rotation and material based on orientation - switch (this.currentOrientation) { - case PlaneOrientation.VERTICAL_ABOVE: - this.intersectionPlane.rotation.x = -Math.PI / 2; - this.intersectionPlane.rotation.y = 0; - this.updatePlaneMaterial(0xAA0000); - break; - case PlaneOrientation.VERTICAL_BELOW: - this.intersectionPlane.rotation.x = Math.PI / 2; - this.intersectionPlane.rotation.y = 0; - this.updatePlaneMaterial(0xAA0000); - break; - case PlaneOrientation.HORIZONTAL_FRONT: - this.intersectionPlane.rotation.x = 0; - this.intersectionPlane.rotation.y = 0; - this.updatePlaneMaterial(0x00AA00); - break; - case PlaneOrientation.HORIZONTAL_BEHIND: - this.intersectionPlane.rotation.x = 0; - this.intersectionPlane.rotation.y = Math.PI; - this.updatePlaneMaterial(0x00AA00); - break; - case PlaneOrientation.HORIZONTAL_RIGHT: - this.intersectionPlane.rotation.x = 0; - this.intersectionPlane.rotation.y = Math.PI / 2; - this.updatePlaneMaterial(0x0000AA); - break; - case PlaneOrientation.HORIZONTAL_LEFT: - this.intersectionPlane.rotation.x = 0; - this.intersectionPlane.rotation.y = -Math.PI / 2; - this.updatePlaneMaterial(0x0000AA); - break; + if (!this.planeLocked) { + this.currentOrientation = this.determinePlaneOrientation(camera); } + this.updateIntersectionPlaneOrientation() //Restrict plane position to the new bounds and update it this.planePosition = Math.max(this.getMinOffset(), Math.min(this.getMaxOffset(), this.planePosition)); @@ -218,4 +187,69 @@ export class IntersectionPlaneService { public getMinOffset(): number { return this.getMaxOffset() * -1; } + + /** + * Gets the current plane orientation + */ + public getCurrentOrientation(): PlaneOrientation { + return this.currentOrientation; + } + + /** + * Sets the plane orientation manually + */ + public setPlaneOrientation(orientation: PlaneOrientation): void { + this.currentOrientation = orientation; + this.updateIntersectionPlaneOrientation(); + this.updatePlanePosition(this.planePosition); + } + + private updateIntersectionPlaneOrientation() { + switch (this.currentOrientation) { + case PlaneOrientation.VERTICAL_ABOVE: + this.intersectionPlane.rotation.x = -Math.PI / 2; + this.intersectionPlane.rotation.y = 0; + this.updatePlaneMaterial(0xAA0000); + break; + case PlaneOrientation.VERTICAL_BELOW: + this.intersectionPlane.rotation.x = Math.PI / 2; + this.intersectionPlane.rotation.y = 0; + this.updatePlaneMaterial(0xAA0000); + break; + case PlaneOrientation.HORIZONTAL_FRONT: + this.intersectionPlane.rotation.x = 0; + this.intersectionPlane.rotation.y = 0; + this.updatePlaneMaterial(0x00AA00); + break; + case PlaneOrientation.HORIZONTAL_BEHIND: + this.intersectionPlane.rotation.x = 0; + this.intersectionPlane.rotation.y = Math.PI; + this.updatePlaneMaterial(0x00AA00); + break; + case PlaneOrientation.HORIZONTAL_RIGHT: + this.intersectionPlane.rotation.x = 0; + this.intersectionPlane.rotation.y = Math.PI / 2; + this.updatePlaneMaterial(0x0000AA); + break; + case PlaneOrientation.HORIZONTAL_LEFT: + this.intersectionPlane.rotation.x = 0; + this.intersectionPlane.rotation.y = -Math.PI / 2; + this.updatePlaneMaterial(0x0000AA); + break; + } + } + + /** + * Gets whether the plane orientation is locked + */ + public isPlaneLocked(): boolean { + return this.planeLocked; + } + + /** + * Sets whether the plane orientation is locked + */ + public setPlaneLocked(locked: boolean): void { + this.planeLocked = locked; + } }