in src/viewer/PlayService.ts [122:421]
public play(): void {
if (this._playing) {
return;
}
this._stateService.cutImages();
const stateSpeed: number = this._setSpeed(this._speed);
this._stateService.setSpeed(stateSpeed);
this._graphModeSubscription = this._speed$.pipe(
map(
(speed: number): GraphMode => {
return speed > PlayService.sequenceSpeed ? GraphMode.Sequence : GraphMode.Spatial;
}),
distinctUntilChanged())
.subscribe(
(mode: GraphMode): void => {
this._graphService.setGraphMode(mode);
});
this._cacheSubscription = observableCombineLatest(
this._stateService.currentImage$.pipe(
map(
(image: Image): [string, string] => {
return [image.sequenceId, image.id];
}),
distinctUntilChanged(
undefined,
([sequenceId]: [string, string]): string => {
return sequenceId;
})),
this._graphService.graphMode$,
this._direction$).pipe(
switchMap(
([[sequenceId, imageId], mode, direction]: [[string, string], GraphMode, NavigationDirection]):
Observable<[Sequence, NavigationDirection]> => {
if (direction !== NavigationDirection.Next && direction !== NavigationDirection.Prev) {
return observableOf<[Sequence, NavigationDirection]>([undefined, direction]);
}
const sequence$: Observable<Sequence> = (mode === GraphMode.Sequence ?
this._graphService.cacheSequenceImages$(sequenceId, imageId) :
this._graphService.cacheSequence$(sequenceId)).pipe(
retry(3),
catchError(
(error: Error): Observable<Sequence> => {
console.error(error);
return observableOf(undefined);
}));
return observableCombineLatest(
sequence$,
observableOf(direction));
}),
switchMap(
([sequence, direction]: [Sequence, NavigationDirection]): Observable<string> => {
if (sequence === undefined) {
return observableEmpty();
}
const imageIds: string[] = sequence.imageIds.slice();
if (direction === NavigationDirection.Prev) {
imageIds.reverse();
}
return this._stateService.currentState$.pipe(
map(
(frame: AnimationFrame): [string, number] => {
return [frame.state.trajectory[frame.state.trajectory.length - 1].id, frame.state.imagesAhead];
}),
scan(
(
[lastRequestKey, previousRequestKeys]: [string, string[]],
[lastTrajectoryKey, imagesAhead]: [string, number]):
[string, string[]] => {
if (lastRequestKey === undefined) {
lastRequestKey = lastTrajectoryKey;
}
const lastIndex: number = imageIds.length - 1;
if (imagesAhead >= this._imagesAhead || imageIds[lastIndex] === lastRequestKey) {
return [lastRequestKey, []];
}
const current: number = imageIds.indexOf(lastTrajectoryKey);
const start: number = imageIds.indexOf(lastRequestKey) + 1;
const end: number = Math.min(lastIndex, current + this._imagesAhead - imagesAhead) + 1;
if (end <= start) {
return [lastRequestKey, []];
}
return [imageIds[end - 1], imageIds.slice(start, end)];
},
[undefined, []]),
mergeMap(
([lastRequestKey, newRequestKeys]: [string, string[]]): Observable<string> => {
return observableFrom(newRequestKeys);
}));
}),
mergeMap(
(key: string): Observable<Image> => {
return this._graphService.cacheImage$(key).pipe(
catchError(
(): Observable<Image> => {
return observableEmpty();
}));
},
6))
.subscribe();
this._playingSubscription = this._stateService.currentState$.pipe(
filter(
(frame: AnimationFrame): boolean => {
return frame.state.imagesAhead < this._imagesAhead;
}),
distinctUntilChanged(
undefined,
(frame: AnimationFrame): string => {
return frame.state.lastImage.id;
}),
map(
(frame: AnimationFrame): [Image, boolean] => {
const lastImage: Image = frame.state.lastImage;
const trajectory: Image[] = frame.state.trajectory;
let increasingTime: boolean = undefined;
for (let i: number = trajectory.length - 2; i >= 0; i--) {
const image: Image = trajectory[i];
if (image.sequenceId !== lastImage.sequenceId) {
break;
}
if (image.capturedAt !== lastImage.capturedAt) {
increasingTime = image.capturedAt < lastImage.capturedAt;
break;
}
}
return [frame.state.lastImage, increasingTime];
}),
withLatestFrom(this._direction$),
switchMap(
([[image, increasingTime], direction]: [[Image, boolean], NavigationDirection]): Observable<Image> => {
return observableZip(
([NavigationDirection.Next, NavigationDirection.Prev].indexOf(direction) > -1 ?
image.sequenceEdges$ :
image.spatialEdges$).pipe(
first(
(status: NavigationEdgeStatus): boolean => {
return status.cached;
}),
timeout(15000)),
observableOf<NavigationDirection>(direction)).pipe(
map(
([s, d]: [NavigationEdgeStatus, NavigationDirection]): string => {
for (let edge of s.edges) {
if (edge.data.direction === d) {
return edge.target;
}
}
return null;
}),
switchMap(
(key: string): Observable<Image> => {
return key != null ?
this._graphService.cacheImage$(key) :
observableEmpty();
}));
}))
.subscribe(
(image: Image): void => {
this._stateService.appendImagess([image]);
},
(error: Error): void => {
console.error(error);
this.stop();
});
this._clearSubscription = this._stateService.currentImage$.pipe(
bufferCount(1, 10))
.subscribe(
(images: Image[]): void => {
this._stateService.clearPriorImages();
});
this._setPlaying(true);
const currentLastImages$: Observable<Image> = this._stateService.currentState$.pipe(
map(
(frame: AnimationFrame): IAnimationState => {
return frame.state;
}),
distinctUntilChanged(
([kc1, kl1]: [string, string], [kc2, kl2]: [string, string]): boolean => {
return kc1 === kc2 && kl1 === kl2;
},
(state: IAnimationState): [string, string] => {
return [state.currentImage.id, state.lastImage.id];
}),
filter(
(state: IAnimationState): boolean => {
return state.currentImage.id === state.lastImage.id &&
state.currentIndex === state.trajectory.length - 1;
}),
map(
(state: IAnimationState): Image => {
return state.currentImage;
}));
this._stopSubscription = observableCombineLatest(
currentLastImages$,
this._direction$).pipe(
switchMap(
([image, direction]: [Image, NavigationDirection]): Observable<boolean> => {
const edgeStatus$: Observable<NavigationEdgeStatus> = (
[NavigationDirection.Next, NavigationDirection.Prev].indexOf(direction) > -1 ?
image.sequenceEdges$ :
image.spatialEdges$).pipe(
first(
(status: NavigationEdgeStatus): boolean => {
return status.cached;
}),
timeout(15000),
catchError(
(error: Error): Observable<NavigationEdgeStatus> => {
console.error(error);
return observableOf<NavigationEdgeStatus>({ cached: false, edges: [] });
}));
return observableCombineLatest(
observableOf(direction),
edgeStatus$).pipe(
map(
([d, es]: [NavigationDirection, NavigationEdgeStatus]): boolean => {
for (const edge of es.edges) {
if (edge.data.direction === d) {
return true;
}
}
return false;
}));
}),
mergeMap(
(hasEdge: boolean): Observable<boolean> => {
if (hasEdge || !this._bridging$) {
return observableOf(hasEdge);
}
return this._bridging$.pipe(
map(
(image: Image): boolean => {
return image != null;
}),
catchError(
(error: Error): Observable<boolean> => {
console.error(error);
return observableOf<boolean>(false);
}));
}),
first(
(hasEdge: boolean): boolean => {
return !hasEdge;
}))
.subscribe(
undefined,
undefined,
(): void => { this.stop(); });
if (this._stopSubscription.closed) {
this._stopSubscription = null;
}
this._earthSubscription = this._stateService.state$
.pipe(
map(
(state: State): boolean => {
return state === State.Earth;
}),
distinctUntilChanged(),
first(
(earth: boolean): boolean => {
return earth;
}))
.subscribe(
undefined,
undefined,
(): void => { this.stop(); });
if (this._earthSubscription.closed) {
this._earthSubscription = null;
}
}