Skip to content
Draft
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
8 changes: 4 additions & 4 deletions packages/app/src/__tests__/CorePack.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { Vis } from '../vis-packs/core/visualizations';
test('visualize raw dataset', async () => {
const { selectExplorerNode } = await renderApp('/entities/raw');

expect(getVisTabs()).toEqual([Vis.Raw]);
expect(getSelectedVisTab()).toBe(Vis.Raw);
expect(getVisTabs()).toEqual([Vis.Scalar]);
expect(getSelectedVisTab()).toBe(Vis.Scalar);
expect(screen.getByText(/"int": 42/)).toBeVisible();

await selectExplorerNode('raw_large');
Expand All @@ -18,8 +18,8 @@ test('visualize raw dataset', async () => {
test('visualize raw image dataset', async () => {
await renderApp('/entities/raw_png');

expect(getVisTabs()).toEqual([Vis.Raw]);
expect(getSelectedVisTab()).toBe(Vis.Raw);
expect(getVisTabs()).toEqual([Vis.Scalar]);
expect(getSelectedVisTab()).toBe(Vis.Scalar);
expect(screen.getByAltText('raw_png')).toBeVisible();
});

Expand Down
8 changes: 4 additions & 4 deletions packages/app/src/vis-packs/core/array/ArrayVisContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { useValuesInCache } from '../../../dimension-mapper/hooks';
import { useDimMappingState } from '../../../dimension-mapper/store';
import { type VisContainerProps } from '../../models';
import VisBoundary from '../../VisBoundary';
import { useRawConfig } from '../raw/config';
import MappedRawVis from '../raw/MappedRawVis';
import { useScalarConfig } from '../scalar/config';
import MappedScalarVis from '../scalar/MappedScalarVis';
import { getSliceSelection } from '../utils';
import ValueFetcher from '../ValueFetcher';

Expand All @@ -18,7 +18,7 @@ function ArrayVisContainer(props: VisContainerProps) {
const { shape: dims } = entity;
const [dimMapping, setDimMapping] = useDimMappingState(dims, 0); // no axes, slicing only

const config = useRawConfig();
const config = useScalarConfig();
const selection = getSliceSelection(dimMapping);

return (
Expand All @@ -34,7 +34,7 @@ function ArrayVisContainer(props: VisContainerProps) {
dataset={entity}
selection={selection}
render={(value) => (
<MappedRawVis
<MappedScalarVis
dataset={entity}
value={value}
toolbarContainer={toolbarContainer}
Expand Down
2 changes: 1 addition & 1 deletion packages/app/src/vis-packs/core/configs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { RawConfigProvider } from './raw/config';
export { ScalarConfigProvider } from './scalar/config';
export { MatrixConfigProvider } from './matrix/config';
export { LineConfigProvider } from './line/config';
export { HeatmapConfigProvider } from './heatmap/config';
Expand Down
1 change: 0 additions & 1 deletion packages/app/src/vis-packs/core/containers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export { default as RawVisContainer } from './raw/RawVisContainer';
export { default as ScalarVisContainer } from './scalar/ScalarVisContainer';
export { default as ArrayVisContainer } from './array/ArrayVisContainer';
export { default as MatrixVisContainer } from './matrix/MatrixVisContainer';
Expand Down
33 changes: 0 additions & 33 deletions packages/app/src/vis-packs/core/raw/RawVisContainer.tsx

This file was deleted.

10 changes: 0 additions & 10 deletions packages/app/src/vis-packs/core/raw/utils.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { RawImageVis, RawVis } from '@h5web/lib';
import { BinaryImageVis, ScalarVis } from '@h5web/lib';
import {
type ArrayShape,
type Dataset,
Expand All @@ -8,31 +8,32 @@ import { createPortal } from 'react-dom';

import visualizerStyles from '../../../visualizer/Visualizer.module.css';
import { useExportEntries } from '../hooks';
import { type RawConfig } from './config';
import RawToolbar from './RawToolbar';
import { isBinaryImage } from './utils';
import { type ScalarConfig } from './config';
import ScalarToolbar from './ScalarToolbar';
import { getFormatter, isBinaryImage } from './utils';

interface Props {
dataset: Dataset<ScalarShape | ArrayShape>;
value: unknown;
toolbarContainer?: HTMLDivElement | undefined;
config: RawConfig;
config: ScalarConfig;
}

function MappedRawVis(props: Props) {
function MappedScalarVis(props: Props) {
const { dataset, value, toolbarContainer, config } = props;

const isImage = value instanceof Uint8Array && isBinaryImage(value);

const exportEntries = useExportEntries(['json'], dataset, undefined, {
json: () => JSON.stringify(value, null, 2),
});

const isImage = value instanceof Uint8Array && isBinaryImage(value);
const formatter = getFormatter(dataset.type);

return (
<>
{toolbarContainer &&
createPortal(
<RawToolbar
<ScalarToolbar
isImage={isImage}
config={config}
exportEntries={exportEntries}
Expand All @@ -41,17 +42,17 @@ function MappedRawVis(props: Props) {
)}

{isImage ? (
<RawImageVis
<BinaryImageVis
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I renamed this visualization component, since the term "Raw" no longer makes much sense.

className={visualizerStyles.vis}
value={value}
title={dataset.name}
fit={config.fitImage}
/>
) : (
<RawVis className={visualizerStyles.vis} value={value} />
<ScalarVis className={visualizerStyles.vis} value={formatter(value)} />
)}
</>
);
}

export default MappedRawVis;
export default MappedScalarVis;
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { ExportMenu, Separator, ToggleBtn, Toolbar } from '@h5web/lib';
import { type ExportEntry } from '@h5web/shared/vis-models';
import { MdOutlineFitScreen } from 'react-icons/md';

import { type RawConfig } from './config';
import { type ScalarConfig } from './config';

interface Props {
isImage: boolean;
config: RawConfig;
config: ScalarConfig;
exportEntries: ExportEntry[];
}

function RawToolbar(props: Props) {
function ScalarToolbar(props: Props) {
const { isImage, config, exportEntries } = props;
const { fitImage, toggleFitImage } = config;

Expand All @@ -34,4 +34,4 @@ function RawToolbar(props: Props) {
);
}

export default RawToolbar;
export default ScalarToolbar;
24 changes: 10 additions & 14 deletions packages/app/src/vis-packs/core/scalar/ScalarVisContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
import { ScalarVis } from '@h5web/lib';
import {
assertDataset,
assertPrintableType,
assertScalarShape,
} from '@h5web/shared/guards';
import { assertDataset, assertScalarShape } from '@h5web/shared/guards';

import visualizerStyles from '../../../visualizer/Visualizer.module.css';
import { type VisContainerProps } from '../../models';
import VisBoundary from '../../VisBoundary';
import ValueFetcher from '../ValueFetcher';
import { getFormatter } from './utils';
import { useScalarConfig } from './config';
import MappedScalarVis from './MappedScalarVis';

function ScalarVisContainer(props: VisContainerProps) {
const { entity } = props;
const { entity, toolbarContainer } = props;
assertDataset(entity);
assertScalarShape(entity);
assertPrintableType(entity);

const formatter = getFormatter(entity);
const config = useScalarConfig();

return (
<VisBoundary>
<ValueFetcher
dataset={entity}
render={(value) => (
<ScalarVis
className={visualizerStyles.vis}
value={formatter(value)}
<MappedScalarVis
dataset={entity}
value={value}
toolbarContainer={toolbarContainer}
config={config}
/>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,38 @@ import { persist } from 'zustand/middleware';

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

export interface RawConfig {
export interface ScalarConfig {
fitImage: boolean;
toggleFitImage: () => void;
}

function createRawConfigStore() {
return createStore<RawConfig>()(
function createScalarConfigStore() {
return createStore<ScalarConfig>()(
persist(
(set): RawConfig => ({
(set): ScalarConfig => ({
fitImage: true,
toggleFitImage: () => set((state) => ({ fitImage: !state.fitImage })),
}),
{
name: 'h5web:raw',
name: 'h5web:scalar',
version: 1,
},
),
);
}

const StoreContext = createContext({} as StoreApi<RawConfig>);
const StoreContext = createContext({} as StoreApi<ScalarConfig>);

export function RawConfigProvider(props: ConfigProviderProps) {
export function ScalarConfigProvider(props: ConfigProviderProps) {
const { children } = props;

const [store] = useState(createRawConfigStore);
const [store] = useState(createScalarConfigStore);

return (
<StoreContext.Provider value={store}>{children}</StoreContext.Provider>
);
}

export function useRawConfig(): RawConfig {
export function useScalarConfig(): ScalarConfig {
return useStore(useContext(StoreContext));
}
55 changes: 38 additions & 17 deletions packages/app/src/vis-packs/core/scalar/utils.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,56 @@
import { hasBoolType, hasComplexType, hasEnumType } from '@h5web/shared/guards';
import {
type ArrayShape,
type Dataset,
type PrintableType,
type ScalarValue,
} from '@h5web/shared/hdf5-models';
isBoolType,
isComplexType,
isEnumType,
isNumericType,
isStringType,
isTypedArray,
} from '@h5web/shared/guards';
import { type DType, type ScalarValue } from '@h5web/shared/hdf5-models';
import { type ValueFormatter } from '@h5web/shared/vis-models';
import {
createComplexFormatter,
createEnumFormatter,
formatBool,
} from '@h5web/shared/vis-utils';

export function getFormatter(
dataset: Dataset<ArrayShape, PrintableType>,
): (val: ScalarValue<PrintableType>) => string; // override distributivity of `ValueFormatter`
const MAGIC_NUMBERS = [
[0xff, 0xd8, 0xff], // JPEG
[0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], // PNG
];

export function getFormatter<T extends PrintableType>(
dataset: Dataset<ArrayShape, T>,
): ValueFormatter<PrintableType> {
if (hasBoolType(dataset)) {
export function isBinaryImage(binArray: Uint8Array): boolean {
return MAGIC_NUMBERS.some(
(nums) => binArray.slice(0, nums.length).toString() === nums.toString(),
);
}

export function getFormatter<T extends DType>(
type: T,
): (val: ScalarValue<T>) => string;

export function getFormatter(type: DType): ValueFormatter {
if (isStringType(type) || isNumericType(type)) {
return (val: number | bigint | string) => val.toString();
}

if (isBoolType(type)) {
return formatBool;
}

if (hasEnumType(dataset)) {
return createEnumFormatter(dataset.type.mapping);
if (isEnumType(type)) {
return createEnumFormatter(type.mapping);
}

if (hasComplexType(dataset)) {
if (isComplexType(type)) {
return createComplexFormatter((val) => val.toString());
}

return (val: number | bigint | string) => val.toString();
return (val: unknown) => {
if (isTypedArray(val)) {
return `${val.constructor.name} [ ${val.toString()} ]`;
}

return JSON.stringify(val, null, 2);
};
Comment on lines +49 to +55
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This takes care of formatting non-printable scalar values. This code was previously in RawVis.

}
Loading