@itk-viewer/element
Version:
Web Component for multi-dimensional viewer
145 lines • 5.26 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { LitElement, html } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { quat, vec3 } from 'gl-matrix';
import { createArcballCamera } from '@itk-viewer/arcball';
import { SelectorController } from 'xstate-lit';
const PAN_SPEED = 1;
const ZOOM_SPEED = 0.001;
const bindCamera = (camera, viewport, onUpdate) => {
let width = viewport.clientWidth;
let height = viewport.clientHeight;
const updateView = () => {
onUpdate(camera.center, camera.rotation, camera.distance);
};
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
width = entry.contentRect.width;
height = entry.contentRect.height;
}
});
resizeObserver.observe(viewport);
let rotate = false;
let pan = false;
let scale = false;
const onPointerDown = (e) => {
if (e.button === 0) {
rotate = true;
}
else if (e.button === 1) {
scale = true;
}
else if (e.button === 2) {
pan = true;
}
};
viewport.addEventListener('pointerdown', onPointerDown);
const onPointerUp = (e) => {
if (e.button === 0) {
rotate = false;
}
else if (e.button === 1) {
scale = false;
}
else if (e.button === 2) {
pan = false;
}
};
window.addEventListener('pointerup', onPointerUp);
let prevPointerX = 0;
let prevPointerY = 0;
const onPointerMove = (e) => {
const pointerX = e.offsetX;
const pointerY = e.offsetY;
if (rotate) {
camera.rotate([pointerX / width - 0.5, pointerY / height - 0.5], [prevPointerX / width - 0.5, prevPointerY / height - 0.5]);
}
if (pan) {
camera.pan([
(PAN_SPEED * (pointerX - prevPointerX)) / width,
(PAN_SPEED * (pointerY - prevPointerY)) / height,
]);
}
if (scale) {
const d = pointerY - prevPointerY;
if (d)
camera.distance *= Math.exp(d / height);
}
prevPointerX = pointerX;
prevPointerY = pointerY;
if (!rotate && !pan && !scale)
return;
updateView();
};
viewport.addEventListener('pointermove', onPointerMove);
const onWheel = (e) => {
camera.zoom(ZOOM_SPEED * camera.distance * e.deltaY);
updateView();
};
viewport.addEventListener('wheel', onWheel, { passive: false });
const preventDefault = (e) => e.preventDefault();
viewport.addEventListener('contextmenu', preventDefault);
const unBind = () => {
resizeObserver.disconnect();
viewport.removeEventListener('pointerdown', onPointerDown);
window.removeEventListener('pointerup', onPointerUp);
viewport.removeEventListener('pointermove', onPointerMove);
viewport.removeEventListener('wheel', onWheel);
viewport.removeEventListener('contextmenu', preventDefault);
};
return unBind;
};
let ItkCamera = class ItkCamera extends LitElement {
constructor() {
super();
this.cameraController = createArcballCamera([0, 0, -1], [0, 0, 0], [0, 1, 0]);
}
connectedCallback() {
super.connectedCallback();
this.unBind = bindCamera(this.cameraController, this, (center, rotation, distance) => {
if (!this.actor)
return;
this.actor.send({
type: 'setPose',
pose: {
center,
rotation,
distance,
},
});
});
}
disconnectedCallback() {
super.disconnectedCallback();
this.unBind?.();
}
willUpdate(changedProperties) {
if (changedProperties.has('actor') && this.actor) {
this.pose = new SelectorController(this, this.actor, (state) => state?.context.pose);
}
if (this.pose?.value !== this.oldPose) {
this.oldPose = this.pose?.value;
if (this.pose?.value) {
vec3.copy(this.cameraController.center, this.pose.value.center);
quat.copy(this.cameraController.rotation, this.pose.value.rotation);
this.cameraController.distance = this.pose.value.distance;
}
}
}
render() {
return html `<slot></slot>`;
}
};
__decorate([
property({ attribute: false })
], ItkCamera.prototype, "actor", void 0);
ItkCamera = __decorate([
customElement('itk-camera')
], ItkCamera);
export { ItkCamera };
//# sourceMappingURL=itk-camera.js.map