diff --git a/src/components/SegmentGroupControls.vue b/src/components/SegmentGroupControls.vue index 913e82e7..8f1f0dd9 100644 --- a/src/components/SegmentGroupControls.vue +++ b/src/components/SegmentGroupControls.vue @@ -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'; @@ -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, + }, + }); + }, }; }); }); @@ -207,6 +219,20 @@ function openSaveDialog(id: string) {
{{ group.name }} + + mdi-eye + mdi-eye-off + {{ + group.visibility ? 'Hide' : 'Show' + }} + 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; @@ -11,37 +8,24 @@ const props = defineProps<{ 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), + }, + }); }; diff --git a/src/components/vtk/VtkSegmentationSliceRepresentation.vue b/src/components/vtk/VtkSegmentationSliceRepresentation.vue index 9d71df31..5a5a1d45 100644 --- a/src/components/vtk/VtkSegmentationSliceRepresentation.vue +++ b/src/components/vtk/VtkSegmentationSliceRepresentation.vue @@ -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 diff --git a/src/composables/useGlobalLayerColorConfig.ts b/src/composables/useGlobalLayerColorConfig.ts new file mode 100644 index 00000000..69dc247f --- /dev/null +++ b/src/composables/useGlobalLayerColorConfig.ts @@ -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) => { + 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) => { + layerConfigs.value.forEach(({ viewID }) => + layerColoringStore.updateConfig(viewID, unref(layerId), patch) + ); + }; + + return { sampledConfig, updateConfig }; +}; diff --git a/src/io/state-file/schema.ts b/src/io/state-file/schema.ts index 87f1a09c..53843aab 100644 --- a/src/io/state-file/schema.ts +++ b/src/io/state-file/schema.ts @@ -203,6 +203,7 @@ const VolumeColorConfig = z.object({ const BlendConfig = z.object({ opacity: z.number(), + visibility: z.boolean(), }) satisfies z.ZodType; const LayersConfig = z.object({ diff --git a/src/store/view-configs/layers.ts b/src/store/view-configs/layers.ts index 74a38f8a..1cfc0baa 100644 --- a/src/store/view-configs/layers.ts +++ b/src/store/view-configs/layers.ts @@ -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', () => { diff --git a/src/types/views.ts b/src/types/views.ts index 953221ea..5e908a29 100644 --- a/src/types/views.ts +++ b/src/types/views.ts @@ -66,4 +66,5 @@ export interface CVRConfig { export interface BlendConfig { opacity: number; + visibility: boolean; }