Skip to content
This repository has been archived by the owner on Mar 8, 2023. It is now read-only.

Commit

Permalink
TextElementsRenderer supports Font updates (#2008)
Browse files Browse the repository at this point in the history
* HARP-13326: TextElementsRenderer supports update of FontCatalogs

   * FontCatalogLoader only loads FontCatalogs
   * MapView only ever has one instance of TextElementsRenderer
   * MapViewThemeManager awaits Theme updates on DataSources

Signed-off-by: Frauke Fritz <frauke.fritz@here.com>

* HARP-13326: Moves TextElementsRenderer dependencies to TextElementsRenderer

Signed-off-by: Frauke Fritz <frauke.fritz@here.com>

* HARP-13326: Adresses review commentss

Signed-off-by: Frauke Fritz <frauke.fritz@here.com>

* HARP-13326: RFixes default TextCanvas handling to remove warnings

Signed-off-by: Frauke Fritz <frauke.fritz@here.com>

* HARP-13326: Adds more Font Catalog Update Tests

Signed-off-by: Frauke Fritz <frauke.fritz@here.com>

* HARP-13326: Fixes to the Default Style handling in TextElementsRenderer

Signed-off-by: Frauke Fritz <frauke.fritz@here.com>

* HARP-13326: Removes obsolete ? on access of existing Array

Signed-off-by: Frauke Fritz <frauke.fritz@here.com>
  • Loading branch information
FraukeF committed Dec 14, 2020
1 parent ef945ee commit 9c589d4
Show file tree
Hide file tree
Showing 14 changed files with 411 additions and 358 deletions.
110 changes: 15 additions & 95 deletions @here/harp-mapview/lib/MapView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,14 @@ import { MapViewThemeManager } from "./MapViewThemeManager";
import { PickHandler, PickResult } from "./PickHandler";
import { PickingRaycaster } from "./PickingRaycaster";
import { PoiManager } from "./poi/PoiManager";
import { PoiRenderer } from "./poi/PoiRenderer";
import { PoiTableManager } from "./poi/PoiTableManager";
import { PolarTileDataSource } from "./PolarTileDataSource";
import { ScreenCollisions, ScreenCollisionsDebug } from "./ScreenCollisions";
import { ScreenProjector } from "./ScreenProjector";
import { FrameStats, PerformanceStatistics } from "./Statistics";
import { FontCatalogLoader } from "./text/FontCatalogLoader";
import { MapViewState } from "./text/MapViewState";
import { TextCanvasFactory } from "./text/TextCanvasFactory";
import { TextElement } from "./text/TextElement";
import { TextElementsRenderer, ViewUpdateCallback } from "./text/TextElementsRenderer";
import { TextElementsRendererOptions } from "./text/TextElementsRendererOptions";
import { TextStyleCache } from "./text/TextStyleCache";
import { Tile } from "./Tile";
import { TileObjectRenderer } from "./TileObjectsRenderer";
import { MapViewUtils } from "./Utils";
Expand Down Expand Up @@ -457,11 +452,6 @@ export interface MapViewOptions extends TextElementsRendererOptions, Partial<Loo
*/
enablePickTechnique?: boolean;

/**
* An optional canvas element that renders 2D collision debug information.
*/
collisionDebugCanvas?: HTMLCanvasElement;

/**
* Maximum timeout, in milliseconds, before a [[MOVEMENT_FINISHED_EVENT]] is sent after the
* latest frame with a camera movement. The default is 300ms.
Expand Down Expand Up @@ -794,9 +784,6 @@ export class MapView extends EventDispatcher {
private m_postEffects?: PostEffects;

private readonly m_screenProjector: ScreenProjector;
private readonly m_screenCollisions:
| ScreenCollisions
| ScreenCollisionsDebug = new ScreenCollisions();

private m_visibleTiles: VisibleTileSet;
private readonly m_tileObjectRenderer: TileObjectRenderer;
Expand All @@ -816,8 +803,6 @@ export class MapView extends EventDispatcher {
private m_geoMaxBounds?: GeoBox;
private m_worldMaxBounds?: THREE.Box3 | OrientedBox3;

private readonly m_screenCamera = new THREE.OrthographicCamera(-1, 1, 1, -1);

private readonly m_camera: THREE.PerspectiveCamera;

/**
Expand Down Expand Up @@ -906,7 +891,6 @@ export class MapView extends EventDispatcher {

private readonly m_poiManager: PoiManager = new PoiManager(this);

private m_poiRenderer: PoiRenderer;
private readonly m_poiTableManager: PoiTableManager = new PoiTableManager(this);

private readonly m_collisionDebugCanvas: HTMLCanvasElement | undefined;
Expand Down Expand Up @@ -1021,14 +1005,6 @@ export class MapView extends EventDispatcher {
this.m_languages = this.m_options.languages;
this.m_politicalView = this.m_options.politicalView;

if (
this.m_options.collisionDebugCanvas !== undefined &&
this.m_options.collisionDebugCanvas !== null
) {
this.m_collisionDebugCanvas = this.m_options.collisionDebugCanvas;
this.m_screenCollisions = new ScreenCollisionsDebug(this.m_collisionDebugCanvas);
}

this.handleRequestAnimationFrame = this.renderLoop.bind(this);
this.m_pickHandler = new PickHandler(
this,
Expand Down Expand Up @@ -1152,11 +1128,6 @@ export class MapView extends EventDispatcher {

this.m_themeManager = new MapViewThemeManager(this, this.m_uriResolver);

this.m_poiRenderer = new PoiRenderer(this.m_renderer, this.m_poiManager, [
this.imageCache,
this.userImageCache
]);

// will initialize with an empty theme and updated when theme is loaded and set
this.m_textElementsRenderer = this.createTextRenderer();

Expand Down Expand Up @@ -1253,7 +1224,6 @@ export class MapView extends EventDispatcher {

this.m_enableMixedLod = enableMixedLod;
this.m_visibleTiles = this.createVisibleTileSet();
//FIXME: Why is this needed: this.resetTextRenderer(theme);
this.update();
}

Expand Down Expand Up @@ -3267,15 +3237,9 @@ export class MapView extends EventDispatcher {
this.m_rteCamera.position.setScalar(0);
this.m_rteCamera.updateMatrixWorld(true);

this.m_screenCamera.left = width / -2;
this.m_screenCamera.right = width / 2;
this.m_screenCamera.bottom = height / -2;
this.m_screenCamera.top = height / 2;
this.m_screenCamera.updateProjectionMatrix();
this.m_screenCamera.updateMatrixWorld(false);
this.m_textElementsRenderer?.updateCamera();

this.m_screenProjector.update(this.camera, width, height);
this.m_screenCollisions.update(width, height);

this.m_pixelToWorld = undefined;
this.m_sceneEnvironment.update();
Expand Down Expand Up @@ -3534,7 +3498,6 @@ export class MapView extends EventDispatcher {
this.m_visibleTiles.allVisibleTilesLoaded &&
this.m_connectedDataSources.size + this.m_failedDataSources.size ===
this.m_tileDataSources.length &&
!this.m_textElementsRenderer.initializing &&
!this.m_textElementsRenderer.loading
) {
this.m_initialTextPlacementDone = true;
Expand Down Expand Up @@ -3584,8 +3547,11 @@ export class MapView extends EventDispatcher {
// The camera used to render the scene.
const camera = this.m_pointOfView !== undefined ? this.m_pointOfView : this.m_rteCamera;

if (this.renderLabels) {
this.prepareRenderTextElements(frameStartTime);
if (this.renderLabels && !this.m_pointOfView) {
this.m_textElementsRenderer.placeText(
this.m_visibleTiles.dataSourceTileList,
frameStartTime
);
}

if (gatherStatistics) {
Expand All @@ -3603,8 +3569,8 @@ export class MapView extends EventDispatcher {
drawTime = PerformanceTimer.now();
}

if (this.renderLabels) {
this.finishRenderTextElements();
if (this.renderLabels && !this.m_pointOfView) {
this.m_textElementsRenderer.renderText(this.m_viewRanges.maximum);
}

if (this.m_overlaySceneRoot.children.length > 0) {
Expand Down Expand Up @@ -3697,29 +3663,6 @@ export class MapView extends EventDispatcher {
}
}

private prepareRenderTextElements(time: number) {
// Disable rendering of text elements for debug camera. TextElements are rendered using an
// orthographic camera that covers the entire available screen space. Unfortunately, this
// particular camera set up is not compatible with the debug camera.
const debugCameraActive = this.m_pointOfView !== undefined;

if (debugCameraActive) {
return;
}

this.m_textElementsRenderer.placeText(this.m_visibleTiles.dataSourceTileList, time);
}

private finishRenderTextElements() {
const canRenderTextElements = this.m_pointOfView === undefined;

if (canRenderTextElements) {
// copy far value from scene camera, as the distance to the POIs matter now.
this.m_screenCamera.far = this.m_viewRanges.maximum;
this.m_textElementsRenderer.renderText(this.m_screenCamera);
}
}

private setupCamera() {
const { width, height } = this.getCanvasClientSize();

Expand All @@ -3744,9 +3687,6 @@ export class MapView extends EventDispatcher {

// ### move & customize
this.resize(width, height);

this.m_screenCamera.position.z = 1;
this.m_screenCamera.near = 0;
}

private createVisibleTileSet(): VisibleTileSet {
Expand Down Expand Up @@ -3886,26 +3826,18 @@ export class MapView extends EventDispatcher {
tileObjectRenderer.setupRenderer();
}

private createTextRenderer(
fontCatalogs?: FontCatalogConfig[],
textStyles?: TextStyleDefinition[],
defaultTextStyle?: TextStyleDefinition
): TextElementsRenderer {
private createTextRenderer(): TextElementsRenderer {
const updateCallback: ViewUpdateCallback = () => {
this.update();
};

return new TextElementsRenderer(
new MapViewState(this, this.checkIfTilesChanged.bind(this)),
this.m_camera,
updateCallback,
this.m_screenCollisions,
this.m_screenProjector,
new TextCanvasFactory(this.m_renderer),
this.m_poiManager,
this.m_poiRenderer,
new FontCatalogLoader(fontCatalogs),
new TextStyleCache(textStyles, defaultTextStyle),
this.m_renderer,
[this.imageCache, this.userImageCache],
this.m_options
);
}
Expand All @@ -3921,18 +3853,9 @@ export class MapView extends EventDispatcher {
textStyles?: TextStyleDefinition[],
defaultTextStyle?: TextStyleDefinition
): Promise<void> {
const overlayText = this.m_textElementsRenderer.overlayText;
this.m_poiRenderer.reset();
this.m_textElementsRenderer = this.createTextRenderer(
fontCatalogs,
textStyles,
defaultTextStyle
);
if (overlayText !== undefined) {
this.m_textElementsRenderer.addOverlayText(overlayText);
}
await this.m_textElementsRenderer.waitInitialized();
await this.m_textElementsRenderer.waitLoaded();
await this.m_textElementsRenderer.updateFontCatalogs(fontCatalogs);
await this.m_textElementsRenderer.updateTextStyles(textStyles, defaultTextStyle);
this.update();
}

/**
Expand All @@ -3953,10 +3876,7 @@ export class MapView extends EventDispatcher {
private readonly onWebGLContextRestored = (event: Event) => {
this.dispatchEvent(this.CONTEXT_RESTORED_EVENT);
if (this.m_renderer !== undefined) {
this.m_poiRenderer = new PoiRenderer(this.m_renderer, this.m_poiManager, [
this.imageCache,
this.userImageCache
]);
this.textElementsRenderer.restoreRenderers(this.m_renderer);
this.getTheme().then(theme => {
this.m_sceneEnvironment.updateClearColor(theme.clearColor, theme.clearAlpha);
this.update();
Expand Down
3 changes: 1 addition & 2 deletions @here/harp-mapview/lib/MapViewThemeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,10 +137,9 @@ export class MapViewThemeManager {
this.m_theme.styles = theme.styles ?? {};
this.m_theme.definitions = theme.definitions;

// TODO: this is asynchronouse too
environment.clearBackgroundDataSource();
for (const dataSource of this.m_mapView.dataSources) {
dataSource.setTheme(this.m_theme);
await dataSource.setTheme(this.m_theme);
}
}

Expand Down
72 changes: 13 additions & 59 deletions @here/harp-mapview/lib/text/FontCatalogLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,69 +5,23 @@
*/
import { FontCatalogConfig } from "@here/harp-datasource-protocol";
import { FontCatalog } from "@here/harp-text-canvas";
import { assert, LoggerManager } from "@here/harp-utils";

export const DEFAULT_FONT_CATALOG_NAME = "default";
import { LoggerManager } from "@here/harp-utils";

const logger = LoggerManager.instance.create("FontCatalogLoader");

type FontCatalogCallback = (name: string, catalog: FontCatalog) => void;

export class FontCatalogLoader {
private m_catalogsLoading: number = 0;

constructor(private m_fontCatalogs?: FontCatalogConfig[]) {}

/**
* Initializes font catalog loader.
* @param defaultFontCatalogUrl - Url of the font catalog that will be used by default if the
* theme doesn't define any font catalog.
* @returns Name of the default font catalog.
*/
initialize(defaultFontCatalogUrl: string): string {
if (this.m_fontCatalogs === undefined || this.m_fontCatalogs.length === 0) {
this.m_fontCatalogs = [
{
name: DEFAULT_FONT_CATALOG_NAME,
url: defaultFontCatalogUrl
}
];
return DEFAULT_FONT_CATALOG_NAME;
}

const defaultFontCatalogName = this.m_fontCatalogs[0].name;
return defaultFontCatalogName;
}

async loadCatalogs(
catalogCallback: FontCatalogCallback,
failureCallback?: (name: string, error: Error) => void
): Promise<void[]> {
assert(this.m_fontCatalogs !== undefined);
assert(this.m_fontCatalogs!.length > 0);

const promises: Array<Promise<void>> = [];

this.m_fontCatalogs!.forEach(fontCatalogConfig => {
this.m_catalogsLoading += 1;
const fontCatalogPromise: Promise<void> = FontCatalog.load(fontCatalogConfig.url, 1024)
.then<void>(catalogCallback.bind(undefined, fontCatalogConfig.name))
.catch((error: Error) => {
logger.error("Failed to load FontCatalog: ", error);
if (failureCallback) {
failureCallback(fontCatalogConfig.name, error);
}
})
.finally(() => {
this.m_catalogsLoading -= 1;
});
promises.push(fontCatalogPromise);
export async function loadFontCatalog(
fontCatalogConfig: FontCatalogConfig,
onSuccess: FontCatalogCallback,
onError?: (error: Error) => void
): Promise<void> {
return await FontCatalog.load(fontCatalogConfig.url, 1024)
.then<void>(onSuccess.bind(undefined, fontCatalogConfig.name))
.catch((error: Error) => {
logger.error("Failed to load FontCatalog: ", fontCatalogConfig.name, error);
if (onError) {
onError(error);
}
});

return Promise.all(promises);
}

get loading(): boolean {
return this.m_catalogsLoading > 0;
}
}
2 changes: 0 additions & 2 deletions @here/harp-mapview/lib/text/Placement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ const tmpPlacementBounds = new THREE.Box2();
* @param textElement - The Text element to check.
* @param poiIndex - If TextElement is a line marker, the index into the line marker positions
* @param viewState - The view for which the text element will be placed.
* @param viewCamera - The view's camera.
* @param m_poiManager - To prepare pois for rendering.
* @param maxViewDistance - If specified, text elements farther than this max distance will be
* rejected.
Expand All @@ -195,7 +194,6 @@ export function checkReadyForPlacement(
textElement: TextElement,
poiIndex: number | undefined,
viewState: ViewState,
viewCamera: THREE.Camera,
poiManager: PoiManager,
maxViewDistance?: number
): { result: PrePlacementResult; viewDistance: number | undefined } {
Expand Down
Loading

0 comments on commit 9c589d4

Please sign in to comment.