plotly.js
Version:
The open source javascript graphing library that powers plotly
278 lines (229 loc) • 7.42 kB
JavaScript
var b64decode = require('base64-arraybuffer').decode;
var isPlainObject = require('./is_plain_object');
var isArray = Array.isArray;
var ab = ArrayBuffer;
var dv = DataView;
function isTypedArray(a) {
return ab.isView(a) && !(a instanceof dv);
}
exports.isTypedArray = isTypedArray;
function isArrayOrTypedArray(a) {
return isArray(a) || isTypedArray(a);
}
exports.isArrayOrTypedArray = isArrayOrTypedArray;
/*
* Test whether an input object is 1D.
*
* Assumes we already know the object is an array.
*
* Looks only at the first element, if the dimensionality is
* not consistent we won't figure that out here.
*/
function isArray1D(a) {
return !isArrayOrTypedArray(a[0]);
}
exports.isArray1D = isArray1D;
/*
* Ensures an array has the right amount of storage space. If it doesn't
* exist, it creates an array. If it does exist, it returns it if too
* short or truncates it in-place.
*
* The goal is to just reuse memory to avoid a bit of excessive garbage
* collection.
*/
exports.ensureArray = function(out, n) {
// TODO: typed array support here? This is only used in
// traces/carpet/compute_control_points
if(!isArray(out)) out = [];
// If too long, truncate. (If too short, it will grow
// automatically so we don't care about that case)
out.length = n;
return out;
};
var typedArrays = {
u1c: typeof Uint8ClampedArray === 'undefined' ? undefined :
Uint8ClampedArray, // not supported in numpy?
i1: typeof Int8Array === 'undefined' ? undefined :
Int8Array,
u1: typeof Uint8Array === 'undefined' ? undefined :
Uint8Array,
i2: typeof Int16Array === 'undefined' ? undefined :
Int16Array,
u2: typeof Uint16Array === 'undefined' ? undefined :
Uint16Array,
i4: typeof Int32Array === 'undefined' ? undefined :
Int32Array,
u4: typeof Uint32Array === 'undefined' ? undefined :
Uint32Array,
f4: typeof Float32Array === 'undefined' ? undefined :
Float32Array,
f8: typeof Float64Array === 'undefined' ? undefined :
Float64Array,
/* TODO: potentially add Big Int
i8: typeof BigInt64Array === 'undefined' ? undefined :
BigInt64Array,
u8: typeof BigUint64Array === 'undefined' ? undefined :
BigUint64Array,
*/
};
typedArrays.uint8c = typedArrays.u1c;
typedArrays.uint8 = typedArrays.u1;
typedArrays.int8 = typedArrays.i1;
typedArrays.uint16 = typedArrays.u2;
typedArrays.int16 = typedArrays.i2;
typedArrays.uint32 = typedArrays.u4;
typedArrays.int32 = typedArrays.i4;
typedArrays.float32 = typedArrays.f4;
typedArrays.float64 = typedArrays.f8;
function isArrayBuffer(a) {
return a.constructor === ArrayBuffer;
}
exports.isArrayBuffer = isArrayBuffer;
exports.decodeTypedArraySpec = function(vIn) {
var out = [];
var v = coerceTypedArraySpec(vIn);
var dtype = v.dtype;
var T = typedArrays[dtype];
if(!T) throw new Error('Error in dtype: "' + dtype + '"');
var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT;
var buffer = v.bdata;
if(!isArrayBuffer(buffer)) {
buffer = b64decode(buffer);
}
var shape = v.shape === undefined ?
// detect 1-d length
[buffer.byteLength / BYTES_PER_ELEMENT] :
// convert number to string and split to array
('' + v.shape).split(',');
shape.reverse(); // i.e. to match numpy order
var ndim = shape.length;
var nj, j;
var ni = +shape[0];
var rowBytes = BYTES_PER_ELEMENT * ni;
var pos = 0;
if(ndim === 1) {
out = new T(buffer);
} else if(ndim === 2) {
nj = +shape[1];
for(j = 0; j < nj; j++) {
out[j] = new T(buffer, pos, ni);
pos += rowBytes;
}
} else if(ndim === 3) {
nj = +shape[1];
var nk = +shape[2];
for(var k = 0; k < nk; k++) {
out[k] = [];
for(j = 0; j < nj; j++) {
out[k][j] = new T(buffer, pos, ni);
pos += rowBytes;
}
}
} else {
throw new Error('ndim: ' + ndim + 'is not supported with the shape:"' + v.shape + '"');
}
// attach bdata, dtype & shape to array for json export
out.bdata = v.bdata;
out.dtype = v.dtype;
out.shape = shape.reverse().join(',');
vIn._inputArray = out;
return out;
};
exports.isTypedArraySpec = function(v) {
return (
isPlainObject(v) &&
v.hasOwnProperty('dtype') && (typeof v.dtype === 'string') &&
v.hasOwnProperty('bdata') && (typeof v.bdata === 'string' || isArrayBuffer(v.bdata)) &&
(v.shape === undefined || (
v.hasOwnProperty('shape') && (typeof v.shape === 'string' || typeof v.shape === 'number')
))
);
};
function coerceTypedArraySpec(v) {
return {
bdata: v.bdata,
dtype: v.dtype,
shape: v.shape
};
}
/*
* TypedArray-compatible concatenation of n arrays
* if all arrays are the same type it will preserve that type,
* otherwise it falls back on Array.
* Also tries to avoid copying, in case one array has zero length
* But never mutates an existing array
*/
exports.concat = function() {
var args = [];
var allArray = true;
var totalLen = 0;
var _constructor, arg0, i, argi, posi, leni, out, j;
for(i = 0; i < arguments.length; i++) {
argi = arguments[i];
leni = argi.length;
if(leni) {
if(arg0) args.push(argi);
else {
arg0 = argi;
posi = leni;
}
if(isArray(argi)) {
_constructor = false;
} else {
allArray = false;
if(!totalLen) {
_constructor = argi.constructor;
} else if(_constructor !== argi.constructor) {
// TODO: in principle we could upgrade here,
// ie keep typed array but convert all to Float64Array?
_constructor = false;
}
}
totalLen += leni;
}
}
if(!totalLen) return [];
if(!args.length) return arg0;
if(allArray) return arg0.concat.apply(arg0, args);
if(_constructor) {
// matching typed arrays
out = new _constructor(totalLen);
out.set(arg0);
for(i = 0; i < args.length; i++) {
argi = args[i];
out.set(argi, posi);
posi += argi.length;
}
return out;
}
// mismatched types or Array + typed
out = new Array(totalLen);
for(j = 0; j < arg0.length; j++) out[j] = arg0[j];
for(i = 0; i < args.length; i++) {
argi = args[i];
for(j = 0; j < argi.length; j++) out[posi + j] = argi[j];
posi += j;
}
return out;
};
exports.maxRowLength = function(z) {
return _rowLength(z, Math.max, 0);
};
exports.minRowLength = function(z) {
return _rowLength(z, Math.min, Infinity);
};
function _rowLength(z, fn, len0) {
if(isArrayOrTypedArray(z)) {
if(isArrayOrTypedArray(z[0])) {
var len = len0;
for(var i = 0; i < z.length; i++) {
len = fn(len, z[i].length);
}
return len;
} else {
return z.length;
}
}
return 0;
}
;