interaction = {
reset;
const halfTurn = Math.PI / 2;
const rot = (dx, dy) =>
new THREE.Quaternion().setFromEuler(
new THREE.Euler(dx * halfTurn, dy * halfTurn, 0)
);
const rotations = {
left: rot(0, 1),
right: rot(0, -1),
up: rot(1, 0),
down: rot(-1, 0)
};
const localAxes = (obj3d) => {
let axes = [new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3()];
obj3d.matrix.extractBasis(...axes);
return axes;
};
const inFront = () =>
positionToCell(camera.position.clone().sub(localAxes(camera)[2]));
const behind = () =>
positionToCell(camera.position.clone().add(localAxes(camera)[2]));
const turnLeft = () => {
animationManager.rotate(camera, rotations.left, render);
};
const turnRight = () => {
animationManager.rotate(camera, rotations.right, render);
};
const moveAhead = () => {
animationManager.translate(camera, cellToPosition(inFront()), render);
};
const moveBack = () => {
animationManager.translate(camera, cellToPosition(behind()), render);
};
const switchCameras = () => {
controls.enabled = !controls.enabled;
render();
};
const keyboardCallback = (e) => {
e.preventDefault();
switch (e.key) {
case "ArrowLeft":
turnLeft();
break;
case "ArrowRight":
turnRight();
break;
case "ArrowUp":
moveAhead();
break;
case "ArrowDown":
moveBack();
break;
case "Tab":
switchCameras();
break;
case "Delete":
case "Backspace":
case " ":
default:
break;
}
};
const mouseCallback = (e) => {
if (controls.enabled) return;
if (e.offsetX < canvas.width / 3) turnLeft();
else if (e.offsetX > (canvas.width / 3) * 2) turnRight();
else if (e.offsetY < canvas.height / 2) moveAhead();
else moveBack();
};
canvas.addEventListener("keydown", keyboardCallback);
canvas.addEventListener("mousedown", mouseCallback);
invalidation.then(() => {
canvas.removeEventListener("keydown", keyboardCallback);
canvas.removeEventListener("mousedown", mouseCallback);
});
for (let frame = 1; ; frame++) {
if (animationManager.animMap.size > 0) {
render();
yield `${frame} rendered`;
} else yield `${frame} (no render)`;
}
}