initial setup

This commit is contained in:
MrPlatnum committed 2025-09-08 08:42:53 +02:00
commit ff12bb40ad
77 files changed
+98381

No files matched your search

@@ -0,0 +1 @@
<p>demographics-assessment works!</p>
@@ -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();
});
});
@@ -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 {
}
@@ -0,0 +1 @@
<p>ergonomic-assessment works!</p>
@@ -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();
});
});
@@ -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 {
}
@@ -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);
});
}
}
}
@@ -0,0 +1 @@
<p>rotation-speed-assessment works!</p>
@@ -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();
});
});
@@ -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);
});
}
}
}
@@ -0,0 +1 @@
<p>scale-reference-assessment works!</p>
@@ -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();
});
});
@@ -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 {
}
@@ -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>
@@ -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();
});
});
@@ -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();
}
}
@@ -0,0 +1,5 @@
.annotation {
line-height: 1.4;
}
@@ -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>
@@ -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();
});
});
@@ -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;
}
}