Add manual plane orientation controls with lock/unlock functionality
Implemented a UI overlay in `ParticlesComponent` for manual plane orientation selection with buttons for different orientations. Added lock/unlock toggle to control automatic orientation adjustment. Refactored `IntersectionPlaneService` to support locked state and manual orientation updates. Updated styles and layout to integrate the new controls seamlessly.
This commit is contained in:
parent
c13b7077a7
commit
9808b5d63d
|
|
@ -15,6 +15,44 @@
|
|||
</div>
|
||||
<div class="flex middle-column">
|
||||
<div #rendererContainer class="renderer-container">
|
||||
<div class="plane-controls-overlay">
|
||||
<button mat-mini-fab color="primary" (click)="togglePlaneLock()" [matTooltip]="isPlaneLocked ? 'Unlock Plane' : 'Lock Plane'">
|
||||
<mat-icon>{{ isPlaneLocked ? 'lock' : 'lock_open' }}</mat-icon>
|
||||
</button>
|
||||
|
||||
<div *ngIf="isPlaneLocked" class="plane-orientation-buttons">
|
||||
<button mat-mini-fab color="warn" (click)="setPlaneOrientation(planeOrientations.VERTICAL_ABOVE)"
|
||||
[class.active]="currentPlaneOrientation === planeOrientations.VERTICAL_ABOVE"
|
||||
matTooltip="Vertical Above">
|
||||
<mat-icon>arrow_upward</mat-icon>
|
||||
</button>
|
||||
<button mat-mini-fab color="warn" (click)="setPlaneOrientation(planeOrientations.VERTICAL_BELOW)"
|
||||
[class.active]="currentPlaneOrientation === planeOrientations.VERTICAL_BELOW"
|
||||
matTooltip="Vertical Below">
|
||||
<mat-icon>arrow_downward</mat-icon>
|
||||
</button>
|
||||
<button mat-mini-fab color="primary" (click)="setPlaneOrientation(planeOrientations.HORIZONTAL_FRONT)"
|
||||
[class.active]="currentPlaneOrientation === planeOrientations.HORIZONTAL_FRONT"
|
||||
matTooltip="Horizontal Front">
|
||||
<mat-icon>arrow_forward</mat-icon>
|
||||
</button>
|
||||
<button mat-mini-fab color="primary" (click)="setPlaneOrientation(planeOrientations.HORIZONTAL_BEHIND)"
|
||||
[class.active]="currentPlaneOrientation === planeOrientations.HORIZONTAL_BEHIND"
|
||||
matTooltip="Horizontal Behind">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
<button mat-mini-fab color="accent" (click)="setPlaneOrientation(planeOrientations.HORIZONTAL_RIGHT)"
|
||||
[class.active]="currentPlaneOrientation === planeOrientations.HORIZONTAL_RIGHT"
|
||||
matTooltip="Horizontal Right">
|
||||
<mat-icon>arrow_right</mat-icon>
|
||||
</button>
|
||||
<button mat-mini-fab color="accent" (click)="setPlaneOrientation(planeOrientations.HORIZONTAL_LEFT)"
|
||||
[class.active]="currentPlaneOrientation === planeOrientations.HORIZONTAL_LEFT"
|
||||
matTooltip="Horizontal Left">
|
||||
<mat-icon>arrow_left</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="plane-controls">
|
||||
<label>Plane Position (Z-axis):</label>
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user