Factor camera logic into a separate object in preparation for the 3D demo
This commit is contained in:
parent
444e7bbf96
commit
7360a41a60
|
@ -19,6 +19,7 @@ import {BUILTIN_FONT_URI, TextLayout, PathfinderGlyph} from "./text";
|
|||
import {PathfinderError, panic, unwrapNull} from "./utils";
|
||||
import {PathfinderDemoView, Timings} from "./view";
|
||||
import SSAAStrategy from "./ssaa-strategy";
|
||||
import { OrthographicCamera } from "./camera";
|
||||
|
||||
const TEXT: string = "Lorem ipsum dolor sit amet";
|
||||
|
||||
|
@ -80,7 +81,10 @@ class ThreeDView extends PathfinderDemoView {
|
|||
super(commonShaderSource, shaderSources);
|
||||
|
||||
this.appController = appController;
|
||||
this._scale = 1.0;
|
||||
|
||||
this.camera = new OrthographicCamera(this.canvas);
|
||||
this.camera.onPan = () => this.setDirty();
|
||||
this.camera.onZoom = () => this.setDirty();
|
||||
}
|
||||
|
||||
uploadPathMetadata(pathCount: number) {
|
||||
|
@ -98,7 +102,6 @@ class ThreeDView extends PathfinderDemoView {
|
|||
|
||||
const textGlyph = textGlyphs[pathIndex];
|
||||
const glyphRect = textGlyph.getRect(PIXELS_PER_UNIT);
|
||||
console.log(glyphRect);
|
||||
pathTransforms.set([1, 1, glyphRect[0], glyphRect[1]], startOffset);
|
||||
}
|
||||
|
||||
|
@ -135,25 +138,19 @@ class ThreeDView extends PathfinderDemoView {
|
|||
return glmatrix.vec2.fromValues(1.0, 1.0);
|
||||
}
|
||||
|
||||
protected get scale(): number {
|
||||
return this._scale;
|
||||
}
|
||||
|
||||
protected set scale(newScale: number) {
|
||||
this._scale = newScale;
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
protected get worldTransform() {
|
||||
const transform = glmatrix.mat4.create();
|
||||
glmatrix.mat4.fromTranslation(transform, [this.translation[0], this.translation[1], 0]);
|
||||
glmatrix.mat4.scale(transform, transform, [this.scale, this.scale, 1.0]);
|
||||
const translation = this.camera.translation;
|
||||
glmatrix.mat4.fromTranslation(transform, [translation[0], translation[1], 0]);
|
||||
glmatrix.mat4.scale(transform, transform, [this.camera.scale, this.camera.scale, 1.0]);
|
||||
return transform;
|
||||
}
|
||||
|
||||
private _scale: number;
|
||||
|
||||
private appController: ThreeDController;
|
||||
|
||||
camera: OrthographicCamera;
|
||||
}
|
||||
|
||||
class ThreeDGlyph extends PathfinderGlyph {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
// pathfinder/client/src/camera.ts
|
||||
//
|
||||
// Copyright © 2017 The Pathfinder Project Developers.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
import * as glmatrix from 'gl-matrix';
|
||||
import {PathfinderView} from "./view";
|
||||
|
||||
const SCALE_FACTOR: number = 1.0 / 100.0;
|
||||
|
||||
export abstract class Camera {
|
||||
constructor(canvas: HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
}
|
||||
|
||||
protected canvas: HTMLCanvasElement;
|
||||
}
|
||||
|
||||
export class OrthographicCamera extends Camera {
|
||||
constructor(canvas: HTMLCanvasElement) {
|
||||
super(canvas);
|
||||
|
||||
this.translation = glmatrix.vec2.create();
|
||||
this.scale = 1.0;
|
||||
|
||||
this.canvas.addEventListener('wheel', event => this.onWheel(event), false);
|
||||
|
||||
this.onPan = null;
|
||||
this.onZoom = null;
|
||||
}
|
||||
|
||||
onWheel(event: MouseWheelEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.ctrlKey) {
|
||||
// Zoom event: see https://developer.mozilla.org/en-US/docs/Web/Events/wheel
|
||||
const mouseLocation = glmatrix.vec2.fromValues(event.clientX, event.clientY);
|
||||
const canvasLocation = this.canvas.getBoundingClientRect();
|
||||
mouseLocation[0] -= canvasLocation.left;
|
||||
mouseLocation[1] = canvasLocation.bottom - mouseLocation[1];
|
||||
glmatrix.vec2.scale(mouseLocation, mouseLocation, window.devicePixelRatio);
|
||||
|
||||
const absoluteTranslation = glmatrix.vec2.create();
|
||||
glmatrix.vec2.sub(absoluteTranslation, this.translation, mouseLocation);
|
||||
glmatrix.vec2.scale(absoluteTranslation, absoluteTranslation, 1.0 / this.scale);
|
||||
|
||||
this.scale *= 1.0 - event.deltaY * window.devicePixelRatio * SCALE_FACTOR;
|
||||
|
||||
glmatrix.vec2.scale(absoluteTranslation, absoluteTranslation, this.scale);
|
||||
glmatrix.vec2.add(this.translation, absoluteTranslation, mouseLocation);
|
||||
|
||||
if (this.onZoom != null)
|
||||
this.onZoom();
|
||||
} else {
|
||||
// Pan event.
|
||||
const delta = glmatrix.vec2.fromValues(-event.deltaX, event.deltaY);
|
||||
glmatrix.vec2.scale(delta, delta, window.devicePixelRatio);
|
||||
glmatrix.vec2.add(this.translation, this.translation, delta);
|
||||
|
||||
if (this.onPan != null)
|
||||
this.onPan();
|
||||
}
|
||||
}
|
||||
|
||||
onPan: (() => void) | null;
|
||||
onZoom: (() => void) | null;
|
||||
|
||||
translation: glmatrix.vec2;
|
||||
scale: number;
|
||||
}
|
|
@ -11,6 +11,7 @@
|
|||
import * as glmatrix from 'gl-matrix';
|
||||
|
||||
import {AppController} from "./app-controller";
|
||||
import {OrthographicCamera} from "./camera";
|
||||
import {B_QUAD_SIZE, B_QUAD_UPPER_LEFT_VERTEX_OFFSET} from "./meshes";
|
||||
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";
|
||||
|
@ -67,6 +68,7 @@ class MeshDebuggerView extends PathfinderView {
|
|||
super();
|
||||
|
||||
this.appController = appController;
|
||||
this.camera = new OrthographicCamera(this.canvas);
|
||||
this.scale = 1.0;
|
||||
}
|
||||
|
||||
|
@ -85,7 +87,8 @@ class MeshDebuggerView extends PathfinderView {
|
|||
context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
context.save();
|
||||
context.translate(this.translation[0], this.canvas.height - this.translation[1]);
|
||||
context.translate(this.camera.translation[0],
|
||||
this.canvas.height - this.camera.translation[1]);
|
||||
context.scale(this.scale, this.scale);
|
||||
|
||||
context.font = POINT_LABEL_FONT;
|
||||
|
@ -156,6 +159,8 @@ class MeshDebuggerView extends PathfinderView {
|
|||
protected scale: number;
|
||||
|
||||
private appController: MeshDebuggerAppController;
|
||||
|
||||
camera: OrthographicCamera;
|
||||
}
|
||||
|
||||
class MeshDebuggerGlyph extends PathfinderGlyph {}
|
||||
|
|
|
@ -14,6 +14,7 @@ import 'path-data-polyfill.js';
|
|||
|
||||
import {DemoAppController} from './app-controller';
|
||||
import {AntialiasingStrategy, AntialiasingStrategyName, NoAAStrategy} from "./aa-strategy";
|
||||
import {OrthographicCamera} from "./camera";
|
||||
import {ECAAStrategy, ECAAMulticolorStrategy} from "./ecaa-strategy";
|
||||
import {PathfinderMeshData} from "./meshes";
|
||||
import {ShaderMap, ShaderProgramSource} from './shader-loader';
|
||||
|
@ -172,7 +173,10 @@ class SVGDemoView extends PathfinderDemoView {
|
|||
super(commonShaderSource, shaderSources);
|
||||
|
||||
this.appController = appController;
|
||||
this._scale = 1.0;
|
||||
|
||||
this.camera = new OrthographicCamera(this.canvas);
|
||||
this.camera.onPan = () => this.setDirty();
|
||||
this.camera.onZoom = () => this.setDirty();
|
||||
}
|
||||
|
||||
get destAllocatedSize(): glmatrix.vec2 {
|
||||
|
@ -223,25 +227,17 @@ class SVGDemoView extends PathfinderDemoView {
|
|||
return glmatrix.vec2.fromValues(1.0, 1.0);
|
||||
}
|
||||
|
||||
protected get scale(): number {
|
||||
return this._scale;
|
||||
}
|
||||
|
||||
protected set scale(newScale: number) {
|
||||
this._scale = newScale;
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
protected get worldTransform() {
|
||||
const transform = glmatrix.mat4.create();
|
||||
glmatrix.mat4.fromTranslation(transform, [this.translation[0], this.translation[1], 0]);
|
||||
glmatrix.mat4.scale(transform, transform, [this.scale, this.scale, 1.0]);
|
||||
const translation = this.camera.translation;
|
||||
glmatrix.mat4.fromTranslation(transform, [translation[0], translation[1], 0]);
|
||||
glmatrix.mat4.scale(transform, transform, [this.camera.scale, this.camera.scale, 1.0]);
|
||||
return transform;
|
||||
}
|
||||
|
||||
private _scale: number;
|
||||
|
||||
private appController: SVGDemoController;
|
||||
|
||||
camera: OrthographicCamera;
|
||||
}
|
||||
|
||||
function main() {
|
||||
|
|
|
@ -27,6 +27,7 @@ import {PathfinderError, assert, expectNotNull, UINT32_SIZE, unwrapNull, panic}
|
|||
import {MonochromePathfinderView, Timings} from './view';
|
||||
import PathfinderBufferTexture from './buffer-texture';
|
||||
import SSAAStrategy from './ssaa-strategy';
|
||||
import { OrthographicCamera } from "./camera";
|
||||
|
||||
const DEFAULT_TEXT: string =
|
||||
`’Twas brillig, and the slithy toves
|
||||
|
@ -227,6 +228,10 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
|
||||
this.appController = appController;
|
||||
|
||||
this.camera = new OrthographicCamera(this.canvas);
|
||||
this.camera.onPan = () => this.onPan();
|
||||
this.camera.onZoom = () => this.onZoom();
|
||||
|
||||
this.canvas.addEventListener('dblclick', () => this.appController.showTextEditor(), false);
|
||||
}
|
||||
|
||||
|
@ -279,10 +284,11 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
const pixelsPerUnit = this.appController.pixelsPerUnit;
|
||||
|
||||
// Only build glyphs in view.
|
||||
const canvasRect = glmatrix.vec4.fromValues(-this.translation[0],
|
||||
-this.translation[1],
|
||||
-this.translation[0] + this.canvas.width,
|
||||
-this.translation[1] + this.canvas.height);
|
||||
const translation = this.camera.translation;
|
||||
const canvasRect = glmatrix.vec4.fromValues(-translation[0],
|
||||
-translation[1],
|
||||
-translation[0] + this.canvas.width,
|
||||
-translation[1] + this.canvas.height);
|
||||
|
||||
let atlasGlyphs =
|
||||
textGlyphs.filter(glyph => rectsIntersect(glyph.getRect(pixelsPerUnit), canvasRect))
|
||||
|
@ -387,8 +393,14 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
this.setDirty();
|
||||
}
|
||||
|
||||
protected panned() {
|
||||
super.panned();
|
||||
protected onPan() {
|
||||
this.setDirty();
|
||||
this.rebuildAtlasIfNecessary();
|
||||
}
|
||||
|
||||
protected onZoom() {
|
||||
this.appController.fontSize = this.camera.scale * INITIAL_FONT_SIZE;
|
||||
this.setDirty();
|
||||
this.rebuildAtlasIfNecessary();
|
||||
}
|
||||
|
||||
|
@ -434,7 +446,7 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
[2.0 / this.canvas.width, 2.0 / this.canvas.height, 1.0]);
|
||||
glmatrix.mat4.translate(transform,
|
||||
transform,
|
||||
[this.translation[0], this.translation[1], 0.0]);
|
||||
[this.camera.translation[0], this.camera.translation[1], 0.0]);
|
||||
|
||||
// Blit.
|
||||
this.gl.uniformMatrix4fv(blitProgram.uniforms.uTransform, false, transform);
|
||||
|
@ -468,14 +480,6 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
return this.appController.atlas.usedSize;
|
||||
}
|
||||
|
||||
protected get scale(): number {
|
||||
return this.appController.fontSize;
|
||||
}
|
||||
|
||||
protected set scale(newScale: number) {
|
||||
this.appController.fontSize = newScale;
|
||||
}
|
||||
|
||||
protected createAAStrategy(aaType: AntialiasingStrategyName, aaLevel: number):
|
||||
AntialiasingStrategy {
|
||||
return new (ANTIALIASING_STRATEGIES[aaType])(aaLevel);
|
||||
|
@ -497,6 +501,8 @@ class TextDemoView extends MonochromePathfinderView {
|
|||
glyphElementsBuffer: WebGLBuffer;
|
||||
|
||||
appController: TextDemoController;
|
||||
|
||||
camera: OrthographicCamera;
|
||||
}
|
||||
|
||||
interface AntialiasingStrategyTable {
|
||||
|
|
|
@ -17,8 +17,7 @@ import {PathfinderShaderProgram, SHADER_NAMES, ShaderMap} from './shader-loader'
|
|||
import {ShaderProgramSource, UnlinkedShaderProgram} from './shader-loader';
|
||||
import {PathfinderError, UINT32_SIZE, expectNotNull, unwrapNull} from './utils';
|
||||
import PathfinderBufferTexture from './buffer-texture';
|
||||
|
||||
const SCALE_FACTOR: number = 1.0 / 100.0;
|
||||
import { Camera } from "./camera";
|
||||
|
||||
const TIME_INTERVAL_DELAY: number = 32;
|
||||
|
||||
|
@ -51,12 +50,8 @@ export abstract class PathfinderView {
|
|||
constructor() {
|
||||
this.dirty = false;
|
||||
|
||||
this.translation = glmatrix.vec2.create();
|
||||
|
||||
this.canvas = unwrapNull(document.getElementById('pf-canvas')) as HTMLCanvasElement;
|
||||
|
||||
this.canvas.addEventListener('wheel', event => this.onWheel(event), false);
|
||||
|
||||
window.addEventListener('resize', () => this.resizeToFit(false), false);
|
||||
this.resizeToFit(true);
|
||||
}
|
||||
|
@ -78,42 +73,6 @@ export abstract class PathfinderView {
|
|||
this.resized();
|
||||
}
|
||||
|
||||
private onWheel(event: WheelEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.ctrlKey) {
|
||||
// Zoom event: see https://developer.mozilla.org/en-US/docs/Web/Events/wheel
|
||||
const mouseLocation = glmatrix.vec2.fromValues(event.clientX, event.clientY);
|
||||
const canvasLocation = this.canvas.getBoundingClientRect();
|
||||
mouseLocation[0] -= canvasLocation.left;
|
||||
mouseLocation[1] = canvasLocation.bottom - mouseLocation[1];
|
||||
glmatrix.vec2.scale(mouseLocation, mouseLocation, window.devicePixelRatio);
|
||||
|
||||
const absoluteTranslation = glmatrix.vec2.create();
|
||||
glmatrix.vec2.sub(absoluteTranslation, this.translation, mouseLocation);
|
||||
glmatrix.vec2.scale(absoluteTranslation, absoluteTranslation, 1.0 / this.scale);
|
||||
|
||||
this.scale *= 1.0 - event.deltaY * window.devicePixelRatio * SCALE_FACTOR;
|
||||
|
||||
glmatrix.vec2.scale(absoluteTranslation, absoluteTranslation, this.scale);
|
||||
glmatrix.vec2.add(this.translation, absoluteTranslation, mouseLocation);
|
||||
|
||||
this.setDirty();
|
||||
return;
|
||||
}
|
||||
|
||||
// Pan event.
|
||||
const delta = glmatrix.vec2.fromValues(-event.deltaX, event.deltaY);
|
||||
glmatrix.vec2.scale(delta, delta, window.devicePixelRatio);
|
||||
glmatrix.vec2.add(this.translation, this.translation, delta);
|
||||
|
||||
this.panned();
|
||||
}
|
||||
|
||||
protected panned(): void {
|
||||
this.setDirty();
|
||||
}
|
||||
|
||||
protected resized(): void {
|
||||
this.setDirty();
|
||||
}
|
||||
|
@ -129,12 +88,9 @@ export abstract class PathfinderView {
|
|||
this.dirty = false;
|
||||
}
|
||||
|
||||
protected abstract get scale(): number;
|
||||
protected abstract set scale(newScale: number);
|
||||
|
||||
protected canvas: HTMLCanvasElement;
|
||||
|
||||
protected translation: glmatrix.vec2;
|
||||
protected camera: Camera;
|
||||
|
||||
private dirty: boolean;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue