public play()

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;
        }
    }