Skip to content

Commit 9fcbf93

Browse files
committed
Improve "Show only selected structure" feature in PathManagerUI
1 parent 8d2ed70 commit 9fcbf93

6 files changed

Lines changed: 108 additions & 9 deletions

File tree

‎src/main/java/sc/fiji/snt/PathAndFillManager.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3690,8 +3690,10 @@ protected void update3DViewerContents() {
36903690
else if (selected) color3f = plugin.selectedColor3f;
36913691
else color3f = plugin.deselectedColor3f;
36923692

3693+
final boolean isolationOk = !plugin.isTreeIsolationActive()
3694+
|| p.getTreeID() == plugin.getIsolatedTreeID();
36933695
p.updateContent3D(plugin.univ, // The appropriate 3D universe
3694-
(selected || !showOnlySelectedPaths), // Visible at all?
3696+
(selected || !showOnlySelectedPaths) && isolationOk, // Visible at all?
36953697
plugin.getPaths3DDisplay(), // How to display?
36963698
color3f, plugin.colorImage); // Colour?
36973699

‎src/main/java/sc/fiji/snt/PathManagerUI.java‎

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -739,7 +739,10 @@ protected void done() {
739739
swcTypeMenu.addSeparator();
740740
swcTypeMenu.add(jmi);
741741
if (applyPromptOptions && pathAndFillManager.size() > 0 && guiUtils
742-
.getConfirmation("Apply new color options to all paths?", "Apply Colors"))
742+
.getConfirmation(plugin.isTreeIsolationActive()
743+
? "Apply new color options to all paths, including structures currently hidden by "
744+
+ "the isolation filter?"
745+
: "Apply new color options to all paths?", "Apply Colors"))
743746
{
744747
final List<Path> allPaths = pathAndFillManager.getPathsFiltered();
745748
if (assignColors) {
@@ -4390,8 +4393,15 @@ private class DeleteCommand implements PathCommand {
43904393
@Override
43914394
public void execute(List<Path> selectedPaths, String cmd) {
43924395
boolean assumeAll = tree.getSelectionCount() == 0 || tree.getSelectionCount() == tree.getNumberOfNodes();
4393-
String message = assumeAll ? "Are you really sure you want to delete all paths?"
4394-
: "Delete the selected " + selectedPaths.size() + " path(s)?";
4396+
String message;
4397+
if (!assumeAll) {
4398+
message = "Delete the selected " + selectedPaths.size() + " path(s)?";
4399+
} else if (plugin.isTreeIsolationActive()) {
4400+
message = "Delete all " + selectedPaths.size() + " listed path(s)? (Structures currently "
4401+
+ "hidden will <i>not</i> be affected.)";
4402+
} else {
4403+
message = "Are you really sure you want to delete all paths?";
4404+
}
43954405
if (guiUtils.getConfirmation(message, "Confirm Deletion?")) {
43964406
deletePaths(selectedPaths);
43974407
}
@@ -6289,6 +6299,7 @@ void restoreFullModelState() {
62896299
arborChoice = null;
62906300
tree.setModel(fullTreeModel);
62916301
hideOthersButton.setSelected(false);
6302+
plugin.clearIsolatedTreeID(); // undo any active canvas/Bdv/Bvv isolation from applyHideOthers(true)
62926303
// Re-enable sorting now that the full (multi-arbor) model is restored:
62936304
// applyHideOthers(true) disables it while a single arbor is isolated, and
62946305
// this path (e.g. the "show all structures" button) must undo that.
@@ -6383,7 +6394,11 @@ private void expandAndRevealTree(final String label) {
63836394
}
63846395
}
63856396

6386-
/** Show only the chosen arbor or restore the full list. */
6397+
/**
6398+
* Show only the chosen arbor (in both the JTree and every tracing viewer - classic canvas,
6399+
* legacy 3D viewer, and Bdv/Bvv, via {@link SNT#setIsolatedTreeID(int)}) or restore the full
6400+
* list/display.
6401+
*/
63876402
private void applyHideOthers(final boolean hide) {
63886403
if (fullTreeModel == null || arborChoice == null) return;
63896404

@@ -6394,10 +6409,17 @@ private void applyHideOthers(final boolean hide) {
63946409
final Path[] primaryPaths = pathAndFillManager.getPathsStructured();
63956410
tree.setModel(new HelpfulTreeModel(primaryPaths, arborChoice));
63966411
sortArborsButton.setEnabled(false);
6412+
// Mirror the JTree's own filtering in the tracing viewers: getTree(String) returns
6413+
// null if arborChoice no longer matches any tree (e.g. deleted between UI updates),
6414+
// in which case clearing isolation is the safer fallback over isolating a stale ID
6415+
final Tree isolatedTree = pathAndFillManager.getTree(arborChoice);
6416+
plugin.setIsolatedTreeID((isolatedTree != null) ? isolatedTree.getTreeID() : -1);
6417+
if (isolatedTree != null) zoomToBoundingBox(isolatedTree.list()); // zoom to selected choice
63976418
} else {
63986419
hideOthersButton.setSelected(false); // ensure sync
63996420
tree.setModel(fullTreeModel); // will call reload
64006421
sortArborsButton.setEnabled(true);
6422+
plugin.clearIsolatedTreeID();
64016423
}
64026424
GuiUtils.JTrees.expandAllNodes(tree);
64036425
// 3) Restore selection if that exists in the new model

‎src/main/java/sc/fiji/snt/SNT.java‎

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,14 @@ public String toString() {
245245
protected volatile boolean snapCursor;
246246
protected volatile boolean showOnlySelectedPaths;
247247
protected volatile boolean showOnlyActiveCTposPaths;
248+
/**
249+
* ID of the single tree (arbor) currently isolated for display in PathManagerUI or -1 if no isolation is active
250+
* Orthogonal to path selection: unlike {@code showOnlySelectedPaths}, isolating a tree does not alter which paths
251+
* are considered "selected", nor their rendered color - it only hides paths belonging to other trees.
252+
* Set via {@link #setIsolatedTreeID(int)}; consulted by {@link TracerCanvas}, the legacy 3D viewer
253+
* ({@link PathAndFillManager#update3DViewerContents()}), and Bdv/Bvv ({@code Bvv.PathOverlay}).
254+
*/
255+
protected volatile int isolatedTreeID = -1;
248256
protected volatile boolean autoCT;
249257
private boolean drawDiameters;
250258
protected double manualRadius;
@@ -2645,8 +2653,7 @@ public synchronized ImagePlus makePathVolume(final Collection<Path> paths, final
26452653
newStack.addSlice(null, thisSlice.convertToByteProcessor(false));
26462654
}
26472655

2648-
final ImagePlus newImp = new ImagePlus(xy.getShortTitle() +
2649-
" Rendered Paths", newStack);
2656+
final ImagePlus newImp = new ImagePlus("Rasterized Paths", newStack);
26502657
newImp.setCalibration(getCalibration());
26512658
return newImp;
26522659
}
@@ -4949,6 +4956,35 @@ public void setShowOnlySelectedPaths(final boolean showOnlySelectedPaths) {
49494956
setShowOnlySelectedPaths(showOnlySelectedPaths, true);
49504957
}
49514958

4959+
/**
4960+
* Isolates a single tree (arbor) for display across every tracing viewer, hiding paths belonging to any other tree.
4961+
* Orthogonal to {@link #setShowOnlySelectedPaths(boolean)}: does not touch path selection or rendered colors, it
4962+
* only restricts which trees are candidates for display at all. Pass -1 (or call {@link #clearIsolatedTreeID()}) to
4963+
* lift the restriction.
4964+
*
4965+
* @param treeID the ID of the tree to isolate (see {@link Tree#getTreeID()}), or -1 to show all
4966+
* @see PathManagerUI
4967+
*/
4968+
public void setIsolatedTreeID(final int treeID) {
4969+
this.isolatedTreeID = treeID;
4970+
updateTracingViewers(true);
4971+
}
4972+
4973+
/** Convenience for {@code setIsolatedTreeID(-1)}: lifts any active tree isolation. */
4974+
public void clearIsolatedTreeID() {
4975+
setIsolatedTreeID(-1);
4976+
}
4977+
4978+
/** @return the ID of the currently isolated tree, or -1 if no isolation is active */
4979+
public int getIsolatedTreeID() {
4980+
return isolatedTreeID;
4981+
}
4982+
4983+
/** @return whether a single tree is currently isolated for display (see {@link #setIsolatedTreeID(int)}) */
4984+
public boolean isTreeIsolationActive() {
4985+
return isolatedTreeID != -1;
4986+
}
4987+
49524988
/**
49534989
* Gets the Image associated with a view pane.
49544990
*

‎src/main/java/sc/fiji/snt/TracerCanvas.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ protected void drawOverlay(final Graphics2D g) {
127127
// not the fitted copy, so test against p rather than drawPath
128128
final boolean isSelected = pathAndFillManager.isSelected(p);
129129
if (!isSelected && plugin.isOnlySelectedPathsVisible()) continue;
130+
if (plugin.isTreeIsolationActive() && p.getTreeID() != plugin.getIsolatedTreeID()) continue;
130131
if (enforceActiveCTpos && (imp.getC() != drawPath
131132
.getChannel() || imp.getT() != drawPath.getFrame()))
132133
{

‎src/main/java/sc/fiji/snt/Tree.java‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,6 +1271,27 @@ public String getLabel() {
12711271
return label;
12721272
}
12731273

1274+
/**
1275+
* Returns the ID shared by every {@link Path} in this tree (see {@link Path#getTreeID()}), i.e., the arbor's
1276+
* identity as tracked by the {@link PathAndFillManager} that assigned it.
1277+
* <p>
1278+
* <b>Uniqueness is scoped to a single {@link PathAndFillManager} instance</b>, e.g., all the Trees of a running SNT
1279+
* session, or all the Trees parsed from one multi-tree reconstruction file. It is <i>not</i> guaranteed across
1280+
* independently-created Trees: each standalone parse (a call to {@link #fromFile(String)}, or each individual file
1281+
* visited by {@link #listFromFile(String)}/{@link #listFromDir(String)}) gets its own manager with its own ID
1282+
* sequence starting from scratch, so two such Trees - even from the same {@code listFromDir()} call - may share the
1283+
* same ID. Once a Tree is merged into a live manager (e.g., via {@link PathAndFillManager#addTree(Tree)}), its
1284+
* paths are assigned fresh, unique, collision-free IDs scoped to that manager; any IDs from the Tree's
1285+
* original source file are discarded.
1286+
* </p>
1287+
*
1288+
* @return the tree ID, or -1 if this tree has no paths (a treeID is always &gt;= 1 otherwise -
1289+
* see {@code PathAndFillManager#maxUsedTreeID})
1290+
*/
1291+
public int getTreeID() {
1292+
return (isEmpty()) ? -1 : list().getFirst().getTreeID();
1293+
}
1294+
12741295
public List<SWCPoint> getNodesAsSWCPoints() throws IllegalArgumentException {
12751296
if (isEmpty()) return new ArrayList<>();
12761297
initPathAndFillManager();

‎src/main/java/sc/fiji/snt/viewer/Bvv.java‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3812,18 +3812,35 @@ boolean isRenderingEnable() {
38123812
}
38133813

38143814
void updatePaths() {
3815-
final Collection<Tree> trees = sntViewer.getRenderedTrees();
3815+
final Collection<Tree> trees = filterIsolated(sntViewer.getRenderedTrees());
38163816
overlayRenderer.updatePaths(trees);
38173817
viewerPanel.requestRepaint();
38183818
}
38193819

38203820
void updatePaths(final Tree transientTree) {
38213821
final Collection<Tree> trees = new ArrayList<>(sntViewer.getRenderedTrees());
38223822
if (transientTree != null) trees.add(transientTree);
3823-
overlayRenderer.updatePaths(trees);
3823+
overlayRenderer.updatePaths(filterIsolated(trees));
38243824
viewerPanel.requestRepaint();
38253825
}
38263826

3827+
/**
3828+
* Restricts {@code trees} to the single tree isolated via {@link SNT#setIsolatedTreeID(int)}
3829+
* (e.g. {@code PathManagerUI}'s "Hide others" toolbar button), if any; a no-op passthrough
3830+
* otherwise. Shared by both {@code updatePaths()} overloads above, and - since {@code Bdv}
3831+
* reuses this same {@code PathOverlay} class (see its class javadoc) - covers both viewers.
3832+
*/
3833+
private Collection<Tree> filterIsolated(final Collection<Tree> trees) {
3834+
final SNT snt = sntViewer.getSNT();
3835+
if (snt == null || !snt.isTreeIsolationActive()) return trees;
3836+
final int isolatedID = snt.getIsolatedTreeID();
3837+
final List<Tree> filtered = new ArrayList<>();
3838+
for (final Tree tree : trees) {
3839+
if (tree.getTreeID() == isolatedID) filtered.add(tree);
3840+
}
3841+
return filtered;
3842+
}
3843+
38273844
/**
38283845
* Lightweight alternative to {@link #updatePaths()} for pure selection changes: patches
38293846
* color/thickness in place for already-rendered trees. See {@link OverlayRenderer#recolor(Tree)}.

0 commit comments

Comments
 (0)