UNPKG

mapillary-js

Version:

WebGL JavaScript library for displaying street level imagery from mapillary.com

441 lines (323 loc) 17.9 kB
import {empty as observableEmpty, BehaviorSubject, Subscription, Subject} from "rxjs"; import {map} from "rxjs/operators"; import * as THREE from "three"; import { GLRenderer, GLRenderStage, RenderMode, RenderCamera, IGLRender, IGLRenderFunction, IGLRenderHash, RenderService, ISize, } from "../../src/Render"; import {IFrame} from "../../src/State"; import { FrameHelper } from "../helper/FrameHelper.spec"; class RendererMock implements THREE.Renderer { public domElement: HTMLCanvasElement = document.createElement("canvas"); public render(s: THREE.Scene, c: THREE.Camera): void { return; } public setSize(w: number, h: number, updateStyle?: boolean): void { return; } public setClearColor(color: THREE.Color | string | number, alpha?: number): void { return; } public setPixelRatio(ratio: number): void { return; } public clear(): void { return; } public clearDepth(): void { return; } } class RenderServiceMock extends RenderService { private _sizeMock$: Subject<ISize> = new Subject<ISize>(); private _renderModeMock$: Subject<RenderMode> = new Subject<RenderMode>(); private _renderCameraFrameMock$: Subject<RenderCamera> = new Subject<RenderCamera>(); constructor(element: HTMLElement) { super(element, observableEmpty(), RenderMode.Letterbox); } public get size$(): Subject<ISize> { return this._sizeMock$; } public set size$(value: Subject<ISize>) { this._sizeMock$ = value; } public get renderMode$(): Subject<RenderMode> { return this._renderModeMock$; } public set renderMode$(value: Subject<RenderMode>) { this._renderModeMock$ = value; } public get renderCameraFrame$(): Subject<RenderCamera> { return this._renderCameraFrameMock$; } public set renderCameraFrame$(value: Subject<RenderCamera>) { this._renderCameraFrameMock$ = value; } } describe("GLRenderer.ctor", () => { it("should be contructed", () => { spyOn(THREE, "WebGLRenderer"); let element: HTMLDivElement = document.createElement("div"); let canvasContainer: HTMLElement = document.createElement("div"); let renderService: RenderService = new RenderServiceMock(element); let glRenderer: GLRenderer = new GLRenderer(canvasContainer, renderService); expect(glRenderer).toBeDefined(); }); it("should not instantiate a WebGL context", () => { spyOn(THREE, "WebGLRenderer"); let element: HTMLDivElement = document.createElement("div"); let canvasContainer: HTMLElement = document.createElement("div"); let renderService: RenderService = new RenderServiceMock(element); let glRenderer: GLRenderer = new GLRenderer(canvasContainer, renderService); expect(glRenderer).toBeDefined(); expect(THREE.WebGLRenderer).not.toHaveBeenCalled(); }); }); describe("GLRenderer.renderer", () => { let createGLRenderHash: (frameId: number, needsRender: boolean, name?: string) => IGLRenderHash = (frameId: number, needsRender: boolean, name?: string): IGLRenderHash => { let renderFunction: IGLRenderFunction = (pc: THREE.PerspectiveCamera, r: THREE.WebGLRenderer): void => { r.render(new THREE.Scene(), pc); }; let render: IGLRender = { frameId: frameId, needsRender: needsRender, render: renderFunction, stage: GLRenderStage.Background, }; let renderHash: IGLRenderHash = { name: name != null ? name : "mock", render: render, }; return renderHash; }; it("should be created on first render", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); let renderHash: IGLRenderHash = createGLRenderHash(0, true); glRenderer.render$.next(renderHash); expect(THREE.WebGLRenderer).toHaveBeenCalled(); }); it("should render on new hash", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(rendererMock, "render"); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); let renderHash: IGLRenderHash = createGLRenderHash(0, true); glRenderer.render$.next(renderHash); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 0; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(1); }); it("should only render once for the same frame id", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(rendererMock, "render"); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); let renderHash: IGLRenderHash = createGLRenderHash(0, true); glRenderer.render$.next(renderHash); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 0; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); glRenderer.render$.next(renderHash); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(1); }); it("should render twice for two frame ids", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(rendererMock, "render"); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 1; let frame$: BehaviorSubject<IFrame> = new BehaviorSubject<IFrame>(frame); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$ = new BehaviorSubject<RenderCamera>(renderCamera); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); frame$.pipe( map( (f: IFrame): IGLRenderHash => { let renderHash: IGLRenderHash = createGLRenderHash(f.id, true); return renderHash; })) .subscribe(glRenderer.render$); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(1); frame.id = 2; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); frame$.next(frame); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(2); }); it("should clear when hash is cleared", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(rendererMock, "clear"); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 1; let frame$: BehaviorSubject<IFrame> = new BehaviorSubject<IFrame>(frame); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$ = new BehaviorSubject<RenderCamera>(renderCamera); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); let frameSubscription: Subscription = frame$.pipe( map( (f: IFrame): IGLRenderHash => { let renderHash: IGLRenderHash = createGLRenderHash(f.id, true); return renderHash; })) .subscribe(glRenderer.render$); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(1); frameSubscription.unsubscribe(); glRenderer.clear("mock"); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(3); }); it("should not clear or render on frames when no renders registered", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(rendererMock, "clear"); spyOn(rendererMock, "render"); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 1; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$ = new BehaviorSubject<RenderCamera>(renderCamera); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); expect(glRenderer).toBeDefined(); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(0); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(0); frame.id = 2; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(0); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(0); }); it("should not render frame if not needed", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(rendererMock, "render"); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 1; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$ = new BehaviorSubject<RenderCamera>(renderCamera); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); glRenderer.render$.next(createGLRenderHash(frame.id, true)); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(1); frame.id = 2; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); glRenderer.render$.next(createGLRenderHash(frame.id, false)); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(1); }); it("should render frame if camera has changed", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(rendererMock, "render"); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 1; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$ = new BehaviorSubject<RenderCamera>(renderCamera); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); glRenderer.render$.next(createGLRenderHash(frame.id, true)); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(1); frame.id = 2; renderCamera.setRenderMode(RenderMode.Letterbox); renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); glRenderer.render$.next(createGLRenderHash(frame.id, false)); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(2); }); it("should render on resize", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(rendererMock, "render"); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 1; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$ = new BehaviorSubject<RenderCamera>(renderCamera); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); glRenderer.render$.next(createGLRenderHash(frame.id, true)); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(1); renderServiceMock.size$.next({ height: 1, width: 1}); frame.id = 2; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); glRenderer.render$.next(createGLRenderHash(frame.id, false)); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(2); }); it("should not render a frame until all render hashes has submitted", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let hash1: string = "hash1"; let hash2: string = "hash2"; let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 1; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$ = new BehaviorSubject<RenderCamera>(renderCamera); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); let renderHash1: IGLRenderHash = createGLRenderHash(frame.id, true, hash1); let renderHash2: IGLRenderHash = createGLRenderHash(frame.id, true, hash2); glRenderer.render$.next(renderHash1); glRenderer.render$.next(renderHash2); spyOn(rendererMock, "clear"); spyOn(rendererMock, "render"); frame.id = 2; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(0); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(0); renderHash1 = createGLRenderHash(frame.id, true, hash1); renderHash2 = createGLRenderHash(frame.id, true, hash2); glRenderer.render$.next(renderHash1); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(0); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(0); glRenderer.render$.next(renderHash2); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(1); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(2); }); it("should render when one of multiple render hashes is cleared", () => { let rendererMock: RendererMock = new RendererMock(); spyOn(THREE, "WebGLRenderer").and.returnValue(<THREE.WebGLRenderer>rendererMock); let hash1: string = "hash1"; let hash2: string = "hash2"; let renderServiceMock: RenderServiceMock = new RenderServiceMock(document.createElement("div")); let renderCamera: RenderCamera = new RenderCamera(1, 1, RenderMode.Letterbox); const frame: IFrame = new FrameHelper().createFrame(); frame.id = 1; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$ = new BehaviorSubject<RenderCamera>(renderCamera); let glRenderer: GLRenderer = new GLRenderer(document.createElement("div"), renderServiceMock); let renderHash1: IGLRenderHash = createGLRenderHash(frame.id, true, hash1); let renderHash2: IGLRenderHash = createGLRenderHash(frame.id, true, hash2); glRenderer.render$.next(renderHash1); glRenderer.render$.next(renderHash2); spyOn(rendererMock, "clear"); spyOn(rendererMock, "render"); frame.id = 2; renderCamera.setFrame(frame); renderServiceMock.renderCameraFrame$.next(renderCamera); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(0); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(0); renderHash1 = createGLRenderHash(frame.id, false, hash1); glRenderer.render$.next(renderHash1); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(0); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(0); glRenderer.clear(hash2); expect((<jasmine.Spy>rendererMock.clear).calls.count()).toBe(1); expect((<jasmine.Spy>rendererMock.render).calls.count()).toBe(1); }); });