How to snap a plane to the face of an object?

Hi, I am trying to create a plane matching the parameters(size and position) of the face of an unknown object in the scene, by using mouse clicks, Is this possible? and are there any reference to related queries or guides?

any help will be much appreciated!

When you cast a ray to hit the face, the intersection include the point and normal.
Raycaster#intersectObject – three.js docs (threejs.org)

You should be able to apply this to you plane to have it match the position and orientation of the face

3 Likes

Piggybacking on what @anidivr said, the raycaster will give you back a position on the surface, and the normal… the outward facing vector for that face, that you can add to the point, and then use .lookAt to make your object point toward…
something like…

let hits = raycast ( ...   do the mouse raycast here...

if(hits[0]){ // got a hit on an object..
plane.position.copy( hits[0].position );  //Put the plane at the hit point
plane.lookAt(hits[0].position.clone().add(rayHit.normal); //Make the plane rotate toward the normal, by adding normal to position and plane.lookAt that point.
}

Hi @manthrax ! I tried your solution but the plane seems to be set at the initial position, here’s my snippet:

raycaster.setFromCamera(mouse, camera);
    const plane = new THREE.Plane(face, 0);
    const helper = new THREE.PlaneHelper(plane, 5);
    const intersects = raycaster.intersectObjects(scene.children, false);
    if (intersects.length > 0) {
        console.log("plane and intersects: ", helper, intersects[0]);
        helper.position.copy(intersects[0].object.position);
        helper.lookAt(intersects[0].object.position.clone().add(intersects[0].object.normalMatrix));
        scene.add(helper);
    }

Let me know If I have missed something or the code is wrong at any place

Thanks!

I think you want to position the plane, not the helper… the helper will snap back to the object its tracking, which is the plane.

Ohh and I misunderstood what you meant by Plane…

I was thinking like a plane mesh…
so like:

let plane = new THREE.Mesh(new THREE.PlaneGeometry(),new THREE.MeshStandardMaterial({side:THREE.DoubleSide))

scene.remove(plane) //Remove the plane before we raycast so we dont hit it with raycast..
raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(scene.children, false);
    if (intersects.length > 0) {
        console.log("plane and intersects: ", plane, intersects[0]);
scene.add(plane)       //Add the plane back..
plane.position.copy(intersects[0].object.position);
       plane.lookAt(intersects[0].object.position.clone().add(intersects[0].object.normal));
    }

No problem, plane has only the normal data, so thought of using a helper

Ya… also you had a typo in there with “.normalMatrix” instead of “.normal”

Alright, I tried your solution and I see a plane on the middle (at 0,0,0), I might have to go through my code if there’s something else causing this issue, and here’s the screenshot of the result:

I had a typo… its the rayhit.point not .position…

Glitch :・゚✧ – code at the bottom of script.js

https://garnet-factual-orbit.glitch.me – view only link…

here’s the code that worked for me:


let sphere = new THREE.Mesh(new THREE.SphereGeometry(),new THREE.MeshStandardMaterial());
scene.add(sphere);
let hitPlane = new THREE.Mesh(new THREE.PlaneGeometry(),new THREE.MeshStandardMaterial({color:'red'}));
updateTasks.push((time)=>{
  if(raycasting.startHit){
    scene.add(hitPlane);
    hitPlane.position.copy(raycasting.startHit.point)
    hitPlane.lookAt(raycasting.startHit.point.clone().add(raycasting.startHit.normal))
  }
})

In that snippet above. I don’t show the raycasting bits… but they are in the glitch if you need to see that too. You don’t have to use the same flow that I did… just replace with your raycast + hit setup.

The only tricky bit to manage is that once you place the plane on the sphere, you will start getting ray hits with the plane instead… so you can either specifically raycast against just the target object… or… remove the plane object before doing the raycast, and then only add it back if you get a hit.

This solution works, but I need a Plane instead of a planeGeometry, and I am unable to change the position of a plane, is it impossible to position the plane?

THREE.Plane doesn’t have a position.

It’s " A two dimensional surface that extends infinitely in 3d space, represented in Hessian normal form by a unit length normal vector and a constant."

So it’s a plane normal, and a distance to the origin along that plane normal.
But you can create a Plane from the planeMesh something like:

let normal = new THREE.Vector3(0,0,1).applyQuaternion(planeMesh.quaternion)
let dist = planeMesh.position.dot(normal);
let plane = new THREE.Plane( normal, dist )

Can you please provide a reference to what planeMesh is here?

Looks at this code in DragControls. It essentially does what you are trying to do