Skip to content

Commit

Permalink
Make layer order editable via drag and drop (#7188)
Browse files Browse the repository at this point in the history
* make layer settings items sortable via drag

* keep grabbing cursor during dragging of layersettings

* move layer order to dataset config

* render layers in configured order

* fix color layer accessing

* save layer order in backend and fix tests

* only make color layers draggable

* added changelog entry

* rename field to colorLayerOrder and fix CI

* apply review feedback and only enable layer reordering in cover mode

* apply code review suggestion
  • Loading branch information
MichaelBuessemeyer authored Jul 27, 2023
1 parent 571cde6 commit 65f15f4
Show file tree
Hide file tree
Showing 12 changed files with 227 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ For upgrade instructions, please check the [migration guide](MIGRATIONS.released
[Commits](https://github.com/scalableminds/webknossos/compare/23.08.0...HEAD)

### Added
- Added the option to change the ordering of color layers via drag and drop. This is useful when using the cover blend mode. [#7188](https://github.com/scalableminds/webknossos/pull/7188)
- Added configuration to require users' emails to be verified, added flow to verify emails via link. [#7161](https://github.com/scalableminds/webknossos/pull/7161)

### Changed
Expand Down
2 changes: 1 addition & 1 deletion frontend/javascripts/messages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const settingsTooltips: Partial<Record<keyof RecommendedConfiguration, st
zoom: "Zoom in or out in the data viewports",
displayScalebars: "Show a scale in the lower-right corner of each viewport",
blendMode:
"Set the blend mode for the dataset. The additive mode (default) adds the data values of all color layers. In cover mode, color layers are rendered on top of each other so that the data values of lower color layers are hidden by values of higher layers.",
"Set the blend mode for the dataset. The additive mode (default) adds the data values of all color layers. In cover mode, color layers are rendered on top of each other so that the data values of lower color layers are hidden by values of higher layers. Cover mode enables reordering of color layers.",
renderWatermark: "Show a WEBKNOSSOS logo in the lower-left corner of each screenshot.",
antialiasRendering: "Antialias rendering (can impact performance)",
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,23 @@ class PlaneMaterialFactory {
),
);

let oldLayerOrder: Array<string> = [];
this.storePropertyUnsubscribers.push(
listenToStoreProperty(
(state) => state.datasetConfiguration.colorLayerOrder,
(colorLayerOrder) => {
let changedLayerOrder =
colorLayerOrder.length !== oldLayerOrder.length ||
colorLayerOrder.some((layerName, index) => layerName !== oldLayerOrder[index]);
if (changedLayerOrder) {
oldLayerOrder = [...colorLayerOrder];
this.recomputeShaders();
}
app.vent.emit("rerender");
},
false,
),
);
if (Model.hasSegmentationLayer()) {
this.storePropertyUnsubscribers.push(
listenToStoreProperty(
Expand Down Expand Up @@ -864,7 +881,9 @@ class PlaneMaterialFactory {
app.vent.emit("rerender");
}, RECOMPILATION_THROTTLE_TIME);

getLayersToRender(maximumLayerCountToRender: number): [Array<string>, Array<string>, number] {
getLayersToRender(
maximumLayerCountToRender: number,
): [Array<string>, Array<string>, Array<string>, number] {
// This function determines for which layers
// the shader code should be compiled. If the GPU supports
// all layers, we can simply return all layers here.
Expand All @@ -874,21 +893,28 @@ class PlaneMaterialFactory {
// The first array contains the color layer names and the second the segmentation layer names.
// The third parameter returns the number of globally available layers (this is not always equal
// to the sum of the lengths of the first two arrays, as not all layers might be rendered.)
const state = Store.getState();
const sanitizedOrderedColorLayerNames =
state.datasetConfiguration.colorLayerOrder.map(sanitizeName);
const colorLayerNames = getSanitizedColorLayerNames();
const segmentationLayerNames = Model.getSegmentationLayers().map((layer) =>
sanitizeName(layer.name),
);
const globalLayerCount = colorLayerNames.length + segmentationLayerNames.length;
if (maximumLayerCountToRender <= 0) {
return [[], [], globalLayerCount];
return [[], [], [], globalLayerCount];
}

if (maximumLayerCountToRender >= globalLayerCount) {
// We can simply render all available layers.
return [colorLayerNames, segmentationLayerNames, globalLayerCount];
return [
colorLayerNames,
segmentationLayerNames,
sanitizedOrderedColorLayerNames,
globalLayerCount,
];
}

const state = Store.getState();
const enabledLayers = getEnabledLayers(state.dataset, state.datasetConfiguration, {}).map(
({ name, category }) => ({ name, isSegmentationLayer: category === "segmentation" }),
);
Expand Down Expand Up @@ -918,7 +944,12 @@ class PlaneMaterialFactory {
({ isSegmentationLayer }) => !isSegmentationLayer,
).map((layers) => layers.map(({ name }) => sanitizeName(name)));

return [sanitizedColorLayerNames, sanitizedSegmentationLayerNames, globalLayerCount];
return [
sanitizedColorLayerNames,
sanitizedSegmentationLayerNames,
sanitizedOrderedColorLayerNames,
globalLayerCount,
];
}

onDisableLayer = (layerName: string, isSegmentationLayer: boolean) => {
Expand All @@ -939,7 +970,7 @@ class PlaneMaterialFactory {

getFragmentShaderWithUniforms(): [string, Uniforms] {
const { maximumLayerCountToRender } = Store.getState().temporaryConfiguration.gpuSetup;
const [colorLayerNames, segmentationLayerNames, globalLayerCount] =
const [colorLayerNames, segmentationLayerNames, orderedColorLayerNames, globalLayerCount] =
this.getLayersToRender(maximumLayerCountToRender);

const availableLayerNames = colorLayerNames.concat(segmentationLayerNames);
Expand All @@ -953,6 +984,7 @@ class PlaneMaterialFactory {
const datasetScale = dataset.dataSource.scale;
const code = getMainFragmentShader({
globalLayerCount,
orderedColorLayerNames,
colorLayerNames,
segmentationLayerNames,
textureLayerInfos,
Expand All @@ -978,7 +1010,7 @@ class PlaneMaterialFactory {

getVertexShader(): string {
const { maximumLayerCountToRender } = Store.getState().temporaryConfiguration.gpuSetup;
const [colorLayerNames, segmentationLayerNames, globalLayerCount] =
const [colorLayerNames, segmentationLayerNames, orderedColorLayerNames, globalLayerCount] =
this.getLayersToRender(maximumLayerCountToRender);

const textureLayerInfos = getTextureLayerInfos();
Expand All @@ -987,6 +1019,7 @@ class PlaneMaterialFactory {

return getMainVertexShader({
globalLayerCount,
orderedColorLayerNames,
colorLayerNames,
segmentationLayerNames,
textureLayerInfos,
Expand Down
27 changes: 26 additions & 1 deletion frontend/javascripts/oxalis/model_initialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
getSegmentationLayers,
getLayerByName,
getSegmentationLayerByName,
isColorLayer,
} from "oxalis/model/accessors/dataset_accessor";
import { getNullableSkeletonTracing } from "oxalis/model/accessors/skeletontracing_accessor";
import { getServerVolumeTracings } from "oxalis/model/accessors/volumetracing_accessor";
Expand Down Expand Up @@ -180,11 +181,15 @@ export async function initialize(
dataset,
initialDatasetSettings,
);
const initialDatasetSettingsWithLayerOrder = ensureDatasetSettingsHasLayerOrder(
annotationSpecificDatasetSettings,
dataset,
);
const enforcedInitialUserSettings =
enforcePricingRestrictionsOnUserConfiguration(initialUserSettings);
initializeSettings(
enforcedInitialUserSettings,
annotationSpecificDatasetSettings,
initialDatasetSettingsWithLayerOrder,
initialDatasetSettings,
);
let initializationInformation = null;
Expand Down Expand Up @@ -787,6 +792,26 @@ function enforcePricingRestrictionsOnUserConfiguration(
return userConfiguration;
}

function ensureDatasetSettingsHasLayerOrder(
datasetConfiguration: DatasetConfiguration,
dataset: APIDataset,
): DatasetConfiguration {
const colorLayerNames = _.keys(datasetConfiguration.layers).filter((layerName) =>
isColorLayer(dataset, layerName),
);
const onlyExistingLayers =
datasetConfiguration?.colorLayerOrder?.filter(
(layerName) => colorLayerNames.indexOf(layerName) >= 0,
) || [];
if (onlyExistingLayers.length < colorLayerNames.length) {
return {
...datasetConfiguration,
colorLayerOrder: colorLayerNames,
};
}
return { ...datasetConfiguration, colorLayerOrder: onlyExistingLayers };
}

function applyAnnotationSpecificViewConfiguration(
annotation: APIAnnotation | null | undefined,
dataset: APIDataset,
Expand Down
6 changes: 4 additions & 2 deletions frontend/javascripts/oxalis/shaders/main_data_shaders.glsl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
type Params = {
globalLayerCount: number;
colorLayerNames: string[];
orderedColorLayerNames: string[];
segmentationLayerNames: string[];
textureLayerInfos: Record<string, { packingDegree: number; dataTextureCount: number }>;
resolutionsCount: number;
Expand Down Expand Up @@ -202,7 +203,8 @@ void main() {
// Get Color Value(s)
vec3 color_value = vec3(0.0);
<% _.each(colorLayerNames, function(name, layerIndex) { %>
<% _.each(orderedColorLayerNames, function(name, layerIndex) { %>
<% const color_layer_index = colorLayerNames.indexOf(name); %>
float <%= name %>_effective_alpha = <%= name %>_alpha * (1. - <%= name %>_unrenderable);
if (<%= name %>_effective_alpha > 0.) {
// Get grayscale value for <%= name %>
Expand All @@ -216,7 +218,7 @@ void main() {
if (!isOutsideOfBoundingBox(transformedCoordUVW)) {
MaybeFilteredColor maybe_filtered_color =
getMaybeFilteredColorOrFallback(
<%= formatNumberAsGLSLFloat(layerIndex) %>,
<%= formatNumberAsGLSLFloat(color_layer_index) %>,
<%= name %>_data_texture_width,
<%= formatNumberAsGLSLFloat(textureLayerInfos[name].packingDegree) %>,
transformedCoordUVW,
Expand Down
1 change: 1 addition & 0 deletions frontend/javascripts/oxalis/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ export type DatasetConfiguration = {
readonly fourBit: boolean;
readonly interpolation: boolean;
readonly layers: Record<string, DatasetLayerConfiguration>;
readonly colorLayerOrder: Array<string>;
readonly position?: Vector3;
readonly zoom?: number;
readonly rotation?: Vector3;
Expand Down
Loading

0 comments on commit 65f15f4

Please sign in to comment.