nmr-processing 7.4.2 → 8.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/lib/assignment/getAssignments.d.ts +6 -8
  2. package/lib/assignment/getAssignments.js +1 -1
  3. package/lib/assignment/getAssignments.js.map +1 -1
  4. package/lib/assignment/utils/getAssignment/buildAssignments.js +5 -0
  5. package/lib/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
  6. package/lib/assignment/utils/getAssignment/checkIDs.js +5 -9
  7. package/lib/assignment/utils/getAssignment/checkIDs.js.map +1 -1
  8. package/lib/assignment/utils/getAssignment/formatData.js +30 -3
  9. package/lib/assignment/utils/getAssignment/formatData.js.map +1 -1
  10. package/lib/assignment/utils/getAssignment/partialScore.js +1 -1
  11. package/lib/assignment/utils/getAssignment/partialScore.js.map +1 -1
  12. package/lib/databases/getDatabase.d.ts +6 -1
  13. package/lib/databases/getDatabase.js +13 -3
  14. package/lib/databases/getDatabase.js.map +1 -1
  15. package/lib/index.d.ts +2 -2
  16. package/lib/index.js +1 -1
  17. package/lib/index.js.map +1 -1
  18. package/lib/peaks/NMRPeak1D.d.ts +0 -1
  19. package/lib/peaks/solventSuppression.d.ts +3 -0
  20. package/lib/peaks/solventSuppression.js +147 -0
  21. package/lib/peaks/solventSuppression.js.map +1 -0
  22. package/lib/peaks/util/jAnalyzer.d.ts +1 -1
  23. package/lib/prediction/predictAllSpectra.d.ts +22 -4
  24. package/lib/prediction/predictAllSpectra.js +102 -16
  25. package/lib/prediction/predictAllSpectra.js.map +1 -1
  26. package/lib/ranges/rangesToXY.js +27 -3
  27. package/lib/ranges/rangesToXY.js.map +1 -1
  28. package/lib/signals/addDummySignals.d.ts +2 -0
  29. package/lib/signals/addDummySignals.js +56 -0
  30. package/lib/signals/addDummySignals.js.map +1 -0
  31. package/lib/signals/hackSignalsToXY.js +2 -48
  32. package/lib/signals/hackSignalsToXY.js.map +1 -1
  33. package/lib/signals/simulation/simulate1D.d.ts +1 -39
  34. package/lib/signals/simulation/simulate1D.js +13 -240
  35. package/lib/signals/simulation/simulate1D.js.map +1 -1
  36. package/lib/signals/simulation/simulateXYPeaks.d.ts +47 -0
  37. package/lib/signals/simulation/simulateXYPeaks.js +246 -0
  38. package/lib/signals/simulation/simulateXYPeaks.js.map +1 -0
  39. package/lib/utilities/getFrequency.d.ts +1 -1
  40. package/lib/utilities/getFrequency.js +4 -4
  41. package/lib/utilities/getFrequency.js.map +1 -1
  42. package/lib/xyz/NMRSignal2D.d.ts +3 -3
  43. package/lib-esm/assignment/getAssignments.js +1 -1
  44. package/lib-esm/assignment/getAssignments.js.map +1 -1
  45. package/lib-esm/assignment/utils/getAssignment/buildAssignments.js +5 -0
  46. package/lib-esm/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
  47. package/lib-esm/assignment/utils/getAssignment/checkIDs.js +5 -9
  48. package/lib-esm/assignment/utils/getAssignment/checkIDs.js.map +1 -1
  49. package/lib-esm/assignment/utils/getAssignment/formatData.js +30 -3
  50. package/lib-esm/assignment/utils/getAssignment/formatData.js.map +1 -1
  51. package/lib-esm/assignment/utils/getAssignment/partialScore.js +1 -1
  52. package/lib-esm/assignment/utils/getAssignment/partialScore.js.map +1 -1
  53. package/lib-esm/databases/getDatabase.js +13 -3
  54. package/lib-esm/databases/getDatabase.js.map +1 -1
  55. package/lib-esm/index.js +1 -1
  56. package/lib-esm/index.js.map +1 -1
  57. package/lib-esm/peaks/solventSuppression.js +140 -0
  58. package/lib-esm/peaks/solventSuppression.js.map +1 -0
  59. package/lib-esm/prediction/predictAllSpectra.js +102 -16
  60. package/lib-esm/prediction/predictAllSpectra.js.map +1 -1
  61. package/lib-esm/ranges/rangesToXY.js +27 -3
  62. package/lib-esm/ranges/rangesToXY.js.map +1 -1
  63. package/lib-esm/signals/addDummySignals.js +52 -0
  64. package/lib-esm/signals/addDummySignals.js.map +1 -0
  65. package/lib-esm/signals/hackSignalsToXY.js +2 -48
  66. package/lib-esm/signals/hackSignalsToXY.js.map +1 -1
  67. package/lib-esm/signals/simulation/simulate1D.js +14 -238
  68. package/lib-esm/signals/simulation/simulate1D.js.map +1 -1
  69. package/lib-esm/signals/simulation/simulateXYPeaks.js +239 -0
  70. package/lib-esm/signals/simulation/simulateXYPeaks.js.map +1 -0
  71. package/lib-esm/utilities/getFrequency.js +1 -1
  72. package/lib-esm/utilities/getFrequency.js.map +1 -1
  73. package/package.json +14 -12
  74. package/src/assignment/getAssignments.ts +8 -9
  75. package/src/assignment/utils/getAssignment/buildAssignments.ts +6 -0
  76. package/src/assignment/utils/getAssignment/checkIDs.ts +8 -8
  77. package/src/assignment/utils/getAssignment/formatData.ts +39 -3
  78. package/src/assignment/utils/getAssignment/partialScore.ts +2 -2
  79. package/src/databases/getDatabase.ts +19 -2
  80. package/src/index.ts +2 -2
  81. package/src/peaks/NMRPeak1D.ts +0 -1
  82. package/src/peaks/solventSuppression.ts +186 -0
  83. package/src/peaks/util/jAnalyzer.ts +1 -1
  84. package/src/prediction/predictAllSpectra.ts +151 -14
  85. package/src/ranges/rangesToXY.ts +33 -4
  86. package/src/signals/addDummySignals.ts +77 -0
  87. package/src/signals/hackSignalsToXY.ts +2 -72
  88. package/src/signals/simulation/simulate1D.ts +14 -319
  89. package/src/signals/simulation/simulateXYPeaks.ts +332 -0
  90. package/src/utilities/getFrequency.ts +1 -1
  91. package/src/xyz/NMRSignal2D.ts +3 -3
  92. package/lib/constants/gyromagneticRatio.d.ts +0 -6
  93. package/lib/constants/gyromagneticRatio.js +0 -26
  94. package/lib/constants/gyromagneticRatio.js.map +0 -1
  95. package/lib-esm/constants/gyromagneticRatio.js +0 -23
  96. package/lib-esm/constants/gyromagneticRatio.js.map +0 -1
  97. package/src/constants/gyromagneticRatio.ts +0 -49
@@ -0,0 +1,186 @@
1
+ import { PointXY } from 'cheminfo-types';
2
+ import linearSumAssignment from 'linear-sum-assignment';
3
+ import { Matrix } from 'ml-matrix';
4
+ import { gaussianFct } from 'ml-peak-shape-generator';
5
+ import { xFindClosestIndex, xMaxValue } from 'ml-spectra-processing';
6
+
7
+ import { NMRSignal1D } from '../signals/NMRSignal1D';
8
+ import { addDummySignals } from '../signals/addDummySignals';
9
+ import { signalsToSpinSystem } from '../signals/simulation/signalsToSpinSystem';
10
+ import { simulateXYPeaks } from '../signals/simulation/simulateXYPeaks';
11
+ import { splitSpinSystem } from '../signals/simulation/splitSpinSystem';
12
+
13
+ import { NMRPeak1D } from './NMRPeak1D';
14
+
15
+ export function solventSuppression(
16
+ peakList: NMRPeak1D[],
17
+ solvent: NMRSignal1D[],
18
+ options: any = {},
19
+ ) {
20
+ const peaks = [...peakList];
21
+ sortAscending(peaks);
22
+ const xValues = peaks.map((peak) => peak.x);
23
+
24
+ const { markSolventPeaks = false } = options;
25
+
26
+ let solventSignals = solvent; //addDummySignals(solvent);
27
+ // solventSignals = solventSignals.slice(0, solventSignals.findIndex((signal) => signal.delta > 400));
28
+
29
+ for (const solventSignal of solventSignals) {
30
+ const solventXYPeaks = solventSignal.peaks
31
+ ? solventSignal.peaks
32
+ : getSolventPeaks(solventSignal);
33
+ sortAscending(solventXYPeaks);
34
+ let upIndex = xFindClosestIndex(
35
+ xValues,
36
+ solventXYPeaks[solventXYPeaks.length - 1].x + 1,
37
+ );
38
+ let lowIndex = xFindClosestIndex(xValues, solventXYPeaks[0].x - 1);
39
+ if (upIndex === lowIndex) continue;
40
+ const nearPeaks = peaks.slice(lowIndex, upIndex + 1);
41
+ const amplitudeResiduals = [];
42
+ const deltaResiduals = [];
43
+ const positionResiduals = [];
44
+ for (let peak of nearPeaks) {
45
+ const shiftedSolventPeaks = getShiftedSolventPeaks(
46
+ peak,
47
+ solventSignal,
48
+ solventXYPeaks,
49
+ );
50
+ const closestPeaks = getClosestPeaks(shiftedSolventPeaks, nearPeaks);
51
+ let deltaResidual = 0;
52
+ let amplitudeResidual = 0;
53
+ let positionResidual = 0;
54
+ for (let i = 0; i < closestPeaks.length; i++) {
55
+ amplitudeResidual += Math.abs(
56
+ shiftedSolventPeaks[i].y - closestPeaks[i].y,
57
+ );
58
+ deltaResidual += Math.abs(shiftedSolventPeaks[i].x - closestPeaks[i].x);
59
+ }
60
+
61
+ if (closestPeaks.length === 0) {
62
+ deltaResidual = Number.MAX_SAFE_INTEGER;
63
+ amplitudeResidual = Number.MAX_SAFE_INTEGER;
64
+ positionResidual = Number.MAX_SAFE_INTEGER;
65
+ } else {
66
+ positionResidual = gaussianFct(solventSignal.delta - peak.x, 0.5);
67
+ }
68
+ amplitudeResiduals.push(amplitudeResidual);
69
+ deltaResiduals.push(deltaResidual);
70
+ positionResiduals.push(positionResidual);
71
+ }
72
+
73
+ const maxAmplitude = xMaxValue(amplitudeResiduals);
74
+ const maxDelta = xMaxValue(deltaResiduals);
75
+ const maxPosition = xMaxValue(positionResiduals);
76
+
77
+ let minIndex = -1;
78
+ let minScore = Number.MAX_SAFE_INTEGER;
79
+ for (let i = 0; i < deltaResiduals.length; i++) {
80
+ const value =
81
+ (amplitudeResiduals[i] / maxAmplitude +
82
+ deltaResiduals[i] / maxDelta +
83
+ (1 - positionResiduals[i]) / maxPosition) /
84
+ 3;
85
+ if (minScore > value) {
86
+ minIndex = i;
87
+ minScore = value;
88
+ }
89
+ }
90
+ const shiftedSolventPeaks = getShiftedSolventPeaks(
91
+ nearPeaks[minIndex],
92
+ solventSignal,
93
+ solventXYPeaks,
94
+ );
95
+ const diff = getDiffMatrix(shiftedSolventPeaks, nearPeaks);
96
+ const { rowAssignments, gain } = linearSumAssignment(diff, {
97
+ maximaze: false,
98
+ });
99
+ if (gain === -1) return peaks;
100
+
101
+ if (markSolventPeaks) {
102
+ for (let index of rowAssignments) {
103
+ peaks[index + lowIndex].kind = 'solvent';
104
+ }
105
+ } else {
106
+ rowAssignments.sort((a, b) => b - a);
107
+ for (let index of rowAssignments) {
108
+ peaks.splice(index + lowIndex, 1);
109
+ }
110
+ }
111
+ }
112
+
113
+ return peaks;
114
+ }
115
+
116
+ function getSolventPeaks(
117
+ signal: NMRSignal1D,
118
+ options: { frequency?: number; maxClusterSize?: number } = {},
119
+ ) {
120
+ let signals = addDummySignals([signal]);
121
+ let spinSystem = signalsToSpinSystem(signals);
122
+
123
+ const { frequency = 400, maxClusterSize = 8 } = options;
124
+
125
+ spinSystem.clusters = splitSpinSystem(spinSystem, {
126
+ frequency,
127
+ maxClusterSize,
128
+ });
129
+
130
+ const peaks = simulateXYPeaks(spinSystem);
131
+ return peaks.filter((peak) => peak.x < 1000);
132
+ }
133
+
134
+ function getShiftedSolventPeaks(
135
+ peak: PointXY,
136
+ solventSignal: any,
137
+ solventXYPeaks: PointXY[],
138
+ ) {
139
+ const shiftedSolventPeaks: PointXY[] = JSON.parse(
140
+ JSON.stringify(solventXYPeaks),
141
+ );
142
+ // shift x values of solventPeaks to center it to the current peak.
143
+ const deltaPPM = peak.x - solventSignal.delta;
144
+ const maxIntensity = shiftedSolventPeaks.reduce(
145
+ (max, current) => (current.y > max ? current.y : max),
146
+ shiftedSolventPeaks[0].y,
147
+ );
148
+ for (let shiftedSolventPeak of shiftedSolventPeaks) {
149
+ shiftedSolventPeak.x += deltaPPM;
150
+ shiftedSolventPeak.y /= maxIntensity;
151
+ }
152
+ return shiftedSolventPeaks;
153
+ }
154
+
155
+ function getClosestPeaks(shiftedSolventPeaks: PointXY[], nearPeaks: PointXY[]) {
156
+ const diff = getDiffMatrix(shiftedSolventPeaks, nearPeaks);
157
+ const { rowAssignments, gain } = linearSumAssignment(diff, {
158
+ maximaze: false,
159
+ });
160
+ if (gain === -1) return [];
161
+
162
+ const assignmentPeaks = [];
163
+ let maxValue = Number.MIN_SAFE_INTEGER;
164
+ for (let index of rowAssignments) {
165
+ if (maxValue < nearPeaks[index].y) maxValue = nearPeaks[index].y;
166
+ assignmentPeaks.push({ ...nearPeaks[index] });
167
+ }
168
+ assignmentPeaks.forEach((peak, i, arr) => (arr[i].y /= maxValue));
169
+ return assignmentPeaks;
170
+ }
171
+
172
+ function sortAscending(peaks: PointXY[]) {
173
+ return peaks[0].x > peaks[1].x ? peaks.sort((a, b) => a.x - b.x) : peaks;
174
+ }
175
+
176
+ function getDiffMatrix(rows: PointXY[], columns: PointXY[]) {
177
+ const nbColumns = columns.length;
178
+ const nbRows = rows.length;
179
+ const diff = new Matrix(nbRows, nbColumns);
180
+ for (let r = 0; r < nbRows; r++) {
181
+ for (let c = 0; c < nbColumns; c++) {
182
+ diff.set(r, c, Math.abs(rows[r].x - columns[c].x));
183
+ }
184
+ }
185
+ return diff;
186
+ }
@@ -29,7 +29,7 @@ interface IntergralData {
29
29
  to: number;
30
30
  }
31
31
 
32
- export type Peak1DIntern = Omit<NMRPeak1D, 'y' | 'shape' | 'fwhm'> & {
32
+ export type Peak1DIntern = Omit<NMRPeak1D, 'y' | 'shape'> & {
33
33
  intensity: number;
34
34
  };
35
35
 
@@ -1,6 +1,9 @@
1
+ import { FromTo, PointXY } from 'cheminfo-types';
1
2
  import type { Molecule } from 'openchemlib';
2
3
 
4
+ import { signals2DToZ } from '../signals/signals2DToZ';
3
5
  import { OptionsSignalsToXY, signalsToXY } from '../signals/signalsToXY';
6
+ import { getFrequency } from '../utilities/getFrequency';
4
7
 
5
8
  import { PredictAllOptions, predictAll } from './predictAll';
6
9
  /**
@@ -10,39 +13,173 @@ import { PredictAllOptions, predictAll } from './predictAll';
10
13
  * @param options
11
14
  */
12
15
 
16
+ interface OneDOptions {
17
+ proton: FromTo;
18
+ carbon: FromTo;
19
+ nbPoints: number;
20
+ lineWidth: number;
21
+ }
22
+
23
+ interface TwoDOptions {
24
+ from: PointXY;
25
+ to: PointXY;
26
+ nbPoints: PointXY;
27
+ }
28
+ interface SimulationOptions {
29
+ frequency: number;
30
+ oneD?: Partial<OneDOptions>;
31
+ twoD?: Partial<TwoDOptions>;
32
+ }
33
+ interface PredictAllSpectraOptions {
34
+ prediction?: PredictAllOptions;
35
+ simulation?: SimulationOptions;
36
+ }
37
+
13
38
  export async function predictAllSpectra(
14
39
  molecule: Molecule,
15
- options: {
16
- prediction?: PredictAllOptions;
17
- simulation?: OptionsSignalsToXY;
18
- } = {},
40
+ options: PredictAllSpectraOptions = {},
19
41
  ) {
20
- const predictions = await predictAll(molecule, options.prediction);
42
+ const {
43
+ simulation: simulationOptions = { oneD: {}, twoD: {} } as SimulationOptions,
44
+ prediction: predictionOptions = {},
45
+ } = options;
46
+
47
+ const predictions = await predictAll(molecule, predictionOptions);
21
48
  const spectra: any[] = [];
22
49
  const result = { spectra, molecules: [{ molfile: predictions.molfile }] };
23
- spectra.push(getProton(predictions.proton, options.simulation));
50
+
51
+ const oneDOptions: OneDOptions = {
52
+ ...{
53
+ proton: { from: 0, to: 14 },
54
+ carbon: { from: 0, to: 200 },
55
+ nbPoints: 4098,
56
+ lineWidth: 0.03,
57
+ },
58
+ ...(simulationOptions.oneD || {}),
59
+ };
60
+
61
+ const twoDOptions: TwoDOptions = {
62
+ ...{
63
+ from: { x: oneDOptions.proton.from, y: oneDOptions.carbon.from },
64
+ to: { x: oneDOptions.proton.to, y: oneDOptions.carbon.to },
65
+ nbPoints: { x: 1024, y: 1024 },
66
+ },
67
+ ...(simulationOptions.twoD || {}),
68
+ };
69
+
70
+ for (const experiment in predictions) {
71
+ switch (experiment) {
72
+ case 'carbon':
73
+ case 'proton': {
74
+ spectra.push(
75
+ get1DSpectrum(predictions[experiment], {
76
+ nbPoints: oneDOptions.nbPoints,
77
+ lineWidth: oneDOptions.lineWidth,
78
+ ...oneDOptions[experiment],
79
+ experiment,
80
+ frequency: simulationOptions.frequency,
81
+ }),
82
+ );
83
+ break;
84
+ }
85
+ case 'cosy':
86
+ case 'hsqc':
87
+ case 'hmbc': {
88
+ spectra.push(
89
+ get2DSpectrum(predictions[experiment], {
90
+ ...twoDOptions,
91
+ experiment,
92
+ frequency: simulationOptions.frequency,
93
+ }),
94
+ );
95
+ break;
96
+ }
97
+ default:
98
+ break;
99
+ }
100
+ }
24
101
 
25
102
  return result;
26
103
  }
27
104
 
28
- function getProton(prediction: any, options?: OptionsSignalsToXY) {
29
- const { x, y } = signalsToXY(prediction.signals, options);
105
+ type Get2DSpectrumOptions = TwoDOptions & {
106
+ experiment: string;
107
+ frequency: number;
108
+ };
109
+
110
+ function get2DSpectrum(prediction: any, options: Get2DSpectrumOptions) {
111
+ const { signals, zones, nuclei } = prediction;
112
+ const { frequency: baseFrequency, experiment } = options;
113
+
114
+ const width = get2DWidth(nuclei);
115
+ const frequency = calculateFrequency(nuclei, baseFrequency);
116
+
117
+ const spectrumData = signals2DToZ(signals, {
118
+ ...options,
119
+ width,
120
+ factor: 3,
121
+ });
122
+
123
+ const spectrum = {
124
+ data: { ...spectrumData, noise: 0.01 },
125
+ info: {
126
+ nucleus: nuclei,
127
+ originFrequency: frequency,
128
+ baseFrequency: frequency,
129
+ pulseSequence: experiment,
130
+ experiment: '2d',
131
+ },
132
+ zones: { values: zones },
133
+ };
134
+ return spectrum;
135
+ }
136
+
137
+ function get2DWidth(nucleus: string[]) {
138
+ return nucleus[0] === nucleus[1] ? 0.02 : { x: 0.02, y: 0.2133 };
139
+ }
140
+
141
+ type Get1DSpectrumOptions = OptionsSignalsToXY & { experiment: string };
142
+
143
+ function get1DSpectrum(prediction: any, options: Get1DSpectrumOptions) {
144
+ const { frequency = 400, experiment } = options;
145
+ const { signals, nucleus } = prediction;
146
+ const realFrequency = calculateFrequency(nucleus, frequency) as number;
147
+ const { x, y } = signalsToXY(signals, {
148
+ ...options,
149
+ frequency: realFrequency,
150
+ });
30
151
  const spectrum = {
31
- id: '',
32
152
  // todo Array.from is temporary for the developement
33
153
  data: { x: Array.from(x), re: Array.from(y), im: null },
34
154
  info: {
35
155
  nucleus: prediction.nucleus,
36
156
  dimension: 1,
37
- originFrequency: 400,
38
- baseFrequency: 400,
157
+ originFrequency: realFrequency,
158
+ baseFrequency: realFrequency,
39
159
  pulseSequence: 'prediction',
40
- experiment: 'proton',
160
+ experiment,
41
161
  isFt: true,
42
162
  },
43
- // todo: currently we disable the ranges because there is no ID
44
- //ranges: { values: prediction.ranges },
163
+ ranges: { values: prediction.ranges },
45
164
  };
46
165
 
47
166
  return spectrum;
48
167
  }
168
+
169
+ function calculateFrequency(
170
+ nucleus: string | string[],
171
+ frequency: number,
172
+ ): number | string {
173
+ if (typeof nucleus === 'string') {
174
+ return getFrequency(nucleus, { nucleus: '1H', frequency });
175
+ } else {
176
+ if (nucleus[0] === nucleus[1]) {
177
+ return `${frequency},${frequency}`;
178
+ } else {
179
+ return `${frequency},${getFrequency(nucleus[1], {
180
+ nucleus: nucleus[0],
181
+ frequency,
182
+ })}`;
183
+ }
184
+ }
185
+ }
@@ -49,13 +49,21 @@ function checkForSignals(
49
49
  if (!range.signals) throw new Error('range has not signals');
50
50
  }
51
51
  }
52
+
53
+ const defaultFromTo = (nucleus = '') => {
54
+ switch (nucleus.toUpperCase()) {
55
+ case '13C':
56
+ return { from: -5, to: 206 };
57
+ default:
58
+ return { from: -0.5, to: 10.5 };
59
+ }
60
+ };
61
+
52
62
  export function rangesToXY(ranges: NMRRange[], options: any = {}) {
53
63
  checkForSignals(ranges);
54
64
  let {
55
65
  frequency = 400,
56
66
  lineWidth = 1,
57
- from = 0,
58
- to = 10,
59
67
  nbPoints = 16 * 1024,
60
68
  shape = { kind: 'gaussian' },
61
69
  } = options;
@@ -66,9 +74,10 @@ export function rangesToXY(ranges: NMRRange[], options: any = {}) {
66
74
  }
67
75
  };
68
76
 
77
+ const { from, to } = getFromTo(ranges, options);
69
78
  const spectrumOptions = {
70
- from,
71
79
  to,
80
+ from,
72
81
  nbPoints,
73
82
  shape,
74
83
  lineWidth,
@@ -139,7 +148,7 @@ function peaksOfMultiplet(delta: number, options: any) {
139
148
  const {
140
149
  frequency,
141
150
  lineWidth,
142
- intensities = [1, 2, 5, 4, 5, 3, 4, 2, 1],
151
+ intensities = [1, 2, 5, 4, 5, 7, 5, 4, 5, 2, 1],
143
152
  } = options;
144
153
 
145
154
  const lineWidthPpm = lineWidth / frequency;
@@ -182,3 +191,23 @@ function normalizeSpectrum(
182
191
  }
183
192
  }
184
193
  }
194
+
195
+ function getFromTo(ranges: NMRRange[], options: any) {
196
+ const { from: defaultFrom, to: defaultTo } = defaultFromTo(options.nucleus);
197
+
198
+ let rangesFrom = Number.MAX_SAFE_INTEGER;
199
+ let rangesTo = Number.MIN_SAFE_INTEGER;
200
+ for (const range of ranges) {
201
+ for (const signal of range.signals || []) {
202
+ if (rangesFrom > signal.delta) rangesFrom = signal.delta;
203
+ if (rangesTo < signal.delta) rangesTo = signal.delta;
204
+ }
205
+ }
206
+
207
+ const {
208
+ from = Math.min(rangesFrom - 0.5, defaultFrom),
209
+ to = Math.max(rangesTo + 0.5, defaultTo),
210
+ } = options;
211
+
212
+ return { from, to };
213
+ }
@@ -0,0 +1,77 @@
1
+ import { couplingPatterns } from '../constants/couplingPatterns';
2
+
3
+ import { NMRSignal1D } from './NMRSignal1D';
4
+ import type { Jcoupling } from './jcoupling';
5
+
6
+ export function addDummySignals(signals: NMRSignal1D[]) {
7
+ let newSignals: NMRSignal1D[] = JSON.parse(JSON.stringify(signals));
8
+
9
+ signals.forEach((signal, s) => {
10
+ const { js: jCouplings = [], atoms: signalAssignment = [s] } = signal;
11
+
12
+ let { newCouplings, tempSignals } = checkCouplings(
13
+ jCouplings,
14
+ newSignals,
15
+ signalAssignment,
16
+ );
17
+
18
+ if (tempSignals.length > 0) newSignals.push(...tempSignals);
19
+
20
+ newSignals[s].js = newCouplings;
21
+ newSignals[s].atoms = signalAssignment;
22
+ });
23
+
24
+ return newSignals.sort((a, b) => a.delta - b.delta);
25
+ }
26
+
27
+ function checkCouplings(
28
+ jCouplings: Jcoupling[],
29
+ signals: NMRSignal1D[],
30
+ signalAssignment: number[],
31
+ ) {
32
+ let newSignalAssignment = signals.length - 1;
33
+ let tempSignals: NMRSignal1D[] = [];
34
+ const newCouplings = jCouplings.reduce<Jcoupling[]>(
35
+ (newCouplings, jCoupling) => {
36
+ const { atoms = [], multiplicity, coupling } = jCoupling;
37
+ if (atoms.length === 0) {
38
+ if (coupling && multiplicity) {
39
+ let tempCouplings: Jcoupling[] = [];
40
+ const nbLinks = couplingPatterns.indexOf(multiplicity);
41
+ for (let i = 0; i < nbLinks; i++) {
42
+ newSignalAssignment++;
43
+ tempCouplings.push({
44
+ coupling,
45
+ atoms: [newSignalAssignment],
46
+ });
47
+ tempSignals.push(
48
+ formatSignal(coupling, [newSignalAssignment], signalAssignment),
49
+ );
50
+ }
51
+ } else {
52
+ newCouplings.push(jCoupling);
53
+ }
54
+ }
55
+ return newCouplings;
56
+ },
57
+ [],
58
+ );
59
+ return { newCouplings, tempSignals };
60
+ }
61
+
62
+ function formatSignal(
63
+ coupling: number,
64
+ newSignalAssignment: number[],
65
+ signalAssignment: number[],
66
+ ) {
67
+ return {
68
+ delta: 100000,
69
+ atoms: newSignalAssignment,
70
+ js: [
71
+ {
72
+ coupling,
73
+ atoms: signalAssignment,
74
+ },
75
+ ],
76
+ };
77
+ }
@@ -1,7 +1,5 @@
1
- import { couplingPatterns } from '../constants/couplingPatterns';
2
-
3
1
  import type { NMRSignal1D } from './NMRSignal1D';
4
- import type { Jcoupling } from './jcoupling';
2
+ import { addDummySignals } from './addDummySignals';
5
3
  import { OptionsSignalsToXY, signalsToXY } from './signalsToXY';
6
4
 
7
5
  /**
@@ -13,74 +11,6 @@ export function hackSignalsToXY(
13
11
  signals: NMRSignal1D[],
14
12
  options: OptionsSignalsToXY = {},
15
13
  ) {
16
- let newSignals = JSON.parse(JSON.stringify(signals));
17
-
18
- signals.forEach((signal, s) => {
19
- const { js: jCouplings = [], atoms: signalAssignment = [s] } = signal;
20
-
21
- let { newCouplings, tempSignals } = checkCouplings(
22
- jCouplings,
23
- newSignals,
24
- signalAssignment,
25
- );
26
-
27
- if (tempSignals.length > 0) newSignals.push(...tempSignals);
28
-
29
- newSignals[s].js = newCouplings;
30
- newSignals[s].atoms = signalAssignment;
31
- });
32
-
14
+ let newSignals = addDummySignals(signals);
33
15
  return signalsToXY(newSignals, options);
34
16
  }
35
-
36
- function checkCouplings(
37
- jCouplings: Jcoupling[],
38
- signals: NMRSignal1D[],
39
- signalAssignment: number[],
40
- ) {
41
- let newSignalAssignment = signals.length - 1;
42
- let tempSignals: NMRSignal1D[] = [];
43
- const newCouplings = jCouplings.reduce<Jcoupling[]>(
44
- (newCouplings, jCoupling) => {
45
- const { atoms = [], multiplicity, coupling } = jCoupling;
46
- if (atoms.length === 0) {
47
- if (coupling && multiplicity) {
48
- let tempCouplings: Jcoupling[] = [];
49
- const nbLinks = couplingPatterns.indexOf(multiplicity);
50
- for (let i = 0; i < nbLinks; i++) {
51
- newSignalAssignment++;
52
- tempCouplings.push({
53
- coupling,
54
- atoms: [newSignalAssignment],
55
- });
56
- tempSignals.push(
57
- formatSignal(coupling, [newSignalAssignment], signalAssignment),
58
- );
59
- }
60
- } else {
61
- newCouplings.push(jCoupling);
62
- }
63
- }
64
- return newCouplings;
65
- },
66
- [],
67
- );
68
- return { newCouplings, tempSignals };
69
- }
70
-
71
- function formatSignal(
72
- coupling: number,
73
- newSignalAssignment: number[],
74
- signalAssignment: number[],
75
- ) {
76
- return {
77
- delta: 100000,
78
- atoms: newSignalAssignment,
79
- js: [
80
- {
81
- coupling,
82
- atoms: signalAssignment,
83
- },
84
- ],
85
- };
86
- }