Skip to content

Commit dfef6f3

Browse files
feat: reduce oversized spectra to 1024 points
1 parent fdbf3f2 commit dfef6f3

3 files changed

Lines changed: 177 additions & 14 deletions

File tree

src/component/2d/ft/Contours.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import { memo, useMemo, useRef } from 'react';
44

55
import type { LevelSign } from '../../../data/data2d/Spectrum2D/contours.js';
66
import { drawContours } from '../../../data/data2d/Spectrum2D/contours.js';
7+
import { isFt2DSpectrum } from '../../../data/data2d/Spectrum2D/isSpectrum2D.ts';
78
import { useChartData } from '../../context/ChartContext.js';
89
import { usePreferences } from '../../context/PreferencesContext.js';
910
import { useToaster } from '../../context/ToasterContext.js';
11+
import type { SpectrumData } from '../../hooks/use2DReducer.tsx';
12+
import { use2DReducer } from '../../hooks/use2DReducer.tsx';
1013
import { useActiveSpectrum } from '../../hooks/useActiveSpectrum.js';
1114
import { PathBuilder } from '../../utility/PathBuilder.js';
1215
import { getSpectraByNucleus } from '../../utility/getSpectraByNucleus.js';
@@ -16,18 +19,15 @@ interface ContoursPathsProps {
1619
id: string;
1720
color: string;
1821
sign: LevelSign;
19-
spectrum: Spectrum2D;
22+
spectrum: SpectrumData;
2023
onTimeout: () => void;
2124
}
2225

2326
interface ContoursInnerProps {
2427
spectra: Spectrum2D[];
2528
}
2629

27-
function usePath(
28-
spectrum: Spectrum2D,
29-
contours: ReturnType<typeof drawContours>['contours'],
30-
) {
30+
function usePath(contours: ReturnType<typeof drawContours>['contours']) {
3131
const scaleX = useScale2DX();
3232
const scaleY = useScale2DY();
3333

@@ -49,7 +49,7 @@ function usePath(
4949
return pathBuilder.toString();
5050
}
5151

52-
const useContoursLevel = (spectrum: Spectrum2D, sign: LevelSign) => {
52+
const useContoursLevel = (spectrum: SpectrumData, sign: LevelSign) => {
5353
const {
5454
display: { contourOptions },
5555
} = spectrum;
@@ -79,7 +79,7 @@ function ContoursPaths({
7979
return contours;
8080
}, [spectrum, level, onTimeout, sign]);
8181

82-
const path = usePath(spectrum, contours);
82+
const path = usePath(contours);
8383

8484
const opacity =
8585
activeSpectrum === null || spectrumID === activeSpectrum.id
@@ -113,10 +113,11 @@ function ContoursInner({ spectra }: ContoursInnerProps) {
113113
function timeoutHandler() {
114114
debounceAlert.current();
115115
}
116+
const spectraData = use2DReducer(spectra);
116117

117118
return (
118119
<g className="contours">
119-
{spectra?.map((spectrum) => {
120+
{spectraData?.map((spectrum) => {
120121
return (
121122
<g key={spectrum.id}>
122123
{spectrum.display.isPositiveVisible && (
@@ -155,8 +156,8 @@ export default function Contours() {
155156
},
156157
} = useChartData();
157158
const spectra2d = useMemo<Spectrum2D[]>(() => {
158-
return getSpectraByNucleus(activeTab, spectra).filter(
159-
(datum) => datum.info.isFt,
159+
return getSpectraByNucleus(activeTab, spectra).filter((datum) =>
160+
isFt2DSpectrum(datum),
160161
) as Spectrum2D[];
161162
}, [activeTab, spectra]);
162163

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import type { Spectrum2D } from '@zakodium/nmrium-core';
2+
import type { NmrData2DContent, NmrData2DFt } from 'cheminfo-types';
3+
import { xyEquallySpaced } from 'ml-spectra-processing';
4+
5+
import { useChartData } from '../context/ChartContext.tsx';
6+
7+
export interface SpectrumData extends Pick<Spectrum2D, 'display' | 'id'> {
8+
data: NmrData2DContent;
9+
}
10+
export function use2DReducer(spectra: Spectrum2D[]) {
11+
const { xDomain, yDomain } = useChartData();
12+
const [fromX, toX] = xDomain;
13+
const [fromY, toY] = yDomain;
14+
const outputSpectra: SpectrumData[] = [];
15+
for (const spectrum of spectra) {
16+
const { id, display, data } = spectrum;
17+
const { rr } = data as NmrData2DFt;
18+
const reducedData = reduce2DSpectrum(rr, {
19+
fromX,
20+
toX,
21+
fromY,
22+
toY,
23+
});
24+
outputSpectra.push({
25+
data: reducedData,
26+
id,
27+
display,
28+
});
29+
}
30+
return outputSpectra;
31+
}
32+
33+
interface Reduce2DSpectrumOptions {
34+
numberOfPoints?: number;
35+
fromX?: number;
36+
toX?: number;
37+
fromY?: number;
38+
toY?: number;
39+
}
40+
41+
export function reduce2DSpectrum(
42+
data: NmrData2DContent,
43+
options: Reduce2DSpectrumOptions,
44+
) {
45+
const {
46+
minY: originalMinY,
47+
minX: originalMinX,
48+
maxY: originalMaxY,
49+
maxX: originalMaxX,
50+
z,
51+
} = data;
52+
const { numberOfPoints = 1024, fromX, fromY, toX, toY } = options;
53+
const nbPointsY = z.length;
54+
const nbPointsX = z[0]?.length || 0;
55+
56+
if (nbPointsX <= numberOfPoints && nbPointsY <= numberOfPoints) {
57+
return data;
58+
}
59+
60+
const minX = fromX ?? originalMinX;
61+
const maxX = toX ?? originalMaxX;
62+
const minY = fromY ?? originalMinY;
63+
const maxY = toY ?? originalMaxY;
64+
65+
const shouldReduceX = nbPointsX > numberOfPoints;
66+
const shouldReduceY = nbPointsY > numberOfPoints;
67+
68+
const targetPointX = shouldReduceX ? numberOfPoints : nbPointsX;
69+
const targetPointY = shouldReduceY ? numberOfPoints : nbPointsY;
70+
71+
let reducedX: Float64Array[] = [];
72+
// Reduce over x dimension
73+
if (shouldReduceX) {
74+
const xAXis = generate1DArray({
75+
min: originalMinX,
76+
max: originalMaxX,
77+
numberOfPoints: nbPointsX,
78+
});
79+
for (let row = 0; row < nbPointsY; row++) {
80+
const output = xyEquallySpaced(
81+
{ x: xAXis, y: z[row] },
82+
{
83+
numberOfPoints: targetPointX,
84+
from: minX,
85+
to: maxX,
86+
},
87+
);
88+
reducedX[row] = Float64Array.from(output.y);
89+
}
90+
} else {
91+
reducedX = structuredClone(z);
92+
}
93+
94+
if (!shouldReduceY) {
95+
return {
96+
...data,
97+
minX,
98+
maxX,
99+
minY,
100+
maxY,
101+
z: reducedX,
102+
};
103+
}
104+
const yAXis = generate1DArray({
105+
min: originalMinY,
106+
max: originalMaxY,
107+
numberOfPoints: nbPointsY,
108+
});
109+
110+
const reducedMatrix: Float64Array[] = [];
111+
112+
for (let i = 0; i < targetPointY; i++) {
113+
reducedMatrix[i] = new Float64Array(targetPointX);
114+
}
115+
116+
const colBuffer = new Float64Array(nbPointsY);
117+
118+
//Reduce over y dimension
119+
for (let col = 0; col < targetPointX; col++) {
120+
for (let row = 0; row < nbPointsY; row++) {
121+
colBuffer[row] = reducedX[row][col];
122+
}
123+
const output = xyEquallySpaced(
124+
{ x: yAXis, y: colBuffer },
125+
{
126+
numberOfPoints: targetPointY,
127+
from: minY,
128+
to: maxY,
129+
},
130+
);
131+
132+
//resample column into the matrix
133+
for (let row = 0; row < targetPointY; row++) {
134+
reducedMatrix[row][col] = output.y[row];
135+
}
136+
}
137+
138+
return {
139+
...data,
140+
minX,
141+
maxX,
142+
minY,
143+
maxY,
144+
z: reducedMatrix,
145+
};
146+
}
147+
148+
interface Generate1DArray {
149+
min: number;
150+
max: number;
151+
numberOfPoints: number;
152+
}
153+
154+
function generate1DArray(options: Generate1DArray) {
155+
const { min, max, numberOfPoints } = options;
156+
const array = new Float64Array(numberOfPoints);
157+
const step = (max - min) / (numberOfPoints - 1);
158+
for (let i = 0; i < numberOfPoints; i++) {
159+
array[i] = min + i * step;
160+
}
161+
162+
return array;
163+
}

src/data/data2d/Spectrum2D/contours.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { NmrData2DFt } from 'cheminfo-types';
33
import { Conrec } from 'ml-conrec';
44
import { xMaxAbsoluteValue } from 'ml-spectra-processing';
55

6+
import type { SpectrumData } from '../../../component/hooks/use2DReducer.tsx';
67
import { calculateSanPlot } from '../../utilities/calculateSanPlot.js';
78

89
interface Level {
@@ -184,18 +185,16 @@ function range(from: number, to: number, step: number) {
184185

185186
function drawContours(
186187
level: ContourItem,
187-
spectrum: Spectrum2D,
188+
spectrum: SpectrumData,
188189
negative = false,
189-
quadrant = 'rr',
190190
) {
191191
const { contourLevels, numberOfLayers } = level;
192192

193193
return getContours({
194194
negative,
195195
boundary: contourLevels,
196196
nbLevels: numberOfLayers,
197-
// @ts-expect-error type of NmrData2D should have a discriminator field to separate fid and ft
198-
data: spectrum.data[quadrant],
197+
data: spectrum.data,
199198
});
200199
}
201200

0 commit comments

Comments
 (0)