From 360083b9131fdb4ee5f22a2d86996c09ae851884 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Thu, 24 Aug 2023 15:29:53 -0400 Subject: [PATCH 1/9] feat!: remove inflate --- .../saalfeldlab/paintera/meshes/MeshGenerator.java | 6 ------ .../config/MeshSettingsSerializer.java | 6 ------ .../saalfeldlab/paintera/meshes/MeshSettings.kt | 13 ------------- .../paintera/meshes/ui/MeshSettingsController.kt | 5 ----- 4 files changed, 30 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/meshes/MeshGenerator.java b/src/main/java/org/janelia/saalfeldlab/paintera/meshes/MeshGenerator.java index c7a05dc65..df908c0d9 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/meshes/MeshGenerator.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/meshes/MeshGenerator.java @@ -268,9 +268,6 @@ else if (blockOutlineRemoved instanceof Shape3D) ((PhongMaterial)meshAdded.getMaterial()).diffuseColorProperty().bind(this.state.premultipliedColor); meshAdded.drawModeProperty().bind(this.state.settings.getDrawModeProperty()); meshAdded.cullFaceProperty().bind(this.state.settings.getCullFaceProperty()); - meshAdded.scaleXProperty().bind(this.state.settings.getInflateProperty()); - meshAdded.scaleYProperty().bind(this.state.settings.getInflateProperty()); - meshAdded.scaleZProperty().bind(this.state.settings.getInflateProperty()); } if (change.getValueAdded().getB() != null) { @@ -284,9 +281,6 @@ else if (blockOutlineAdded instanceof Shape3D) material = null; if (material instanceof PhongMaterial) ((PhongMaterial)material).diffuseColorProperty().bind(this.state.premultipliedColor); - blockOutlineAdded.scaleXProperty().bind(this.state.settings.getInflateProperty()); - blockOutlineAdded.scaleYProperty().bind(this.state.settings.getInflateProperty()); - blockOutlineAdded.scaleZProperty().bind(this.state.settings.getInflateProperty()); blockOutlineAdded.setDisable(true); } } diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/serialization/config/MeshSettingsSerializer.java b/src/main/java/org/janelia/saalfeldlab/paintera/serialization/config/MeshSettingsSerializer.java index 0bc2912c6..f3ee04180 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/serialization/config/MeshSettingsSerializer.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/serialization/config/MeshSettingsSerializer.java @@ -39,8 +39,6 @@ public class MeshSettingsSerializer implements PainteraSerialization.PainteraAda private static final String CULL_FACE_KEY = "cullFace"; - private static final String INFLATE_KEY = "inflate"; - private static final String IS_VISIBLE_KEY = "isVisible"; public static MeshSettings deserializeInto( @@ -54,7 +52,6 @@ public static MeshSettings deserializeInto( Optional.ofNullable(map.get(SMOOTHING_ITERATIONS_KEY)).map(JsonElement::getAsInt).ifPresent(settings::setSmoothingIterations); Optional.ofNullable(map.get(SMOOTHING_LAMBDA_KEY)).map(JsonElement::getAsDouble).ifPresent(settings::setSmoothingLambda); Optional.ofNullable(map.get(OPACITY_KEY)).map(JsonElement::getAsDouble).ifPresent(settings::setOpacity); - Optional.ofNullable(map.get(INFLATE_KEY)).map(JsonElement::getAsDouble).ifPresent(settings::setInflate); Optional.ofNullable(map.get(DRAW_MODE_KEY)).map(el -> (DrawMode)context.deserialize(el, DrawMode.class)).ifPresent(settings::setDrawMode); Optional.ofNullable(map.get(CULL_FACE_KEY)).map(el -> (CullFace)context.deserialize(el, CullFace.class)).ifPresent(settings::setCullFace); Optional.ofNullable(map.get(IS_VISIBLE_KEY)).map(JsonElement::getAsBoolean).ifPresent(settings::setVisible); @@ -112,9 +109,6 @@ public JsonElement serialize(final MeshSettings src, final Type typeOfSrc, final if (defaults.getOpacity() != src.getOpacityProperty().get()) map.addProperty(OPACITY_KEY, src.getOpacityProperty().get()); - if (defaults.getInflate() != src.getInflate()) - map.addProperty(INFLATE_KEY, src.getInflate()); - if (defaults.isVisible() != src.isVisible()) map.addProperty(IS_VISIBLE_KEY, src.isVisible()); diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt index 37b6fff53..a6c7a7cc6 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/MeshSettings.kt @@ -20,7 +20,6 @@ class MeshSettings @JvmOverloads constructor( val opacity: Double val drawMode: DrawMode val cullFace: CullFace - val inflate: Double val isVisible: Boolean val minLabelRatio: Double val levelOfDetail: Int @@ -45,9 +44,6 @@ class MeshSettings @JvmOverloads constructor( @JvmStatic val cullFace = CullFace.FRONT - @JvmStatic - val inflate = 1.0 - @JvmStatic val isVisible = true @@ -83,7 +79,6 @@ class MeshSettings @JvmOverloads constructor( override var opacity: Double = Defaults.Values.opacity override var drawMode: DrawMode = Defaults.Values.drawMode override var cullFace: CullFace = Defaults.Values.cullFace - override var inflate: Double = Defaults.Values.inflate override var isVisible: Boolean = Defaults.Values.isVisible override var minLabelRatio: Double = Defaults.Values.minLabelRatio override var levelOfDetail: Int = Defaults.Values.levelOfDetail @@ -103,7 +98,6 @@ class MeshSettings @JvmOverloads constructor( val opacityProperty: DoubleProperty = SimpleDoubleProperty(defaults.opacity) val drawModeProperty: ObjectProperty = SimpleObjectProperty(defaults.drawMode) val cullFaceProperty: ObjectProperty = SimpleObjectProperty(defaults.cullFace) - val inflateProperty: DoubleProperty = SimpleDoubleProperty(defaults.inflate) val isVisibleProperty: BooleanProperty = SimpleBooleanProperty(defaults.isVisible) val minLabelRatioProperty: DoubleProperty = SimpleDoubleProperty(defaults.minLabelRatio) val levelOfDetailProperty: IntegerProperty = SimpleIntegerProperty(defaults.levelOfDetail) @@ -116,7 +110,6 @@ class MeshSettings @JvmOverloads constructor( var opacity by opacityProperty.nonnull() var drawMode by drawModeProperty.nonnull() var cullFace by cullFaceProperty.nonnull() - var inflate by inflateProperty.nonnull() var isVisible by isVisibleProperty.nonnull() var minLabelRatio by minLabelRatioProperty.nonnull() var levelOfDetail by levelOfDetailProperty.nonnull() @@ -147,7 +140,6 @@ class MeshSettings @JvmOverloads constructor( opacity = that.opacity drawMode = that.drawMode cullFace = that.cullFace - inflate = that.inflate isVisible = that.isVisible } @@ -171,7 +163,6 @@ class MeshSettings @JvmOverloads constructor( opacityProperty.bind(that.opacityProperty) drawModeProperty.bind(that.drawModeProperty) cullFaceProperty.bind(that.cullFaceProperty) - inflateProperty.bind(that.inflateProperty) isVisibleProperty.bind(that.isVisibleProperty) } @@ -186,7 +177,6 @@ class MeshSettings @JvmOverloads constructor( opacityProperty.unbind() drawModeProperty.unbind() cullFaceProperty.unbind() - inflateProperty.unbind() isVisibleProperty.unbind() } @@ -201,7 +191,6 @@ class MeshSettings @JvmOverloads constructor( opacityProperty.bindBidirectional(that.opacityProperty) drawModeProperty.bindBidirectional(that.drawModeProperty) cullFaceProperty.bindBidirectional(that.cullFaceProperty) - inflateProperty.bindBidirectional(that.inflateProperty) isVisibleProperty.bindBidirectional(that.isVisibleProperty) } @@ -216,7 +205,6 @@ class MeshSettings @JvmOverloads constructor( opacityProperty.unbindBidirectional(that.opacityProperty) drawModeProperty.unbindBidirectional(that.drawModeProperty) cullFaceProperty.unbindBidirectional(that.cullFaceProperty) - inflateProperty.unbindBidirectional(that.inflateProperty) isVisibleProperty.unbindBidirectional(that.isVisibleProperty) } @@ -229,7 +217,6 @@ class MeshSettings @JvmOverloads constructor( && opacity == defaults.opacity && defaults.drawMode == drawMode && defaults.cullFace == cullFace - && inflate == defaults.inflate && isVisible == defaults.isVisible } diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt index 775ab2189..b8cc9f7f4 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt @@ -34,7 +34,6 @@ class MeshSettingsController @JvmOverloads constructor( private val smoothingLambda: DoubleProperty, private val smoothingIterations: IntegerProperty, private val minLabelRatio: DoubleProperty, - private val inflate: DoubleProperty, private val drawMode: Property, private val cullFace: Property, private val isVisible: BooleanProperty, @@ -51,7 +50,6 @@ class MeshSettingsController @JvmOverloads constructor( meshSettings.smoothingLambdaProperty, meshSettings.smoothingIterationsProperty, meshSettings.minLabelRatioProperty, - meshSettings.inflateProperty, meshSettings.drawModeProperty, meshSettings.cullFaceProperty, meshSettings.isVisibleProperty, @@ -77,7 +75,6 @@ class MeshSettingsController @JvmOverloads constructor( NumericSliderWithField(0.0, 1.00, .05).apply { slider.valueProperty().bindBidirectional(smoothingLambda) }, NumericSliderWithField(0, 10, 5).apply { slider.valueProperty().bindBidirectional(smoothingIterations) }, NumericSliderWithField(0.0, 1.0, 0.5).apply { slider.valueProperty().bindBidirectional(minLabelRatio) }, - NumericSliderWithField(0.5, 2.0, inflate.value).apply { slider.valueProperty().bindBidirectional(inflate) }, ComboBox(FXCollections.observableArrayList(*DrawMode.values())).apply { valueProperty().bindBidirectional(drawMode) }, ComboBox(FXCollections.observableArrayList(*CullFace.values())).apply { valueProperty().bindBidirectional(cullFace) }) } @@ -154,7 +151,6 @@ class MeshSettingsController @JvmOverloads constructor( smoothingLambdaSlider: NumericSliderWithField, smoothingIterationsSlider: NumericSliderWithField, minLabelRatioSlider: NumericSliderWithField, - inflateSlider: NumericSliderWithField, drawModeChoice: ComboBox, cullFaceChoice: ComboBox, ): GridPane { @@ -188,7 +184,6 @@ class MeshSettingsController @JvmOverloads constructor( addGridOption("Min label ratio", minLabelRatioSlider, tooltipText) } - addGridOption("Inflate", inflateSlider, "Inflate Meshes by Factor") addGridOption("Draw Mode", drawModeChoice) addGridOption("Cull Face", cullFaceChoice) return this From f3484d8ad645e507a9afb8b6c34e452f13564b69 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Thu, 24 Aug 2023 15:29:56 -0400 Subject: [PATCH 2/9] feat: change mesh setting defaults --- .../java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java | 4 ++-- .../saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java b/src/main/java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java index 7b8cb894f..fe376e430 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/meshes/Smooth.java @@ -22,9 +22,9 @@ public class Smooth { */ private static final Logger LOG = LoggerFactory.getLogger(Smooth.class); - public static final double DEFAULT_LAMBDA = 0.5; + public static final double DEFAULT_LAMBDA = 1.0; - public static final int DEFAULT_ITERATIONS = 3; + public static final int DEFAULT_ITERATIONS = 1; private static boolean isBoundary( final ArrayList vertexTriangleLUT, diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt index b8cc9f7f4..0537b709e 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/meshes/ui/MeshSettingsController.kt @@ -72,8 +72,8 @@ class MeshSettingsController @JvmOverloads constructor( it.slider.valueProperty().bindBidirectional(coarsestScaleLevel) }, NumericSliderWithField(0, this.numScaleLevels - 1, finestScaleLevel.value).apply { slider.valueProperty().bindBidirectional(finestScaleLevel) }, - NumericSliderWithField(0.0, 1.00, .05).apply { slider.valueProperty().bindBidirectional(smoothingLambda) }, - NumericSliderWithField(0, 10, 5).apply { slider.valueProperty().bindBidirectional(smoothingIterations) }, + NumericSliderWithField(0.0, 1.00, 1.0).apply { slider.valueProperty().bindBidirectional(smoothingLambda) }, + NumericSliderWithField(0, 10, 1).apply { slider.valueProperty().bindBidirectional(smoothingIterations) }, NumericSliderWithField(0.0, 1.0, 0.5).apply { slider.valueProperty().bindBidirectional(minLabelRatio) }, ComboBox(FXCollections.observableArrayList(*DrawMode.values())).apply { valueProperty().bindBidirectional(drawMode) }, ComboBox(FXCollections.observableArrayList(*CullFace.values())).apply { valueProperty().bindBidirectional(cullFace) }) From 98e7140bd946a5bafec655d68d62df9dd1b569fb Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Thu, 24 Aug 2023 15:29:59 -0400 Subject: [PATCH 3/9] feat: change screen scale defaults --- .../janelia/saalfeldlab/paintera/config/ScreenScalesConfig.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/config/ScreenScalesConfig.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/config/ScreenScalesConfig.kt index 7a297e2da..2d81502d0 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/config/ScreenScalesConfig.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/config/ScreenScalesConfig.kt @@ -55,7 +55,7 @@ class ScreenScalesConfig @JvmOverloads constructor(vararg initialScales: Double companion object { - private val DEFAULT_SCREEN_SCALES = doubleArrayOf(1.0, 0.5, 0.25, 0.125, 0.0625) + private val DEFAULT_SCREEN_SCALES = doubleArrayOf(0.5, 0.25, 0.125) @JvmStatic fun defaultScreenScalesCopy(): DoubleArray { From 7f5d9888222e687a599c537e972f602c9b0cc56f Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 25 Aug 2023 15:11:00 -0400 Subject: [PATCH 4/9] refactor!: rename methods --- .../saalfeldlab/paintera/PainteraCommandLineArgs.java | 4 ++-- .../saalfeldlab/paintera/data/n5/N5HDF5WriterAdapter.kt | 4 ++-- .../saalfeldlab/paintera/state/metadata/MetadataState.kt | 6 +++--- .../kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/PainteraCommandLineArgs.java b/src/main/java/org/janelia/saalfeldlab/paintera/PainteraCommandLineArgs.java index 6e40142e7..bea216693 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/PainteraCommandLineArgs.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/PainteraCommandLineArgs.java @@ -76,7 +76,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import static org.janelia.saalfeldlab.util.n5.N5Helpers.getReaderOrWriterIfN5ContainerExists; +import static org.janelia.saalfeldlab.util.n5.N5Helpers.getReaderOrGetWriterIfExistsAndWritable; @Command(name = "Paintera", showDefaultValues = true, resourceBundle = "org.janelia.saalfeldlab.paintera.PainteraCommandLineArgs", usageHelpWidth = 120, parameterListHeading = "%n@|bold,underline Parameters|@:%n", @@ -649,7 +649,7 @@ private void addToViewer(final PainteraBaseView viewer, final Supplier p for (final String container : containers) { LOG.debug("Adding datasets for container {}", container); - N5Reader n5Container = getReaderOrWriterIfN5ContainerExists(container); + N5Reader n5Container = getReaderOrGetWriterIfExistsAndWritable(container); final Predicate datasetFilter = options.useDataset(); final ExecutorService es = getDiscoveryExecutorService(); diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/data/n5/N5HDF5WriterAdapter.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/data/n5/N5HDF5WriterAdapter.kt index 265ce5102..953b3b6e0 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/data/n5/N5HDF5WriterAdapter.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/data/n5/N5HDF5WriterAdapter.kt @@ -7,7 +7,7 @@ import org.janelia.saalfeldlab.paintera.Paintera.Companion.n5Factory import org.janelia.saalfeldlab.paintera.serialization.GsonExtensions import org.janelia.saalfeldlab.paintera.serialization.StatefulSerializer import org.janelia.saalfeldlab.paintera.state.SourceState -import org.janelia.saalfeldlab.util.n5.N5Helpers.getReaderOrWriterIfN5ContainerExists +import org.janelia.saalfeldlab.util.n5.N5Helpers.getReaderOrGetWriterIfExistsAndWritable import org.janelia.saalfeldlab.util.n5.N5Helpers.getWriterIfN5ContainerExists import org.scijava.plugin.Plugin import java.lang.reflect.Type @@ -87,7 +87,7 @@ class N5HDF5ReaderAdapter : StatefulSerializer.SerializerAndDeserializer = HDF5Deserializer(projectDirectory) { file, overrideBlockSize, defaultBlockSize -> n5Factory.hdf5OverrideBlockSize(overrideBlockSize) n5Factory.hdf5DefaultBlockSize(*(defaultBlockSize ?: intArrayOf())) - (getReaderOrWriterIfN5ContainerExists(file) as? N5HDF5Reader) ?: throw hdf5OpenError(file) + (getReaderOrGetWriterIfExistsAndWritable(file) as? N5HDF5Reader) ?: throw hdf5OpenError(file) } override fun getTargetClass() = N5HDF5Reader::class.java diff --git a/src/main/kotlin/org/janelia/saalfeldlab/paintera/state/metadata/MetadataState.kt b/src/main/kotlin/org/janelia/saalfeldlab/paintera/state/metadata/MetadataState.kt index 501f54b0a..99613bcee 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/paintera/state/metadata/MetadataState.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/paintera/state/metadata/MetadataState.kt @@ -18,7 +18,7 @@ import org.janelia.saalfeldlab.paintera.state.metadata.MetadataState.Companion.i import org.janelia.saalfeldlab.util.n5.ImagesWithTransform import org.janelia.saalfeldlab.util.n5.N5Data import org.janelia.saalfeldlab.util.n5.N5Helpers -import org.janelia.saalfeldlab.util.n5.N5Helpers.getReaderOrWriterIfN5ContainerExists +import org.janelia.saalfeldlab.util.n5.N5Helpers.getReaderOrGetWriterIfExistsAndWritable import org.janelia.saalfeldlab.util.n5.metadata.N5PainteraDataMultiScaleGroup import java.util.Optional @@ -229,7 +229,7 @@ class MetadataUtils { @JvmStatic fun createMetadataState(n5containerAndDataset: String): Optional { - val reader = getReaderOrWriterIfN5ContainerExists(n5containerAndDataset) ?: return Optional.empty() + val reader = getReaderOrGetWriterIfExistsAndWritable(n5containerAndDataset) ?: return Optional.empty() val n5ContainerState = N5ContainerState(reader) return N5Helpers.parseMetadata(reader).map { treeNode -> @@ -243,7 +243,7 @@ class MetadataUtils { @JvmStatic fun createMetadataState(n5container: String, dataset: String?): Optional { - val reader = getReaderOrWriterIfN5ContainerExists(n5container) ?: return Optional.empty() + val reader = getReaderOrGetWriterIfExistsAndWritable(n5container) ?: return Optional.empty() val n5ContainerState = N5ContainerState(reader) val metadataRoot = N5Helpers.parseMetadata(reader) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt b/src/main/kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt index dd8d1a407..d0ffc230a 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt @@ -779,9 +779,9 @@ object N5Helpers { * @return [N5Writer] or [N5Reader] if container exists */ @JvmStatic - fun getReaderOrWriterIfN5ContainerExists(uri: String): N5Reader? { + fun getReaderOrGetWriterIfExistsAndWritable(uri: String): N5Reader? { val cachedContainer = getReaderOrWriterIfCached(uri) - return cachedContainer ?: openReaderOrWriterIfContainerExists(uri) + return cachedContainer ?: openReaderOrGetWriterIfExistsAndWritable(uri) } /** @@ -792,7 +792,7 @@ object N5Helpers { */ @JvmStatic fun getWriterIfN5ContainerExists(uri: String): N5Writer? { - return getReaderOrWriterIfN5ContainerExists(uri) as? N5Writer + return getReaderOrGetWriterIfExistsAndWritable(uri) as? N5Writer } /** @@ -805,7 +805,7 @@ object N5Helpers { * @param container the path to the container * @return a reader or writer as N5Reader, or null if the container does not exist */ - private fun openReaderOrWriterIfContainerExists(container: String) = try { + private fun openReaderOrGetWriterIfExistsAndWritable(container: String) = try { n5Factory.openReader(container)?.let { var reader: N5Reader? = it if (it is N5HDF5Reader) { From 5981ef9c5b2fdb68bd59e457a0b7cc254b7d7f01 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 25 Aug 2023 15:11:14 -0400 Subject: [PATCH 5/9] feat: ensure N5Readers are valid, and N5Writers are writable before caching --- .../util/n5/universe/N5FactoryWithCache.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/util/n5/universe/N5FactoryWithCache.java b/src/main/java/org/janelia/saalfeldlab/util/n5/universe/N5FactoryWithCache.java index ca00129a9..f83ac39c3 100644 --- a/src/main/java/org/janelia/saalfeldlab/util/n5/universe/N5FactoryWithCache.java +++ b/src/main/java/org/janelia/saalfeldlab/util/n5/universe/N5FactoryWithCache.java @@ -59,7 +59,10 @@ public class N5FactoryWithCache extends N5Factory { readerCache.remove(url).close(); } } else { - readerCache.put(url, super.openReader(url)); + final N5Reader reader = super.openReader(url); + if (reader.getAttribute("/", N5Reader.VERSION_KEY, String.class) != null) { + readerCache.put(url, reader); + } } return readerCache.get(url); } @@ -67,10 +70,11 @@ public class N5FactoryWithCache extends N5Factory { @Override public N5Writer openWriter(String url) { /* Check if writer is valid (it may have been closed by someone) */ - final N5Reader cachedContainer = writerCache.get(url); + final N5Writer cachedContainer = writerCache.get(url); if (cachedContainer != null) { try { - cachedContainer.getVersion(); + /* See if its open, and we still have write permissions */ + cachedContainer.setAttribute("/", N5Reader.VERSION_KEY, cachedContainer.getVersion().toString()); } catch (Exception e) { writerCache.remove(url).close(); if (readerCache.get(url) == cachedContainer) { @@ -79,6 +83,8 @@ public class N5FactoryWithCache extends N5Factory { } } else { final N5Writer n5Writer = super.openWriter(url); + /* See if we have write permissions before we declare success */ + n5Writer.setAttribute("/", N5Reader.VERSION_KEY, n5Writer.getVersion()); writerCache.put(url, n5Writer); if (readerCache.get(url) != null) { readerCache.remove(url).close(); From 74e1fabe7851a3df358db86c31c8913ade7eafff Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 25 Aug 2023 15:11:17 -0400 Subject: [PATCH 6/9] feat: better handle unavailable containers --- .../janelia/saalfeldlab/util/n5/N5Helpers.kt | 103 +++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt b/src/main/kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt index d0ffc230a..f61ff7c12 100644 --- a/src/main/kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt +++ b/src/main/kotlin/org/janelia/saalfeldlab/util/n5/N5Helpers.kt @@ -5,14 +5,27 @@ import com.google.gson.JsonElement import com.google.gson.JsonObject import javafx.beans.property.BooleanProperty import javafx.beans.value.ChangeListener +import javafx.event.EventHandler +import javafx.scene.control.Button +import javafx.scene.control.ButtonType +import javafx.scene.control.Label +import javafx.scene.control.TextArea +import javafx.scene.control.TextField +import javafx.scene.layout.HBox +import javafx.scene.layout.Priority +import javafx.scene.layout.VBox +import javafx.stage.DirectoryChooser import net.imglib2.img.cell.CellGrid import net.imglib2.realtransform.AffineTransform3D import net.imglib2.realtransform.ScaleAndTranslation import net.imglib2.realtransform.Translation3D +import org.janelia.saalfeldlab.fx.util.InvokeOnJavaFXApplicationThread import org.janelia.saalfeldlab.labels.blocks.LabelBlockLookup import org.janelia.saalfeldlab.labels.blocks.LabelBlockLookupAdapter import org.janelia.saalfeldlab.labels.blocks.n5.LabelBlockLookupFromN5Relative import org.janelia.saalfeldlab.n5.DatasetAttributes +import org.janelia.saalfeldlab.n5.N5Exception +import org.janelia.saalfeldlab.n5.N5Exception.N5IOException import org.janelia.saalfeldlab.n5.N5Reader import org.janelia.saalfeldlab.n5.N5Writer import org.janelia.saalfeldlab.n5.hdf5.N5HDF5Reader @@ -32,6 +45,7 @@ import org.janelia.saalfeldlab.paintera.state.metadata.MetadataState import org.janelia.saalfeldlab.paintera.state.metadata.MetadataUtils.Companion.metadataIsValid import org.janelia.saalfeldlab.paintera.state.metadata.MultiScaleMetadataState import org.janelia.saalfeldlab.paintera.state.raw.n5.SerializationKeys +import org.janelia.saalfeldlab.paintera.ui.PainteraAlerts import org.janelia.saalfeldlab.paintera.util.n5.metadata.LabelBlockLookupGroup import org.janelia.saalfeldlab.util.NamedThreadFactory import org.janelia.saalfeldlab.util.n5.metadata.N5PainteraDataMultiScaleMetadata.PainteraDataMultiScaleParser @@ -45,6 +59,7 @@ import java.util.* import java.util.List import java.util.concurrent.ExecutorService import java.util.concurrent.Executors +import java.util.concurrent.atomic.AtomicReference import java.util.function.* import kotlin.collections.ArrayList import kotlin.collections.HashMap @@ -55,6 +70,7 @@ import kotlin.collections.filter import kotlin.collections.indices import kotlin.collections.isNotEmpty import kotlin.collections.map +import kotlin.collections.plusAssign import kotlin.collections.set import kotlin.collections.sortBy import kotlin.collections.toDoubleArray @@ -818,7 +834,15 @@ object N5Helpers { reader ?: n5Factory.openReader(container) } } + } catch (e: N5IOException) { + if (e.message?.startsWith("No container exists at") == true) { + throw N5ContainerDoesntExist(container, e) + } else { + LOG.debug("Cannot Open Reader", e) + null + } } catch (e: Exception) { + LOG.debug("Cannot Open Container At: $container", e) null } @@ -854,6 +878,83 @@ object N5Helpers { ?.asJsonObject?.let { it.get("basePath") ?: it.get("file") } ?.asString val uri = fromClassInfo ?: json[URI]?.asString ?: paintera.projectDirectory.actualDirectory.toURI().toString() - return N5Helpers.getReaderOrWriterIfN5ContainerExists(uri)!! + return getN5ContainerWithRetryPrompt(uri) + } + + internal fun getN5ContainerWithRetryPrompt(uri : String) : N5Reader { + return try { + getReaderOrGetWriterIfExistsAndWritable(uri)!! + } catch (e : N5ContainerDoesntExist) { + promptForNewLocationOrRemove(uri, e) + } + } + + internal fun promptForNewLocationOrRemove(uri: String, cause : Throwable) : N5Reader { + var exception : () -> Throwable = { cause } + val n5Container = AtomicReference() + InvokeOnJavaFXApplicationThread.invokeAndWait { + PainteraAlerts.confirmation("Accept", "Quit", true).also { alert -> + alert.headerText = "Container Not Found" + alert.contentText = """ + N5 container does not exist at $uri + If the container has moved, specify it's new location. + If the container no longer exists, you can remove this source. + """.trimIndent() + + alert.buttonTypes.add(ButtonType.FINISH) + (alert.dialogPane.lookupButton(ButtonType.FINISH) as Button).apply { + text = "Remove Source" + onAction = EventHandler { + it.consume() + alert.close() + exception = { RemoveSourceException(uri) } + } + } + + val newLocationField = TextField() + (alert.dialogPane.lookupButton(ButtonType.OK) as Button).apply { + disableProperty().bind(newLocationField.textProperty().isEmpty) + onAction = EventHandler { + it.consume() + alert.close() + n5Container.set(getN5ContainerWithRetryPrompt(newLocationField.textProperty().get())) + } + } + + + alert.dialogPane.content = VBox().apply { + children += HBox().apply { + children += TextArea("Invalid Location:\n\t$uri").also { it.editableProperty().set(false) } + } + children += HBox().apply { + children += Label("New Location ").also { HBox.setHgrow(it, Priority.NEVER) } + children += newLocationField + newLocationField.maxWidth = Double.MAX_VALUE + HBox.setHgrow(newLocationField, Priority.ALWAYS) + children += Button("Browse").also { + HBox.setHgrow(it, Priority.NEVER) + it.onAction = EventHandler { + DirectoryChooser().showDialog(alert.owner)?.let { newLocationField.textProperty().set(it.canonicalPath) } + } + } + } + } + alert.showAndWait() + } + } + return n5Container.get() ?: throw exception() + } + + class RemoveSourceException : PainteraException { + + constructor(location: String) : super("Source expected at:\n$location\nshould be removed") + constructor(location: String, cause: Throwable) : super("Source expected at:\n$location\nshould be removed", cause) + + } + + class N5ContainerDoesntExist : N5Exception { + + constructor(location: String) : super("Cannot Open $location") + constructor(location: String, cause: Throwable) : super("Cannot Open $location", cause) } } From c6a6f4768b44bbff8d6c3453b402f8d93ff3b467 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 25 Aug 2023 15:11:23 -0400 Subject: [PATCH 7/9] feat: support source state removal by exception handling --- .../serialization/SourceInfoSerializer.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/serialization/SourceInfoSerializer.java b/src/main/java/org/janelia/saalfeldlab/paintera/serialization/SourceInfoSerializer.java index d315c14f6..c9e6b3282 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/serialization/SourceInfoSerializer.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/serialization/SourceInfoSerializer.java @@ -16,7 +16,7 @@ import org.janelia.saalfeldlab.paintera.serialization.sourcestate.SourceStateSerialization; import org.janelia.saalfeldlab.paintera.state.SourceInfo; import org.janelia.saalfeldlab.paintera.state.SourceState; -import org.janelia.saalfeldlab.paintera.state.raw.ConnectomicsRawState; +import org.janelia.saalfeldlab.util.n5.N5Helpers; import org.scijava.plugin.Plugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -157,9 +157,11 @@ public static void populate( } final SourceState[] sourceStates = new SourceState[numStates]; + final boolean[] removeState = new boolean[numStates]; for (int i = 0; i < numStates && Arrays.stream(sourceStates).anyMatch(Objects::isNull); ++i) { for (int k = 0; k < numStates; ++k) { + if (removeState[k]) continue; if (sourceStates[k] == null) { final SourceState[] dependencies = IntStream .of(dependsOn[k].toArray()) @@ -180,18 +182,36 @@ public static void populate( @SuppressWarnings("unchecked") final Class> clazz = (Class>)Class .forName(state.get(STATE_TYPE_KEY).getAsString()); LOG.debug("Deserializing state={}, class={}", state, clazz); - sourceStates[k] = gson.fromJson(state.get(STATE_KEY), clazz); + try { + sourceStates[k] = gson.fromJson(state.get(STATE_KEY), clazz); + } catch (Exception e) { + //noinspection ConstantValue + if (e instanceof N5Helpers.RemoveSourceException) { + LOG.info("Removing source: {}", stateName); + sourceStates[k] = null; + removeState[k] = true; + continue; + } else { + throw e; + } + } logSourceForDependencies.accept(k, sourceStates[k]); } } } } - if (Arrays.stream(sourceStates).anyMatch(Objects::isNull)) { - throw new RuntimeException("Unable to deserialize all source states"); + final ArrayList> states = new ArrayList<>(); + for (int i = 0; i < sourceStates.length; i++) { + final SourceState sourceState = sourceStates[i]; + if (sourceState == null && !removeState[i]) + throw new RuntimeException("Unable to deserialize all source states"); + else if (sourceState != null) + states.add(sourceState); + } - return sourceStates; + return states.toArray(SourceState[]::new); } From a6f50a7cab0b8f2c0bc27e91439352fe2d17c0cb Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 25 Aug 2023 15:11:29 -0400 Subject: [PATCH 8/9] feat: select all by default --- .../paintera/ui/source/mesh/SegmentMeshExporterDialog.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/janelia/saalfeldlab/paintera/ui/source/mesh/SegmentMeshExporterDialog.java b/src/main/java/org/janelia/saalfeldlab/paintera/ui/source/mesh/SegmentMeshExporterDialog.java index 3b4915bcc..5740906f4 100644 --- a/src/main/java/org/janelia/saalfeldlab/paintera/ui/source/mesh/SegmentMeshExporterDialog.java +++ b/src/main/java/org/janelia/saalfeldlab/paintera/ui/source/mesh/SegmentMeshExporterDialog.java @@ -191,8 +191,9 @@ private void createMultiIdsDialog(final ObservableList ids) { int row = createCommonDialog(contents); checkListView.setItems(ids); - checkListView.prefHeightProperty() - .bind(Bindings.size(checkListView.itemsProperty().get()).multiply(LIST_CELL_HEIGHT)); + checkListView.getSelectionModel().selectAll(); + selectAll.setSelected(true); + checkListView.prefHeightProperty().bind(Bindings.size(checkListView.itemsProperty().get()).multiply(LIST_CELL_HEIGHT)); final Button button = new Button("Browse"); button.setOnAction(event -> { From b177fc3a6ae84da72adbc983c68a298d278ff724 Mon Sep 17 00:00:00 2001 From: Caleb Hulbert Date: Fri, 25 Aug 2023 15:11:32 -0400 Subject: [PATCH 9/9] fix: don't queue unnecessarily until paintable --- src/main/java/bdv/fx/viewer/ViewerPanelFX.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/bdv/fx/viewer/ViewerPanelFX.java b/src/main/java/bdv/fx/viewer/ViewerPanelFX.java index 3a9cce6c1..e46d04d3b 100644 --- a/src/main/java/bdv/fx/viewer/ViewerPanelFX.java +++ b/src/main/java/bdv/fx/viewer/ViewerPanelFX.java @@ -190,7 +190,7 @@ public ViewerPanelFX( transformListeners.add(tf -> Paintera.whenPaintable(getDisplay()::drawOverlays)); this.state = new ViewerState(numTimepoints, this); - state.addListener(obs -> Paintera.whenPaintable(this::requestRepaint)); + state.addListener(obs -> Paintera.ifPaintable(this::requestRepaint)); Paintera.whenPaintable(() -> getDisplay().drawOverlays());