ml-peak-shape-generator
Advanced tools
Comparing version 1.0.0 to 2.0.0
# Changelog | ||
## [2.0.0](https://www.github.com/mljs/peak-shape-generator/compare/v1.0.0...v2.0.0) (2021-08-06) | ||
### ⚠ BREAKING CHANGES | ||
* | ||
### Features | ||
* abstract methods to abstract class Shape2D ([52c065e](https://www.github.com/mljs/peak-shape-generator/commit/52c065e765b665339cd1773f5ce400ac714fd2fe)) | ||
* classes extend Shape1D or Shape2D ([de6ce2c](https://www.github.com/mljs/peak-shape-generator/commit/de6ce2c3576e045bda335c1ace9815d25359dfe0)) | ||
* classes extend Shape1D or Shape2D ([99dc46c](https://www.github.com/mljs/peak-shape-generator/commit/99dc46c4b95748a017da175cbc659893f61e7384)) | ||
* typescript migration ([#45](https://www.github.com/mljs/peak-shape-generator/issues/45)) ([f546490](https://www.github.com/mljs/peak-shape-generator/commit/f546490c897039e7ec5790acecc0227644b726d3)) | ||
### Bug Fixes | ||
* workflows master branch is now 'main' ([69dc7a0](https://www.github.com/mljs/peak-shape-generator/commit/69dc7a05e9ccd54f9dde7fb012d3a674bf0982f3)) | ||
## [1.0.0](https://www.github.com/mljs/peak-shape-generator/compare/v0.14.0...v1.0.0) (2021-03-24) | ||
@@ -4,0 +23,0 @@ |
773
lib/index.js
@@ -1,743 +0,34 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
const GAUSSIAN_EXP_FACTOR = -4 * Math.LN2; | ||
const ROOT_PI_OVER_LN2 = Math.sqrt(Math.PI / Math.LN2); | ||
const ROOT_THREE = Math.sqrt(3); | ||
const ROOT_2LN2 = Math.sqrt(2 * Math.LN2); | ||
const ROOT_2LN2_MINUS_ONE = Math.sqrt(2 * Math.LN2) - 1; | ||
// https://en.wikipedia.org/wiki/Error_function#Inverse_functions | ||
// This code yields to a good approximation | ||
// If needed a better implementation using polynomial can be found on https://en.wikipedia.org/wiki/Error_function#Inverse_functions | ||
function erfinv(x) { | ||
let a = 0.147; | ||
if (x === 0) return 0; | ||
let ln1MinusXSqrd = Math.log(1 - x * x); | ||
let lnEtcBy2Plus2 = ln1MinusXSqrd / 2 + 2 / (Math.PI * a); | ||
let firstSqrt = Math.sqrt(lnEtcBy2Plus2 ** 2 - ln1MinusXSqrd / a); | ||
let secondSqrt = Math.sqrt(firstSqrt - lnEtcBy2Plus2); | ||
return secondSqrt * (x > 0 ? 1 : -1); | ||
} | ||
class Gaussian { | ||
/** | ||
* @param {object} [options = {}] | ||
* @param {number} [options.height=4*LN2/(PI*FWHM)] Define the height of the peak, by default area=1 (normalized) | ||
* @param {number} [options.fwhm = 500] - Full Width at Half Maximum in the number of points in FWHM. | ||
* @param {number} [options.sd] - Standard deviation, if it's defined options.fwhm will be ignored and the value will be computed sd * Math.sqrt(8 * Math.LN2); | ||
*/ | ||
constructor(options = {}) { | ||
this.fwhm = options.sd | ||
? Gaussian.widthToFWHM(2 * options.sd) | ||
: options.fwhm | ||
? options.fwhm | ||
: 500; | ||
this.height = | ||
options.height === undefined | ||
? Math.sqrt(-GAUSSIAN_EXP_FACTOR / Math.PI) / this.fwhm | ||
: options.height; | ||
} | ||
/** | ||
* Calculate a gaussian shape | ||
* @param {object} [options = {}] | ||
* @param {number} [options.factor = 6] - Number of time to take fwhm to calculate length. Default covers 99.99 % of area. | ||
* @param {number} [options.length = fwhm * factor + 1] - total number of points to calculate | ||
* @return {Float64Array} y values | ||
*/ | ||
getData(options = {}) { | ||
let { length, factor = this.getFactor() } = options; | ||
if (!length) { | ||
length = Math.min(Math.ceil(this.fwhm * factor), Math.pow(2, 25) - 1); | ||
if (length % 2 === 0) length++; | ||
} | ||
const center = (length - 1) / 2; | ||
const data = new Float64Array(length); | ||
for (let i = 0; i <= center; i++) { | ||
data[i] = this.fct(i - center) * this.height; | ||
data[length - 1 - i] = data[i]; | ||
} | ||
return data; | ||
} | ||
/** | ||
* Return a parameterized function of a gaussian shape (see README for equation). | ||
* @param {number} x - x value to calculate. | ||
* @returns {number} - the y value of gaussian with the current parameters. | ||
*/ | ||
fct(x) { | ||
return Gaussian.fct(x, this.fwhm); | ||
} | ||
/** | ||
* Calculate the number of times FWHM allows to reach a specific area coverage | ||
* @param {number} [area=0.9999] | ||
* @returns {number} | ||
*/ | ||
getFactor(area = 0.9999) { | ||
return Gaussian.getFactor(area); | ||
} | ||
/** | ||
* Calculate the area of the shape. | ||
* @returns {number} - returns the area. | ||
*/ | ||
getArea() { | ||
return Gaussian.getArea(this.fwhm, { height: this.height }); | ||
} | ||
/** | ||
* Compute the value of Full Width at Half Maximum (FWHM) from the width between the inflection points. | ||
* //https://mathworld.wolfram.com/GaussianFunction.html | ||
* @param {number} width - Width between the inflection points | ||
* @returns {number} fwhm | ||
*/ | ||
widthToFWHM(width) { | ||
//https://mathworld.wolfram.com/GaussianFunction.html | ||
return Gaussian.widthToFWHM(width); | ||
} | ||
/** | ||
* Compute the value of width between the inflection points from Full Width at Half Maximum (FWHM). | ||
* //https://mathworld.wolfram.com/GaussianFunction.html | ||
* @param {number} fwhm - Full Width at Half Maximum. | ||
* @returns {number} width | ||
*/ | ||
fwhmToWidth(fwhm = this.fwhm) { | ||
return Gaussian.fwhmToWidth(fwhm); | ||
} | ||
/** | ||
* set a new full width at half maximum | ||
* @param {number} fwhm - full width at half maximum | ||
*/ | ||
setFWHM(fwhm) { | ||
this.fwhm = fwhm; | ||
} | ||
/** | ||
* set a new height | ||
* @param {number} height - The maximal intensity of the shape. | ||
*/ | ||
setHeight(height) { | ||
this.height = height; | ||
} | ||
} | ||
/** | ||
* Return a parameterized function of a gaussian shape (see README for equation). | ||
* @param {number} x - x value to calculate. | ||
* @param {number} fwhm - full width half maximum | ||
* @returns {number} - the y value of gaussian with the current parameters. | ||
*/ | ||
Gaussian.fct = function fct(x, fwhm = 500) { | ||
return Math.exp(GAUSSIAN_EXP_FACTOR * Math.pow(x / fwhm, 2)); | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
/** | ||
* Compute the value of Full Width at Half Maximum (FWHM) from the width between the inflection points. | ||
* //https://mathworld.wolfram.com/GaussianFunction.html | ||
* @param {number} width - Width between the inflection points | ||
* @returns {number} fwhm | ||
*/ | ||
Gaussian.widthToFWHM = function widthToFWHM(width) { | ||
return width * ROOT_2LN2; | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
/** | ||
* Compute the value of width between the inflection points from Full Width at Half Maximum (FWHM). | ||
* //https://mathworld.wolfram.com/GaussianFunction.html | ||
* @param {number} fwhm - Full Width at Half Maximum. | ||
* @returns {number} width | ||
*/ | ||
Gaussian.fwhmToWidth = function fwhmToWidth(fwhm) { | ||
return fwhm / ROOT_2LN2; | ||
}; | ||
/** | ||
* Calculate the area of a specific shape. | ||
* @param {number} fwhm - Full width at half maximum. | ||
* @param {object} [options = {}] - options. | ||
* @param {number} [options.height = 1] - Maximum y value of the shape. | ||
* @returns {number} - returns the area of the specific shape and parameters. | ||
*/ | ||
Gaussian.getArea = function getArea(fwhm, options = {}) { | ||
let { height = 1 } = options; | ||
return (height * ROOT_PI_OVER_LN2 * fwhm) / 2; | ||
}; | ||
/** | ||
* Calculate the number of times FWHM allows to reach a specific area coverage. | ||
* @param {number} [area=0.9999] | ||
* @returns {number} | ||
*/ | ||
Gaussian.getFactor = function getFactor(area = 0.9999) { | ||
return Math.sqrt(2) * erfinv(area); | ||
}; | ||
class Lorentzian { | ||
/** | ||
* @param {object} [options = {}] | ||
* @param {number} [options.height=2/(PI*FWHM)] Define the height of the peak, by default area=1 (normalized) | ||
* @param {number} [options.fwhm = 500] - Full Width at Half Maximum in the number of points in FWHM. | ||
* @param {number} [options.sd] - Standard deviation, if it's defined options.fwhm will be ignored and the value will be computed sd * Math.sqrt(8 * Math.LN2); | ||
*/ | ||
constructor(options = {}) { | ||
this.fwhm = options.fwhm === undefined ? 500 : options.fwhm; | ||
this.height = | ||
options.height === undefined ? 2 / Math.PI / this.fwhm : options.height; | ||
} | ||
/** | ||
* Calculate a lorentzian shape | ||
* @param {object} [options = {}] | ||
* @param {number} [options.factor = Math.tan(Math.PI * (0.9999 - 0.5))] - Number of time to take fwhm to calculate length. Default covers 99.99 % of area. | ||
* @param {number} [options.length = fwhm * factor + 1] - total number of points to calculate | ||
* @return {Float64Array} y values | ||
*/ | ||
getData(options = {}) { | ||
let { length, factor = this.getFactor() } = options; | ||
if (!length) { | ||
length = Math.min(Math.ceil(this.fwhm * factor), Math.pow(2, 25) - 1); | ||
if (length % 2 === 0) length++; | ||
} | ||
const center = (length - 1) / 2; | ||
const data = new Float64Array(length); | ||
for (let i = 0; i <= center; i++) { | ||
data[i] = this.fct(i - center) * this.height; | ||
data[length - 1 - i] = data[i]; | ||
} | ||
return data; | ||
} | ||
/** | ||
* Return a parameterized function of a lorentzian shape (see README for equation). | ||
* @param {number} x - x value to calculate. | ||
* @returns {number} - the y value of lorentzian with the current parameters. | ||
*/ | ||
fct(x) { | ||
return Lorentzian.fct(x, this.fwhm); | ||
} | ||
/** | ||
* Calculate the number of times FWHM allows to reach a specific area coverage | ||
* @param {number} [area=0.9999] | ||
* @returns {number} | ||
*/ | ||
getFactor(area = 0.9999) { | ||
return Lorentzian.getFactor(area); | ||
} | ||
/** | ||
* Calculate the area of the shape. | ||
* @returns {number} - returns the area. | ||
*/ | ||
getArea() { | ||
return Lorentzian.getArea(this.fwhm, { height: this.height }); | ||
} | ||
/** | ||
* Compute the value of width between the inflection points of a specific shape from Full Width at Half Maximum (FWHM). | ||
* //https://mathworld.wolfram.com/LorentzianFunction.html | ||
* @param {number} [fwhm] - Full Width at Half Maximum. | ||
* @returns {number} width between the inflection points | ||
*/ | ||
fwhmToWidth(fwhm = this.fwhm) { | ||
return Lorentzian.fwhmToWidth(fwhm); | ||
} | ||
/** | ||
* Compute the value of Full Width at Half Maximum (FWHM) of a specific shape from the width between the inflection points. | ||
* //https://mathworld.wolfram.com/LorentzianFunction.html | ||
* @param {number} [width] Width between the inflection points | ||
* @returns {number} fwhm | ||
*/ | ||
widthToFWHM(width) { | ||
return Lorentzian.widthToFWHM(width); | ||
} | ||
/** | ||
* set a new full width at half maximum | ||
* @param {number} fwhm - full width at half maximum | ||
*/ | ||
setFWHM(fwhm) { | ||
this.fwhm = fwhm; | ||
} | ||
/** | ||
* set a new height | ||
* @param {number} height - The maximal intensity of the shape. | ||
*/ | ||
setHeight(height) { | ||
this.height = height; | ||
} | ||
} | ||
/** | ||
* Return a parameterized function of a gaussian shape (see README for equation). | ||
* @param {number} x - x value to calculate. | ||
* @param {number} fwhm - full width half maximum | ||
* @returns {number} - the y value of gaussian with the current parameters. | ||
*/ | ||
Lorentzian.fct = function fct(x, fwhm) { | ||
const squareFWHM = fwhm * fwhm; | ||
return squareFWHM / (4 * Math.pow(x, 2) + squareFWHM); | ||
}; | ||
/** | ||
* Compute the value of width between the inflection points of a specific shape from Full Width at Half Maximum (FWHM). | ||
* //https://mathworld.wolfram.com/LorentzianFunction.html | ||
* @param {number} [fwhm] - Full Width at Half Maximum. | ||
* @returns {number} width between the inflection points | ||
*/ | ||
Lorentzian.fwhmToWidth = function fwhmToWidth(fwhm) { | ||
return fwhm / ROOT_THREE; | ||
}; | ||
/** | ||
* Compute the value of Full Width at Half Maximum (FWHM) of a specific shape from the width between the inflection points. | ||
* //https://mathworld.wolfram.com/LorentzianFunction.html | ||
* @param {number} [width] Width between the inflection points | ||
* @returns {number} fwhm | ||
*/ | ||
Lorentzian.widthToFWHM = function widthToFWHM(width) { | ||
return width * ROOT_THREE; | ||
}; | ||
/** | ||
* Calculate the area of a specific shape. | ||
* @param {number} fwhm - Full width at half maximum. | ||
* @param {*} [options = {}] - options. | ||
* @param {number} [options.height = 1] - Maximum y value of the shape. | ||
* @returns {number} - returns the area of the specific shape and parameters. | ||
*/ | ||
Lorentzian.getArea = function getArea(fwhm, options = {}) { | ||
let { height = 1 } = options; | ||
return (height * Math.PI * fwhm) / 2; | ||
}; | ||
/** | ||
* Calculate the number of times FWHM allows to reach a specific area coverage | ||
* @param {number} [area=0.9999] | ||
* @returns {number} | ||
*/ | ||
Lorentzian.getFactor = function getFactor(area = 0.9999) { | ||
return 2 * Math.tan(Math.PI * (area - 0.5)); | ||
}; | ||
class PseudoVoigt { | ||
/** | ||
* @param {object} [options={}] | ||
* @param {number} [options.height=1/(mu*FWHM/sqrt(4*LN2/PI)+(1-mu)*fwhm*PI*0.5)] Define the height of the peak, by default area=1 (normalized) | ||
* @param {number} [options.fwhm=500] - Full Width at Half Maximum in the number of points in FWHM. | ||
* @param {number} [options.mu=0.5] - ratio of gaussian contribution. | ||
*/ | ||
constructor(options = {}) { | ||
this.mu = options.mu === undefined ? 0.5 : options.mu; | ||
this.fwhm = options.fwhm === undefined ? 500 : options.fwhm; | ||
this.height = | ||
options.height === undefined | ||
? 1 / | ||
((this.mu / Math.sqrt(-GAUSSIAN_EXP_FACTOR / Math.PI)) * this.fwhm + | ||
((1 - this.mu) * this.fwhm * Math.PI) / 2) | ||
: options.height; | ||
} | ||
/** | ||
* Calculate a linear combination of gaussian and lorentzian function width an same full width at half maximum | ||
* @param { object } [options = {}] | ||
* @param { number } [options.factor = 2 * Math.tan(Math.PI * (0.9999 - 0.5))] - Number of time to take fwhm in the calculation of the length.Default covers 99.99 % of area. | ||
* @param { number } [options.length = fwhm * factor + 1] - total number of points to calculate | ||
* @return { object } - { fwhm, data<Float64Array>} - An with the number of points at half maximum and the array of y values covering the 99.99 % of the area. | ||
*/ | ||
getData(options = {}) { | ||
let { length, factor = this.getFactor() } = options; | ||
if (!length) { | ||
length = Math.ceil(this.fwhm * factor); | ||
if (length % 2 === 0) length++; | ||
} | ||
const center = (length - 1) / 2; | ||
let data = new Float64Array(length); | ||
for (let i = 0; i <= center; i++) { | ||
data[i] = this.fct(i - center) * this.height; | ||
data[length - 1 - i] = data[i]; | ||
} | ||
return data; | ||
} | ||
/** | ||
* Return a parameterized function of a linear combination of Gaussian and Lorentzian shapes where the full width at half maximum are the same for both kind of shapes (see README for equation). | ||
* @param {number} [x] x value to calculate. | ||
* @returns {number} - the y value of a pseudo voigt with the current parameters. | ||
*/ | ||
fct(x) { | ||
return PseudoVoigt.fct(x, this.fwhm, this.mu); | ||
} | ||
/** | ||
* Calculate the number of times FWHM allows to reach a specific area coverage | ||
* @param {number} [area=0.9999] - required area to be coverage | ||
* @param {number} [mu=this.mu] - ratio of gaussian contribution. | ||
* @returns {number} | ||
*/ | ||
getFactor(area = 0.9999, mu = this.mu) { | ||
return PseudoVoigt.getFactor(area, mu); | ||
} | ||
/** | ||
* Calculate the area of the shape. | ||
* @returns {number} - returns the area. | ||
*/ | ||
getArea() { | ||
return PseudoVoigt.getArea(this.fwhm, { height: this.height, mu: this.mu }); | ||
} | ||
/** | ||
* Compute the value of Full Width at Half Maximum (FMHM) from width between the inflection points. | ||
* @param {number} width - width between the inflection points | ||
* @param {number} [mu = 0.5] - ratio of gaussian contribution. | ||
* @returns {number} Full Width at Half Maximum (FMHM). | ||
*/ | ||
widthToFWHM(width, mu) { | ||
return PseudoVoigt.widthToFWHM(width, mu); | ||
} | ||
/** | ||
* Compute the value of width between the inflection points from Full Width at Half Maximum (FWHM). | ||
* @param {number} fwhm - Full Width at Half Maximum. | ||
* @param {number} [mu] - ratio of gaussian contribution. | ||
* @returns {number} width between the inflection points. | ||
*/ | ||
fwhmToWidth(fwhm = this.fwhm, mu = this.mu) { | ||
return PseudoVoigt.fwhmToWidth(fwhm, mu); | ||
} | ||
/** | ||
* set a new full width at half maximum | ||
* @param {number} fwhm - full width at half maximum | ||
*/ | ||
setFWHM(fwhm) { | ||
this.fwhm = fwhm; | ||
} | ||
/** | ||
* set a new height | ||
* @param {number} height - The maximal intensity of the shape. | ||
*/ | ||
setHeight(height) { | ||
this.height = height; | ||
} | ||
/** | ||
* set a new mu | ||
* @param {number} mu - ratio of gaussian contribution. | ||
*/ | ||
setMu(mu) { | ||
this.mu = mu; | ||
} | ||
} | ||
/** | ||
* Return a parameterized function of a gaussian shape (see README for equation). | ||
* @param {number} x - x value to calculate. | ||
* @param {number} fwhm - full width half maximum | ||
* @param {number} [mu=0.5] - ratio of gaussian contribution. | ||
* @returns {number} - the y value of gaussian with the current parameters. | ||
*/ | ||
PseudoVoigt.fct = function fct(x, fwhm, mu = 0.5) { | ||
return (1 - mu) * Lorentzian.fct(x, fwhm) + mu * Gaussian.fct(x, fwhm); | ||
}; | ||
/** | ||
* Compute the value of Full Width at Half Maximum (FMHM) from width between the inflection points. | ||
* @param {number} width - width between the inflection points | ||
* @param {number} [mu = 0.5] - ratio of gaussian contribution. | ||
* @returns {number} Full Width at Half Maximum (FMHM). | ||
*/ | ||
PseudoVoigt.widthToFWHM = function widthToFWHM(width, mu = 0.5) { | ||
return width * (mu * ROOT_2LN2_MINUS_ONE + 1); | ||
}; | ||
/** | ||
* Compute the value of width between the inflection points from Full Width at Half Maximum (FWHM). | ||
* @param {number} fwhm - Full Width at Half Maximum. | ||
* @param {number} [mu = 0.5] - ratio of gaussian contribution. | ||
* @returns {number} width between the inflection points. | ||
*/ | ||
PseudoVoigt.fwhmToWidth = function fwhmToWidth(fwhm, mu = 0.5) { | ||
return fwhm / (mu * ROOT_2LN2_MINUS_ONE + 1); | ||
}; | ||
/** | ||
* Calculate the area of a specific shape. | ||
* @param {number} fwhm - Full width at half maximum. | ||
* @param {*} [options = {}] - options. | ||
* @param {number} [options.height = 1] - Maximum y value of the shape. | ||
* @param {number} [options.mu = 0.5] - ratio of gaussian contribution. | ||
* @returns {number} - returns the area of the specific shape and parameters. | ||
*/ | ||
PseudoVoigt.getArea = function getArea(fwhm, options = {}) { | ||
let { height = 1, mu = 0.5 } = options; | ||
return (fwhm * height * (mu * ROOT_PI_OVER_LN2 + (1 - mu) * Math.PI)) / 2; | ||
}; | ||
/** | ||
* Calculate the number of times FWHM allows to reach a specific area coverage | ||
* @param {number} [area=0.9999] - required area to be coverage | ||
* @param {number} [mu=this.mu] - ratio of gaussian contribution. | ||
* @returns {number} | ||
*/ | ||
PseudoVoigt.getFactor = function getFactor(area = 0.9999, mu = 0.5) { | ||
return mu < 1 ? Lorentzian.getFactor(area) : Gaussian.getFactor(area); | ||
}; | ||
let axis = ['x', 'y']; | ||
class Gaussian2D { | ||
/** | ||
* @param {object} [options = {}] | ||
* @param {number} [options.height=4*LN2/(PI*xFWHM*yFWHM)] Define the height of the peak, by default area=1 (normalized). | ||
* @param {number} [options.fwhm = 500] - Full Width at Half Maximum in the number of points in FWHM used if x or y has not the fwhm property. | ||
* @param {object} [options.x] - Options for x axis. | ||
* @param {number} [options.x.fwhm = fwhm] - Full Width at Half Maximum in the number of points in FWHM for x axis. | ||
* @param {number} [options.x.sd] - Standard deviation for x axis, if it's defined options.x.fwhm will be ignored and the value will be computed sd * Math.sqrt(8 * Math.LN2); | ||
* @param {object} [options.y] - Options for y axis. | ||
* @param {number} [options.y.fwhm = fwhm] - Full Width at Half Maximum in the number of points in FWHM for y axis. | ||
* @param {number} [options.y.sd] - Standard deviation for y axis, if it's defined options.y.fwhm will be ignored and the value will be computed sd * Math.sqrt(8 * Math.LN2); | ||
*/ | ||
constructor(options = {}) { | ||
let { fwhm: globalFWHM = 500 } = options; | ||
for (let i of axis) { | ||
let fwhm; | ||
if (!options[i]) { | ||
fwhm = globalFWHM; | ||
} else { | ||
fwhm = options[i].sd | ||
? Gaussian2D.widthToFWHM(2 * options[i].sd) | ||
: options[i].fwhm || globalFWHM; | ||
} | ||
this[i] = { fwhm }; | ||
} | ||
this.height = | ||
options.height === undefined | ||
? -GAUSSIAN_EXP_FACTOR / Math.PI / this.x.fwhm / this.y.fwhm | ||
: options.height; | ||
} | ||
/** | ||
* Calculate a Gaussian2D shape | ||
* @param {object} [options = {}] | ||
* @param {number} [options.factor] - Number of time to take fwhm to calculate length. Default covers 99.99 % of area. | ||
* @param {object} [options.x] - parameter for x axis. | ||
* @param {number} [options.x.length=fwhm*factor+1] - length on x axis. | ||
* @param {number} [options.x.factor=factor] - Number of time to take fwhm to calculate length. Default covers 99.99 % of area. | ||
* @param {object} [options.y] - parameter for y axis. | ||
* @param {number} [options.y.length=fwhm*factor+1] - length on y axis. | ||
* @param {number} [options.y.factor=factor] - Number of time to take fwhm to calculate length. Default covers 99.99 % of area. | ||
* @return {Array<Float64Array>} - z values. | ||
*/ | ||
getData(options = {}) { | ||
let { x = {}, y = {}, factor = this.getFactor(), length } = options; | ||
let xLength = x.length || length; | ||
if (!xLength) { | ||
let { factor: xFactor = factor } = x; | ||
xLength = Math.min(Math.ceil(this.x.fwhm * xFactor), Math.pow(2, 25) - 1); | ||
if (xLength % 2 === 0) xLength++; | ||
} | ||
let yLength = y.length || length; | ||
if (!yLength) { | ||
let { factor: yFactor = factor } = y; | ||
yLength = Math.min(Math.ceil(this.y.fwhm * yFactor), Math.pow(2, 25) - 1); | ||
if (yLength % 2 === 0) yLength++; | ||
} | ||
const xCenter = (xLength - 1) / 2; | ||
const yCenter = (yLength - 1) / 2; | ||
const data = new Array(xLength); | ||
for (let i = 0; i < xLength; i++) { | ||
data[i] = new Array(yLength); | ||
} | ||
for (let i = 0; i < xLength; i++) { | ||
for (let j = 0; j < yLength; j++) { | ||
data[i][j] = this.fct(i - xCenter, j - yCenter) * this.height; | ||
} | ||
} | ||
return data; | ||
} | ||
/** | ||
* Return the intensity value of a 2D gaussian shape (see README for equation). | ||
* @param {number} x - x value to calculate. | ||
* @param {number} y - y value to calculate. | ||
* @returns {number} - the z value of bi-dimensional gaussian with the current parameters. | ||
*/ | ||
fct(x, y) { | ||
return Gaussian2D.fct(x, y, this.x.fwhm, this.y.fwhm); | ||
} | ||
/** | ||
* Calculate the number of times FWHM allows to reach a specific volume coverage. | ||
* @param {number} [volume=0.9999] | ||
* @returns {number} | ||
*/ | ||
getFactor(volume = 0.9999) { | ||
return Gaussian2D.getFactor(volume); | ||
} | ||
/** | ||
* Calculate the volume of the shape. | ||
* @returns {number} - returns the volume. | ||
*/ | ||
getVolume() { | ||
return Gaussian2D.getVolume(this.x.fwhm, this.y.fwhm, { | ||
height: this.height, | ||
}); | ||
} | ||
/** | ||
* Compute the value of Full Width at Half Maximum (FWHM) from the width between the inflection points. | ||
* //https://mathworld.wolfram.com/Gaussian2DFunction.html | ||
* @param {number} width - Width between the inflection points | ||
* @returns {number} fwhm | ||
*/ | ||
widthToFWHM(width) { | ||
//https://mathworld.wolfram.com/Gaussian2DFunction.html | ||
return Gaussian2D.widthToFWHM(width); | ||
} | ||
/** | ||
* Compute the value of width between the inflection points from Full Width at Half Maximum (FWHM). | ||
* //https://mathworld.wolfram.com/Gaussian2DFunction.html | ||
* @param {number} fwhm - Full Width at Half Maximum. | ||
* @returns {number} width | ||
*/ | ||
fwhmToWidth(fwhm = this.x.fwhm) { | ||
return Gaussian2D.fwhmToWidth(fwhm); | ||
} | ||
/** | ||
* set a new full width at half maximum | ||
* @param {number} fwhm - full width at half maximum | ||
* @param {string|Array<string>} axisLabel - label of axis, if it is undefined fwhm is set to both axis. | ||
*/ | ||
setFWHM(fwhm, axisLabel) { | ||
if (!axisLabel) axisLabel = axis; | ||
if (!Array.isArray(axisLabel)) axisLabel = [axisLabel]; | ||
for (let i of axisLabel) { | ||
let axisName = i.toLowerCase(); | ||
if (axisName !== 'y' && axisName !== 'x') { | ||
throw new Error('axis label should be x or y'); | ||
} | ||
this[axisName].fwhm = fwhm; | ||
} | ||
} | ||
/** | ||
* set a new height | ||
* @param {number} height - The maximal intensity of the shape. | ||
*/ | ||
setHeight(height) { | ||
this.height = height; | ||
} | ||
} | ||
/** | ||
* Return a parameterized function of a Gaussian2D shape (see README for equation). | ||
* @param {number} x - x value to calculate. | ||
* @param {number} y - y value to calculate. | ||
* @param {number} fwhmX - full width half maximum in the x axis. | ||
* @param {number} fwhmY - full width half maximum in the y axis. | ||
* @returns {number} - the z value of bi-dimensional gaussian with the current parameters. | ||
*/ | ||
Gaussian2D.fct = function fct(x, y, xFWHM = 500, yFWHM = 500) { | ||
return Math.exp( | ||
GAUSSIAN_EXP_FACTOR * (Math.pow(x / xFWHM, 2) + Math.pow(y / yFWHM, 2)), | ||
); | ||
}; | ||
/** | ||
* Compute the value of Full Width at Half Maximum (FWHM) from the width between the inflection points. | ||
* //https://mathworld.wolfram.com/Gaussian2DFunction.html | ||
* @param {number} width - Width between the inflection points | ||
* @returns {number} fwhm | ||
*/ | ||
Gaussian2D.widthToFWHM = function widthToFWHM(width) { | ||
return width * ROOT_2LN2; | ||
}; | ||
/** | ||
* Compute the value of width between the inflection points from Full Width at Half Maximum (FWHM). | ||
* //https://mathworld.wolfram.com/Gaussian2DFunction.html | ||
* @param {number} fwhm - Full Width at Half Maximum. | ||
* @returns {number} width | ||
*/ | ||
Gaussian2D.fwhmToWidth = function fwhmToWidth(fwhm) { | ||
return fwhm / ROOT_2LN2; | ||
}; | ||
/** | ||
* Calculate the volume of a specific shape. | ||
* @param {number} xFWHM - Full width at half maximum for x axis. | ||
* @param {number} yFWHM - Full width at half maximum for y axis. | ||
* @param {object} [options = {}] - options. | ||
* @param {number} [options.height = 1] - Maximum z value of the shape. | ||
* @returns {number} - returns the area of the specific shape and parameters. | ||
*/ | ||
Gaussian2D.getVolume = function getVolume(xFWHM, yFWHM, options = {}) { | ||
let { height = 1 } = options; | ||
return (height * Math.PI * xFWHM * yFWHM) / Math.LN2 / 4; | ||
}; | ||
/**@TODO look for a better factor | ||
* Calculate the number of times FWHM allows to reach a specific volume coverage. | ||
* @param {number} [volume=0.9999] | ||
* @returns {number} | ||
*/ | ||
Gaussian2D.getFactor = function getFactor(volume = 0.9999) { | ||
return Math.sqrt(2) * erfinv(volume); | ||
}; | ||
function getShapeGenerator(options) { | ||
let { kind = 'Gaussian', options: shapeOptions } = options; | ||
switch (kind.toLowerCase().replace(/[^a-z^0-9]/g, '')) { | ||
case 'gaussian': | ||
return new Gaussian(shapeOptions); | ||
case 'lorentzian': | ||
return new Lorentzian(shapeOptions); | ||
case 'pseudovoigt': | ||
return new PseudoVoigt(shapeOptions); | ||
case 'gaussian2d': | ||
return new Gaussian2D(shapeOptions); | ||
default: | ||
throw new Error(`Unknown kind: ${kind}`); | ||
} | ||
} | ||
exports.Gaussian = Gaussian; | ||
exports.Gaussian2D = Gaussian2D; | ||
exports.Lorentzian = Lorentzian; | ||
exports.PseudoVoigt = PseudoVoigt; | ||
exports.getShapeGenerator = getShapeGenerator; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.gaussian2D = exports.pseudoVoigt = exports.lorentzian = exports.gaussian = void 0; | ||
exports.gaussian = __importStar(require("./shapes/1d/gaussian/Gaussian")); | ||
exports.lorentzian = __importStar(require("./shapes/1d/lorentzian/Lorentzian")); | ||
exports.pseudoVoigt = __importStar(require("./shapes/1d/pseudoVoigt/PseudoVoigt")); | ||
exports.gaussian2D = __importStar(require("./shapes/2d/gaussian2D/Gaussian2D")); | ||
__exportStar(require("./util/getShape1D"), exports); | ||
__exportStar(require("./util/getShape2D"), exports); | ||
__exportStar(require("./shapes/1d/Shape1D"), exports); | ||
__exportStar(require("./shapes/2d/Shape2D"), exports); | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "ml-peak-shape-generator", | ||
"version": "1.0.0", | ||
"version": "2.0.0", | ||
"description": "Generate various peak shapes", | ||
"main": "lib/index.js", | ||
"module": "src/index.js", | ||
"main": "./lib/index.js", | ||
"module": "./lib-esm/index.js", | ||
"types": "./lib/index.d.ts", | ||
"keywords": [ | ||
"gaussian", | ||
"shape", | ||
"generator", | ||
"lorentzian", | ||
"gaussian 2D" | ||
], | ||
"author": "Luc Patiny", | ||
"contributors": [ | ||
"J. Alejandro Bolaños A. <jobo322>" | ||
], | ||
"license": "MIT", | ||
"files": [ | ||
"src", | ||
"lib", | ||
"src" | ||
"lib-esm" | ||
], | ||
"scripts": { | ||
"eslint": "eslint src", | ||
"check-types": "tsc --noEmit", | ||
"clean": "rimraf lib lib-esm", | ||
"eslint": "eslint src --cache", | ||
"eslint-fix": "npm run eslint -- --fix", | ||
"compile": "rollup -c", | ||
"prepublishOnly": "npm run compile", | ||
"test": "npm run test-coverage && npm run eslint", | ||
"test-coverage": "jest --coverage", | ||
"test-only": "jest" | ||
"prepack": "npm run tsc", | ||
"prettier": "prettier --check src", | ||
"prettier-write": "prettier --write src", | ||
"test": "npm run test-coverage && npm run eslint && npm run prettier && npm run check-types", | ||
"test-coverage": "npm run test-only -- --coverage", | ||
"test-only": "jest", | ||
"tsc": "npm run clean && npm run tsc-cjs && npm run tsc-esm", | ||
"tsc-cjs": "tsc --project tsconfig.cjs.json", | ||
"tsc-esm": "tsc --project tsconfig.esm.json" | ||
}, | ||
@@ -24,5 +44,2 @@ "repository": { | ||
}, | ||
"keywords": [], | ||
"author": "Luc Patiny", | ||
"license": "MIT", | ||
"bugs": { | ||
@@ -33,2 +50,3 @@ "url": "https://github.com/mljs/peak-shape-generator/issues" | ||
"jest": { | ||
"preset": "ts-jest", | ||
"testEnvironment": "node" | ||
@@ -44,12 +62,12 @@ }, | ||
"devDependencies": { | ||
"@babel/plugin-transform-modules-commonjs": "^7.13.8", | ||
"@types/jest": "^26.0.24", | ||
"compute-erfinv": "^3.0.1", | ||
"eslint": "^7.22.0", | ||
"eslint-config-cheminfo": "^5.2.3", | ||
"esm": "^3.2.25", | ||
"jest": "^26.6.3", | ||
"jest-matcher-deep-close-to": "^2.0.1", | ||
"prettier": "^2.2.1", | ||
"rollup": "^2.42.3" | ||
"eslint": "^7.32.0", | ||
"eslint-config-cheminfo-typescript": "^8.0.9", | ||
"jest": "^27.0.6", | ||
"prettier": "^2.3.2", | ||
"rimraf": "^3.0.2", | ||
"ts-jest": "^27.0.4", | ||
"typescript": "^4.3.5" | ||
} | ||
} |
@@ -5,4 +5,6 @@ # ml-peak-shape-generator | ||
[![build status][ci-image]][ci-url] | ||
[![Test coverage][codecov-image]][codecov-url] | ||
[![npm download][download-image]][download-url] | ||
Generate various peak shapes. | ||
@@ -30,22 +32,52 @@ | ||
```js | ||
import { Gaussian, Lorentzian, PseudoVoigt} from 'ml-peak-shape-generator'; | ||
import { gaussian, lorentzian, pseudoVoigt} from 'ml-peak-shape-generator'; | ||
// It's possible to specify the windows size with factor option | ||
let data = new Gaussian({factor: 3.5, sd: 500}).getData(); | ||
let data = gaussian.getData({factor: 3.5, sd: 500}); | ||
// or fix the number of points as Full Width at Half Maximum | ||
let data = new Gaussian({factor: 3.5, fwhm: 500}).getData(); | ||
let data = gaussian.getData({factor: 3.5, fwhm: 500}); | ||
// It's possible to specify the windows size with factor option | ||
let data = new Loretzian({factor: 5, fwhm: 500}).getData(); | ||
let data = loretzian.getData({factor: 5, fwhm: 500}); | ||
// It's possible to specify the windows size with factor option | ||
let data = new PseudoVoigt({{factor: 5, fwhm: 500}}).getData(); | ||
let data = pseudoVoigt.getData({factor: 5, fwhm: 500}); | ||
``` | ||
It is also possible to take an instance of each kind of shape: | ||
```js | ||
import { getShapeGenerator } from 'ml-peak-shape-generator'; | ||
import { gaussian, gaussian2D } from 'ml-peak-shape-shape'; | ||
const gaussianShape = new gaussian.Gaussian({ fwhm: 500, factor: 3.5, height: 1}); | ||
// It is possible to set a new value for fwhm and height | ||
gaussianShape.fwhm = 300; | ||
gaussianShape.height = 2; | ||
// By default the height value ensure a surface/area equal 1. | ||
const gaussian2DShape = new gaussian2D.Gaussian2D({ fwhm: 500, factor: 3.5}); | ||
// It is possible to set values for sd, fwhm and factor for each axes. | ||
const gaussian2DShape = new gaussian2D.Gaussian2D({ fwhm: { x: 300, y: 500 }, factor: 3.5}); | ||
// It is possible to set new value for fwhm by: | ||
gaussian2D.fwhm = { x: 300, y: 500 }; | ||
// or set the same value for both axes. | ||
gaussian2D.fwhm = 400; | ||
//An instance of any shape has the same methods accessible for each | ||
//shape e.g. fct or getData, but these use the internal parameters. e.g: | ||
const gaussianShape = new gaussian.Gaussian({ fwhm: 500 }); | ||
gaussianShape.fct(5); | ||
gaussian.fct(5, 500); | ||
``` | ||
```js | ||
import { getShape1D, getShape2D } from 'ml-peak-shape-generator'; | ||
// If you want to dynamically select a shape you can use the `getShapeGenerator` method. It returns a instance of required kind of shape. | ||
let shapeGenerator = getShapeGenerator('lorentzian', {factor: 3.5, sd: 500}); | ||
let shapeGenerator = getShape1D('lorentzian', {factor: 3.5, sd: 500}); | ||
let shapeGenerator = getShape2D('gaussian2D', {factor: 3.5, sd: 500}); | ||
``` | ||
@@ -56,5 +88,4 @@ | ||
```js | ||
import { Gaussian } from 'ml-peak-shape-generator'; | ||
const func = Gaussian.fct(x - mean, fwhm); | ||
import { gaussian } from 'ml-peak-shape-generator'; | ||
const func = gaussian.fct(x - mean, fwhm); | ||
``` | ||
@@ -70,5 +101,7 @@ | ||
[npm-url]: https://www.npmjs.com/package/ml-peak-shape-generator | ||
[ci-image]: https://github.com/mljs/peak-shape-generator/workflows/Node.js%20CI/badge.svg?branch=master | ||
[ci-image]: https://github.com/mljs/peak-shape-generator/workflows/Node.js%20CI/badge.svg?branch=main | ||
[ci-url]: https://github.com/mljs/peak-shape-generator/actions?query=workflow%3A%22Node.js+CI%22 | ||
[codecov-image]: https://img.shields.io/codecov/c/github/mljs/peak-shape-generator.svg | ||
[codecov-url]: https://codecov.io/gh/mljs/peak-shape-generator | ||
[download-image]: https://img.shields.io/npm/dm/ml-peak-shape-generator.svg | ||
[download-url]: https://www.npmjs.com/package/ml-peak-shape-generator | ||
[download-url]: https://www.npmjs.com/package/ml-peak-shape-generator |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
127584
71
2345
103
1