diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index 24c6e59fa5..5225af3bd7 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -11,6 +11,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released [Commits](https://github.com/scalableminds/webknossos/compare/22.06.0...HEAD) ### Added +Added a warning for when the resolution in the XY viewport on z=1-downsampled datasets becomes too low, explaining the problem and how to mitigate it. [#6255](https://github.com/scalableminds/webknossos/pull/6255) ### Changed - For the api routes that return annotation info objects, the user field was renamed to owner. User still exists as an alias, but will be removed in a future release. [#6250](https://github.com/scalableminds/webknossos/pull/6250) diff --git a/frontend/javascripts/messages.ts b/frontend/javascripts/messages.ts index 2a9575c078..28e4d7bd4f 100644 --- a/frontend/javascripts/messages.ts +++ b/frontend/javascripts/messages.ts @@ -284,6 +284,8 @@ instead. Only enable this option if you understand its effect. All layers will n "This dataset location is marked as 'scratch' and meant for testing only. Please move this dataset to a permanent storage location and reimport it.", "dataset.resolution_mismatch": "This dataset contains multiple layers which differ in their resolution. Please convert the layers to make their resolutions match. Otherwise, rendering errors cannot be avoided.", + "dataset.z1_downsampling_hint": + "The currently rendered quality is not optimal due to the available magnifications and the viewport arrangement. To improve the quality try to increase the size of the XY viewport (e.g. by maximizing it).", "annotation.finish": "Are you sure you want to permanently finish this annotation?", "annotation.was_finished": "Annotation was archived", "annotation.no_fallback_data_included": diff --git a/frontend/javascripts/oxalis/model/sagas/dataset_saga.ts b/frontend/javascripts/oxalis/model/sagas/dataset_saga.ts index ef48552aae..46d321d601 100644 --- a/frontend/javascripts/oxalis/model/sagas/dataset_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/dataset_saga.ts @@ -1,10 +1,11 @@ -/* eslint-disable import/prefer-default-export */ import type { Saga } from "oxalis/model/sagas/effect-generators"; import { select } from "oxalis/model/sagas/effect-generators"; -import { takeEvery } from "typed-redux-saga"; +import { call, take, takeEvery, takeLatest } from "typed-redux-saga"; +import { sleep } from "libs/utils"; import { getEnabledLayers } from "oxalis/model/accessors/dataset_accessor"; import Toast from "libs/toast"; import messages from "messages"; +import { getCurrentResolution } from "../accessors/flycam_accessor"; export function* watchMaximumRenderableLayers(): Saga { function* warnMaybe(): Saga { const maximumLayerCountToRender = yield* select( @@ -31,3 +32,44 @@ export function* watchMaximumRenderableLayers(): Saga { yield* takeEvery(["WK_READY", "UPDATE_LAYER_SETTING", "UPDATE_DATASET_SETTING"], warnMaybe); } + +let userClosedWarning = false; +export function* watchZ1Downsampling(): Saga { + function* maybeShowWarning(): Saga { + // In combination with `takeLatest` sleeping here at the beginning of the saga + // effectively debounces the saga to avoid that it is executed unnecessarily often. + yield* call(sleep, 200); + const currentRes = yield* select((state) => getCurrentResolution(state)); + const currentZoomStep = yield* select((state) => state.flycam.zoomStep); + if (currentZoomStep < 1) { + // If the user has zoomed into the data, + // the rendering quality is expected to be relatively low. + // Don't show any warnings in that case. + return; + } + const minVoxelPerPixel = 0.1; + if (!userClosedWarning) { + // checking only the downsampled dimensions x and y + const showWarning = + currentZoomStep / currentRes[0] < minVoxelPerPixel || + currentZoomStep / currentRes[1] < minVoxelPerPixel; + if (showWarning) { + Toast.warning(messages["dataset.z1_downsampling_hint"], { + sticky: true, + key: "DOWNSAMPLING_CAUSES_BAD_QUALITY", + onClose: () => { + userClosedWarning = true; + }, + }); + } else { + Toast.close("DOWNSAMPLING_CAUSES_BAD_QUALITY"); + } + } + } + yield* take("WK_READY"); + yield* call(maybeShowWarning); + yield* takeLatest( + ["ZOOM_IN", "ZOOM_OUT", "ZOOM_BY_DELTA", "SET_ZOOM_STEP", "SET_STORED_LAYOUTS"], + maybeShowWarning, + ); +} diff --git a/frontend/javascripts/oxalis/model/sagas/root_saga.ts b/frontend/javascripts/oxalis/model/sagas/root_saga.ts index 746f4c6c87..a8a3665c6c 100644 --- a/frontend/javascripts/oxalis/model/sagas/root_saga.ts +++ b/frontend/javascripts/oxalis/model/sagas/root_saga.ts @@ -9,7 +9,7 @@ import SkeletontracingSagas from "oxalis/model/sagas/skeletontracing_saga"; import ErrorHandling from "libs/error_handling"; import handleMeshChanges from "oxalis/model/sagas/handle_mesh_changes"; import isosurfaceSaga from "oxalis/model/sagas/isosurface_saga"; -import { watchMaximumRenderableLayers } from "oxalis/model/sagas/dataset_saga"; +import { watchMaximumRenderableLayers, watchZ1Downsampling } from "oxalis/model/sagas/dataset_saga"; import { watchToolDeselection } from "oxalis/model/sagas/annotation_tool_saga"; import SettingsSaga from "oxalis/model/sagas/settings_saga"; import watchTasksAsync, { warnAboutMagRestriction } from "oxalis/model/sagas/task_saga"; @@ -47,6 +47,7 @@ function* restartableSaga(): Saga { ...AnnotationSagas.map((saga) => call(saga)), ...SaveSagas.map((saga) => call(saga)), ...VolumetracingSagas.map((saga) => call(saga)), + call(watchZ1Downsampling), ]); } catch (err) { rootSagaCrashed = true;