@@ -169,8 +169,8 @@ static AxisOrder getAxisOrder(final ImagePlus imp) {
169169 }
170170
171171 /**
172- * Returns the world-space endpoints of the perspective ray through the
173- * current mouse cursor position in the given viewer panel.
172+ * Returns the world-space endpoints of the perspective ray through an arbitrary screen
173+ * point in the given viewer panel.
174174 *
175175 * <p>BVV uses the formula {@code pf = dCam / (dCam + viewerZ)} to project
176176 * world points onto the screen. Inverting that relationship gives the viewer-space
@@ -187,29 +187,29 @@ static AxisOrder getAxisOrder(final ImagePlus imp) {
187187 * @param dCam camera distance in screen-pixel units (from OverlayRenderer.dCam)
188188 * @param nearClip near clip distance in screen-pixel units (>0, <dCam)
189189 * @param farClip far clip distance in screen-pixel units (>0)
190+ * @param screenX screen-space X of the point the ray passes through
191+ * @param screenY screen-space Y of the point the ray passes through
190192 * @return a 2x3 array { nearWorld, farWorld } in world coordinates, or null
191- * if the mouse is outside the display or the viewer state is unavailable
193+ * if the viewer state is unavailable
192194 */
193- static double [][] findClickRay (final VolumeViewerPanel vp ,
195+ private static double [][] rayFromScreenPoint (final VolumeViewerPanel vp ,
194196 final double dCam ,
195197 final double nearClip ,
196- final double farClip ) {
198+ final double farClip ,
199+ final double screenX ,
200+ final double screenY ) {
197201 if (vp == null || dCam <= 0 ) return null ;
198202
199- // Mouse position in display coordinates.
200- final Point mouse = vp .getDisplay ().getComponent ().getMousePosition ();
201- if (mouse == null ) return null ;
202-
203203 final AffineTransform3D t = new AffineTransform3D ();
204204 vp .state ().getViewerTransform (t );
205205
206206 // Screen center = perspective vanishing point.
207207 final double cx = vp .getDisplay ().getWidth () / 2.0 ;
208208 final double cy = vp .getDisplay ().getHeight () / 2.0 ;
209209
210- // Offset of the cursor from screen center.
211- final double dx = mouse . x - cx ;
212- final double dy = mouse . y - cy ;
210+ // Offset of the requested point from screen center.
211+ final double dx = screenX - cx ;
212+ final double dy = screenY - cy ;
213213
214214 // Viewer-space X,Y scale factor at each clip plane:
215215 // at near (viewerZ = -nearClip): scale = 1 - nearClip/dCam
@@ -229,15 +229,59 @@ static double[][] findClickRay(final VolumeViewerPanel vp,
229229
230230 if (SNTUtils .isDebugMode ()) {
231231 SNTUtils .log (String .format (
232- "BVV click- ray: mouse =(%d,%d ) center=(%.1f,%.1f) dCam=%.1f near=%.1f far=%.1f%n"
232+ "BVV ray: screen =(%.1f,%.1f ) center=(%.1f,%.1f) dCam=%.1f near=%.1f far=%.1f%n"
233233 + " nearW=(%.3f,%.3f,%.3f) farW=(%.3f,%.3f,%.3f)" ,
234- mouse . x , mouse . y , cx , cy , dCam , nearClip , farClip ,
234+ screenX , screenY , cx , cy , dCam , nearClip , farClip ,
235235 nearW [0 ], nearW [1 ], nearW [2 ], farW [0 ], farW [1 ], farW [2 ]));
236236 }
237237
238238 return new double [][]{nearW , farW };
239239 }
240240
241+ /**
242+ * Returns the world-space endpoints of the perspective ray through the
243+ * current mouse cursor position in the given viewer panel.
244+ *
245+ * @param vp the BVV viewer panel
246+ * @param dCam camera distance in screen-pixel units (from OverlayRenderer.dCam)
247+ * @param nearClip near clip distance in screen-pixel units (>0, <dCam)
248+ * @param farClip far clip distance in screen-pixel units (>0)
249+ * @return a 2x3 array { nearWorld, farWorld } in world coordinates, or null
250+ * if the mouse is outside the display or the viewer state is unavailable
251+ */
252+ static double [][] findClickRay (final VolumeViewerPanel vp ,
253+ final double dCam ,
254+ final double nearClip ,
255+ final double farClip ) {
256+ if (vp == null ) return null ;
257+ final Point mouse = vp .getDisplay ().getComponent ().getMousePosition ();
258+ if (mouse == null ) return null ;
259+ return rayFromScreenPoint (vp , dCam , nearClip , farClip , mouse .x , mouse .y );
260+ }
261+
262+ /**
263+ * Returns the world-space endpoints of the perspective ray through the center of the
264+ * viewer panel's display, i.e. its optical axis. Unlike {@link #findClickRay}, this
265+ * does not depend on the mouse cursor and is defined even when the pointer is outside
266+ * the display (or the viewer has no focus).
267+ *
268+ * @param vp the BVV viewer panel
269+ * @param dCam camera distance in screen-pixel units (from OverlayRenderer.dCam)
270+ * @param nearClip near clip distance in screen-pixel units (>0, <dCam)
271+ * @param farClip far clip distance in screen-pixel units (>0)
272+ * @return a 2x3 array { nearWorld, farWorld } in world coordinates, or null
273+ * if the viewer state is unavailable
274+ */
275+ static double [][] findCenterRay (final VolumeViewerPanel vp ,
276+ final double dCam ,
277+ final double nearClip ,
278+ final double farClip ) {
279+ if (vp == null ) return null ;
280+ final double cx = vp .getDisplay ().getWidth () / 2.0 ;
281+ final double cy = vp .getDisplay ().getHeight () / 2.0 ;
282+ return rayFromScreenPoint (vp , dCam , nearClip , farClip , cx , cy );
283+ }
284+
241285 /**
242286 * Returns the mip level BVV would choose for the focal plane, replicating
243287 * BVV's own MipmapSizes.bestLevel logic exactly.
0 commit comments