Mandroid
Mandroid

Reputation: 7524

Verifying if a point is inside a cube in three.js

I have created a cube as:

var cubeGeometry = new THREE.BoxGeometry( 1, 1, 1 );
var cubeMaterial = new THREE.MeshLambertMaterial( { color: 
       0xffff00,wireframe: true } );
var cube = new THREE.Mesh( cubeGeometry, cubeMaterial );
cube.position.x = p.x;
cube.position.y = p.y;
cube.position.z = p.z;
scene.add(cube);

p is a input point to my function. So this code creates a cube at position p and adds it to the scene.

How can I check that some point,say A, lies inside this cube? I couldn't find any helper function like containsPoint etc for Three.Mesh. I may do some additional checks to verify, but I am looking for a Three.js function.

Upvotes: 1

Views: 2235

Answers (3)

manthrax
manthrax

Reputation: 5036

@prisoner849 Your solution doesn't work if the box is rotated. Here's an illustration of the problem. I render both solutions and you can see where the Box3 version breaks with the rotated cube, whereas the analytical once works.

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(2, 5, 10);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

scene.add(new THREE.GridHelper(10, 10));
var boxDimensions = new THREE.Vector3(2,2,2);
var cube = new THREE.Mesh(new THREE.BoxGeometry(boxDimensions.x,boxDimensions.y,boxDimensions.z), new THREE.MeshBasicMaterial({
  color: "aqua",
  wireframe: true
}));
cube.position.set(0, 1, 0);
cube.rotation.y = Math.PI*0.25;
scene.add(cube);

var pointA = new THREE.Vector3(0.95, 0.95, 0.95);

var pointC = new THREE.Vector3(-0.65, 0.65, -0.65);
var pa = point(pointA, 0x00ff00);

var pc = point(pointC, 0x00ff00);

function point(point, color) {
  p = new THREE.Mesh(new THREE.SphereGeometry(0.25, 4, 2), new THREE.MeshBasicMaterial({
    color: color
  }));
  p.position.copy(point);
  scene.add(p);
  return p;
}

var bb = new THREE.Box3(); // for re-use
bb.setFromObject(cube);
console.log(bb);


function correctPointInBox(pt,cube,boxDim){
 cube.updateMatrixWorld(); //Make sure the object matrix is current with the position/rotation/scaling of the object...
var localPt = cube.worldToLocal(pt.clone());  //Transform the point from world space into the objects space
if(Math.abs(localPt.x)<=boxDim.x*0.5&&Math.abs(localPt.y)<=boxDim.y*0.5&&Math.abs(localPt.z)<=boxDim.z*0.5)
return true;
else
return false;
}


render();

function render() {
   pa.position.x = Math.sin(performance.now()*0.001)*2;
   pc.position.z = Math.cos(performance.now()*0.001)*2;

   if(bb.containsPoint(pa.position))
       pa.material.color.set("red")
   else 
       pa.material.color.set("green")

    if(correctPointInBox(pc.position,cube,boxDimensions))
         pc.material.color.set("red")
    else 
        pc.material.color.set("green")

  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 0

prisoner849
prisoner849

Reputation: 17596

You can create THREE.Box3() instance, using its .setFromObject() the cube as the parameter, then call .containsPoint(), passing the point you want to check as the parameter to this method:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(2, 5, 10);
var renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

scene.add(new THREE.GridHelper(10, 10));

var cube = new THREE.Mesh(new THREE.BoxGeometry(2, 2, 2), new THREE.MeshBasicMaterial({
  color: "aqua",
  wireframe: true
}));
cube.position.set(0, 1, 0);
scene.add(cube);

var pointA = new THREE.Vector3(0, 1, 0);
var pointB = new THREE.Vector3(2, 1, 0);
point(pointA, 0x00ff00);
point(pointB, "yellow");

function point(point, color) {
  p = new THREE.Mesh(new THREE.SphereGeometry(0.25, 4, 2), new THREE.MeshBasicMaterial({
    color: color
  }));
  p.position.copy(point);
  scene.add(p);
}

var bb = new THREE.Box3(); // for re-use
bb.setFromObject(cube);
console.log(bb);
console.log(bb.containsPoint(pointA), bb.containsPoint(pointB));

render();

function render() {
  requestAnimationFrame(render);
  renderer.render(scene, camera);
}
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Upvotes: 3

manthrax
manthrax

Reputation: 5036

cube.updateMatrixWorld(); //Make sure the object matrix is current with the position/rotation/scaling of the object...
var localPt = cube.worldToLocal(yourPoint.clone());  //Transform the point from world space into the objects space
if(Math.abs(localPt.x)<=0.5&&Math.abs(localPt.y)<=0.5&&Math.abs(localPt.z)<=0.5)
    console.log("Point is inside!"); //Check if all the axis are within the size of the cube.. if your cube sizes arent 1,1,1, you'll have to adjust these checks to be half of width/height/depth..

Something like that?

Upvotes: 2

Related Questions