nmr-processing 9.3.6 → 9.3.8-pre.1681135907

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 (104) hide show
  1. package/lib/apodization/apodization.d.ts +9 -4
  2. package/lib/apodization/apodization.js +3 -1
  3. package/lib/apodization/apodization.js.map +1 -1
  4. package/lib/apodization/applyWindow.js.map +1 -1
  5. package/lib/apodization/compose.js.map +1 -1
  6. package/lib/apodization/getFunction.js +3 -0
  7. package/lib/apodization/getFunction.js.map +1 -1
  8. package/lib/apodization/shapes/WindowFunctions.d.ts +6 -1
  9. package/lib/apodization/shapes/sineBell.d.ts +27 -0
  10. package/lib/apodization/shapes/sineBell.js +17 -0
  11. package/lib/apodization/shapes/sineBell.js.map +1 -0
  12. package/lib/apodization/utils/getData.js.map +1 -1
  13. package/lib/assignment/utils/buildAssignments.js +1 -2
  14. package/lib/assignment/utils/buildAssignments.js.map +1 -1
  15. package/lib/assignment/utils/exploreTreeRec.js +1 -2
  16. package/lib/assignment/utils/exploreTreeRec.js.map +1 -1
  17. package/lib/assignment/utils/getAssignment/buildAssignments.js +1 -2
  18. package/lib/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
  19. package/lib/assignment/utils/getAssignment/exploreTree.js +1 -2
  20. package/lib/assignment/utils/getAssignment/exploreTree.js.map +1 -1
  21. package/lib/signals/signalsToFID.d.ts +3 -0
  22. package/lib/utilities/checkData2DFid.d.ts +3 -0
  23. package/lib/utilities/checkData2DFid.js +10 -0
  24. package/lib/utilities/checkData2DFid.js.map +1 -0
  25. package/lib/xyz/Data2D.d.ts +21 -0
  26. package/lib/xyz/Data2D.js +3 -0
  27. package/lib/xyz/Data2D.js.map +1 -0
  28. package/lib/xyz/quadrature.d.ts +8 -0
  29. package/lib/xyz/quadrature.js +54 -0
  30. package/lib/xyz/quadrature.js.map +1 -0
  31. package/lib/xyz/util/fft2d/digitalFilter.d.ts +7 -0
  32. package/lib/xyz/util/fft2d/digitalFilter.js +20 -0
  33. package/lib/xyz/util/fft2d/digitalFilter.js.map +1 -0
  34. package/lib/xyz/util/fft2d/fftDirectDimension.d.ts +24 -0
  35. package/lib/xyz/util/fft2d/fftDirectDimension.js +57 -0
  36. package/lib/xyz/util/fft2d/fftDirectDimension.js.map +1 -0
  37. package/lib/xyz/util/fft2d/fftIndirectDimension.d.ts +23 -0
  38. package/lib/xyz/util/fft2d/fftIndirectDimension.js +72 -0
  39. package/lib/xyz/util/fft2d/fftIndirectDimension.js.map +1 -0
  40. package/lib/xyz/util/fft2d/removeDCOffset.d.ts +5 -0
  41. package/lib/xyz/util/fft2d/removeDCOffset.js +19 -0
  42. package/lib/xyz/util/fft2d/removeDCOffset.js.map +1 -0
  43. package/lib/xyz/util/fft2d/zeroFilling.d.ts +10 -0
  44. package/lib/xyz/util/fft2d/zeroFilling.js +29 -0
  45. package/lib/xyz/util/fft2d/zeroFilling.js.map +1 -0
  46. package/lib/xyz/xyzBidimensionalFFT.d.ts +41 -0
  47. package/lib/xyz/xyzBidimensionalFFT.js +79 -0
  48. package/lib/xyz/xyzBidimensionalFFT.js.map +1 -0
  49. package/lib-esm/apodization/apodization.js +3 -1
  50. package/lib-esm/apodization/apodization.js.map +1 -1
  51. package/lib-esm/apodization/applyWindow.js.map +1 -1
  52. package/lib-esm/apodization/compose.js.map +1 -1
  53. package/lib-esm/apodization/getFunction.js +3 -0
  54. package/lib-esm/apodization/getFunction.js.map +1 -1
  55. package/lib-esm/apodization/shapes/sineBell.js +13 -0
  56. package/lib-esm/apodization/shapes/sineBell.js.map +1 -0
  57. package/lib-esm/apodization/utils/getData.js.map +1 -1
  58. package/lib-esm/assignment/utils/buildAssignments.js +1 -2
  59. package/lib-esm/assignment/utils/buildAssignments.js.map +1 -1
  60. package/lib-esm/assignment/utils/exploreTreeRec.js +1 -2
  61. package/lib-esm/assignment/utils/exploreTreeRec.js.map +1 -1
  62. package/lib-esm/assignment/utils/getAssignment/buildAssignments.js +1 -2
  63. package/lib-esm/assignment/utils/getAssignment/buildAssignments.js.map +1 -1
  64. package/lib-esm/assignment/utils/getAssignment/exploreTree.js +1 -2
  65. package/lib-esm/assignment/utils/getAssignment/exploreTree.js.map +1 -1
  66. package/lib-esm/utilities/checkData2DFid.js +6 -0
  67. package/lib-esm/utilities/checkData2DFid.js.map +1 -0
  68. package/lib-esm/xyz/Data2D.js +2 -0
  69. package/lib-esm/xyz/Data2D.js.map +1 -0
  70. package/lib-esm/xyz/quadrature.js +50 -0
  71. package/lib-esm/xyz/quadrature.js.map +1 -0
  72. package/lib-esm/xyz/util/fft2d/digitalFilter.js +16 -0
  73. package/lib-esm/xyz/util/fft2d/digitalFilter.js.map +1 -0
  74. package/lib-esm/xyz/util/fft2d/fftDirectDimension.js +50 -0
  75. package/lib-esm/xyz/util/fft2d/fftDirectDimension.js.map +1 -0
  76. package/lib-esm/xyz/util/fft2d/fftIndirectDimension.js +65 -0
  77. package/lib-esm/xyz/util/fft2d/fftIndirectDimension.js.map +1 -0
  78. package/lib-esm/xyz/util/fft2d/removeDCOffset.js +15 -0
  79. package/lib-esm/xyz/util/fft2d/removeDCOffset.js.map +1 -0
  80. package/lib-esm/xyz/util/fft2d/zeroFilling.js +25 -0
  81. package/lib-esm/xyz/util/fft2d/zeroFilling.js.map +1 -0
  82. package/lib-esm/xyz/xyzBidimensionalFFT.js +75 -0
  83. package/lib-esm/xyz/xyzBidimensionalFFT.js.map +1 -0
  84. package/package.json +17 -17
  85. package/src/apodization/apodization.ts +6 -4
  86. package/src/apodization/applyWindow.ts +0 -1
  87. package/src/apodization/compose.ts +1 -0
  88. package/src/apodization/getFunction.ts +3 -0
  89. package/src/apodization/shapes/WindowFunctions.ts +7 -1
  90. package/src/apodization/shapes/sineBell.ts +41 -0
  91. package/src/apodization/utils/getData.ts +0 -1
  92. package/src/assignment/utils/buildAssignments.ts +2 -3
  93. package/src/assignment/utils/exploreTreeRec.ts +1 -2
  94. package/src/assignment/utils/getAssignment/buildAssignments.ts +2 -2
  95. package/src/assignment/utils/getAssignment/exploreTree.ts +1 -2
  96. package/src/utilities/checkData2DFid.ts +10 -0
  97. package/src/xyz/Data2D.ts +25 -0
  98. package/src/xyz/quadrature.ts +69 -0
  99. package/src/xyz/util/fft2d/digitalFilter.ts +24 -0
  100. package/src/xyz/util/fft2d/fftDirectDimension.ts +111 -0
  101. package/src/xyz/util/fft2d/fftIndirectDimension.ts +123 -0
  102. package/src/xyz/util/fft2d/removeDCOffset.ts +19 -0
  103. package/src/xyz/util/fft2d/zeroFilling.ts +39 -0
  104. package/src/xyz/xyzBidimensionalFFT.ts +123 -0
@@ -0,0 +1,24 @@
1
+ import { DataReIm } from 'ml-spectra-processing';
2
+
3
+ export function digitalFilter(
4
+ data: DataReIm,
5
+ options: { digitalFilterValue: number },
6
+ ) {
7
+ let { digitalFilterValue = 0 } = options;
8
+ let re = new Float64Array(data.re);
9
+ let im = new Float64Array(data.im);
10
+
11
+ let pointsToShift = Math.floor(digitalFilterValue);
12
+
13
+ const skip = 0;
14
+ pointsToShift += 0;
15
+
16
+ const newRe = new Float64Array(re.length);
17
+ const newIm = new Float64Array(im.length);
18
+ newRe.set(re.slice(pointsToShift));
19
+ newRe.set(re.slice(skip, pointsToShift), re.length - pointsToShift);
20
+ newIm.set(im.slice(pointsToShift));
21
+ newIm.set(im.slice(skip, pointsToShift), im.length - pointsToShift);
22
+
23
+ return { re: newRe, im: newIm };
24
+ }
@@ -0,0 +1,111 @@
1
+ import { DoubleArray } from 'cheminfo-types';
2
+ import Matrix from 'ml-matrix';
3
+ import {
4
+ reimAutoPhaseCorrection,
5
+ reimFFT,
6
+ reimPhaseCorrection,
7
+ } from 'ml-spectra-processing';
8
+
9
+ import {
10
+ apodization,
11
+ ApodizationOptions,
12
+ } from '../../../apodization/apodization';
13
+
14
+ import { digitalFilter } from './digitalFilter';
15
+ import { removeDCOffset } from './removeDCOffset';
16
+ import { zeroFilling, ZeroFillingOptions } from './zeroFilling';
17
+
18
+ interface PhaseCorrection {
19
+ mode?: string;
20
+ ph0?: number;
21
+ ph1?: number;
22
+ }
23
+
24
+ export interface DirectDimensionOptions {
25
+ fnMode?: string;
26
+ digitalFilterValue?: number;
27
+ zeroFilling?: ZeroFillingOptions;
28
+ apodization?: ApodizationOptions;
29
+ phaseCorrection?: PhaseCorrection;
30
+ }
31
+
32
+ export function fftDirectDimension(
33
+ data: { re: DoubleArray[]; im: DoubleArray[] },
34
+ options: DirectDimensionOptions,
35
+ ) {
36
+ const { re: reData, im: imData } = data;
37
+ const nbRows = reData.length;
38
+ const nbColumns = reData[0].length;
39
+
40
+ const { zeroFilling: zeroFillingF2, digitalFilterValue = 0 } = options;
41
+
42
+ const { factor: f2Factor = 2 } = zeroFillingF2 || {};
43
+ const nbPointsF2 =
44
+ zeroFillingF2?.nbPoints || 2 ** Math.round(Math.log2(nbColumns * f2Factor));
45
+
46
+ const reFFT = new Matrix(nbRows, nbPointsF2);
47
+ const imFFT = new Matrix(nbRows, nbPointsF2);
48
+
49
+ const { apodization: apodizationF2 } = options;
50
+ let {
51
+ ph0: ph0F2,
52
+ ph1: ph1F2,
53
+ mode: pcModeF2 = 'no',
54
+ } = options?.phaseCorrection || {};
55
+
56
+ for (let i = 0; i < nbRows; i++) {
57
+ const newData = digitalFilter(
58
+ { re: reData[i], im: imData[i] },
59
+ { digitalFilterValue },
60
+ );
61
+
62
+ Object.assign(newData, removeDCOffset(newData, { digitalFilterValue }));
63
+
64
+ if (apodizationF2) {
65
+ const { re: aRe, im: aIm } = apodization(newData, apodizationF2);
66
+ Object.assign(newData, { re: aRe, im: aIm });
67
+ }
68
+
69
+ Object.assign(
70
+ newData,
71
+ zeroFilling(newData, { digitalFilterValue, nbPoints: nbPointsF2 }),
72
+ );
73
+
74
+ Object.assign(newData, reimFFT(newData, { applyZeroShift: true }));
75
+
76
+ const dfResidual =
77
+ (digitalFilterValue - Math.round(digitalFilterValue)) * Math.PI * 2;
78
+ if (dfResidual > 0) {
79
+ Object.assign(newData, reimPhaseCorrection(newData, 0, -dfResidual));
80
+ }
81
+
82
+ if (
83
+ i === 0 &&
84
+ pcModeF2 === 'pk' &&
85
+ ph0F2 === undefined &&
86
+ ph1F2 === undefined
87
+ ) {
88
+ const phased = reimAutoPhaseCorrection(newData, { magnitudeMode: false });
89
+ ph0F2 = phased.ph0;
90
+ ph1F2 = phased.ph1;
91
+ }
92
+ if (pcModeF2 === 'pk' && ph0F2 !== undefined && ph1F2 !== undefined) {
93
+ Object.assign(
94
+ newData,
95
+ reimPhaseCorrection(
96
+ newData,
97
+ (ph0F2 * Math.PI) / 180,
98
+ (ph1F2 * Math.PI) / 180,
99
+ ),
100
+ );
101
+ }
102
+
103
+ reFFT.setRow(i, newData.re);
104
+ imFFT.setRow(i, newData.im);
105
+ }
106
+
107
+ return {
108
+ re: reFFT,
109
+ im: imFFT,
110
+ };
111
+ }
@@ -0,0 +1,123 @@
1
+ import { DoubleArray } from 'cheminfo-types';
2
+ import Matrix from 'ml-matrix';
3
+ import {
4
+ reimAbsolute,
5
+ reimAutoPhaseCorrection,
6
+ reimFFT,
7
+ reimPhaseCorrection,
8
+ } from 'ml-spectra-processing';
9
+
10
+ import {
11
+ apodization,
12
+ ApodizationOptions,
13
+ } from '../../../apodization/apodization';
14
+
15
+ import { zeroFilling, ZeroFillingOptions } from './zeroFilling';
16
+
17
+ interface PhaseCorrection {
18
+ mode?: string;
19
+ ph0?: number;
20
+ ph1?: number;
21
+ }
22
+
23
+ export interface IndirectDimensionOptions {
24
+ fnMode?: string;
25
+ zeroFilling?: ZeroFillingOptions;
26
+ apodization?: ApodizationOptions;
27
+ phaseCorrection?: PhaseCorrection;
28
+ }
29
+
30
+ export function fftIndirectDimension(
31
+ data: { re: DoubleArray[] | Matrix; im: DoubleArray[] | Matrix },
32
+ options: IndirectDimensionOptions,
33
+ ) {
34
+ const reData = Matrix.checkMatrix(data.re);
35
+ const imData = Matrix.checkMatrix(data.im);
36
+
37
+ const nbRows = reData.rows;
38
+ const nbColumns = reData.columns;
39
+
40
+ const { zeroFilling: zeroFillingF1 } = options;
41
+ const { factor: f1Factor = 2 } = zeroFillingF1 || {};
42
+ const nbPointsF1 =
43
+ zeroFillingF1?.nbPoints || 2 ** Math.round(Math.log2(nbRows * f1Factor));
44
+ const { apodization: apodizationF1 } = options;
45
+ let {
46
+ ph0: ph0F1,
47
+ ph1: ph1F1,
48
+ mode: pcModeF1,
49
+ } = options.phaseCorrection || {};
50
+
51
+ const reResult = new Matrix(nbPointsF1, nbColumns);
52
+ const imResult = new Matrix(nbPointsF1, nbColumns);
53
+ for (let i = 0; i < nbColumns; i++) {
54
+ const { re: aRe, im: aIm } = apodization(
55
+ {
56
+ re: reData.getColumn(i),
57
+ im: imData.getColumn(i),
58
+ },
59
+ apodizationF1,
60
+ );
61
+
62
+ const newRe = [];
63
+ const newIm = [];
64
+ if (options.fnMode === 'echo') {
65
+ for (let j = 0; j < aRe.length; j += 2) {
66
+ newRe.push(aRe[j]);
67
+ newIm.push(aRe[j + 1]);
68
+ }
69
+ } else {
70
+ for (let j = 0; j < aRe.length; j++) {
71
+ newRe.push(aRe[j]);
72
+ newIm.push(-aIm[j]);
73
+ }
74
+ }
75
+
76
+ let transformed = reimFFT(
77
+ zeroFilling(
78
+ {
79
+ re: newRe,
80
+ im: newIm,
81
+ },
82
+ { digitalFilterValue: 0, nbPoints: nbPointsF1 },
83
+ ),
84
+ { applyZeroShift: true },
85
+ );
86
+ let { re, im } = transformed;
87
+
88
+ if (options.phaseCorrection?.mode === 'mc') {
89
+ re = reimAbsolute({ re, im });
90
+ im = new Float64Array(im.length);
91
+ }
92
+
93
+ if (pcModeF1 === 'pk') {
94
+ if (i === 0 && ph0F1 === undefined && ph1F1 === undefined) {
95
+ //here we can improve it by checking if the experiment is phase sensitive.
96
+ const phased = reimAutoPhaseCorrection(
97
+ { re, im },
98
+ { magnitudeMode: false },
99
+ );
100
+ ph0F1 = phased.ph0;
101
+ ph1F1 = phased.ph1;
102
+ }
103
+ if (ph0F1 !== undefined && ph1F1 !== undefined) {
104
+ Object.assign(
105
+ transformed,
106
+ reimPhaseCorrection(
107
+ transformed,
108
+ (ph0F1 * Math.PI) / 180,
109
+ (ph1F1 * Math.PI) / 180,
110
+ ),
111
+ );
112
+ }
113
+ }
114
+
115
+ reResult.setColumn(i, re);
116
+ imResult.setColumn(i, im);
117
+ }
118
+
119
+ return {
120
+ re: reResult,
121
+ im: imResult,
122
+ };
123
+ }
@@ -0,0 +1,19 @@
1
+ import { DataReIm, xMean } from 'ml-spectra-processing';
2
+
3
+ export function removeDCOffset(data: DataReIm, options: any) {
4
+ let { digitalFilterValue = 0 } = options;
5
+ const nbPoints = data.re.length;
6
+ const newRe = new Float64Array(data.re);
7
+ const newIm = new Float64Array(data.im);
8
+ const averageRe = xMean(
9
+ data.re.slice((nbPoints * 0.75) >> 0, nbPoints - digitalFilterValue),
10
+ );
11
+ const averageIm = xMean(
12
+ data.im.slice((nbPoints * 0.75) >> 0, nbPoints - digitalFilterValue),
13
+ );
14
+ for (let i = digitalFilterValue; i < nbPoints - digitalFilterValue; i++) {
15
+ newRe[i] -= averageRe;
16
+ newIm[i] -= averageIm;
17
+ }
18
+ return { re: newRe, im: newIm };
19
+ }
@@ -0,0 +1,39 @@
1
+ import { DataReIm } from 'ml-spectra-processing';
2
+
3
+ export interface ZeroFillingOptions {
4
+ nbPoints?: number;
5
+ factor?: number;
6
+ digitalFilterValue?: number;
7
+ }
8
+
9
+ export function zeroFilling(data: DataReIm, options: ZeroFillingOptions) {
10
+ let { nbPoints, factor = 2, digitalFilterValue: grpdly = 0 } = options;
11
+
12
+ if (!nbPoints) {
13
+ nbPoints = 2 ** Math.round(Math.log2(data.re.length * factor));
14
+ }
15
+
16
+ let pointsToShift;
17
+ if (grpdly > 0) {
18
+ pointsToShift = Math.floor(grpdly);
19
+ } else {
20
+ pointsToShift = 0;
21
+ }
22
+
23
+ const { re, im } = data;
24
+
25
+ let newRE = new Float64Array(nbPoints);
26
+ let newIM = new Float64Array(nbPoints);
27
+
28
+ const length = Math.min(nbPoints, re.length);
29
+
30
+ newRE.set(re.slice(0, length - pointsToShift));
31
+ newIM.set(im.slice(0, length - pointsToShift));
32
+
33
+ if (pointsToShift > 0 && pointsToShift < nbPoints) {
34
+ newRE.set(re.slice(re.length - pointsToShift), nbPoints - pointsToShift);
35
+ newIM.set(im.slice(re.length - pointsToShift), nbPoints - pointsToShift);
36
+ }
37
+
38
+ return { re: newRE, im: newIM };
39
+ }
@@ -0,0 +1,123 @@
1
+ import { DoubleArray } from 'cheminfo-types';
2
+ import Matrix from 'ml-matrix';
3
+
4
+ import { ApodizationOptions } from '../apodization/apodization';
5
+ import { Data2D, Data2DFid } from './Data2D';
6
+
7
+ import { quadrature } from './quadrature';
8
+ import {
9
+ DirectDimensionOptions,
10
+ fftDirectDimension,
11
+ } from './util/fft2d/fftDirectDimension';
12
+ import {
13
+ fftIndirectDimension,
14
+ IndirectDimensionOptions,
15
+ } from './util/fft2d/fftIndirectDimension';
16
+ import { ZeroFillingOptions } from './util/fft2d/zeroFilling';
17
+
18
+ type Info2D = any;
19
+
20
+ interface BidimensionalFFTOptions {
21
+ fnMode: string;
22
+ info: Info2D;
23
+ zeroFilling?: ZeroFillingOptions;
24
+ phaseCorrection?: { ph0: number; ph1: number };
25
+ digitalFilterValue?: number;
26
+ apodization?: ApodizationOptions;
27
+ indirect?: IndirectDimensionOptions;
28
+ direct?: DirectDimensionOptions;
29
+ }
30
+
31
+ export function xyzBidimensionalFFT(
32
+ data: Data2D,
33
+ options: BidimensionalFFTOptions,
34
+ ) {
35
+ ensureData2DFid(data);
36
+
37
+ const {
38
+ info,
39
+ fnMode,
40
+ digitalFilterValue = 0,
41
+ zeroFilling: zeroFillingGeneral,
42
+ } = options;
43
+
44
+ const orderedData = quadrature(data, { fnMode });
45
+
46
+ const { re: reData } = orderedData;
47
+ const nbRows = reData.length;
48
+ const nbColumns = reData[0].length;
49
+ const { direct = {}, indirect = {} } = options;
50
+
51
+ const { zeroFilling: zeroFillingF2 = zeroFillingGeneral } = direct;
52
+
53
+ const { factor: f2Factor = 2 } = zeroFillingF2 || {};
54
+ const nbPointsF2 =
55
+ zeroFillingF2?.nbPoints || 2 ** Math.round(Math.log2(nbColumns * f2Factor));
56
+
57
+ const transformed = fftDirectDimension(orderedData, {
58
+ ...direct,
59
+ fnMode,
60
+ digitalFilterValue,
61
+ zeroFilling: { nbPoints: nbPointsF2 },
62
+ });
63
+
64
+ const { zeroFilling: zeroFillingF1 } = options;
65
+ const { factor: f1Factor = 2 } = zeroFillingF1 || {};
66
+ const nbPointsF1 =
67
+ zeroFillingF1?.nbPoints || 2 ** Math.round(Math.log2(nbRows * f1Factor));
68
+ const { re: reResult, im: imResult } = fftIndirectDimension(transformed, {
69
+ ...indirect,
70
+ fnMode,
71
+ zeroFilling: { nbPoints: nbPointsF1 },
72
+ });
73
+
74
+ const minMaxXY = getMinMaxXY(info);
75
+ return {
76
+ re: {
77
+ z: convertToDoubleArray(reResult),
78
+ maxZ: reResult.max(),
79
+ minZ: reResult.min(),
80
+ ...minMaxXY,
81
+ },
82
+ im: {
83
+ z: convertToDoubleArray(imResult),
84
+ maxZ: imResult.max(),
85
+ minZ: imResult.min(),
86
+ ...minMaxXY,
87
+ },
88
+ };
89
+ }
90
+
91
+ function ensureData2DFid(data: Data2D): asserts data is Data2DFid {
92
+ if (!('re' in data)) {
93
+ throw new Error('bidimensional FFT only can be applied to raw data');
94
+ }
95
+ }
96
+
97
+ function convertToDoubleArray(data: Matrix) {
98
+ const result: DoubleArray[] = new Array(data.rows);
99
+ for (let i = 0; i < data.rows; i++) {
100
+ result[i] = new Float64Array(data.getRow(i));
101
+ }
102
+ return result;
103
+ }
104
+
105
+ function getMinMaxXY(info: Info2D) {
106
+ const min: number[] = [];
107
+ const max: number[] = [];
108
+ for (let i = 0; i < info.baseFrequency.length; i++) {
109
+ const baseFrequency = Number.parseFloat(info.baseFrequency[i]);
110
+ const frequencyOffset = Number.parseFloat(info.frequencyOffset[i]);
111
+ const spectralWidth = Number.parseFloat(info.spectralWidth[i]);
112
+ const offset = frequencyOffset / baseFrequency;
113
+ min.push(offset - spectralWidth * 0.5);
114
+ max.push(offset + spectralWidth * 0.5);
115
+ }
116
+
117
+ return {
118
+ minX: min[0],
119
+ maxX: max[0],
120
+ minY: min[1],
121
+ maxY: max[1],
122
+ };
123
+ }