@@ -451,17 +451,14 @@ private void goToRow(final int tableRow) {
451451 // viewer mode: animate camera to marker world position
452452 flyTo (tableRow );
453453 } else {
454+ // Classic mode: move the image windows and any open Bvv/Bdv viewer
455+ final Bookmark b = model .getDataList ().get (table .convertRowIndexToModel (tableRow ));
456+ final int nViewers = flyOpenViewers (b );
454457 final ImagePlus imp = sntui .plugin .getImagePlus ();
455458 if (imp == null ) {
456- noImageOpenError ();
459+ if ( nViewers == 0 ) noImageOpenError ();
457460 } else {
458- goTo (tableRow , imp );
459- if (!sntui .plugin .getSinglePane ()) {
460- final ImagePlus zyImp = sntui .plugin .getImagePlus (SNT .ZY_PLANE );
461- if (zyImp != null ) goTo (tableRow , zyImp , SNT .ZY_PLANE );
462- final ImagePlus xzImp = sntui .plugin .getImagePlus (SNT .XZ_PLANE );
463- if (xzImp != null ) goTo (tableRow , xzImp , SNT .XZ_PLANE );
464- }
461+ goToAllPlanes (sntui .plugin , b , false );
465462 }
466463 }
467464 }
@@ -1303,7 +1300,7 @@ public void actionPerformed(final ActionEvent e) {
13031300 viewer .promptGoTo ();
13041301 return ;
13051302 }
1306- if (sntui .plugin .getImagePlus () == null ) {
1303+ if (sntui .plugin .getImagePlus () == null && sntui . getOpenBigViewers (). isEmpty () ) {
13071304 noImageOpenError ();
13081305 return ;
13091306 }
@@ -1317,13 +1314,8 @@ public void actionPerformed(final ActionEvent e) {
13171314 final PointInImage pim = SNTPoint .fromString (pos );
13181315 final Bookmark b = new Bookmark (String .format ("%.2f, %.2f, %.2f" , pim .x , pim .y , pim .z ),
13191316 pim .x , pim .y , pim .z , 1 , 1 );
1320- goTo (b , sntui .plugin .getImagePlus (), SNT .XY_PLANE );
1321- if (!sntui .plugin .getSinglePane ()) {
1322- final ImagePlus zyImp = sntui .plugin .getImagePlus (SNT .ZY_PLANE );
1323- if (zyImp != null ) goTo (b , zyImp , SNT .ZY_PLANE );
1324- final ImagePlus xzImp = sntui .plugin .getImagePlus (SNT .XZ_PLANE );
1325- if (xzImp != null ) goTo (b , xzImp , SNT .XZ_PLANE );
1326- }
1317+ flyOpenViewers (b );
1318+ goToAllPlanes (sntui .plugin , b , false );
13271319 sntui .showStatus (String .format ("Zoomed to %s" , b .label ), true );
13281320 } catch (final Throwable ex ) {
13291321 getGuiUtils ().error ("Could not extract a valid location from \" " + pos + "\" ." );
@@ -1367,11 +1359,14 @@ private boolean noBookmarksError() {
13671359 return false ;
13681360 }
13691361
1370- private void goTo (final int row , final ImagePlus imp , final int plane ) {
1371- goTo (model .getDataList ().get (table .convertRowIndexToModel (row )), imp , plane );
1372- }
13731362
1374- private void goTo (final Bookmark b , final ImagePlus imp , final int plane ) {
1363+ /**
1364+ * Moves the display of an image to a bookmark
1365+ *
1366+ * @param silent if true, no error is displayed if the bookmark lies outside the image
1367+ * @return true if the bookmark is within the image (and the display was moved)
1368+ */
1369+ private boolean goTo (final Bookmark b , final ImagePlus imp , final int plane , final boolean silent ) {
13751370 assert imp != null ;
13761371
13771372 // Bookmark coordinates are always world/calibrated; convert to pixel space, which is
@@ -1400,33 +1395,80 @@ private void goTo(final Bookmark b, final ImagePlus imp, final int plane) {
14001395 }
14011396 };
14021397
1403- if (viewX > imp .getWidth () || viewY > imp .getHeight ()) {
1398+ final boolean outsideXY = viewX < 0 || viewY < 0 || viewX > imp .getWidth () || viewY > imp .getHeight ();
1399+ // Z is only checked on the main XY plane: side views show all Z by definition
1400+ final boolean outsideZ = plane == SNT .XY_PLANE && (pz < 0 || pz >= imp .getNSlices ());
1401+ if (outsideXY || outsideZ ) {
14041402 // Only show error for the main XY plane
1405- if (plane == SNT .XY_PLANE ) {
1406- sntui .guiUtils .error ("Location is outside image XY dimensions" );
1403+ if (plane == SNT .XY_PLANE && !silent ) {
1404+ final SNT snt = snt ();
1405+ final String what = (snt != null && snt .isMaterializedCrop ()) ? "the materialized crop" : "image" ;
1406+ getGuiUtils ().error ((outsideXY && outsideZ ) ? "Location is outside " + what + " bounds"
1407+ : (outsideXY ) ? "Location is outside " + what + " XY dimensions"
1408+ : "Location is outside " + what + " Z range" );
14071409 }
1408- return ;
1410+ return false ;
14091411 }
14101412 if (plane == SNT .XY_PLANE ) {
14111413 // NB: ImagePlus#setPosition uses 1-based slice indices; pz is 0-based (see worldToPixel)
14121414 imp .setPosition (b .c , (int ) pz + 1 , b .t );
14131415 }
14141416 // Side views don't need setPosition - they show all Z by definition
14151417 ImpUtils .zoomTo (imp , visitingZoom .fraction (), (int ) viewX , (int ) viewY );
1418+ return true ;
14161419 }
14171420
1418- private void goTo (final int row , final ImagePlus imp ) {
1419- goTo (row , imp , SNT .XY_PLANE );
1421+ /**
1422+ * Viewer mode: moves the image windows of a materialized crop (Stream mode), if any, to a bookmark. Bookmarks
1423+ * outside the crop, which is only a portion of the streamed data, are skipped with a notice in the viewer
1424+ */
1425+ private void goToMaterializedCrop (final Bookmark b ) {
1426+ final SNT snt = snt ();
1427+ if (snt == null || !snt .isStreamMode () || !snt .isMaterializedCrop ()) return ;
1428+ if (snt .getImagePlus () == null ) return ;
1429+ if (!goToAllPlanes (snt , b , true )) viewer .showViewerMessage ("Bookmark is outside the materialized crop" );
1430+ }
1431+
1432+ /**
1433+ * Moves the image of the main (XY) view and, if displayed, the side views to a bookmark. Side views are only
1434+ * moved if the bookmark lies within the main view
1435+ *
1436+ * @param silent if true, no error is displayed if the bookmark lies outside the image
1437+ * @return true if the bookmark is within the image (and the displays were moved). False if there is no image
1438+ */
1439+ private boolean goToAllPlanes (final SNT snt , final Bookmark b , final boolean silent ) {
1440+ final ImagePlus imp = snt .getImagePlus ();
1441+ if (imp == null || !goTo (b , imp , SNT .XY_PLANE , silent )) return false ;
1442+ if (!snt .getSinglePane ()) {
1443+ final ImagePlus zyImp = snt .getImagePlus (SNT .ZY_PLANE );
1444+ if (zyImp != null ) goTo (b , zyImp , SNT .ZY_PLANE , silent );
1445+ final ImagePlus xzImp = snt .getImagePlus (SNT .XZ_PLANE );
1446+ if (xzImp != null ) goTo (b , xzImp , SNT .XZ_PLANE , silent );
1447+ }
1448+ return true ;
14201449 }
14211450
14221451 /** Viewer mode: animates the camera to the world position of the selected marker row. */
14231452 private void flyTo (final int row ) {
14241453 flyTo (model .getDataList ().get (table .convertRowIndexToModel (row )));
14251454 }
14261455
1456+ /**
1457+ * SNT mode: animates every open Bvv/Bdv viewer to the location of the bookmark
1458+ *
1459+ * @return the number of viewers that were moved
1460+ */
1461+ private int flyOpenViewers (final Bookmark b ) {
1462+ if (sntui == null ) return 0 ;
1463+ final java .util .List <AbstractBigViewer > viewers = sntui .getOpenBigViewers ();
1464+ for (final AbstractBigViewer v : viewers ) v .flyTo (b .getX (), b .getY (), b .getZ (), b .t , b .label );
1465+ return viewers .size ();
1466+ }
1467+
14271468 private void flyTo (final Bookmark b ) {
14281469 if (viewer == null ) return ;
14291470 viewer .flyTo (b .getX (), b .getY (), b .getZ (), b .t , b .label );
1471+ goToMaterializedCrop (b ); // Stream mode only
14301472 }
14311473
14321474 private void loadBookmarksFromFile (final File file ) {
0 commit comments