Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support complex auxiliaries #1676

Merged
merged 2 commits into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
loichuder marked this conversation as resolved.
Show resolved Hide resolved

- 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