Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: calculate only missing contour levels #3224

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 18 additions & 12 deletions src/component/2d/ft/Contours.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import debounce from 'lodash/debounce';
import get from 'lodash/get';
import { Spectrum2D } from 'nmr-load-save';
import { memo, useMemo, useRef } from 'react';
import { memo, useEffect, useMemo, useRef } from 'react';

import {
drawContours,
Expand All @@ -16,6 +16,8 @@
import { getSpectraByNucleus } from '../../utility/getSpectraByNucleus';
import { useScale2DX, useScale2DY } from '../utilities/scale';

import { ContoursProvider, useContours } from './ContoursContext';

interface ContoursPathsProps {
id: string;
color: string;
Expand Down Expand Up @@ -75,21 +77,21 @@
}: ContoursPathsProps) {
const activeSpectrum = useActiveSpectrum();
const preferences = usePreferences();
const level = useContoursLevel(spectrum, sign);

Check failure on line 80 in src/component/2d/ft/Contours.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

'level' is assigned a value but never used

const contours = useMemo(() => {
const { contours, timeout } = drawContours(
level,
spectrum,
sign === 'negative',
);
if (timeout) {
const contours = useContours();
const signContours = contours?.[spectrumID][sign] || {
contours: [],
timeout: false,
};

useEffect(() => {
if (signContours.timeout) {
onTimeout();
}
return contours;
}, [spectrum, level, onTimeout, sign]);
}, [onTimeout, signContours.timeout]);

const path = usePath(spectrum, contours);
const path = usePath(spectrum, signContours.contours);

const opacity =
activeSpectrum === null || spectrumID === activeSpectrum.id
Expand Down Expand Up @@ -169,5 +171,9 @@
) as Spectrum2D[];
}, [activeTab, spectra]);

return <MemoizedContours {...{ spectra: spectra2d, displayerKey }} />;
return (
<ContoursProvider>
<MemoizedContours {...{ spectra: spectra2d, displayerKey }} />
</ContoursProvider>
);
}
71 changes: 71 additions & 0 deletions src/component/2d/ft/ContoursContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { DrawContourResult } from 'ml-conrec';
import { Spectrum2D } from 'nmr-load-save';
import { createContext, ReactNode, useContext, useMemo } from 'react';

import { drawContours } from '../../../data/data2d/Spectrum2D/contours';
import useSpectraByActiveNucleus from '../../hooks/useSpectraPerNucleus';

interface Contour {
positive: DrawContourResult<'basic'>;
negative: DrawContourResult<'basic'>;
}

type Contours = Record<string, Contour>;

const ContoursContext = createContext<Contours>({});

interface ContoursProviderProps {
children: ReactNode;
}

function getContours(spectrum: Spectrum2D, negative = false) {
//TODO change the static contour options.

Check warning on line 22 in src/component/2d/ft/ContoursContext.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Unexpected 'todo' comment: 'TODO change the static contour options.'
return drawContours(
spectrum,
{
contourLevels: [0, 100],
numberOfLayers: 10,
},
negative,
);
}

export function ContoursProvider({ children }: ContoursProviderProps) {
const spectra = useSpectraByActiveNucleus() as Spectrum2D[];

// TODO: Move the contour options from the `display` object within the spectrum object to `view` to prevent recalculating the contours when those options change.

Check warning on line 36 in src/component/2d/ft/ContoursContext.tsx

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Unexpected 'todo' comment: 'TODO: Move the contour options from the...'
const contours = useMemo(() => {
const contours: Contours = {};
for (const spectrum of spectra) {
const {
id,
info: { isFt },
} = spectrum;

if (!isFt) {
continue;
}

const positive = getContours(spectrum);
const negative = getContours(spectrum, true);
contours[id] = { positive, negative };
}
return contours;
}, [spectra]);

return (
<ContoursContext.Provider value={contours}>
{children}
</ContoursContext.Provider>
);
}

export function useContours() {
const context = useContext(ContoursContext);

if (!context) {
throw new Error('Contours context was not found');
}

return context;
}
16 changes: 16 additions & 0 deletions src/component/hooks/useContourCache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { BasicContour } from 'ml-conrec/lib/BasicContourDrawer';
import { useState } from 'react';

export interface ContourResult {
contours: BasicContour[];
timeout: boolean;
}

export type ContourCache = Record<
string,
{ positive?: ContourResult; negative?: ContourResult }
>;
const initialContourCache: ContourCache = {};
export function useContourCache() {
return useState(initialContourCache);
}
169 changes: 143 additions & 26 deletions src/data/data2d/Spectrum2D/contours.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import { xMaxAbsoluteValue } from 'ml-spectra-processing';
import { Spectrum2D } from 'nmr-load-save';

import { calculateSanPlot } from '../../utilities/calculateSanPlot';
// import {
// ContourCache,
// ContourResult,
// } from '../../../component/hooks/useContourCache';
// import { calculateSanPlot } from '../../utilities/calculateSanPlot';

interface Level {
positive: ContourItem;
Expand Down Expand Up @@ -44,6 +48,14 @@
checkLevel: () => Level;
}

interface ContoursCalcOptions {
boundary: [number, number];
negative?: boolean;
timeout?: number;
nbLevels: number;
data: NmrData2DFt['rr'];
}

function getDefaultContoursLevel(spectrum: Spectrum2D, quadrant = 'rr') {
const { data, info } = spectrum;

Expand Down Expand Up @@ -174,35 +186,11 @@
return result;
}

function drawContours(
level: ContourItem,
spectrum: Spectrum2D,
negative = false,
quadrant = 'rr',
) {
const { contourLevels, numberOfLayers } = level;

return getContours({
negative,
boundary: contourLevels,
nbLevels: numberOfLayers,
data: spectrum.data[quadrant],
});
}

interface ContoursCalcOptions {
boundary: [number, number];
negative?: boolean;
timeout?: number;
nbLevels: number;
data: NmrData2DFt['rr'];
}

function getContours(options: ContoursCalcOptions) {
const {
boundary,
negative = false,
timeout = 2000,
timeout = 4000,
nbLevels,
data,
} = options;
Expand Down Expand Up @@ -235,6 +223,135 @@
});
}

function drawContours(
spectrum: Spectrum2D,
level: ContourItem,
negative = false,
quadrant = 'rr',
) {
const { contourLevels, numberOfLayers } = level;

return getContours({
boundary: contourLevels,
nbLevels: numberOfLayers,
data: spectrum.data[quadrant],

Check failure on line 237 in src/data/data2d/Spectrum2D/contours.ts

View workflow job for this annotation

GitHub Actions / nodejs / test (18)

src/data/data2d/__tests__/Datum2D.test.ts > Datum2D

TypeError: Cannot read properties of undefined (reading 'rr') ❯ Module.drawContours src/data/data2d/Spectrum2D/contours.ts:237:24 ❯ src/data/data2d/__tests__/Datum2D.test.ts:28:20

Check failure on line 237 in src/data/data2d/Spectrum2D/contours.ts

View workflow job for this annotation

GitHub Actions / nodejs / test (20)

src/data/data2d/__tests__/Datum2D.test.ts > Datum2D

TypeError: Cannot read properties of undefined (reading 'rr') ❯ Module.drawContours src/data/data2d/Spectrum2D/contours.ts:237:24 ❯ src/data/data2d/__tests__/Datum2D.test.ts:28:20

Check failure on line 237 in src/data/data2d/Spectrum2D/contours.ts

View workflow job for this annotation

GitHub Actions / nodejs / test (22)

src/data/data2d/__tests__/Datum2D.test.ts > Datum2D

TypeError: Cannot read properties of undefined (reading 'rr') ❯ Module.drawContours src/data/data2d/Spectrum2D/contours.ts:237:24 ❯ src/data/data2d/__tests__/Datum2D.test.ts:28:20
negative,
});
}

// function drawContours(
// level: ContourItem,
// spectrum: Spectrum2D,
// contourCache: ContourCache,
// sign: 'positive' | 'negative' = 'positive',
// quadrant = 'rr',
// ) {
// const { contourLevels, numberOfLayers } = level;

// const nbLevels = Math.min(
// numberOfLayers,
// contourLevels[1] - contourLevels[0],
// );

// const { id, data } = spectrum;
// if (!contourCache[id]) {
// contourCache[id] = {};
// }

// if (!(sign in contourCache[id])) {
// contourCache[id][sign] = { contours: [], timeout: false };
// }

// const oneSenseContours = (contourCache[id][sign] as ContourResult).contours;
// const selectedLevels = getRange(
// Math.max(0, contourLevels[0]),
// contourLevels[1],
// nbLevels,
// ).map((e) => Math.round(e));

// const levels = selectedLevels.filter((level) => !oneSenseContours[level]);

// if (levels.length > 0 && levels.length < numberOfLayers) {
// const totalSize = data[quadrant].z[0].length * data[quadrant].z.length;
// if (totalSize < 5e6) {
// addAditionalLevels(levels, oneSenseContours, numberOfLayers);
// }
// }

// const { contours, timeout } = getContours({
// levels,
// sign,
// data: data[quadrant],
// });

// if (sign === 'negative') {
// levels.reverse();
// }
// for (const [i, level] of contours.entries()) {
// oneSenseContours[levels[i]] = level;
// }
// contourCache[id][sign] = { contours: oneSenseContours, timeout };

// return {
// contours: selectedLevels.map((level) => oneSenseContours[level]),
// timeout,
// };
// }

// function addAditionalLevels(
// levels: number[],
// oneSenseContours: ContourResult['contours'],
// numberOfLayers: number,
// ) {
// for (let i = 100; levels.length < numberOfLayers && i <= 0; i--) {
// if (!oneSenseContours[i] && !levels[i]) levels.push(i);
// }
// levels.sort((a, b) => a - b);
// }

// interface ContoursCalcOptions {
// levels: number[];
// sign?: 'positive' | 'negative';
// timeout?: number;
// data: NmrData2DFt['rr'];
// }

// function getContours(options: ContoursCalcOptions) {
// const { levels, sign = 'positive', timeout = 4000, data } = options;
// const max = Math.max(Math.abs(data.minZ), Math.abs(data.maxZ));
// let _range = levels.map((level) => calculateValueOfLevel(level, max));

// if (sign === 'negative') {
// _range = _range.map((value) => -value);
// if (_range.filter((value) => value >= data.minZ).length === 0) {
// return emptyResult(_range);
// }
// } else if (_range.filter((value) => value <= data.maxZ).length === 0) {
// return emptyResult(_range);
// }

// if (_range.every((r) => r === 0)) {
// return emptyResult(_range);
// }
// const ys = getRange(data.minY, data.maxY, data.z.length);
// const xs = getRange(data.minX, data.maxX, data.z[0].length);

// const conrec = new Conrec(data.z, { xs, ys, swapAxes: false });
// return conrec.drawContour({
// contourDrawer: 'basic',
// levels: Array.from(_range),
// timeout,
// });
// }

// function emptyResult(_range: number[]) {
// const emptyLine: number[] = [];
// return {
// contours: _range.map((r) => ({ zValue: r, lines: emptyLine })),
// timeout: false,
// };
// }

/**
* calculate the intensity value in the Z matrix based in the max value of Z matrix
* and the contour level (0-100).
Expand Down
Loading