protected _activate()

in src/component/image/ImageComponent.ts [136:498]


    protected _activate(): void {
        const subs = this._subscriptions;

        subs.push(this._renderer$.pipe(
            map(
                (renderer: ImageGLRenderer): GLRenderHash => {
                    const renderHash: GLRenderHash = {
                        name: this._name,
                        renderer: {
                            frameId: renderer.frameId,
                            needsRender: renderer.needsRender,
                            render: renderer.render.bind(renderer),
                            pass: RenderPass.Background,
                        },
                    };

                    renderer.clearNeedsRender();

                    return renderHash;
                }))
            .subscribe(this._container.glRenderer.render$));

        this._rendererCreator$.next(null);

        subs.push(this._navigator.stateService.currentState$.pipe(
            map(
                (frame: AnimationFrame): ImageGLRendererOperation => {
                    return (renderer: ImageGLRenderer): ImageGLRenderer => {
                        renderer.updateFrame(frame);

                        return renderer;
                    };
                }))
            .subscribe(this._rendererOperation$));

        const textureProvider$ =
            this._container.configurationService.imageTiling$.pipe(
                switchMap(
                    (active): Observable<AnimationFrame> => {
                        return active ?
                            this._navigator.stateService.currentState$ :
                            new Subject();
                    }),
                distinctUntilChanged(
                    undefined,
                    (frame: AnimationFrame): string => {
                        return frame.state.currentImage.id;
                    }),
                withLatestFrom(
                    this._container.glRenderer.webGLRenderer$),
                map(
                    ([frame, renderer]: TextureProviderInput)
                        : TextureProvider => {
                        const state = frame.state;
                        const currentNode = state.currentImage;
                        const currentTransform = state.currentTransform;

                        return new TextureProvider(
                            currentNode.id,
                            currentTransform.basicWidth,
                            currentTransform.basicHeight,
                            currentNode.image,
                            this._imageTileLoader,
                            new TileStore(),
                            renderer);
                    }),
                publishReplay(1),
                refCount());

        subs.push(textureProvider$.subscribe(() => { /*noop*/ }));

        subs.push(textureProvider$.pipe(
            map(
                (provider: TextureProvider): ImageGLRendererOperation => {
                    return (renderer: ImageGLRenderer): ImageGLRenderer => {
                        renderer.setTextureProvider(provider.id, provider);

                        return renderer;
                    };
                }))
            .subscribe(this._rendererOperation$));

        subs.push(textureProvider$.pipe(
            pairwise())
            .subscribe(
                (pair: [TextureProvider, TextureProvider]): void => {
                    const previous = pair[0];
                    previous.abort();
                }));

        const roiTrigger$ =
            this._container.configurationService.imageTiling$.pipe(
                switchMap(
                    (active): Observable<[State, boolean]> => {
                        return active ?
                            observableCombineLatest(
                                this._navigator.stateService.state$,
                                this._navigator.stateService.inTranslation$) :
                            new Subject();
                    }),
                switchMap(
                    ([state, inTranslation]: [State, boolean]) => {
                        const streetState =
                            state === State.Traversing ||
                            state === State.Waiting ||
                            state === State.WaitingInteractively;
                        const active = streetState && !inTranslation;
                        return active ?
                            this._container.renderService.renderCameraFrame$ :
                            observableEmpty();
                    }),
                map(
                    (camera: RenderCamera): PositionLookat => {
                        return {
                            camera,
                            height: camera.size.height.valueOf(),
                            lookat: camera.camera.lookat.clone(),
                            width: camera.size.width.valueOf(),
                            zoom: camera.zoom.valueOf(),
                        };
                    }),
                pairwise(),
                map(
                    ([pl0, pl1]: [PositionLookat, PositionLookat])
                        : StalledCamera => {
                        const stalled =
                            pl0.width === pl1.width &&
                            pl0.height === pl1.height &&
                            pl0.zoom === pl1.zoom &&
                            pl0.lookat.equals(pl1.lookat);

                        return { camera: pl1.camera, stalled };
                    }),
                distinctUntilChanged(
                    (x, y): boolean => {
                        return x.stalled === y.stalled;
                    }),
                filter(
                    (camera: StalledCamera): boolean => {
                        return camera.stalled;
                    }),
                withLatestFrom(
                    this._container.renderService.size$,
                    this._navigator.stateService.currentTransform$));

        subs.push(textureProvider$.pipe(
            switchMap(
                (provider: TextureProvider):
                    Observable<[TileRegionOfInterest, TextureProvider]> => {
                    return roiTrigger$.pipe(
                        map(
                            ([stalled, size, transform]: RoiTrigger)
                                : [TileRegionOfInterest, TextureProvider] => {

                                const camera = stalled.camera;
                                const basic = new ViewportCoords()
                                    .viewportToBasic(
                                        0,
                                        0,
                                        transform,
                                        camera.perspective);

                                if (basic[0] < 0 ||
                                    basic[1] < 0 ||
                                    basic[0] > 1 ||
                                    basic[1] > 1) {
                                    return undefined;
                                }

                                return [
                                    this._roiCalculator
                                        .computeRegionOfInterest(
                                            camera,
                                            size,
                                            transform),
                                    provider,
                                ];
                            }),
                        filter(
                            (args: [TileRegionOfInterest, TextureProvider]): boolean => {
                                return !!args;
                            }));
                }),
            filter(
                (args: [TileRegionOfInterest, TextureProvider]): boolean => {
                    return !args[1].disposed;
                }))
            .subscribe(
                ([roi, provider]: [TileRegionOfInterest, TextureProvider])
                    : void => {
                    provider.setRegionOfInterest(roi);
                }));

        const hasTexture$ = textureProvider$
            .pipe(
                switchMap(
                    (provider: TextureProvider): Observable<boolean> => {
                        return provider.hasTexture$;
                    }),
                startWith(false),
                publishReplay(1),
                refCount());

        subs.push(hasTexture$.subscribe(() => { /*noop*/ }));

        subs.push(this._navigator.panService.panImages$.pipe(
            filter(
                (panNodes: []): boolean => {
                    return panNodes.length === 0;
                }),
            map(
                (): ImageGLRendererOperation => {
                    return (renderer: ImageGLRenderer): ImageGLRenderer => {
                        renderer.clearPeripheryPlanes();

                        return renderer;
                    };
                }))
            .subscribe(this._rendererOperation$));

        const cachedPanNodes$ = this._navigator.panService.panImages$.pipe(
            switchMap(
                (nts: [ImageNode, Transform, number][]): Observable<[ImageNode, Transform]> => {
                    return observableFrom(nts).pipe(
                        mergeMap(
                            ([n, t]: [ImageNode, Transform, number]): Observable<[ImageNode, Transform]> => {
                                return observableCombineLatest(
                                    this._navigator.graphService.cacheImage$(n.id).pipe(
                                        catchError(
                                            (error: Error): Observable<ImageNode> => {
                                                console.error(`Failed to cache periphery image (${n.id})`, error);

                                                return observableEmpty();
                                            })),
                                    observableOf(t));
                            }));
                }),
            share());

        subs.push(cachedPanNodes$.pipe(
            map(
                ([n, t]: [ImageNode, Transform]): ImageGLRendererOperation => {
                    return (renderer: ImageGLRenderer): ImageGLRenderer => {
                        renderer.addPeripheryPlane(n, t);

                        return renderer;
                    };
                }))
            .subscribe(this._rendererOperation$));

        subs.push(cachedPanNodes$.pipe(
            mergeMap(
                ([n]: [ImageNode, Transform]): Observable<ImageNode> => {
                    return n.cacheImage$().pipe(
                        catchError(
                            (): Observable<ImageNode> => {
                                return observableEmpty();
                            }));
                }),
            map(
                (n: ImageNode): ImageGLRendererOperation => {
                    return (renderer: ImageGLRenderer): ImageGLRenderer => {
                        renderer.updateTextureImage(n.image, n);

                        return renderer;
                    };
                }))
            .subscribe(this._rendererOperation$));

        const inTransition$ = this._navigator.stateService.currentState$.pipe(
            map(
                (frame: AnimationFrame): boolean => {
                    return frame.state.alpha < 1;
                }),
            distinctUntilChanged());

        const panTrigger$ = observableCombineLatest(
            this._container.mouseService.active$,
            this._container.touchService.active$,
            this._navigator.stateService.inMotion$,
            inTransition$).pipe(
                map(
                    ([mouseActive, touchActive, inMotion, inTransition]: [boolean, boolean, boolean, boolean]): boolean => {
                        return !(mouseActive || touchActive || inMotion || inTransition);
                    }),
                filter(
                    (trigger: boolean): boolean => {
                        return trigger;
                    }));

        subs.push(this._navigator.stateService.state$
            .pipe(
                switchMap(
                    state => {
                        return state === State.Traversing ||
                            state === State.GravityTraversing ?
                            this._navigator.panService.panImages$ :
                            observableEmpty();

                    }),
                switchMap(
                    (nts: [ImageNode, Transform, number][]):
                        Observable<[RenderCamera, ImageNode, Transform, [ImageNode, Transform, number][]]> => {

                        return panTrigger$.pipe(
                            withLatestFrom(
                                this._container.renderService.renderCamera$,
                                this._navigator.stateService.currentImage$,
                                this._navigator.stateService.currentTransform$),
                            mergeMap(
                                ([, renderCamera, currentNode, currentTransform]: [boolean, RenderCamera, ImageNode, Transform]):
                                    Observable<[RenderCamera, ImageNode, Transform, [ImageNode, Transform, number][]]> => {
                                    return observableOf(
                                        [
                                            renderCamera,
                                            currentNode,
                                            currentTransform,
                                            nts,
                                        ] as [RenderCamera, ImageNode, Transform, [ImageNode, Transform, number][]]);
                                }));
                    }),
                switchMap(
                    ([camera, cn, ct, nts]:
                        [
                            RenderCamera,
                            ImageNode,
                            Transform,
                            [ImageNode, Transform, number][],
                        ]): Observable<ImageNode> => {

                        const direction = camera.camera.lookat.clone().sub(camera.camera.position);

                        const cd = new Spatial().viewingDirection(cn.rotation);
                        const ca = cd.angleTo(direction);
                        const closest: [number, string] = [ca, undefined];
                        const basic = new ViewportCoords().viewportToBasic(0, 0, ct, camera.perspective);

                        if (basic[0] >= 0 && basic[0] <= 1 && basic[1] >= 0 && basic[1] <= 1) {
                            closest[0] = Number.NEGATIVE_INFINITY;
                        }

                        for (const [n] of nts) {
                            const d = new Spatial().viewingDirection(n.rotation);
                            const a = d.angleTo(direction);

                            if (a < closest[0]) {
                                closest[0] = a;
                                closest[1] = n.id;
                            }
                        }

                        if (!closest[1]) {
                            return observableEmpty();
                        }

                        return this._navigator.moveTo$(closest[1]).pipe(
                            catchError(
                                (): Observable<ImageNode> => {
                                    return observableEmpty();
                                }));
                    }))
            .subscribe());
    }