diff --git a/pom.xml b/pom.xml index a95bff9d8..5643027dc 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ org.janelia.saalfeldlab paintera - 1.7.1-SNAPSHOT + 1.8.0-SNAPSHOT Paintera New Era Painting and annotation tool @@ -68,7 +68,7 @@ org.janelia.saalfeldlab.paintera.Paintera Paintera paintera - 1.7.0 + 1.8.0 javafx.base,javafx.controls,javafx.fxml,javafx.media,javafx.swing,javafx.web,javafx.graphics,java.naming,java.management,java.sql UTF-8 diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/ui/PainteraAlerts.java b/src/main/java/org/janelia/saalfeldlab/paintera/ui/PainteraAlerts.java index 48c18e2bd..c6cfdde80 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/ui/PainteraAlerts.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/ui/PainteraAlerts.java @@ -100,7 +100,11 @@ public static Alert alert(final Alert.AlertType type) { public static Alert alert(final Alert.AlertType type, boolean isResizable) { final AtomicReference alertRef = new AtomicReference<>(); - PlatformImpl.runAndWait(() -> alertRef.set(new Alert(type))); + try { + InvokeOnJavaFXApplicationThread.invokeAndWait(() -> alertRef.set(new Alert(type))); + } catch (InterruptedException e) { + LOG.error("Could not create alert", e); + } final Alert alert = alertRef.get(); alert.setTitle(Constants.NAME); alert.setResizable(isResizable); diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/Paintera.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/Paintera.kt index 0930e37c9..f841f82e8 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/Paintera.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/Paintera.kt @@ -51,6 +51,8 @@ class Paintera : Application() { private lateinit var painteraArgs: PainteraCommandLineArgs private var projectDir: String? = null + internal lateinit var mainWindow : PainteraMainWindow + init { application = this /* add window listener for scenes */ @@ -59,7 +61,7 @@ class Paintera : Application() { override fun init() { paintable = false if (!::commandlineArguments.isInitialized) { - commandlineArguments = parameters.raw.toTypedArray() + commandlineArguments = parameters?.raw?.toTypedArray() ?: emptyArray() } painteraArgs = PainteraCommandLineArgs() if (commandlineArguments.isNotEmpty() && !parsePainteraCommandLine(*commandlineArguments)) { @@ -67,7 +69,9 @@ class Paintera : Application() { return } Platform.setImplicitExit(true) - paintera = PainteraMainWindow() + paintera = PainteraMainWindow().also { + mainWindow = it + } projectDir = painteraArgs.project() val projectPath = projectDir?.let { File(it).absoluteFile } diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/PainteraMainWindow.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/PainteraMainWindow.kt index 5ddc54a7f..f3dbbf334 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/PainteraMainWindow.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/PainteraMainWindow.kt @@ -189,11 +189,13 @@ class PainteraMainWindow(val gateway: PainteraGateway = PainteraGateway()) { } private fun showSaveCompleteNotification(owner: Any = baseView.node.scene.window) { - Notifications.create() + val saveNotification = Notifications.create() .graphic(FontAwesome[FontAwesomeIcon.CHECK_CIRCLE]) .title("Save Project") .text("Save Complete") .owner(owner) + saveNotification + .threshold(1, saveNotification) .show() } diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/ShapeInterpolationController.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/ShapeInterpolationController.kt index 0978a331c..4b5ff8d68 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/ShapeInterpolationController.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/ShapeInterpolationController.kt @@ -33,7 +33,9 @@ import net.imglib2.type.numeric.RealType import net.imglib2.type.numeric.integer.UnsignedLongType import net.imglib2.type.numeric.real.FloatType import net.imglib2.type.volatiles.VolatileUnsignedLongType -import net.imglib2.util.* +import net.imglib2.util.ConstantUtils +import net.imglib2.util.Intervals +import net.imglib2.util.Util import net.imglib2.view.ExtendedRealRandomAccessibleRealInterval import net.imglib2.view.IntervalView import net.imglib2.view.Views @@ -64,7 +66,6 @@ import java.math.RoundingMode import java.util.concurrent.CancellationException import java.util.concurrent.atomic.AtomicReference import java.util.function.Supplier -import kotlin.Pair import kotlin.math.absoluteValue import kotlin.math.sqrt @@ -473,21 +474,16 @@ class ShapeInterpolationController>( if (freezeInterpolation) return synchronized(source) { source.resetMasks(false) - /* If preview is on, hide all except the first and last fill mask */ val fillMasks: MutableList> = mutableListOf() val slices = slicesAndInterpolants.slices slices.forEachIndexed { idx, slice -> - if (idx == 0 || idx == slices.size - 1 || !includeInterpolant) { - fillMasks += slice.mask.run { - viewerImg - .expandborder(0, 0, 1) - .extendValue(Label.INVALID) - .interpolateNearestNeighbor() - .affineReal(initialGlobalToMaskTransform.inverse()) - .realInterval(slice.globalBoundingBox!!) - } - - + fillMasks += slice.mask.run { + viewerImg + .expandborder(0, 0, 1) + .extendValue(Label.INVALID) + .interpolateNearestNeighbor() + .affineReal(initialGlobalToMaskTransform.inverse()) + .realInterval(slice.globalBoundingBox!!) } } val invalidLabel = UnsignedLongType(Label.INVALID) @@ -586,7 +582,7 @@ class ShapeInterpolationController>( try { setCompositeMask() } catch (e: MaskInUse) { - LOG.error { "Label source already has an active mask" } + LOG.error(e) { "Label source already has an active mask" } } } } diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/modes/ShapeInterpolationMode.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/modes/ShapeInterpolationMode.kt index 3e09125e6..fade18cc6 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/modes/ShapeInterpolationMode.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/modes/ShapeInterpolationMode.kt @@ -1,14 +1,19 @@ package org.janelia.saalfeldlab.paintera.control.modes import bdv.fx.viewer.render.RenderUnitState +import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView import io.github.oshai.kotlinlogging.KotlinLogging +import javafx.beans.property.SimpleBooleanProperty import javafx.beans.value.ChangeListener import javafx.collections.FXCollections import javafx.collections.ObservableList import javafx.event.Event +import javafx.scene.input.KeyCode import javafx.scene.input.KeyEvent.KEY_PRESSED import javafx.scene.input.KeyEvent.KEY_RELEASED +import javafx.util.Subscription +import kotlinx.coroutines.runBlocking import net.imglib2.Interval import net.imglib2.algorithm.labeling.ConnectedComponents import net.imglib2.algorithm.morphology.distance.DistanceTransform @@ -21,16 +26,12 @@ import net.imglib2.type.numeric.integer.UnsignedLongType import net.imglib2.util.Intervals import net.imglib2.view.IntervalView import net.imglib2.view.Views +import org.controlsfx.control.Notifications import org.janelia.saalfeldlab.bdv.fx.viewer.getDataSourceAndConverter import org.janelia.saalfeldlab.control.mcu.MCUButtonControl -import org.janelia.saalfeldlab.fx.actions.ActionSet +import org.janelia.saalfeldlab.fx.actions.* import org.janelia.saalfeldlab.fx.actions.ActionSet.Companion.installActionSet import org.janelia.saalfeldlab.fx.actions.ActionSet.Companion.removeActionSet -import org.janelia.saalfeldlab.fx.actions.DragActionSet -import org.janelia.saalfeldlab.fx.actions.NamedKeyBinding -import org.janelia.saalfeldlab.fx.actions.painteraActionSet -import org.janelia.saalfeldlab.fx.actions.painteraDragActionSet -import org.janelia.saalfeldlab.fx.actions.painteraMidiActionSet import org.janelia.saalfeldlab.fx.midi.MidiButtonEvent import org.janelia.saalfeldlab.fx.midi.MidiToggleEvent import org.janelia.saalfeldlab.fx.midi.ToggleAction @@ -57,6 +58,7 @@ import org.janelia.saalfeldlab.paintera.control.tools.Tool import org.janelia.saalfeldlab.paintera.control.tools.paint.Fill2DTool import org.janelia.saalfeldlab.paintera.control.tools.paint.PaintBrushTool import org.janelia.saalfeldlab.paintera.control.tools.paint.SamPredictor +import org.janelia.saalfeldlab.paintera.control.tools.paint.SamPredictor.SparseLabel import org.janelia.saalfeldlab.paintera.control.tools.paint.SamTool import org.janelia.saalfeldlab.paintera.control.tools.shapeinterpolation.ShapeInterpolationFillTool import org.janelia.saalfeldlab.paintera.control.tools.shapeinterpolation.ShapeInterpolationPaintBrushTool @@ -65,8 +67,9 @@ import org.janelia.saalfeldlab.paintera.control.tools.shapeinterpolation.ShapeIn import org.janelia.saalfeldlab.paintera.data.mask.MaskInfo import org.janelia.saalfeldlab.paintera.data.mask.MaskedSource import org.janelia.saalfeldlab.paintera.paintera +import org.janelia.saalfeldlab.paintera.ui.FontAwesome import org.janelia.saalfeldlab.util.* -import kotlin.collections.set +import kotlin.math.roundToLong class ShapeInterpolationMode>(val controller: ShapeInterpolationController, private val previousMode: ControlMode) : AbstractToolMode() { @@ -161,6 +164,8 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat converter.activeFragmentAlphaProperty().set((activeSelectionAlpha * 255).toInt()) } + internal val samStyleBoxToggle = SimpleBooleanProperty(true) + private fun modeActions(): List { return mutableListOf( painteraActionSet(CANCEL, ignoreDisable = true) { @@ -225,6 +230,7 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat }, painteraDragActionSet("drag activate SAM mode with box", PaintActionType.Paint, ignoreDisable = true, consumeMouseClicked = true) { onDragDetected { + verify("primary click drag only ") { it.isPrimaryButtonDown && !it.isSecondaryButtonDown && !it.isMiddleButtonDown } verify("can't trigger box prompt with active tool") { activeTool in listOf(NavigationTool, shapeInterpolationTool, samTool) } switchTool(samTool) } @@ -234,6 +240,29 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat } } }, + painteraActionSet("change auto sam style") { + KEY_PRESSED(KeyCode.B) { + onAction { + samStyleBoxToggle.set(!samStyleBoxToggle.get()) + data class SamStyleToggle(val icon: FontAwesomeIcon, val title: String, val text: String) + val (toggle, title, text) = if (samStyleBoxToggle.get()) + SamStyleToggle(FontAwesomeIcon.TOGGLE_RIGHT, "Toggle Sam Style", "Style: Interpolant Interval") + else + SamStyleToggle(FontAwesomeIcon.TOGGLE_LEFT, "Toggle Sam Style", "Style: Interpolant Distance Point") + + InvokeOnJavaFXApplicationThread { + val notification = Notifications.create() + .graphic(FontAwesome[toggle]) + .title(title) + .text(text) + .owner(paintera.baseView.node) + notification + .threshold(1, notification) + .show() + } + } + } + }, DeviceManager.xTouchMini?.let { device -> activeViewerProperty.get()?.viewer()?.let { viewer -> painteraMidiActionSet("midi paint tool switch actions", device, viewer, PaintActionType.Paint) { @@ -331,13 +360,32 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat ).filterNotNull() } + internal fun applyShapeInterpolationAndExitMode() { + with(controller) { + var applyMaskTriggered = false + var selfReference: Subscription? = null + val subscription = source.isApplyingMaskProperty.subscribe { applyingMask -> + if (applyMaskTriggered && !applyingMask) { + selfReference?.unsubscribe() + InvokeOnJavaFXApplicationThread { + paintera.baseView.changeMode(previousMode) + } + } + + } + selfReference = subscription + applyMaskTriggered = true + if (!applyMask()) + subscription?.unsubscribe() + } + } fun switchAndApplyShapeInterpolationActions(toolActions: ActionSet) { with(toolActions) { KEY_PRESSED(CANCEL) { name = "cancel_to_shape_interpolation_tool" onAction { - switchTool(shapeInterpolationTool) + runBlocking { switchTool(shapeInterpolationTool)?.join() } controller.setMaskOverlay(replaceExistingInterpolants = true) } handleException { @@ -346,11 +394,13 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat } KEY_PRESSED(SHAPE_INTERPOLATION__ACCEPT_INTERPOLATION) { onAction { - switchTool(shapeInterpolationTool) - if (controller.applyMask()) - paintera.baseView.changeMode(previousMode) + runBlocking { switchTool(shapeInterpolationTool)?.join() } + applyShapeInterpolationAndExitMode() + } + handleException { + LOG.error(it) {} + paintera.baseView.changeMode(previousMode) } - handleException { paintera.baseView.changeMode(previousMode) } } } } @@ -384,7 +434,7 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat } } - internal fun cacheLoadSamSliceInfo(depth: Double, translate: Boolean = depth != controller.currentDepth, provideGlobalToViewerTransform : AffineTransform3D? = null): SamSliceInfo { + internal fun cacheLoadSamSliceInfo(depth: Double, translate: Boolean = depth != controller.currentDepth, provideGlobalToViewerTransform: AffineTransform3D? = null): SamSliceInfo { return samSliceCache[depth] ?: with(controller) { val viewerAndTransforms = this@ShapeInterpolationMode.activeViewerProperty.value!! val viewer = viewerAndTransforms.viewer()!! @@ -397,11 +447,12 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat else -> AffineTransform3D().also { viewerAndTransforms.viewer().state.getViewerTransform(it) } } - val predictionPositions = provideGlobalToViewerTransform?.let { listOf(doubleArrayOf(width / 2.0, height / 2.0, 0.0)) } ?: let { - controller.getInterpolationImg(globalToViewerTransform, closest = true)?.let { - val interpolantInViewer = if (translate) alignTransformAndViewCenter(it, globalToViewerTransform, width, height) else it - interpolantInViewer.getComponentMaxDistancePosition() - } ?: listOf(doubleArrayOf(width / 2.0, height / 2.0, 0.0)) + val fallbackPrompt = listOf(doubleArrayOf(width / 2.0, height / 2.0, 0.0) to SparseLabel.IN) + val predictionPositions = provideGlobalToViewerTransform?.let { fallbackPrompt } ?: let { + controller.getInterpolationImg(globalToViewerTransform, closest = true)?.let { + val interpolantInViewer = if (translate) alignTransformAndViewCenter(it, globalToViewerTransform, width, height) else it + interpolantInViewer.getInterpolantPrompt(samStyleBoxToggle.get()) + } ?: fallbackPrompt } @@ -411,12 +462,11 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat val activeSource = activeSourceStateProperty.value!!.sourceAndConverter!!.spimSource val sources = mask.viewer.state.sources .filter { it.spimSource !== activeSource } - .map { sac -> getDataSourceAndConverter (sac) } // to ensure non-volatile + .map { sac -> getDataSourceAndConverter(sac) } // to ensure non-volatile .toList() val renderState = RenderUnitState(mask.initialGlobalToViewerTransform.copy(), mask.info.time, sources, width.toLong(), height.toLong()) - val predictionRequest = SamPredictor.SparsePrediction(predictionPositions.map { (x, y) -> renderState.getSamPoint(x, y, SamPredictor.SparseLabel.IN) }) - + val predictionRequest = SamPredictor.SparsePrediction(predictionPositions.map { (pos, label) -> renderState.getSamPoint(pos[0], pos[1], label) }) SamSliceInfo(renderState, mask, predictionRequest, null, false).also { SamEmbeddingLoaderCache.load(renderState) samSliceCache[depth] = it @@ -523,11 +573,26 @@ class ShapeInterpolationMode>(val controller: ShapeInterpolat } } -internal fun RenderUnitState.getSamPoint(screenX: Double, screenY: Double, label: SamPredictor.SparseLabel): SamPredictor.SamPoint { +internal fun RenderUnitState.getSamPoint(screenX: Double, screenY: Double, label: SparseLabel): SamPredictor.SamPoint { val screenScaleFactor = calculateTargetSamScreenScaleFactor() return SamPredictor.SamPoint(screenX * screenScaleFactor, screenY * screenScaleFactor, label) } +internal fun IntervalView.getInterpolantPrompt(box: Boolean): List> { + return if (box) + getMinMaxIntervalPositions().mapIndexed { idx, it -> it to if (idx == 0) SparseLabel.TOP_LEFT_BOX else SparseLabel.BOTTOM_RIGHT_BOX } + else + getComponentMaxDistancePosition().map { it to SparseLabel.IN } +} + +internal fun IntervalView.getMinMaxIntervalPositions(): List { + val interval = Intervals.expand(this, *dimensionsAsLongArray().map { (it * .1).roundToLong() }.toLongArray()) + return listOf( + interval.minAsDoubleArray(), + interval.maxAsDoubleArray() + ) +} + internal fun IntervalView.getComponentMaxDistancePosition(): List { /* find the max point to initialize with */ val invalidBorderRai = extendValue(Label.INVALID).interval(Intervals.expand(this, 1, 1, 0)) @@ -614,11 +679,16 @@ internal data class SamSliceInfo(val renderState: RenderUnitState, val mask: Vie val preGenerated get() = sliceInfo == null val globalToViewerTransform get() = renderState.transform - fun updatePrediction(viewerX: Double, viewerY: Double, label: SamPredictor.SparseLabel = SamPredictor.SparseLabel.IN) { + fun updatePrediction(viewerX: Double, viewerY: Double, label: SparseLabel = SparseLabel.IN) { prediction = SamPredictor.SparsePrediction(listOf(renderState.getSamPoint(viewerX, viewerY, label))) } - fun updatePrediction(viewerPositions: List, label: SamPredictor.SparseLabel = SamPredictor.SparseLabel.IN) { + fun updatePrediction(viewerPositions: List, label: SparseLabel = SparseLabel.IN) { prediction = SamPredictor.SparsePrediction(viewerPositions.map { (x, y) -> renderState.getSamPoint(x, y, label) }) } + + fun updatePrediction(viewerPositionsAndLabels: List>) { + prediction = SamPredictor.SparsePrediction(viewerPositionsAndLabels.map { (pos, label) -> renderState.getSamPoint(pos[0], pos[1], label) }) + + } } \ No newline at end of file diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/SamTool.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/SamTool.kt index a14d95489..24b9bd879 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/SamTool.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/paint/SamTool.kt @@ -725,7 +725,7 @@ open class SamTool(activeSourceStateProperty: SimpleObjectProperty drawPromptPoints(points) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/shapeinterpolation/ShapeInterpolationSAMTool.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/shapeinterpolation/ShapeInterpolationSAMTool.kt index 4a0fc8048..5b1d6d4b2 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/shapeinterpolation/ShapeInterpolationSAMTool.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/shapeinterpolation/ShapeInterpolationSAMTool.kt @@ -37,7 +37,12 @@ internal class ShapeInterpolationSAMTool(private val controller: ShapeInterpolat /* If we are requesting a new embedding that isn't already pre-cached, * then likely the existing requests are no longer needed. * Cancel any that have not yet returned. */ - shapeInterpolationMode.samSliceCache[controller.currentDepth] ?: let { SamEmbeddingLoaderCache.cancelPendingRequests() } + var drawPrompt = false + shapeInterpolationMode.samSliceCache[controller.currentDepth]?.let { + drawPrompt = true + } ?: let { + SamEmbeddingLoaderCache.cancelPendingRequests() + } val info = shapeInterpolationMode.cacheLoadSamSliceInfo(controller.currentDepth) maskedSource?.resetMasks(false) @@ -46,7 +51,9 @@ internal class ShapeInterpolationSAMTool(private val controller: ShapeInterpolat super.activate() - temporaryPrompt = !info.locked + if (drawPrompt) + info.prediction.drawPrompt() + requestPrediction(info.prediction) } diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/shapeinterpolation/ShapeInterpolationTool.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/shapeinterpolation/ShapeInterpolationTool.kt index cee453d0e..7608f23bd 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/shapeinterpolation/ShapeInterpolationTool.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/control/tools/shapeinterpolation/ShapeInterpolationTool.kt @@ -1,6 +1,7 @@ package org.janelia.saalfeldlab.paintera.control.tools.shapeinterpolation import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView +import io.github.oshai.kotlinlogging.KotlinLogging import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleStringProperty import javafx.scene.input.KeyCode @@ -9,7 +10,7 @@ import javafx.scene.input.MouseButton import javafx.scene.input.MouseEvent import javafx.scene.input.MouseEvent.MOUSE_CLICKED import javafx.util.Duration -import kotlinx.coroutines.* +import kotlinx.coroutines.Job import net.imglib2.realtransform.AffineTransform3D import org.janelia.saalfeldlab.fx.actions.* import org.janelia.saalfeldlab.fx.actions.ActionSet.Companion.installActionSet @@ -29,13 +30,17 @@ import org.janelia.saalfeldlab.paintera.cache.SamEmbeddingLoaderCache import org.janelia.saalfeldlab.paintera.control.ShapeInterpolationController import org.janelia.saalfeldlab.paintera.control.actions.NavigationActionType import org.janelia.saalfeldlab.paintera.control.actions.PaintActionType -import org.janelia.saalfeldlab.paintera.control.modes.* +import org.janelia.saalfeldlab.paintera.control.modes.ControlMode +import org.janelia.saalfeldlab.paintera.control.modes.NavigationTool +import org.janelia.saalfeldlab.paintera.control.modes.ShapeInterpolationMode +import org.janelia.saalfeldlab.paintera.control.modes.getInterpolantPrompt import org.janelia.saalfeldlab.paintera.control.navigation.TranslationController import org.janelia.saalfeldlab.paintera.control.tools.ViewerTool import org.janelia.saalfeldlab.paintera.control.tools.paint.Fill2DTool import org.janelia.saalfeldlab.paintera.control.tools.paint.SamTool import org.janelia.saalfeldlab.paintera.paintera -import org.janelia.saalfeldlab.util.* +import org.janelia.saalfeldlab.util.extendValue +import org.janelia.saalfeldlab.util.get internal class ShapeInterpolationTool( private val controller: ShapeInterpolationController<*>, @@ -129,7 +134,7 @@ internal class ShapeInterpolationTool( ) } - private fun requestSamPredictionAtViewerPoint(vat : ViewerAndTransforms, requestMidPoint : Boolean = true, runAfter : () -> Unit = {}) { + private fun requestSamPredictionAtViewerPoint(vat: ViewerAndTransforms, requestMidPoint: Boolean = true, runAfter: () -> Unit = {}) { with(controller) { val viewer = vat.viewer() val dX = viewer.width / 2 - viewer.mouseXProperty.value @@ -149,7 +154,7 @@ internal class ShapeInterpolationTool( val depth = depthAt(resultActiveGlobalToViewer) if (!requestMidPoint) { - requestSamPrediction(depth, refresh = true, provideGlobalToViewerTransform = resultActiveGlobalToViewer) {runAfter() } + requestSamPrediction(depth, refresh = true, provideGlobalToViewerTransform = resultActiveGlobalToViewer) { runAfter() } } else { requestSamPrediction(depth, refresh = true, provideGlobalToViewerTransform = resultActiveGlobalToViewer) { val depths = sortedSliceDepths @@ -170,7 +175,6 @@ internal class ShapeInterpolationTool( } - internal fun requestEmbedding(depth: Double) { shapeInterpolationMode.cacheLoadSamSliceInfo(depth) } @@ -196,9 +200,11 @@ internal class ShapeInterpolationTool( val samSliceInfo = shapeInterpolationMode.cacheLoadSamSliceInfo(depth, provideGlobalToViewerTransform = provideGlobalToViewerTransform) if (!newPrediction && refresh) { - controller.getInterpolationImg(samSliceInfo.globalToViewerTransform, closest = true)?.getComponentMaxDistancePosition()?.let { positions -> - samSliceInfo.updatePrediction(positions) + controller.getInterpolationImg(samSliceInfo.globalToViewerTransform, closest = true)?.run { + val points = getInterpolantPrompt(shapeInterpolationMode.samStyleBoxToggle.get()) + samSliceInfo.updatePrediction(points) } + } val viewerMask = samSliceInfo.mask @@ -265,13 +271,9 @@ internal class ShapeInterpolationTool( verifyAll(KEY_PRESSED) { isControllerActive } KEY_PRESSED(SHAPE_INTERPOLATION__ACCEPT_INTERPOLATION) { graphic = { GlyphScaleView(FontAwesomeIconView().apply { styleClass += listOf("accept", "accept-shape-interpolation") }) } - onAction { - if (applyMask()) { - paintera.baseView.changeMode(previousMode) - } - } + onAction { shapeInterpolationMode.applyShapeInterpolationAndExitMode() } handleException { - it.printStackTrace() + LOG.error(it) {} paintera.baseView.changeMode(previousMode) } } @@ -556,4 +558,8 @@ internal class ShapeInterpolationTool( } } } + + companion object { + private val LOG = KotlinLogging.logger { } + } } \ No newline at end of file