ml-spectra-processing
Advanced tools
Comparing version 1.2.0 to 2.0.0
@@ -0,1 +1,19 @@ | ||
# [2.0.0](https://github.com/cheminfo/spectra-processing/compare/v1.3.1...v2.0.0) (2020-02-08) | ||
### Bug Fixes | ||
* eslint ([af497eb](https://github.com/cheminfo/spectra-processing/commit/af497eb5fd8e20e2b9111a174b8259894f240617)) | ||
* use Number.NEGATIVE_INFINITY and not Number.MIN_VALUE ([06827c1](https://github.com/cheminfo/spectra-processing/commit/06827c1b58f4fa75263e05a839c798130b430474)) | ||
### Features | ||
* add XY.extract ([5dae851](https://github.com/cheminfo/spectra-processing/commit/5dae851b16653b97b072a7da5a871f9a0e8eb4c1)) | ||
* add zones ([bbe4a2f](https://github.com/cheminfo/spectra-processing/commit/bbe4a2f473d758fc4dc001d318a01e162a4b40ea)) | ||
* add zones in reduce ([9a2c573](https://github.com/cheminfo/spectra-processing/commit/9a2c573b5b3f7f10399e443b2cf9d2da442922c2)) | ||
* normalizeZones allow from / to ([a7e0762](https://github.com/cheminfo/spectra-processing/commit/a7e0762731a6919dbe56009a5590e6e9d5049645)) | ||
# [1.2.0](https://github.com/cheminfo/spectra-processing/compare/v1.1.0...v1.2.0) (2019-12-13) | ||
@@ -2,0 +20,0 @@ |
367
lib/index.js
@@ -21,2 +21,91 @@ 'use strict'; | ||
/** | ||
* Normalize an array of zones: | ||
* - ensure than from < to | ||
* - merge overlapping zones | ||
* @param {object} [zones=[]] | ||
* @param {object} [options={}] | ||
* @param {number} [options.from=Number.MIN_VALUE] | ||
* @param {number} [options.to=Number.MAX_VALUE] | ||
*/ | ||
function normalizeZones(zones = [], options = {}) { | ||
if (zones.length === 0) return []; | ||
zones = JSON.parse(JSON.stringify(zones)).map((zone) => | ||
zone.from > zone.to ? { from: zone.to, to: zone.from } : zone, | ||
); | ||
let { | ||
from = Number.NEGATIVE_INFINITY, | ||
to = Number.POSITIVE_INFINITY, | ||
} = options; | ||
if (from > to) { | ||
[from, to] = [to, from]; | ||
} | ||
zones = zones.sort((a, b) => { | ||
if (a.from !== b.from) return a.from - b.from; | ||
return a.to - b.to; | ||
}); | ||
zones.forEach((zone) => { | ||
if (from > zone.from) zone.from = from; | ||
if (to < zone.to) zone.to = to; | ||
}); | ||
zones = zones.filter((zone) => zone.from <= zone.to); | ||
if (zones.length === 0) return []; | ||
let currentZone = zones[0]; | ||
let result = [currentZone]; | ||
for (let i = 1; i < zones.length; i++) { | ||
let zone = zones[i]; | ||
if (zone.from <= currentZone.to) { | ||
currentZone.to = zone.to; | ||
} else { | ||
currentZone = zone; | ||
result.push(currentZone); | ||
} | ||
} | ||
return result; | ||
} | ||
/** | ||
* Extract zones from a XY data | ||
* @param {object} [points={}] - Object of points contains property x (an ordered increasing array) and y (an array) | ||
* @param {object} [options={}] | ||
* @param {Array} [options.zones=[]] | ||
* @return {Array} Array of points | ||
*/ | ||
function extract(points = {}, options = {}) { | ||
check(points); | ||
const { x, y } = points; | ||
let { zones } = options; | ||
zones = normalizeZones(zones); | ||
if (!Array.isArray(zones) || zones.length === 0) return points; | ||
let newX = []; | ||
let newY = []; | ||
let currentZone = zones[0]; | ||
let position = 0; | ||
loop: for (let i = 0; i < x.length; i++) { | ||
while (currentZone.to < x[i]) { | ||
position++; | ||
currentZone = zones[position]; | ||
if (!currentZone) { | ||
i = x.length; | ||
break loop; | ||
} | ||
} | ||
if (x[i] >= currentZone.from) { | ||
newX.push(x[i]); | ||
newY.push(y[i]); | ||
} | ||
} | ||
return { x: newX, y: newY }; | ||
} | ||
/** | ||
* Returns the closest index of a `target` in an ordered array | ||
@@ -232,4 +321,3 @@ * @param {array} array | ||
* display many spectra as SVG | ||
* @param {array} x | ||
* @param {array} y | ||
* @param {object} [points={}] - Object of points contains property x (an ordered increasing array) and y (an array) | ||
* @param {object} [options={}] | ||
@@ -239,6 +327,9 @@ * @param {number} [options.from=x[0]] | ||
* @param {number} [options.nbPoints=4001] Number of points | ||
* @param {number} [options.zones=[]] Array of zones to keep (from/to object) | ||
* @param {number} [options.optimize=false] If optimize we may have less than nbPoints at the end | ||
*/ | ||
function reduce(x, y, options = {}) { | ||
function reduce(points, options = {}) { | ||
check(points); | ||
const { x, y } = points; | ||
let { | ||
@@ -249,44 +340,104 @@ from = x[0], | ||
optimize = false, | ||
zones = [], | ||
} = options; | ||
let fromIndex = findClosestIndex(x, from); | ||
let toIndex = findClosestIndex(x, to); | ||
zones = normalizeZones(zones, { from, to }); | ||
if (zones.length === 0) zones = [{ from, to }]; // we take everything | ||
if (fromIndex > 0 && x[fromIndex] > from) fromIndex--; | ||
if (toIndex < x.length - 1 && x[toIndex] < to) toIndex++; | ||
// for each zone we should know the first index, the last index and the number of points | ||
if (toIndex - fromIndex < nbPoints) { | ||
let totalPoints = 0; | ||
for (let zone of zones) { | ||
zone.fromIndex = findClosestIndex(x, zone.from); | ||
zone.toIndex = findClosestIndex(x, zone.to); | ||
if (zone.fromIndex > 0 && x[zone.fromIndex] > zone.from) { | ||
zone.fromIndex--; | ||
} | ||
if (zone.toIndex < x.length - 1 && x[zone.toIndex] < zone.to) { | ||
zone.toIndex++; | ||
} | ||
zone.nbPoints = zone.toIndex - zone.fromIndex + 1; | ||
totalPoints += zone.nbPoints; | ||
} | ||
// we calculate the number of points per zone that we should keep | ||
if (totalPoints > nbPoints) { | ||
// need to reduce number of points | ||
let ratio = nbPoints / totalPoints; | ||
let currentTotal = 0; | ||
for (let i = 0; i < zones.length - 1; i++) { | ||
const zone = zones[i]; | ||
zone.nbPoints = Math.round(zone.nbPoints * ratio); | ||
currentTotal += zone.nbPoints; | ||
} | ||
zones[zones.length - 1].nbPoints = nbPoints - currentTotal; | ||
} else { | ||
let newX = new Float64Array(totalPoints); | ||
let newY = new Float64Array(totalPoints); | ||
let index = 0; | ||
for (let zone of zones) { | ||
for (let i = zone.fromIndex; i < zone.toIndex + 1; i++) { | ||
newX[index] = x[i]; | ||
newY[index] = y[i]; | ||
index++; | ||
} | ||
} | ||
return { | ||
x: x.slice(fromIndex, toIndex + 1), | ||
y: y.slice(fromIndex, toIndex + 1), | ||
x: newX, | ||
y: newY, | ||
}; | ||
} | ||
let newX = [x[fromIndex]]; | ||
let newY = [y[fromIndex]]; | ||
let minY = Number.MAX_VALUE; | ||
let maxY = Number.MIN_VALUE; | ||
if (nbPoints % 2 === 0) { | ||
nbPoints = nbPoints / 2 + 1; | ||
} else { | ||
nbPoints = (nbPoints - 1) / 2 + 1; | ||
let newX = []; | ||
let newY = []; | ||
for (let zone of zones) { | ||
if (!zone.nbPoints) continue; | ||
appendFromTo(zone.fromIndex, zone.toIndex, zone.nbPoints); | ||
} | ||
return { x: newX, y: newY }; | ||
let slot = (x[toIndex] - x[fromIndex]) / (nbPoints - 1); | ||
let currentX = x[fromIndex] + slot; | ||
let first = true; | ||
for (let i = fromIndex + 1; i <= toIndex; i++) { | ||
if (first) { | ||
minY = y[i]; | ||
maxY = y[i]; | ||
first = false; | ||
function appendFromTo(fromIndex, toIndex, zoneNbPoints) { | ||
if (zoneNbPoints === 1) { | ||
newX.push(x[Math.round((toIndex - fromIndex) / 2)]); | ||
newY.push(y[Math.round((toIndex - fromIndex) / 2)]); | ||
return; | ||
} | ||
if (zoneNbPoints === 2) { | ||
newX.push(x[fromIndex], x[toIndex]); | ||
newY.push(y[fromIndex], y[toIndex]); | ||
return; | ||
} | ||
newX.push(x[fromIndex]); | ||
newY.push(y[fromIndex]); | ||
let minY = Number.MAX_VALUE; | ||
let maxY = Number.MIN_VALUE; | ||
if (zoneNbPoints % 2 === 0) { | ||
zoneNbPoints = zoneNbPoints / 2 + 1; | ||
} else { | ||
if (y[i] < minY) minY = y[i]; | ||
if (y[i] > maxY) maxY = y[i]; | ||
zoneNbPoints = (zoneNbPoints - 1) / 2 + 1; | ||
} | ||
if (x[i] >= currentX || i === toIndex) { | ||
if (optimize) { | ||
if (minY > newY[newX.length - 1]) ; else if (maxY < newY[newX.length - 1]) { | ||
// we can skip the intermediate value | ||
maxY = minY; | ||
// we will need to make some kind of min / max because there are too many points | ||
// we will always keep the first point and the last point | ||
let slot = (x[toIndex] - x[fromIndex]) / (zoneNbPoints - 1); | ||
let currentX = x[fromIndex] + slot; | ||
let first = true; | ||
for (let i = fromIndex + 1; i <= toIndex; i++) { | ||
if (first) { | ||
minY = y[i]; | ||
maxY = y[i]; | ||
first = false; | ||
} else { | ||
if (y[i] < minY) minY = y[i]; | ||
if (y[i] > maxY) maxY = y[i]; | ||
} | ||
if (x[i] >= currentX || i === toIndex) { | ||
if (optimize) { | ||
if (minY > newY[newX.length - 1]) ; else if (maxY < newY[newX.length - 1]) { | ||
// we can skip the intermediate value | ||
maxY = minY; | ||
} else { | ||
newX.push(currentX - slot / 2); | ||
newY.push(minY); | ||
} | ||
} else { | ||
@@ -296,19 +447,11 @@ newX.push(currentX - slot / 2); | ||
} | ||
} else { | ||
newX.push(currentX - slot / 2); | ||
newY.push(minY); | ||
} | ||
newX.push(currentX); | ||
newY.push(maxY); | ||
newX.push(currentX); | ||
newY.push(maxY); | ||
currentX += slot; | ||
first = true; | ||
currentX += slot; | ||
first = true; | ||
} | ||
} | ||
} | ||
// we will need to make some kind of min / max because there are too many points | ||
// we will always keep the first point and the last point | ||
return { x: newX, y: newY }; | ||
} | ||
@@ -653,2 +796,3 @@ | ||
check, | ||
extract, | ||
integral, | ||
@@ -877,2 +1021,92 @@ integration, | ||
/** | ||
* This function multiply the first array by the second array or a constant value to each element of the first array | ||
* @param {Array} array1 - the array that will be rotated | ||
* @param {Array|Number} array2 | ||
* @return {Float64Array} | ||
*/ | ||
function multiply(array1, array2) { | ||
let isConstant = false; | ||
let constant; | ||
if (Array.isArray(array2)) { | ||
if (array1.length !== array2.length) { | ||
throw new Error('sub: size of array1 and array2 must be identical'); | ||
} | ||
} else { | ||
isConstant = true; | ||
constant = Number(array2); | ||
} | ||
let array3 = new Float64Array(array1.length); | ||
if (isConstant) { | ||
for (let i = 0; i < array1.length; i++) { | ||
array3[i] = array1[i] * constant; | ||
} | ||
} else { | ||
for (let i = 0; i < array1.length; i++) { | ||
array3[i] = array1[i] * array2[i]; | ||
} | ||
} | ||
return array3; | ||
} | ||
function dotProduct(A, B) { | ||
let g = multiply(A, B); | ||
let result = 0; | ||
for (let i = 0; i < A.length; i++) { | ||
result += g[i]; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Calculates the cross-correlation between 2 vectors | ||
* @param {Array} [A] - fixed array | ||
* @param {Array} [B] - sweeping array | ||
* @param {object} [options={}] | ||
* @param {number} [options.tau = 1] - sweep increment size (in number of points, min = 1, max = A.length) | ||
* @param {number} [options.lag = A.length - 1] - scalar lag parameter | ||
*/ | ||
function crossCorrelation(A, B, options = {}) { | ||
let { tau = 1, lag = A.length - 1 } = options; | ||
let result = new Float64Array(1 + (2 * lag) / tau); | ||
if (A.length === B.length) { | ||
let n = B.length; | ||
let g = new Float64Array(2 * n); | ||
let q = new Float64Array(2 * n); | ||
for (let i = 0; i < n; i++) { | ||
q[n + i] = B[i]; | ||
} | ||
for (let i = n * 2 - (tau - 1); i > 0; i -= tau) { | ||
let k = 0; | ||
for (let j = i; j < n * 2; j++) { | ||
g[k] = q[j]; | ||
k++; | ||
} | ||
let w = []; | ||
for (let l = 0; l < n; l++) { | ||
w[l] = g[l]; | ||
} | ||
result[(k - (n - lag)) / tau] = dotProduct(A, w); | ||
} | ||
} | ||
return result; | ||
} | ||
/** | ||
* Calculates the auto-correlation of a vector | ||
* @param {Array} [A] - the array that will be fixed | ||
* @param {object} [options={}] | ||
* @param {number} [options.tau = 1] - sweep increment size (in number of points, min = 1, max = A.length) | ||
* @param {number} [options.lag = A.length - 1] - scalar lag parameter | ||
*/ | ||
function autoCorrelation(A, options = {}) { | ||
return crossCorrelation(A, A, options); | ||
} | ||
/** | ||
/** | ||
* Calculates the correlation between 2 vectors | ||
@@ -942,36 +1176,2 @@ * https://en.wikipedia.org/wiki/Correlation_and_dependence | ||
/** | ||
/** | ||
* This function multiply the first array by the second array or a constant value to each element of the first array | ||
* @param {Array} array1 - the array that will be rotated | ||
* @param {Array|Number} array2 | ||
* @return {Array} | ||
*/ | ||
function multiply(array1, array2) { | ||
let isConstant = false; | ||
let constant; | ||
if (Array.isArray(array2)) { | ||
if (array1.length !== array2.length) { | ||
throw new Error('sub: size of array1 and array2 must be identical'); | ||
} | ||
} else { | ||
isConstant = true; | ||
constant = Number(array2); | ||
} | ||
let array3 = new Array(array1.length); | ||
if (isConstant) { | ||
for (let i = 0; i < array1.length; i++) { | ||
array3[i] = array1[i] * constant; | ||
} | ||
} else { | ||
for (let i = 0; i < array1.length; i++) { | ||
array3[i] = array1[i] * array2[i]; | ||
} | ||
} | ||
return array3; | ||
} | ||
/** | ||
* This function performs a circular shift to a new array | ||
@@ -1027,4 +1227,6 @@ * Positive values of shifts will shift to the right and negative values will do to the left | ||
add, | ||
autoCorrelation, | ||
boxPlot, | ||
correlation, | ||
crossCorrelation, | ||
divide, | ||
@@ -1128,3 +1330,8 @@ findClosestIndex, | ||
const Util = { | ||
normalizeZones, | ||
}; | ||
exports.ReIm = ReIm; | ||
exports.Util = Util; | ||
exports.X = X; | ||
@@ -1131,0 +1338,0 @@ exports.XReIm = XReIm; |
{ | ||
"name": "ml-spectra-processing", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"description": "Various method to process spectra", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -6,1 +6,2 @@ export { XY } from './xy/index.js'; | ||
export { XYObject } from './xyObject/index.js'; | ||
export { Util } from './util/index.js'; |
import { add } from './add'; | ||
import { boxPlot } from './boxPlot'; | ||
import { autoCorrelation } from './autoCorrelation'; | ||
import { crossCorrelation } from './crossCorrelation'; | ||
import { correlation } from './correlation'; | ||
@@ -14,4 +16,6 @@ import { divide } from './divide'; | ||
add, | ||
autoCorrelation, | ||
boxPlot, | ||
correlation, | ||
crossCorrelation, | ||
divide, | ||
@@ -18,0 +22,0 @@ findClosestIndex, |
@@ -7,3 +7,3 @@ /** | ||
* @param {Array|Number} array2 | ||
* @return {Array} | ||
* @return {Float64Array} | ||
*/ | ||
@@ -22,3 +22,3 @@ export function multiply(array1, array2) { | ||
let array3 = new Array(array1.length); | ||
let array3 = new Float64Array(array1.length); | ||
if (isConstant) { | ||
@@ -25,0 +25,0 @@ for (let i = 0; i < array1.length; i++) { |
import { check } from './check'; | ||
import { extract } from './extract'; | ||
import { integration } from './integration'; | ||
@@ -20,2 +21,3 @@ import { integral } from './integral'; | ||
check, | ||
extract, | ||
integral, | ||
@@ -22,0 +24,0 @@ integration, |
import { findClosestIndex } from '../x/findClosestIndex'; | ||
import { normalizeZones } from '../util/normalizeZones'; | ||
import { check } from './check'; | ||
/** | ||
* Reduce the number of points while keeping visually the same noise. Practical to | ||
* display many spectra as SVG | ||
* @param {array} x | ||
* @param {array} y | ||
* @param {object} [points={}] - Object of points contains property x (an ordered increasing array) and y (an array) | ||
* @param {object} [options={}] | ||
@@ -12,6 +13,9 @@ * @param {number} [options.from=x[0]] | ||
* @param {number} [options.nbPoints=4001] Number of points | ||
* @param {number} [options.zones=[]] Array of zones to keep (from/to object) | ||
* @param {number} [options.optimize=false] If optimize we may have less than nbPoints at the end | ||
*/ | ||
export function reduce(x, y, options = {}) { | ||
export function reduce(points, options = {}) { | ||
check(points); | ||
const { x, y } = points; | ||
let { | ||
@@ -22,46 +26,106 @@ from = x[0], | ||
optimize = false, | ||
zones = [], | ||
} = options; | ||
let fromIndex = findClosestIndex(x, from); | ||
let toIndex = findClosestIndex(x, to); | ||
zones = normalizeZones(zones, { from, to }); | ||
if (zones.length === 0) zones = [{ from, to }]; // we take everything | ||
if (fromIndex > 0 && x[fromIndex] > from) fromIndex--; | ||
if (toIndex < x.length - 1 && x[toIndex] < to) toIndex++; | ||
// for each zone we should know the first index, the last index and the number of points | ||
if (toIndex - fromIndex < nbPoints) { | ||
let totalPoints = 0; | ||
for (let zone of zones) { | ||
zone.fromIndex = findClosestIndex(x, zone.from); | ||
zone.toIndex = findClosestIndex(x, zone.to); | ||
if (zone.fromIndex > 0 && x[zone.fromIndex] > zone.from) { | ||
zone.fromIndex--; | ||
} | ||
if (zone.toIndex < x.length - 1 && x[zone.toIndex] < zone.to) { | ||
zone.toIndex++; | ||
} | ||
zone.nbPoints = zone.toIndex - zone.fromIndex + 1; | ||
totalPoints += zone.nbPoints; | ||
} | ||
// we calculate the number of points per zone that we should keep | ||
if (totalPoints > nbPoints) { | ||
// need to reduce number of points | ||
let ratio = nbPoints / totalPoints; | ||
let currentTotal = 0; | ||
for (let i = 0; i < zones.length - 1; i++) { | ||
const zone = zones[i]; | ||
zone.nbPoints = Math.round(zone.nbPoints * ratio); | ||
currentTotal += zone.nbPoints; | ||
} | ||
zones[zones.length - 1].nbPoints = nbPoints - currentTotal; | ||
} else { | ||
let newX = new Float64Array(totalPoints); | ||
let newY = new Float64Array(totalPoints); | ||
let index = 0; | ||
for (let zone of zones) { | ||
for (let i = zone.fromIndex; i < zone.toIndex + 1; i++) { | ||
newX[index] = x[i]; | ||
newY[index] = y[i]; | ||
index++; | ||
} | ||
} | ||
return { | ||
x: x.slice(fromIndex, toIndex + 1), | ||
y: y.slice(fromIndex, toIndex + 1), | ||
x: newX, | ||
y: newY, | ||
}; | ||
} | ||
let newX = [x[fromIndex]]; | ||
let newY = [y[fromIndex]]; | ||
let minY = Number.MAX_VALUE; | ||
let maxY = Number.MIN_VALUE; | ||
if (nbPoints % 2 === 0) { | ||
nbPoints = nbPoints / 2 + 1; | ||
} else { | ||
nbPoints = (nbPoints - 1) / 2 + 1; | ||
let newX = []; | ||
let newY = []; | ||
for (let zone of zones) { | ||
if (!zone.nbPoints) continue; | ||
appendFromTo(zone.fromIndex, zone.toIndex, zone.nbPoints); | ||
} | ||
return { x: newX, y: newY }; | ||
let slot = (x[toIndex] - x[fromIndex]) / (nbPoints - 1); | ||
let currentX = x[fromIndex] + slot; | ||
let first = true; | ||
for (let i = fromIndex + 1; i <= toIndex; i++) { | ||
if (first) { | ||
minY = y[i]; | ||
maxY = y[i]; | ||
first = false; | ||
function appendFromTo(fromIndex, toIndex, zoneNbPoints) { | ||
if (zoneNbPoints === 1) { | ||
newX.push(x[Math.round((toIndex - fromIndex) / 2)]); | ||
newY.push(y[Math.round((toIndex - fromIndex) / 2)]); | ||
return; | ||
} | ||
if (zoneNbPoints === 2) { | ||
newX.push(x[fromIndex], x[toIndex]); | ||
newY.push(y[fromIndex], y[toIndex]); | ||
return; | ||
} | ||
newX.push(x[fromIndex]); | ||
newY.push(y[fromIndex]); | ||
let minY = Number.MAX_VALUE; | ||
let maxY = Number.MIN_VALUE; | ||
if (zoneNbPoints % 2 === 0) { | ||
zoneNbPoints = zoneNbPoints / 2 + 1; | ||
} else { | ||
if (y[i] < minY) minY = y[i]; | ||
if (y[i] > maxY) maxY = y[i]; | ||
zoneNbPoints = (zoneNbPoints - 1) / 2 + 1; | ||
} | ||
if (x[i] >= currentX || i === toIndex) { | ||
if (optimize) { | ||
if (minY > newY[newX.length - 1]) { | ||
// we can skip the intermediate value | ||
} else if (maxY < newY[newX.length - 1]) { | ||
// we can skip the intermediate value | ||
maxY = minY; | ||
// we will need to make some kind of min / max because there are too many points | ||
// we will always keep the first point and the last point | ||
let slot = (x[toIndex] - x[fromIndex]) / (zoneNbPoints - 1); | ||
let currentX = x[fromIndex] + slot; | ||
let first = true; | ||
for (let i = fromIndex + 1; i <= toIndex; i++) { | ||
if (first) { | ||
minY = y[i]; | ||
maxY = y[i]; | ||
first = false; | ||
} else { | ||
if (y[i] < minY) minY = y[i]; | ||
if (y[i] > maxY) maxY = y[i]; | ||
} | ||
if (x[i] >= currentX || i === toIndex) { | ||
if (optimize) { | ||
if (minY > newY[newX.length - 1]) { | ||
// we can skip the intermediate value | ||
} else if (maxY < newY[newX.length - 1]) { | ||
// we can skip the intermediate value | ||
maxY = minY; | ||
} else { | ||
newX.push(currentX - slot / 2); | ||
newY.push(minY); | ||
} | ||
} else { | ||
@@ -71,19 +135,11 @@ newX.push(currentX - slot / 2); | ||
} | ||
} else { | ||
newX.push(currentX - slot / 2); | ||
newY.push(minY); | ||
} | ||
newX.push(currentX); | ||
newY.push(maxY); | ||
newX.push(currentX); | ||
newY.push(maxY); | ||
currentX += slot; | ||
first = true; | ||
currentX += slot; | ||
first = true; | ||
} | ||
} | ||
} | ||
// we will need to make some kind of min / max because there are too many points | ||
// we will always keep the first point and the last point | ||
return { x: newX, y: newY }; | ||
} |
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
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
82929
51
2435