Keep the camera and view in a sensible state when changing fonts in the text demo

This commit is contained in:
Patrick Walton 2017-09-23 13:09:45 -07:00
parent db32009d7b
commit 2197896c4f
7 changed files with 70 additions and 65 deletions

View File

@ -127,14 +127,14 @@ class ThreeDController extends DemoAppController<ThreeDView> {
return sides.map(side => ({ lines: side.upper.lines.concat(side.lower.lines) }));
}
protected fileLoaded(): void {
const font = opentype.parse(this.fileData);
protected fileLoaded(fileData: ArrayBuffer): void {
const font = opentype.parse(fileData);
assert(font.isSupported(), "The font type is unsupported!");
this.monumentPromise.then(monument => this.layoutMonument(font, monument));
this.monumentPromise.then(monument => this.layoutMonument(font, fileData, monument));
}
private layoutMonument(font: opentype.Font, monument: MonumentSide[]) {
private layoutMonument(font: opentype.Font, fileData: ArrayBuffer, monument: MonumentSide[]) {
const createGlyph = (glyph: opentype.Glyph) => new ThreeDGlyph(glyph);
let textFrames = [];
for (const monumentSide of monument) {
@ -163,7 +163,7 @@ class ThreeDController extends DemoAppController<ThreeDView> {
textFrames.push(new TextFrame(textRuns, font));
}
this.glyphStorage = new TextFrameGlyphStorage(this.fileData, textFrames, font);
this.glyphStorage = new TextFrameGlyphStorage(fileData, textFrames, font);
this.glyphStorage.layoutRuns();
this.glyphStorage.partition().then((baseMeshes: PathfinderMeshData) => {

View File

@ -33,19 +33,14 @@ export abstract class AppController {
protected fetchFile(file: string, builtinFileURI: string) {
window.fetch(`${builtinFileURI}/${file}`)
.then(response => response.arrayBuffer())
.then(data => {
this.fileData = data;
this.fileLoaded();
});
.then(data => this.fileLoaded(data));
}
protected canvas: HTMLCanvasElement;
protected screenshotButton: HTMLButtonElement | null;
protected fileData: ArrayBuffer;
protected abstract fileLoaded(): void;
protected abstract fileLoaded(data: ArrayBuffer): void;
protected abstract get defaultFile(): string;
}
@ -108,10 +103,7 @@ export abstract class DemoAppController<View extends PathfinderDemoView> extends
this.filePickerView = FilePickerView.create();
if (this.filePickerView != null) {
this.filePickerView.onFileLoaded = fileData => {
this.fileData = fileData;
this.fileLoaded();
};
this.filePickerView.onFileLoaded = fileData => this.fileLoaded(fileData);
}
const selectFileElement = document.getElementById('pf-select-file') as

View File

@ -59,8 +59,8 @@ class BenchmarkAppController extends DemoAppController<BenchmarkTestView> {
this.loadInitialFile(this.builtinFileURI);
}
protected fileLoaded(): void {
const font = opentype.parse(this.fileData);
protected fileLoaded(fileData: ArrayBuffer): void {
const font = opentype.parse(fileData);
this.font = font;
assert(this.font.isSupported(), "The font type is unsupported!");
@ -68,7 +68,7 @@ class BenchmarkAppController extends DemoAppController<BenchmarkTestView> {
const textRun = new TextRun<BenchmarkGlyph>(STRING, [0, 0], font, createGlyph);
this.textRun = textRun;
const textFrame = new TextFrame([textRun], font);
this.glyphStorage = new TextFrameGlyphStorage(this.fileData, [textFrame], font);
this.glyphStorage = new TextFrameGlyphStorage(fileData, [textFrame], font);
this.glyphStorage.partition().then(baseMeshes => {
this.baseMeshes = baseMeshes;

View File

@ -150,7 +150,10 @@ export class OrthographicCamera extends Camera {
if (this.ignoreBounds)
return;
const bounds = this.bounds;
const bounds = glmatrix.vec4.clone(this.bounds);
if (!this.scaleBounds)
glmatrix.vec4.scale(bounds, bounds, this.scale);
for (let axis = 0; axis < 2; axis++) {
const viewportLength = axis === 0 ? this.canvas.width : this.canvas.height;
const axisBounds = [bounds[axis + 0], bounds[axis + 2]];
@ -170,8 +173,8 @@ export class OrthographicCamera extends Camera {
}
zoomToFit(): void {
const upperLeft = glmatrix.vec2.fromValues(this._bounds[0], this._bounds[1]);
const lowerRight = glmatrix.vec2.fromValues(this._bounds[2], this._bounds[3]);
const upperLeft = glmatrix.vec2.clone([this._bounds[0], this._bounds[1]]);
const lowerRight = glmatrix.vec2.clone([this._bounds[2], this._bounds[3]]);
const width = this._bounds[2] - this._bounds[0];
const height = Math.abs(this._bounds[1] - this._bounds[3]);
@ -184,11 +187,6 @@ export class OrthographicCamera extends Camera {
glmatrix.vec2.scale(this.translation, this.translation, -this.scale);
this.translation[0] += this.canvas.width * 0.5;
this.translation[1] += this.canvas.height * 0.5;
if (this.onZoom != null)
this.onZoom();
if (this.onPan != null)
this.onPan();
}
zoomIn(): void {

View File

@ -57,10 +57,7 @@ class MeshDebuggerAppController extends AppController {
this.view = new MeshDebuggerView(this);
this.filePicker = unwrapNull(FilePickerView.create());
this.filePicker.onFileLoaded = fileData => {
this.fileData = fileData;
this.fileLoaded();
};
this.filePicker.onFileLoaded = fileData => this.fileLoaded(fileData);
this.openModal = unwrapNull(document.getElementById('pf-open-modal'));
this.fontPathSelectGroup =
@ -101,20 +98,20 @@ class MeshDebuggerAppController extends AppController {
this.fetchFile(results[2], BUILTIN_URIS[this.fileType]);
}
protected fileLoaded(): void {
protected fileLoaded(fileData: ArrayBuffer): void {
while (this.fontPathSelect.lastChild != null)
this.fontPathSelect.removeChild(this.fontPathSelect.lastChild);
this.fontPathSelectGroup.classList.remove('pf-display-none');
if (this.fileType === 'font')
this.fontLoaded();
this.fontLoaded(fileData);
else if (this.fileType === 'svg')
this.svgLoaded();
this.svgLoaded(fileData);
}
private fontLoaded(): void {
this.file = opentype.parse(this.fileData);
private fontLoaded(fileData: ArrayBuffer): void {
this.file = opentype.parse(fileData);
assert(this.file.isSupported(), "The font type is unsupported!");
const glyphCount = this.file.numGlyphs;
@ -131,10 +128,10 @@ class MeshDebuggerAppController extends AppController {
this.loadPath(this.file.charToGlyph(CHARACTER));
}
private svgLoaded(): void {
private svgLoaded(fileData: ArrayBuffer): void {
this.file = new SVGLoader;
this.file.scale = SVG_SCALE;
this.file.loadFile(this.fileData);
this.file.loadFile(fileData);
const pathCount = this.file.pathInstances.length;
for (let pathIndex = 0; pathIndex < pathCount; pathIndex++) {
@ -157,7 +154,7 @@ class MeshDebuggerAppController extends AppController {
}
const glyph = new MeshDebuggerGlyph(opentypeGlyph);
const glyphStorage = new GlyphStorage(this.fileData, [glyph], this.file);
const glyphStorage = new GlyphStorage(this.file.toArrayBuffer(), [glyph], this.file);
promise = glyphStorage.partition();
} else if (this.file instanceof SVGLoader) {
promise = this.file.partition(this.fontPathSelect.selectedIndex);

View File

@ -50,8 +50,8 @@ class SVGDemoController extends DemoAppController<SVGDemoView> {
this.loadInitialFile(this.builtinFileURI);
}
protected fileLoaded() {
this.loader.loadFile(this.fileData);
protected fileLoaded(fileData: ArrayBuffer) {
this.loader.loadFile(fileData);
this.loader.partition().then(meshes => {
this.meshes = meshes;
this.meshesReceived();

View File

@ -76,7 +76,7 @@ const B_PATH_INDEX_SIZE: number = 2;
const ATLAS_SIZE: glmatrix.vec2 = glmatrix.vec2.fromValues(2048, 4096);
const MIN_SCALE: number = 0.02;
const MIN_SCALE: number = 0.001;
const MAX_SCALE: number = 10.0;
declare global {
@ -157,7 +157,7 @@ class TextDemoController extends DemoAppController<TextDemoView> {
private updateText(): void {
this.text = this.editTextArea.value;
this.recreateLayout();
//this.recreateLayout();
window.jQuery(this.editTextModal).modal('hide');
}
@ -168,17 +168,19 @@ class TextDemoController extends DemoAppController<TextDemoView> {
unwrapNull(this.shaderSources));
}
protected fileLoaded() {
this.recreateLayout();
protected fileLoaded(fileData: ArrayBuffer) {
this.recreateLayout(fileData);
}
private recreateLayout() {
this.layout = new SimpleTextLayout(this.fileData,
this.text,
glyph => new GlyphInstance(glyph));
this.layout.glyphStorage.partition().then((meshes: PathfinderMeshData) => {
this.meshes = meshes;
private recreateLayout(fileData: ArrayBuffer) {
const newLayout = new SimpleTextLayout(fileData,
this.text,
glyph => new GlyphInstance(glyph));
newLayout.glyphStorage.partition().then((meshes: PathfinderMeshData) => {
this.view.then(view => {
this.layout = newLayout;
this.meshes = meshes;
view.attachText();
view.uploadPathColors(1);
view.attachMeshes([this.meshes]);
@ -250,8 +252,6 @@ class TextDemoView extends MonochromePathfinderView {
minScale: MIN_SCALE,
maxScale: MAX_SCALE,
});
this.camera.onPan = () => this.onPan();
this.camera.onZoom = () => this.onZoom();
this.canvas.addEventListener('dblclick', () => this.appController.showTextEditor(), false);
}
@ -281,7 +281,6 @@ class TextDemoView extends MonochromePathfinderView {
layout.layoutRuns();
let textBounds = layout.textFrame.bounds;
textBounds = scaleRect(textBounds, this.appController.pixelsPerUnit);
this.camera.bounds = textBounds;
const textGlyphs = layout.glyphStorage.allGlyphs;
@ -361,6 +360,8 @@ class TextDemoView extends MonochromePathfinderView {
const pathHintsBufferTexture = new PathfinderBufferTexture(this.gl, 'uPathHints');
pathHintsBufferTexture.upload(this.gl, pathHints);
this.pathHintsBufferTexture = pathHintsBufferTexture;
this.setGlyphTexCoords();
}
protected pathTransformsForObject(objectIndex: number): Float32Array {
@ -446,37 +447,42 @@ class TextDemoView extends MonochromePathfinderView {
}
attachText() {
this.panZoomEventsEnabled = false;
if (this.atlasFramebuffer == null)
this.createAtlasFramebuffer();
this.relayoutText();
this.layoutText();
this.camera.zoomToFit();
this.appController.fontSize = this.camera.scale *
this.appController.layout.glyphStorage.font.unitsPerEm;
this.buildAtlasGlyphs();
this.setDirty();
this.panZoomEventsEnabled = true;
}
relayoutText() {
this.layoutText();
this.rebuildAtlasIfNecessary();
}
private rebuildAtlasIfNecessary() {
this.buildAtlasGlyphs();
this.setGlyphTexCoords();
this.setDirty();
}
protected onPan() {
this.buildAtlasGlyphs();
this.setDirty();
this.rebuildAtlasIfNecessary();
}
protected onZoom() {
this.appController.fontSize = this.camera.scale * INITIAL_FONT_SIZE;
this.appController.fontSize = this.camera.scale *
this.appController.layout.glyphStorage.font.unitsPerEm;
this.buildAtlasGlyphs();
this.setDirty();
this.rebuildAtlasIfNecessary();
}
updateHinting(): void {
this.buildAtlasGlyphs();
this.setDirty();
this.rebuildAtlasIfNecessary();
}
private setIdentityTexScaleUniform(uniforms: UniformMap) {
@ -524,7 +530,9 @@ class TextDemoView extends MonochromePathfinderView {
[2.0 / this.canvas.width, 2.0 / this.canvas.height, 1.0]);
glmatrix.mat4.translate(transform,
transform,
[this.camera.translation[0], this.camera.translation[1], 0.0]);
[this.camera.translation[0],
this.camera.translation[1],
0.0]);
// Blit.
this.gl.uniformMatrix4fv(blitProgram.uniforms.uTransform, false, transform);
@ -545,6 +553,16 @@ class TextDemoView extends MonochromePathfinderView {
this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
}
private set panZoomEventsEnabled(flag: boolean) {
if (flag) {
this.camera.onPan = () => this.onPan();
this.camera.onZoom = () => this.onZoom();
} else {
this.camera.onPan = null;
this.camera.onZoom = null;
}
}
readonly bgColor: glmatrix.vec4 = glmatrix.vec4.fromValues(1.0, 1.0, 1.0, 0.0);
readonly fgColor: glmatrix.vec4 = glmatrix.vec4.fromValues(0.0, 0.0, 0.0, 1.0);