Skip to content

Commit 04c05da

Browse files
committed
Curation panel: update doc anchors + minor fixes
1 parent d1b3e05 commit 04c05da

2 files changed

Lines changed: 83 additions & 41 deletions

File tree

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

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1221,22 +1221,13 @@ private void runOnDemandAsync() {
12211221
onDemandButton.setEnabled(true);
12221222
return;
12231223
}
1224-
// Signal quality requires an image. Instead of aborting the entire scan when no image is loaded, soft-disable
1225-
// just this check and proceed with the rest. The user gets a non-blocking status update explaining why;
1226-
// they can re-enable manually after loading an image
1227-
final PlausibilityCheck.SignalQuality sq = monitor.getDeepCheck(PlausibilityCheck.SignalQuality.class);
1228-
if (sq != null && sq.isEnabled() && sntui.plugin.getLoadedData() == null) {
1229-
// Untick the checkbox AND fire its listener so the underlying check is also disabled. Using
1230-
// doClick() toggles the selected state and triggers all registered action listeners in one call
1231-
sntui.error("\"Min. signal contrast ratio\" disabled: It requires a loaded image.");
1232-
if (signalQualityCheckbox.isSelected()) signalQualityCheckbox.doClick(0);
1233-
// If SignalQuality was the only enabled parameter, there's nothing left to scan.
1234-
// Re-check after auto-disabling
1235-
if (noParametersSelected()) {
1236-
onDemandButton.setEnabled(true);
1237-
return;
1238-
}
1224+
// Image-requiring checks: when no image is loaded, soft-disable each
1225+
// one (rather than aborting the scan) and proceed with the rest.
1226+
if (disableImageDependentChecksIfNoImage()) {
1227+
onDemandButton.setEnabled(true);
1228+
return;
12391229
}
1230+
final PlausibilityCheck.SignalQuality sq = monitor.getDeepCheck(PlausibilityCheck.SignalQuality.class);
12401231
// Provide image context for checks that need it (e.g., SignalQuality)
12411232
monitor.setImageData(sntui.plugin.getLoadedData());
12421233
// Ensure image statistics are available for auto-threshold. If not yet
@@ -1805,6 +1796,7 @@ private void wireCheckbox(final JCheckBox cb, final JSpinner spinner,
18051796
// Set initial enabled state
18061797
if (spinner != null) {
18071798
spinner.setEnabled(cb.isSelected());
1799+
enableLiveCommit(spinner);
18081800
}
18091801
cb.addActionListener(e -> {
18101802
final boolean sel = cb.isSelected();
@@ -1814,6 +1806,20 @@ private void wireCheckbox(final JCheckBox cb, final JSpinner spinner,
18141806
});
18151807
}
18161808

1809+
/**
1810+
* Configures the spinner's editor so typed values commit on every valid keystroke rather than only on
1811+
* Enter / focus loss / arrow click. Fixes the case where typing a new threshold and then clicking the
1812+
* histogram button (or running a full scan) would otherwise use the previously-committed value.
1813+
*/
1814+
private static void enableLiveCommit(final JSpinner spinner) {
1815+
if (spinner.getEditor() instanceof JSpinner.DefaultEditor de) {
1816+
final javax.swing.JFormattedTextField tf = de.getTextField();
1817+
if (tf.getFormatter() instanceof javax.swing.text.DefaultFormatter df) {
1818+
df.setCommitsOnValidEdit(true);
1819+
}
1820+
}
1821+
}
1822+
18171823
@Override
18181824
public void warningsUpdated(final List<PlausibilityCheck.Warning> warnings) {
18191825
SwingUtilities.invokeLater(() -> {
@@ -1924,31 +1930,29 @@ private void showCuratedPathsInPathManager() {
19241930
}
19251931

19261932
private static String getDocAnchor(final String checkName) {
1927-
// Anchor strings here must match the auto-generated header IDs in
1928-
// curation.md (Jekyll/kramdown: lowercase, strip punctuation, replace
1929-
// spaces with hyphens). Update both sides together when renaming
1930-
// sections in the docs.
1933+
// Anchor strings here must match the auto-generated header IDs in curation.md (Jekyll/kramdown:
1934+
// all lowercase, strip punctuation, replace spaces with hyphens).
19311935
return switch (checkName) {
19321936
// Live checks
1933-
case "Branch angle" -> "#min-fork-angle-";
1934-
case "Direction continuity" -> "#max-direction-change-at-fork-";
1935-
case "Radius continuity" -> "#max-ratio-of-radius-change-at-fork";
1936-
case "Inter-fork distance" -> "#min-inter-fork-distance";
1937-
case "Terminal path length" -> "#min-length-of-terminal-paths";
1938-
case "Tortuosity consistency" -> "#max-tortuosity-mismatch-at-fork";
1939-
case "Constant radii" -> "#flag-paths-with-uniform-radii";
1937+
case "Branch angle" -> "#fork-angle-min-";
1938+
case "Direction continuity" -> "#fork-direction-flow-max-change-";
1939+
case "Radius continuity" -> "#radius-continuity-max-ratio-at-fork";
1940+
case "Inter-fork distance" -> "#inter-fork-distance-min";
1941+
case "Terminal path length" -> "#terminal-paths-min-length";
1942+
case "Tortuosity consistency" -> "#tortuosity-max-mismatch-at-fork";
1943+
case "Constant radii" -> "#uniform-thickness-flag";
19401944
// On-demand (deep) checks
1941-
case "Path overlap" -> "#max-proximity-for-path-cross-overs";
1942-
case "Bundled paths" -> "#max-proximity-for-bundled-paths";
1943-
case "Missed-fork candidate" -> "#min-distance-for-missed-fork-candidates";
1944-
case "Z-extent ratio" -> "#min-z-extent-ratio";
1945-
case "Thickness jumps" -> "#max-ratio-of-abrupt-thickness-changes";
1946-
case "Thickness inversions" -> "#min-run-length-for-thickness-inversions";
1947-
case "Path signal quality" -> "#min-signal-contrast-ratio";
1948-
case "Tip signal quality" -> "#min-tip-signal-contrast-ratio";
1949-
case "Path signal quality dips" -> "#min-signal-quality-dip";
1945+
case "Path overlap" -> "#cross-over-detection-max-proximity";
1946+
case "Bundled paths" -> "#bundle-detection-max-angle-";
1947+
case "Missed-fork candidate" -> "#missed-fork-candidate-max-proximity";
1948+
case "Z-extent ratio" -> "#z-extent-min-ratio";
1949+
case "Thickness jumps" -> "#thickness-jumps-max-ratio";
1950+
case "Thickness inversions" -> "#thickness-inversions-min-run-length";
1951+
case "Path signal quality" -> "#path-signal-quality-min-contrast";
1952+
case "Tip signal quality" -> "#tip-signal-quality-min-contrast";
1953+
case "Path signal quality dips" -> "#path-signal-quality-dips-min-drop";
19501954
// Scripting-only checks (no UI but Help on Issue... still routes here)
1951-
case "Soma distance" -> "#max-distance-from-soma-or-root";
1955+
case "Soma distance" -> "#max-distance-from-soma-root";
19521956
case "Boundary proximity" -> "#min-distance-from-image-boundary";
19531957
// Externally-contributed warnings (e.g. from the Seeds tab's
19541958
// "Send Selected Seeds to Curation Assistant" submenu).
@@ -1987,6 +1991,45 @@ private boolean noParametersSelected() {
19871991
return noParametersSelected(null, "");
19881992
}
19891993

1994+
/**
1995+
* Soft-disables any image-dependent on-demand check when no image is loaded: unticks the corresponding checkbox
1996+
* (and fires its listener via {@code doClick()} so the underlying {@code PlausibilityCheck} also gets disabled),
1997+
* then surfaces a single consolidated error naming the disabled checks. Returns {@code true} when the caller
1998+
* should abort the scan because no enabled parameters remain after the auto-disable; {@code false} when the scan
1999+
* can proceed (either because an image is loaded, no image-based check was enabled, or other checks remain enabled).
2000+
*/
2001+
private boolean disableImageDependentChecksIfNoImage() {
2002+
if (sntui.plugin.getLoadedData() != null) return false;
2003+
final List<String> disabled = new ArrayList<>(3);
2004+
autoDisableImageCheck(monitor.getDeepCheck(PlausibilityCheck.SignalQuality.class),
2005+
signalQualityCheckbox, "Min. signal contrast ratio", disabled);
2006+
autoDisableImageCheck(monitor.getDeepCheck(PlausibilityCheck.UncertainTerminal.class),
2007+
uncertainTerminalCheckbox, "Min. tip signal contrast ratio", disabled);
2008+
autoDisableImageCheck(monitor.getDeepCheck(PlausibilityCheck.IntensityValley.class),
2009+
intensityValleyCheckbox, "Min. signal-quality dip", disabled);
2010+
if (disabled.isEmpty()) return false;
2011+
final String msg = (disabled.size() == 1)
2012+
? "\"" + disabled.getFirst() + "\" disabled: it requires a loaded image."
2013+
: "The following validations were disabled because no image is loaded: " + String.join(", ", disabled) + ".";
2014+
sntui.error(msg);
2015+
// Re-check after auto-disabling: if every parameter is now off,
2016+
// the caller should abort the scan rather than running with nothing.
2017+
return noParametersSelected();
2018+
}
2019+
2020+
/**
2021+
* Helper for {@link #disableImageDependentChecksIfNoImage}: if the given image-dependent check is currently
2022+
* enabled, unticks its checkbox (firing the listener) and appends its display label to {@code tracker}.
2023+
* Does nothing if the check is null, already disabled, or the checkbox is null/unselected.
2024+
*/
2025+
private static void autoDisableImageCheck(final PlausibilityCheck.DeepCheck check,
2026+
final JCheckBox checkbox, final String label,
2027+
final List<String> tracker) {
2028+
if (check == null || !check.isEnabled()) return;
2029+
if (checkbox != null && checkbox.isSelected()) checkbox.doClick(0);
2030+
tracker.add(label);
2031+
}
2032+
19902033
private boolean noParametersSelected(final List<JCheckBox> scope, final String category) {
19912034
// Defensive null-handling: liveCheckboxes / onDemandCheckboxes are assigned only after the panel has been
19922035
// built. Callers that arrive before construction (script-driven scans, programmatic triggers from external

‎src/main/java/sc/fiji/snt/analysis/curation/PlausibilityCheck.java‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,10 +1027,9 @@ public List<Warning> scan(final Collection<Path> paths) {
10271027
}
10281028

10291029
/**
1030-
* The measurement is each event's median angle: a histogram lets the
1031-
* user see how their {@code maxParallelAngleDeg} sits in the
1032-
* distribution of detected angles. Sweep at 2x the configured angle
1033-
* for context, so the histogram shows the tail beyond the threshold.
1030+
* The measurement is each detected proximity-run's median angle: the histogram lets the user see how
1031+
* their {@code maxParallelAngleDeg} sits in the angle distribution. Sweep at the full 0-90° range so
1032+
* the histogram reflects the true distribution irrespective of the spinner value
10341033
*/
10351034
@Override
10361035
public Measurements measure(final Collection<Path> paths) {
@@ -1040,7 +1039,7 @@ public Measurements measure(final Collection<Path> paths) {
10401039
}
10411040
final sc.fiji.snt.util.BundleDetector.Config cfg = new sc.fiji.snt.util.BundleDetector.Config()
10421041
.proximity(proximityUm)
1043-
.maxParallelAngleDeg(Math.min(90.0, maxParallelAngleDeg * 2.0))
1042+
.maxParallelAngleDeg(90.0)
10441043
.minRunNodes(minRunNodes);
10451044
final List<CrossoverFinder.CrossoverEvent> events;
10461045
try {

0 commit comments

Comments
 (0)