import { xFindClosestIndex } from 'ml-spectra-processing'; import { Fragment, useMemo } from 'react'; import { MF } from 'react-mf'; import { get1DDataXY } from '../../data/data1d/Spectrum1D/index.js'; import { isFid2DData, isSpectrum2D, } from '../../data/data2d/Spectrum2D/isSpectrum2D.js'; import { useBrushTracker } from '../EventsTrackers/BrushTracker.js'; import { useMouseTracker } from '../EventsTrackers/MouseTracker.js'; import { useChartData } from '../context/ChartContext.js'; import { FooterContainer, InfoItem } from '../elements/Footer.js'; import { useActiveSpectrum } from '../hooks/useActiveSpectrum.js'; import { useFormatNumberByNucleus } from '../hooks/useFormatNumberByNucleus.js'; import { options } from '../toolbar/ToolTypes.js'; import type { Spectrum1DTraces } from './useTracesSpectra.js'; import type { Get2DDimensionLayoutReturn } from './utilities/DimensionLayout.js'; import { LAYOUT, getLayoutID } from './utilities/DimensionLayout.js'; import { get1DYScale, get2DXScale, get2DYScale } from './utilities/scale.js'; interface FooterBannerProps { layout: Get2DDimensionLayoutReturn; data1D: Spectrum1DTraces; } export default function FooterBanner(props: FooterBannerProps) { const { layout, data1D } = props; const position = useMouseTracker(); const { startX, endX, startY, endY, step, mouseButton } = useBrushTracker(); const { margin, width, height, xDomain, yDomain, yDomains, view: { spectra: { activeTab }, }, data: spectra, toolOptions: { selectedTool }, mode, } = useChartData(); const activeSpectrum = useActiveSpectrum(); const trackID = position && getLayoutID(layout, { startX: position.x, startY: position.y, }); const nuclei = activeTab.split(','); const [formatX, formatY] = useFormatNumberByNucleus(nuclei); const hasTraces = data1D.x || data1D.y; const scaleX = useMemo(() => { if (selectedTool === options.slicing.id || !trackID) { return null; } if (!hasTraces || trackID === 'MAIN' || trackID === 'TOP') { return get2DXScale({ width, margin, xDomain, mode }); } return get2DYScale({ height, margin, yDomain }); }, [ hasTraces, selectedTool, width, margin, xDomain, mode, trackID, height, yDomain, ]); const scaleY = useMemo(() => { if (selectedTool === options.slicing.id || !trackID) { return null; } if (!hasTraces || trackID === 'MAIN') { return get2DYScale({ height, margin, yDomain }); } if (trackID === 'TOP') { return data1D.x ? get1DYScale(yDomains[data1D.x.id], margin.top) : null; } return data1D.y ? get1DYScale(yDomains[data1D.y.id], margin.left) : null; }, [ data1D.x, data1D.y, hasTraces, height, margin, selectedTool, trackID, yDomain, yDomains, ]); if ( !activeSpectrum || !position || position.y < 10 || position.x < 10 || position.x > width - margin.right || position.y > height - margin.bottom ) { return ; } const getRealYValue = (coordinate: number) => { let axis: 'x' | 'y' | null = null; if (trackID === LAYOUT.top) { axis = 'x'; } else if (trackID === LAYOUT.left) { axis = 'y'; } const spectrum = axis && data1D[axis]; if (!scaleX || !spectrum) { return 1; } const datum = get1DDataXY(spectrum); const xIndex = xFindClosestIndex(datum.x, scaleX.invert(coordinate)); return datum.y[xIndex]; }; const getXValue = (x: number | null = null) => { if (!scaleX || !trackID) return 0; if (trackID === 'MAIN') { return scaleX.invert(x || position.y); } // return if the trackID = "MAIN" | "TOP" return scaleX.invert(x || position.x); }; const getYValue = () => { if (!scaleY || !trackID) return 0; if (trackID === 'LEFT') { return scaleY.invert(position.x); } return scaleY.invert(position.y); }; const getRation = () => { if (!trackID || trackID === 'MAIN') return 0; if (trackID === 'TOP') { return ( (getRealYValue(startX) / (getRealYValue(endX) || Number.MIN_VALUE)) * 100 ).toFixed(2); } // tracKId = "LEFT" return ( (getRealYValue(startY) / (getRealYValue(endY) || Number.MIN_VALUE)) * 100 ).toFixed(2); }; const getDeltaX = () => { if (!trackID || trackID === 'MAIN') return 0; if (trackID === 'TOP') { return (getXValue(startX) - getXValue(endX)).toPrecision(6); } return (getXValue(startY) - getXValue(endY)).toPrecision(6); }; const getLabel = (label2d: string, label1d: string, nucleus: string) => { return trackID === LAYOUT.main ? ( {label2d} ( ) ) : ( label1d ); }; const getZValue = () => { const spectrum = spectra[activeSpectrum.index]; if (trackID === LAYOUT.main && isSpectrum2D(spectrum)) { const { data } = spectrum; const { maxX, maxY, minX, minY, z } = isFid2DData(data) ? data.re : data.rr; const xStep = (maxX - minX) / (z[0].length - 1); const yStep = (maxY - minY) / (z.length - 1); const xIndex = Math.floor((getXValue() - minX) / xStep); const yIndex = Math.floor((getYValue() - minY) / yStep); if (xIndex < 0 || xIndex >= z[0].length) return 0; if (yIndex < 0 || yIndex >= z.length) return 0; return z[yIndex][xIndex]; } return 0; }; const isBrushing = step === 'brushing' && mouseButton === 'main'; return ( {getLabel('F2', 'X', nuclei[0])} : {formatX(getXValue())} ppm {getLabel('F1', 'Y', nuclei[1])} : {formatY(getYValue())} ppm Intensity : {getZValue()} {isBrushing && ( Δppm : {getDeltaX()} )} {isBrushing && ( ratio : {getRation()}% )} ); }