Xander Luciano
Xander Luciano

Reputation: 3883

Three JS Raycasting - Find point closest to cursor

Three.js r85

When raycasting with Three JS, a series of points is returned, and I'd like to find the point that is closest to the cursor. The first point returned seems to be the point that is closest to the camera.

Is there a way to find the distance between the cursor position and a point?

Here's the code I'm using to debug this right now:

var raygun = new THREE.Raycaster();
raygun.setFromCamera(mouse, camera);
var hits = raygun.intersectObjects([plotpoints]);

if (hits.length > 0) {
    scope.remove(dotPlot);
    scope.remove(dotPlot2);

    // All points except the first one - Grey
    dotGeo = new THREE.Geometry();
    for (var i=1; i < hits.length; i++) {
        dotGeo.vertices.push(plotpoints.geometry.vertices[hits[i].index]);
    }
    dotPlot = new THREE.Points(dotGeo, dotMat);
    scope.add(dotPlot);

    // First point - Orange
    var geo2 = new THREE.Geometry();
    geo2.vertices.push(plotpoints.geometry.vertices[hits[0].index]);
    dotPlot2 = new THREE.Points(geo2, dotMat2);
    scope.add(dotPlot2);

    scope.render();
}

And here's what I'm seeing:

First point is not close to cursor

Upvotes: 2

Views: 2208

Answers (1)

Xander Luciano
Xander Luciano

Reputation: 3883

Ah, figured it out with math!

First thing to note is that hits[].points returns a point directly under the cursor, but it doesn't "snap" to points.

In order to get the actual position of the point, we need to use hits[].index first to get the index number of the point/vertex we hit. We can then access that vertex directly by using GEOMETRY.vertices[] which returns a THREE.Vector3 of the vertex point we hit with our raycast.

So by feeding in the index, we can get the exact position of each vertex hit by our raycast:
GEOMETRY.vertices[hits[i].index]

This provides rudimentary "snapping" to vertices.

Note: When using THREE.LineSegments, the result will always be the starting point, and not the ending point. To get the ending point, you can just add 1 to the index value:
GEOMETRY.vertices[hits[i+1].index]

To snap directly to the vertex closest to the cursor, we need to find the vertex that has the shortest perpendicular distance from the raycaster's ray. To do this we use a cross product of 2 vectors. This is more of a math concept than a programming concept though, so if you want to understand the why behind this, look up something like: perpendicular distance from a point to a line

I just took the code from this question and translated it: http://answers.unity3d.com/questions/568773/shortest-distance-from-a-point-to-a-vector.html

And the end result:

// Variables to record and compare
var smallestDist = 99;
var smallestPointIndex = 0;

// Declare variables outside of loop to save memory
var m_ray = raycaster.ray;
var raydir = m_ray.direction; 
var origin = m_ray.origin;
var hitray = new THREE.Vector3(0,0,0);
var dist = 1;

// Loop over all points to find the closest
for (var i=0; i<hits.length; i++){
    // Math is magic
    hitray.subVectors(plotpoints.geometry.vertices[hits[i].index], origin);
    dist = new THREE.Vector3().crossVectors(raydir, hitray).lengthSq();

    // Record the closest point
    if (dist < smallestDist) {
        smallestDist = dist;
        smallestPointIndex = i;
    }
}

// Now we can use that single point

Here's the result :)

Success

Upvotes: 1

Related Questions