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;
+ }
}