in src/component/spatial/SpatialComponent.ts [147:583]
protected _activate(): void {
this._navigator.cacheService.configure({ cellDepth: 3 });
const subs = this._subscriptions;
subs.push(this._navigator.stateService.reference$
.pipe(
pairwise())
.subscribe(
([prevReference, reference]: [LngLatAlt, LngLatAlt]): void => {
this._scene.resetReference(reference, prevReference);
}
));
subs.push(this._navigator.graphService.filter$
.subscribe(imageFilter => { this._scene.setFilter(imageFilter); }));
const bearing$ = this._container.renderService.bearing$.pipe(
map(
(bearing: number): number => {
const interval = 6;
const discrete = interval * Math.floor(bearing / interval);
return discrete;
}),
distinctUntilChanged(),
publishReplay(1),
refCount());
const cellId$ = this._navigator.stateService.currentImage$
.pipe(
map(
(image: Image): string => {
return this._navigator.api.data.geometry
.lngLatToCellId(image.originalLngLat);
}),
distinctUntilChanged(),
publishReplay(1),
refCount());
const cellGridDepth$ = this._configuration$
.pipe(
map(
(c: SpatialConfiguration): number => {
return this._spatial.clamp(c.cellGridDepth, 1, 3);
}),
distinctUntilChanged(),
publishReplay(1),
refCount());
const sequencePlay$ = observableCombineLatest(
this._navigator.playService.playing$,
this._navigator.playService.speed$).pipe(
map(
([playing, speed]: [boolean, number]): boolean => {
return playing && speed > PlayService.sequenceSpeed;
}),
distinctUntilChanged(),
publishReplay(1),
refCount());
const isOverview$ = this._navigator.stateService.state$.pipe(
map(
(state: State): boolean => {
return isOverviewState(state);
}),
distinctUntilChanged(),
publishReplay(1),
refCount());
subs.push(isOverview$.subscribe(
(isOverview: boolean): void => {
this._scene.setNavigationState(isOverview);
}));
const cell$ = observableCombineLatest(
isOverview$,
sequencePlay$,
bearing$,
cellGridDepth$,
this._navigator.stateService.currentImage$)
.pipe(
distinctUntilChanged((
[o1, s1, b1, d1, i1]: AdjancentParams,
[o2, s2, b2, d2, i2]: AdjancentParams)
: boolean => {
if (o1 !== o2) {
return false;
}
const isd = i1.id === i2.id && s1 === s2 && d1 === d2;
if (o1) {
return isd;
}
return isd && b1 === b2;
}),
concatMap(
([isOverview, sequencePlay, bearing, depth, image]
: AdjancentParams)
: Observable<string[]> => {
if (isOverview) {
const geometry = this._navigator.api.data.geometry;
const cellId = geometry
.lngLatToCellId(image.originalLngLat);
const cells = sequencePlay ?
[cellId] :
connectedComponent(cellId, depth, geometry);
return observableOf(cells);
}
const fov = sequencePlay ? 30 : 90;
return observableOf(
this._cellsInFov(
image,
bearing,
fov));
}),
switchMap(
(cellIds: string[]): Observable<Cell> => {
return observableFrom(cellIds).pipe(
mergeMap(
(cellId: string): Observable<Cell> => {
const t$ = this._cache.hasCell(cellId) ?
observableOf(
this._cache.getCell(cellId)) :
this._cache.cacheCell$(cellId);
return t$.pipe(
map((images: Image[]) => ({ id: cellId, images })));
},
6));
}));
subs.push(cell$.pipe(
withLatestFrom(this._navigator.stateService.reference$))
.subscribe(
([cell, reference]: [Cell, LngLatAlt]): void => {
if (this._scene.hasCell(cell.id)) {
return;
}
this._scene.addCell(
this._cellToTopocentric(cell.id, reference),
cell.id);
}));
subs.push(cell$.pipe(
withLatestFrom(this._navigator.stateService.reference$))
.subscribe(
([cell, reference]: [Cell, LngLatAlt]): void => {
this._addSceneImages(cell, reference);
}));
subs.push(cell$.pipe(
concatMap(
(cell: Cell): Observable<[string, ClusterContract]> => {
const cellId = cell.id;
let reconstructions$: Observable<ClusterContract>;
if (this._cache.hasClusters(cellId)) {
reconstructions$ = observableFrom(this._cache.getClusters(cellId));
} else if (this._cache.isCachingClusters(cellId)) {
reconstructions$ = this._cache.cacheClusters$(cellId).pipe(
last(null, {}),
switchMap(
(): Observable<ClusterContract> => {
return observableFrom(this._cache.getClusters(cellId));
}));
} else if (this._cache.hasCell(cellId)) {
reconstructions$ = this._cache.cacheClusters$(cellId);
} else {
reconstructions$ = observableEmpty();
}
return observableCombineLatest(observableOf(cellId), reconstructions$);
}),
withLatestFrom(this._navigator.stateService.reference$))
.subscribe(
([[cellId, reconstruction], reference]: [[string, ClusterContract], LngLatAlt]): void => {
if (this._scene
.hasCluster(
reconstruction.id,
cellId)) {
return;
}
this._scene.addCluster(
reconstruction,
this._computeTranslation(
reconstruction,
reference),
cellId);
}));
subs.push(this._configuration$.pipe(
map(
(c: SpatialConfiguration): SpatialConfiguration => {
c.cameraSize = this._spatial.clamp(c.cameraSize, 0.01, 1);
c.pointSize = this._spatial.clamp(c.pointSize, 0.01, 1);
const pointVisualizationMode =
c.pointsVisible ?
c.pointVisualizationMode ?? PointVisualizationMode.Original :
PointVisualizationMode.Hidden;
return {
cameraSize: c.cameraSize,
cameraVisualizationMode: c.cameraVisualizationMode,
cellsVisible: c.cellsVisible,
originalPositionMode: c.originalPositionMode,
pointSize: c.pointSize,
pointVisualizationMode,
};
}),
distinctUntilChanged(
(c1: SpatialConfiguration, c2: SpatialConfiguration)
: boolean => {
return c1.cameraSize === c2.cameraSize &&
c1.cameraVisualizationMode === c2.cameraVisualizationMode &&
c1.cellsVisible === c2.cellsVisible &&
c1.originalPositionMode === c2.originalPositionMode &&
c1.pointSize === c2.pointSize &&
c1.pointVisualizationMode === c2.pointVisualizationMode;
}))
.subscribe(
(c: SpatialConfiguration): void => {
this._scene.setCameraSize(c.cameraSize);
const cvm = c.cameraVisualizationMode;
this._scene.setCameraVisualizationMode(cvm);
this._scene.setCellVisibility(c.cellsVisible);
this._scene.setPointSize(c.pointSize);
const pvm = c.pointVisualizationMode;
this._scene.setPointVisualizationMode(pvm);
const opm = c.originalPositionMode;
this._scene.setPositionMode(opm);
}));
subs.push(observableCombineLatest(cellId$, cellGridDepth$)
.subscribe(
([cellId, depth]: [string, number]): void => {
const keepCells = connectedComponent(
cellId,
depth,
this._navigator.api.data.geometry);
this._scene.uncache(keepCells);
this._cache.uncache(keepCells);
}));
subs.push(this._navigator.playService.playing$.pipe(
switchMap(
(playing: boolean): Observable<MouseEvent> => {
return playing ?
observableEmpty() :
this._container.mouseService.dblClick$;
}),
withLatestFrom(this._container.renderService.renderCamera$),
switchMap(
([event, render]: [MouseEvent, RenderCamera]): Observable<Image> => {
const element = this._container.container;
const [canvasX, canvasY] = this._viewportCoords
.canvasPosition(event, element);
const viewport = this._viewportCoords.canvasToViewport(
canvasX,
canvasY,
element);
const id = this._scene.intersection
.intersectObjects(viewport, render.perspective);
return !!id ?
this._navigator.moveTo$(id).pipe(
catchError(
(): Observable<Image> => {
return observableEmpty();
})) :
observableEmpty();
}))
.subscribe());
const intersectChange$ = observableCombineLatest(
this._configuration$,
this._navigator.stateService.state$).pipe(
map(
([c, state]: [SpatialConfiguration, State])
: IntersectConfiguration => {
c.cameraSize = this._spatial.clamp(
c.cameraSize,
0.01,
1);
return {
size: c.cameraSize,
visible: isModeVisible(c.cameraVisualizationMode),
state,
};
}),
distinctUntilChanged(
(c1: IntersectConfiguration, c2: IntersectConfiguration): boolean => {
return c1.size === c2.size &&
c1.visible === c2.visible &&
c1.state === c2.state;
}));
const mouseMove$ = this._container.mouseService.mouseMove$.pipe(
publishReplay(1),
refCount());
subs.push(mouseMove$.subscribe());
const mouseHover$ = observableMerge(
this._container.mouseService.mouseEnter$,
this._container.mouseService.mouseLeave$,
this._container.mouseService.windowBlur$);
subs.push(observableCombineLatest(
this._navigator.playService.playing$,
mouseHover$,
isOverview$,
this._navigator.graphService.filter$)
.pipe(
switchMap(
([playing, mouseHover]:
[boolean, IntersectEvent, boolean, FilterFunction])
: Observable<[IntersectEvent, RenderCamera, IntersectConfiguration]> => {
return !playing && mouseHover.type === "pointerenter" ?
observableCombineLatest(
observableConcat(
mouseMove$.pipe(take(1)),
this._container.mouseService.mouseMove$),
this._container.renderService.renderCamera$,
intersectChange$) :
observableCombineLatest(
observableOf(mouseHover),
observableOf(null),
observableOf(null));
}))
.subscribe(
([event, render]
: [IntersectEvent, RenderCamera, IntersectConfiguration]): void => {
if (event.type !== "pointermove") {
this._scene.setHoveredImage(null);
return;
}
const element = this._container.container;
const [canvasX, canvasY] = this._viewportCoords.canvasPosition(<MouseEvent>event, element);
const viewport = this._viewportCoords.canvasToViewport(
canvasX,
canvasY,
element);
const key = this._scene.intersection
.intersectObjects(viewport, render.perspective);
this._scene.setHoveredImage(key);
}));
subs.push(this._navigator.stateService.currentId$
.subscribe(
(id: string): void => {
this._scene.setSelectedImage(id);
}));
subs.push(this._navigator.stateService.currentState$
.pipe(
map((frame: AnimationFrame): GLRenderHash => {
const scene = this._scene;
return {
name: this._name,
renderer: {
frameId: frame.id,
needsRender: scene.needsRender,
render: scene.render.bind(scene),
pass: RenderPass.Opaque,
},
};
}))
.subscribe(this._container.glRenderer.render$));
const updatedCell$ = this._navigator.graphService.dataAdded$
.pipe(
filter(
(cellId: string) => {
return this._cache.hasCell(cellId);
}),
mergeMap(
(cellId: string): Observable<[Cell, LngLatAlt]> => {
return this._cache.updateCell$(cellId).pipe(
map((images: Image[]) => ({ id: cellId, images })),
withLatestFrom(
this._navigator.stateService.reference$
)
);
}),
publish<[Cell, LngLatAlt]>(),
refCount());
subs.push(updatedCell$
.subscribe(
([cell, reference]: [Cell, LngLatAlt]): void => {
this._addSceneImages(cell, reference);
}));
subs.push(updatedCell$
.pipe(
concatMap(
([cell]: [Cell, LngLatAlt]): Observable<[string, ClusterContract]> => {
const cellId = cell.id;
const cache = this._cache;
let reconstructions$: Observable<ClusterContract>;
if (cache.hasClusters(cellId)) {
reconstructions$ =
cache.updateClusters$(cellId);
} else if (cache.isCachingClusters(cellId)) {
reconstructions$ = this._cache.cacheClusters$(cellId).pipe(
last(null, {}),
switchMap(
(): Observable<ClusterContract> => {
return observableFrom(
cache.updateClusters$(cellId));
}));
} else {
reconstructions$ = observableEmpty();
}
return observableCombineLatest(
observableOf(cellId),
reconstructions$);
}),
withLatestFrom(this._navigator.stateService.reference$))
.subscribe(
([[cellId, reconstruction], reference]: [[string, ClusterContract], LngLatAlt]): void => {
if (this._scene.hasCluster(reconstruction.id, cellId)) {
return;
}
this._scene.addCluster(
reconstruction,
this._computeTranslation(reconstruction, reference),
cellId);
}));
}