diff --git a/src/component/1d/tool/PeakPointer.tsx b/src/component/1d/tool/PeakPointer.tsx index e8557db78..d2c1966a5 100644 --- a/src/component/1d/tool/PeakPointer.tsx +++ b/src/component/1d/tool/PeakPointer.tsx @@ -1,7 +1,5 @@ -import max from 'ml-array-max'; import { Spectrum1D } from 'nmr-load-save'; -import { get1DDataXY } from '../../../data/data1d/Spectrum1D/get1DDataXY'; import { useBrushTracker } from '../../EventsTrackers/BrushTracker'; import { useMouseTracker } from '../../EventsTrackers/MouseTracker'; import { useChartData } from '../../context/ChartContext'; @@ -9,6 +7,7 @@ import { useScaleChecked } from '../../context/ScaleContext'; import { useActiveSpectrum } from '../../hooks/useActiveSpectrum'; import useSpectraByActiveNucleus from '../../hooks/useSpectraPerNucleus'; import { options } from '../../toolbar/ToolTypes'; +import { getClosePeak } from '../../utility/getClosePeak'; const styles = { radius: 10, @@ -18,31 +17,8 @@ const styles = { SVGPadding: 1, }; -interface PeakPosition { - x: number; - y: number; -} - const LookWidth = 10; -function getClosePeak( - spectrum: Spectrum1D, - range: number[], -): PeakPosition | null { - const datum = get1DDataXY(spectrum); - const maxIndex = datum.x.findIndex((number) => number >= range[1]) - 1; - const minIndex = datum.x.findIndex((number) => number >= range[0]); - - const yDataRange = datum.y.slice(minIndex, maxIndex); - if (!yDataRange || yDataRange.length === 0) return null; - - const y = max(yDataRange); - const xIndex = minIndex + yDataRange.indexOf(y); - const x = datum.x[xIndex]; - - return { x, y }; -} - function PeakPointer() { const { height, @@ -74,14 +50,17 @@ function PeakPointer() { if (spectrumIndex === -1) return null; - const range = [ + const [from, to] = [ scaleX().invert(position.x - LookWidth), scaleX().invert(position.x + LookWidth), ].sort((a, b) => { return a - b; }); - const closePeak = getClosePeak(spectra[spectrumIndex] as Spectrum1D, range); + const closePeak = getClosePeak(spectra[spectrumIndex] as Spectrum1D, { + from, + to, + }); if (!closePeak) return null; const x = scaleX()(closePeak.x); diff --git a/src/component/reducer/actions/PeaksActions.ts b/src/component/reducer/actions/PeaksActions.ts index dcb4d591b..a28c82015 100644 --- a/src/component/reducer/actions/PeaksActions.ts +++ b/src/component/reducer/actions/PeaksActions.ts @@ -1,5 +1,4 @@ import { v4 } from '@lukeed/uuid'; -import { NmrData1D } from 'cheminfo-types'; import { Draft, original } from 'immer'; import { xFindClosestIndex } from 'ml-spectra-processing'; import { @@ -12,7 +11,6 @@ import { Peak1D, OptionsXYAutoPeaksPicking } from 'nmr-processing'; import { getShiftX, - lookupPeak, autoPeakPicking, optimizePeaks, } from '../../../data/data1d/Spectrum1D'; @@ -23,6 +21,7 @@ import { State } from '../Reducer'; import { getActiveSpectrum } from '../helper/getActiveSpectrum'; import getRange from '../helper/getRange'; import { ActionType } from '../types/ActionType'; +import { getClosePeak } from '../../utility/getClosePeak'; type AddPeakAction = ActionType<'ADD_PEAK', { x: number }>; type AddPeaksAction = ActionType<'ADD_PEAKS', { startX: number; endX: number }>; @@ -74,7 +73,7 @@ function handleAddPeak(draft: Draft, action: AddPeakAction) { const startX = mouseXPosition - xShift; const endX = mouseXPosition + xShift; const [from, to] = getRange(draft, { startX, endX }); - const candidatePeak = lookupPeak(state.data[index].data as NmrData1D, { + const candidatePeak = getClosePeak(state.data[index] as Spectrum1D, { from, to, }); @@ -108,7 +107,7 @@ function handleAddPeaks(draft: Draft, action: AddPeaksAction) { const [from, to] = getRange(draft, { startX, endX }); if (from !== to) { - const peak = lookupPeak(datumOriginal.data, { from, to }); + const peak = getClosePeak(datumOriginal, { from, to }); const shiftX = getShiftX(draft.data[index] as Spectrum1D); diff --git a/src/component/utility/getClosePeak.ts b/src/component/utility/getClosePeak.ts new file mode 100644 index 000000000..d4635b4e3 --- /dev/null +++ b/src/component/utility/getClosePeak.ts @@ -0,0 +1,32 @@ +import { Spectrum1D } from 'nmr-load-save'; + +import { get1DDataXY } from '../../data/data1d/Spectrum1D'; + +import { maxAbsoluteValue } from './maxAbsoluteValue'; + +interface PeakPosition { + x: number; + y: number; +} + +export function getClosePeak( + spectrum: Spectrum1D, + lookRange: { + from: number; + to: number; + }, +): PeakPosition | null { + const { from, to } = lookRange; + const datum = get1DDataXY(spectrum); + const maxIndex = datum.x.findIndex((number) => number >= to) - 1; + const minIndex = datum.x.findIndex((number) => number >= from); + + const yDataRange = datum.y.slice(minIndex, maxIndex); + if (!yDataRange || yDataRange.length === 0) return null; + + const y = maxAbsoluteValue(yDataRange); + const xIndex = minIndex + yDataRange.indexOf(y); + const x = datum.x[xIndex]; + + return { x, y }; +} diff --git a/src/component/utility/maxAbsoluteValue.ts b/src/component/utility/maxAbsoluteValue.ts new file mode 100644 index 000000000..68f9a1e48 --- /dev/null +++ b/src/component/utility/maxAbsoluteValue.ts @@ -0,0 +1,31 @@ +import { NumberArray } from 'cheminfo-types'; +import { + XGetFromToIndexOptions, + xCheck, + xGetFromToIndex, +} from 'ml-spectra-processing'; + +//This function is an adapted version of the xMaxAbsoluteValue function from ml-spectra-processing. It identifies the maximum absolute value and returns the corresponding maximum value along with its sign +export function maxAbsoluteValue( + array: NumberArray, + options: XGetFromToIndexOptions = {}, +): number { + xCheck(array); + + const { fromIndex, toIndex } = xGetFromToIndex(array, options); + let maxValue = array[fromIndex]; + let sign = 1; + + for (let i = fromIndex + 1; i <= toIndex; i++) { + if (array[i] >= 0) { + if (array[i] > maxValue) { + maxValue = array[i]; + sign = 1; + } + } else if (-array[i] > maxValue) { + maxValue = -array[i]; + sign = Math.sign(array[i]); + } + } + return maxValue * sign; +} diff --git a/src/data/data1d/Spectrum1D/index.ts b/src/data/data1d/Spectrum1D/index.ts index 8117e1423..e06e39873 100644 --- a/src/data/data1d/Spectrum1D/index.ts +++ b/src/data/data1d/Spectrum1D/index.ts @@ -5,7 +5,6 @@ export { initiateDatum1D } from './initiateDatum1D'; export { changeIntegralsRelative } from './integrals/changeIntegralsRelative'; export { isSpectrum1D } from './isSpectrum1D'; export { autoPeakPicking } from './peaks/autoPeakPicking'; -export { lookupPeak } from './peaks/lookupPeak'; export { optimizePeaks } from './peaks/optimizePeaks'; export { addRange } from './ranges/addRange'; export { changeRange } from './ranges/changeRange'; diff --git a/src/data/data1d/Spectrum1D/peaks/lookupPeak.ts b/src/data/data1d/Spectrum1D/peaks/lookupPeak.ts deleted file mode 100644 index dd7ccd2b3..000000000 --- a/src/data/data1d/Spectrum1D/peaks/lookupPeak.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { NmrData1D } from 'cheminfo-types'; -import max from 'ml-array-max'; - -// Lookup for apeak while the mouse move -/** - * - * @param {object<{x:Array,re:Array}>} data - * @param {object<{from:number,to:number}>} options - */ - -interface LookupPeakOptions { - from: number; - to: number; -} -interface LookupPeakResult { - x: number; - y: number; - xIndex: number; -} - -export function lookupPeak( - data: NmrData1D, - options: LookupPeakOptions, -): LookupPeakResult | null { - const { from, to } = options; - let minIndex = data.x.findIndex((number) => number >= from); - let maxIndex = data.x.findIndex((number) => number >= to) - 1; - - if (minIndex > maxIndex) { - minIndex = maxIndex; - maxIndex = minIndex; - } - const dataRange = data.re.slice(minIndex, maxIndex); - if (dataRange && dataRange.length > 0) { - const yValue = max(dataRange); - const xIndex = dataRange.indexOf(yValue); - const xValue = data.x[minIndex + xIndex]; - - return { x: xValue, y: yValue, xIndex: minIndex + xIndex }; - } - return null; -}