mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-01 14:48:12 +08:00
* Added TS. Updated build and linting config. Added types. * [ts-migrate][.] Rename files from JS/JSX to TS/TSX Co-authored-by: ts-migrate <> * [ts-migrate][.] Run TS Migrate Co-authored-by: ts-migrate <> * Corrected files mangled by ts-migrate. * Updated lint config, minor linting. * Re-enabled Husky. * Updated tests and config. * Reverted webpack devtool config. * Removed obsolete snapshots. * Added annotations snap. * Updated tsconfig includes wrt linting. * Removed ts-migrate. Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>
This commit is contained in:
co-authored by
ts-migrate
Timmy Huang
parent
7328cbdbd5
commit
934cc5c69b
@@ -1,180 +0,0 @@
|
||||
import { vec2, mat3 } from "gl-matrix";
|
||||
|
||||
const EPSILON = 0.000001;
|
||||
|
||||
const scaleSpeed = 0.5;
|
||||
const scaleMax = 3.0;
|
||||
const scaleMin = 0.5;
|
||||
const panBound = 0.8;
|
||||
|
||||
// private
|
||||
const scratch0 = new Float32Array(16);
|
||||
const scratch1 = new Float32Array(16);
|
||||
|
||||
function clamp(val, rng) {
|
||||
return Math.max(Math.min(val, rng[1]), rng[0]);
|
||||
}
|
||||
|
||||
class Camera {
|
||||
constructor(canvas) {
|
||||
this.prevEvent = {
|
||||
clientX: 0,
|
||||
clientY: 0,
|
||||
type: 0,
|
||||
};
|
||||
this.canvas = canvas;
|
||||
this.viewMatrix = mat3.create();
|
||||
this.viewMatrixInv = mat3.create();
|
||||
}
|
||||
|
||||
view() {
|
||||
return this.viewMatrix;
|
||||
}
|
||||
|
||||
invView() {
|
||||
return this.viewMatrixInv;
|
||||
}
|
||||
|
||||
distance() {
|
||||
return this.viewMatrix[0];
|
||||
}
|
||||
|
||||
pan(dx, dy) {
|
||||
const m = this.viewMatrix;
|
||||
const dyRange = [
|
||||
-panBound - (m[7] + 1) / m[4],
|
||||
panBound - (m[7] - 1) / m[4],
|
||||
];
|
||||
const dxRange = [
|
||||
-panBound - (m[6] + 1) / m[0],
|
||||
panBound - (m[6] - 1) / m[0],
|
||||
];
|
||||
|
||||
const dxClamped = clamp(dx, dxRange);
|
||||
const dyClamped = clamp(dy, dyRange);
|
||||
if (Math.abs(dxClamped) <= EPSILON && Math.abs(dyClamped) <= EPSILON)
|
||||
return;
|
||||
|
||||
mat3.translate(m, m, [dxClamped, dyClamped]);
|
||||
mat3.invert(this.viewMatrixInv, m);
|
||||
}
|
||||
|
||||
zoomAt(d, x = 0, y = 0) {
|
||||
/*
|
||||
Camera zoom at [x,y]
|
||||
*/
|
||||
const m = this.viewMatrix;
|
||||
const bounds = [-panBound, panBound];
|
||||
x = clamp(x, bounds);
|
||||
y = clamp(y, bounds);
|
||||
|
||||
const dClamped = clamp(d * m[0], [scaleMin, scaleMax]) / m[0];
|
||||
if (Math.abs(1 - dClamped) <= EPSILON) return; // noop request
|
||||
|
||||
mat3.translate(m, m, [x, y]);
|
||||
mat3.scale(m, m, [dClamped, dClamped]);
|
||||
mat3.translate(m, m, [-x, -y]);
|
||||
|
||||
mat3.invert(this.viewMatrixInv, m);
|
||||
}
|
||||
|
||||
/*
|
||||
Event handling
|
||||
*/
|
||||
|
||||
flush(e) {
|
||||
this.prevEvent.type = e.type;
|
||||
this.prevEvent.clientX = e.clientX;
|
||||
this.prevEvent.clientY = e.clientY;
|
||||
}
|
||||
|
||||
localPosition(target, canvasX, canvasY, projectionInvTF) {
|
||||
/*
|
||||
Convert mouse position to local
|
||||
*/
|
||||
const { height, width } = target;
|
||||
const targetRect = target.getBoundingClientRect();
|
||||
canvasX -= targetRect.left;
|
||||
canvasY -= targetRect.top;
|
||||
|
||||
const pos = vec2.fromValues(
|
||||
2 * (canvasX / width) - 1,
|
||||
-2 * (canvasY / height) + 1
|
||||
);
|
||||
if (projectionInvTF) {
|
||||
vec2.transformMat3(pos, pos, projectionInvTF);
|
||||
}
|
||||
vec2.transformMat3(pos, pos, this.invView());
|
||||
return pos;
|
||||
}
|
||||
|
||||
mousePan(e, projectionTF) {
|
||||
const projectionInvTF = mat3.invert(scratch0, projectionTF);
|
||||
const pos = this.localPosition(
|
||||
this.canvas,
|
||||
e.clientX,
|
||||
e.clientY,
|
||||
projectionInvTF
|
||||
);
|
||||
const prev = this.localPosition(
|
||||
this.canvas,
|
||||
this.prevEvent.clientX,
|
||||
this.prevEvent.clientY,
|
||||
projectionInvTF
|
||||
);
|
||||
|
||||
const delta = vec2.sub(scratch1, pos, prev);
|
||||
this.pan(delta[0], delta[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
wheelZoom(e, projectionTF) {
|
||||
const { height } = this.canvas;
|
||||
const { deltaY, deltaMode, clientX, clientY } = e;
|
||||
const scale = scaleSpeed * (deltaMode === 1 ? 12 : 1) * (deltaY || 0);
|
||||
|
||||
const projectionInvTF = mat3.invert(scratch0, projectionTF);
|
||||
const pos = this.localPosition(
|
||||
this.canvas,
|
||||
clientX,
|
||||
clientY,
|
||||
projectionInvTF
|
||||
);
|
||||
this.zoomAt(1 / Math.exp(scale / height), pos[0], pos[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
handleEvent(e, projectionTF) {
|
||||
/*
|
||||
process the event, and return true if camera view changed
|
||||
*/
|
||||
let viewChanged = false;
|
||||
switch (e.type) {
|
||||
case "mousemove": {
|
||||
/* eslint-disable-next-line no-bitwise --- MouseEvent.buttons exposes a bitmask, best acted on with bitops */
|
||||
if (e.buttons & 0x1) {
|
||||
viewChanged = this.mousePan(e, projectionTF);
|
||||
}
|
||||
this.flush(e);
|
||||
break;
|
||||
}
|
||||
|
||||
case "wheel": {
|
||||
viewChanged = this.wheelZoom(e, projectionTF);
|
||||
this.flush(e);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// noop
|
||||
break;
|
||||
}
|
||||
return viewChanged;
|
||||
}
|
||||
}
|
||||
|
||||
function attachCamera(canvas) {
|
||||
return new Camera(canvas);
|
||||
}
|
||||
|
||||
export default attachCamera;
|
||||
Reference in New Issue
Block a user