175 lines
4.9 KiB
TypeScript
175 lines
4.9 KiB
TypeScript
import {
|
|
Component,
|
|
AfterViewInit,
|
|
OnDestroy,
|
|
ViewChild,
|
|
ElementRef,
|
|
CUSTOM_ELEMENTS_SCHEMA,
|
|
ChangeDetectorRef,
|
|
EventEmitter,
|
|
Output,
|
|
inject
|
|
} from '@angular/core';
|
|
import { CommonModule } from '@angular/common';
|
|
import { FormsModule } from '@angular/forms';
|
|
import { MetricsTrackerService } from '../../../../services/metrics-tracker.service';
|
|
|
|
import '../../../../../assets/scripts/model-viewer';
|
|
|
|
@Component({
|
|
selector: 'app-spatial-stability-assessment',
|
|
standalone: true,
|
|
imports: [CommonModule, FormsModule],
|
|
templateUrl: './spatial-stability-assessment.component.html',
|
|
styleUrls: ['./spatial-stability-assessment.component.css'],
|
|
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
|
})
|
|
export class SpatialStabilityAssessmentComponent implements AfterViewInit, OnDestroy { // <-- Implement OnDestroy
|
|
@ViewChild('modelViewer') modelViewerRef!: ElementRef<any>;
|
|
@Output() testComplete = new EventEmitter<void>();
|
|
@Output() redoTest = new EventEmitter<number>();
|
|
|
|
private metricsService = inject(MetricsTrackerService);
|
|
|
|
public verticalOffset = 0;
|
|
public currentPhase = 0; // 0: waiting, 1: adjust, 2: lock, 3: countdown, 4: complete
|
|
public remainingTime = 20;
|
|
public progressPercentage = 0;
|
|
|
|
private initialArAnchor: { x: number, y: number, z: number } | null = null;
|
|
private lockedPosition: { verticalOffset: number, anchor: string } | null = null;
|
|
private countdownInterval: any = null;
|
|
protected isModelPlaced = false;
|
|
|
|
constructor(private cdr: ChangeDetectorRef) {
|
|
this.logInteraction = this.logInteraction.bind(this);
|
|
}
|
|
|
|
ngAfterViewInit() {
|
|
const modelViewer = this.modelViewerRef.nativeElement;
|
|
|
|
this.metricsService.startDeviceOrientationTracking();
|
|
this.metricsService.startArTracking(modelViewer);
|
|
|
|
modelViewer.addEventListener('ar-status', (event: any) => {
|
|
if (event.detail.status === 'session-started' && !this.isModelPlaced) {
|
|
setTimeout(() => this.getInitialAnchor(), 1000);
|
|
}
|
|
});
|
|
}
|
|
|
|
public logInteraction(event: Event) {
|
|
this.metricsService.logInteraction(event);
|
|
}
|
|
|
|
private async getInitialAnchor() {
|
|
const modelViewer = this.modelViewerRef.nativeElement;
|
|
const anchorString = modelViewer.getAnchor();
|
|
if (!anchorString.includes('not placed')) {
|
|
const coords = anchorString.split(' ').map(parseFloat);
|
|
this.initialArAnchor = { x: coords[0], y: coords[1], z: coords[2] };
|
|
this.isModelPlaced = true;
|
|
this.currentPhase = 1;
|
|
this.cdr.detectChanges();
|
|
} else {
|
|
setTimeout(() => this.getInitialAnchor(), 500);
|
|
}
|
|
}
|
|
|
|
onSliderInput() {
|
|
if (this.initialArAnchor && this.currentPhase === 1) {
|
|
const newY = this.initialArAnchor.y + this.verticalOffset;
|
|
const anchor = `${this.initialArAnchor.x} ${newY} ${this.initialArAnchor.z}`;
|
|
this.modelViewerRef.nativeElement.setAttribute('ar-anchor', anchor);
|
|
}
|
|
}
|
|
|
|
lockPosition() {
|
|
this.lockedPosition = {
|
|
verticalOffset: this.verticalOffset,
|
|
anchor: this.modelViewerRef.nativeElement.getAttribute('ar-anchor')
|
|
};
|
|
this.currentPhase = 2;
|
|
}
|
|
|
|
adjustMore() {
|
|
this.currentPhase = 1;
|
|
}
|
|
|
|
startCountdown() {
|
|
this.currentPhase = 3;
|
|
this.remainingTime = 20;
|
|
this.progressPercentage = 0;
|
|
|
|
this.countdownInterval = setInterval(() => {
|
|
this.remainingTime--;
|
|
this.progressPercentage = ((20 - this.remainingTime) / 20) * 100;
|
|
|
|
if (this.remainingTime <= 0) {
|
|
this.completeTest(true);
|
|
}
|
|
this.cdr.detectChanges();
|
|
}, 1000);
|
|
}
|
|
|
|
cancelTest() {
|
|
if (this.countdownInterval) {
|
|
clearInterval(this.countdownInterval);
|
|
this.countdownInterval = null;
|
|
}
|
|
this.currentPhase = 2;
|
|
this.metricsService.logInteraction(new CustomEvent('test-cancelled', { detail: { phase: 'countdown' }}));
|
|
}
|
|
|
|
private completeTest(wasCompleted: boolean) {
|
|
if (this.countdownInterval) {
|
|
clearInterval(this.countdownInterval);
|
|
this.countdownInterval = null;
|
|
}
|
|
|
|
const finalResults = {
|
|
testCompletedSuccessfully: wasCompleted,
|
|
initialAnchor: this.initialArAnchor,
|
|
lockedPosition: this.lockedPosition,
|
|
};
|
|
|
|
this.metricsService.logInteraction(new CustomEvent('test-results', {
|
|
detail: {
|
|
testName: 'SpatialStability',
|
|
results: finalResults
|
|
}
|
|
}));
|
|
|
|
if (wasCompleted) {
|
|
this.currentPhase = 4;
|
|
}
|
|
|
|
if(wasCompleted) {
|
|
this.testComplete.emit();
|
|
}
|
|
}
|
|
|
|
restartTest() {
|
|
this.currentPhase = 1;
|
|
this.remainingTime = 20;
|
|
this.progressPercentage = 0;
|
|
this.lockedPosition = null;
|
|
if (this.countdownInterval) {
|
|
clearInterval(this.countdownInterval);
|
|
this.countdownInterval = null;
|
|
}
|
|
this.metricsService.resetMetrics();
|
|
}
|
|
|
|
finishAssessment() {
|
|
this.testComplete.emit();
|
|
}
|
|
|
|
ngOnDestroy() {
|
|
if (this.countdownInterval) {
|
|
clearInterval(this.countdownInterval);
|
|
}
|
|
this.metricsService.cleanup();
|
|
}
|
|
}
|