CAD-style orbit pivot: how to keep the rotation center close to nearby objects smoothly?

Hi everyone,

I’m building a CAD/GIS-style Three.js viewer and want orbit rotation to behave like in this reference CAD software (Trimble Business Center).

Goal: when rotating, the pivot should stay close to the object near the cursor, so navigation feels smooth around points, lines, arcs, hatches and meshes.

Currently I do this on middle-mouse drag start:

  • raycast terrain / mesh / hatch / arc objects

  • if no mesh hit: search nearby points and lines in screen space

  • for lines: find closest point on the projected 2D segment and interpolate the 3D point

  • fallback: intersect a work plane / view plane

Then I rotate the camera around that pivot manually with quaternions.

It works, but not perfectly:

  • thin lines or small points are easy to miss

  • fallback pivots sometimes feel too far away

  • switching pivots can feel jumpy

  • keeping one pivot for the whole drag is stable, but not always smart

Question:

What is the best strategy for a CAD-like orbit pivot in Three.js?

Would you recommend:

  • updating a “soft pivot” continuously while hovering?

  • raycasting meshes but using screen-space nearest-object search for lines/points?

  • damping/interpolating the pivot?

  • using hysteresis/sticky pivot logic?

  • GPU/depth picking?

  • BVH/spatial index plus screen-space scoring?

I’d like the pivot to feel close to visible geometry without sudden jumps.

are you using OrbitControls and updating the target on mouse down?

Yes and no.

I do use OrbitControls in the viewer, mainly for pan/zoom and for keeping a common controls.target for other UI parts. But the actual middle-mouse orbit is custom.

For MMB drag I do not simply set OrbitControls.target on mouse down and let OrbitControls rotate. On mouse down I resolve a pivot near the cursor, then I rotate the camera around that pivot manually with quaternions. During the drag I keep that pivot stable and update/sync controls.target afterward so the rest of the app stays consistent.

So OrbitControls is present, but the CAD-like cursor pivot orbit is not handled by OrbitControls directly.