diff --git a/src/component/2d/ft/Contours.tsx b/src/component/2d/ft/Contours.tsx
index ecf15e3598..8f798c0104 100644
--- a/src/component/2d/ft/Contours.tsx
+++ b/src/component/2d/ft/Contours.tsx
@@ -75,19 +75,8 @@ function ContoursPaths({
}: ContoursPathsProps) {
const activeSpectrum = useActiveSpectrum();
const preferences = usePreferences();
- const level = useContoursLevel(spectrum, sign);
- const contours = useMemo(() => {
- const { contours, timeout } = drawContours(
- level,
- spectrum,
- sign === 'negative',
- );
- if (timeout) {
- onTimeout();
- }
- return contours;
- }, [spectrum, level, onTimeout, sign]);
+ const contours = useContours(spectrum, sign, onTimeout);
const path = usePath(spectrum, contours);
@@ -171,3 +160,19 @@ export default function Contours() {
return ;
}
+
+function useContours(spectrum: Spectrum2D, sign, onTimeout) {
+ const cache = useRef(new Map());
+ const level = useContoursLevel(spectrum, sign);
+
+ return useMemo(() => {
+ const { contours, timeout } = drawContours(spectrum, level, {
+ negative: sign === 'negative',
+ cache: cache.current,
+ });
+ if (timeout) {
+ onTimeout();
+ }
+ return contours;
+ }, [spectrum, level, onTimeout, sign]);
+}
diff --git a/src/data/data2d/Spectrum2D/contours.ts b/src/data/data2d/Spectrum2D/contours.ts
index 75a409f74c..1bb7f329f8 100644
--- a/src/data/data2d/Spectrum2D/contours.ts
+++ b/src/data/data2d/Spectrum2D/contours.ts
@@ -1,5 +1,6 @@
import type { NmrData2DFt } from 'cheminfo-types';
import { Conrec } from 'ml-conrec';
+import { BasicContour } from 'ml-conrec/lib/BasicContourDrawer';
import { xMaxAbsoluteValue } from 'ml-spectra-processing';
import type { Spectrum2D } from 'nmr-load-save';
@@ -174,12 +175,18 @@ function range(from: number, to: number, step: number) {
return result;
}
+interface DrawContoursOptions {
+ negative?: boolean;
+ quadrant?: 'rr' | 'ri' | 'ir' | 'ii';
+ cache: Map;
+}
+
function drawContours(
- level: ContourItem,
spectrum: Spectrum2D,
- negative = false,
- quadrant = 'rr',
+ level: ContourItem,
+ options: DrawContoursOptions,
) {
+ const { negative = false, quadrant = 'rr', cache } = options;
const { contourLevels, numberOfLayers } = level;
return getContours({
@@ -187,6 +194,7 @@ function drawContours(
boundary: contourLevels,
nbLevels: numberOfLayers,
data: spectrum.data[quadrant],
+ cache,
});
}
@@ -196,6 +204,7 @@ interface ContoursCalcOptions {
timeout?: number;
nbLevels: number;
data: NmrData2DFt['rr'];
+ cache: Map;
}
function getContours(options: ContoursCalcOptions) {
@@ -205,34 +214,33 @@ function getContours(options: ContoursCalcOptions) {
timeout = 2000,
nbLevels,
data,
+ cache,
} = options;
- const xs = getRange(data.minX, data.maxX, data.z[0].length);
- const ys = getRange(data.minY, data.maxY, data.z.length);
- const conrec = new Conrec(data.z, { xs, ys, swapAxes: false });
- const max = Math.max(Math.abs(data.minZ), Math.abs(data.maxZ));
- const minLevel = calculateValueOfLevel(boundary[0], max);
- const maxLevel = calculateValueOfLevel(boundary[1], max);
- const diffRange = boundary[1] - boundary[0];
+ const range = calculateRange(boundary, data, nbLevels, negative);
- let _range = getRange(minLevel, maxLevel, Math.min(nbLevels, diffRange), 2);
- if (negative) {
- _range = _range.map((value) => -value);
+ if (isZeroRange(range)) {
+ return createEmptyResult(range);
}
- if (_range.every((r) => r === 0)) {
- const emptyLine: number[] = [];
+ const diffRange = boundary[1] - boundary[0];
+
+ if (cache.has(diffRange)) {
return {
- contours: _range.map((r) => ({ zValue: r, lines: emptyLine })),
+ contours: cache.get(diffRange) ?? [],
timeout: false,
};
}
- return conrec.drawContour({
+ const conrec = initializeConrec(data);
+ const result = conrec.drawContour({
contourDrawer: 'basic',
- levels: Array.from(_range),
+ levels: range,
timeout,
});
+ cache.set(diffRange, result.contours);
+
+ return result;
}
/**
@@ -251,6 +259,38 @@ function calculateValueOfLevel(level: number, max: number, invert = false) {
return (max * (2 ** (level / 10) - 1)) / (2 ** 10 - 1);
}
+function calculateRange(
+ boundary: [number, number],
+ data: ContoursCalcOptions['data'],
+ nbLevels: number,
+ negative: boolean,
+): number[] {
+ const max = Math.max(Math.abs(data.minZ), Math.abs(data.maxZ));
+ const minLevel = calculateValueOfLevel(boundary[0], max);
+ const maxLevel = calculateValueOfLevel(boundary[1], max);
+ const diffRange = boundary[1] - boundary[0];
+
+ const range = getRange(minLevel, maxLevel, Math.min(nbLevels, diffRange), 2);
+ return negative ? range.map((value) => -value) : range;
+}
+
+function isZeroRange(range: number[]): boolean {
+ return range.every((r) => r === 0);
+}
+
+function createEmptyResult(range: number[]) {
+ return {
+ contours: range.map((r) => ({ zValue: r, lines: [] })),
+ timeout: false,
+ };
+}
+
+function initializeConrec(data: ContoursCalcOptions['data']): Conrec {
+ const xs = getRange(data.minX, data.maxX, data.z[0].length);
+ const ys = getRange(data.minY, data.maxY, data.z.length);
+ return new Conrec(data.z, { xs, ys, swapAxes: false });
+}
+
export {
drawContours,
contoursManager,
diff --git a/src/data/data2d/__tests__/Datum2D.test.ts b/src/data/data2d/__tests__/Datum2D.test.ts
index 9a0f499c2f..2008dbac4f 100644
--- a/src/data/data2d/__tests__/Datum2D.test.ts
+++ b/src/data/data2d/__tests__/Datum2D.test.ts
@@ -1,7 +1,8 @@
import { readFileSync } from 'node:fs';
import path from 'node:path';
-import { expect, test } from 'vitest';
+import type { BasicContour } from 'ml-conrec/lib/BasicContourDrawer';
+import { test, expect } from 'vitest';
import { addJcamp } from '../../SpectraManager.js';
import { drawContours } from '../Spectrum2D/contours.js';
@@ -9,6 +10,7 @@ import { drawContours } from '../Spectrum2D/contours.js';
test('Datum2D', () => {
const jcamp = readFileSync(path.join(__dirname, './data/cosy.jdx'), 'utf8');
const spectra: any[] = [];
+ const cache = new Map();
addJcamp(
spectra,
@@ -26,13 +28,14 @@ test('Datum2D', () => {
);
const positive = drawContours(
- { contourLevels: [10, 100], numberOfLayers: 10 },
spectra[0],
+ { contourLevels: [10, 100], numberOfLayers: 10 },
+ { cache },
);
const negative = drawContours(
- { contourLevels: [10, 100], numberOfLayers: 10 },
spectra[0],
- true,
+ { contourLevels: [10, 100], numberOfLayers: 10 },
+ { cache, negative: true },
);
expect(positive.contours).toHaveLength(10);
expect(positive.timeout).toBeFalsy();