Skip to content

Commit

Permalink
feat: use nmr processing types (#1310)
Browse files Browse the repository at this point in the history
* chore: update nmr-processing to v4.0.0

* refactor: Peaks type

* refactor: ranges type

* chore: update nmr-processing

* refactor: zones type

* feat: adaptation detectionZones close #1294

* feat: migrate .nmrium from version 0 > 1 > 2

* chore: update nmr-processing

* test: add test case for migration from version 1 to latest

* chore: fix code format

Co-authored-by: jobo322 <jose.bolanos@correounivalle.edu.co>
  • Loading branch information
hamed-musallam and jobo322 committed Dec 7, 2021
1 parent ee989fc commit fc576ef
Show file tree
Hide file tree
Showing 40 changed files with 1,254,464 additions and 272 deletions.
283 changes: 240 additions & 43 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -67,7 +67,7 @@
"multiplet-analysis": "^2.0.0",
"nmr-correlation": "^2.1.3",
"nmr-parser": "^1.7.1",
"nmr-processing": "3.3.0",
"nmr-processing": "^6.0.0",
"nmredata": "^0.6.1",
"numeral": "^2.0.6",
"openchemlib": "^7.4.3",
Expand Down
10 changes: 5 additions & 5 deletions src/component/1d/PeakAnnotations.tsx
Expand Up @@ -44,14 +44,14 @@ function PeakAnnotations() {
key={d.id}
transform={`translate(0,-${getVerticalAlign(d.id)})`}
>
{d.peaks.values.map(({ delta, intensity, id }) => (
{d.peaks.values.map(({ x, y, id }) => (
<PeakAnnotation
key={id}
x={scaleX()(delta)}
y={scaleY(d.id)(intensity) - 5}
sign={Math.sign(intensity)}
x={scaleX()(x)}
y={scaleY(d.id)(y) - 5}
sign={Math.sign(y)}
id={id}
value={delta}
value={x}
color="#730000"
nucleus={d.info.nucleus}
isActive={
Expand Down
4 changes: 2 additions & 2 deletions src/component/1d/multiplicityTree/LevelNode.tsx
@@ -1,10 +1,10 @@
import { Signal1D } from '../../../data/types/data1d';
import { useScaleChecked } from '../../context/ScaleContext';

import { SignalNodeProps } from './MultiplicityTree';
import { TREE_LEVEL_COLORS } from './TreeColors';

interface LevelNodeProps {
signal: SignalNodeProps;
signal: Signal1D;
startY: number;
levelHeight: number;
}
Expand Down
33 changes: 19 additions & 14 deletions src/component/1d/multiplicityTree/MultiplicityTree.tsx
Expand Up @@ -2,6 +2,7 @@
import lodashGet from 'lodash/get';
import { useMemo, useState, useEffect, CSSProperties } from 'react';

import { Signal1D } from '../../../data/types/data1d';
import { useAssignment } from '../../assignment';
import { useChartData } from '../../context/ChartContext';
import { useScaleChecked } from '../../context/ScaleContext';
Expand All @@ -23,16 +24,16 @@ const styles = {
strokeWidth: 1,
};

export interface SignalNodeProps {
id: string;
delta: number;
multiplicity: string;
}
// export interface SignalNodeProps {
// id: string;
// delta: number;
// multiplicity: string;
// }

interface MultiplicityTreeProps {
rangeFrom: number;
rangeTo: number;
signal: SignalNodeProps;
signal: Signal1D;
labelOptions?: {
distance: number;
fontSize: CSSProperties['fontSize'];
Expand Down Expand Up @@ -90,9 +91,10 @@ function MultiplicityTree({
const _treeHeight = _drawInFullRange ? _treeWidth / 3 : _treeWidth / 2;
// +2 because of multiplicity text and start level node before the actual tree starts
// 2* for levels between nodes (edges)
const length = signal?.multiplicity?.length || 0;
const _treeLevelHeight = _drawInFullRange
? _treeHeight / (signal.multiplicity.length + 2)
: _treeHeight / (2 * signal.multiplicity.length + 2);
? _treeHeight / (length + 2)
: _treeHeight / (2 * length + 2);

setTreeProps({
width: _treeWidth,
Expand Down Expand Up @@ -140,13 +142,16 @@ function MultiplicityTree({
]);

const treeNodesData = useMemo(() => {
const buildTreeNodesData = createTreeNodes(signal, spectrumData);
const jIndices = signal.multiplicity
.split('')
.map((_mult, i) => (hasCouplingConstant(_mult) ? i : undefined))
.filter((_i) => _i !== undefined);
if (signal.multiplicity) {
const buildTreeNodesData = createTreeNodes(signal, spectrumData);
const jIndices = signal.multiplicity
.split('')
.map((_mult, i) => (hasCouplingConstant(_mult) ? i : undefined))
.filter((_i) => _i !== undefined);

return buildTreeNodesData(0, jIndices, [], signal.delta);
return buildTreeNodesData(0, jIndices, [], signal.delta);
}
return [];
}, [signal, spectrumData]);

useEffect(() => {
Expand Down
6 changes: 3 additions & 3 deletions src/component/1d/multiplicityTree/StringNode.tsx
@@ -1,11 +1,11 @@
/* eslint-disable import/order */
import { CSSProperties } from 'react';

import { useScaleChecked } from '../../context/ScaleContext';

import { SignalNodeProps } from './MultiplicityTree';
import { Signal1D } from '../../../data/types/data1d/Signal1D';

interface StringNodeProps {
signal: SignalNodeProps;
signal: Signal1D;
startY: number;
levelHeight: number;
fontSize: CSSProperties['fontSize'];
Expand Down
12 changes: 9 additions & 3 deletions src/component/1d/multiplicityTree/buildTreeNode.ts
@@ -1,19 +1,24 @@
import lodashGet from 'lodash/get';

import { Signal1D } from '../../../data/types/data1d';
import {
getMultiplicityNumber,
getPascal,
} from '../../panels/extra/utilities/MultiplicityUtilities';

import { TREE_LEVEL_COLORS } from './TreeColors';

function createTreeNodes(signal, spectrumData) {
function createTreeNodes(signal: Signal1D, spectrumData) {
function buildTreeNodesData(
multiplicityIndex,
jIndices,
treeNodesData,
startX,
) {
if (!signal.multiplicity) {
return null;
}

if (multiplicityIndex >= signal.multiplicity.length) {
return treeNodesData;
}
Expand All @@ -27,7 +32,7 @@ function createTreeNodes(signal, spectrumData) {
const frequency = lodashGet(spectrumData, 'info.originFrequency', 0);

const coupling =
jIndex >= 0 && frequency > 0 && signal.js[jIndex]
jIndex >= 0 && frequency > 0 && signal?.js?.[jIndex]
? signal.js[jIndex].coupling / frequency // convert to ppm
: null;

Expand All @@ -49,8 +54,9 @@ function createTreeNodes(signal, spectrumData) {
);
} else {
// in case of other multiplets
const multiplicity = signal.multiplicity || '';
const pascal = getPascal(
getMultiplicityNumber(signal.multiplicity.charAt(multiplicityIndex)),
getMultiplicityNumber(multiplicity.charAt(multiplicityIndex)),
0.5,
); // @TODO for now we use the default spin of 1 / 2 only

Expand Down
7 changes: 3 additions & 4 deletions src/component/1d/ranges/Range.tsx
Expand Up @@ -2,6 +2,7 @@
import { css } from '@emotion/react';
import { useCallback, useState, useEffect } from 'react';

import { Signal1D } from '../../../data/types/data1d';
import { checkRangeKind } from '../../../data/utilities/RangeUtilities';
import {
filterForIDsWithAssignment,
Expand All @@ -14,9 +15,7 @@ import { HighlightedSource, useHighlight } from '../../highlight';
import { RESIZE_RANGE } from '../../reducer/types/Types';
import { options } from '../../toolbar/ToolTypes';
import Resizable from '../Resizable';
import MultiplicityTree, {
SignalNodeProps,
} from '../multiplicityTree/MultiplicityTree';
import MultiplicityTree from '../multiplicityTree/MultiplicityTree';
import TempMultiplicityTree from '../multiplicityTree/TempMultiplicityTree';

const stylesOnHover = css`
Expand Down Expand Up @@ -54,7 +53,7 @@ export interface RangeData {
from: number;
to: number;
integration: number;
signals: Array<SignalNodeProps>;
signals: Signal1D[];
}

interface RangeProps {
Expand Down
1 change: 1 addition & 0 deletions src/component/2d/zones/Signal.tsx
Expand Up @@ -81,6 +81,7 @@ const Signal = memo(({ signal, isVisible }: SignalProps) => {
)}
<g className="zone-signal-peak">
{isVisible.peaks &&
signal.peaks &&
signal.peaks.map((peak, i) => (
<circle
key={`${signal.id + i}`}
Expand Down
6 changes: 3 additions & 3 deletions src/component/panels/PeaksPanel/PeaksPanel.tsx
Expand Up @@ -79,19 +79,19 @@ function PeaksPanelInner({
}
if (peaks?.values) {
const _peaks = filterIsActive
? peaks.values.filter((peak) => isInRange(peak.delta))
? peaks.values.filter((peak) => isInRange(peak.x))
: peaks.values;

return _peaks
.map((peak) => {
const value = Number(format(peak.delta));
const value = Number(format(peak.x));
return {
value: value,
valueHz: info?.originFrequency
? Number(value) * info.originFrequency
: '',
id: peak.id,
intensity: peak.intensity,
intensity: peak.y,
peakWidth: peak.width ? peak.width : '',
isConstantlyHighlighted: isInRange(value),
};
Expand Down
6 changes: 3 additions & 3 deletions src/component/reducer/Reducer.ts
Expand Up @@ -4,7 +4,7 @@ import { buildCorrelationData, Types } from 'nmr-correlation';
import { predictSpectra } from '../../data/PredictionManager';
import * as SpectraManager from '../../data/SpectraManager';
import { SpectraAnalysis } from '../../data/data1d/MultipleAnalysis';
import migrateData from '../../data/migration';
import { migrate } from '../../data/migration/MigrationManager';
import { Molecule } from '../../data/molecules/Molecule';
import { Range } from '../../data/types/data1d';
import { Contours } from '../../data/types/data2d/Contours';
Expand Down Expand Up @@ -389,7 +389,7 @@ export function dispatchMiddleware(dispatch) {
switch (action.type) {
case types.INITIATE: {
if (action.payload) {
const { spectra, ...res } = migrateData(action.payload);
const { spectra, ...res } = migrate(action.payload);
void SpectraManager.fromJSON(spectra, usedColors).then((data) => {
action.payload = { spectra: data, ...res, usedColors };
dispatch(action);
Expand All @@ -400,7 +400,7 @@ export function dispatchMiddleware(dispatch) {
}
case types.LOAD_JSON_FILE: {
const parsedData = JSON.parse(action.files[0].binary.toString());
const data = migrateData(parsedData);
const data = migrate(parsedData);
void SpectraManager.fromJSON(data.spectra, usedColors).then(
(spectra) => {
action.payload = Object.assign(data, { spectra, usedColors });
Expand Down
27 changes: 17 additions & 10 deletions src/component/reducer/actions/PeaksActions.ts
Expand Up @@ -7,6 +7,8 @@ import {
autoPeakPicking,
} from '../../../data/data1d/Spectrum1D';
import { Datum1D } from '../../../data/types/data1d';
import { Data1D } from '../../../data/types/data1d/Data1D';
import { Peak } from '../../../data/types/data1d/Peak';
import generateID from '../../../data/utilities/generateID';
import { options } from '../../toolbar/ToolTypes';
import { State } from '../Reducer';
Expand All @@ -20,16 +22,20 @@ function addPeak(draft: Draft<State>, mouseCoordinates) {
const startX = mouseCoordinates.x - xShift;
const endX = mouseCoordinates.x + xShift;
const [from, to] = getRange(draft, { startX, endX });
const candidatePeak = lookupPeak(state.data[index].data, { from, to });
const candidatePeak = lookupPeak(state.data[index].data as Data1D, {
from,
to,
});

const shiftX = getShiftX(draft.data[index] as Datum1D);

if (candidatePeak) {
const peak = {
const peak: Peak = {
id: generateID(),
originDelta: candidatePeak.x - shiftX,
delta: candidatePeak.x,
intensity: candidatePeak.y,
originalX: candidatePeak.x - shiftX,
x: candidatePeak.x,
y: candidatePeak.y,
width: 0,
};
(draft.data[index] as Datum1D).peaks.values.push(peak);
}
Expand All @@ -51,12 +57,13 @@ function addPeaks(draft: Draft<State>, action) {

const shiftX = getShiftX(draft.data[index] as Datum1D);

if (peak && !datumOriginal.peaks.values.some((p) => p.delta === peak.x)) {
const newPeak = {
if (peak && !datumOriginal.peaks.values.some((p) => p.x === peak.x)) {
const newPeak: Peak = {
id: generateID(),
originDelta: peak.x - shiftX,
delta: peak.x,
intensity: peak.y,
originalX: peak.x - shiftX,
x: peak.x,
y: peak.y,
width: 0,
};
(draft.data[index] as Datum1D).peaks.values.push(newPeak);
}
Expand Down
27 changes: 14 additions & 13 deletions src/data/PredictionManager.ts
Expand Up @@ -115,19 +115,20 @@ function generated1DSpectrum(params: {
function mapZones(zones: Array<Partial<Zone>>) {
return zones.reduce<Array<Zone>>((zonesAcc, zone: any) => {
const { signals, ...resZone } = zone;
const newSignals = (signals as Array<Partial<Signal2D>>).reduce<
Array<Partial<Signal2D>>
>((signalsAcc, signal) => {
const { x, y, ...resSignal } = signal;
signalsAcc.push({
id: generateID(),
kind: 'signal',
x: { ...x, originDelta: x?.delta },
y: { ...y, originDelta: y?.delta },
...resSignal,
});
return signalsAcc;
}, []);
const newSignals = signals.reduce(
(signalsAcc: Signal2D[], signal: Signal2D) => {
const { x, y, id, ...resSignal } = signal;
signalsAcc.push({
id: id || generateID(),
kind: 'signal',
x: { ...x, originDelta: x.delta || 0 },
y: { ...y, originDelta: y.delta || 0 },
...resSignal,
});
return signalsAcc;
},
[],
);

zonesAcc.push({
id: generateID(),
Expand Down
2 changes: 1 addition & 1 deletion src/data/SpectraManager.ts
Expand Up @@ -6,7 +6,7 @@ import * as Data1DManager from './data1d/Data1DManager';
import * as Datum1D from './data1d/Spectrum1D';
import * as Data2DManager from './data2d/Data2DManager';
import * as Datum2D from './data2d/Spectrum2D';
import { CURRENT_EXPORT_VERSION } from './migration';
import { CURRENT_EXPORT_VERSION } from './migration/MigrationManager';
import * as Molecule from './molecules/Molecule';

export enum DataExportOptions {
Expand Down
2 changes: 1 addition & 1 deletion src/data/data1d/Spectrum1D/initiateDatum1D.ts
Expand Up @@ -55,7 +55,7 @@ export function initiateDatum1D(options: any, usedColors = {}): Datum1D {
);
datum.originalData = datum.data;

datum.peaks = Object.assign({ values: [], options: {} }, options.peaks);
datum.peaks = merge({ values: [], options: {} }, options.peaks);
// array of object {index: xIndex, xShift}
// in case the peak does not exactly correspond to the point value
// we can think about a second attributed `xShift`
Expand Down
14 changes: 7 additions & 7 deletions src/data/data1d/Spectrum1D/peaks/autoPeakPicking.ts
@@ -1,7 +1,7 @@
import median from 'ml-array-median';
import { xyAutoPeaksPicking } from 'nmr-processing';

import { Datum1D } from '../../../types/data1d';
import { Datum1D, Peak } from '../../../types/data1d';
import generateID from '../../../utilities/generateID';
import { getShiftX } from '../shift/getShiftX';

Expand Down Expand Up @@ -43,19 +43,19 @@ export function autoPeakPicking(datum1D: Datum1D, options) {

const error = (x[x.length - 1] - x[0]) / 10000;

return peaks.reduce((acc, newPeak) => {
return peaks.reduce<Peak[]>((acc, newPeak) => {
// check if the peak is already exists
for (const { delta } of currentPeaks) {
if (Math.abs(newPeak.x - delta) < error) {
for (const { x } of currentPeaks) {
if (Math.abs(newPeak.x - x) < error) {
return acc;
}
}

acc.push({
id: generateID(),
originDelta: newPeak.x - shiftX,
delta: newPeak.x,
intensity: newPeak.y,
originalX: newPeak.x - shiftX,
x: newPeak.x,
y: newPeak.y,
width: newPeak.width,
});

Expand Down

0 comments on commit fc576ef

Please sign in to comment.