SourBanana
SourBanana

Reputation: 13

Why is the bounding box my code generates always at the origin, and not rendering at the cursor position when mouse is down?

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

Answers (1)

量子蔷薇
量子蔷薇

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

Related Questions