Finding nearest vertex of a Mesh to mouse cursor

Imagine this three.js scene, set up with an OrthographicCamera and OrbitControls:


When the user drags the yellow disc (meant to represent the Sun), the disc needs to move along its yellow circle in response to this action. Here’s the scene from another angle, so you can see the full yellow circle:


So, my event handler must determine which point on this circle is closest to the current cursor position. This yellow circle is a THREE.Mesh, by the way.

I’m using THREE.Raycaster to determine some mouseover events, using its intersectObjects() function, but it’s not clear to me how to find the nearest point of a single object with this Raycaster. I’m guessing there is some simple math I can do after translating the mouse’s position to world co-ordinates. Can someone help me with this? Is Three.js’s Raycaster useful here? If not, how do I determine the nearest point of this mesh?

The full source code is here, if it’s helpful: Search for this.sunDeclination , which corresponds to the yellow circle’s Mesh object.

For a working demo, go here:

For reference, the sun should behave like this: (requires Flash)

As far as I can see at your code, you are using a TorusBufferGeometry to visualize the yellow circle, right? If so, why don’t you just move the sun disc to the next intersection point between the ray and the torus geometry? I’m not sure why you need the nearest vertex for this :thinking:

Maybe this demo helps you further:

I’ve just put together something like the jsfiddle you linked to. intersectObject() only returns intersections of a mesh that the mouse is over. What if I’m dragging the sun and the mouse doesn’t happen to be over the yellow sunDeclination orbit line? That’s where I’m having trouble imagining how to use the Raycaster.

Okay, I see what you mean. I’ve changed the fiddle to something different. It’s not perfect but it should produce your desired result:

The idea is to do a ray intersection test with a plane in which the torus lies. Next, the code searches for the closest point to the torus geometry. This is done via Triangle.closestPointToPoint() for all triangles. Keep in mind that this approach is computationally expensive, so use it carefully.

1 Like

Maybe it’s better to use THREE.Line() or THREE.LineLoop() or THREE.LineSegments() for a circle and set bigger value for raycaster.linePrecision?
But it’s just a thought :slight_smile:

Yeah, this could be a fast alternative. However, normal lines are drawn with a width of one which is not super
user-friendly if users should interact with them (I refer to the visual feedback not to the actual intersection test).

Well, a line could be wrapped with a tube (for better visual), but a raycaster will check for intersection with the line only :thinking:

1 Like

Good idea. This is something that @nikolas could try and see how good it works.

Actually, the line can be even invisible (with the visible wrapping tube), we need it just to find a point :slight_smile:

Thank you, that’s exactly what I’m looking for! I’ll take a closer look at this example and implement it in my scene.

@prisoner849, thanks for the invisible Line tip. I’ll probably end up modifying Mugen87’s solution to do it your way.

This is your scene, so feel free to use any approach you want :slight_smile: :beers:

Haha woops i wasn’t clear. I meant… I’ll use your method, but building off @Mugen87’s. :stuck_out_tongue:

So, this solution is almost working for me. But, the marker isn’t sticking to my torus (the yellow ring). Is this because my torus is rotated?

You can see the marker as a red dot here, is off, though the transparent red plane is in the correct position.

So, in your example @Mugen87, imagine the torus is rotated when certain values in the simulation change, like this:

How would I then apply the same rotation to the triangle code here?

It seems the like the geometry.index needs the rotation info applied to it, or something like that.

Okay, well I solved my issues by applying some rotations to the closestPoint that the mouse position finds. Now it seems to be working perfectly.