From 3eba27e938569304a271860feaa32ba92a9b6312 Mon Sep 17 00:00:00 2001 From: Axel Bocciarelli Date: Tue, 21 Jan 2025 14:14:48 +0100 Subject: [PATCH] Fix support for enum/bool auxiliary signals in NX Line visualization --- packages/app/src/providers/mock/mock-file.ts | 12 ++++- packages/app/src/vis-packs/core/hooks.ts | 23 ++++++---- .../src/vis-packs/core/line/MappedLineVis.tsx | 15 ++++--- .../src/vis-packs/nexus/NxSignalPicker.tsx | 11 +++-- .../src/vis-packs/nexus/NxValuesFetcher.tsx | 11 +++-- packages/app/src/vis-packs/nexus/guards.ts | 45 ++++++++++++++++--- packages/app/src/vis-packs/nexus/hooks.ts | 4 +- packages/app/src/vis-packs/nexus/models.ts | 2 +- packages/shared/src/mock-values.ts | 18 ++++++++ 9 files changed, 111 insertions(+), 30 deletions(-) diff --git a/packages/app/src/providers/mock/mock-file.ts b/packages/app/src/providers/mock/mock-file.ts index fc1716027..4954a0ebb 100644 --- a/packages/app/src/providers/mock/mock-file.ts +++ b/packages/app/src/providers/mock/mock-file.ts @@ -284,11 +284,21 @@ export function makeMockFile(): GroupWithChildren { }), nxGroup('numeric-like', 'NXprocess', { children: [ - nxData('bool', { signal: array('twoD_bool') }), + nxData('bool', { + signal: array('twoD_bool'), + auxiliary: { secondary_bool: array('secondary_bool') }, + auxAttr: ['secondary_bool'], + }), nxData('enum', { signal: array('twoD_enum', { type: enumType(intType(false, 8), ENUM_MAPPING), }), + auxiliary: { + secondary_enum: array('secondary_enum', { + type: enumType(intType(false, 8), ENUM_MAPPING), + }), + }, + auxAttr: ['secondary_enum'], }), ], }), diff --git a/packages/app/src/vis-packs/core/hooks.ts b/packages/app/src/vis-packs/core/hooks.ts index 1f6b36b2c..b439d8cd5 100644 --- a/packages/app/src/vis-packs/core/hooks.ts +++ b/packages/app/src/vis-packs/core/hooks.ts @@ -4,6 +4,7 @@ import { type ArrayShape, type ArrayValue, type Dataset, + type NumericLikeType, type ScalarShape, type Value, } from '@h5web/shared/hdf5-models'; @@ -25,6 +26,12 @@ import { export const useToNumArray = createMemo(toNumArray); +export function useToNumArrays( + arrays: ArrayValue[], +): NumArray[] { + return useMemo(() => arrays.map(toNumArray), arrays); // eslint-disable-line react-hooks/exhaustive-deps +} + export function useValuesInCache( ...datasets: (Dataset | undefined)[] ): (dimMapping: DimensionMapping) => boolean { @@ -129,23 +136,23 @@ export function useMappedArray( return useMemo(() => applyMapping(baseArray, mapping), [baseArray, mapping]); } -export function useMappedArrays( - values: NumArray[], +export function useMappedArrays( + values: T[], dims: number[], mapping: DimensionMapping, -): NdArray[]; +): NdArray[]; -export function useMappedArrays( - values: (NumArray | undefined)[], +export function useMappedArrays( + values: (T | undefined)[], dims: number[], mapping: DimensionMapping, -): (NdArray | undefined)[]; +): (NdArray | undefined)[]; export function useMappedArrays( - values: (NumArray | undefined)[], + values: (ArrayValue | undefined)[], dims: number[], mapping: DimensionMapping, -): (NdArray | undefined)[] { +): (NdArray | undefined)[] { const baseArrays = useMemo( () => values.map((arr) => getBaseArray(arr, dims)), [dims, ...values], // eslint-disable-line react-hooks/exhaustive-deps diff --git a/packages/app/src/vis-packs/core/line/MappedLineVis.tsx b/packages/app/src/vis-packs/core/line/MappedLineVis.tsx index 71714ec11..e99c9c24f 100644 --- a/packages/app/src/vis-packs/core/line/MappedLineVis.tsx +++ b/packages/app/src/vis-packs/core/line/MappedLineVis.tsx @@ -8,9 +8,9 @@ import { } from '@h5web/lib'; import { type ArrayShape, - type ArrayValue, type Dataset, type NumericLikeType, + type Value, } from '@h5web/shared/hdf5-models'; import { type AxisMapping } from '@h5web/shared/nexus-models'; import { type NumArray } from '@h5web/shared/vis-models'; @@ -24,18 +24,19 @@ import { useMappedArrays, useSlicedDimsAndMapping, useToNumArray, + useToNumArrays, } from '../hooks'; import { DEFAULT_DOMAIN, formatNumLikeType, getSliceSelection } from '../utils'; import { type LineConfig } from './config'; import LineToolbar from './LineToolbar'; interface Props { - dataset?: Dataset; - value: ArrayValue; + dataset: Dataset; + value: Value; valueLabel?: string; errors?: NumArray; auxLabels?: string[]; - auxValues?: NumArray[]; + auxValues?: Value[]; auxErrors?: (NumArray | undefined)[]; dims: number[]; dimMapping: DimensionMapping; @@ -76,12 +77,13 @@ function MappedLineVis(props: Props) { } = config; const numArray = useToNumArray(value); + const numAuxArrays = useToNumArrays(auxValues); const [slicedDims, slicedMapping] = useSlicedDimsAndMapping(dims, dimMapping); const hookArgs = [slicedDims, slicedMapping] as const; const dataArray = useMappedArray(numArray, ...hookArgs); const errorArray = useMappedArray(errors, ...hookArgs); - const auxArrays = useMappedArrays(auxValues, ...hookArgs); + const auxArrays = useMappedArrays(numAuxArrays, ...hookArgs); const auxErrorsArrays = useMappedArrays(auxErrors, ...hookArgs); const dataDomain = useDomain( @@ -114,7 +116,6 @@ function MappedLineVis(props: Props) { config={config} getExportURL={ getExportURL && - dataset && ((format) => getExportURL(format, dataset, selection, value)) } />, @@ -135,7 +136,7 @@ function MappedLineVis(props: Props) { }} ordinateLabel={valueLabel} title={title} - dtype={dataset && formatNumLikeType(dataset.type)} + dtype={formatNumLikeType(dataset.type)} errorsArray={errorArray} showErrors={showErrors} auxiliaries={auxArrays.map((array, i) => ({ diff --git a/packages/app/src/vis-packs/nexus/NxSignalPicker.tsx b/packages/app/src/vis-packs/nexus/NxSignalPicker.tsx index 1a0d58304..81a55c345 100644 --- a/packages/app/src/vis-packs/nexus/NxSignalPicker.tsx +++ b/packages/app/src/vis-packs/nexus/NxSignalPicker.tsx @@ -1,14 +1,19 @@ -import { type ComplexType, type NumericType } from '@h5web/shared/hdf5-models'; +import { + type ComplexType, + type NumericLikeType, +} from '@h5web/shared/hdf5-models'; import { type DatasetDef } from './models'; import styles from './NxSignalPicker.module.css'; -interface Props { +interface Props { definitions: DatasetDef[]; onChange: (def: DatasetDef) => void; } -function NxSignalPicker(props: Props) { +function NxSignalPicker( + props: Props, +) { const { definitions, onChange } = props; return ( diff --git a/packages/app/src/vis-packs/nexus/NxValuesFetcher.tsx b/packages/app/src/vis-packs/nexus/NxValuesFetcher.tsx index 25fec89ab..0b020e453 100644 --- a/packages/app/src/vis-packs/nexus/NxValuesFetcher.tsx +++ b/packages/app/src/vis-packs/nexus/NxValuesFetcher.tsx @@ -1,4 +1,7 @@ -import { type ComplexType, type NumericType } from '@h5web/shared/hdf5-models'; +import { + type ComplexType, + type NumericLikeType, +} from '@h5web/shared/hdf5-models'; import { type ReactNode } from 'react'; import { @@ -8,13 +11,15 @@ import { } from '../core/hooks'; import { type NxData, type NxValues } from './models'; -interface Props { +interface Props { nxData: NxData; selection?: string; // for slice-by-slice fetching render: (val: NxValues) => ReactNode; } -function NxValuesFetcher(props: Props) { +function NxValuesFetcher( + props: Props, +) { const { nxData, selection, render } = props; const { signalDef, axisDefs, auxDefs, titleDataset } = nxData; diff --git a/packages/app/src/vis-packs/nexus/guards.ts b/packages/app/src/vis-packs/nexus/guards.ts index 5e183b385..d2d4d37d9 100644 --- a/packages/app/src/vis-packs/nexus/guards.ts +++ b/packages/app/src/vis-packs/nexus/guards.ts @@ -2,37 +2,72 @@ import { assertComplexType, assertNumericLikeType, assertNumericType, + isDefined, } from '@h5web/shared/guards'; -import { type ComplexType, type NumericType } from '@h5web/shared/hdf5-models'; +import { + type ComplexType, + type NumericLikeType, + type NumericType, +} from '@h5web/shared/hdf5-models'; import { type NxData } from './models'; export function assertNumericLikeNxData( nxData: NxData, -): asserts nxData is NxData { - const { signalDef, auxDefs } = nxData; +): asserts nxData is NxData { + const { signalDef, auxDefs, axisDefs } = nxData; + assertNumericLikeType(signalDef.dataset); + auxDefs.forEach((def) => { assertNumericLikeType(def.dataset); }); + + if (signalDef.errorDataset) { + assertNumericType(signalDef.errorDataset); + } + + axisDefs.filter(isDefined).forEach((def) => { + assertNumericType(def.dataset); + }); } export function assertNumericNxData( nxData: NxData, ): asserts nxData is NxData { - const { signalDef, auxDefs } = nxData; + const { signalDef, auxDefs, axisDefs } = nxData; + assertNumericType(signalDef.dataset); + auxDefs.forEach((def) => { assertNumericType(def.dataset); }); + + if (signalDef.errorDataset) { + assertNumericType(signalDef.errorDataset); + } + + axisDefs.filter(isDefined).forEach((def) => { + assertNumericType(def.dataset); + }); } export function assertComplexNxData( nxData: NxData, ): asserts nxData is NxData { - const { signalDef, auxDefs } = nxData; + const { signalDef, auxDefs, axisDefs } = nxData; + assertComplexType(signalDef.dataset); + auxDefs.forEach((def) => { assertComplexType(def.dataset); }); + + if (signalDef.errorDataset) { + assertNumericType(signalDef.errorDataset); + } + + axisDefs.filter(isDefined).forEach((def) => { + assertNumericType(def.dataset); + }); } diff --git a/packages/app/src/vis-packs/nexus/hooks.ts b/packages/app/src/vis-packs/nexus/hooks.ts index 4cfc618d2..8c50d088b 100644 --- a/packages/app/src/vis-packs/nexus/hooks.ts +++ b/packages/app/src/vis-packs/nexus/hooks.ts @@ -1,7 +1,7 @@ import { type ComplexType, type GroupWithChildren, - type NumericType, + type NumericLikeType, } from '@h5web/shared/hdf5-models'; import { type DimensionMapping } from '../../dimension-mapper/models'; @@ -48,7 +48,7 @@ export function useNxData(group: GroupWithChildren): NxData { }; } -export function useNxImageDataToFetch( +export function useNxImageDataToFetch( nxData: NxData, selectedDef: DatasetDef, ): NxData { diff --git a/packages/app/src/vis-packs/nexus/models.ts b/packages/app/src/vis-packs/nexus/models.ts index 530ebce3e..c6444c447 100644 --- a/packages/app/src/vis-packs/nexus/models.ts +++ b/packages/app/src/vis-packs/nexus/models.ts @@ -56,7 +56,7 @@ export interface NxData< silxStyle: SilxStyle; } -export interface NxValues { +export interface NxValues { title: string; signal: ArrayValue; errors?: NumArray; diff --git a/packages/shared/src/mock-values.ts b/packages/shared/src/mock-values.ts index ce1da3f7d..fe07c00d0 100644 --- a/packages/shared/src/mock-values.ts +++ b/packages/shared/src/mock-values.ts @@ -266,6 +266,24 @@ export const mockValues = { ].flat(1), [2, 2], ), + secondary_bool: () => { + const { data: dataOneDBool } = oneD_bool(); + return ndarray( + dataOneDBool.flatMap((rowBool) => + dataOneDBool.map((colBool) => (rowBool ? !colBool : colBool)), + ), + [10, 10], + ); + }, + secondary_enum: () => { + const { data: dataOneDEnum } = oneD_enum(); + return ndarray( + dataOneDEnum.flatMap((rowEnum) => + dataOneDEnum.map((colEnum, j) => (j % 2 === 0 ? colEnum : rowEnum)), + ), + [10, 10], + ); + }, tertiary: () => ndarray( twoD().data.map((v) => v / 2),