initial setup
This commit is contained in:
commit
ff12bb40ad
77 files changed
+98381
No files matched your search
Whitespace-only changes.
+1
@@ -0,0 +1 @@
|
||||
<p>demographics-assessment works!</p>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DemographicsAssessmentComponent } from './demographics-assessment.component';
|
||||
|
||||
describe('DemographicsAssessmentComponent', () => {
|
||||
let component: DemographicsAssessmentComponent;
|
||||
let fixture: ComponentFixture<DemographicsAssessmentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [DemographicsAssessmentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DemographicsAssessmentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-demographics-assessment',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './demographics-assessment.component.html',
|
||||
styleUrl: './demographics-assessment.component.css'
|
||||
})
|
||||
export class DemographicsAssessmentComponent {
|
||||
|
||||
}
|
||||
Whitespace-only changes.
+1
@@ -0,0 +1 @@
|
||||
<p>ergonomic-assessment works!</p>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ErgonomicAssessmentComponent } from './ergonomic-assessment.component';
|
||||
|
||||
describe('ErgonomicAssessmentComponent', () => {
|
||||
let component: ErgonomicAssessmentComponent;
|
||||
let fixture: ComponentFixture<ErgonomicAssessmentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ErgonomicAssessmentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ErgonomicAssessmentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-ergonomic-assessment',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './ergonomic-assessment.component.html',
|
||||
styleUrl: './ergonomic-assessment.component.css'
|
||||
})
|
||||
export class ErgonomicAssessmentComponent {
|
||||
|
||||
}
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
import { Component, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { ArLoggerService } from '../../../../services/ar-logger.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-first-input-assessment',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
templateUrl: './first-input-assessment.component.html',
|
||||
styleUrls: ['./first-input-assessment.component.css']
|
||||
})
|
||||
export class FirstInputAssessmentComponent implements OnInit, OnDestroy {
|
||||
@Output() testComplete = new EventEmitter<void>();
|
||||
@Output() redoTest = new EventEmitter<void>();
|
||||
|
||||
showManualComplete = false;
|
||||
showRedoButton = false;
|
||||
firstInputDetected = false;
|
||||
sessionStartTime: number = 0;
|
||||
xrSession: any = null;
|
||||
|
||||
constructor(private logger: ArLoggerService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.logger.updateStatus('Assessment 1 initialized - Ready to begin first input detection');
|
||||
|
||||
setTimeout(() => {
|
||||
this.showManualComplete = true;
|
||||
this.showRedoButton = true;
|
||||
}, 30000);
|
||||
|
||||
this.initializeWebXRTracking();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.exitWebXRSession();
|
||||
}
|
||||
|
||||
private initializeWebXRTracking(): void {
|
||||
const modelViewer = document.getElementById('first-input-model') as any;
|
||||
|
||||
if (modelViewer) {
|
||||
modelViewer.addEventListener('ar-status', (event: any) => {
|
||||
if (event.detail.status === 'session-started') {
|
||||
this.xrSession = modelViewer.model?.webXRCamera?.session;
|
||||
this.sessionStartTime = performance.now();
|
||||
this.firstInputDetected = false;
|
||||
|
||||
console.log('WebXR session started for first input test');
|
||||
this.logger.updateStatus('WebXR active - Waiting for first input');
|
||||
|
||||
this.setupFirstInputTracking();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private setupFirstInputTracking(): void {
|
||||
if (!this.xrSession) return;
|
||||
|
||||
const onXRFrame = (time: number, frame: any) => {
|
||||
if (!this.xrSession || this.firstInputDetected) return;
|
||||
|
||||
const inputSources = this.xrSession.inputSources;
|
||||
|
||||
for (let inputSource of inputSources) {
|
||||
if (inputSource.gamepad) {
|
||||
inputSource.gamepad.buttons.forEach((button: any, index: number) => {
|
||||
if (button.pressed && !this.firstInputDetected) {
|
||||
this.handleFirstInput('button', { buttonIndex: index, inputSource: inputSource.handedness });
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
inputSource.gamepad.axes.forEach((axis: any, index: number) => {
|
||||
if (Math.abs(axis) > 0.1 && !this.firstInputDetected) {
|
||||
this.handleFirstInput('axis', { axisIndex: index, value: axis, inputSource: inputSource.handedness });
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.xrSession && !this.firstInputDetected) {
|
||||
this.xrSession.requestAnimationFrame(onXRFrame);
|
||||
}
|
||||
};
|
||||
|
||||
this.xrSession.requestAnimationFrame(onXRFrame);
|
||||
}
|
||||
|
||||
private handleFirstInput(inputType: string, inputData: any): void {
|
||||
if (this.firstInputDetected) return;
|
||||
|
||||
this.firstInputDetected = true;
|
||||
const endTime = performance.now();
|
||||
const sessionDuration = endTime - this.sessionStartTime;
|
||||
|
||||
const testData = {
|
||||
duration: sessionDuration,
|
||||
firstInputType: inputType,
|
||||
firstInputData: inputData,
|
||||
sessionDuration: sessionDuration,
|
||||
completedSuccessfully: true
|
||||
};
|
||||
|
||||
this.logger.markTestComplete('firstInputTest');
|
||||
|
||||
console.log('First input detected:', inputType, inputData);
|
||||
this.logger.updateStatus(`First input detected: ${inputType} - Auto completing test`);
|
||||
|
||||
setTimeout(() => {
|
||||
this.exitWebXRSession();
|
||||
this.completeTest();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
manualCompleteFirstTest(): void {
|
||||
if (!this.firstInputDetected) {
|
||||
this.logger.markTestComplete('firstInputTest');
|
||||
}
|
||||
this.completeTest();
|
||||
}
|
||||
|
||||
private completeTest(): void {
|
||||
this.showManualComplete = true;
|
||||
this.showRedoButton = true;
|
||||
this.testComplete.emit();
|
||||
}
|
||||
|
||||
onRedoTest(): void {
|
||||
this.firstInputDetected = false;
|
||||
this.showManualComplete = false;
|
||||
this.showRedoButton = false;
|
||||
this.exitWebXRSession();
|
||||
this.redoTest.emit();
|
||||
}
|
||||
|
||||
private exitWebXRSession(): void {
|
||||
if (this.xrSession) {
|
||||
this.xrSession.end().then(() => {
|
||||
console.log('WebXR session ended');
|
||||
this.xrSession = null;
|
||||
}).catch((err: any) => {
|
||||
console.error('Error ending WebXR session:', err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
Whitespace-only changes.
Whitespace-only changes.
+1
@@ -0,0 +1 @@
|
||||
<p>rotation-speed-assessment works!</p>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RotationSpeedAssessmentComponent } from './rotation-speed-assessment.component';
|
||||
|
||||
describe('RotationSpeedAssessmentComponent', () => {
|
||||
let component: RotationSpeedAssessmentComponent;
|
||||
let fixture: ComponentFixture<RotationSpeedAssessmentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [RotationSpeedAssessmentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(RotationSpeedAssessmentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import { Component, Output, EventEmitter, OnInit, OnDestroy } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ArLoggerService } from '../../../../services/ar-logger.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-rotation-speed-assessment',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
template: ``})
|
||||
export class RotationSpeedAssessmentComponent implements OnInit, OnDestroy {
|
||||
@Output() testComplete = new EventEmitter<void>();
|
||||
@Output() redoTest = new EventEmitter<void>();
|
||||
|
||||
rotationSpeed = 50;
|
||||
showConfirmButton = false;
|
||||
showRedoButton = false;
|
||||
xrSession: any = null;
|
||||
|
||||
constructor(private logger: ArLoggerService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.logger.updateStatus('Assessment 2 initialized - Ready to test rotation speed');
|
||||
this.initializeWebXRTracking();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.exitWebXRSession();
|
||||
}
|
||||
|
||||
private initializeWebXRTracking(): void {
|
||||
const modelViewer = document.getElementById('speed-model') as any;
|
||||
|
||||
if (modelViewer) {
|
||||
modelViewer.addEventListener('ar-status', (event: any) => {
|
||||
if (event.detail.status === 'session-started') {
|
||||
this.xrSession = modelViewer.model?.webXRCamera?.session;
|
||||
this.logger.updateStatus('WebXR active - Adjust rotation speed with slider');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onSpeedChange(event: Event): void {
|
||||
const target = event.target as HTMLInputElement;
|
||||
this.rotationSpeed = parseInt(target.value);
|
||||
|
||||
this.logger.logParameterAdjustment('speed', 'rotationSpeed', this.rotationSpeed);
|
||||
|
||||
this.updateSpeedModel(this.rotationSpeed);
|
||||
}
|
||||
|
||||
private updateSpeedModel(speed: number): void {
|
||||
const modelViewer = document.getElementById('speed-model') as any;
|
||||
if (modelViewer) {
|
||||
modelViewer.style.setProperty('--auto-rotate-delay', `${101 - speed}0ms`);
|
||||
}
|
||||
}
|
||||
|
||||
confirmSpeedSetting(): void {
|
||||
this.logger.markTestComplete('speedTest');
|
||||
this.showConfirmButton = true;
|
||||
this.showRedoButton = true;
|
||||
this.logger.updateStatus(`Speed confirmed at ${this.rotationSpeed}%`);
|
||||
}
|
||||
|
||||
protected completeTest(): void {
|
||||
this.exitWebXRSession();
|
||||
this.testComplete.emit();
|
||||
}
|
||||
|
||||
onRedoTest(): void {
|
||||
this.rotationSpeed = 50;
|
||||
this.showConfirmButton = false;
|
||||
this.showRedoButton = false;
|
||||
this.exitWebXRSession();
|
||||
this.redoTest.emit();
|
||||
}
|
||||
|
||||
private exitWebXRSession(): void {
|
||||
if (this.xrSession) {
|
||||
this.xrSession.end().then(() => {
|
||||
console.log('WebXR session ended');
|
||||
this.xrSession = null;
|
||||
}).catch((err: any) => {
|
||||
console.error('Error ending WebXR session:', err);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
+1
@@ -0,0 +1 @@
|
||||
<p>scale-reference-assessment works!</p>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ScaleReferenceAssessmentComponent } from './scale-reference-assessment.component';
|
||||
|
||||
describe('ScaleReferenceAssessmentComponent', () => {
|
||||
let component: ScaleReferenceAssessmentComponent;
|
||||
let fixture: ComponentFixture<ScaleReferenceAssessmentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ScaleReferenceAssessmentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ScaleReferenceAssessmentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-scale-reference-assessment',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './scale-reference-assessment.component.html',
|
||||
styleUrl: './scale-reference-assessment.component.css'
|
||||
})
|
||||
export class ScaleReferenceAssessmentComponent {
|
||||
|
||||
}
|
||||
Whitespace-only changes.
+54
@@ -0,0 +1,54 @@
|
||||
<div class="relative w-full h-screen">
|
||||
<model-viewer
|
||||
#modelViewer
|
||||
src="https://modelviewer.dev/shared-assets/models/Astronaut.glb"
|
||||
ar
|
||||
ar-modes="webxr"
|
||||
camera-controls
|
||||
interaction-prompt="none"
|
||||
class="w-full h-full"
|
||||
>
|
||||
<button slot="ar-button" class="bg-blue-500 text-white font-bold py-2 px-4 rounded-lg absolute bottom-4 right-4">
|
||||
Enter WebXR
|
||||
</button>
|
||||
|
||||
<!-- UI container for sliders, visible only in WebXR mode -->
|
||||
<div
|
||||
*ngIf="isInWebXR"
|
||||
class="absolute bottom-24 left-1/2 -translate-x-1/2 flex flex-col items-center space-y-6 bg-black bg-opacity-60 p-5 rounded-xl"
|
||||
>
|
||||
<!-- Scale Slider -->
|
||||
<div class="flex flex-col items-center text-white w-64">
|
||||
<label for="scale-slider" class="text-sm font-medium mb-2">Scale</label>
|
||||
<input
|
||||
id="scale-slider"
|
||||
type="range"
|
||||
min="0.5"
|
||||
max="2.5"
|
||||
step="0.01"
|
||||
[(ngModel)]="scale"
|
||||
(input)="updateModelTransform()"
|
||||
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"
|
||||
/>
|
||||
<span class="text-xs mt-1">{{ scale.toFixed(2) }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Vertical Position Slider -->
|
||||
<div class="flex flex-col items-center text-white w-64">
|
||||
<label for="offset-slider" class="text-sm font-medium mb-2">Vertical Position</label>
|
||||
<input
|
||||
id="offset-slider"
|
||||
type="range"
|
||||
min="-1"
|
||||
max="1"
|
||||
step="0.01"
|
||||
[(ngModel)]="verticalOffset"
|
||||
(input)="updateModelTransform()"
|
||||
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"
|
||||
/>
|
||||
<span class="text-xs mt-1">{{ verticalOffset.toFixed(2) }}m</span>
|
||||
</div>
|
||||
</div>
|
||||
</model-viewer>
|
||||
</div>
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SpatialPositionAssessmentComponent } from './spatial-position-assessment.component';
|
||||
|
||||
describe('SpatialPositionAssessmentComponent', () => {
|
||||
let component: SpatialPositionAssessmentComponent;
|
||||
let fixture: ComponentFixture<SpatialPositionAssessmentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [SpatialPositionAssessmentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(SpatialPositionAssessmentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { Component, AfterViewInit, ElementRef, ViewChild, ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-spatial-position-assessment',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
templateUrl: './spatial-position-assessment.component.html',
|
||||
styleUrl: './spatial-position-assessment.component.css',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class SpatialPositionAssessmentComponent implements AfterViewInit {
|
||||
@ViewChild('modelViewer') modelViewerRef!: ElementRef<any>;
|
||||
|
||||
isInWebXR = false;
|
||||
scale = 1;
|
||||
verticalOffset = 0;
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
const modelViewerElement = this.modelViewerRef.nativeElement;
|
||||
|
||||
modelViewerElement.addEventListener('enter-vr', () => {
|
||||
this.isInWebXR = true;
|
||||
modelViewerElement.cameraControls = false;
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
|
||||
modelViewerElement.addEventListener('exit-vr', () => {
|
||||
this.isInWebXR = false;
|
||||
modelViewerElement.cameraControls = true;
|
||||
this.resetModelTransform();
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
updateModelTransform() {
|
||||
const model = this.modelViewerRef.nativeElement.model;
|
||||
if (model) {
|
||||
model.scale.set(this.scale, this.scale, this.scale);
|
||||
model.position.y = this.verticalOffset;
|
||||
}
|
||||
}
|
||||
|
||||
resetModelTransform() {
|
||||
this.scale = 1;
|
||||
this.verticalOffset = 0;
|
||||
this.updateModelTransform();
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
|
||||
.annotation {
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
<div class="w-full h-screen relative">
|
||||
<div class="absolute top-0 left-0 p-6 z-10 bg-white bg-opacity-80 rounded-br-lg">
|
||||
<h3 class="text-lg font-bold">Assessment: Text Legibility Optimization</h3>
|
||||
<p class="text-sm mt-2"><strong>Objective:</strong> Determine optimal text scaling for ceiling-AR applications.</p>
|
||||
<p class="text-sm"><strong>Procedure:</strong> Adjust text size using the WebXR slider until it is clearly legible.</p>
|
||||
</div>
|
||||
|
||||
<model-viewer
|
||||
#modelViewer
|
||||
id="text-model"
|
||||
src="https://modelviewer.dev/shared-assets/models/Astronaut.glb"
|
||||
ar
|
||||
ar-modes="webxr"
|
||||
ar-placement="ceiling"
|
||||
camera-orbit="0deg 75deg 2m"
|
||||
reveal="manual"
|
||||
class="w-full h-full"
|
||||
>
|
||||
<button slot="ar-button" class="bg-blue-500 text-white font-bold py-2 px-4 rounded-lg absolute bottom-4 right-4 z-10">
|
||||
Enter WebXR - Text Size Test
|
||||
</button>
|
||||
|
||||
<!-- Hotspot with annotation text -->
|
||||
<button class="hotspot" slot="hotspot-text" data-position="0.2 0.1 0.1" data-normal="0 1 0">
|
||||
<div
|
||||
class="annotation"
|
||||
[ngStyle]="{'font-size.px': textSize}"
|
||||
style="background: rgba(255,255,255,0.9); padding: 10px; border-radius: 5px; color: black; width: 250px;"
|
||||
>
|
||||
This sample text represents typical information display requirements for ceiling-AR applications.
|
||||
Text must remain legible despite challenging viewing angles inherent to overhead displays.
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<!-- UI container for the text size slider, visible only in WebXR -->
|
||||
<div
|
||||
*ngIf="isInWebXR"
|
||||
class="absolute bottom-24 left-1/2 -translate-x-1/2 flex flex-col items-center space-y-4 bg-black bg-opacity-60 p-5 rounded-xl"
|
||||
>
|
||||
<div class="flex flex-col items-center text-white w-64">
|
||||
<label for="text-size-slider" class="text-sm font-medium mb-2">Text Size: {{ textSize }}px</label>
|
||||
<input
|
||||
id="text-size-slider"
|
||||
type="range"
|
||||
min="10"
|
||||
max="32"
|
||||
step="1"
|
||||
[(ngModel)]="textSize"
|
||||
(input)="updateTextSize()"
|
||||
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</model-viewer>
|
||||
</div>
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TextLegibilityAssessmentComponent } from './text-legibility-assessment.component';
|
||||
|
||||
describe('TextLegibilityAssessmentComponent', () => {
|
||||
let component: TextLegibilityAssessmentComponent;
|
||||
let fixture: ComponentFixture<TextLegibilityAssessmentComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [TextLegibilityAssessmentComponent]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(TextLegibilityAssessmentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { Component, AfterViewInit, ViewChild, ElementRef, ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-text-legibility-assessment',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
templateUrl: './text-legibility-assessment.component.html',
|
||||
styleUrl: './text-legibility-assessment.component.css',
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
||||
})
|
||||
export class TextLegibilityAssessmentComponent implements AfterViewInit {
|
||||
@ViewChild('modelViewer') modelViewerRef!: ElementRef<any>;
|
||||
|
||||
isInWebXR = false;
|
||||
textSize = 16;
|
||||
|
||||
constructor(private cdr: ChangeDetectorRef) {}
|
||||
|
||||
ngAfterViewInit() {
|
||||
const modelViewerElement = this.modelViewerRef.nativeElement;
|
||||
|
||||
modelViewerElement.addEventListener('enter-vr', () => {
|
||||
this.isInWebXR = true;
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
|
||||
modelViewerElement.addEventListener('exit-vr', () => {
|
||||
this.isInWebXR = false;
|
||||
this.resetTextSize();
|
||||
this.cdr.detectChanges();
|
||||
});
|
||||
}
|
||||
|
||||
updateTextSize() {
|
||||
}
|
||||
|
||||
resetTextSize() {
|
||||
this.textSize = 16;
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
@@ -0,0 +1,91 @@
|
||||
<div class="max-w-4xl mx-auto p-5 bg-white rounded-lg shadow-lg mt-8">
|
||||
<h1 class="text-3xl font-bold text-gray-800 mb-6">Ceiling-AR Design Parameter Assessment Suite</h1>
|
||||
|
||||
<!-- Progress Section -->
|
||||
<div class="mb-6">
|
||||
<div class="flex justify-between items-center mb-2">
|
||||
<span class="text-sm font-medium text-gray-700">
|
||||
Assessment {{ currentTest }} of {{ totalTests }}
|
||||
</span>
|
||||
<span class="text-sm font-medium text-gray-700">
|
||||
{{ progress | number:'1.1-1' }}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div class="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
class="bg-gradient-to-r from-blue-600 to-blue-400 h-full rounded-full transition-all duration-300 ease-out"
|
||||
[style.width.%]="progress">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status Display -->
|
||||
<div class="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-6 font-mono text-sm">
|
||||
<div class="text-gray-600">
|
||||
Status: {{ status }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Assessment Components Container -->
|
||||
<div class="assessment-container">
|
||||
|
||||
<!-- Assessment 1: First Input Detection -->
|
||||
<div *ngIf="currentTest === 1">
|
||||
<app-first-input-assessment
|
||||
(testComplete)="onTestComplete()"
|
||||
(redoTest)="redoTest(1)">
|
||||
</app-first-input-assessment>
|
||||
</div>
|
||||
|
||||
<!-- Assessment 2: Rotation Speed Optimization -->
|
||||
<div *ngIf="currentTest === 2">
|
||||
<app-rotation-speed-assessment
|
||||
(testComplete)="onTestComplete()"
|
||||
(redoTest)="redoTest(2)">
|
||||
</app-rotation-speed-assessment>
|
||||
</div>
|
||||
|
||||
<!-- Assessment 3: Ergonomic Position Optimization -->
|
||||
<div *ngIf="currentTest === 3">
|
||||
<app-ergonomic-assessment
|
||||
(testComplete)="onTestComplete()"
|
||||
(redoTest)="redoTest(3)">
|
||||
</app-ergonomic-assessment>
|
||||
</div>
|
||||
|
||||
<!-- Assessment 4: Text Legibility Optimization -->
|
||||
<div *ngIf="currentTest === 4">
|
||||
<app-text-legibility-assessment
|
||||
(testComplete)="onTestComplete()"
|
||||
(redoTest)="redoTest(4)">
|
||||
</app-text-legibility-assessment>
|
||||
</div>
|
||||
|
||||
<!-- Assessment 5: Scale Reference Calibration -->
|
||||
<div *ngIf="currentTest === 5">
|
||||
<app-scale-reference-assessment
|
||||
(testComplete)="onTestComplete()"
|
||||
(redoTest)="redoTest(5)">
|
||||
</app-scale-reference-assessment>
|
||||
</div>
|
||||
|
||||
<!-- Assessment 6: Spatial Positioning Optimization -->
|
||||
<div *ngIf="currentTest === 6">
|
||||
<app-spatial-position-assessment
|
||||
(testComplete)="onTestComplete()"
|
||||
(redoTest)="redoTest(6)">
|
||||
</app-spatial-position-assessment>
|
||||
</div>
|
||||
|
||||
<!-- Assessment 7: Demographics and Completion -->
|
||||
<div *ngIf="currentTest === 7">
|
||||
<app-demographics-assessment
|
||||
(testComplete)="onTestComplete()"
|
||||
(redoTest)="redoTest(7)">
|
||||
</app-demographics-assessment>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,80 @@
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Subject, takeUntil } from 'rxjs';
|
||||
import { TestProgressService } from '../../services/test-progress.service';
|
||||
import { ArLoggerService } from '../../services/ar-logger.service';
|
||||
|
||||
import { RotationSpeedAssessmentComponent } from './assessments/rotation-speed-assessment/rotation-speed-assessment.component';
|
||||
import { ErgonomicAssessmentComponent } from './assessments/ergonomic-assessment/ergonomic-assessment.component';
|
||||
import { TextLegibilityAssessmentComponent } from './assessments/text-legibility-assessment/text-legibility-assessment.component';
|
||||
import { ScaleReferenceAssessmentComponent } from './assessments/scale-reference-assessment/scale-reference-assessment.component';
|
||||
import { SpatialPositionAssessmentComponent } from './assessments/spatial-position-assessment/spatial-position-assessment.component';
|
||||
import { DemographicsAssessmentComponent } from './assessments/demographics-assessment/demographics-assessment.component';
|
||||
import { FirstInputAssessmentComponent } from './assessments/first-input-assesment/first-input-assesment.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-test-suite',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FirstInputAssessmentComponent,
|
||||
RotationSpeedAssessmentComponent,
|
||||
ErgonomicAssessmentComponent,
|
||||
TextLegibilityAssessmentComponent,
|
||||
ScaleReferenceAssessmentComponent,
|
||||
SpatialPositionAssessmentComponent,
|
||||
DemographicsAssessmentComponent
|
||||
],
|
||||
templateUrl: './test-suite.component.html',
|
||||
styleUrls: ['./test-suite.component.css']
|
||||
})
|
||||
export class TestSuiteComponent implements OnInit, OnDestroy {
|
||||
currentTest = 1;
|
||||
totalTests = 7;
|
||||
progress = 14.3;
|
||||
status = 'Ready to begin assessments';
|
||||
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
constructor(
|
||||
private progressService: TestProgressService,
|
||||
private logger: ArLoggerService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.progressService.currentTest$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(test => {
|
||||
this.currentTest = test;
|
||||
this.logger.setCurrentTest(test);
|
||||
});
|
||||
|
||||
this.progressService.progress$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(progress => {
|
||||
this.progress = progress;
|
||||
});
|
||||
|
||||
this.logger.status$
|
||||
.pipe(takeUntil(this.destroy$))
|
||||
.subscribe(status => {
|
||||
this.status = status;
|
||||
});
|
||||
|
||||
this.totalTests = this.progressService.getTotalTests();
|
||||
this.logger.updateStatus('Test suite initialized - Assessment 1 ready');
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
}
|
||||
|
||||
onTestComplete(): void {
|
||||
this.progressService.nextTest();
|
||||
}
|
||||
|
||||
redoTest(testNumber: number): void {
|
||||
this.progressService.goToTest(testNumber);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user