You've already forked vtt-mapper
New Input and Selection manager
This commit is contained in:
178
src/renderer/input.class.ts
Normal file
178
src/renderer/input.class.ts
Normal file
@@ -0,0 +1,178 @@
|
||||
import { Point } from "../physics/common";
|
||||
import Renderer from "./renderer.class";
|
||||
|
||||
const dblClickTiming = 1000;
|
||||
|
||||
export default class Input
|
||||
{
|
||||
static #cursor: Point = {x: 0, y: 0};
|
||||
static #delta: Point = {x: 0, y: 0};
|
||||
static #holdButtons: number;
|
||||
static #previousClickTime: number;
|
||||
|
||||
//Drag util fields
|
||||
static #dragging: boolean = false;
|
||||
static #dragStarted: boolean = false;
|
||||
static #dragInitPos: Point = {x: 0, y: 0};
|
||||
static #dragPrevPos: Point = {x: 0, y: 0};
|
||||
|
||||
static #scroll: number = 0;
|
||||
static #keys: Record<string, boolean> = {};
|
||||
|
||||
static #canvas: HTMLCanvasElement;
|
||||
|
||||
static #clickCb?: (point: Point, button: number) => void;
|
||||
static #dblClickCb?: (point: Point, button: number) => void;
|
||||
static #moveCb?: (point: Point) => void;
|
||||
static #dragStartCb?: (start: Point, button: number) => void;
|
||||
static #dragCb?: (delta: Point, start: Point, end: Point, button: number) => void;
|
||||
static #dragEndCb?: (start: Point, end: Point, button: number) => void;
|
||||
static #wheelCb?: (delta: number) => void;
|
||||
static #inputCb?: () => void;
|
||||
|
||||
static init(canvas: HTMLCanvasElement)
|
||||
{
|
||||
Input.#canvas = canvas;
|
||||
|
||||
canvas.addEventListener('mousedown', Input.#mousedown.bind(Input), false);
|
||||
canvas.addEventListener('mouseup', Input.#mouseup.bind(Input), false);
|
||||
canvas.addEventListener('mousemove', Input.#mousemove.bind(Input), false);
|
||||
|
||||
canvas.addEventListener('wheel', Input.#wheel.bind(Input), false);
|
||||
|
||||
window.addEventListener('keydown', Input.#keydown.bind(Input), false);
|
||||
window.addEventListener('keyup', Input.#keyup.bind(Input), false);
|
||||
|
||||
canvas.addEventListener('contextmenu', (e) => e.preventDefault());
|
||||
}
|
||||
|
||||
static get dragging()
|
||||
{
|
||||
return Input.#dragging && Input.#dragStarted;
|
||||
}
|
||||
static get cursor()
|
||||
{
|
||||
return Input.#cursor;
|
||||
}
|
||||
static get delta()
|
||||
{
|
||||
return Input.#delta;
|
||||
}
|
||||
static get scroll()
|
||||
{
|
||||
return Input.#scroll;
|
||||
}
|
||||
static get keys()
|
||||
{
|
||||
return Input.#keys;
|
||||
}
|
||||
|
||||
static onClick(cb: (point: Point, button: number) => void): void
|
||||
{
|
||||
Input.#clickCb = cb;
|
||||
}
|
||||
static onMove(cb: (point: Point) => void): void
|
||||
{
|
||||
Input.#moveCb = cb;
|
||||
}
|
||||
static onDblClick(cb: (point: Point, button: number) => void): void
|
||||
{
|
||||
Input.#dblClickCb = cb;
|
||||
}
|
||||
static onDragStart(cb: (start: Point, button: number) => void): void
|
||||
{
|
||||
Input.#dragStartCb = cb;
|
||||
}
|
||||
static onDrag(cb: (delta: Point, start: Point, end: Point, button: number) => void): void
|
||||
{
|
||||
Input.#dragCb = cb;
|
||||
}
|
||||
static onDragEnd(cb: (start: Point, end: Point, button: number) => void): void
|
||||
{
|
||||
Input.#dragEndCb = cb;
|
||||
}
|
||||
static onWheel(cb: (delta: number) => void): void
|
||||
{
|
||||
Input.#wheelCb = cb;
|
||||
}
|
||||
static onInput(cb: () => void): void
|
||||
{
|
||||
Input.#inputCb = cb;
|
||||
}
|
||||
|
||||
static #mousedown(e: MouseEvent): void
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
Input.#holdButtons = e.buttons;
|
||||
|
||||
Input.#dragging = true;
|
||||
Input.#dragStarted = false;
|
||||
Input.#dragInitPos = Renderer.screenSpaceToCameraSpace(e.clientX, e.clientY);
|
||||
Input.#dragPrevPos = Renderer.screenSpaceToCameraSpace(e.clientX, e.clientY, true);
|
||||
}
|
||||
static #mousemove(e: MouseEvent): void
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
if(Input.#dragging && !Input.#dragStarted)
|
||||
{
|
||||
Input.#dragStartCb && Input.#dragStartCb(Input.#dragInitPos, e.buttons);
|
||||
Input.#dragStarted = true;
|
||||
}
|
||||
|
||||
const cursor = Renderer.screenSpaceToCameraSpace(e.clientX, e.clientY);
|
||||
const cursorOmitted = Renderer.screenSpaceToCameraSpace(e.clientX, e.clientY, true);
|
||||
|
||||
Input.#cursor = cursor;
|
||||
|
||||
if(Input.dragging)
|
||||
{
|
||||
Input.#delta.x = cursorOmitted.x - this.#dragPrevPos.x;
|
||||
Input.#delta.y = cursorOmitted.y - this.#dragPrevPos.y;
|
||||
|
||||
Input.#dragPrevPos = cursorOmitted;
|
||||
|
||||
Input.#dragCb && Input.#dragCb(Input.#delta, Input.#dragInitPos, Input.#cursor, e.buttons);
|
||||
}
|
||||
|
||||
Input.#moveCb && Input.#moveCb(Input.#cursor);
|
||||
}
|
||||
static #mouseup(e: MouseEvent): void
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
if(Input.dragging)
|
||||
Input.#dragEndCb && Input.#dragEndCb(Input.#dragInitPos, Input.#cursor, Input.#holdButtons);
|
||||
else
|
||||
{
|
||||
Input.#clickCb && Input.#clickCb(Input.#cursor, Input.#holdButtons);
|
||||
const timing = performance.now();
|
||||
|
||||
if(timing - Input.#previousClickTime <= dblClickTiming)
|
||||
Input.#dblClickCb && Input.#dblClickCb(Input.#cursor, Input.#holdButtons);
|
||||
|
||||
Input.#previousClickTime = timing;
|
||||
}
|
||||
|
||||
Input.#holdButtons = e.buttons;
|
||||
Input.#dragging = false;
|
||||
}
|
||||
static #wheel(e: WheelEvent): void
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
Input.#scroll = e.deltaY;
|
||||
Input.#wheelCb && Input.#wheelCb(Input.scroll);
|
||||
}
|
||||
static #keydown(e: KeyboardEvent): void
|
||||
{
|
||||
Input.#keys[e.key] = true;
|
||||
}
|
||||
static #keyup(e: KeyboardEvent): void
|
||||
{
|
||||
Input.#keys[e.key] = false;
|
||||
|
||||
Input.#inputCb && Input.#inputCb();
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,10 @@ export default class Renderer
|
||||
return false;
|
||||
}
|
||||
}
|
||||
static get canvas(): HTMLCanvasElement
|
||||
{
|
||||
return this.renderer.domElement;
|
||||
}
|
||||
static get zoom(): number
|
||||
{
|
||||
return this.#zoom;
|
||||
@@ -60,8 +64,8 @@ export default class Renderer
|
||||
this.camera.position.x += x;
|
||||
this.camera.position.y += y;
|
||||
}
|
||||
static screenSpaceToCameraSpace(x: number, y: number, offset: boolean): Point {
|
||||
return { x: ((x / window.innerWidth - 0.5) * FRUSTUMSIZE * this.aspect - (offset ? this.#pos.x : 0)) / this.#zoom, y: (- (y / window.innerHeight - 0.5) * FRUSTUMSIZE - (offset ? this.#pos.x : 0)) / this.zoom };
|
||||
static screenSpaceToCameraSpace(x: number, y: number, omit: boolean = false): Point {
|
||||
return { x: ((x / window.innerWidth - 0.5) * FRUSTUMSIZE * this.aspect) / this.#zoom + (omit ? 0 : this.#pos.x), y: (- (y / window.innerHeight - 0.5) * FRUSTUMSIZE) / this.zoom + (omit ? 0 : this.#pos.y) };
|
||||
}
|
||||
static #resize(): void
|
||||
{
|
||||
|
||||
89
src/renderer/selector.class.ts
Normal file
89
src/renderer/selector.class.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import * as Three from "three";
|
||||
import Asset from "../assets/asset.class";
|
||||
import { Point } from "../physics/common";
|
||||
import Renderer from "./renderer.class";
|
||||
|
||||
export default class Selector
|
||||
{
|
||||
static #assets: Asset[];
|
||||
static #selected: boolean = false;
|
||||
|
||||
static #previewMesh: Three.Box3Helper;
|
||||
static #selectionMesh: Three.Box3Helper;
|
||||
|
||||
static get selected(): boolean
|
||||
{
|
||||
return Selector.#selected;
|
||||
}
|
||||
static init(): void
|
||||
{
|
||||
Selector.#previewMesh = new Three.Box3Helper(new Three.Box3(), 0x2980B9);
|
||||
Selector.#selectionMesh = new Three.Box3Helper(new Three.Box3(), 0xffffff);
|
||||
|
||||
Renderer.scene.add(Selector.#previewMesh);
|
||||
Renderer.scene.add(Selector.#selectionMesh);
|
||||
|
||||
Selector.hide();
|
||||
}
|
||||
static preview(start: Point, end: Point): void
|
||||
{
|
||||
Selector.hide();
|
||||
|
||||
Selector.#previewMesh.box.setFromArray([start.x, start.y, 0, end.x, end.y, 0]);
|
||||
Selector.#previewMesh.updateMatrix();
|
||||
|
||||
Selector.#previewMesh.visible = true;
|
||||
}
|
||||
static ghostSelect(asset: Asset): void
|
||||
{
|
||||
Selector.#assets = [asset];
|
||||
Selector.hide();
|
||||
|
||||
if(!asset)
|
||||
return;
|
||||
|
||||
Selector.#selected = false;
|
||||
|
||||
Selector.#selectionMesh.box.setFromArray([asset.x1, asset.y1, 0, asset.x2, asset.y2, 0]);
|
||||
Selector.#previewMesh.updateMatrix();
|
||||
|
||||
Selector.#selectionMesh.visible = true;
|
||||
}
|
||||
static select(assets: Asset[]): void
|
||||
{
|
||||
Selector.#assets = assets;
|
||||
Selector.hide();
|
||||
|
||||
if(assets.length <= 0)
|
||||
return;
|
||||
|
||||
Selector.#selected = true;
|
||||
|
||||
Selector.#selectionMesh.box.setFromArray([
|
||||
assets.map(e => e.x1).reduce((p, v) => Math.min(p, v), Infinity),
|
||||
assets.map(e => e.y1).reduce((p, v) => Math.min(p, v), Infinity),
|
||||
0,
|
||||
assets.map(e => e.x2).reduce((p, v) => Math.max(p, v), -Infinity),
|
||||
assets.map(e => e.y2).reduce((p, v) => Math.max(p, v), -Infinity),
|
||||
0
|
||||
]);
|
||||
Selector.#previewMesh.updateMatrix();
|
||||
|
||||
Selector.#selectionMesh.visible = true;
|
||||
}
|
||||
static add(assets: Asset[]): void
|
||||
{
|
||||
Selector.select([...Selector.#assets, ...assets].filter((e, i, a) => a.indexOf(e) === i));
|
||||
}
|
||||
static clear(): void
|
||||
{
|
||||
Selector.#assets = [];
|
||||
|
||||
Selector.hide();
|
||||
}
|
||||
static hide(): void
|
||||
{
|
||||
Selector.#previewMesh.visible = false;
|
||||
Selector.#selectionMesh.visible = false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user