Fix zooming
This commit is contained in:
parent
8ad5130466
commit
a187ccded2
4 changed files with 57 additions and 11 deletions
|
|
@ -39,6 +39,10 @@ export default new (class App {
|
||||||
this.gui.on("reset", () => {
|
this.gui.on("reset", () => {
|
||||||
this.resetRenderer();
|
this.resetRenderer();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.gui.on("zoomChange", () => {
|
||||||
|
this.scenes.screen.applyCameraZoom();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private resetRenderer() {
|
private resetRenderer() {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ const defaults = {
|
||||||
antSpeed: 1,
|
antSpeed: 1,
|
||||||
antRotationAngle: Math.PI / 30,
|
antRotationAngle: Math.PI / 30,
|
||||||
brushRadius: 20,
|
brushRadius: 20,
|
||||||
|
cameraZoom: 0,
|
||||||
// per-channel pheromone params
|
// per-channel pheromone params
|
||||||
repellentFadeOutFactor: 0.0005,
|
repellentFadeOutFactor: 0.0005,
|
||||||
repellentBlurRadius: 0.05,
|
repellentBlurRadius: 0.05,
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,12 @@ class GUIController {
|
||||||
.add(Config, "brushRadius", 1, 100)
|
.add(Config, "brushRadius", 1, 100)
|
||||||
.name("Brush radius")
|
.name("Brush radius")
|
||||||
.onChange(() => saveConfig());
|
.onChange(() => saveConfig());
|
||||||
|
controlsFolder
|
||||||
|
.add(Config, "cameraZoom")
|
||||||
|
.name("Zoom")
|
||||||
|
.step(0.1)
|
||||||
|
.listen()
|
||||||
|
.onChange(() => this.emit("zoomChange"));
|
||||||
|
|
||||||
simFolder.open();
|
simFolder.open();
|
||||||
controlsFolder.open();
|
controlsFolder.open();
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ export default class ScreenScene extends AbstractScene {
|
||||||
public readonly groundMaterial: THREE.ShaderMaterial;
|
public readonly groundMaterial: THREE.ShaderMaterial;
|
||||||
public readonly pointerPosition: THREE.Vector2 = new THREE.Vector2();
|
public readonly pointerPosition: THREE.Vector2 = new THREE.Vector2();
|
||||||
public drawMode: PointerState = PointerState.None;
|
public drawMode: PointerState = PointerState.None;
|
||||||
private cameraZoomLinear: number = 0;
|
// zoom stored in Config.cameraZoom
|
||||||
private isPointerDown: boolean = false;
|
private isPointerDown: boolean = false;
|
||||||
public renderWidth: number = 1;
|
public renderWidth: number = 1;
|
||||||
public renderHeight: number = 1;
|
public renderHeight: number = 1;
|
||||||
|
|
@ -90,8 +90,9 @@ export default class ScreenScene extends AbstractScene {
|
||||||
this.renderer.canvas.addEventListener("pointerdown", (e) => {
|
this.renderer.canvas.addEventListener("pointerdown", (e) => {
|
||||||
this.isPointerDown = true;
|
this.isPointerDown = true;
|
||||||
|
|
||||||
raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1;
|
const rect = this.renderer.canvas.getBoundingClientRect();
|
||||||
raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
raycastVector.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
raycastVector.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
|
||||||
raycaster.setFromCamera(raycastVector, this.camera);
|
raycaster.setFromCamera(raycastVector, this.camera);
|
||||||
|
|
||||||
|
|
@ -105,17 +106,18 @@ export default class ScreenScene extends AbstractScene {
|
||||||
|
|
||||||
this.renderer.canvas.addEventListener("pointermove", (e) => {
|
this.renderer.canvas.addEventListener("pointermove", (e) => {
|
||||||
if (this.isPointerDown) {
|
if (this.isPointerDown) {
|
||||||
|
const rect = this.renderer.canvas.getBoundingClientRect();
|
||||||
const dx = e.movementX;
|
const dx = e.movementX;
|
||||||
const dy = e.movementY;
|
const dy = e.movementY;
|
||||||
|
|
||||||
this.camera.position.x -=
|
this.camera.position.x -= dx / rect.height / this.camera.zoom;
|
||||||
dx / window.innerHeight / this.camera.zoom;
|
this.camera.position.y += dy / rect.height / this.camera.zoom;
|
||||||
this.camera.position.y +=
|
this.clampCamera();
|
||||||
dy / window.innerHeight / this.camera.zoom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
raycastVector.x = (e.clientX / window.innerWidth) * 2 - 1;
|
const rect = this.renderer.canvas.getBoundingClientRect();
|
||||||
raycastVector.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
raycastVector.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
||||||
|
raycastVector.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
||||||
|
|
||||||
raycaster.setFromCamera(raycastVector, this.camera);
|
raycaster.setFromCamera(raycastVector, this.camera);
|
||||||
|
|
||||||
|
|
@ -136,9 +138,11 @@ export default class ScreenScene extends AbstractScene {
|
||||||
});
|
});
|
||||||
|
|
||||||
this.renderer.canvas.addEventListener("wheel", (e) => {
|
this.renderer.canvas.addEventListener("wheel", (e) => {
|
||||||
this.cameraZoomLinear -= e.deltaY * 0.001;
|
Config.cameraZoom -= e.deltaY * 0.001;
|
||||||
|
Config.cameraZoom = Math.max(-1, Config.cameraZoom);
|
||||||
|
|
||||||
this.updateCameraZoom();
|
this.updateCameraZoom();
|
||||||
|
this.clampCamera();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("keydown", (e) => {
|
window.addEventListener("keydown", (e) => {
|
||||||
|
|
@ -168,10 +172,36 @@ export default class ScreenScene extends AbstractScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateCameraZoom() {
|
private updateCameraZoom() {
|
||||||
this.camera.zoom = 2 ** this.cameraZoomLinear;
|
this.camera.zoom = 2 ** Config.cameraZoom;
|
||||||
this.camera.updateProjectionMatrix();
|
this.camera.updateProjectionMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private clampCamera() {
|
||||||
|
// world plane spans from -0.5 to 0.5 in both axes
|
||||||
|
// visible half-extents from camera center
|
||||||
|
const halfW =
|
||||||
|
(this.camera.right - this.camera.left) / 2 / this.camera.zoom;
|
||||||
|
const halfH =
|
||||||
|
(this.camera.top - this.camera.bottom) / 2 / this.camera.zoom;
|
||||||
|
|
||||||
|
// camera center can go at most to world edge + half viewport,
|
||||||
|
// but never so far that the world leaves the screen entirely.
|
||||||
|
// when zoomed out (halfW > 0.5), the world fits in view — lock to center.
|
||||||
|
const minX = halfW >= 0.5 ? 0 : -0.5 + halfW;
|
||||||
|
const maxX = halfW >= 0.5 ? 0 : 0.5 - halfW;
|
||||||
|
const minY = halfH >= 0.5 ? 0 : -0.5 + halfH;
|
||||||
|
const maxY = halfH >= 0.5 ? 0 : 0.5 - halfH;
|
||||||
|
|
||||||
|
this.camera.position.x = Math.max(
|
||||||
|
minX,
|
||||||
|
Math.min(maxX, this.camera.position.x),
|
||||||
|
);
|
||||||
|
this.camera.position.y = Math.max(
|
||||||
|
minY,
|
||||||
|
Math.min(maxY, this.camera.position.y),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private createInstancedAntsMesh() {
|
private createInstancedAntsMesh() {
|
||||||
if (this.ants) {
|
if (this.ants) {
|
||||||
this.remove(this.ants);
|
this.remove(this.ants);
|
||||||
|
|
@ -216,4 +246,9 @@ export default class ScreenScene extends AbstractScene {
|
||||||
}
|
}
|
||||||
|
|
||||||
public update() {}
|
public update() {}
|
||||||
|
|
||||||
|
public applyCameraZoom() {
|
||||||
|
this.updateCameraZoom();
|
||||||
|
this.clampCamera();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue