Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

spectrum-generator

Package Overview
Dependencies
Maintainers
6
Versions
56
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

spectrum-generator - npm Package Compare versions

Comparing version 1.1.0 to 2.0.0

src/__tests__/__snapshots__/SpectrumGenerator.js.snap

5

History.md

@@ -0,1 +1,6 @@

<a name="2.0.0"></a>
# [2.0.0](https://github.com/cheminfo/spectrum-generator/compare/v1.1.0...v2.0.0) (2018-07-28)
<a name="1.1.0"></a>

@@ -2,0 +7,0 @@ # [1.1.0](https://github.com/cheminfo/spectrum-generator/compare/v1.0.1...v1.1.0) (2018-03-12)

344

lib/index.js

@@ -5,2 +5,69 @@ 'use strict';

function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var random = _interopDefault(require('random'));
var seedrandom = _interopDefault(require('seedrandom'));
/**
* Add a baseline to the spectrum
* @param {object} [data] - Your spectrum data in the format {x:[x1, x2, ...], y:[y1, y2, ...]}
* @param {function} [baselineFct] - Mathematical function producing the baseline you want
* @return {object} data
*/
function addBaseline(data, baselineFct) {
if (!baselineFct) return data;
var xs = data.x;
var ys = data.y;
for (let i = 0; i < xs.length; i++) {
ys[i] += baselineFct(xs[i]);
}
return data;
}
/**
* Add noise to the spectrum
* @param {object} [data] - Your spectrum data in the format {x:[x1, x2, ...], y:[y1, y2, ...]}
* @param {number} [percent = 0] - Noise's amplitude in percents of the spectrum max value
* @param {object} [options={}]
* @param {boolean} [options.seed=false] - Deterministic sequence of random number
* @param {string} [options.distribution='uniform'] -Type of random: 'uniform' (true random), 'normal' (gaussian distribution),
* @return {data} data
*/
function addNoise(data, percent = 0, options = {}) {
const {
distribution = 'uniform',
seed = false
} = options;
if (seed) random.use(seedrandom(0));
let generateRandomNumber;
switch (distribution) {
case 'uniform':
generateRandomNumber = random.uniform(-0.5, 0.5);
break;
case 'normal':
generateRandomNumber = random.normal();
break;
default:
throw new Error(`Unknown distribution ${options.distribution}`);
}
if (!percent) return data;
var ys = data.y;
var factor = percent * findMax(ys) / 100;
for (let i = 0; i < ys.length; i++) {
ys[i] += generateRandomNumber() * factor;
}
return data;
}
function findMax(array) {
let max = Number.MIN_VALUE;
for (let item of array) {
if (item > max) max = item;
}
return max;
}
const gaussianFactor = 5; // after 5 the value is nearly 0, nearly no artifacts

@@ -11,19 +78,8 @@ const gaussianWidth = 1000; // half height peak Width in point

for (let i = 0; i <= gaussianWidth * gaussianFactor; i++) {
gaussian.push(Math.exp(-1 / 2 * Math.pow((i - (gaussianFactor * gaussianWidth / 2)) * 2 / gaussianWidth * ratio, 2)));
gaussian.push(Math.exp(-1 / 2 * Math.pow((i - (gaussianFactor * gaussianWidth / 2)) * 2 / gaussianWidth * ratio, 2)));
}
function defaultGetWidth(value) {
return 1 + 3 * value / 1000;
}
const kStart = Symbol('start');
const kEnd = Symbol('end');
const kPointsPerUnit = Symbol('pointsPerUnit');
const kGetWidth = Symbol('getWidth');
const kSize = Symbol('size');
const kSpectrum = Symbol('spectrum');
const kMaxSize = Symbol('maxSize');
class SpectrumGenerator {
/**
/**
* @class SpectrumGenerator

@@ -34,39 +90,67 @@ * @constructor

* @param {number} [options.end=1000] - Last x value (inclusive)
* @param {function} [options.peakWidthFct=function(x){return(5)}] - Width of peak depending the x value
* @param {number} [options.pointsPerUnit=5] - Number of values between each unit of the x axis
* @param {number} [options.maxSize=1e7] - maximal array size
* @param {function} [options.getWidth] - Returns the width of a peak for a given value. Defaults to (1 + 3 * value / 1000)
*
* @example
* import SG from 'spectrum-generator';
* const sg = new SG({start: 0, end: 100, pointsPerUnit: 5, peakWidthFct: (x) => 1 + 3 * x / 1000 });
* sg.addPeak( [5, 50] );
* sg.addPeak([20, 100], { width: 3 });
* sg.addPeak([35, 100], { widthLeft: 10, widthRight: 30 });
* sg.addPeak([50, 10], { widthLeft: 5, widthRight: 5 });
* sg.addPeaks([ [70,20], [80,40], [90,10] ]);
* sg.addNoise(10);
* sg.addBaseline( (x) => x * x / 100 );
* var spectrum = sg.getSpectrum();
*
* @example
* import SG from 'spectrum-generator';
* const spectrum=SG.generateSpectrum([ [20,3], [30,2], [40,2] ], {
* start: 0,
* end: 100,
* pointsPerUnit: 1,
* noise: {
* percent: 10,
* distribution: 'normal',
* seed: true
* },
* baseline: (x) => 2 * x
* })
*/
constructor(options = {}) {
const {
start = 0,
end = 1000,
pointsPerUnit = 5,
getWidth = defaultGetWidth,
maxSize = 1e7
} = options;
constructor(options = {}) {
options = Object.assign({}, {
start: 0,
end: 1000,
pointsPerUnit: 5,
peakWidthFct: (() => 5),
maxSize: 1e7
}, options);
this.start = options.start;
this.end = options.end;
this.pointsPerUnit = options.pointsPerUnit;
this.peakWidthFct = options.peakWidthFct;
this.maxSize = options.maxSize;
assertInteger(start, 'start');
assertInteger(end, 'end');
assertInteger(pointsPerUnit, 'pointsPerUnit');
assertInteger(maxSize, 'maxSize');
assertInteger(this.start, 'start');
assertInteger(this.end, 'end');
assertInteger(this.pointsPerUnit, 'pointsPerUnit');
assertInteger(this.maxSize, 'maxSize');
if (end <= start) {
throw new RangeError('end option must be larger than start');
}
if (this.end <= this.start) {
throw new RangeError('end option must be larger than start');
}
if (typeof getWidth !== 'function') {
throw new TypeError('getWidth option must be a function');
}
if (typeof this.peakWidthFct !== 'function') {
throw new TypeError('peakWidthFct option must be a function');
}
this[kStart] = start;
this[kEnd] = end;
this[kPointsPerUnit] = pointsPerUnit;
this[kGetWidth] = getWidth;
this[kMaxSize] = maxSize;
this[kSize] = (end - start) * pointsPerUnit + 1;
this.reset();
}
this.reset(maxSize);
}
get size() {
return (this.end - this.start) * this.pointsPerUnit + 1;
}
/**
/**
* Add a series of peaks to the spectrum.

@@ -76,46 +160,79 @@ * @param {Array<Array<number>>} peaks

*/
addPeaks(peaks) {
if (!Array.isArray(peaks)) {
throw new TypeError('peaks must be an array');
}
for (const peak of peaks) {
this.addPeak(peak);
}
return this;
addPeaks(peaks) {
if (!Array.isArray(peaks)) {
throw new TypeError('peaks must be an array');
}
for (const peak of peaks) {
this.addPeak(peak);
}
return this;
}
/**
/**
* Add a single peak to the spectrum.
* @param {Array<number>} peak
* @param {object} [options={}]
* @param {number} [options.width] Half-height width
* @param {number} [options.widthLeft] Half-height width left (asymmetric peak)
* @param {number} [options.widthRight] Half-height width right (asymmetric peak)
* @return {this}
*/
addPeak(peak) {
if (!Array.isArray(peak) || peak.length !== 2) {
throw new Error('peak must be an array with two values');
}
addPeak(peak, options = {}) {
if (!Array.isArray(peak) || peak.length !== 2) {
throw new Error('peak must be an array with two values');
}
const value = peak[0];
const intensity = peak[1];
const width = this[kGetWidth](value);
const firstValue = value - (width / 2 * gaussianFactor);
const lastValue = value + (width / 2 * gaussianFactor);
const value = peak[0];
const intensity = peak[1];
const firstPoint = Math.floor(firstValue * this[kPointsPerUnit]);
const lastPoint = Math.ceil(lastValue * this[kPointsPerUnit]);
const middlePoint = (firstPoint + lastPoint) / 2;
let {
width = this.peakWidthFct(value),
widthLeft,
widthRight
} = options;
for (var j = firstPoint; j <= lastPoint; j++) {
var index = j - this[kStart] * this[kPointsPerUnit];
if (index >= 0 && index < this[kSize]) {
var gaussianIndex = Math.floor(gaussianWidth / width * (j - middlePoint) / this[kPointsPerUnit] + gaussianFactor * gaussianWidth / 2);
if (gaussianIndex >= 0 && gaussianIndex < gaussian.length) {
this[kSpectrum].y[index] += gaussian[gaussianIndex] * intensity;
}
}
if (!widthLeft) widthLeft = width;
if (!widthRight) widthRight = width;
const firstValue = value - (widthLeft / 2 * gaussianFactor);
const lastValue = value + (widthRight / 2 * gaussianFactor);
const firstPoint = Math.floor(firstValue * this.pointsPerUnit);
const lastPoint = Math.ceil(lastValue * this.pointsPerUnit);
const middlePoint = value * this.pointsPerUnit;
// we calculate the left part of the gaussian
for (let j = firstPoint; j < middlePoint; j++) {
let index = j - this.start * this.pointsPerUnit;
if (index >= 0 && index < this.size) {
let gaussianIndex = Math.floor(gaussianWidth / widthLeft * (j - middlePoint) / this.pointsPerUnit + gaussianFactor * gaussianWidth / 2);
if (gaussianIndex >= 0 && gaussianIndex < gaussian.length) {
this.data.y[index] += gaussian[gaussianIndex] * intensity;
}
}
}
return this;
// we calculate the right part of the gaussian
for (let j = middlePoint; j <= lastPoint; j++) {
let index = j - this.start * this.pointsPerUnit;
if (index >= 0 && index < this.size) {
let gaussianIndex = Math.floor(gaussianWidth / widthRight * (j - middlePoint) / this.pointsPerUnit + gaussianFactor * gaussianWidth / 2);
if (gaussianIndex >= 0 && gaussianIndex < gaussian.length) {
this.data.y[index] += gaussian[gaussianIndex] * intensity;
}
}
}
/**
return this;
}
addBaseline(baselineFct) {
addBaseline(this.data, baselineFct);
}
addNoise(percent, options) {
addNoise(this.data, percent, options);
}
/**
* Get the generated spectrum.

@@ -126,52 +243,49 @@ * @param {boolean} [copy=true] - If true, returns a copy of the spectrum.

*/
getSpectrum(copy = true) {
if (copy) {
return {
x: this[kSpectrum].x.slice(),
y: this[kSpectrum].y.slice()
};
} else {
return this[kSpectrum];
}
getSpectrum(copy = true) {
if (copy) {
return {
x: this.data.x.slice(),
y: this.data.y.slice()
};
} else {
return this.data;
}
}
/**
/**
* Resets the generator with an empty spectrum.
* @return {this}
*/
reset() {
let finalSize = (this[kEnd] - this[kStart]) * this[kPointsPerUnit] + 1;
if (finalSize > this[kMaxSize]) {
throw new Error('Generated array has size ' + finalSize + ' larger than maxSize: ' + this[kMaxSize]);
}
reset() {
if (this.size > this.maxSize) {
throw new Error(`Generated array has size ${this.size} larger than maxSize: ${this.maxSize}`);
}
const spectrum = this[kSpectrum] = {
x: [],
y: []
};
const spectrum = this.data = {
x: [],
y: new Array(this.size).fill(0)
};
const interval = 1 / this[kPointsPerUnit];
const js = [];
for (let j = 0; j < this[kPointsPerUnit]; j++) {
js.push(j * interval);
}
const interval = 1 / this.pointsPerUnit;
const js = [];
for (let j = 0; j < this.pointsPerUnit; j++) {
js.push(j * interval);
}
for (let i = this[kStart]; i < this[kEnd]; i++) {
for (let j = 0; j < this[kPointsPerUnit]; j++) {
spectrum.x.push(i + js[j]);
spectrum.y.push(0);
}
}
for (let i = this.start; i < this.end; i++) {
for (let j = 0; j < this.pointsPerUnit; j++) {
spectrum.x.push(i + js[j]);
}
}
spectrum.x.push(this[kEnd]);
spectrum.y.push(0);
spectrum.x.push(this.end);
return this;
}
return this;
}
}
function assertInteger(value, name) {
if (!Number.isInteger(value)) {
throw new TypeError(`${name} option must be an integer`);
}
if (!Number.isInteger(value)) {
throw new TypeError(`${name} option must be an integer`);
}
}

@@ -185,9 +299,11 @@

*/
function generateSpectrum(peaks, options) {
const generator = new SpectrumGenerator(options);
generator.addPeaks(peaks);
return generator.getSpectrum();
function generateSpectrum(peaks, options = {}) {
const generator = new SpectrumGenerator(options);
generator.addPeaks(peaks);
if (options.baseline) generator.addBaseline(options.baseline);
if (options.noise) generator.addNoise(options.noise.percent, options.noise);
return generator.getSpectrum();
}
exports.SpectrumGenerator = SpectrumGenerator;
exports['default'] = SpectrumGenerator;
exports.generateSpectrum = generateSpectrum;
{
"name": "spectrum-generator",
"version": "1.1.0",
"version": "2.0.0",
"description": "generate a spectrum from discrete peaks",

@@ -26,3 +26,4 @@ "main": "lib/index.js",

"contributors": [
"Michaël Zasso"
"Michaël Zasso",
"Océane Patiny"
],

@@ -42,7 +43,15 @@ "license": "MIT",

"eslint-config-cheminfo": "^1.8.0",
"eslint-plugin-import": "^2.9.0",
"eslint-plugin-jest": "^21.15.0",
"eslint-plugin-no-only-tests": "^2.0.0",
"esm": "^3.0.69",
"jest": "^21.2.1",
"npm-run-all": "^4.1.2",
"rollup": "^0.51.0"
},
"dependencies": {
"debug": "^3.1.0",
"random": "^2.0.12",
"seedrandom": "^2.4.3"
}
}

@@ -1,6 +0,6 @@

import {SpectrumGenerator} from '..';
import SpectrumGenerator from '..';
const integerReg = /^\w+ option must be an integer$/;
const endStartReg = /^end option must be larger than start$/;
const getWidthReg = /^getWidth option must be a function$/;
const peakWidthReg = /^peakWidthFct option must be a function$/;
const addPeaksReg = /^peaks must be an array$/;

@@ -10,33 +10,33 @@ const addPeakReg = /^peak must be an array with two values$/;

describe('errors', () => {
it('wrong options', () => {
expect(() => new SpectrumGenerator({start: 0.5})).toThrow(integerReg);
expect(() => new SpectrumGenerator({start: false})).toThrow(integerReg);
it('wrong options', () => {
expect(() => new SpectrumGenerator({ start: 0.5 })).toThrow(integerReg);
expect(() => new SpectrumGenerator({ start: false })).toThrow(integerReg);
expect(() => new SpectrumGenerator({end: 0.5})).toThrow(integerReg);
expect(() => new SpectrumGenerator({end: false})).toThrow(integerReg);
expect(() => new SpectrumGenerator({ end: 0.5 })).toThrow(integerReg);
expect(() => new SpectrumGenerator({ end: false })).toThrow(integerReg);
expect(() => new SpectrumGenerator({pointsPerUnit: 0.5})).toThrow(integerReg);
expect(() => new SpectrumGenerator({pointsPerUnit: false})).toThrow(integerReg);
expect(() => new SpectrumGenerator({ pointsPerUnit: 0.5 })).toThrow(integerReg);
expect(() => new SpectrumGenerator({ pointsPerUnit: false })).toThrow(integerReg);
expect(() => new SpectrumGenerator({start: 0, end: 0})).toThrow(endStartReg);
expect(() => new SpectrumGenerator({start: 0, end: -10})).toThrow(endStartReg);
expect(() => new SpectrumGenerator({ start: 0, end: 0 })).toThrow(endStartReg);
expect(() => new SpectrumGenerator({ start: 0, end: -10 })).toThrow(endStartReg);
expect(() => new SpectrumGenerator({getWidth: null})).toThrow(getWidthReg);
});
expect(() => new SpectrumGenerator({ peakWidthFct: null })).toThrow(peakWidthReg);
});
it('addPeaks not an array', () => {
const generator = new SpectrumGenerator();
expect(() => generator.addPeaks()).toThrow(addPeaksReg);
expect(() => generator.addPeaks({})).toThrow(addPeaksReg);
});
it('addPeaks not an array', () => {
const generator = new SpectrumGenerator();
expect(() => generator.addPeaks()).toThrow(addPeaksReg);
expect(() => generator.addPeaks({})).toThrow(addPeaksReg);
});
it('addPeak not an array', () => {
const generator = new SpectrumGenerator();
expect(() => generator.addPeak()).toThrow(addPeakReg);
expect(() => generator.addPeak({})).toThrow(addPeakReg);
expect(() => generator.addPeak({})).toThrow(addPeakReg);
expect(() => generator.addPeak([])).toThrow(addPeakReg);
expect(() => generator.addPeak([1])).toThrow(addPeakReg);
expect(() => generator.addPeak([1, 2, 3])).toThrow(addPeakReg);
});
it('addPeak not an array', () => {
const generator = new SpectrumGenerator();
expect(() => generator.addPeak()).toThrow(addPeakReg);
expect(() => generator.addPeak({})).toThrow(addPeakReg);
expect(() => generator.addPeak({})).toThrow(addPeakReg);
expect(() => generator.addPeak([])).toThrow(addPeakReg);
expect(() => generator.addPeak([1])).toThrow(addPeakReg);
expect(() => generator.addPeak([1, 2, 3])).toThrow(addPeakReg);
});
});

@@ -1,2 +0,2 @@

import {generateSpectrum} from '..';
import { generateSpectrum } from '..';

@@ -6,86 +6,83 @@ const simpleGetWidth = () => 1;

describe('generateSpectrum', () => {
it('should work from zero', () => {
assertSimple({
start: 0,
end: 10,
peak: 5
});
it('should work from zero', () => {
assertSimple({
start: 0,
end: 10,
peak: 5
});
});
it('should work from positive start', () => {
assertSimple({
start: 5,
end: 15,
peak: 10
});
it('should work from positive start', () => {
assertSimple({
start: 5,
end: 15,
peak: 10
});
});
it('should work from negative start', () => {
assertSimple({
start: -15,
end: -5,
peak: -10
});
it('should work from negative start', () => {
assertSimple({
start: -15,
end: -5,
peak: -10
});
});
});
describe('generateSpectrum with one peak and small window', () => {
it('should work from 11', () => {
const spectrum = generateSpectrum([[12, 1]], {
start: 11,
end: 13,
pointsPerUnit: 10,
getWidth: () => 0.1
});
expect(Math.max(...spectrum.y)).toBe(1);
it('should work from 11', () => {
const spectrum = generateSpectrum([[12, 1]], {
start: 11,
end: 13,
pointsPerUnit: 10,
getWidth: () => 0.1
});
expect(Math.max(...spectrum.y)).toBe(1);
});
});
describe('generateSpectrum check large size', () => {
let data = [];
for (let i = 0; i < 10000; i++) {
data.push([i, Math.random()]);
}
it('should throw error for huge array', () => {
expect(() => generateSpectrum(data, {
start: 0,
end: 10000,
pointsPerUnit: 1000,
getWidth: () => 0.1
})).toThrow('Generated array has size 10000001 larger than maxSize: 10000000');
});
let data = [];
for (let i = 0; i < 10000; i++) {
data.push([i, Math.random()]);
}
it('should throw error for huge array', () => {
expect(() => generateSpectrum(data, {
start: 0,
end: 10000,
pointsPerUnit: 1000,
getWidth: () => 0.1
})).toThrow('Generated array has size 10000001 larger than maxSize: 10000000');
});
it('should throw for simple array is maxSize=1', () => {
expect(() => generateSpectrum([[1, 1]], {
start: 0,
end: 2,
pointsPerUnit: 1,
maxSize: 1,
getWidth: () => 0.1
})).toThrow('Generated array has size 3 larger than maxSize: 1');
});
it('should throw for simple array is maxSize=1', () => {
expect(() => generateSpectrum([[1, 1]], {
start: 0,
end: 2,
pointsPerUnit: 1,
maxSize: 1,
getWidth: () => 0.1
})).toThrow('Generated array has size 3 larger than maxSize: 1');
});
});
function assertSimple({start, end, peak}) {
const spectrum = generateSpectrum([[peak, 1]], {start, end, pointsPerUnit: 1, getWidth: simpleGetWidth});
assertSize(spectrum, end - start + 1);
assertInterval(spectrum, start);
function assertSimple({ start, end, peak }) {
const spectrum = generateSpectrum([[peak, 1]], { start, end, pointsPerUnit: 1, getWidth: simpleGetWidth });
assertSize(spectrum, end - start + 1);
assertInterval(spectrum, start);
}
function assertSize(spectrum, size) {
expect(spectrum.x.length).toBe(size);
expect(spectrum.y.length).toBe(size);
expect(spectrum.x).toHaveLength(size);
expect(spectrum.y).toHaveLength(size);
}
function assertInterval(spectrum, start) {
let expected = start;
for (const value of spectrum.x) {
expect(value).toBe(expected);
expected++;
}
let expected = start;
for (const value of spectrum.x) {
expect(value).toBe(expected);
expected++;
}
}

@@ -1,92 +0,102 @@

import {SpectrumGenerator} from '..';
import SpectrumGenerator from '..';
describe('SpectrumGenerator', () => {
it('0 half peak', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 2,
pointsPerUnit: 5
});
it('0 half peak', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 2,
pointsPerUnit: 5
});
generator.addPeak([0, 1]);
generator.addPeak([0, 1]);
const spectrum = generator.getSpectrum();
expectValue(spectrum, 0, 1);
const spectrum = generator.getSpectrum();
expectValue(spectrum, 0, 1);
});
it('end half peak', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 2,
pointsPerUnit: 5
});
it('end half peak', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 2,
pointsPerUnit: 5
});
generator.addPeak([2, 1]);
generator.addPeak([2, 1]);
const spectrum = generator.getSpectrum();
expectValue(spectrum, 2 * 5, 1);
});
const spectrum = generator.getSpectrum();
expectValue(spectrum, 2 * 5, 1);
it('1 middle peak', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 2,
pointsPerUnit: 5
});
it('1 middle peak', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 2,
pointsPerUnit: 5
});
generator.addPeak([1, 1]);
generator.addPeak([1, 1]);
const spectrum = generator.getSpectrum();
expectValue(spectrum, 1 * 5, 1);
});
const spectrum = generator.getSpectrum();
expectValue(spectrum, 1 * 5, 1);
it('check asymmetric peak', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 100,
pointsPerUnit: 2
});
generator.addPeak([35, 100], { widthLeft: 10, widthRight: 30 });
expect(generator.getSpectrum()).toMatchSnapshot();
});
it('1 middle peak check width', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 2,
pointsPerUnit: 10
});
it('1 middle peak check width', () => {
const generator = new SpectrumGenerator({
start: 0,
end: 2,
pointsPerUnit: 10,
peakWidthFct: (x) => 1 + 3 * x / 1000
});
generator.addPeak([1, 1]);
generator.addPeak([1, 1]);
const spectrum = generator.getSpectrum();
expect(spectrum.y[ 0.5 * 10]).toBeCloseTo(0.5, 2);
expect(spectrum.y[ 1.5 * 10]).toBeCloseTo(0.5, 2);
expectValue(spectrum, 1 * 10, 1);
});
const spectrum = generator.getSpectrum();
expect(spectrum.y[0.5 * 10]).toBeCloseTo(0.5, 2);
expect(spectrum.y[1.5 * 10]).toBeCloseTo(0.5, 2);
expectValue(spectrum, 1 * 10, 1);
});
it('full generation', () => {
const generator = new SpectrumGenerator();
it('full generation', () => {
const generator = new SpectrumGenerator();
generator.addPeak([0, 1]);
generator.addPeak([50, 12]);
generator.addPeaks([[100, 10], [14, 2]]);
generator.addPeak([0, 1]);
generator.addPeak([50, 12]);
generator.addPeaks([[100, 10], [14, 2]]);
const spectrum = generator.getSpectrum();
const spectrum = generator.getSpectrum();
expectValue(spectrum, 0, 1);
expectValue(spectrum, 50 * 5, 12);
expectValue(spectrum, 100 * 5, 10);
expectValue(spectrum, 14 * 5, 2);
expectValue(spectrum, 0, 1);
expectValue(spectrum, 50 * 5, 12);
expectValue(spectrum, 100 * 5, 10);
expectValue(spectrum, 14 * 5, 2);
});
});
it('getSpectrum', () => {
const generator = new SpectrumGenerator();
it('getSpectrum', () => {
const generator = new SpectrumGenerator();
const s1 = generator.getSpectrum();
const s2 = generator.getSpectrum();
const s1 = generator.getSpectrum();
const s2 = generator.getSpectrum();
expect(s1).not.toBe(s2);
expect(s1).not.toBe(s2);
const s3 = generator.getSpectrum(false);
const s4 = generator.getSpectrum(false);
const s3 = generator.getSpectrum(false);
const s4 = generator.getSpectrum(false);
expect(s3).toBe(s4);
expect(s3).not.toBe(s2);
});
expect(s3).toBe(s4);
expect(s3).not.toBe(s2);
});
});
function expectValue(spectrum, index, value) {
expect(spectrum.y[index]).toBe(value);
expect(spectrum.y[index]).toBe(value);
}

@@ -0,1 +1,5 @@

import addBaseline from './util/addBaseline.js';
import addNoise from './util/addNoise.js';
const gaussianFactor = 5; // after 5 the value is nearly 0, nearly no artifacts

@@ -6,19 +10,8 @@ const gaussianWidth = 1000; // half height peak Width in point

for (let i = 0; i <= gaussianWidth * gaussianFactor; i++) {
gaussian.push(Math.exp(-1 / 2 * Math.pow((i - (gaussianFactor * gaussianWidth / 2)) * 2 / gaussianWidth * ratio, 2)));
gaussian.push(Math.exp(-1 / 2 * Math.pow((i - (gaussianFactor * gaussianWidth / 2)) * 2 / gaussianWidth * ratio, 2)));
}
function defaultGetWidth(value) {
return 1 + 3 * value / 1000;
}
const kStart = Symbol('start');
const kEnd = Symbol('end');
const kPointsPerUnit = Symbol('pointsPerUnit');
const kGetWidth = Symbol('getWidth');
const kSize = Symbol('size');
const kSpectrum = Symbol('spectrum');
const kMaxSize = Symbol('maxSize');
export class SpectrumGenerator {
/**
export default class SpectrumGenerator {
/**
* @class SpectrumGenerator

@@ -29,39 +22,67 @@ * @constructor

* @param {number} [options.end=1000] - Last x value (inclusive)
* @param {function} [options.peakWidthFct=function(x){return(5)}] - Width of peak depending the x value
* @param {number} [options.pointsPerUnit=5] - Number of values between each unit of the x axis
* @param {number} [options.maxSize=1e7] - maximal array size
* @param {function} [options.getWidth] - Returns the width of a peak for a given value. Defaults to (1 + 3 * value / 1000)
*
* @example
* import SG from 'spectrum-generator';
* const sg = new SG({start: 0, end: 100, pointsPerUnit: 5, peakWidthFct: (x) => 1 + 3 * x / 1000 });
* sg.addPeak( [5, 50] );
* sg.addPeak([20, 100], { width: 3 });
* sg.addPeak([35, 100], { widthLeft: 10, widthRight: 30 });
* sg.addPeak([50, 10], { widthLeft: 5, widthRight: 5 });
* sg.addPeaks([ [70,20], [80,40], [90,10] ]);
* sg.addNoise(10);
* sg.addBaseline( (x) => x * x / 100 );
* var spectrum = sg.getSpectrum();
*
* @example
* import SG from 'spectrum-generator';
* const spectrum=SG.generateSpectrum([ [20,3], [30,2], [40,2] ], {
* start: 0,
* end: 100,
* pointsPerUnit: 1,
* noise: {
* percent: 10,
* distribution: 'normal',
* seed: true
* },
* baseline: (x) => 2 * x
* })
*/
constructor(options = {}) {
const {
start = 0,
end = 1000,
pointsPerUnit = 5,
getWidth = defaultGetWidth,
maxSize = 1e7
} = options;
constructor(options = {}) {
options = Object.assign({}, {
start: 0,
end: 1000,
pointsPerUnit: 5,
peakWidthFct: (() => 5),
maxSize: 1e7
}, options);
this.start = options.start;
this.end = options.end;
this.pointsPerUnit = options.pointsPerUnit;
this.peakWidthFct = options.peakWidthFct;
this.maxSize = options.maxSize;
assertInteger(start, 'start');
assertInteger(end, 'end');
assertInteger(pointsPerUnit, 'pointsPerUnit');
assertInteger(maxSize, 'maxSize');
assertInteger(this.start, 'start');
assertInteger(this.end, 'end');
assertInteger(this.pointsPerUnit, 'pointsPerUnit');
assertInteger(this.maxSize, 'maxSize');
if (end <= start) {
throw new RangeError('end option must be larger than start');
}
if (this.end <= this.start) {
throw new RangeError('end option must be larger than start');
}
if (typeof getWidth !== 'function') {
throw new TypeError('getWidth option must be a function');
}
if (typeof this.peakWidthFct !== 'function') {
throw new TypeError('peakWidthFct option must be a function');
}
this[kStart] = start;
this[kEnd] = end;
this[kPointsPerUnit] = pointsPerUnit;
this[kGetWidth] = getWidth;
this[kMaxSize] = maxSize;
this[kSize] = (end - start) * pointsPerUnit + 1;
this.reset();
}
this.reset(maxSize);
}
get size() {
return (this.end - this.start) * this.pointsPerUnit + 1;
}
/**
/**
* Add a series of peaks to the spectrum.

@@ -71,46 +92,79 @@ * @param {Array<Array<number>>} peaks

*/
addPeaks(peaks) {
if (!Array.isArray(peaks)) {
throw new TypeError('peaks must be an array');
}
for (const peak of peaks) {
this.addPeak(peak);
}
return this;
addPeaks(peaks) {
if (!Array.isArray(peaks)) {
throw new TypeError('peaks must be an array');
}
for (const peak of peaks) {
this.addPeak(peak);
}
return this;
}
/**
/**
* Add a single peak to the spectrum.
* @param {Array<number>} peak
* @param {object} [options={}]
* @param {number} [options.width] Half-height width
* @param {number} [options.widthLeft] Half-height width left (asymmetric peak)
* @param {number} [options.widthRight] Half-height width right (asymmetric peak)
* @return {this}
*/
addPeak(peak) {
if (!Array.isArray(peak) || peak.length !== 2) {
throw new Error('peak must be an array with two values');
}
addPeak(peak, options = {}) {
if (!Array.isArray(peak) || peak.length !== 2) {
throw new Error('peak must be an array with two values');
}
const value = peak[0];
const intensity = peak[1];
const width = this[kGetWidth](value);
const firstValue = value - (width / 2 * gaussianFactor);
const lastValue = value + (width / 2 * gaussianFactor);
const value = peak[0];
const intensity = peak[1];
const firstPoint = Math.floor(firstValue * this[kPointsPerUnit]);
const lastPoint = Math.ceil(lastValue * this[kPointsPerUnit]);
const middlePoint = (firstPoint + lastPoint) / 2;
let {
width = this.peakWidthFct(value),
widthLeft,
widthRight
} = options;
for (var j = firstPoint; j <= lastPoint; j++) {
var index = j - this[kStart] * this[kPointsPerUnit];
if (index >= 0 && index < this[kSize]) {
var gaussianIndex = Math.floor(gaussianWidth / width * (j - middlePoint) / this[kPointsPerUnit] + gaussianFactor * gaussianWidth / 2);
if (gaussianIndex >= 0 && gaussianIndex < gaussian.length) {
this[kSpectrum].y[index] += gaussian[gaussianIndex] * intensity;
}
}
if (!widthLeft) widthLeft = width;
if (!widthRight) widthRight = width;
const firstValue = value - (widthLeft / 2 * gaussianFactor);
const lastValue = value + (widthRight / 2 * gaussianFactor);
const firstPoint = Math.floor(firstValue * this.pointsPerUnit);
const lastPoint = Math.ceil(lastValue * this.pointsPerUnit);
const middlePoint = value * this.pointsPerUnit;
// we calculate the left part of the gaussian
for (let j = firstPoint; j < middlePoint; j++) {
let index = j - this.start * this.pointsPerUnit;
if (index >= 0 && index < this.size) {
let gaussianIndex = Math.floor(gaussianWidth / widthLeft * (j - middlePoint) / this.pointsPerUnit + gaussianFactor * gaussianWidth / 2);
if (gaussianIndex >= 0 && gaussianIndex < gaussian.length) {
this.data.y[index] += gaussian[gaussianIndex] * intensity;
}
}
}
return this;
// we calculate the right part of the gaussian
for (let j = middlePoint; j <= lastPoint; j++) {
let index = j - this.start * this.pointsPerUnit;
if (index >= 0 && index < this.size) {
let gaussianIndex = Math.floor(gaussianWidth / widthRight * (j - middlePoint) / this.pointsPerUnit + gaussianFactor * gaussianWidth / 2);
if (gaussianIndex >= 0 && gaussianIndex < gaussian.length) {
this.data.y[index] += gaussian[gaussianIndex] * intensity;
}
}
}
/**
return this;
}
addBaseline(baselineFct) {
addBaseline(this.data, baselineFct);
}
addNoise(percent, options) {
addNoise(this.data, percent, options);
}
/**
* Get the generated spectrum.

@@ -121,52 +175,49 @@ * @param {boolean} [copy=true] - If true, returns a copy of the spectrum.

*/
getSpectrum(copy = true) {
if (copy) {
return {
x: this[kSpectrum].x.slice(),
y: this[kSpectrum].y.slice()
};
} else {
return this[kSpectrum];
}
getSpectrum(copy = true) {
if (copy) {
return {
x: this.data.x.slice(),
y: this.data.y.slice()
};
} else {
return this.data;
}
}
/**
/**
* Resets the generator with an empty spectrum.
* @return {this}
*/
reset() {
let finalSize = (this[kEnd] - this[kStart]) * this[kPointsPerUnit] + 1;
if (finalSize > this[kMaxSize]) {
throw new Error('Generated array has size ' + finalSize + ' larger than maxSize: ' + this[kMaxSize]);
}
reset() {
if (this.size > this.maxSize) {
throw new Error(`Generated array has size ${this.size} larger than maxSize: ${this.maxSize}`);
}
const spectrum = this[kSpectrum] = {
x: [],
y: []
};
const spectrum = this.data = {
x: [],
y: new Array(this.size).fill(0)
};
const interval = 1 / this[kPointsPerUnit];
const js = [];
for (let j = 0; j < this[kPointsPerUnit]; j++) {
js.push(j * interval);
}
const interval = 1 / this.pointsPerUnit;
const js = [];
for (let j = 0; j < this.pointsPerUnit; j++) {
js.push(j * interval);
}
for (let i = this[kStart]; i < this[kEnd]; i++) {
for (let j = 0; j < this[kPointsPerUnit]; j++) {
spectrum.x.push(i + js[j]);
spectrum.y.push(0);
}
}
for (let i = this.start; i < this.end; i++) {
for (let j = 0; j < this.pointsPerUnit; j++) {
spectrum.x.push(i + js[j]);
}
}
spectrum.x.push(this[kEnd]);
spectrum.y.push(0);
spectrum.x.push(this.end);
return this;
}
return this;
}
}
function assertInteger(value, name) {
if (!Number.isInteger(value)) {
throw new TypeError(`${name} option must be an integer`);
}
if (!Number.isInteger(value)) {
throw new TypeError(`${name} option must be an integer`);
}
}

@@ -180,6 +231,8 @@

*/
export function generateSpectrum(peaks, options) {
const generator = new SpectrumGenerator(options);
generator.addPeaks(peaks);
return generator.getSpectrum();
export function generateSpectrum(peaks, options = {}) {
const generator = new SpectrumGenerator(options);
generator.addPeaks(peaks);
if (options.baseline) generator.addBaseline(options.baseline);
if (options.noise) generator.addNoise(options.noise.percent, options.noise);
return generator.getSpectrum();
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc