Reputation: 13
I am trying to create a bounding box zoom, over a three js render, where the user can drag a box and then the camera will zoom on the bounded area. However, the box is not generating correctly. ` public dragZoom(value: boolean): void { const geometry = new THREE.PlaneGeometry(1, 1); const material = new THREE.MeshBasicMaterial({ color: 0x808080, opacity: 0.1, transparent: true, });
let mesh = new THREE.Mesh(geometry, material);
let isDragging = false;
let initialCursorPos = new THREE.Vector2();
let initialBoxPos = new THREE.Vector3();
const borderGeometry = new THREE.EdgesGeometry(geometry);
const borderMaterial = new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 2 });
const borderLines = new THREE.LineSegments(borderGeometry, borderMaterial);
mesh.add(borderLines);
const onMouseDown = (event: MouseEvent) => {
isDragging = true;
initialCursorPos.x = event.clientX;
initialCursorPos.y = event.clientY;
initialBoxPos.x = (event.clientX / window.innerWidth) * 2 - 1;
initialBoxPos.y = -(event.clientY / window.innerHeight) * 2 + 1;
mesh.position.set(initialBoxPos.x, initialBoxPos.y, 0);
this.scene.add(mesh);
};
const onMouseMove = (event: MouseEvent) => {
if (isDragging) {
const deltaX = (event.clientX - initialCursorPos.x) / 40; // Divide by a smaller value for less aggressive scaling
const deltaY = (event.clientY - initialCursorPos.y) / 40; // Divide by a smaller value for less aggressive scaling
mesh.scale.set(deltaX, deltaY, 1);
const boxSize = new THREE.Box3().setFromObject(mesh).getSize(new THREE.Vector3());
const width = boxSize.x;
const height = boxSize.y;
const newX = initialBoxPos.x + (width / 2) * Math.sign(deltaX);
const newY = initialBoxPos.y - (height / 2) * Math.sign(deltaY);
mesh.position.set(newX, newY, 0);
}
};
const onMouseUp = () => {
if (isDragging) {
isDragging = false;
const boundingBox = new THREE.Box3().setFromObject(mesh);
const boxSize = boundingBox.getSize(new THREE.Vector3());
const cameraAspect = this.camera.aspect;
const horizontalPadding = (boxSize.x - boxSize.y * cameraAspect) / 2;
const verticalPadding = (boxSize.y - boxSize.x / cameraAspect) / 2;
this.controls.fitToBox(mesh, true, {
paddingTop: verticalPadding,
paddingBottom: verticalPadding,
paddingLeft: horizontalPadding,
paddingRight: horizontalPadding,
});
this.scene.remove(mesh);
mesh = null;
}
};
this.renderer.domElement.addEventListener('mousedown', onMouseDown);
this.renderer.domElement.addEventListener('mousemove', onMouseMove);
this.renderer.domElement.addEventListener('mouseup', onMouseUp);
}`
I tried using mesh.position, but it doesn't seem to affect anything.
Upvotes: 0
Views: 37
Reputation: 1
In most cases, screen coordinates cannot be directly used as the coordinates for entities within a scene.
mesh.position.set(initialBoxPos.x, initialBoxPos.y, 0);
You need to check your camera; it is very likely that 1 unit of length in the scene is not the same as 1 pixel on the screen.
You need to convert screen coordinates into scene coordinates, which depends on the camera, like this:
const canvasBounds = canvas.getBoundingClientRect();
const ndc = {
x: ((clientX - canvasBounds.left) / canvas.width) * 2 - 1,
y: (-(clientY - canvasBounds.top) / canvas.height) * 2 + 1,
z: 0,
};
const worldPosition = new THREE.Vector3(ndc.x, ndc.y, ndc.z);
worldPosition.unproject(camera);
The meaning of NDC is Normalized Device Coordinates. All coordinates in NDC space range from [−1, 1]
Upvotes: 0