From 4cc2778ed5cedd6b5cb42d33b2f5e94ed58cba9b Mon Sep 17 00:00:00 2001 From: jobo322 Date: Tue, 3 Sep 2024 16:16:24 -0500 Subject: [PATCH 1/5] chore: approach of context to keep the calculated contours --- src/component/2d/ft/Contours.tsx | 14 +++-- src/component/context/ContourCacheContext.tsx | 9 +++ src/data/data2d/Spectrum2D/contours.ts | 58 +++++++++++++------ 3 files changed, 59 insertions(+), 22 deletions(-) create mode 100644 src/component/context/ContourCacheContext.tsx diff --git a/src/component/2d/ft/Contours.tsx b/src/component/2d/ft/Contours.tsx index 184ddcc7cc..b6f9b40f46 100644 --- a/src/component/2d/ft/Contours.tsx +++ b/src/component/2d/ft/Contours.tsx @@ -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, @@ -9,6 +9,7 @@ import { LevelSign, } from '../../../data/data2d/Spectrum2D/contours'; import { useChartData } from '../../context/ChartContext'; +import { useContourCache } from '../../context/ContourCacheContext'; import { usePreferences } from '../../context/PreferencesContext'; import { useToaster } from '../../context/ToasterContext'; import { useActiveSpectrum } from '../../hooks/useActiveSpectrum'; @@ -76,20 +77,25 @@ function ContoursPaths({ const activeSpectrum = useActiveSpectrum(); const preferences = usePreferences(); const level = useContoursLevel(spectrum, sign); + const [cache, setCache] = useContourCache(); const contours = useMemo(() => { const { contours, timeout } = drawContours( level, spectrum, + cache, sign === 'negative', ); if (timeout) { onTimeout(); } - return contours; - }, [spectrum, level, onTimeout, sign]); + setCache(cache); + return { contours, cache }; + }, [spectrum, level, onTimeout, sign, cache, setCache]); - const path = usePath(spectrum, contours); + // useEffect(() => {}, [setCache, contours]); + + const path = usePath(spectrum, contours.contours); const opacity = activeSpectrum === null || spectrumID === activeSpectrum.id diff --git a/src/component/context/ContourCacheContext.tsx b/src/component/context/ContourCacheContext.tsx new file mode 100644 index 0000000000..1537f77ec7 --- /dev/null +++ b/src/component/context/ContourCacheContext.tsx @@ -0,0 +1,9 @@ +import React, { createContext, useState } from 'react'; + +const initialContourCache: Record = {}; +const ContourCacheContext = createContext(null); +export const ContourCacheProvider = ContourCacheContext.Provider; +export function useContourCache() { + React.useContext(ContourCacheContext); + return useState(initialContourCache); +} diff --git a/src/data/data2d/Spectrum2D/contours.ts b/src/data/data2d/Spectrum2D/contours.ts index bbace79122..1cb8c252b5 100644 --- a/src/data/data2d/Spectrum2D/contours.ts +++ b/src/data/data2d/Spectrum2D/contours.ts @@ -177,45 +177,67 @@ function range(from: number, to: number, step: number) { function drawContours( level: ContourItem, spectrum: Spectrum2D, + contourCache: Record, negative = false, quadrant = 'rr', ) { const { contourLevels, numberOfLayers } = level; + const key = negative ? 'negative' : 'positive'; - return getContours({ + const nbLevels = Math.min( + numberOfLayers, + contourLevels[1] - contourLevels[0], + ); + + const { id, data } = spectrum; + if (!contourCache[id]) { + contourCache[id] = {}; + } + + if (!(key in contourCache[id])) { + contourCache[id][key] = { contours: [], timeout: false }; + } + + const oneSenseContours = contourCache[id][key].contours; + const selectedLevels = getRange( + Math.max(0, contourLevels[0]), + contourLevels[1], + nbLevels, + ).map((e) => Math.round(e)); + + const levels = selectedLevels.filter((level) => !oneSenseContours[level]); + const { contours, timeout } = getContours({ + levels, negative, - boundary: contourLevels, - nbLevels: numberOfLayers, - data: spectrum.data[quadrant], + data: data[quadrant], }); + + for (const [i, level] of contours.entries()) { + oneSenseContours[levels[i]] = level; + } + contourCache[id][key] = { contours: oneSenseContours, timeout }; + + return { + contours: selectedLevels.map((level) => oneSenseContours[level]), + timeout, + }; } interface ContoursCalcOptions { - boundary: [number, number]; + levels: number[]; negative?: boolean; timeout?: number; - nbLevels: number; data: NmrData2DFt['rr']; } function getContours(options: ContoursCalcOptions) { - const { - boundary, - negative = false, - timeout = 2000, - nbLevels, - data, - } = options; + const { levels, negative = false, timeout = 2000, data } = 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]; - let _range = getRange(minLevel, maxLevel, Math.min(nbLevels, diffRange), 2); + let _range = levels.map((level) => calculateValueOfLevel(level, max)); if (negative) { _range = _range.map((value) => -value); } From a9b6e392cef99ebf5b33bddbdbf88eb6735770f9 Mon Sep 17 00:00:00 2001 From: jobo322 Date: Tue, 3 Sep 2024 23:21:25 -0500 Subject: [PATCH 2/5] chore: fix eslint --- src/component/2d/ft/Contours.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/component/2d/ft/Contours.tsx b/src/component/2d/ft/Contours.tsx index b6f9b40f46..ad8dc89ad1 100644 --- a/src/component/2d/ft/Contours.tsx +++ b/src/component/2d/ft/Contours.tsx @@ -1,7 +1,7 @@ import debounce from 'lodash/debounce'; import get from 'lodash/get'; import { Spectrum2D } from 'nmr-load-save'; -import { memo, useEffect, useMemo, useRef } from 'react'; +import { memo, useMemo, useRef } from 'react'; import { drawContours, From 507f1a3257ca937f209dbdc810464fc82972f1c9 Mon Sep 17 00:00:00 2001 From: jobo322 Date: Wed, 4 Sep 2024 05:31:30 -0500 Subject: [PATCH 3/5] chore: do not use context --- src/component/2d/ft/Contours.tsx | 2 +- src/component/context/ContourCacheContext.tsx | 9 --------- src/component/hooks/useContourCache.ts | 14 ++++++++++++++ 3 files changed, 15 insertions(+), 10 deletions(-) delete mode 100644 src/component/context/ContourCacheContext.tsx create mode 100644 src/component/hooks/useContourCache.ts diff --git a/src/component/2d/ft/Contours.tsx b/src/component/2d/ft/Contours.tsx index ad8dc89ad1..6f50f4290c 100644 --- a/src/component/2d/ft/Contours.tsx +++ b/src/component/2d/ft/Contours.tsx @@ -9,10 +9,10 @@ import { LevelSign, } from '../../../data/data2d/Spectrum2D/contours'; import { useChartData } from '../../context/ChartContext'; -import { useContourCache } from '../../context/ContourCacheContext'; import { usePreferences } from '../../context/PreferencesContext'; import { useToaster } from '../../context/ToasterContext'; import { useActiveSpectrum } from '../../hooks/useActiveSpectrum'; +import { useContourCache } from '../../hooks/useContourCache'; import { PathBuilder } from '../../utility/PathBuilder'; import { getSpectraByNucleus } from '../../utility/getSpectraByNucleus'; import { useScale2DX, useScale2DY } from '../utilities/scale'; diff --git a/src/component/context/ContourCacheContext.tsx b/src/component/context/ContourCacheContext.tsx deleted file mode 100644 index 1537f77ec7..0000000000 --- a/src/component/context/ContourCacheContext.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import React, { createContext, useState } from 'react'; - -const initialContourCache: Record = {}; -const ContourCacheContext = createContext(null); -export const ContourCacheProvider = ContourCacheContext.Provider; -export function useContourCache() { - React.useContext(ContourCacheContext); - return useState(initialContourCache); -} diff --git a/src/component/hooks/useContourCache.ts b/src/component/hooks/useContourCache.ts new file mode 100644 index 0000000000..92e25cb44e --- /dev/null +++ b/src/component/hooks/useContourCache.ts @@ -0,0 +1,14 @@ +import { BasicContour } from 'ml-conrec/lib/BasicContourDrawer'; +import { useState } from 'react'; + +interface ContourResult { + contour: BasicContour[]; + timeout: boolean; +} +const initialContourCache: Record< + string, + { positive: ContourResult; negative: ContourResult } +> = {}; +export function useContourCache() { + return useState(initialContourCache); +} From de01ae774eb16d21adb57566cb59a48f5b080c71 Mon Sep 17 00:00:00 2001 From: jobo322 Date: Thu, 5 Sep 2024 11:02:12 -0500 Subject: [PATCH 4/5] fix: improve useless negative level calculations --- src/component/2d/ft/Contours.tsx | 12 ++-- src/component/hooks/useContourCache.ts | 12 ++-- src/data/data2d/Spectrum2D/contours.ts | 76 +++++++++++++++++++------- 3 files changed, 67 insertions(+), 33 deletions(-) diff --git a/src/component/2d/ft/Contours.tsx b/src/component/2d/ft/Contours.tsx index 6f50f4290c..695523456b 100644 --- a/src/component/2d/ft/Contours.tsx +++ b/src/component/2d/ft/Contours.tsx @@ -80,16 +80,14 @@ function ContoursPaths({ const [cache, setCache] = useContourCache(); const contours = useMemo(() => { - const { contours, timeout } = drawContours( - level, - spectrum, - cache, - sign === 'negative', - ); + const { contours, timeout } = drawContours(level, spectrum, cache, sign); + if (timeout) { onTimeout(); + } else { + setCache(cache); } - setCache(cache); + return { contours, cache }; }, [spectrum, level, onTimeout, sign, cache, setCache]); diff --git a/src/component/hooks/useContourCache.ts b/src/component/hooks/useContourCache.ts index 92e25cb44e..0e9e7187da 100644 --- a/src/component/hooks/useContourCache.ts +++ b/src/component/hooks/useContourCache.ts @@ -1,14 +1,16 @@ import { BasicContour } from 'ml-conrec/lib/BasicContourDrawer'; import { useState } from 'react'; -interface ContourResult { - contour: BasicContour[]; +export interface ContourResult { + contours: BasicContour[]; timeout: boolean; } -const initialContourCache: Record< + +export type ContourCache = Record< string, - { positive: ContourResult; negative: ContourResult } -> = {}; + { positive?: ContourResult; negative?: ContourResult } +>; +const initialContourCache: ContourCache = {}; export function useContourCache() { return useState(initialContourCache); } diff --git a/src/data/data2d/Spectrum2D/contours.ts b/src/data/data2d/Spectrum2D/contours.ts index 1cb8c252b5..a921c96e1c 100644 --- a/src/data/data2d/Spectrum2D/contours.ts +++ b/src/data/data2d/Spectrum2D/contours.ts @@ -3,6 +3,10 @@ import { Conrec } from 'ml-conrec'; import { xMaxAbsoluteValue } from 'ml-spectra-processing'; import { Spectrum2D } from 'nmr-load-save'; +import { + ContourCache, + ContourResult, +} from '../../../component/hooks/useContourCache'; import { calculateSanPlot } from '../../utilities/calculateSanPlot'; interface Level { @@ -177,12 +181,11 @@ function range(from: number, to: number, step: number) { function drawContours( level: ContourItem, spectrum: Spectrum2D, - contourCache: Record, - negative = false, + contourCache: ContourCache, + sign: 'positive' | 'negative' = 'positive', quadrant = 'rr', ) { const { contourLevels, numberOfLayers } = level; - const key = negative ? 'negative' : 'positive'; const nbLevels = Math.min( numberOfLayers, @@ -194,11 +197,11 @@ function drawContours( contourCache[id] = {}; } - if (!(key in contourCache[id])) { - contourCache[id][key] = { contours: [], timeout: false }; + if (!(sign in contourCache[id])) { + contourCache[id][sign] = { contours: [], timeout: false }; } - const oneSenseContours = contourCache[id][key].contours; + const oneSenseContours = (contourCache[id][sign] as ContourResult).contours; const selectedLevels = getRange( Math.max(0, contourLevels[0]), contourLevels[1], @@ -206,16 +209,27 @@ function drawContours( ).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, - negative, + sign, data: data[quadrant], }); + if (sign === 'negative') { + levels.reverse(); + } for (const [i, level] of contours.entries()) { oneSenseContours[levels[i]] = level; } - contourCache[id][key] = { contours: oneSenseContours, timeout }; + contourCache[id][sign] = { contours: oneSenseContours, timeout }; return { contours: selectedLevels.map((level) => oneSenseContours[level]), @@ -223,33 +237,45 @@ function drawContours( }; } +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[]; - negative?: boolean; + sign?: 'positive' | 'negative'; timeout?: number; data: NmrData2DFt['rr']; } function getContours(options: ContoursCalcOptions) { - const { levels, negative = false, timeout = 2000, data } = 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 { 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 (negative) { + + 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)) { - const emptyLine: number[] = []; - return { - contours: _range.map((r) => ({ zValue: r, lines: emptyLine })), - timeout: false, - }; + 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), @@ -257,6 +283,14 @@ function getContours(options: ContoursCalcOptions) { }); } +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). From f2d29f990ebda05dab0b950d1c96222db9827922 Mon Sep 17 00:00:00 2001 From: hamed musallam Date: Fri, 6 Sep 2024 11:41:47 +0200 Subject: [PATCH 5/5] refactor: contours context and hook --- src/component/2d/ft/Contours.tsx | 34 +-- src/component/2d/ft/ContoursContext.tsx | 71 +++++++ src/data/data2d/Spectrum2D/contours.ts | 263 +++++++++++++++--------- 3 files changed, 251 insertions(+), 117 deletions(-) create mode 100644 src/component/2d/ft/ContoursContext.tsx diff --git a/src/component/2d/ft/Contours.tsx b/src/component/2d/ft/Contours.tsx index 695523456b..aac485beec 100644 --- a/src/component/2d/ft/Contours.tsx +++ b/src/component/2d/ft/Contours.tsx @@ -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, @@ -12,11 +12,12 @@ import { useChartData } from '../../context/ChartContext'; import { usePreferences } from '../../context/PreferencesContext'; import { useToaster } from '../../context/ToasterContext'; import { useActiveSpectrum } from '../../hooks/useActiveSpectrum'; -import { useContourCache } from '../../hooks/useContourCache'; import { PathBuilder } from '../../utility/PathBuilder'; import { getSpectraByNucleus } from '../../utility/getSpectraByNucleus'; import { useScale2DX, useScale2DY } from '../utilities/scale'; +import { ContoursProvider, useContours } from './ContoursContext'; + interface ContoursPathsProps { id: string; color: string; @@ -77,23 +78,20 @@ function ContoursPaths({ const activeSpectrum = useActiveSpectrum(); const preferences = usePreferences(); const level = useContoursLevel(spectrum, sign); - const [cache, setCache] = useContourCache(); - const contours = useMemo(() => { - const { contours, timeout } = drawContours(level, spectrum, cache, sign); - - if (timeout) { + const contours = useContours(); + const signContours = contours?.[spectrumID][sign] || { + contours: [], + timeout: false, + }; + + useEffect(() => { + if (signContours.timeout) { onTimeout(); - } else { - setCache(cache); } + }, [onTimeout, signContours.timeout]); - return { contours, cache }; - }, [spectrum, level, onTimeout, sign, cache, setCache]); - - // useEffect(() => {}, [setCache, contours]); - - const path = usePath(spectrum, contours.contours); + const path = usePath(spectrum, signContours.contours); const opacity = activeSpectrum === null || spectrumID === activeSpectrum.id @@ -173,5 +171,9 @@ export default function Contours() { ) as Spectrum2D[]; }, [activeTab, spectra]); - return ; + return ( + + + + ); } diff --git a/src/component/2d/ft/ContoursContext.tsx b/src/component/2d/ft/ContoursContext.tsx new file mode 100644 index 0000000000..f7b1986a3c --- /dev/null +++ b/src/component/2d/ft/ContoursContext.tsx @@ -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; + +const ContoursContext = createContext({}); + +interface ContoursProviderProps { + children: ReactNode; +} + +function getContours(spectrum: Spectrum2D, negative = false) { + //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. + 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 ( + + {children} + + ); +} + +export function useContours() { + const context = useContext(ContoursContext); + + if (!context) { + throw new Error('Contours context was not found'); + } + + return context; +} diff --git a/src/data/data2d/Spectrum2D/contours.ts b/src/data/data2d/Spectrum2D/contours.ts index a921c96e1c..f428666904 100644 --- a/src/data/data2d/Spectrum2D/contours.ts +++ b/src/data/data2d/Spectrum2D/contours.ts @@ -3,11 +3,11 @@ import { Conrec } from 'ml-conrec'; import { xMaxAbsoluteValue } from 'ml-spectra-processing'; import { Spectrum2D } from 'nmr-load-save'; -import { - ContourCache, - ContourResult, -} from '../../../component/hooks/useContourCache'; -import { calculateSanPlot } from '../../utilities/calculateSanPlot'; +// import { +// ContourCache, +// ContourResult, +// } from '../../../component/hooks/useContourCache'; +// import { calculateSanPlot } from '../../utilities/calculateSanPlot'; interface Level { positive: ContourItem; @@ -48,6 +48,14 @@ interface ReturnContoursManager { 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; @@ -178,104 +186,36 @@ function range(from: number, to: number, step: number) { return result; } -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 { + boundary, + negative = false, + timeout = 4000, + nbLevels, + data, + } = 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)); - let _range = levels.map((level) => calculateValueOfLevel(level, max)); + const minLevel = calculateValueOfLevel(boundary[0], max); + const maxLevel = calculateValueOfLevel(boundary[1], max); + + const diffRange = boundary[1] - boundary[0]; - if (sign === 'negative') { + let _range = getRange(minLevel, maxLevel, Math.min(nbLevels, diffRange), 2); + if (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 emptyLine: number[] = []; + return { + contours: _range.map((r) => ({ zValue: r, lines: emptyLine })), + timeout: false, + }; } - 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), @@ -283,14 +223,135 @@ function getContours(options: ContoursCalcOptions) { }); } -function emptyResult(_range: number[]) { - const emptyLine: number[] = []; - return { - contours: _range.map((r) => ({ zValue: r, lines: emptyLine })), - timeout: false, - }; +function drawContours( + spectrum: Spectrum2D, + level: ContourItem, + negative = false, + quadrant = 'rr', +) { + const { contourLevels, numberOfLayers } = level; + + return getContours({ + boundary: contourLevels, + nbLevels: numberOfLayers, + data: spectrum.data[quadrant], + 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).