Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,41 +1,70 @@
package org.micromanager.lightsheetmanager.api.data;

import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

// TODO: account for different naming on each geometry type (SLICE_SCAN_ONLY)
// To add a new AcquisitionMode:
// 1) Add a new constant to the enum.
// 2) Add the AcquisitionMode to the GeometryType in MODES_BY_GEOMETRY.

// Note: The UI display order is determined by the order in the MODES_BY_GEOMETRY lists.
// Note: Each text string must be unique to support reliable lookup in fromString().

/**
* Acquisition modes for all microscope geometry types.
*/
public enum AcquisitionMode {
NONE("None"),
PIEZO_SLICE_SCAN("Synchronous piezo/slice scan"),
NO_SCAN("No scan (fixed sheet)"),

// Stage scan modes
STAGE_SCAN("Stage scan"),
STAGE_SCAN_INTERLEAVED("Stage scan interleaved"),
STAGE_SCAN_UNIDIRECTIONAL("Stage scan unidirectional"),
//SLICE_SCAN_ONLY("Slice scan only"), // for diSPIM
SLICE_SCAN_ONLY("Galvo scan"), // for SCAPE
PIEZO_SCAN_ONLY("Piezo scan only");

// Maps GeometryType to a set of valid AcquisitionModes
private static final Map<GeometryType, List<AcquisitionMode>> MODES_BY_GEOMETRY_TYPE = Map.of(
GeometryType.SCAPE, List.of(NO_SCAN, STAGE_SCAN, SLICE_SCAN_ONLY),
GeometryType.DISPIM, List.of(NO_SCAN, STAGE_SCAN, SLICE_SCAN_ONLY) // TODO: add all valid modes
);

private static final List<AcquisitionMode> STAGE_SCAN_MODES = List.of(
// SCAPE
GALVO_SCAN("Galvo scan"),

// diSPIM
SLICE_SCAN_ONLY("Slice scan only"),
PIEZO_SCAN_ONLY("Piezo scan only"),
PIEZO_SLICE_SCAN("Synchronous piezo/slice scan");

// Stage scan modes, conditionally included based on hardware availability
private static final EnumSet<AcquisitionMode> STAGE_SCAN_MODES = EnumSet.of(
STAGE_SCAN, STAGE_SCAN_INTERLEAVED, STAGE_SCAN_UNIDIRECTIONAL
);

// Maps GeometryType to a List of valid AcquisitionMode constants
private static final Map<GeometryType, List<AcquisitionMode>> MODES_BY_GEOMETRY =
new EnumMap<>(GeometryType.class);

// Add valid AcquisitionModes to GeometryTypes.
// The order of the constants determines the order in dropdown menus.
static {
MODES_BY_GEOMETRY.put(GeometryType.SCAPE, List.of(
NO_SCAN, STAGE_SCAN, GALVO_SCAN
));

MODES_BY_GEOMETRY.put(GeometryType.DISPIM, List.of(
NO_SCAN, STAGE_SCAN, STAGE_SCAN_INTERLEAVED, STAGE_SCAN_UNIDIRECTIONAL,
SLICE_SCAN_ONLY, PIEZO_SCAN_ONLY, PIEZO_SLICE_SCAN
));

// Example: Add future geometry types here
// MODES_BY_GEOMETRY.put(GeometryType.MESOSPIM, List.of(...));
}

// Display text used in the UI
private final String text_;

private static final Map<String, AcquisitionMode> stringToEnum =
Stream.of(values()).collect(Collectors.toMap(Object::toString, e -> e));
private static final Map<String, AcquisitionMode> STRING_TO_ENUM =
Stream.of(values()).collect(Collectors.toMap(Object::toString, e -> e));

AcquisitionMode(final String text) {
text_ = text;
Expand All @@ -46,27 +75,58 @@ public String toString() {
return text_;
}

public static AcquisitionMode fromString(final String symbol) {
return stringToEnum.getOrDefault(symbol, AcquisitionMode.NONE);
/**
* Returns {@code true} if the {@link AcquisitionMode} is a stage scan mode.
*
* @return {@code true} if the mode is stage scan mode
*/
public boolean isStageScanMode() {
return STAGE_SCAN_MODES.contains(this);
}

public static String[] toArray() {
return Arrays.stream(values())
.map(AcquisitionMode::toString)
.toArray(String[]::new);
/**
* Returns the available acquisition modes supported by a specific microscope geometry.
* <p>
* This list is filtered based on hardware capabilities: if {@code hasStageScanning}
* is {@code false}, all stage-scan related modes are excluded.
*
* @param geometry the {@link GeometryType} to query; if {@code null}, an empty list is returned
* @param hasStageScanning {@code true} if stage scan hardware is available
* @return a {@code List} of {@link AcquisitionMode} constants
*/
public static List<AcquisitionMode> getValidModes(final GeometryType geometry, final boolean hasStageScanning) {
return Optional.ofNullable(geometry)
.map(MODES_BY_GEOMETRY::get) // returns null if geometry is not in map
.orElse(Collections.emptyList())
.stream()
.filter(mode -> hasStageScanning || !mode.isStageScanMode())
.collect(Collectors.toList());
}

/**
* Returns an array of valid acquisition modes as strings.
* Return an array of {@code String} labels for a dropdown menu.
*
* @param isStageScanning {@code true} if stage scanning
* @param geometry the microscope {@link GeometryType}
* @param hasStageScanning {@code true} if stage scan hardware is available
* @return an array of strings
*/
public static String[] getValidKeys(final GeometryType geometry, final boolean isStageScanning) {
final List<AcquisitionMode> baseModes = MODES_BY_GEOMETRY_TYPE.getOrDefault(geometry, List.of());
return baseModes.stream()
.filter(mode -> isStageScanning || !STAGE_SCAN_MODES.contains(mode))
.map(AcquisitionMode::toString)
.toArray(String[]::new);
public static String[] getLabels(final GeometryType geometry, final boolean hasStageScanning) {
return getValidModes(geometry, hasStageScanning).stream()
.map(AcquisitionMode::toString)
.toArray(String[]::new);
}

/**
* Returns the {@link AcquisitionMode} associated with the string label.
* <p>
* If the provided string is {@code null} or does not match any known
* acquisition mode, an empty {@link Optional} is returned.
*
* @param str the string label to convert (Example: "Stage scan")
* @return an {@link Optional} containing the matching {@link AcquisitionMode},
* or an empty {@code Optional} if no match is found.
*/
public static Optional<AcquisitionMode> fromString(final String str) {
return Optional.ofNullable(str).map(STRING_TO_ENUM::get);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static class Builder extends DefaultAcquisitionSettings.Builder<Builder>
private DefaultScanSettings.Builder scsb_ = new DefaultScanSettings.Builder();
private DefaultSheetCalibration.Builder[] shcb_ = new DefaultSheetCalibration.Builder[2];
private DefaultSliceCalibration.Builder[] slcb_ = new DefaultSliceCalibration.Builder[2];
private AcquisitionMode acquisitionMode_ = AcquisitionMode.NONE;
private AcquisitionMode acquisitionMode_ = AcquisitionMode.NO_SCAN;
private MultiChannelMode channelMode_ = MultiChannelMode.NONE;
private CameraMode cameraMode_ = CameraMode.EDGE;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public static class Builder extends DefaultAcquisitionSettings.Builder<Builder>
private DefaultScanSettings.Builder scsb_ = new DefaultScanSettings.Builder();
private DefaultSheetCalibration.Builder[] shcb_ = new DefaultSheetCalibration.Builder[1];
private DefaultSliceCalibration.Builder[] slcb_ = new DefaultSliceCalibration.Builder[1];
private AcquisitionMode acquisitionMode_ = AcquisitionMode.NONE;
private AcquisitionMode acquisitionMode_ = AcquisitionMode.NO_SCAN;
private CameraMode cameraMode_ = CameraMode.EDGE;

private boolean useChannels_ = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private void createUserInterface() {
// acquisition mode combo box
final boolean isUsingScanSettings = model_.devices().isUsingStageScanning();
final GeometryType geometryType = model_.devices().getDeviceAdapter().getMicroscopeGeometry();
cmbAcquisitionModes_ = new ComboBox(AcquisitionMode.getValidKeys(geometryType, isUsingScanSettings),
cmbAcquisitionModes_ = new ComboBox(AcquisitionMode.getLabels(geometryType, isUsingScanSettings),
acqSettings.acquisitionMode().toString(),
180, 24);

Expand Down Expand Up @@ -266,8 +266,9 @@ private void createEventHandlers() {
// select the acquisition mode
cmbAcquisitionModes_.registerListener(e -> {
final String selected = cmbAcquisitionModes_.getSelected();
model_.acquisitions().settingsBuilder()
.acquisitionMode(AcquisitionMode.fromString(selected));
AcquisitionMode.fromString(selected).ifPresent(mode -> {
model_.acquisitions().settingsBuilder().acquisitionMode(mode);
});
});

// TODO: should timing recalc be part of setting use advanced timing value in model?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,13 +497,6 @@ public boolean prepareControllerForAcquisitionSide(
double sliceAmplitude = piezoAmplitude / sliceRate;
double sliceCenter = (piezoCenter - sliceOffset) / sliceRate;

if (settings.acquisitionMode() == AcquisitionMode.PIEZO_SCAN_ONLY) {
if (cameraMode == CameraMode.OVERLAP) {
double actualPiezoCenter = piezoCenter - piezoAmplitude / (2 * (numSlicesHW - 1));
sliceCenter = (actualPiezoCenter - sliceOffset) / sliceRate;
}
sliceAmplitude = 0.0;
}
// round to nearest 0.0001 degrees, which is approximately the DAC resolution
sliceAmplitude = NumberUtils.roundToPlace(sliceAmplitude, 4);
sliceCenter = NumberUtils.roundToPlace(sliceCenter, 4);
Expand Down Expand Up @@ -532,9 +525,9 @@ public boolean prepareControllerForAcquisitionSide(
// get the piezo card ready
// need to do this for stage scanning too, which makes sure the piezo amplitude is 0

// if mode SLICE_SCAN_ONLY we have computed slice movement as if we
// if mode GALVO_SCAN we have computed slice movement as if we
// were moving the piezo but now make piezo stay still
if (settings.acquisitionMode() == AcquisitionMode.SLICE_SCAN_ONLY) {
if (settings.acquisitionMode() == AcquisitionMode.GALVO_SCAN) {
// if we artificially shifted centers due to extra trigger and only moving piezo
// then move galvo center back to where it would have been
if (settings.cameraMode() == CameraMode.OVERLAP) {
Expand Down Expand Up @@ -846,9 +839,7 @@ public boolean triggerControllerStartAcquisition(final AcquisitionMode acqMode,
scanner_.setSPIMState(ASIScanner.SPIMState.ARMED);
xyStage_.setScanState(ASIXYStage.ScanState.RUNNING);
break;
case PIEZO_SLICE_SCAN:
case SLICE_SCAN_ONLY:
case PIEZO_SCAN_ONLY:
case GALVO_SCAN:
case NO_SCAN:
// in actuality only matters which device we trigger if there are
// two micro-mirror cards, which hasn't ever been done in practice yet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,6 @@ public Future<?> requestRun(boolean speedTest) {
}
} else {
studio_.logs().logMessage("Preparing Acquisition: plugin version " + LightSheetManagerPlugin.version);
// TODO: put this here? generic setup tasks? put in own method?
System.out.println("acqSettings_.acquisitionMode(): " + acqSettings_.acquisitionMode());
if (acqSettings_.acquisitionMode() == AcquisitionMode.NONE) {
studio_.logs().showError("please select a valid acquisition mode!");
return; // early exit
}
// run abstract methods implemented by acquisition engine geometry types
if (!setup()) {
studio_.logs().logError("error during setup!");
Expand Down
Loading