diff --git a/CHANGELOG.md b/CHANGELOG.md index b3816eec83..d9d665815c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.md). - Added a button to the users list view that revokes admin rights from all selected users. [#3378](https://github.com/scalableminds/webknossos/pull/3378) - Hybrid tracings are now enabled by default. They allow to combine the functionality of skeleton and volume annotations in one tracing. [#3399](https://github.com/scalableminds/webknossos/pull/3399) - A User can now have multiple layouts for tracing views. [#3299](https://github.com/scalableminds/webknossos/pull/3299) +- Added support for datasets with sparse resolutions (e.g., [[1, 1, 1], [16, 16, 16]]). [#3406](https://github.com/scalableminds/webknossos/pull/3406) - The info tab in tracing views now displays the extent of the current dataset. [#3371](https://github.com/scalableminds/webknossos/pull/3371). ### Changed diff --git a/app/assets/javascripts/oxalis/model_initialization.js b/app/assets/javascripts/oxalis/model_initialization.js index 5fa437e6e2..2d429f7876 100644 --- a/app/assets/javascripts/oxalis/model_initialization.js +++ b/app/assets/javascripts/oxalis/model_initialization.js @@ -27,6 +27,7 @@ import * as Utils from "libs/utils"; import DataLayer from "oxalis/model/data_layer"; import ConnectionInfo from "oxalis/model/data_connection_info"; import { ControlModeEnum } from "oxalis/constants"; +import type { Vector3 } from "oxalis/constants"; import Toast from "libs/toast"; import ErrorHandling from "libs/error_handling"; import UrlManager from "oxalis/controller/url_manager"; @@ -252,9 +253,38 @@ function initializeDataset( dataset.dataSource.dataLayers = newDataLayers; }); + ensureDenseLayerResolutions(dataset); Store.dispatch(setDatasetAction(dataset)); } +function ensureDenseLayerResolutions(dataset: APIDataset) { + for (const layer of dataset.dataSource.dataLayers) { + layer.resolutions = convertToDenseResolution(layer.resolutions); + } +} + +function convertToDenseResolution(resolutions: Array) { + // Each resolution entry can be characterized by it's greatest resolution dimension. + // E.g., the resolution array [[1, 1, 1], [2, 2, 1], [4, 4, 2]] defines that + // a log zoomstep of 2 corresponds to the resolution [2, 2, 1] (and not [4, 4, 2]). + // Therefore, the largest dim for each resolution has to be unique across all resolutions. + + // This function returns an array of resolutions, for which each index will + // hold a resolution with highest_dim === 2**index. + + if (resolutions.length !== _.uniqBy(resolutions.map(_.max)).length) { + throw new Error("Max dimension in resolutions is not unique."); + } + const paddedResolutionCount = 1 + Math.log2(_.max(resolutions.map(v => _.max(v)))); + const resolutionsLookUp = _.keyBy(resolutions, _.max); + + return _.range(0, paddedResolutionCount).map(exp => { + const resPower = 2 ** exp; + // If the resolution does not exist, use a fallback resolution + return resolutionsLookUp[resPower] || [resPower, resPower, resPower]; + }); +} + function initializeSettings(initialUserSettings: Object, initialDatasetSettings: Object): void { Store.dispatch(initializeSettingsAction(initialUserSettings, initialDatasetSettings)); } diff --git a/flow-typed/npm/lodash_v4.x.x.js b/flow-typed/npm/lodash_v4.x.x.js index 79f573f4b9..ba249e7285 100644 --- a/flow-typed/npm/lodash_v4.x.x.js +++ b/flow-typed/npm/lodash_v4.x.x.js @@ -472,6 +472,7 @@ declare module "lodash" { divide(dividend: number, divisor: number): number; floor(number: number, precision?: number): number; max(array: ?Array): T; + max(array: [T, T, T]): T; maxBy(array: ?Array, iteratee?: Iteratee): T; mean(array: Array<*>): number; meanBy(array: Array, iteratee?: Iteratee): number;