Skip to content

Commit

Permalink
feat(segment-groups): add visibility toggle button
Browse files Browse the repository at this point in the history
closes #649
  • Loading branch information
PaulHax committed Sep 27, 2024
1 parent 5675ca4 commit 1ea9aa1
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 33 deletions.
26 changes: 26 additions & 0 deletions src/components/SegmentGroupControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
DataSelection,
} from '@/src/utils/dataSelection';
import { useSegmentGroupStore } from '@/src/store/segmentGroups';
import { useGlobalLayerColorConfig } from '@/src/composables/useGlobalLayerColorConfig';
import { usePaintToolStore } from '@/src/store/tools/paint';
import { Maybe } from '@/src/types';
import { reactive, ref, computed, watch, toRaw } from 'vue';
Expand All @@ -26,9 +27,20 @@ const currentSegmentGroups = computed(() => {
const { orderByParent, metadataByID } = segmentGroupStore;
if (!(currentImageID.value in orderByParent)) return [];
return orderByParent[currentImageID.value].map((id) => {
const { sampledConfig, updateConfig } = useGlobalLayerColorConfig(id);
return {
id,
name: metadataByID[id].name,
visibility: sampledConfig.value?.config?.blendConfig.visibility ?? true,
toggleVisibility: () => {
const currentBlend = sampledConfig.value!.config!.blendConfig;
updateConfig({
blendConfig: {
...currentBlend,
visibility: !currentBlend.visibility,
},
});
},
};
});
});
Expand Down Expand Up @@ -207,6 +219,20 @@ function openSaveDialog(id: string) {
<div class="d-flex flex-row align-center w-100" :title="group.name">
<span class="group-name">{{ group.name }}</span>
<v-spacer />
<v-btn
icon
variant="flat"
size="small"
@click.stop="group.toggleVisibility"
>
<v-icon v-if="group.visibility" style="pointer-events: none"
>mdi-eye</v-icon
>
<v-icon v-else style="pointer-events: none">mdi-eye-off</v-icon>
<v-tooltip location="left" activator="parent">{{
group.visibility ? 'Hide' : 'Show'
}}</v-tooltip>
</v-btn>
<v-btn
icon="mdi-content-save"
size="small"
Expand Down
42 changes: 13 additions & 29 deletions src/components/SegmentGroupOpacity.vue
Original file line number Diff line number Diff line change
@@ -1,47 +1,31 @@
<script setup lang="ts">
import { computed, toRefs } from 'vue';
import { BlendConfig } from '@/src/types/views';
import useLayerColoringStore from '@/src/store/view-configs/layers';
import { useSegmentGroupConfigInitializer } from '@/src/composables/useSegmentGroupConfigInitializer';
import { InitViewSpecs } from '../config';
import { useGlobalLayerColorConfig } from '@/src/composables/useGlobalLayerColorConfig';
const props = defineProps<{
groupId: string;
}>();
const { groupId } = toRefs(props);
const layerColoringStore = useLayerColoringStore();
const { sampledConfig, updateConfig } = useGlobalLayerColorConfig(groupId);
const VIEWS_2D = Object.entries(InitViewSpecs)
.filter(([, { viewType }]) => viewType === '2D')
.map(([viewID]) => viewID);
const blendConfig = computed(() => sampledConfig.value!.config!.blendConfig);
useSegmentGroupConfigInitializer(VIEWS_2D[0], groupId.value);
const layerConfigs = computed(() =>
VIEWS_2D.map((viewID) => ({
config: layerColoringStore.getConfig(viewID, groupId.value),
viewID,
}))
);
const blendConfig = computed(
() => layerConfigs.value.find(({ config }) => config)!.config!.blendConfig
);
const setBlendConfig = (key: keyof BlendConfig, value: any) => {
layerConfigs.value.forEach(({ viewID }) =>
layerColoringStore.updateBlendConfig(viewID, groupId.value, {
[key]: value,
})
);
const setOpacity = (opacity: number) => {
updateConfig({
blendConfig: {
...blendConfig.value,
// 1.0 puts us in Opaque render pass which changes stack order.
opacity: Math.min(opacity, 0.9999),
},
});
};
</script>

<template>
<v-slider
class="py-4"
class="pa-4"
label="Segment Group Opacity"
min="0"
max="1"
Expand All @@ -50,6 +34,6 @@ const setBlendConfig = (key: keyof BlendConfig, value: any) => {
hide-details
thumb-label
:model-value="blendConfig.opacity"
@update:model-value="setBlendConfig('opacity', $event)"
@update:model-value="setOpacity($event)"
/>
</template>
16 changes: 13 additions & 3 deletions src/components/vtk/VtkSegmentationSliceRepresentation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,26 @@ sliceRep.mapper.setResolveCoincidentTopologyToPolygonOffset();
sliceRep.mapper.setResolveCoincidentTopologyPolygonOffsetParameters(-2, -2);
useSegmentGroupConfigInitializer(viewId.value, segmentationId.value);
const coloringStore = useLayerColoringStore();
// visibility
const visibility = computed(
() =>
coloringStore.getConfig(viewId.value, segmentationId.value)!.blendConfig
.visibility
);
watchEffect(() => {
sliceRep.actor.setVisibility(visibility.value);
});
// opacity
const coloringStore = useLayerColoringStore();
const opacity = computed(
() =>
coloringStore.getConfig(viewId.value, segmentationId.value)?.blendConfig
coloringStore.getConfig(viewId.value, segmentationId.value)!.blendConfig
.opacity
);
watchEffect(() => {
sliceRep.property.setOpacity(opacity.value!);
sliceRep.property.setOpacity(opacity.value);
});
// set slicing mode
Expand Down
35 changes: 35 additions & 0 deletions src/composables/useGlobalLayerColorConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { computed, MaybeRef, unref } from 'vue';
import { InitViewSpecs } from '@/src/config';
import useLayerColoringStore from '@/src/store/view-configs/layers';
import { LayersConfig } from '@/src/store/view-configs/types';
import { useSegmentGroupConfigInitializer } from '@/src/composables/useSegmentGroupConfigInitializer';

// Returns first existing view's config as the "value" and updates all views' configs with updateConfig()
export const useGlobalLayerColorConfig = (layerId: MaybeRef<string>) => {
const layerColoringStore = useLayerColoringStore();

const VIEWS_2D = Object.entries(InitViewSpecs)
.filter(([, { viewType }]) => viewType === '2D')
.map(([viewID]) => viewID);

useSegmentGroupConfigInitializer(VIEWS_2D[0], unref(layerId));

const layerConfigs = computed(() =>
VIEWS_2D.map((viewID) => ({
config: layerColoringStore.getConfig(viewID, unref(layerId)),
viewID,
}))
);

const sampledConfig = computed(() =>
layerConfigs.value.find(({ config }) => config)
);

const updateConfig = (patch: Partial<LayersConfig>) => {
layerConfigs.value.forEach(({ viewID }) =>
layerColoringStore.updateConfig(viewID, unref(layerId), patch)
);
};

return { sampledConfig, updateConfig };
};
1 change: 1 addition & 0 deletions src/io/state-file/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ const VolumeColorConfig = z.object({

const BlendConfig = z.object({
opacity: z.number(),
visibility: z.boolean(),
}) satisfies z.ZodType<BlendConfig>;

const LayersConfig = z.object({
Expand Down
2 changes: 1 addition & 1 deletion src/store/view-configs/layers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export const defaultLayersConfig = (): LayersConfig => ({
gaussians: [],
mappingRange: [0, 1],
},
blendConfig: { opacity: 0.6 },
blendConfig: { opacity: 0.6, visibility: true },
});

export const useLayerColoringStore = defineStore('layerColoring', () => {
Expand Down
1 change: 1 addition & 0 deletions src/types/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,5 @@ export interface CVRConfig {

export interface BlendConfig {
opacity: number;
visibility: boolean;
}

0 comments on commit 1ea9aa1

Please sign in to comment.