diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md index b5735c90530..ba5fa3887ba 100644 --- a/CHANGELOG.unreleased.md +++ b/CHANGELOG.unreleased.md @@ -19,6 +19,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released - When adding remote Zarr datasets with multiple channels, channels are converted into layers. [#6609](https://github.com/scalableminds/webknossos/pull/6609) - When adding a remote OME-NGFF dataset with labels, these are added as segmentation layers. [#6638](https://github.com/scalableminds/webknossos/pull/6638) - The scale bar is now included in screenshots of the viewports made using the `Q` shortcut or the "Screenshot" menu entry. If the scale bar should not be included, disable it using "Settings - Viewport Options - Show Scalebars". [#6644](https://github.com/scalableminds/webknossos/pull/6644) +- When creating an annotation from the dataset view, a previously selected mapping of the segmentation layer is now automatically selected in the volume annotation layer fallback segmentation as well. [#6647](https://github.com/scalableminds/webknossos/pull/6647) ### Changed - The log viewer in the Voxelytics workflow reporting now uses a virtualized list. [#6579](https://github.com/scalableminds/webknossos/pull/6579) diff --git a/app/controllers/AnnotationController.scala b/app/controllers/AnnotationController.scala index f7bc63b1e10..49c86c8e78f 100755 --- a/app/controllers/AnnotationController.scala +++ b/app/controllers/AnnotationController.scala @@ -33,6 +33,7 @@ import scala.concurrent.duration._ case class AnnotationLayerParameters(typ: AnnotationLayerType, fallbackLayerName: Option[String], + mappingName: Option[String] = None, resolutionRestrictions: Option[ResolutionRestrictions], name: String) object AnnotationLayerParameters { diff --git a/app/controllers/LegacyApiController.scala b/app/controllers/LegacyApiController.scala index 86ecbdc71a4..6b98213b759 100644 --- a/app/controllers/LegacyApiController.scala +++ b/app/controllers/LegacyApiController.scala @@ -427,6 +427,7 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, Some( AnnotationLayerParameters(AnnotationLayerType.Skeleton, request.body.fallbackLayerName, + None, request.body.resolutionRestrictions, name = AnnotationLayer.defaultSkeletonLayerName)) val volumeParameters = @@ -435,6 +436,7 @@ class LegacyApiController @Inject()(annotationController: AnnotationController, Some( AnnotationLayerParameters(AnnotationLayerType.Volume, request.body.fallbackLayerName, + None, request.body.resolutionRestrictions, name = AnnotationLayer.defaultVolumeLayerName)) List(skeletonParameters, volumeParameters).flatten diff --git a/app/models/annotation/AnnotationService.scala b/app/models/annotation/AnnotationService.scala index 03ca5b9cc51..0b214ea80a6 100755 --- a/app/models/annotation/AnnotationService.scala +++ b/app/models/annotation/AnnotationService.scala @@ -139,7 +139,8 @@ class AnnotationService @Inject()( boundingBox: Option[BoundingBox] = None, startPosition: Option[Vec3Int] = None, startRotation: Option[Vec3Double] = None, - resolutionRestrictions: ResolutionRestrictions + resolutionRestrictions: ResolutionRestrictions, + mappingName: Option[String] ): Fox[VolumeTracing] = { val resolutions = VolumeTracingDownsampling.resolutionsForVolumeTracing(dataSource, fallbackLayer) val resolutionsRestricted = resolutionRestrictions.filterAllowed(resolutions) @@ -160,6 +161,7 @@ class AnnotationService @Inject()( 0, VolumeTracingDefaults.zoomLevel, organizationName = Some(datasetOrganizationName), + mappingName = mappingName, resolutions = resolutionsRestricted.map(vec3IntToProto) ) } @@ -249,7 +251,8 @@ class AnnotationService @Inject()( datasetOrganizationName, fallbackLayer, resolutionRestrictions = - annotationLayerParameters.resolutionRestrictions.getOrElse(ResolutionRestrictions.empty) + annotationLayerParameters.resolutionRestrictions.getOrElse(ResolutionRestrictions.empty), + mappingName = annotationLayerParameters.mappingName ) volumeTracingAdapted = oldPrecedenceLayerProperties.map { p => volumeTracing.copy( @@ -361,6 +364,7 @@ class AnnotationService @Inject()( newAnnotationLayerParameters = AnnotationLayerParameters( newAnnotationLayerType, usedFallbackLayerName, + None, Some(ResolutionRestrictions.empty), AnnotationLayer.defaultNameForType(newAnnotationLayerType)) _ <- addAnnotationLayer(annotation, organizationName, newAnnotationLayerParameters) ?~> "makeHybrid.createTracings.failed" @@ -524,7 +528,8 @@ class AnnotationService @Inject()( }, startPosition = Some(startPosition), startRotation = Some(startRotation), - resolutionRestrictions = resolutionRestrictions + resolutionRestrictions = resolutionRestrictions, + mappingName = None ) } yield volumeTracing diff --git a/frontend/javascripts/admin/admin_rest_api.ts b/frontend/javascripts/admin/admin_rest_api.ts index f66114c315a..ba3b77bf341 100644 --- a/frontend/javascripts/admin/admin_rest_api.ts +++ b/frontend/javascripts/admin/admin_rest_api.ts @@ -661,6 +661,7 @@ type AnnotationLayerCreateDescriptor = { typ: "Skeleton" | "Volume"; name: string; fallbackLayerName?: string | null | undefined; + mappingName?: string | null | undefined; resolutionRestrictions?: APIResolutionRestrictions | null | undefined; }; @@ -789,6 +790,7 @@ export function createExplorational( datasetId: APIDatasetId, typ: TracingType, fallbackLayerName?: string | null | undefined, + mappingName?: string | null | undefined, resolutionRestrictions?: APIResolutionRestrictions | null | undefined, options: RequestOptions = {}, ): Promise { @@ -808,6 +810,7 @@ export function createExplorational( typ: "Volume", name: fallbackLayerName || "Volume", fallbackLayerName, + mappingName, resolutionRestrictions, }, ]; @@ -821,6 +824,7 @@ export function createExplorational( typ: "Volume", name: fallbackLayerName || "Volume", fallbackLayerName, + mappingName, resolutionRestrictions, }, ]; diff --git a/frontend/javascripts/oxalis/view/action_bar_view.tsx b/frontend/javascripts/oxalis/view/action_bar_view.tsx index 36d544dc4a7..def5de4ca0b 100644 --- a/frontend/javascripts/oxalis/view/action_bar_view.tsx +++ b/frontend/javascripts/oxalis/view/action_bar_view.tsx @@ -12,7 +12,7 @@ import { import { trackAction } from "oxalis/model/helpers/analytics"; import AddNewLayoutModal from "oxalis/view/action-bar/add_new_layout_modal"; import { withAuthentication } from "admin/auth/authentication_modal"; -import type { ViewMode, ControlMode } from "oxalis/constants"; +import { ViewMode, ControlMode, MappingStatusEnum } from "oxalis/constants"; import constants, { ControlModeEnum } from "oxalis/constants"; import DatasetPositionView from "oxalis/view/action-bar/dataset_position_view"; import type { OxalisState } from "oxalis/store"; @@ -26,6 +26,7 @@ import { is2dDataset, doesSupportVolumeWithFallback, getVisibleSegmentationLayer, + getMappingInfoForSupportedLayer, } from "oxalis/model/accessors/dataset_accessor"; import { AsyncButton } from "components/async_clickables"; @@ -96,7 +97,22 @@ class ActionBarView extends React.PureComponent { maybeSegmentationLayer && doesSupportVolumeWithFallback(dataset, maybeSegmentationLayer) ? maybeSegmentationLayer.name : null; - const annotation = await createExplorational(dataset, "hybrid", fallbackLayerName); + + const mappingInfo = getMappingInfoForSupportedLayer(Store.getState()); + let maybeMappingName = null; + if ( + mappingInfo.mappingStatus !== MappingStatusEnum.DISABLED && + mappingInfo.mappingType === "HDF5" + ) { + maybeMappingName = mappingInfo.mappingName; + } + + const annotation = await createExplorational( + dataset, + "hybrid", + fallbackLayerName, + maybeMappingName, + ); trackAction("Create hybrid tracing (from view mode)"); location.href = `${location.origin}/annotations/${annotation.typ}/${annotation.id}${location.hash}`; }; diff --git a/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx b/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx index 8701a2e45fe..e051724a69c 100644 --- a/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx +++ b/frontend/javascripts/oxalis/view/left-border-tabs/modals/add_volume_layer_modal.tsx @@ -8,11 +8,12 @@ import { NewVolumeLayerSelection, RestrictResolutionSlider, } from "dashboard/advanced_dataset/create_explorative_modal"; -import type { Tracing } from "oxalis/store"; +import Store, { type Tracing } from "oxalis/store"; import { addAnnotationLayer } from "admin/admin_rest_api"; import { getDatasetResolutionInfo, getLayerByName, + getMappingInfo, getSegmentationLayers, } from "oxalis/model/accessors/dataset_accessor"; import { @@ -23,6 +24,7 @@ import messages from "messages"; import InputComponent from "oxalis/view/components/input_component"; import api from "oxalis/api/internal_api"; import Toast from "libs/toast"; +import { MappingStatusEnum } from "oxalis/constants"; export type ValidationResult = { isValid: boolean; message: string }; export function checkForLayerNameDuplication( @@ -162,6 +164,19 @@ export default function AddVolumeLayerModal({ } else { selectedSegmentationLayer = getLayerByName(dataset, selectedSegmentationLayerName); const fallbackLayerName = selectedSegmentationLayer.name; + + const mappingInfo = getMappingInfo( + Store.getState().temporaryConfiguration.activeMappingByLayer, + selectedSegmentationLayerName, + ); + let maybeMappingName = null; + if ( + mappingInfo.mappingStatus !== MappingStatusEnum.DISABLED && + mappingInfo.mappingType === "HDF5" + ) { + maybeMappingName = mappingInfo.mappingName; + } + await addAnnotationLayer(tracing.annotationId, tracing.annotationType, { typ: "Volume", name: newLayerName, @@ -170,6 +185,7 @@ export default function AddVolumeLayerModal({ min: minResolutionAllowed, max: maxResolutionAllowed, }, + mappingName: maybeMappingName, }); } diff --git a/frontend/javascripts/router.tsx b/frontend/javascripts/router.tsx index 508df5f6248..852e96aab51 100644 --- a/frontend/javascripts/router.tsx +++ b/frontend/javascripts/router.tsx @@ -584,6 +584,7 @@ class ReactRouter extends React.Component { dataset, type, fallbackLayerName, + null, resolutionRestrictions, ); trackAction(`Create ${type} tracing`); diff --git a/frontend/javascripts/test/puppeteer/dataset_rendering_helpers.ts b/frontend/javascripts/test/puppeteer/dataset_rendering_helpers.ts index 2a9c83957ce..55ef5454ec9 100644 --- a/frontend/javascripts/test/puppeteer/dataset_rendering_helpers.ts +++ b/frontend/javascripts/test/puppeteer/dataset_rendering_helpers.ts @@ -84,6 +84,7 @@ async function _screenshotAnnotationHelper( typ, fallbackLayerName, null, + null, options, ); @@ -107,6 +108,7 @@ export async function screenshotDatasetWithMapping( "skeleton", null, null, + null, options, ); await openTracingView(page, baseUrl, createdExplorational.id); @@ -128,6 +130,7 @@ export async function screenshotDatasetWithMappingLink( "skeleton", null, null, + null, options, ); await openTracingView(page, baseUrl, createdExplorational.id, optionalViewOverride);