From a5b0e9bf9ab41d64b838493fa33f636c800c450e Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 19 Sep 2017 17:35:11 -0700 Subject: [PATCH] Allow different glyphs to be selected in the mesh debugger --- demo/client/css/pathfinder.css | 5 +- demo/client/html/mesh-debugger.html.hbs | 53 +++++++++++++ demo/client/html/partials/spinner.html.hbs | 2 +- demo/client/src/3d-demo.ts | 6 +- demo/client/src/app-controller.ts | 2 +- demo/client/src/benchmark.ts | 6 +- demo/client/src/mesh-debugger.ts | 88 +++++++++++++++++++--- demo/client/src/text.ts | 37 +++++---- 8 files changed, 163 insertions(+), 36 deletions(-) diff --git a/demo/client/css/pathfinder.css b/demo/client/css/pathfinder.css index 60bb9608..940fc717 100644 --- a/demo/client/css/pathfinder.css +++ b/demo/client/css/pathfinder.css @@ -85,7 +85,7 @@ button > svg path { #pf-svg { visibility: hidden; } -#pf-spinner { +.pf-spinner { position: absolute; top: 0; left: 0; @@ -95,6 +95,9 @@ button > svg path { .pf-spinner-hidden { display: none !important; } +.pf-display-none { + display: none !important; +} /* * Arrow diff --git a/demo/client/html/mesh-debugger.html.hbs b/demo/client/html/mesh-debugger.html.hbs index 286488f4..85b18927 100644 --- a/demo/client/html/mesh-debugger.html.hbs +++ b/demo/client/html/mesh-debugger.html.hbs @@ -9,6 +9,59 @@ {{>partials/navbar.html isTool=true}} +
+
+ +
+
+ {{>partials/spinner.html}} diff --git a/demo/client/html/partials/spinner.html.hbs b/demo/client/html/partials/spinner.html.hbs index f45459cd..6ea99543 100644 --- a/demo/client/html/partials/spinner.html.hbs +++ b/demo/client/html/partials/spinner.html.hbs @@ -1,5 +1,5 @@ -
+
diff --git a/demo/client/src/3d-demo.ts b/demo/client/src/3d-demo.ts index e73788e7..08e82d79 100644 --- a/demo/client/src/3d-demo.ts +++ b/demo/client/src/3d-demo.ts @@ -17,7 +17,7 @@ import {PerspectiveCamera} from "./camera"; import {mat4, vec2} from "gl-matrix"; import {PathfinderMeshData} from "./meshes"; import {ShaderMap, ShaderProgramSource} from "./shader-loader"; -import {BUILTIN_FONT_URI, ExpandedMeshData, GlyphStorage, PathfinderGlyph} from "./text"; +import {BUILTIN_FONT_URI, ExpandedMeshData, TextFrameGlyphStorage, PathfinderGlyph} from "./text"; import {Hint, SimpleTextLayout, TextFrame, TextRun} from "./text"; import {PathfinderError, assert, panic, unwrapNull} from "./utils"; import {PathfinderDemoView, Timings} from "./view"; @@ -163,7 +163,7 @@ class ThreeDController extends DemoAppController { textFrames.push(new TextFrame(textRuns, font)); } - this.glyphStorage = new GlyphStorage(this.fileData, textFrames, createGlyph, font); + this.glyphStorage = new TextFrameGlyphStorage(this.fileData, textFrames, font); this.glyphStorage.layoutRuns(); this.glyphStorage.partition().then((baseMeshes: PathfinderMeshData) => { @@ -191,7 +191,7 @@ class ThreeDController extends DemoAppController { return FONT; } - glyphStorage: GlyphStorage; + glyphStorage: TextFrameGlyphStorage; private baseMeshes: PathfinderMeshData; private expandedMeshes: ExpandedMeshData[]; diff --git a/demo/client/src/app-controller.ts b/demo/client/src/app-controller.ts index b335f475..c883748a 100644 --- a/demo/client/src/app-controller.ts +++ b/demo/client/src/app-controller.ts @@ -11,7 +11,7 @@ import {AntialiasingStrategyName} from "./aa-strategy"; import {ShaderLoader, ShaderMap, ShaderProgramSource} from './shader-loader'; import {expectNotNull, unwrapUndef, unwrapNull} from './utils'; -import { PathfinderDemoView, Timings, TIMINGS } from "./view"; +import {PathfinderDemoView, Timings, TIMINGS} from "./view"; export abstract class AppController { start() { diff --git a/demo/client/src/benchmark.ts b/demo/client/src/benchmark.ts index 76b06335..3573abc8 100644 --- a/demo/client/src/benchmark.ts +++ b/demo/client/src/benchmark.ts @@ -13,7 +13,7 @@ import * as opentype from "opentype.js"; import { AppController, DemoAppController } from "./app-controller"; import {PathfinderMeshData} from "./meshes"; -import { BUILTIN_FONT_URI, GlyphStorage, PathfinderGlyph, TextFrame, TextRun, ExpandedMeshData } from "./text"; +import { BUILTIN_FONT_URI, TextFrameGlyphStorage, PathfinderGlyph, TextFrame, TextRun, ExpandedMeshData } from "./text"; import { assert, unwrapNull, PathfinderError } from "./utils"; import { PathfinderDemoView, Timings, MonochromePathfinderView } from "./view"; import { ShaderMap, ShaderProgramSource } from "./shader-loader"; @@ -68,7 +68,7 @@ class BenchmarkAppController extends DemoAppController { const textRun = new TextRun(STRING, [0, 0], font, createGlyph); this.textRun = textRun; const textFrame = new TextFrame([textRun], font); - this.glyphStorage = new GlyphStorage(this.fileData, [textFrame], createGlyph, font); + this.glyphStorage = new TextFrameGlyphStorage(this.fileData, [textFrame], font); this.glyphStorage.partition().then(baseMeshes => { this.baseMeshes = baseMeshes; @@ -116,7 +116,7 @@ class BenchmarkAppController extends DemoAppController { protected readonly defaultFile: string = FONT; protected readonly builtinFileURI: string = BUILTIN_FONT_URI; - private glyphStorage: GlyphStorage; + private glyphStorage: TextFrameGlyphStorage; private baseMeshes: PathfinderMeshData; private expandedMeshes: ExpandedMeshData; diff --git a/demo/client/src/mesh-debugger.ts b/demo/client/src/mesh-debugger.ts index 90e169cc..8b05fd71 100644 --- a/demo/client/src/mesh-debugger.ts +++ b/demo/client/src/mesh-debugger.ts @@ -17,12 +17,14 @@ import {B_QUAD_UPPER_RIGHT_VERTEX_OFFSET} from "./meshes"; import {B_QUAD_UPPER_CONTROL_POINT_VERTEX_OFFSET, B_QUAD_LOWER_LEFT_VERTEX_OFFSET} from "./meshes"; import {B_QUAD_LOWER_RIGHT_VERTEX_OFFSET} from "./meshes"; import {B_QUAD_LOWER_CONTROL_POINT_VERTEX_OFFSET, PathfinderMeshData} from "./meshes"; -import {BUILTIN_FONT_URI, GlyphStorage, PathfinderGlyph, TextRun, TextFrame} from "./text"; +import {BUILTIN_FONT_URI, TextFrameGlyphStorage, PathfinderGlyph, TextRun} from "./text"; +import {GlyphStorage, TextFrame} from "./text"; import {unwrapNull, UINT32_SIZE, UINT32_MAX, assert} from "./utils"; import {PathfinderView} from "./view"; import * as opentype from "opentype.js"; +import {Font} from 'opentype.js'; -const CHARACTER: string = 'r'; +const CHARACTER: string = 'A'; const FONT: string = 'eb-garamond'; @@ -36,19 +38,77 @@ class MeshDebuggerAppController extends AppController { this.view = new MeshDebuggerView(this); + this.openModal = unwrapNull(document.getElementById('pf-open-modal')); + this.fontPathSelectGroup = + unwrapNull(document.getElementById('pf-font-path-select-group')); + this.fontPathSelect = unwrapNull(document.getElementById('pf-font-path-select')) as + HTMLSelectElement; + + this.openFileSelect = unwrapNull(document.getElementById('pf-open-file-select')) as + HTMLSelectElement; + this.openFileSelect.addEventListener('change', () => this.openSelectedFile(), false); + + const openButton = unwrapNull(document.getElementById('pf-open-button')); + openButton.addEventListener('click', () => this.showOpenDialog(), false); + + const openOKButton = unwrapNull(document.getElementById('pf-open-ok-button')); + openOKButton.addEventListener('click', () => this.loadPath(), false); + this.loadInitialFile(); } + private showOpenDialog(): void { + window.jQuery(this.openModal).modal(); + } + + private openSelectedFile(): void { + const selectedOption = this.openFileSelect.selectedOptions[0] as HTMLOptionElement; + const optionValue = selectedOption.value; + + this.fontPathSelectGroup.classList.add('pf-display-none'); + + if (optionValue.startsWith('font-')) { + this.fetchFile(optionValue.substr('font-'.length)); + } else if (optionValue.startsWith('svg-')) { + // TODO(pcwalton) + } + } + protected fileLoaded(): void { - const font = opentype.parse(this.fileData); - assert(font.isSupported(), "The font type is unsupported!"); + this.font = opentype.parse(this.fileData); + assert(this.font.isSupported(), "The font type is unsupported!"); - const createGlyph = (glyph: opentype.Glyph) => new MeshDebuggerGlyph(glyph); - const textRun = new TextRun(CHARACTER, [0, 0], font, createGlyph); - const textFrame = new TextFrame([textRun], font); - this.glyphStorage = new GlyphStorage(this.fileData, [textFrame], createGlyph, font); + while (this.fontPathSelect.lastChild != null) + this.fontPathSelect.removeChild(this.fontPathSelect.lastChild); - this.glyphStorage.partition().then(meshes => { + this.fontPathSelectGroup.classList.remove('pf-display-none'); + + const glyphCount = this.font.numGlyphs; + for (let glyphIndex = 1; glyphIndex < glyphCount; glyphIndex++) { + const newOption = document.createElement('option'); + newOption.value = "" + glyphIndex; + const glyphName = this.font.glyphIndexToName(glyphIndex); + newOption.appendChild(document.createTextNode(glyphName)); + this.fontPathSelect.appendChild(newOption); + } + + // Automatically load a path if this is the initial pageload. + if (this.meshes == null) + this.loadPath(this.font.charToGlyph(CHARACTER)); + } + + protected loadPath(opentypeGlyph?: opentype.Glyph | null) { + window.jQuery(this.openModal).modal('hide'); + + if (opentypeGlyph == null) { + const glyphIndex = parseInt(this.fontPathSelect.selectedOptions[0].value); + opentypeGlyph = this.font.glyphs.get(glyphIndex); + } + + const glyph = new MeshDebuggerGlyph(opentypeGlyph); + const glyphStorage = new GlyphStorage(this.fileData, [glyph], this.font); + + glyphStorage.partition().then(meshes => { this.meshes = meshes; this.view.attachMeshes(); }) @@ -57,8 +117,14 @@ class MeshDebuggerAppController extends AppController { protected readonly defaultFile: string = FONT; protected readonly builtinFileURI: string = BUILTIN_FONT_URI; - glyphStorage: GlyphStorage; - meshes: PathfinderMeshData; + private font: Font; + + meshes: PathfinderMeshData | null; + + private openModal: HTMLElement; + private openFileSelect: HTMLSelectElement; + private fontPathSelectGroup: HTMLElement; + private fontPathSelect: HTMLSelectElement; private view: MeshDebuggerView; } diff --git a/demo/client/src/text.ts b/demo/client/src/text.ts index d4b4f66f..d824a7ff 100644 --- a/demo/client/src/text.ts +++ b/demo/client/src/text.ts @@ -194,33 +194,24 @@ export class TextFrame { readonly origin: glmatrix.vec3; private readonly font: Font; - } export class GlyphStorage { - constructor(fontData: ArrayBuffer, - textFrames: TextFrame[], - createGlyph: CreateGlyphFn, - font?: Font) { + constructor(fontData: ArrayBuffer, glyphs: Glyph[], font?: Font) { if (font == null) { font = opentype.parse(fontData); assert(font.isSupported(), "The font type is unsupported!"); } this.fontData = fontData; - this.textFrames = textFrames; this.font = font; // Determine all glyphs potentially needed. - this.uniqueGlyphs = this.allGlyphs; + this.uniqueGlyphs = glyphs; this.uniqueGlyphs.sort((a, b) => a.index - b.index); this.uniqueGlyphs = _.sortedUniqBy(this.uniqueGlyphs, glyph => glyph.index); } - expandMeshes(meshes: PathfinderMeshData): ExpandedMeshData[] { - return this.textFrames.map(textFrame => textFrame.expandMeshes(this.uniqueGlyphs, meshes)); - } - partition(): Promise { // Build the partitioning request to the server. // @@ -253,6 +244,23 @@ export class GlyphStorage { }); } + readonly fontData: ArrayBuffer; + readonly font: Font; + readonly uniqueGlyphs: Glyph[]; +} + +export class TextFrameGlyphStorage extends GlyphStorage { + constructor(fontData: ArrayBuffer, textFrames: TextFrame[], font?: Font) { + const allGlyphs = _.flatMap(textFrames, textRun => textRun.allGlyphs); + super(fontData, allGlyphs, font); + + this.textFrames = textFrames; + } + + expandMeshes(meshes: PathfinderMeshData): ExpandedMeshData[] { + return this.textFrames.map(textFrame => textFrame.expandMeshes(this.uniqueGlyphs, meshes)); + } + layoutRuns() { for (const textFrame of this.textFrames) textFrame.runs.forEach(textRun => textRun.layout()); @@ -262,10 +270,7 @@ export class GlyphStorage { return _.flatMap(this.textFrames, textRun => textRun.allGlyphs); } - readonly fontData: ArrayBuffer; - readonly font: Font; readonly textFrames: TextFrame[]; - readonly uniqueGlyphs: Glyph[]; } export class SimpleTextLayout { @@ -279,7 +284,7 @@ export class SimpleTextLayout { }); this.textFrame = new TextFrame(textRuns, font); - this.glyphStorage = new GlyphStorage(fontData, [this.textFrame], createGlyph, font); + this.glyphStorage = new TextFrameGlyphStorage(fontData, [this.textFrame], font); } layoutRuns() { @@ -291,7 +296,7 @@ export class SimpleTextLayout { } readonly textFrame: TextFrame; - readonly glyphStorage: GlyphStorage; + readonly glyphStorage: TextFrameGlyphStorage; } export abstract class PathfinderGlyph {