planet-wars/frontend/www/webgl/util.ts

217 lines
5.9 KiB
TypeScript
Raw Permalink Normal View History

2019-09-19 17:52:48 +02:00
var loadSvg = require('load-svg')
var parsePath = require('extract-svg-path').parse
var svgMesh3d = require('svg-mesh-3d')
2019-09-16 21:18:01 +02:00
export interface Dictionary<T> {
[Key: string]: T;
}
interface OnLoadable {
onload: any;
}
export function onload2promise<T extends OnLoadable>(obj: T): Promise<T> {
return new Promise(resolve => {
obj.onload = () => resolve(obj);
});
}
export function resizeCanvasToDisplaySize(
canvas: HTMLCanvasElement,
multiplier?: number,
): boolean {
multiplier = multiplier || 1;
var width = canvas.clientWidth * multiplier | 0;
var height = canvas.clientHeight * multiplier | 0;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
return true;
}
return false;
}
export class FPSCounter {
last: number;
count: number;
2020-03-29 07:15:50 +02:00
_delta: number;
_prev: number
2019-09-16 21:18:01 +02:00
constructor() {
this.last = 0;
this.count = 0;
2020-03-29 07:15:50 +02:00
this._delta = 0;
this._prev = 0;
2019-09-16 21:18:01 +02:00
}
frame(now: number) {
this.count += 1;
2020-03-29 07:15:50 +02:00
this._delta = now - this._prev;
this._prev = now;
2019-09-17 20:19:04 +02:00
if (now - this.last > 1000) {
2019-09-16 21:18:01 +02:00
this.last = now;
console.log(this.count + " fps");
this.count = 0;
}
}
2020-03-29 07:15:50 +02:00
delta(now: number): number {
return this._delta;
}
2019-09-16 21:18:01 +02:00
}
2019-09-17 20:19:04 +02:00
export class Resizer {
2019-09-18 12:29:56 +02:00
hoovering = false;
dragging = false;
2019-09-16 21:18:01 +02:00
2019-09-18 12:29:56 +02:00
mouse_pos = [0, 0];
last_drag = [0, 0];
2019-09-16 21:18:01 +02:00
2019-09-17 20:19:04 +02:00
viewbox: number[];
orig_viewbox: number[];
2019-09-16 21:18:01 +02:00
2019-09-18 12:29:56 +02:00
el_box: number[];
2019-09-16 21:18:01 +02:00
2019-09-17 20:19:04 +02:00
scaleX = 1;
scaleY = 1;
2019-09-16 21:18:01 +02:00
2019-09-17 20:19:04 +02:00
constructor(el: HTMLCanvasElement, viewbox: number[], keep_aspect_ratio=false) {
viewbox = [-viewbox[0] - viewbox[2], - viewbox[1] - viewbox[3], viewbox[2], viewbox[3]];
2019-09-17 20:19:04 +02:00
this.viewbox = [...viewbox];
2019-09-18 12:29:56 +02:00
this.el_box = [el.width, el.height];
2019-09-16 21:18:01 +02:00
2019-09-17 20:19:04 +02:00
if (keep_aspect_ratio) {
const or_width = this.viewbox[2];
const or_height = this.viewbox[3];
2019-09-18 12:29:56 +02:00
const width_percentage = this.viewbox[2] / el.width;
const height_percentage = this.viewbox[3] / el.height;
if (width_percentage < height_percentage) {
// width should be larger
this.viewbox[2] = height_percentage * el.width;
2019-09-17 20:19:04 +02:00
} else {
2019-09-18 12:29:56 +02:00
// height should be larger
2019-09-22 09:41:56 +02:00
this.viewbox[3] = width_percentage * el.height;
2019-09-17 20:19:04 +02:00
}
this.viewbox[0] -= (this.viewbox[2] - or_width) / 2;
this.viewbox[1] -= (this.viewbox[3] - or_height) / 2;
2019-09-18 12:29:56 +02:00
this.scaleX = this.viewbox[2] / this.viewbox[3];
2019-09-17 20:19:04 +02:00
}
this.orig_viewbox = [...this.viewbox];
el.addEventListener("mouseenter", this.mouseenter.bind(this), { capture: false, passive: true});
el.addEventListener("mouseleave", this.mouseleave.bind(this), { capture: false, passive: true});
el.addEventListener("mousemove", this.mousemove.bind(this), { capture: false, passive: true});
el.addEventListener("mousedown", this.mousedown.bind(this), { capture: false, passive: true});
el.addEventListener("mouseup", this.mouseup.bind(this), { capture: false, passive: true});
window.addEventListener('wheel', this.wheel.bind(this), { capture: false, passive: true});
}
2019-09-18 12:29:56 +02:00
_clip_viewbox() {
2019-09-17 20:19:04 +02:00
this.viewbox[0] = Math.max(this.viewbox[0], this.orig_viewbox[0]);
this.viewbox[1] = Math.max(this.viewbox[1], this.orig_viewbox[1]);
this.viewbox[0] = Math.min(this.viewbox[0] + this.viewbox[2], this.orig_viewbox[0] + this.orig_viewbox[2]) - this.viewbox[2];
this.viewbox[1] = Math.min(this.viewbox[1] + this.viewbox[3], this.orig_viewbox[1] + this.orig_viewbox[3]) - this.viewbox[3];
}
mouseenter() {
this.hoovering = true;
}
mouseleave() {
this.hoovering = false;
}
mousemove(e: MouseEvent) {
2019-09-18 12:29:56 +02:00
this.mouse_pos = [e.offsetX, this.el_box[1] - e.offsetY];
2019-09-17 20:19:04 +02:00
if (this.dragging) {
2019-09-18 12:29:56 +02:00
const scaleX = this.viewbox[2] / this.el_box[0];
const scaleY = this.viewbox[3] / this.el_box[1];
this.viewbox[0] += (this.last_drag[0] - this.mouse_pos[0]) * scaleX;
this.viewbox[1] += (this.last_drag[1] - this.mouse_pos[1]) * scaleY;
2019-09-17 20:19:04 +02:00
this.last_drag = [...this.mouse_pos];
2019-09-18 12:29:56 +02:00
this._clip_viewbox();
2019-09-17 20:19:04 +02:00
}
}
mousedown() {
this.dragging = true;
this.last_drag = [...this.mouse_pos];
}
mouseup() {
this.dragging = false;
}
wheel(e: WheelEvent) {
if (this.hoovering) {
2019-09-18 12:29:56 +02:00
const delta = e.deltaY > 0 ? 0.1 * this.viewbox[2] : -0.1 * this.viewbox[2];
const dx = delta * this.scaleX;
const dy = delta * this.scaleY;
2019-09-17 20:19:04 +02:00
2019-09-22 09:41:56 +02:00
const mouse_dx = this.mouse_pos[0] / this.el_box[0];
const mouse_dy = this.mouse_pos[1] / this.el_box[1];
this._zoom([dx, dy], [mouse_dx, mouse_dy]);
2019-09-17 20:19:04 +02:00
}
}
2019-09-22 09:41:56 +02:00
_zoom(deltas: number[], center: number[]) {
this.viewbox[2] += deltas[0];
this.viewbox[0] -= deltas[0] * center[0];
this.viewbox[2] = Math.min(this.viewbox[2], this.orig_viewbox[2]);
this.viewbox[3] += deltas[1];
this.viewbox[1] -= deltas[1] * center[1];
this.viewbox[3] = Math.min(this.viewbox[3], this.orig_viewbox[3]);
this._clip_viewbox();
}
2019-09-17 20:19:04 +02:00
get_viewbox(): number[] {
2019-09-19 17:52:48 +02:00
return this.viewbox;
2019-09-17 20:19:04 +02:00
}
get_mouse_pos(): number[] {
return this.mouse_pos;
}
2019-09-16 21:18:01 +02:00
}
2019-09-19 17:52:48 +02:00
export class Mesh {
cells: number[];
positions: number[];
constructor(mesh: any) {
this.cells = mesh.cells.flat();
this.positions = mesh.positions.flat();
}
}
export async function url_to_mesh(url: string): Promise<Mesh> {
return new Promise(function(resolve) {
loadSvg(url, function (err: any, svg: any) {
if (err) throw err;
var svgPath = parsePath(svg);
var mesh = svgMesh3d(svgPath, {
delaunay: false,
scale: 10,
});
resolve(new Mesh(mesh));
});
});
}