Rework folder structure in frontend
Pages are now grouped per group they appear in on in the header (where possible) Utilities used by multiple pages in the project are grouped in folders such as services/pipes/etc
This commit is contained in:
+54
@@ -0,0 +1,54 @@
|
||||
<div #rendererContainer class="renderer-container">
|
||||
<div class="plane-controls-overlay">
|
||||
<div>
|
||||
<mat-form-field appearance="outline" style="width: 10ch">
|
||||
<mat-label>Opacity</mat-label>
|
||||
<input matInput type="number" [(ngModel)]="opacity" min="0" max="1" step="0.01" placeholder="">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
<div class="button-row">
|
||||
<button mat-mini-fab color="primary" (click)="resetCamera()"
|
||||
matTooltip="Reset camera">
|
||||
<mat-icon>location_searching</mat-icon>
|
||||
</button>
|
||||
|
||||
<button mat-mini-fab color="primary" (click)="togglePlaneLock()"
|
||||
[matTooltip]="isPlaneLocked ? 'Unlock Plane' : 'Lock Plane'">
|
||||
<mat-icon>{{ isPlaneLocked ? 'lock' : 'lock_open' }}</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
.renderer-container {
|
||||
height: 1000px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-primairy);
|
||||
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;
|
||||
}
|
||||
|
||||
.button-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.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 var(--color-tertiary);
|
||||
}
|
||||
+23
@@ -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<RenderContainerComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [RenderContainerComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RenderContainerComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
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';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {MatFormField, MatInput, MatLabel} from '@angular/material/input';
|
||||
|
||||
@Component({
|
||||
selector: 'app-render-container',
|
||||
imports: [
|
||||
MatIcon,
|
||||
MatMiniFabButton,
|
||||
MatTooltip,
|
||||
NgIf,
|
||||
FormsModule,
|
||||
MatInput,
|
||||
MatFormField,
|
||||
MatLabel
|
||||
],
|
||||
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.loadSkinTexture('/public/img/skins/steve.png')
|
||||
.then(() => {
|
||||
// Then create the player model with the texture applied
|
||||
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();
|
||||
}
|
||||
|
||||
public set opacity(opacity: number) {
|
||||
this.intersectionPlaneService.currentOpacity = opacity;
|
||||
}
|
||||
|
||||
public get opacity(): number {
|
||||
return this.intersectionPlaneService.currentOpacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the plane locked state
|
||||
*/
|
||||
public togglePlaneLock(): void {
|
||||
const newLockedState = !this.isPlaneLocked;
|
||||
this.intersectionPlaneService.setPlaneLocked(newLockedState);
|
||||
}
|
||||
|
||||
public resetCamera(): void {
|
||||
this.rendererService.resetCamera();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user