Skip to content

Commit

Permalink
Improve interactions with Legacy 3D viewer
Browse files Browse the repository at this point in the history
- fix shortcuts not working (not working since v3!?)
- intercept ij.IJ.error() calls
  • Loading branch information
tferr committed Nov 2, 2024
1 parent b611ade commit 84908a2
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 63 deletions.
138 changes: 81 additions & 57 deletions src/main/java/sc/fiji/snt/QueueJumpingKeyListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
import ij3d.Image3DUniverse;
import ij3d.behaviors.InteractiveBehavior;
import ij3d.behaviors.Picker;
import sc.fiji.snt.gui.GuiUtils;

class QueueJumpingKeyListener implements KeyListener {

Expand All @@ -50,9 +49,7 @@ class QueueJumpingKeyListener implements KeyListener {
private final SNT tracerPlugin;
private final InteractiveTracerCanvas canvas;
private final Image3DUniverse univ;

private final ArrayList<KeyListener> listeners = new ArrayList<>();

private static final int DOUBLE_PRESS_INTERVAL = 300; // ms
private long timeKeyDown = 0; // last time key was pressed
private int lastKeyPressedCode;
Expand All @@ -66,33 +63,30 @@ class QueueJumpingKeyListener implements KeyListener {
// Extra navigation/zoom keys
KeyEvent.VK_PLUS, KeyEvent.VK_LESS, KeyEvent.VK_GREATER, KeyEvent.VK_TAB };

public QueueJumpingKeyListener(final SNT tracerPlugin,
final InteractiveTracerCanvas canvas)
{

public QueueJumpingKeyListener(final SNT tracerPlugin, final InteractiveTracerCanvas canvas) {
this.tracerPlugin = tracerPlugin;
this.canvas = canvas;
univ = null;
}

public QueueJumpingKeyListener(final SNT tracerPlugin,
final Image3DUniverse univ)
{
public QueueJumpingKeyListener(final SNT tracerPlugin, final Image3DUniverse univ) {
this.tracerPlugin = tracerPlugin;
this.canvas = null;
this.canvas = tracerPlugin.getTracingCanvas();
this.univ = univ;
univ.addInteractiveBehavior(new PointSelectionBehavior(univ));
}

@Override
public void keyPressed(final KeyEvent e) {

if (!tracerPlugin.isUIready() || (canvas != null && canvas
// KEY_PRESSED, KEY_RELEASED, and KEY_TYPED all trigger actions in the Legacy 3D viewer
if (KeyEvent.KEY_PRESSED != e.getID() || !tracerPlugin.isUIready() || (canvas != null && canvas
.isEventsDisabled()))
{
waiveKeyPress(e);
return;
}

final int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE) {// IJ's pan tool shortcut
if (canvas != null) tracerPlugin.panMode = true;
Expand All @@ -108,8 +102,7 @@ public void keyPressed(final KeyEvent e) {
final boolean ctrl_down = (e.getModifiersEx() & CTRL_CMD_MASK) != 0;
final boolean shift_down = e.isShiftDown();

if (keyCode == KeyEvent.VK_ESCAPE && canvas != null) {
// Esc is the documented wand>hand tool toggle for the Legacy 3D viewer
if (keyCode == KeyEvent.VK_ESCAPE) {
if (doublePress) tracerPlugin.getUI().reset();
else tracerPlugin.getUI().abortCurrentOperation();
e.consume();
Expand All @@ -121,12 +114,14 @@ else if (keyCode == KeyEvent.VK_ENTER) {
return;
}
else if (keyCode == KeyEvent.VK_4) {
tracerPlugin.unzoomAllPanes();
if (!calledFromUniv(true))
tracerPlugin.unzoomAllPanes(); // ignore if called from 3D legacy viewer
e.consume();
return;
}
else if (keyCode == KeyEvent.VK_5) {
tracerPlugin.zoom100PercentAllPanes();
if (!calledFromUniv(true))
tracerPlugin.zoom100PercentAllPanes();
e.consume();
return;
}
Expand Down Expand Up @@ -175,17 +170,20 @@ else if (canvas != null && keyChar == '\u0000' && (shift_down || alt_down)) {
}
else if (shift_down && (keyChar == 'b' || keyChar == 'B')) {
// IJ1 built-in: Shift+B Blobs Sample Image
canvas.bookmarkCursorLocation();
System.out.println(e);
bookmarkCursorLocation();
e.consume();
}
else if (shift_down && (keyChar == 'e' || keyChar == 'E')) {
// IJ1 built-in: Shift+E Restore Selection
canvas.toggleEditMode();
if (canvas != null && !calledFromUniv(true))
canvas.toggleEditMode();
e.consume();
}
else if (shift_down && (keyChar == 'p' || keyChar == 'P')) {
// IJ1 built-in: Shift+P Image Properties
canvas.togglePauseTracing();
if (canvas != null && !calledFromUniv(true))
canvas.togglePauseTracing();
e.consume();
}
else if (keyChar == 'g' || keyChar == 'G') {
Expand All @@ -200,7 +198,6 @@ else if (keyChar == '1') {
tracerPlugin.getUI().togglePathsChoice();
e.consume();
}

else if (keyChar == '2') {
// IJ1 built-in: Select Next Lane
tracerPlugin.getUI().togglePartsChoice();
Expand All @@ -216,7 +213,7 @@ else if (keyChar == '3') {
// with InteractiveCanvas). We'll skip hasty keystrokes to avoid
// mis-editing
else if (canvas != null && canvas.isEditMode()) {
if (doublePress) {
if (doublePress || calledFromUniv(true)) {
e.consume();
return;
}
Expand Down Expand Up @@ -251,7 +248,6 @@ else if (keyChar == 'v' || keyChar == 'V') {
canvas.clickAtMaxPoint(false);
}
e.consume();
return;
}

// Keystrokes exclusive to tracing mode
Expand Down Expand Up @@ -290,12 +286,12 @@ else if (keyChar == 'l' || keyChar == 'L') {
tracerPlugin.getUI().toggleSecondaryLayerTracing();
e.consume();
}
else if (keyChar == 'v' || keyChar == 'V') {
else if ((keyChar == 'v' || keyChar == 'V') && !calledFromUniv(true)) {
// IJ1 built-in: Measure
canvas.clickAtMaxPoint(join_modifier_down);
e.consume();
}
else if (keyChar == 's' || keyChar == 'S') {
else if ((keyChar == 's' || keyChar == 'S') && !calledFromUniv(true)) {
// IJ1 built-in: Save
tracerPlugin.toggleSnapCursor();
e.consume();
Expand All @@ -307,48 +303,69 @@ else if (keyChar == 's' || keyChar == 'S') {

}

private void bookmarkCursorLocation() {
System.out.println("bookmarkCursorLocation called");
if (calledFromUniv(false)) {
final Point3d p = getNearestPickedPoint();
if (p != null) {
tracerPlugin.getUI().getBookmarkManager().add((int)p.x, (int)p.y, (int)p.z,
tracerPlugin.getChannel(), tracerPlugin.getFrame());
if (!tracerPlugin.getUI().getBookmarkManager().isShowing())
showStatus("Bookmark added");
}
} else if (canvas != null) {
canvas.bookmarkCursorLocation();
}
}

private void startShollAnalysis() {
if (canvas != null) {
if (calledFromUniv(false)) {
new Thread(() -> {
final NearPoint np = getNearestPickedPointOnAnyPath();
if (np != null) tracerPlugin.startSholl(np.getNode());
}).start();
} else if (canvas != null) {
canvas.startShollAnalysis();
return;
}
new Thread(() -> {
final NearPoint np = getNearestPickedPoint();
if (np != null) tracerPlugin.startSholl(np.getNode());
}).start();
}

private NearPoint getNearestPickedPoint() {
private Point3d getNearestPickedPoint() {
final Point p = univ.getCanvas().getMousePosition();
if (p == null) return null;
final Picker picker = univ.getPicker();
final Content c = picker.getPickedContent(p.x, p.y);
if (null == c) return null;
final Point3d point = picker.getPickPointGeometry(c, p.x, p.y);
final double diagonalLength = tracerPlugin.getImpDiagonalLength(true,
false);
if (null == c) {
showStatus("No content available at cursor location");
return null;
}
return picker.getPickPointGeometry(c, p.x, p.y);
}

private NearPoint getNearestPickedPointOnAnyPath() {
final Point3d point = getNearestPickedPoint();
if (null == point) return null;
final double diagonalLength = tracerPlugin.getImpDiagonalLength(true, false);
final NearPoint np = tracerPlugin.getPathAndFillManager()
.nearestPointOnAnyPath(point.x, point.y, point.z, diagonalLength);
.nearestPointOnAnyPath(point.x, point.y, point.z, diagonalLength);
if (np == null) {
SNTUtils.error("BUG: No nearby path was found within " + diagonalLength +
" of the pointer");
" of the pointer");
}
return np;
}

private void selectNearestPathToMousePointer(final boolean shift_down) {
if (canvas != null) {
if (calledFromUniv(false)) {
new Thread(() -> {
final NearPoint np = getNearestPickedPointOnAnyPath();
if (np != null) {
final Path path = np.getPath();
tracerPlugin.selectPath(path, shift_down);
}
}).start();
} else if (canvas != null) {
canvas.selectNearestPathToMousePointer(shift_down);
return;
}
new Thread(() -> {
final NearPoint np = getNearestPickedPoint();
if (np == null) {
return;
}
final Path path = np.getPath();
tracerPlugin.selectPath(path, shift_down);
}).start();
}

private void waiveKeyPress(final KeyEvent e) {
Expand All @@ -360,9 +377,7 @@ private void waiveKeyPress(final KeyEvent e) {

@Override
public void keyReleased(final KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE && canvas != null) { // IJ's pan
// tool
// shortcut
if (e.getKeyCode() == KeyEvent.VK_SPACE && canvas != null) { // IJ's pan tool shortcut
tracerPlugin.panMode = false;
}
for (final KeyListener kl : listeners) {
Expand Down Expand Up @@ -402,11 +417,9 @@ public void addOtherKeyListeners(final KeyListener[] laterKeyListeners) {

private class PointSelectionBehavior extends InteractiveBehavior {

private final GuiUtils gUtils;

public PointSelectionBehavior(final DefaultUniverse univ) {
super(univ);
this.gUtils = new GuiUtils(univ.getCanvas());
}

@Override
Expand All @@ -415,11 +428,11 @@ public void doProcess(final KeyEvent e) {
final char keyChar = e.getKeyChar();
if (keyChar == 'w' || keyChar == 'W') {
Toolbar.getInstance().setTool(Toolbar.WAND);
gUtils.tempMsg("Wand Tool selected");
showStatus("Wand Tool selected");
}
else if (keyChar == 'h' || keyChar == 'H') {
Toolbar.getInstance().setTool(Toolbar.HAND);
gUtils.tempMsg("Hand Tool selected");
showStatus("Hand Tool selected");
}
else {
keyPressed(e);
Expand All @@ -440,18 +453,29 @@ public void doProcess(final MouseEvent me) {
final Picker picker = univ.getPicker();
final Content c = picker.getPickedContent(me.getX(), me.getY());
if (null == c) {
gUtils.tempMsg("No content picked!");
showStatus("No content picked!");
return;
}

gUtils.tempMsg("Retrieving content...");
showStatus("Retrieving content...");
final Point3d point = picker.getPickPointGeometry(c, me.getX(), me
.getY());
gUtils.tempMsg(SNTUtils.formatDouble(point.x, 3) + ", " + SNTUtils.formatDouble(
showStatus(SNTUtils.formatDouble(point.x, 3) + ", " + SNTUtils.formatDouble(
point.y, 3) + ", " + SNTUtils.formatDouble(point.z, 3));
final boolean joiner_modifier_down = me.isAltDown();
SwingUtilities.invokeLater(() -> tracerPlugin.clickForTrace(point,
joiner_modifier_down));
}
}

void showStatus(final String msg) {
//NB: using new GuiUtils(univ.getWindow()).tempMsg(msg) steals focus from viewer canvas and thus this listener
tracerPlugin.getUI().showStatus(msg, true);
}

boolean calledFromUniv(final boolean warnIfNonApplicableKey) {
if (univ != null && warnIfNonApplicableKey)
showStatus("Shortcut does not apply to Leg. 3D Viewer...");
return univ != null;
}
}
3 changes: 1 addition & 2 deletions src/main/java/sc/fiji/snt/SNT.java
Original file line number Diff line number Diff line change
Expand Up @@ -683,8 +683,7 @@ public void initialize(final boolean singlePane, final int channel,
}

private void addListener(final InteractiveTracerCanvas canvas) {
final QueueJumpingKeyListener listener = new QueueJumpingKeyListener(this,
canvas);
final QueueJumpingKeyListener listener = new QueueJumpingKeyListener(this, canvas);
setAsFirstKeyListener(canvas, listener);
}

Expand Down
20 changes: 16 additions & 4 deletions src/main/java/sc/fiji/snt/SNTUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -1798,9 +1798,9 @@ public void actionPerformed(final ActionEvent e) {
final int defResFactor = Content.getDefaultResamplingFactor(plugin.getImagePlus(),
ContentConstants.VOLUME);
final Double userResFactor = guiUtils.getDouble(
"Please specify the image resampling factor. The default factor for current image is "
+ defResFactor + ".",
"Image Resampling Factor", defResFactor);
"Please specify the downsampling factor to render the image volume. The default " +
"factor for current image is " + defResFactor + "×.",
"Volume Downsampling Factor", defResFactor);

if (userResFactor == null) { // user pressed cancel
plugin.set3DUniverse(null);
Expand Down Expand Up @@ -1857,8 +1857,8 @@ public void windowClosed(final WindowEvent e) {
});

// Build widget for rendering choices
displayChoice.addItem("Lines and discs");
displayChoice.addItem("Lines");
displayChoice.addItem("Lines and discs");
displayChoice.addItem("Surface reconstructions");
applyDisplayChoice.addActionListener(e -> {

Expand Down Expand Up @@ -1996,6 +1996,18 @@ public void actionPerformed(final ActionEvent e) {
c.gridx++;
c.fill = GridBagConstraints.NONE;
p.add(applyActionChoice, c);

// row 5
c.gridy++;
c.gridx = 0;
c.gridwidth = 3;
final JCheckBox jcbx = new JCheckBox("Disable pop-up errors", ij.IJ.redirectingErrorMessages());
jcbx.addActionListener( e -> ij.IJ.redirectErrorMessages(jcbx.isSelected()));
jcbx.setToolTipText("Interactions with the Viewer's canvas may trigger warnings\n" +
"and errors that are displayed in pop-up dialogs. Activate\n" +
"this option to have such messages displayed discretely in\n" +
"a Log window instead.");
p.add(jcbx, c);
return p;
}

Expand Down

0 comments on commit 84908a2

Please sign in to comment.