import type { Domain, NumArray } from '@h5web/shared'; import { assertDefined, assertLength, formatTooltipVal, ScaleType, } from '@h5web/shared'; import type { ThreeEvent } from '@react-three/fiber'; import { useTooltip } from '@visx/tooltip'; import { toArray } from 'lodash'; import type { NdArray } from 'ndarray'; import type { ReactNode } from 'react'; import type { DefaultInteractionsConfig } from '../../interactions/DefaultInteractions'; import DefaultInteractions from '../../interactions/DefaultInteractions'; import ResetZoomButton from '../../toolbar/floating/ResetZoomButton'; import ColorBar from '../heatmap/ColorBar'; import type { ColorMap } from '../heatmap/models'; import { useAxisDomain } from '../hooks'; import TooltipOverlay from '../shared/TooltipOverlay'; import VisCanvas from '../shared/VisCanvas'; import ScatterPoints from './ScatterPoints'; import styles from './ScatterVis.module.css'; import type { ScatterAxisParams } from './models'; interface Props { abscissaParams: ScatterAxisParams; ordinateParams: ScatterAxisParams; dataArray: NdArray; domain: Domain; colorMap?: ColorMap; invertColorMap?: boolean; scaleType?: ScaleType; showGrid?: boolean; title?: string; size?: number; children?: ReactNode; interactions?: DefaultInteractionsConfig; onPointClick?: (index: number, evt: ThreeEvent) => void; } function ScatterVis(props: Props) { const { abscissaParams, ordinateParams, dataArray, domain, colorMap = 'Viridis', invertColorMap = false, scaleType = ScaleType.Linear, showGrid = true, title, size = 10, children, interactions, onPointClick, } = props; const { value: abscissaValue, label: abscissaLabel, scaleType: abscissaScaleType, } = abscissaParams; const { value: ordinateValue, label: ordinateLabel, scaleType: ordinateScaleType, } = ordinateParams; assertLength(abscissaValue, dataArray.size, 'abscissa'); assertLength(ordinateValue, dataArray.size, 'ordinates'); const abscissas = toArray(abscissaValue); const ordinates = toArray(ordinateValue); const abscissaDomain = useAxisDomain(abscissas, abscissaScaleType, 0.01); assertDefined(abscissaDomain, 'Abscissas have undefined domain'); const ordinateDomain = useAxisDomain(ordinates, ordinateScaleType, 0.01); assertDefined(ordinateDomain, 'Ordinates have undefined domain'); const { tooltipOpen, tooltipTop, tooltipLeft, tooltipData: tooltipIndex, showTooltip, hideTooltip, } = useTooltip(); return (
showTooltip({ tooltipData: index, tooltipLeft: evt.sourceEvent.offsetX, tooltipTop: evt.sourceEvent.offsetY, }) } onPointerOut={() => hideTooltip()} /> {tooltipIndex !== undefined && ( <> {`${abscissaLabel ?? 'x'} = ${formatTooltipVal( abscissas[tooltipIndex] )}, ${ordinateLabel ?? 'y'} = ${formatTooltipVal( ordinates[tooltipIndex] )}`}
{formatTooltipVal(dataArray.get(tooltipIndex))}
)}
{children}
); } export type { Props as ScatterVisProps }; export default ScatterVis;