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([