From 04dba87f5d106f134ce8566d4a9f3e43668c7502 Mon Sep 17 00:00:00 2001 From: Forrest Date: Wed, 4 Oct 2023 14:06:15 -0400 Subject: [PATCH] feat(coloring): support config defaults Sets the default color preset for the fetal sample dataset to US-Fetal. --- src/components/SampleDataBrowser.vue | 7 +++ src/config.ts | 3 + src/store/view-configs/volume-coloring.ts | 72 ++++++++++++++++++----- src/types/index.ts | 9 +++ 4 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/components/SampleDataBrowser.vue b/src/components/SampleDataBrowser.vue index 99fa81e96..47d19cba1 100644 --- a/src/components/SampleDataBrowser.vue +++ b/src/components/SampleDataBrowser.vue @@ -7,6 +7,7 @@ import { importDataSources, } from '@/src/io/import/importDataSources'; import { remoteFileToDataSource } from '@/src/io/import/dataSource'; +import useVolumeColoringStore from '@/src/store/view-configs/volume-coloring'; import { SAMPLE_DATA } from '../config'; import { useMessageStore } from '../store/messages'; import { SampleDataset } from '../types'; @@ -105,6 +106,12 @@ export default defineComponent({ selection.type === 'image' ? selection.dataID : selection.volumeKey; loaded.idToURL[id] = sample.url; loaded.urlToID[sample.url] = id; + + useVolumeColoringStore().setDefaults(id, { + transferFunction: { + preset: sample.defaults?.colorPreset, + }, + }); } datasetStore.setPrimarySelection(selection); } catch (error) { diff --git a/src/config.ts b/src/config.ts index 180b91a1b..acca280b9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -168,6 +168,9 @@ export const SAMPLE_DATA: SampleDataset[] = [ '3D ultrasound of a baby. Downloaded from tomovision.com.(8 MB)', url: 'https://data.kitware.com/api/v1/item/635679c311dab8142820a4f4/download', image: USFetusThumbnail, + defaults: { + colorPreset: 'US-Fetal', + }, }, ]; diff --git a/src/store/view-configs/volume-coloring.ts b/src/store/view-configs/volume-coloring.ts index 221099434..7ac4b2b07 100644 --- a/src/store/view-configs/volume-coloring.ts +++ b/src/store/view-configs/volume-coloring.ts @@ -14,7 +14,7 @@ import { getDoubleKeyRecord, patchDoubleKeyRecord, } from '@/src/utils/doubleKeyRecord'; -import { Maybe } from '@/src/types'; +import { DeepPartial, Maybe } from '@/src/types'; import { identity } from '@/src/utils'; import { createViewConfigSerializer } from './common'; import { DEFAULT_PRESET } from '../../vtk/ColorMaps'; @@ -39,6 +39,26 @@ function getPresetFromImageModality(imageID: string) { return DEFAULT_PRESET; } +/** + * Gets partial color and opacity function configs from a preset. + * @param preset + * @returns + */ +function getColorAndOpacityFuncsFromPreset(preset: string) { + const ctFunc: Partial = { + preset, + }; + + const ctRange = getColorFunctionRangeFromPreset(preset); + if (ctRange) { + ctFunc.mappingRange = ctRange; + } + + const opFunc = getOpacityFunctionFromPreset(preset); + + return { colorFunc: ctFunc, opacityFunc: opFunc }; +} + export const defaultVolumeColorConfig = (): VolumeColorConfig => ({ colorBy: { arrayName: '', @@ -69,7 +89,12 @@ export const defaultVolumeColorConfig = (): VolumeColorConfig => ({ }); export const useVolumeColoringStore = defineStore('volumeColoring', () => { - const configs = reactive>({}); + const configs = reactive>( + Object.create(null) + ); + const defaultConfigs = reactive< + Record> + >(Object.create(null)); const getConfig = (viewID: Maybe, dataID: Maybe) => getDoubleKeyRecord(configs, viewID, dataID); @@ -114,19 +139,16 @@ export const useVolumeColoringStore = defineStore('volumeColoring', () => { const setColorPreset = (viewID: string, imageID: string, preset: string) => { const imageStore = useImageStore(); const image = imageStore.dataIndex[imageID]; - if (!image) return; - const imageDataRange = image.getPointData().getScalars().getRange(); + if (!image) throw new Error('Invalid imageID'); - const ctRange = getColorFunctionRangeFromPreset(preset); - const ctFunc: Partial = { - preset, - mappingRange: ctRange || imageDataRange, - }; - updateColorTransferFunction(viewID, imageID, ctFunc); + const imageDataRange = image.getPointData().getScalars().getRange(); + const { colorFunc, opacityFunc } = + getColorAndOpacityFuncsFromPreset(preset); + colorFunc.mappingRange ||= imageDataRange; + opacityFunc.mappingRange = imageDataRange; - const opFunc = getOpacityFunctionFromPreset(preset); - opFunc.mappingRange = imageDataRange; - updateOpacityFunction(viewID, imageID, opFunc); + updateColorTransferFunction(viewID, imageID, colorFunc); + updateOpacityFunction(viewID, imageID, opacityFunc); }; const resetToDefaultColoring = ( @@ -134,13 +156,30 @@ export const useVolumeColoringStore = defineStore('volumeColoring', () => { dataID: string, image: vtkImageData ) => { + const defaults = defaultConfigs[dataID]; const scalars = image.getPointData().getScalars(); updateColorBy(viewID, dataID, { - arrayName: scalars.getName(), - location: 'pointData', + arrayName: defaults?.colorBy?.arrayName ?? scalars.getName(), + location: defaults?.colorBy?.location ?? 'pointData', }); - setColorPreset(viewID, dataID, getPresetFromImageModality(dataID)); + setColorPreset( + viewID, + dataID, + defaults?.transferFunction?.preset ?? getPresetFromImageModality(dataID) + ); + }; + + /** + * Sets the view config defaults for a dataset. + * @param dataID + * @param defaults + */ + const setDefaults = ( + dataID: string, + defaults: DeepPartial + ) => { + defaultConfigs[dataID] = defaults; }; const removeView = (viewID: string) => { @@ -174,6 +213,7 @@ export const useVolumeColoringStore = defineStore('volumeColoring', () => { updateOpacityFunction, updateCVRParameters, resetToDefaultColoring, + setDefaults, setColorPreset, removeView, removeData, diff --git a/src/types/index.ts b/src/types/index.ts index cc35260dc..500d1b7d6 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -15,6 +15,9 @@ export type SampleDataset = { description: string; url: string; image: string; + defaults?: { + colorPreset?: string; + }; }; export type RequiredWithPartial = Required> & @@ -48,3 +51,9 @@ export type TypedArrayConstructorName = | 'Int32Array' | 'Float32Array' | 'Float64Array'; + +export type DeepPartial = T extends object + ? { + [P in keyof T]?: DeepPartial; + } + : T;