diff --git a/src/app/components/entity-feature-annotations/annotationwalkthrough/annotationwalkthrough.component.html b/src/app/components/entity-feature-annotations/annotationwalkthrough/annotationwalkthrough.component.html
index 1bd19d84..f8398dbe 100644
--- a/src/app/components/entity-feature-annotations/annotationwalkthrough/annotationwalkthrough.component.html
+++ b/src/app/components/entity-feature-annotations/annotationwalkthrough/annotationwalkthrough.component.html
@@ -2,6 +2,7 @@
{{ title$ | async }}
- keyboard_arrow_right
+ keyboard_arrow_right
}
diff --git a/src/app/components/entity-feature-annotations/annotationwalkthrough/annotationwalkthrough.component.ts b/src/app/components/entity-feature-annotations/annotationwalkthrough/annotationwalkthrough.component.ts
index 8ee918f8..c750f733 100644
--- a/src/app/components/entity-feature-annotations/annotationwalkthrough/annotationwalkthrough.component.ts
+++ b/src/app/components/entity-feature-annotations/annotationwalkthrough/annotationwalkthrough.component.ts
@@ -17,7 +17,7 @@ export class AnnotationwalkthroughComponent {
public annotationService = inject(AnnotationService);
#isRepositioning = toObservable(this.annotationService.isRepositioning);
- public ranking$ = new BehaviorSubject(-1);
+ public ranking$ = new BehaviorSubject(0);
public selectedAnnotation$ = combineLatest([
this.annotationService.currentAnnotations$,
this.annotationService.selectedAnnotation$,
@@ -48,7 +48,7 @@ export class AnnotationwalkthroughComponent {
firstValueFrom(this.ranking$),
firstValueFrom(this.annotationService.currentAnnotations$),
]);
- const isFirst = ranking === 0;
+ const isFirst = ranking <= 0;
const newRanking = isFirst ? annotations.length - 1 : ranking - 1;
this.ranking$.next(newRanking);
this.annotationService.setSelectedAnnotation(annotations[newRanking]._id.toString());
diff --git a/src/app/services/annotation/annotation.service.ts b/src/app/services/annotation/annotation.service.ts
index c0a99846..12b67a26 100644
--- a/src/app/services/annotation/annotation.service.ts
+++ b/src/app/services/annotation/annotation.service.ts
@@ -12,7 +12,14 @@ import {
UtilityLayerRenderer,
Vector3,
} from '@babylonjs/core';
-import { BehaviorSubject, ReplaySubject, combineLatest, firstValueFrom, fromEvent } from 'rxjs';
+import {
+ BehaviorSubject,
+ Observable,
+ ReplaySubject,
+ combineLatest,
+ firstValueFrom,
+ fromEvent,
+} from 'rxjs';
import { distinct, filter, map, switchMap } from 'rxjs/operators';
import { IAnnotation, asVector3, isAnnotation } from '@kompakkt/common';
import { annotationFallback, annotationLogo } from '../../../assets/annotations/annotations';
@@ -69,6 +76,11 @@ export class AnnotationService {
public isAnnotationMode$ = new BehaviorSubject(false);
+ #isTransitioningSubject = new BehaviorSubject(false);
+ public get isTransitioning$(): Observable {
+ return this.#isTransitioningSubject.asObservable();
+ }
+
private defaultOffset = 0;
public picked$ = new ReplaySubject();
@@ -720,7 +732,7 @@ export class AnnotationService {
// Saved position is { x: alpha, y: beta, z: radius } (spherical ArcRotate)
const position = asVector3(perspective.position);
const target = asVector3(perspective.target);
- this.babylon.cameraManager.smoothCameraTransitionFromSpherical({
+ const animatable = this.babylon.cameraManager.smoothCameraTransitionFromSpherical({
camera: this.babylon.cameraManager.getActiveCamera(),
scene: this.babylon.getScene(),
alpha: position.x,
@@ -728,6 +740,8 @@ export class AnnotationService {
radius: position.z,
target: Vector3.FromArray(Object.values(target)),
});
+ this.#isTransitioningSubject.next(true);
+ animatable.onAnimationEndObservable.addOnce(() => this.#isTransitioningSubject.next(false));
}
this.babylon.hideMesh(selectedAnnotation._id.toString(), true);
}
diff --git a/src/app/services/babylon/camera-handler.ts b/src/app/services/babylon/camera-handler.ts
index 0e53493f..f61bc4e6 100644
--- a/src/app/services/babylon/camera-handler.ts
+++ b/src/app/services/babylon/camera-handler.ts
@@ -233,14 +233,11 @@ export const smoothCameraTransitionFromSpherical = (
},
speed = 1,
) => {
- console.log(
- 'SmoothCameraTransitionFromSpherical from',
- JSON.stringify([camera.alpha, camera.beta, camera.radius, camera.target]),
- 'to',
- JSON.stringify([alpha, beta, radius, target]),
- );
-
- camera.target = new Vector3(target.x, target.y, target.z);
+ // Shortest-path alpha wrap: pick the equivalent alpha value within ±π of
+ // the camera's current alpha so the animation takes the short way around.
+ const TAU = Math.PI * 2;
+ const delta = ((((alpha - camera.alpha + Math.PI) % TAU) + TAU) % TAU) - Math.PI;
+ const wrappedAlpha = camera.alpha + delta;
const animAlpha = new Animation(
'camera_alpha_animation',
@@ -273,7 +270,7 @@ export const smoothCameraTransitionFromSpherical = (
animAlpha.setKeys([
{ frame: 0, value: camera.alpha },
- { frame: FPS, value: alpha },
+ { frame: FPS, value: wrappedAlpha },
]);
animBeta.setKeys([