import type { Spectrum2D } from '@zakodium/nmrium-core'; import type { NmrData2DFid, NmrData2DFt } from 'cheminfo-types'; import { matrixZPivotRescale } from 'ml-spectra-processing'; import { useEffect, useMemo, useRef } from 'react'; import { isFid2DSpectrum } from '../../../data/data2d/Spectrum2D/isSpectrum2D.js'; import { useChartData } from '../../context/ChartContext.js'; import useSpectrum from '../../hooks/useSpectrum.js'; import { get2DXScale, get2DYScale } from '../utilities/scale.js'; export function FidCanvas() { const { width, height, margin, xDomain, yDomain, originDomain, mode } = useChartData(); const canvasRef = useRef(null); const offscreenCanvasRef = useRef(null); const { left, top, right, bottom } = margin; const canvasWidth = width - right - left; const canvasHeight = height - top - bottom; const spectrum = useSpectrum() as Spectrum2D; const imageData = useMemo(() => getImageData(spectrum), [spectrum]); useEffect(() => { if (!imageData || !canvasRef.current) return; if (!offscreenCanvasRef.current) { offscreenCanvasRef.current = document.createElement('canvas'); } const offscreenCanvas = offscreenCanvasRef.current; const offscreenContext = offscreenCanvas.getContext('2d'); const context = canvasRef.current.getContext('2d'); if (!offscreenContext || !context) return; const scale2DX = get2DXScale({ margin, mode, width, xDomain: originDomain.xDomain, }); const scale2DY = get2DYScale({ margin, height, yDomain: originDomain.yDomain, }); const x1 = scale2DX(xDomain[0]) - top; const y1 = scale2DY(yDomain[0]) - left; const x2 = scale2DX(xDomain[1]) - top; const y2 = scale2DY(yDomain[1]) - left; const scaleY = canvasHeight / Math.abs(y2 - y1); const scaleX = canvasWidth / Math.abs(x2 - x1); // Set offscreen canvas size to original image size offscreenCanvas.width = imageData.width; offscreenCanvas.height = imageData.height; // Draw the original image data onto the offscreen canvas offscreenContext.putImageData(imageData, 0, 0); const baseScaleX = canvasWidth / imageData.width; const baseScaleY = canvasHeight / imageData.height; context.resetTransform(); context.clearRect(0, 0, canvasWidth, canvasHeight); context.scale(baseScaleX, baseScaleY); context.scale(scaleX, scaleY); context.translate(-x1 / baseScaleX, -y1 / baseScaleY); context.drawImage(offscreenCanvas, 0, 0); }, [ canvasWidth, canvasHeight, imageData, margin, mode, width, originDomain.xDomain, originDomain.yDomain, height, xDomain, top, yDomain, left, ]); return ( ); } function getImageData(spectrum: Spectrum2D) { const matrix = matrixZPivotRescale( isFid2DSpectrum(spectrum) ? (spectrum.data as NmrData2DFid).re.z : (spectrum.data as NmrData2DFt).rr.z, { max: 255, ArrayConstructor: Int16Array, }, ); const WIDTH = matrix[0].length; const HEIGHT = matrix.length; const arrayBuffer = new ArrayBuffer(WIDTH * HEIGHT * 4); const matrixData = new Uint8ClampedArray(arrayBuffer); for (let y = 0; y < HEIGHT; y++) { for (let x = 0; x < WIDTH; x++) { const i = (y * WIDTH + x) * 4; if (matrix[y][x] > 0) { matrixData[i] = 255; // red matrixData[i + 1] = 0; // green matrixData[i + 2] = 0; // blue matrixData[i + 3] = matrix[y][x]; // alpha } else { matrixData[i] = 0; // red matrixData[i + 1] = 0; // green matrixData[i + 2] = 255; // blue matrixData[i + 3] = -matrix[y][x]; // alpha } } } return new ImageData(matrixData, WIDTH, HEIGHT); }