Allow different glyphs to be selected in the mesh debugger

This commit is contained in:
Patrick Walton 2017-09-19 17:35:11 -07:00
parent b1cf4b9760
commit a5b0e9bf9a
8 changed files with 163 additions and 36 deletions

View File

@ -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

View File

@ -9,6 +9,59 @@
<body class="pf-unscrollable">
{{>partials/navbar.html isTool=true}}
<canvas id="pf-canvas" width="400" height="300"></canvas>
<div class="fixed-bottom mb-3 d-flex justify-content-end align-items-end pf-pointer-events-none">
<div id="pf-toolbar">
<button id="pf-open-button" type="button"
class="btn btn-outline-secondary pf-toolbar-button">
Open…
</button>
</div>
</div>
<div class="modal fade" id="pf-open-modal" tabindex="-1" role="dialog"
aria-labelledby="pf-open-label" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="pf-open-label">Open…</h5>
<button type="button" class="close" data-dismiss="modal"
aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<label for="pf-open-file-label">File</label>
<select id="pf-open-file-select"
class="form-control custom-select">
<option value="font-eb-garamond" selected>
Font: EB Garamond
</option>
<option value="font-open-sans">Font: Open Sans</option>
<option value="font-nimbus-sans">Font: Nimbus Sans</option>
<option value="svg-tiger">SVG: Ghostscript Tiger</option>
</select>
</div>
<div class="form-group" id="pf-font-path-select-group">
<label for="pf-font-path-label">Path</label>
<select id="pf-font-path-select"
class="form-control custom-select">
<option value="65" selected>A</option>
<option value="66">B</option>
<option value="67">C</option>
</select>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary"
data-dismiss="modal">Cancel</button>
<button type="button"
class="btn btn-primary" id="pf-open-ok-button">Open</button>
</div>
</div>
</div>
</div>
{{>partials/spinner.html}}
</body>
</html>

View File

@ -1,5 +1,5 @@
<!-- http://tobiasahlin.com/spinkit/ -->
<div class="sk-fading-circle pf-spinner-hidden" id="pf-spinner">
<div class="sk-fading-circle pf-spinner-hidden pf-spinner">
<div class="sk-circle1 sk-circle"></div>
<div class="sk-circle2 sk-circle"></div>
<div class="sk-circle3 sk-circle"></div>

View File

@ -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<ThreeDView> {
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<ThreeDView> {
return FONT;
}
glyphStorage: GlyphStorage<ThreeDGlyph>;
glyphStorage: TextFrameGlyphStorage<ThreeDGlyph>;
private baseMeshes: PathfinderMeshData;
private expandedMeshes: ExpandedMeshData[];

View File

@ -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() {

View File

@ -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<BenchmarkTestView> {
const textRun = new TextRun<BenchmarkGlyph>(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<BenchmarkTestView> {
protected readonly defaultFile: string = FONT;
protected readonly builtinFileURI: string = BUILTIN_FONT_URI;
private glyphStorage: GlyphStorage<BenchmarkGlyph>;
private glyphStorage: TextFrameGlyphStorage<BenchmarkGlyph>;
private baseMeshes: PathfinderMeshData;
private expandedMeshes: ExpandedMeshData;

View File

@ -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<MeshDebuggerGlyph>(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<MeshDebuggerGlyph>;
meshes: PathfinderMeshData;
private font: Font;
meshes: PathfinderMeshData | null;
private openModal: HTMLElement;
private openFileSelect: HTMLSelectElement;
private fontPathSelectGroup: HTMLElement;
private fontPathSelect: HTMLSelectElement;
private view: MeshDebuggerView;
}

View File

@ -194,33 +194,24 @@ export class TextFrame<Glyph extends PathfinderGlyph> {
readonly origin: glmatrix.vec3;
private readonly font: Font;
}
export class GlyphStorage<Glyph extends PathfinderGlyph> {
constructor(fontData: ArrayBuffer,
textFrames: TextFrame<Glyph>[],
createGlyph: CreateGlyphFn<Glyph>,
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<PathfinderMeshData> {
// Build the partitioning request to the server.
//
@ -253,6 +244,23 @@ export class GlyphStorage<Glyph extends PathfinderGlyph> {
});
}
readonly fontData: ArrayBuffer;
readonly font: Font;
readonly uniqueGlyphs: Glyph[];
}
export class TextFrameGlyphStorage<Glyph extends PathfinderGlyph> extends GlyphStorage<Glyph> {
constructor(fontData: ArrayBuffer, textFrames: TextFrame<Glyph>[], 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<Glyph extends PathfinderGlyph> {
return _.flatMap(this.textFrames, textRun => textRun.allGlyphs);
}
readonly fontData: ArrayBuffer;
readonly font: Font;
readonly textFrames: TextFrame<Glyph>[];
readonly uniqueGlyphs: Glyph[];
}
export class SimpleTextLayout<Glyph extends PathfinderGlyph> {
@ -279,7 +284,7 @@ export class SimpleTextLayout<Glyph extends PathfinderGlyph> {
});
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<Glyph extends PathfinderGlyph> {
}
readonly textFrame: TextFrame<Glyph>;
readonly glyphStorage: GlyphStorage<Glyph>;
readonly glyphStorage: TextFrameGlyphStorage<Glyph>;
}
export abstract class PathfinderGlyph {