showcases/ascii/ascii-layer/ascii-layer.js (98 lines of code) (raw):
import {CompositeLayer, IconLayer, COORDINATE_SYSTEM} from 'deck.gl';
import {makeFontAtlas} from '@deck.gl/layers/dist/esm/text-layer/font-atlas';
import AsciiFilter from './ascii-filter';
const LETTER_HEIGHT = 32;
const PADDING = -4;
const defaultProps = {
video: null,
fontFamily: '"Lucida Console", Monaco, monospace',
timestamp: 0,
sizeScale: 1
};
export default class AsciiLayer extends CompositeLayer {
updateState({props, oldProps}) {
if (props.fontFamily !== oldProps.fontFamily) {
this._createFontTexture(props.fontFamily);
}
if (!props.video || !props.video.readyState) {
return;
}
const {viewport} = this.context;
const {dimension} = this._updateDimension({
sizeScale: props.sizeScale,
width: viewport.width,
height: viewport.height
});
// Update texture
const attributeBuffers = this.state.filter.getBuffers({
width: dimension[0],
height: dimension[1],
video: props.video
});
this.setState({attributeBuffers});
}
_createFontTexture(fontFamily) {
const {gl} = this.context;
const {mapping: iconMapping, texture: iconAtlas} = makeFontAtlas(gl, {fontFamily});
let maxAspectRatio = 0;
for (const char in iconMapping) {
const {width, height} = iconMapping[char];
const aspectRatio = width / height;
maxAspectRatio = aspectRatio > maxAspectRatio ? aspectRatio : maxAspectRatio;
}
this.setState({
iconAtlas,
iconMapping,
letterAspectRatio: maxAspectRatio,
filter: new AsciiFilter(gl, {iconMapping, iconAtlas})
});
}
_updateDimension({width, height, sizeScale}) {
if (
width === this.state.width &&
height === this.state.height &&
sizeScale === this.state.sizeScale
) {
return this.state;
}
const {letterAspectRatio} = this.state;
const vSpacing = sizeScale * (LETTER_HEIGHT + PADDING);
const hSpacing = vSpacing * letterAspectRatio;
const xCount = Math.ceil(width / hSpacing);
const yCount = Math.ceil(height / vSpacing);
const grid = [];
for (let y = 0; y < yCount; y++) {
for (let x = 0; x < xCount; x++) {
grid.push({
x: (x + 0.5) * hSpacing - width / 2,
y: (y + 0.5) * vSpacing - height / 2,
u: x / xCount,
v: y / yCount
});
}
}
const newState = {
grid,
dimension: [xCount, yCount],
width,
height,
sizeScale
};
this.setState(newState);
return newState;
}
renderLayers() {
if (!this.props.video || !this.props.video.readyState) {
return null;
}
const {grid, iconAtlas, iconMapping, attributeBuffers} = this.state;
const {sizeScale, timestamp} = this.props;
return new IconLayer({
id: 'text',
coordinateSystem: COORDINATE_SYSTEM.IDENTITY,
data: grid,
opacity: 1,
iconAtlas,
iconMapping,
sizeScale: LETTER_HEIGHT * sizeScale,
getIcon: d => ' ',
// getColor: d => d.color,
getPosition: d => [d.x, d.y],
...attributeBuffers,
updateTriggers: {
getIcon: timestamp,
getColor: timestamp
}
});
}
}
AsciiLayer.layerName = 'AsciiLayer';
AsciiLayer.defaultProps = defaultProps;