import type { Domain, NumArray, NumericType } from '@h5web/shared'; import { assertDefined, formatTooltipVal, getDims, ScaleType, } from '@h5web/shared'; import type { NdArray } from 'ndarray'; import type { ReactElement, ReactNode } from 'react'; import type { DefaultInteractionsConfig } from '../../interactions/DefaultInteractions'; import DefaultInteractions from '../../interactions/DefaultInteractions'; import ResetZoomButton from '../../toolbar/floating/ResetZoomButton'; import { useAxisDomain, useValueToIndexScale } from '../hooks'; import type { AxisParams, VisScaleType } from '../models'; import TooltipMesh from '../shared/TooltipMesh'; import VisCanvas from '../shared/VisCanvas'; import { DEFAULT_DOMAIN, formatNumType } from '../utils'; import ColorBar from './ColorBar'; import HeatmapMesh from './HeatmapMesh'; import styles from './HeatmapVis.module.css'; import { usePixelEdgeValues, useTextureSafeNdArray } from './hooks'; import type { ColorMap, Layout, TooltipData } from './models'; interface Props { dataArray: NdArray; domain: Domain | undefined; colorMap?: ColorMap; scaleType?: VisScaleType; layout?: Layout; showGrid?: boolean; title?: string; dtype?: NumericType; invertColorMap?: boolean; abscissaParams?: AxisParams; ordinateParams?: AxisParams; alpha?: { array: NdArray; domain: Domain }; flipYAxis?: boolean; renderTooltip?: (data: TooltipData) => ReactElement; children?: ReactNode; interactions?: DefaultInteractionsConfig; } function HeatmapVis(props: Props) { const { dataArray, domain = DEFAULT_DOMAIN, colorMap = 'Viridis', scaleType = ScaleType.Linear, layout = 'cover', showGrid = false, invertColorMap = false, title, dtype, abscissaParams = {}, ordinateParams = {}, alpha, flipYAxis, renderTooltip, children, interactions, } = props; const { label: abscissaLabel, value: abscissaValue } = abscissaParams; const { label: ordinateLabel, value: ordinateValue } = ordinateParams; const { rows, cols } = getDims(dataArray); const abscissas = usePixelEdgeValues(abscissaValue, cols); const abscissaDomain = useAxisDomain(abscissas); assertDefined(abscissaDomain, 'Abscissas have undefined domain'); const ordinates = usePixelEdgeValues(ordinateValue, rows); const ordinateDomain = useAxisDomain(ordinates); assertDefined(ordinateDomain, 'Ordinates have undefined domain'); const abscissaToIndex = useValueToIndexScale(abscissas); const ordinateToIndex = useValueToIndexScale(ordinates); const safeDataArray = useTextureSafeNdArray(dataArray); const safeAlphaArray = useTextureSafeNdArray(alpha?.array); const keepRatio = layout !== 'fill'; return (
{ const xi = abscissaToIndex(x); const yi = ordinateToIndex(y); const abscissa = abscissas[xi]; const ordinate = ordinates[yi]; if (renderTooltip) { return renderTooltip({ abscissa, ordinate, xi, yi, x, y }); } return ( <> {`${abscissaLabel ?? 'x'}=${formatTooltipVal(abscissa)}, `} {`${ordinateLabel ?? 'y'}=${formatTooltipVal(ordinate)}`}
{formatTooltipVal(dataArray.get(yi, xi))} {dtype && {` (${formatNumType(dtype)})`}} {alpha && ` (${formatTooltipVal(alpha.array.get(yi, xi))})`}
); }} /> {children}
); } export type { Props as HeatmapVisProps }; export default HeatmapVis;