Keep the camera and view in a sensible state when changing fonts in the text demo
This commit is contained in:
parent
db32009d7b
commit
2197896c4f
|
@ -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) => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
private recreateLayout(fileData: ArrayBuffer) {
|
||||
const newLayout = new SimpleTextLayout(fileData,
|
||||
this.text,
|
||||
glyph => new GlyphInstance(glyph));
|
||||
this.layout.glyphStorage.partition().then((meshes: PathfinderMeshData) => {
|
||||
this.meshes = meshes;
|
||||
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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue