Skip to content

Commit

Permalink
Merge pull request #1676 from silx-kit/complex-auxiliaries
Browse files Browse the repository at this point in the history
Support complex auxiliaries
  • Loading branch information
axelboc authored Jun 24, 2024
2 parents e91db90 + 03453f9 commit 5ede8e3
Show file tree
Hide file tree
Showing 18 changed files with 152 additions and 46 deletions.
1 change: 1 addition & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = createConfig(__dirname, getDependencies(), [
'testing-library/await-async-utils': 'off', // Cypress has its own way of dealing with asynchronicity
'testing-library/prefer-screen-queries': 'off', // Cypress provides `cy` object instead of `screen`
'sonarjs/no-duplicate-string': 'off', // incompatible with Cypress testing syntax
'sonarjs/cognitive-complexity': 'off', // allow long `describe` and `context` functions
'unicorn/numeric-separators-style': 'off', // not supported
},
},
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ jobs:
uses: actions/cache@v4
with:
path: ~/.cache/pypoetry/virtualenvs
key: poetry-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
key: poetry-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}-0 # increment to reset cache

- name: Create Poetry environments 📜
if: steps.poetry-venvs.outputs.cache-hit != 'true'
Expand Down
34 changes: 30 additions & 4 deletions cypress/e2e/app.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ describe('/mock', () => {
cy.findByRole('figure', { name: 'NeXus 2D' }).should('be.visible');
});

it('visualize dataset with "spectrum" interpretation as NxSpectrum', () => {
it('visualize dataset with "spectrum" interpretation as NxLine', () => {
cy.selectExplorerNode('nexus_entry');
cy.selectExplorerNode('spectrum');

Expand All @@ -312,7 +312,7 @@ describe('/mock', () => {
cy.get('svg[data-type="abscissa"] svg').should('have.text', 'X (nm)');

if (Cypress.env('TAKE_SNAPSHOTS')) {
cy.matchImageSnapshot('nxspectrum');
cy.matchImageSnapshot('nxline');
}
});

Expand Down Expand Up @@ -348,7 +348,7 @@ describe('/mock', () => {
);
});

it('visualize dataset with log scales on both axes on NxSpectrum with SILX_style', () => {
it('visualize dataset with log scales on both axes on NxLine with SILX_style', () => {
cy.selectExplorerNode('nexus_entry');
cy.selectExplorerNode('log_spectrum');

Expand All @@ -365,7 +365,7 @@ describe('/mock', () => {
}
});

it('visualize signal and auxiliary signals datasets as NxSpectrum', () => {
it('visualize signal and auxiliary signals datasets as NxLine', () => {
cy.selectExplorerNode('nexus_entry');
cy.selectExplorerNode('spectrum_with_aux');

Expand Down Expand Up @@ -399,6 +399,32 @@ describe('/mock', () => {
}
});

it('visualize 2D complex signal as NxImage', () => {
cy.selectExplorerNode('nexus_entry');
cy.selectExplorerNode('complex');

cy.findByRole('heading', {
name: 'nexus_entry / complex',
}).should('be.visible');

if (Cypress.env('TAKE_SNAPSHOTS')) {
cy.matchImageSnapshot('nximage_complex_2d');
}
});

it('visualize 2D complex signal with "spectrum" interpretation and auxiliaries as NxLine', () => {
cy.selectExplorerNode('nexus_entry');
cy.selectExplorerNode('complex_spectrum');

cy.findByRole('heading', {
name: 'nexus_entry / complex_spectrum',
}).should('be.visible');

if (Cypress.env('TAKE_SNAPSHOTS')) {
cy.matchImageSnapshot('nxline_complex_2d_aux');
}
});

it('visualize dataset with "rgb-image" interpretation as NxRGB', () => {
cy.selectExplorerNode('nexus_entry');
cy.selectExplorerNode('rgb-image');
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions packages/app/src/providers/mock/mock-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ export function makeMockFile(): GroupWithChildren {
signal: withNxAttr(array('twoD_cplx'), {
interpretation: 'spectrum',
}),
auxiliary: { secondary_cplx: array('secondary_cplx') },
auxAttr: ['secondary_cplx'],
}),
nxData('rgb-image', {
signal: withImageAttr(
Expand Down
52 changes: 32 additions & 20 deletions packages/app/src/vis-packs/core/complex/MappedComplexLineVis.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
import { LineVis, useDomain, useSafeDomain, useVisDomain } from '@h5web/lib';
import type { H5WebComplex } from '@h5web/shared/hdf5-models';
import {
LineVis,
useCombinedDomain,
useDomain,
useDomains,
useSafeDomain,
useVisDomain,
} from '@h5web/lib';
import type { ArrayValue, ComplexType } from '@h5web/shared/hdf5-models';
import type { AxisMapping } from '@h5web/shared/nexus-models';
import type { NumArray } from '@h5web/shared/vis-models';
import { ComplexVisType } from '@h5web/shared/vis-models';
import { useMemo } from 'react';
import { createPortal } from 'react-dom';

import type { DimensionMapping } from '../../../dimension-mapper/models';
import visualizerStyles from '../../../visualizer/Visualizer.module.css';
import { useMappedArray, useSlicedDimsAndMapping } from '../hooks';
import type { LineConfig } from '../line/config';
import { DEFAULT_DOMAIN } from '../utils';
import ComplexLineToolbar from './ComplexLineToolbar';
import { useMappedComplexArrays } from './hooks';
import type { ComplexLineConfig } from './lineConfig';
import { COMPLEX_VIS_TYPE_LABELS, getPhaseAmplitudeValues } from './utils';
import { COMPLEX_VIS_TYPE_LABELS } from './utils';

interface Props {
value: H5WebComplex[];
value: ArrayValue<ComplexType>;
valueLabel?: string;
auxLabels?: string[];
auxValues?: ArrayValue<ComplexType>[];
dims: number[];
dimMapping: DimensionMapping;
axisLabels?: AxisMapping<string>;
Expand All @@ -32,6 +39,8 @@ function MappedComplexLineVis(props: Props) {
const {
value,
valueLabel,
auxLabels = [],
auxValues = [],
dims,
dimMapping,
axisLabels,
Expand All @@ -46,21 +55,20 @@ function MappedComplexLineVis(props: Props) {
const { customDomain, yScaleType, xScaleType, curveType, showGrid } =
lineConfig;

const [slicedDims, slicedMapping] = useSlicedDimsAndMapping(dims, dimMapping);
const { phaseValues, amplitudeValues } = useMemo(
() => getPhaseAmplitudeValues(value),
[value],
const [dataArray, ...auxArrays] = useMappedComplexArrays(
[value, ...auxValues],
dims,
dimMapping,
visType,
);

const dataArray = useMappedArray(
visType === ComplexVisType.Amplitude ? amplitudeValues : phaseValues,
slicedDims,
slicedMapping,
);
const dataDomain = useDomain(dataArray, yScaleType);
const auxDomains = useDomains(auxArrays, yScaleType);
const combinedDomain =
useCombinedDomain([dataDomain, ...auxDomains]) || DEFAULT_DOMAIN;

const dataDomain = useDomain(dataArray, yScaleType) || DEFAULT_DOMAIN;
const visDomain = useVisDomain(customDomain, dataDomain);
const [safeDomain] = useSafeDomain(visDomain, dataDomain, yScaleType);
const visDomain = useVisDomain(customDomain, combinedDomain);
const [safeDomain] = useSafeDomain(visDomain, combinedDomain, yScaleType);

const xDimIndex = dimMapping.indexOf('x');
const ordinateLabel = valueLabel
Expand All @@ -72,7 +80,7 @@ function MappedComplexLineVis(props: Props) {
{toolbarContainer &&
createPortal(
<ComplexLineToolbar
dataDomain={dataDomain}
dataDomain={combinedDomain}
config={config}
lineConfig={lineConfig}
/>,
Expand All @@ -93,6 +101,10 @@ function MappedComplexLineVis(props: Props) {
}}
ordinateLabel={ordinateLabel}
title={title}
auxiliaries={auxArrays.map((array, i) => ({
label: auxLabels[i],
array,
}))}
testid={dimMapping.toString()}
/>
</>
Expand Down
40 changes: 40 additions & 0 deletions packages/app/src/vis-packs/core/complex/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { ComplexVisType } from '@h5web/lib';
import type { H5WebComplex } from '@h5web/shared/hdf5-models';
import type { ComplexLineVisType } from '@h5web/shared/vis-models';
import type { NdArray } from 'ndarray';
import { useMemo } from 'react';

import type { DimensionMapping } from '../../../dimension-mapper/models';
import { useSlicedDimsAndMapping } from '../hooks';
import { applyMapping, getBaseArray } from '../utils';
import { getPhaseAmplitudeValues } from './utils';

export function useMappedComplexArrays(
values: H5WebComplex[][],
dims: number[],
mapping: DimensionMapping,
complexVisType: ComplexLineVisType,
): NdArray<number[]>[] {
const [slicedDims, slicedMapping] = useSlicedDimsAndMapping(dims, mapping);

const phaseAmplitudeValues = useMemo(
() => values.map(getPhaseAmplitudeValues),
[...values], // eslint-disable-line react-hooks/exhaustive-deps
);

const baseArrays = useMemo(() => {
return phaseAmplitudeValues.map((paValues) =>
getBaseArray(
complexVisType === ComplexVisType.Phase
? paValues.phaseValues
: paValues.amplitudeValues,
slicedDims,
),
);
}, [complexVisType, slicedDims, phaseAmplitudeValues]);

return useMemo(
() => baseArrays.map((ndArr) => applyMapping(ndArr, slicedMapping)),
[baseArrays, slicedMapping],
);
}
3 changes: 1 addition & 2 deletions packages/app/src/vis-packs/core/complex/lineConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ComplexLineVisType } from '@h5web/shared/vis-models';
import { ComplexVisType } from '@h5web/shared/vis-models';
import { createContext, useContext, useState } from 'react';
import type { StoreApi } from 'zustand';
Expand All @@ -6,8 +7,6 @@ import { persist } from 'zustand/middleware';

import type { ConfigProviderProps } from '../../models';

type ComplexLineVisType = ComplexVisType.Phase | ComplexVisType.Amplitude;

export interface ComplexLineConfig {
visType: ComplexLineVisType;
setVisType: (visType: ComplexLineVisType) => void;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function NxComplexSpectrumContainer(props: VisContainerProps) {
const nxData = useNxData(entity);
assertComplexNxData(nxData);

const { signalDef, axisDefs, silxStyle } = nxData;
const { signalDef, axisDefs, auxDefs, silxStyle } = nxData;
const signalDims = signalDef.dataset.shape;

const [dimMapping, setDimMapping] = useDimMappingState(signalDims, 1);
Expand Down Expand Up @@ -50,12 +50,14 @@ function NxComplexSpectrumContainer(props: VisContainerProps) {
nxData={nxData}
selection={getSliceSelection(dimMapping)}
render={(nxValues) => {
const { signal, axisValues, title } = nxValues;
const { signal, axisValues, auxValues, title } = nxValues;

return (
<MappedComplexLineVis
value={signal}
valueLabel={signalDef.label}
auxLabels={auxDefs.map((def) => def?.label)}
auxValues={auxValues}
dims={signalDims}
dimMapping={dimMapping}
axisLabels={axisLabels}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ function NxSpectrumContainer(props: VisContainerProps) {

const nxData = useNxData(entity);
assertNumericNxData(nxData);
const { signalDef, axisDefs, auxDefs, silxStyle } = nxData;

const { signalDef, axisDefs, auxDefs, silxStyle } = nxData;
const signalDims = signalDef.dataset.shape;
const errorDims = signalDef.errorDataset?.shape;

Expand Down
6 changes: 4 additions & 2 deletions packages/app/src/vis-packs/nexus/guards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import type { NxData } from './models';
export function assertNumericNxData(
nxData: NxData,
): asserts nxData is NxData<NumericType> {
const { signalDef } = nxData;
const { signalDef, auxDefs } = nxData;
assertNumericType(signalDef.dataset);
auxDefs.forEach((def) => assertNumericType(def.dataset));
}

export function assertComplexNxData(
nxData: NxData,
): asserts nxData is NxData<ComplexType> {
const { signalDef } = nxData;
const { signalDef, auxDefs } = nxData;
assertComplexType(signalDef.dataset);
auxDefs.forEach((def) => assertComplexType(def.dataset));
}
9 changes: 2 additions & 7 deletions packages/app/src/vis-packs/nexus/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { isDefined } from '@h5web/shared/guards';
import type { GroupWithChildren } from '@h5web/shared/hdf5-models';

import type { DimensionMapping } from '../../dimension-mapper/models';
Expand All @@ -7,8 +6,8 @@ import { useValuesInCache } from '../core/hooks';
import type { NxData } from './models';
import {
assertNxDataGroup,
findAssociatedDatasets,
findAuxErrorDataset,
findAuxiliaryDatasets,
findAxesDatasets,
findErrorDataset,
findSignalDataset,
Expand All @@ -23,11 +22,7 @@ export function useNxData(group: GroupWithChildren): NxData {
assertNxDataGroup(group, attrValuesStore);
const signalDataset = findSignalDataset(group, attrValuesStore);
const axisDatasets = findAxesDatasets(group, signalDataset, attrValuesStore);
const auxSignals = findAssociatedDatasets(
group,
'auxiliary_signals',
attrValuesStore,
).filter(isDefined);
const auxSignals = findAuxiliaryDatasets(group, attrValuesStore);

return {
titleDataset: findTitleDataset(group),
Expand Down
5 changes: 2 additions & 3 deletions packages/app/src/vis-packs/nexus/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ type WithError<T extends DatasetDef> = T & {
errorDataset?: NumArrayDataset;
};

export type AuxDef = WithError<DatasetDef<NumericType>>;
export type AxisDef = DatasetDef<NumericType>;

export interface SilxStyle {
Expand All @@ -51,7 +50,7 @@ export interface NxData<
> {
titleDataset?: Dataset<ScalarShape, StringType>;
signalDef: WithError<DatasetDef<T>>;
auxDefs: AuxDef[];
auxDefs: WithError<DatasetDef<T>>[];
axisDefs: AxisMapping<AxisDef>;
silxStyle: SilxStyle;
}
Expand All @@ -60,7 +59,7 @@ export interface NxValues<T extends NumericType | ComplexType> {
title: string;
signal: ArrayValue<T>;
errors?: NumArray;
auxValues: NumArray[];
auxValues: ArrayValue<T>[];
auxErrors: (NumArray | undefined)[];
axisValues: AxisMapping<NumArray>;
}
Loading

0 comments on commit 5ede8e3

Please sign in to comment.