Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 56 additions & 3 deletions example/three/annotationsExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,15 @@ const KIND_TO_ICON = {

};

// localized name variants exposed by the protomaps v4 basemap
const LANGUAGES = [ 'default', 'en', 'ja', 'ko' ];

const params = {

language: 'default',
displayIcons: true,
displayPaths: true,

occupancyGrid: false,
annotationLines: false,
tileHierarchy: false,
Expand All @@ -128,6 +135,10 @@ class ExampleAnnotationsDriver extends MVTAnnotationsDriver {

super();

this.language = 'default';
this.displayIcons = true;
this.displayPaths = true;

const dpr = renderer.getPixelRatio();

// icons for point annotations
Expand Down Expand Up @@ -190,6 +201,27 @@ class ExampleAnnotationsDriver extends MVTAnnotationsDriver {

}

isAnnotationEnabled( layer, properties, type ) {

if ( type === 1 && this.displayIcons ) return true;
if ( type === 2 && this.displayPaths ) return true;
return false;

}

getText( properties ) {

const { language } = this;
if ( language !== 'default' ) {

return properties[ `name:${ language }` ] ?? properties.name ?? '';

}

return properties.name ?? '';

}

measureChar( char ) {

return this.characterPoints.measureChar( char );
Expand Down Expand Up @@ -264,6 +296,7 @@ function reinstantiateTiles() {
tiles.registerPlugin( new TilesFadePlugin() );
tiles.registerPlugin( new MeshBVHPlugin() );
driver = new ExampleAnnotationsDriver();
driver.language = params.language;
tiles.registerPlugin( new MVTAnnotationsPlugin( {
overlay,
camera,
Expand Down Expand Up @@ -331,17 +364,37 @@ function init() {

// GUI
const gui = new GUI();
gui.add( params, 'occupancyGrid' ).onChange( v => {
gui.add( params, 'language', LANGUAGES ).onChange( v => {

driver.language = v;
tiles.getPluginByName( 'UPDATE_ON_CHANGE_PLUGIN' ).needsUpdate = true;

} );
gui.add( params, 'displayIcons' ).onChange( v => {

driver.displayIcons = v;
tiles.getPluginByName( 'UPDATE_ON_CHANGE_PLUGIN' ).needsUpdate = true;

} );
gui.add( params, 'displayPaths' ).onChange( v => {

driver.displayPaths = v;
tiles.getPluginByName( 'UPDATE_ON_CHANGE_PLUGIN' ).needsUpdate = true;

} );

const debugFolder = gui.addFolder( 'Debug' );
debugFolder.add( params, 'occupancyGrid' ).onChange( v => {

tiles.getPluginByName( 'MVT_ANNOTATIONS_PLUGIN' ).debug.occupancy.enabled = v;

} );
gui.add( params, 'annotationLines' ).onChange( v => {
debugFolder.add( params, 'annotationLines' ).onChange( v => {

tiles.getPluginByName( 'MVT_ANNOTATIONS_PLUGIN' ).debug.paths.enabled = v;

} );
gui.add( params, 'tileHierarchy' ).onChange( v => {
debugFolder.add( params, 'tileHierarchy' ).onChange( v => {

tiles.getPluginByName( 'MVT_ANNOTATIONS_PLUGIN' ).debug.hierarchy.enabled = v;

Expand Down
111 changes: 50 additions & 61 deletions example/three/src/plugins/mvt/CharacterPoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ export class CharacterPoints extends GlyphPoints {
const fontSize = Math.round( glyphSize * 0.7 );
this._font = font ?? `400 ${ fontSize }px sans-serif`;

// canvas context for measuring advance widths, normalized to em units ( width / fontSize )
// advance-width cache, keyed per character
this._advanceCache = new Map();

// styling
this._strokeStyle = strokeStyle;
this._strokeWidth = strokeWidth;

// the refs and unused list of the character glyphs
this._refs = {};
this._unused = new Set();
// characters currently rasterized in the atlas, plus a reused scratch set of the characters
// needed this frame. the atlas is reconciled against what's actually rendered ( not against
// add / remove events ), so it stays correct even when an annotation's text changes.
this._drawn = new Set();
this._needed = new Set();

this.glyphAtlas.resize( slotCount, glyphSize );

Expand All @@ -56,89 +58,76 @@ export class CharacterPoints extends GlyphPoints {

}

update( added, removed ) {
// rasterize a character into the atlas, evicting a glyph that isn't needed this frame ( or
// growing the atlas if every rasterized glyph is still in use )
_drawChar( char, needed ) {

// TODO: if the "text" suddenly changes (eg from a language change) this will break
// since we will never get a "removed" event associated with the original text string.
// We may have to track the characters that have been used in a geometry update, instead.
super.update( added, removed );
const { _refs, _unused, _added, _removed, glyphAtlas } = this;
const { glyphAtlas, _drawn } = this;
if ( glyphAtlas.capacity === glyphAtlas.count ) {

// iterate over all the added annotations and increment the ref, drawing the
// glyph to the atlas if needed.
_added.forEach( ( { text } ) => {
let toRemove = null;
for ( const c of _drawn ) {

for ( let i = 0, l = text.length; i < l; i ++ ) {
if ( ! needed.has( c ) ) {

const char = text[ i ];
if ( ! ( char in _refs ) ) {
toRemove = c;
break;

// if we've reached our capacity
if ( glyphAtlas.capacity === glyphAtlas.count ) {

if ( _unused.size > 0 ) {

// if there are unused slots then free up one of those
const unusedChar = _unused.values().next().value;
glyphAtlas.release( unusedChar );
delete _refs[ unusedChar ];
_unused.delete( unusedChar );

} else {

// otherwise resize the atlas to fit more glyphs
glyphAtlas.resize( glyphAtlas.capacity * 2 );
}

}
}

}
if ( toRemove !== null ) {

// draw the glyph
_refs[ char ] = 0;
glyphAtlas.drawChar( char, char, {
font: this._font,
color: 'white',
strokeStyle: this._strokeStyle,
strokeWidth: this._strokeWidth,
} );
glyphAtlas.release( toRemove );
_drawn.delete( toRemove );

}
} else {

_unused.delete( char );
_refs[ char ] ++;
glyphAtlas.resize( glyphAtlas.capacity * 2 );

}

}

glyphAtlas.drawChar( char, char, {
font: this._font,
color: 'white',
strokeStyle: this._strokeStyle,
strokeWidth: this._strokeWidth,
} );
_drawn.add( char );

// decrement the refs and queue up any characters as unused if they reach 0
_removed.forEach( ( { text } ) => {
}

for ( let i = 0, l = text.length; i < l; i ++ ) {
_updateGeometry() {

const char = text[ i ];
_refs[ char ] --;
if ( _refs[ char ] === 0 ) {
const { _orderedEntries, _needed, geometry, position, glyphAtlas } = this;

_unused.add( char );
// collect the characters needed this frame from each items text, and the total glyph
// count
_needed.clear();
let count = 0;
for ( const entry of _orderedEntries ) {

}
const { text, characterPositions } = entry.item;
count += characterPositions.length;
for ( let c = 0, l = text.length; c < l; c ++ ) {

}
_needed.add( text[ c ] );

} );
}

}
}

_updateGeometry() {
// make sure every needed character is rasterized before we look up its uv
for ( const char of _needed ) {

const { _orderedEntries, geometry, position, glyphAtlas } = this;
if ( ! glyphAtlas.has( char ) ) {

// total character count across all entries ( including fading ones )
let count = 0;
for ( const entry of _orderedEntries ) {
this._drawChar( char, _needed );

count += entry.item.characterPositions.length;
}

}

Expand Down
14 changes: 4 additions & 10 deletions example/three/src/plugins/mvt/GlyphPoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ export class GlyphPoints extends Points {
this._entryMap = new Map();
this._orderedEntries = [];
this._lastUpdateTime = - 1;
this._added = [];
this._removed = [];

}

Expand All @@ -58,9 +56,7 @@ export class GlyphPoints extends Points {
const dt = this._lastUpdateTime < 0 ? 0 : Math.min( now - this._lastUpdateTime, 0.1 );
this._lastUpdateTime = now;

const { _entryMap, _orderedEntries, _added, _removed, fadeInDuration, fadeOutDuration } = this;
_added.length = 0;
_removed.length = 0;
const { _entryMap, _orderedEntries, fadeInDuration, fadeOutDuration } = this;

for ( const item of added ) {

Expand All @@ -70,7 +66,6 @@ export class GlyphPoints extends Points {
const entry = { item, fade: 0, state: 'in' };
_entryMap.set( item.id, entry );
_orderedEntries.push( entry );
_added.push( item );

} else {

Expand All @@ -93,6 +88,7 @@ export class GlyphPoints extends Points {

}

let didRemove = false;
for ( const [ id, entry ] of _entryMap ) {

if ( entry.state === 'in' ) {
Expand All @@ -110,24 +106,22 @@ export class GlyphPoints extends Points {
if ( entry.fade <= 0 ) {

_entryMap.delete( id );
_removed.push( entry.item );
didRemove = true;

}

}

}

if ( _removed.length > 0 ) {
if ( didRemove ) {

this._orderedEntries = _orderedEntries.filter( e => _entryMap.has( e.item.id ) );

}

this._updateGeometry();

return _removed;

}

onAfterRender( renderer, scene, camera ) {
Expand Down
6 changes: 5 additions & 1 deletion src/three/plugins/images/sources/PMTilesImageSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ class PMTilesContentCache extends MVTContentCache {
getKey: () => this.url,
getBytes: async ( offset, length, signal ) => {

signal.aborted.throwIfAborted();
if ( signal ) {

signal.throwIfAborted();

}

const { fetchOptions, url } = this;
const res = await this.fetchData( url, {
Expand Down
Loading
Loading