implemented test suite

This commit is contained in:
MrPlatnum committed 2025-09-09 09:30:19 +02:00
1 parent ff12bb40ad
commit 458a1f360e
29 files changed
+1233 -382

No files matched your search

@@ -1,5 +1,26 @@
.ar-controls {
display: none;
}
.annotation {
line-height: 1.4;
}
.ar-hotspot {
display: none;
}
.ar-prompt {
display: none;
}
*/
model-viewer[ar-status="session-started"]:not([ar-tracking="tracking"]) .ar-prompt {
display: block;
}
model-viewer[ar-tracking="tracking"] .ar-hotspot,
model-viewer[ar-tracking="tracking"] .ar-controls {
display: block;
}
model-viewer[ar-tracking="tracking"] .ar-controls {
display: flex;
}
@@ -1,56 +1,73 @@
<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 class="w-full h-full relative">
<model-viewer
#modelViewer
class="absolute inset-0 w-full h-full"
src="https://modelviewer.dev/shared-assets/models/Astronaut.glb"
ar
ar-modes="webxr"
ar-placement="ceiling"
reveal="manual"
camera-orbit="0deg 75deg 2m">
<button
slot="ar-button"
class="text-xl absolute top-4 left-1/2 -translate-x-1/2 bg-blue-500 text-white py-2 px-4 rounded-lg z-10">
WebXR Umgebung laden
</button>
<button slot="hotspot-text" class="hotspot ar-hotspot" data-position="-0.1 0.93 0.1" data-normal="0 1 0">
<div class="annotation" [ngStyle]="{'font-size.px': currentSize}"
style="background:rgba(255,255,255,0.9);padding:8px;border-radius:4px;color:black;">
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
</div>
</model-viewer>
</div>
</button>
<div class="ar-controls absolute bottom-24 left-1/2 -translate-x-1/2 bg-black bg-opacity-70 p-4 rounded-xl space-y-4 text-white z-10">
<!-- Phase: Min -->
<ng-container *ngIf="phase==='min'">
<div>Minimum setzen</div>
<div class="flex items-center space-x-4">
<button (click)="decrease()" class="w-10 h-10 bg-gray-600 rounded-full">–</button>
<span>{{ currentSize }}px</span>
<button (click)="increase()" class="w-10 h-10 bg-gray-600 rounded-full">+</button>
</div>
<button (click)="nextPhase()" class="bg-green-600 py-2 px-6 rounded-lg">Minimum bestätigen</button>
</ng-container>
<!-- Phase: Confirmed Min -->
<ng-container *ngIf="phase==='confirmedMin'">
<div>Minimum bestätigt: {{ currentSize }}px</div>
<button (click)="resetToMid()" class="bg-gray-600 py-2 px-4 rounded-lg">Reset</button>
<button (click)="nextPhase()" class="bg-blue-600 py-2 px-6 rounded-lg">Weiter zu Maximum</button>
</ng-container>
<!-- Phase: Max -->
<ng-container *ngIf="phase==='max'">
<div>Maximum setzen</div>
<div class="flex items-center space-x-4">
<button (click)="decrease()" class="w-10 h-10 bg-gray-600 rounded-full">–</button>
<span>{{ currentSize }}px</span>
<button (click)="increase()" class="w-10 h-10 bg-gray-600 rounded-full">+</button>
</div>
<button (click)="nextPhase()" class="bg-green-600 py-2 px-6 rounded-lg">Maximum bestätigen</button>
</ng-container>
<!-- Phase: Confirmed Max -->
<ng-container *ngIf="phase==='confirmedMax'">
<div>Maximum bestätigt: {{ currentSize }}px</div>
<button (click)="nextPhase()" class="bg-blue-600 py-2 px-6 rounded-lg">Weiter zu optiomal</button>
</ng-container>
<!-- Phase: Comfortable -->
<ng-container *ngIf="phase==='comfortable'">
<div>Optimale Größe setzen</div>
<div class="flex items-center space-x-4">
<button (click)="decrease()" class="w-10 h-10 bg-gray-600 rounded-full">–</button>
<span>{{ currentSize }}px</span>
<button (click)="increase()" class="w-10 h-10 bg-gray-600 rounded-full">+</button>
</div>
<button (click)="finishAssessment()" class="bg-green-600 py-2 px-6 rounded-lg">Optimale Größe setzen</button>
</ng-container>
</div>
</model-viewer>
</div>
@@ -1,42 +1,124 @@
import { Component, AfterViewInit, ViewChild, ElementRef, ChangeDetectorRef, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import {
Component,
AfterViewInit,
ViewChild,
ElementRef,
ChangeDetectorRef,
CUSTOM_ELEMENTS_SCHEMA,
EventEmitter,
Output
} from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import '../../../../../assets/scripts/model-viewer';
@Component({
selector: 'app-text-legibility-assessment',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './text-legibility-assessment.component.html',
styleUrl: './text-legibility-assessment.component.css',
styleUrls: ['./text-legibility-assessment.component.css'],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class TextLegibilityAssessmentComponent implements AfterViewInit {
@ViewChild('modelViewer') modelViewerRef!: ElementRef<any>;
@Output() testComplete = new EventEmitter<void>();
@Output() redoTest = new EventEmitter<number>();
isInWebXR = false;
textSize = 16;
minSize = 2;
maxSize = 64;
currentSize = 16;
comfortableSize = 16;
phase:
| 'min'
| 'confirmedMin'
| 'max'
| 'confirmedMax'
| 'comfortable'
| 'confirmedComfort'
| 'finished' = 'min';
private offsetApplied = false;
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
const modelViewerElement = this.modelViewerRef.nativeElement;
const mv = this.modelViewerRef.nativeElement;
mv.setAttribute('scale', '0.25 0.25 0.25');
modelViewerElement.addEventListener('enter-vr', () => {
this.isInWebXR = true;
this.cdr.detectChanges();
});
modelViewerElement.addEventListener('exit-vr', () => {
this.isInWebXR = false;
this.resetTextSize();
this.cdr.detectChanges();
mv.addEventListener('ar-status', async (e: any) => {
if (e.detail.status === 'session-started' && !this.offsetApplied) {
await mv.updateComplete;
const anchor = mv.getAnchor();
if (!anchor.includes('not placed')) {
const [x, y, z] = anchor.split(' ').map(parseFloat);
mv.setAttribute('ar-anchor', `${x} ${y + 3.0} ${z}`);
this.offsetApplied = true;
}
}
});
}
updateTextSize() {
decrease() {
if (this.currentSize > this.minSize) {
this.currentSize--;
this.syncComfortable();
}
}
resetTextSize() {
this.textSize = 16;
increase() {
if (this.currentSize < this.maxSize) {
this.currentSize++;
this.syncComfortable();
}
}
private syncComfortable() {
if (this.phase === 'comfortable') {
this.comfortableSize = this.currentSize;
}
}
nextPhase() {
switch (this.phase) {
case 'min':
this.phase = 'confirmedMin';
break;
case 'confirmedMin':
this.phase = 'max';
this.currentSize = this.maxSize;
break;
case 'max':
this.phase = 'confirmedMax';
break;
case 'confirmedMax':
this.phase = 'comfortable';
this.currentSize = Math.floor((this.minSize + this.maxSize) / 2);
this.comfortableSize = this.currentSize;
break;
case 'comfortable':
this.phase = 'confirmedComfort';
this.comfortableSize = this.currentSize;
break;
case 'confirmedComfort':
this.phase = 'finished';
this.testComplete.emit();
break;
}
}
resetToMid() {
this.currentSize = Math.floor((this.minSize + this.maxSize) / 2);
this.phase = 'min';
}
retry() {
this.redoTest.emit(1);
}
finishAssessment() {
this.testComplete.emit();
}
}