From dc9f4e59778f23721437b943688fcb08f83b6b42 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 5 Aug 2020 12:18:35 -0400 Subject: [PATCH 001/122] accept objects for encoded typedarrays in data_array valType - move decoding step to the start of calc data --- .eslintrc | 6 ++++ package-lock.json | 21 +++++++++++--- package.json | 1 + src/lib/coerce.js | 37 +++++++++++++++++++++---- src/plots/plots.js | 44 ++++++++++++++++++++++++++++++ src/traces/heatmap/xyz_defaults.js | 16 +++++++---- src/traces/isosurface/defaults.js | 18 ++++++++---- src/traces/scatter/xy_defaults.js | 11 +++++--- src/traces/scatter3d/defaults.js | 6 +++- 9 files changed, 135 insertions(+), 25 deletions(-) diff --git a/.eslintrc b/.eslintrc index dffe72f974e..4cb350f6803 100644 --- a/.eslintrc +++ b/.eslintrc @@ -14,8 +14,14 @@ "Float32Array": true, "Float64Array": true, "Uint8Array": true, + "Int8Array": true, + "Uint8ClampedArray": true, "Int16Array": true, + "Uint16Array": true, "Int32Array": true, + "Uint32Array": true, + "BigInt64Array": true, + "BigUint64Array": true, "ArrayBuffer": true, "DataView": true, "SVGElement": false diff --git a/package-lock.json b/package-lock.json index 09c442ac104..bcbb0409ba1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -942,10 +942,9 @@ } }, "base64-arraybuffer": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", - "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", - "dev": true + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", + "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==" }, "base64-js": { "version": "1.3.1", @@ -3616,6 +3615,14 @@ "base64-arraybuffer": "0.1.5", "blob": "0.0.5", "has-binary2": "~1.0.2" + }, + "dependencies": { + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + } } }, "enhanced-resolve": { @@ -10117,6 +10124,12 @@ "to-array": "0.1.4" }, "dependencies": { + "base64-arraybuffer": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", + "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=", + "dev": true + }, "isarray": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", diff --git a/package.json b/package.json index e953fa40921..cb822e29c71 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@turf/bbox": "^6.0.1", "@turf/centroid": "^6.0.2", "alpha-shape": "^1.0.0", + "base64-arraybuffer": "^0.2.0", "canvas-fit": "^1.5.0", "color-alpha": "1.0.4", "color-normalize": "1.5.0", diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 108370cfe40..4ff5465d3cd 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -18,22 +18,49 @@ var DESELECTDIM = require('../constants/interactions').DESELECTDIM; var nestedProperty = require('./nested_property'); var counterRegex = require('./regex').counter; var modHalf = require('./mod').modHalf; +var isPlainObject = require('./is_plain_object'); var isArrayOrTypedArray = require('./array').isArrayOrTypedArray; +var typedArrays = { + int8: typeof Int8Array !== 'undefined' ? 1 : 0, + uint8: typeof Uint8Array !== 'undefined' ? 1 : 0, + uint8clamped: typeof Uint8ClampedArray !== 'undefined' ? 1 : 0, + int16: typeof Int16Array !== 'undefined' ? 1 : 0, + uint16: typeof Uint16Array !== 'undefined' ? 1 : 0, + int32: typeof Int32Array !== 'undefined' ? 1 : 0, + uint32: typeof Uint32Array !== 'undefined' ? 1 : 0, + float32: typeof Float32Array !== 'undefined' ? 1 : 0, + float64: typeof Float64Array !== 'undefined' ? 1 : 0, + bigint64: typeof BigInt64Array !== 'undefined' ? 1 : 0, + biguint64: typeof BigUint64Array !== 'undefined' ? 1 : 0 +}; + exports.valObjectMeta = { data_array: { // You can use *dflt=[] to force said array to exist though. description: [ 'An {array} of data.', - 'The value MUST be an {array}, or we ignore it.', - 'Note that typed arrays (e.g. Float32Array) are supported.' + 'The value could be an {array}', + 'noting that typed arrays (e.g. Float32Array) are also supported.', + 'It could also be an object in the form of', + 'v: {, dtype: \'float32\', bvals: [/* ... */]}, shape: [dim0 (, dim1, (dim3))]', + 'otherwise, it would be ignored.' ].join(' '), requiredOpts: [], otherOpts: ['dflt'], coerceFunction: function(v, propOut, dflt) { - // TODO maybe `v: {type: 'float32', vals: [/* ... */]}` also - if(isArrayOrTypedArray(v)) propOut.set(v); - else if(dflt !== undefined) propOut.set(dflt); + var wasSet; + if(isArrayOrTypedArray(v)) { + propOut.set(v); + wasSet = true; + } else if(isPlainObject(v)) { + var T = typedArrays[v.dtype]; + if(T) { + propOut.set(v); + wasSet = true; + } + } + if(!wasSet && dflt !== undefined) propOut.set(dflt); } }, enumerated: { diff --git a/src/plots/plots.js b/src/plots/plots.js index c28b8f3df72..9c10b2cf1e9 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -11,6 +11,7 @@ var d3 = require('d3'); var timeFormatLocale = require('d3-time-format').timeFormatLocale; var isNumeric = require('fast-isnumeric'); +var b64 = require('base64-arraybuffer'); var Registry = require('../registry'); var PlotSchema = require('../plot_api/plot_schema'); @@ -2848,7 +2849,50 @@ function _transition(gd, transitionOpts, opts) { return transitionStarting.then(function() { return gd; }); } +var typedArrays = { + int8: typeof Int8Array !== 'undefined' ? Int8Array : null, + uint8: typeof Uint8Array !== 'undefined' ? Uint8Array : null, + uint8clamped: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : null, + int16: typeof Int16Array !== 'undefined' ? Int16Array : null, + uint16: typeof Uint16Array !== 'undefined' ? Uint16Array : null, + int32: typeof Int32Array !== 'undefined' ? Int32Array : null, + uint32: typeof Uint32Array !== 'undefined' ? Uint32Array : null, + float32: typeof Float32Array !== 'undefined' ? Float32Array : null, + float64: typeof Float64Array !== 'undefined' ? Float64Array : null, + bigint64: typeof BigInt64Array !== 'undefined' ? BigInt64Array : null, + biguint64: typeof BigUint64Array !== 'undefined' ? BigUint64Array : null +}; + +function _decode(cont) { + if(cont.dtype && cont.bvals) { + var T = typedArrays[cont.dtype]; + if(T) { + return new T(b64.decode(cont.bvals)); + } + } + + for(var prop in cont) { + if(prop[0] !== '_' && cont.hasOwnProperty(prop)) { + var item = cont[prop]; + if(Lib.isPlainObject(item)) { + var r = _decode(item); + if(r !== undefined) cont[prop] = r; + } + } + } +} + +function decodeB64Arrays(gd) { + for(var i = 0; i < gd._fullData.length; i++) { + _decode(gd._fullData[i]); + } + + _decode(gd._fullLayout); +} + plots.doCalcdata = function(gd, traces) { + decodeB64Arrays(gd); + var axList = axisIDs.list(gd); var fullData = gd._fullData; var fullLayout = gd._fullLayout; diff --git a/src/traces/heatmap/xyz_defaults.js b/src/traces/heatmap/xyz_defaults.js index 8be45ca87f0..5618b4a35b3 100644 --- a/src/traces/heatmap/xyz_defaults.js +++ b/src/traces/heatmap/xyz_defaults.js @@ -19,19 +19,25 @@ module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout, x yName = yName || 'y'; var x, y; - if(z === undefined || !z.length) return 0; + var shapeX = x ? (x.shape ? x.shape[0] : x.length) || 0 : 0; + var shapeY = y ? (y.shape ? y.shape[0] : y.length) || 0 : 0; + var shapeZ = z ? (z.shape ? z.shape[0] : z.length) || 0 : 0; - if(Lib.isArray1D(traceIn.z)) { + var zlen = shapeZ || (z && z.length) || 0; + + if(z === undefined || !zlen) return 0; + + if(Lib.isArray1D(traceIn.z) || (z && z.shape && z.shape.length === 1)) { x = coerce(xName); y = coerce(yName); - var xlen = Lib.minRowLength(x); - var ylen = Lib.minRowLength(y); + var xlen = shapeX || Lib.minRowLength(x); + var ylen = shapeY || Lib.minRowLength(y); // column z must be accompanied by xName and yName arrays if(xlen === 0 || ylen === 0) return 0; - traceOut._length = Math.min(xlen, ylen, z.length); + traceOut._length = Math.min(xlen, ylen, zlen); } else { x = coordDefaults(xName, coerce); y = coordDefaults(yName, coerce); diff --git a/src/traces/isosurface/defaults.js b/src/traces/isosurface/defaults.js index c8b9916c86e..0ce5fc342b8 100644 --- a/src/traces/isosurface/defaults.js +++ b/src/traces/isosurface/defaults.js @@ -38,12 +38,18 @@ function supplyIsoDefaults(traceIn, traceOut, defaultColor, layout, coerce) { var z = coerce('z'); var value = coerce('value'); - if( - !x || !x.length || - !y || !y.length || - !z || !z.length || - !value || !value.length - ) { + var len = 0; + + if(x && y && z && value) { + len = Math.min( + (x.shape ? x.shape[0] : x.length) || 0, + (y.shape ? y.shape[0] : y.length) || 0, + (z.shape ? z.shape[0] : z.length) || 0, + (value.shape ? value.shape[0] : value.length) || 0 + ); + } + + if(!len) { traceOut.visible = false; return; } diff --git a/src/traces/scatter/xy_defaults.js b/src/traces/scatter/xy_defaults.js index 5c8f7ace4ef..697472f2d21 100644 --- a/src/traces/scatter/xy_defaults.js +++ b/src/traces/scatter/xy_defaults.js @@ -19,19 +19,22 @@ module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); + var shapeX = x && x.shape ? x.shape[0] : 0; + var shapeY = y && y.shape ? y.shape[0] : 0; + if(x) { - var xlen = Lib.minRowLength(x); + var xlen = shapeX || Lib.minRowLength(x); if(y) { - len = Math.min(xlen, Lib.minRowLength(y)); + len = shapeY || Math.min(xlen, Lib.minRowLength(y)); } else { - len = xlen; + len = shapeX || xlen; coerce('y0'); coerce('dy'); } } else { if(!y) return 0; - len = Lib.minRowLength(y); + len = shapeY || Lib.minRowLength(y); coerce('x0'); coerce('dx'); } diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js index 0c1b1cf8274..5be93835de4 100644 --- a/src/traces/scatter3d/defaults.js +++ b/src/traces/scatter3d/defaults.js @@ -78,8 +78,12 @@ function handleXYZDefaults(traceIn, traceOut, coerce, layout) { handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout); if(x && y && z) { + var shapeX = (x.shape ? x.shape[0] : x.length) || 0; + var shapeY = (y.shape ? y.shape[0] : y.length) || 0; + var shapeZ = (z.shape ? z.shape[0] : z.length) || 0; + // TODO: what happens if one is missing? - len = Math.min(x.length, y.length, z.length); + len = Math.min(shapeX, shapeY, shapeZ); traceOut._length = traceOut._xlength = traceOut._ylength = traceOut._zlength = len; } From 67ccac94e30ceb9063dd4db6edf469b55988c0a4 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 28 Nov 2020 09:34:48 -0500 Subject: [PATCH 002/122] Revert trace changes --- src/traces/heatmap/xyz_defaults.js | 16 +++++----------- src/traces/isosurface/defaults.js | 18 ++++++------------ src/traces/scatter/xy_defaults.js | 11 ++++------- src/traces/scatter3d/defaults.js | 6 +----- 4 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/traces/heatmap/xyz_defaults.js b/src/traces/heatmap/xyz_defaults.js index 5618b4a35b3..8be45ca87f0 100644 --- a/src/traces/heatmap/xyz_defaults.js +++ b/src/traces/heatmap/xyz_defaults.js @@ -19,25 +19,19 @@ module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout, x yName = yName || 'y'; var x, y; - var shapeX = x ? (x.shape ? x.shape[0] : x.length) || 0 : 0; - var shapeY = y ? (y.shape ? y.shape[0] : y.length) || 0 : 0; - var shapeZ = z ? (z.shape ? z.shape[0] : z.length) || 0 : 0; + if(z === undefined || !z.length) return 0; - var zlen = shapeZ || (z && z.length) || 0; - - if(z === undefined || !zlen) return 0; - - if(Lib.isArray1D(traceIn.z) || (z && z.shape && z.shape.length === 1)) { + if(Lib.isArray1D(traceIn.z)) { x = coerce(xName); y = coerce(yName); - var xlen = shapeX || Lib.minRowLength(x); - var ylen = shapeY || Lib.minRowLength(y); + var xlen = Lib.minRowLength(x); + var ylen = Lib.minRowLength(y); // column z must be accompanied by xName and yName arrays if(xlen === 0 || ylen === 0) return 0; - traceOut._length = Math.min(xlen, ylen, zlen); + traceOut._length = Math.min(xlen, ylen, z.length); } else { x = coordDefaults(xName, coerce); y = coordDefaults(yName, coerce); diff --git a/src/traces/isosurface/defaults.js b/src/traces/isosurface/defaults.js index 0ce5fc342b8..c8b9916c86e 100644 --- a/src/traces/isosurface/defaults.js +++ b/src/traces/isosurface/defaults.js @@ -38,18 +38,12 @@ function supplyIsoDefaults(traceIn, traceOut, defaultColor, layout, coerce) { var z = coerce('z'); var value = coerce('value'); - var len = 0; - - if(x && y && z && value) { - len = Math.min( - (x.shape ? x.shape[0] : x.length) || 0, - (y.shape ? y.shape[0] : y.length) || 0, - (z.shape ? z.shape[0] : z.length) || 0, - (value.shape ? value.shape[0] : value.length) || 0 - ); - } - - if(!len) { + if( + !x || !x.length || + !y || !y.length || + !z || !z.length || + !value || !value.length + ) { traceOut.visible = false; return; } diff --git a/src/traces/scatter/xy_defaults.js b/src/traces/scatter/xy_defaults.js index 697472f2d21..5c8f7ace4ef 100644 --- a/src/traces/scatter/xy_defaults.js +++ b/src/traces/scatter/xy_defaults.js @@ -19,22 +19,19 @@ module.exports = function handleXYDefaults(traceIn, traceOut, layout, coerce) { var handleCalendarDefaults = Registry.getComponentMethod('calendars', 'handleTraceDefaults'); handleCalendarDefaults(traceIn, traceOut, ['x', 'y'], layout); - var shapeX = x && x.shape ? x.shape[0] : 0; - var shapeY = y && y.shape ? y.shape[0] : 0; - if(x) { - var xlen = shapeX || Lib.minRowLength(x); + var xlen = Lib.minRowLength(x); if(y) { - len = shapeY || Math.min(xlen, Lib.minRowLength(y)); + len = Math.min(xlen, Lib.minRowLength(y)); } else { - len = shapeX || xlen; + len = xlen; coerce('y0'); coerce('dy'); } } else { if(!y) return 0; - len = shapeY || Lib.minRowLength(y); + len = Lib.minRowLength(y); coerce('x0'); coerce('dx'); } diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js index 5be93835de4..0c1b1cf8274 100644 --- a/src/traces/scatter3d/defaults.js +++ b/src/traces/scatter3d/defaults.js @@ -78,12 +78,8 @@ function handleXYZDefaults(traceIn, traceOut, coerce, layout) { handleCalendarDefaults(traceIn, traceOut, ['x', 'y', 'z'], layout); if(x && y && z) { - var shapeX = (x.shape ? x.shape[0] : x.length) || 0; - var shapeY = (y.shape ? y.shape[0] : y.length) || 0; - var shapeZ = (z.shape ? z.shape[0] : z.length) || 0; - // TODO: what happens if one is missing? - len = Math.min(shapeX, shapeY, shapeZ); + len = Math.min(x.length, y.length, z.length); traceOut._length = traceOut._xlength = traceOut._ylength = traceOut._zlength = len; } From 712a124ea14702629ae6aecbcbf32c8cb5d6dcbb Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 28 Nov 2020 13:23:40 -0500 Subject: [PATCH 003/122] WIP of decoding in calc. Works for initial render, but typed array is overwritten when supplyDefaults is run again. --- src/lib/array.js | 113 +++++++++++++++++++++++++++++- src/lib/coerce.js | 25 ++----- src/plots/plots.js | 33 +++------ src/traces/isosurface/defaults.js | 2 + 4 files changed, 129 insertions(+), 44 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index bda12db0795..a5121b943af 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -7,6 +7,8 @@ */ 'use strict'; +var b64 = require('base64-arraybuffer'); +var isPlainObject = require('./is_plain_object'); var isArray = Array.isArray; @@ -39,7 +41,7 @@ exports.isArrayOrTypedArray = isArrayOrTypedArray; * not consistent we won't figure that out here. */ function isArray1D(a) { - return !isArrayOrTypedArray(a[0]); + return !(isArrayOrTypedArray(a[0]) || (isTypedArraySpec(a) && a.ndims === 1)); } exports.isArray1D = isArray1D; @@ -63,6 +65,113 @@ exports.ensureArray = function(out, n) { return out; }; +var typedArrays = { + int8: typeof Int8Array !== 'undefined' ? Int8Array : null, + uint8: typeof Uint8Array !== 'undefined' ? Uint8Array : null, + uint8clamped: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : null, + int16: typeof Int16Array !== 'undefined' ? Int16Array : null, + uint16: typeof Uint16Array !== 'undefined' ? Uint16Array : null, + int32: typeof Int32Array !== 'undefined' ? Int32Array : null, + uint32: typeof Uint32Array !== 'undefined' ? Uint32Array : null, + float32: typeof Float32Array !== 'undefined' ? Float32Array : null, + float64: typeof Float64Array !== 'undefined' ? Float64Array : null, + bigint64: typeof BigInt64Array !== 'undefined' ? BigInt64Array : null, + biguint64: typeof BigUint64Array !== 'undefined' ? BigUint64Array : null +}; +exports.typedArrays = typedArrays; + + +exports.decodeTypedArraySpec = function(v) { + // Assume processed by coerceTypedArraySpec + var T = typedArrays[v.dtype]; + var buffer; + if(v.bvals.constructor === ArrayBuffer) { + // Already an ArrayBuffer + buffer = v.bvals; + } else { + // Decode, assuming a string + buffer = b64.decode(v.bvals); + } + + // Check if 1d shape. If so, we're done + if(v.ndims === 1) { + // Construct single Typed array over entire buffer + return new T(buffer); + } else { + // Reshape into nested plain arrays with innermost + // level containing typed arrays + // We could eventually adopt an ndarray library + + // Build cumulative product of dimensions + var cumulativeShape = v.shape.map(function(a, i) { + return a * (v.shape[i - 1] || 1); + }); + + // Loop of dimensions in reverse order + var nestedArray = []; + for(var dimInd = v.ndims - 1; dimInd > 0; dimInd--) { + var subArrayLength = v.shape[dimInd]; + var numSubArrays = cumulativeShape[dimInd - 1]; + var nextArray = []; + + if(dimInd === v.ndims - 1) { + // First time through, we build the + // inner most typed arrays + for(var typedInd = 0; typedInd < numSubArrays; typedInd++) { + var typedOffset = typedInd * subArrayLength; + nextArray.push( + new T(buffer, typedOffset * T.BYTES_PER_ELEMENT, subArrayLength) + ); + } + } else { + // Following times through, build + // next layer of nested arrays + for(var i = 0; i < numSubArrays; i++) { + var offset = i * subArrayLength; + nextArray.push(nextArray.slice(offset, offset + subArrayLength - 1)); + } + } + + // Update nested array with next nesting level + nestedArray = nextArray; + } + + return nestedArray; + } +}; + +function isTypedArraySpec(v) { + // Assume v has not passed through + return isPlainObject(v) && typedArrays[v.dtype] && v.bvals && ( + Number.isInteger(v.shape) || + (isArrayOrTypedArray(v.shape) && + v.shape.length > 0 && + v.shape.every(function(d) { return Number.isInteger(d); })) + ); +} +exports.isTypedArraySpec = isTypedArraySpec; + +function coerceTypedArraySpec(v) { + // Assume isTypedArraySpec passed + var coerced = {dtype: v.dtype, bvals: v.bvals}; + + // Normalize shape to a list + if(Number.isInteger(v.shape)) { + coerced.shape = [v.shape]; + } else { + coerced.shape = v.shape; + } + + // Add length property + coerced.length = v.shape.reduce(function(a, b) { return a * b; }); + + // Add ndims + coerced.ndims = v.shape.length; + + return coerced; +} +exports.coerceTypedArraySpec = coerceTypedArraySpec; + /* * TypedArray-compatible concatenation of n arrays * if all arrays are the same type it will preserve that type, @@ -150,6 +259,8 @@ function _rowLength(z, fn, len0) { } else { return z.length; } + } else if(isTypedArraySpec(z)) { + return z.shape[z.shape.length - 1]; } return 0; } diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 4ff5465d3cd..60229d6f19e 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -18,22 +18,10 @@ var DESELECTDIM = require('../constants/interactions').DESELECTDIM; var nestedProperty = require('./nested_property'); var counterRegex = require('./regex').counter; var modHalf = require('./mod').modHalf; -var isPlainObject = require('./is_plain_object'); var isArrayOrTypedArray = require('./array').isArrayOrTypedArray; +var isTypedArraySpec = require('./array').isTypedArraySpec; +var coerceTypedArraySpec = require('./array').coerceTypedArraySpec; -var typedArrays = { - int8: typeof Int8Array !== 'undefined' ? 1 : 0, - uint8: typeof Uint8Array !== 'undefined' ? 1 : 0, - uint8clamped: typeof Uint8ClampedArray !== 'undefined' ? 1 : 0, - int16: typeof Int16Array !== 'undefined' ? 1 : 0, - uint16: typeof Uint16Array !== 'undefined' ? 1 : 0, - int32: typeof Int32Array !== 'undefined' ? 1 : 0, - uint32: typeof Uint32Array !== 'undefined' ? 1 : 0, - float32: typeof Float32Array !== 'undefined' ? 1 : 0, - float64: typeof Float64Array !== 'undefined' ? 1 : 0, - bigint64: typeof BigInt64Array !== 'undefined' ? 1 : 0, - biguint64: typeof BigUint64Array !== 'undefined' ? 1 : 0 -}; exports.valObjectMeta = { data_array: { @@ -53,12 +41,9 @@ exports.valObjectMeta = { if(isArrayOrTypedArray(v)) { propOut.set(v); wasSet = true; - } else if(isPlainObject(v)) { - var T = typedArrays[v.dtype]; - if(T) { - propOut.set(v); - wasSet = true; - } + } else if(isTypedArraySpec(v)) { + propOut.set(coerceTypedArraySpec(v)); + wasSet = true; } if(!wasSet && dflt !== undefined) propOut.set(dflt); } diff --git a/src/plots/plots.js b/src/plots/plots.js index 9c10b2cf1e9..2643ff778b7 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -11,7 +11,8 @@ var d3 = require('d3'); var timeFormatLocale = require('d3-time-format').timeFormatLocale; var isNumeric = require('fast-isnumeric'); -var b64 = require('base64-arraybuffer'); +var decodeTypedArraySpec = require('../lib/array').decodeTypedArraySpec; +var isTypedArraySpec = require('../lib/array').isTypedArraySpec; var Registry = require('../registry'); var PlotSchema = require('../plot_api/plot_schema'); @@ -2849,40 +2850,26 @@ function _transition(gd, transitionOpts, opts) { return transitionStarting.then(function() { return gd; }); } -var typedArrays = { - int8: typeof Int8Array !== 'undefined' ? Int8Array : null, - uint8: typeof Uint8Array !== 'undefined' ? Uint8Array : null, - uint8clamped: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : null, - int16: typeof Int16Array !== 'undefined' ? Int16Array : null, - uint16: typeof Uint16Array !== 'undefined' ? Uint16Array : null, - int32: typeof Int32Array !== 'undefined' ? Int32Array : null, - uint32: typeof Uint32Array !== 'undefined' ? Uint32Array : null, - float32: typeof Float32Array !== 'undefined' ? Float32Array : null, - float64: typeof Float64Array !== 'undefined' ? Float64Array : null, - bigint64: typeof BigInt64Array !== 'undefined' ? BigInt64Array : null, - biguint64: typeof BigUint64Array !== 'undefined' ? BigUint64Array : null -}; - function _decode(cont) { - if(cont.dtype && cont.bvals) { - var T = typedArrays[cont.dtype]; - if(T) { - return new T(b64.decode(cont.bvals)); - } + if(isTypedArraySpec(cont)) { + return decodeTypedArraySpec(cont); } - for(var prop in cont) { if(prop[0] !== '_' && cont.hasOwnProperty(prop)) { var item = cont[prop]; if(Lib.isPlainObject(item)) { var r = _decode(item); if(r !== undefined) cont[prop] = r; + } else if(Array.isArray(cont) && cont.length > 0 && Lib.isPlainObject(cont[0])) { + for(var i = 0; i < cont.length; i++) { + _decode(cont[i]); + } } } } } -function decodeB64Arrays(gd) { +function decodeTypedArrays(gd) { for(var i = 0; i < gd._fullData.length; i++) { _decode(gd._fullData[i]); } @@ -2891,7 +2878,7 @@ function decodeB64Arrays(gd) { } plots.doCalcdata = function(gd, traces) { - decodeB64Arrays(gd); + decodeTypedArrays(gd); var axList = axisIDs.list(gd); var fullData = gd._fullData; diff --git a/src/traces/isosurface/defaults.js b/src/traces/isosurface/defaults.js index c8b9916c86e..b8283d42424 100644 --- a/src/traces/isosurface/defaults.js +++ b/src/traces/isosurface/defaults.js @@ -38,6 +38,8 @@ function supplyIsoDefaults(traceIn, traceOut, defaultColor, layout, coerce) { var z = coerce('z'); var value = coerce('value'); + console.log(["x", x]); + if( !x || !x.length || !y || !y.length || From 4d5b3820f191a4b8a2e80d5d3659d76b3c7d2feb Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 28 Nov 2020 13:53:15 -0500 Subject: [PATCH 004/122] Back to decoding in coerce --- src/lib/array.js | 5 ++--- src/lib/coerce.js | 4 ++-- src/plots/plots.js | 31 ------------------------------- src/traces/isosurface/defaults.js | 2 -- 4 files changed, 4 insertions(+), 38 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index a5121b943af..0eb0cd65289 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -41,7 +41,7 @@ exports.isArrayOrTypedArray = isArrayOrTypedArray; * not consistent we won't figure that out here. */ function isArray1D(a) { - return !(isArrayOrTypedArray(a[0]) || (isTypedArraySpec(a) && a.ndims === 1)); + return !isArrayOrTypedArray(a[0]); } exports.isArray1D = isArray1D; @@ -83,6 +83,7 @@ exports.typedArrays = typedArrays; exports.decodeTypedArraySpec = function(v) { // Assume processed by coerceTypedArraySpec + v = coerceTypedArraySpec(v) var T = typedArrays[v.dtype]; var buffer; if(v.bvals.constructor === ArrayBuffer) { @@ -259,8 +260,6 @@ function _rowLength(z, fn, len0) { } else { return z.length; } - } else if(isTypedArraySpec(z)) { - return z.shape[z.shape.length - 1]; } return 0; } diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 60229d6f19e..b137e1cad30 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -20,7 +20,7 @@ var counterRegex = require('./regex').counter; var modHalf = require('./mod').modHalf; var isArrayOrTypedArray = require('./array').isArrayOrTypedArray; var isTypedArraySpec = require('./array').isTypedArraySpec; -var coerceTypedArraySpec = require('./array').coerceTypedArraySpec; +var decodeTypedArraySpec = require('./array').decodeTypedArraySpec; exports.valObjectMeta = { @@ -42,7 +42,7 @@ exports.valObjectMeta = { propOut.set(v); wasSet = true; } else if(isTypedArraySpec(v)) { - propOut.set(coerceTypedArraySpec(v)); + propOut.set(decodeTypedArraySpec(v)); wasSet = true; } if(!wasSet && dflt !== undefined) propOut.set(dflt); diff --git a/src/plots/plots.js b/src/plots/plots.js index 2643ff778b7..c28b8f3df72 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -11,8 +11,6 @@ var d3 = require('d3'); var timeFormatLocale = require('d3-time-format').timeFormatLocale; var isNumeric = require('fast-isnumeric'); -var decodeTypedArraySpec = require('../lib/array').decodeTypedArraySpec; -var isTypedArraySpec = require('../lib/array').isTypedArraySpec; var Registry = require('../registry'); var PlotSchema = require('../plot_api/plot_schema'); @@ -2850,36 +2848,7 @@ function _transition(gd, transitionOpts, opts) { return transitionStarting.then(function() { return gd; }); } -function _decode(cont) { - if(isTypedArraySpec(cont)) { - return decodeTypedArraySpec(cont); - } - for(var prop in cont) { - if(prop[0] !== '_' && cont.hasOwnProperty(prop)) { - var item = cont[prop]; - if(Lib.isPlainObject(item)) { - var r = _decode(item); - if(r !== undefined) cont[prop] = r; - } else if(Array.isArray(cont) && cont.length > 0 && Lib.isPlainObject(cont[0])) { - for(var i = 0; i < cont.length; i++) { - _decode(cont[i]); - } - } - } - } -} - -function decodeTypedArrays(gd) { - for(var i = 0; i < gd._fullData.length; i++) { - _decode(gd._fullData[i]); - } - - _decode(gd._fullLayout); -} - plots.doCalcdata = function(gd, traces) { - decodeTypedArrays(gd); - var axList = axisIDs.list(gd); var fullData = gd._fullData; var fullLayout = gd._fullLayout; diff --git a/src/traces/isosurface/defaults.js b/src/traces/isosurface/defaults.js index b8283d42424..c8b9916c86e 100644 --- a/src/traces/isosurface/defaults.js +++ b/src/traces/isosurface/defaults.js @@ -38,8 +38,6 @@ function supplyIsoDefaults(traceIn, traceOut, defaultColor, layout, coerce) { var z = coerce('z'); var value = coerce('value'); - console.log(["x", x]); - if( !x || !x.length || !y || !y.length || From 7233f34e25735a7ab66d4d8271701f96e64dc5b0 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 28 Nov 2020 14:35:14 -0500 Subject: [PATCH 005/122] Cache base64 decoded ArrayBuffers and keep decoding in coerce / supplyDefaults --- src/lib/array.js | 2 +- src/lib/coerce.js | 39 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 0eb0cd65289..bdd5bcf5f64 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -83,7 +83,7 @@ exports.typedArrays = typedArrays; exports.decodeTypedArraySpec = function(v) { // Assume processed by coerceTypedArraySpec - v = coerceTypedArraySpec(v) + v = coerceTypedArraySpec(v); var T = typedArrays[v.dtype]; var buffer; if(v.bvals.constructor === ArrayBuffer) { diff --git a/src/lib/coerce.js b/src/lib/coerce.js index b137e1cad30..c2d931f7b3a 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -21,6 +21,7 @@ var modHalf = require('./mod').modHalf; var isArrayOrTypedArray = require('./array').isArrayOrTypedArray; var isTypedArraySpec = require('./array').isTypedArraySpec; var decodeTypedArraySpec = require('./array').decodeTypedArraySpec; +var coerceTypedArraySpec = require('./array').coerceTypedArraySpec; exports.valObjectMeta = { @@ -42,7 +43,43 @@ exports.valObjectMeta = { propOut.set(v); wasSet = true; } else if(isTypedArraySpec(v)) { - propOut.set(decodeTypedArraySpec(v)); + // Copy and coerce spec + v = coerceTypedArraySpec(v); + + // See if caching location is available + var uid = propOut.obj.uid; + var module = propOut.obj._module; + + if(v.bvals.constructor === ArrayBuffer || !uid || !module) { + // Already an ArrayBuffer + // decoding is cheap + propOut.set(decodeTypedArraySpec(v)); + } else { + var prop = propOut.astr; + var cache = module._b64BufferCache || {}; + + // Check cache + var cachedBuffer = ((cache[uid] || {})[prop] || {})[v.bvals]; + if(cachedBuffer !== undefined) { + // Use cached array buffer instead of base64 encoded + // string + v.bvals = cachedBuffer; + propOut.set(decodeTypedArraySpec(v)); + } else { + // Not in so cache decode + var decoded = decodeTypedArraySpec(v); + propOut.set(decoded); + + // Update cache for next time + cache[uid] = (cache[uid] || {}); + + // Clear any prior cache value (only store one per + // trace property + cache[uid][prop] = {}; + cache[uid][prop][v.bvals] = decoded.buffer; + module._b64BufferCache = cache; + } + } wasSet = true; } if(!wasSet && dflt !== undefined) propOut.set(dflt); From 5079fc79aa9a3f573d910bd534b8cce70fe42ebf Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 28 Nov 2020 15:09:17 -0500 Subject: [PATCH 006/122] bvals -> buffer --- src/lib/array.js | 10 +++++----- src/lib/coerce.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index bdd5bcf5f64..430e4c72aa0 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -86,12 +86,12 @@ exports.decodeTypedArraySpec = function(v) { v = coerceTypedArraySpec(v); var T = typedArrays[v.dtype]; var buffer; - if(v.bvals.constructor === ArrayBuffer) { + if(v.buffer.constructor === ArrayBuffer) { // Already an ArrayBuffer - buffer = v.bvals; + buffer = v.buffer; } else { // Decode, assuming a string - buffer = b64.decode(v.bvals); + buffer = b64.decode(v.buffer); } // Check if 1d shape. If so, we're done @@ -143,7 +143,7 @@ exports.decodeTypedArraySpec = function(v) { function isTypedArraySpec(v) { // Assume v has not passed through - return isPlainObject(v) && typedArrays[v.dtype] && v.bvals && ( + return isPlainObject(v) && typedArrays[v.dtype] && v.buffer && ( Number.isInteger(v.shape) || (isArrayOrTypedArray(v.shape) && v.shape.length > 0 && @@ -154,7 +154,7 @@ exports.isTypedArraySpec = isTypedArraySpec; function coerceTypedArraySpec(v) { // Assume isTypedArraySpec passed - var coerced = {dtype: v.dtype, bvals: v.bvals}; + var coerced = {dtype: v.dtype, buffer: v.buffer}; // Normalize shape to a list if(Number.isInteger(v.shape)) { diff --git a/src/lib/coerce.js b/src/lib/coerce.js index c2d931f7b3a..c29baeee79a 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -32,7 +32,7 @@ exports.valObjectMeta = { 'The value could be an {array}', 'noting that typed arrays (e.g. Float32Array) are also supported.', 'It could also be an object in the form of', - 'v: {, dtype: \'float32\', bvals: [/* ... */]}, shape: [dim0 (, dim1, (dim3))]', + 'v: {, dtype: \'float32\', buffer: [/* ... */]}, shape: [dim0 (, dim1, (dim3))]', 'otherwise, it would be ignored.' ].join(' '), requiredOpts: [], @@ -50,7 +50,7 @@ exports.valObjectMeta = { var uid = propOut.obj.uid; var module = propOut.obj._module; - if(v.bvals.constructor === ArrayBuffer || !uid || !module) { + if(v.buffer.constructor === ArrayBuffer || !uid || !module) { // Already an ArrayBuffer // decoding is cheap propOut.set(decodeTypedArraySpec(v)); @@ -59,11 +59,11 @@ exports.valObjectMeta = { var cache = module._b64BufferCache || {}; // Check cache - var cachedBuffer = ((cache[uid] || {})[prop] || {})[v.bvals]; + var cachedBuffer = ((cache[uid] || {})[prop] || {})[v.buffer]; if(cachedBuffer !== undefined) { // Use cached array buffer instead of base64 encoded // string - v.bvals = cachedBuffer; + v.buffer = cachedBuffer; propOut.set(decodeTypedArraySpec(v)); } else { // Not in so cache decode @@ -76,7 +76,7 @@ exports.valObjectMeta = { // Clear any prior cache value (only store one per // trace property cache[uid][prop] = {}; - cache[uid][prop][v.bvals] = decoded.buffer; + cache[uid][prop][v.buffer] = decoded.buffer; module._b64BufferCache = cache; } } From 38e10bb6b313ca667a29dfd12e3bd6acbef4d6fe Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 28 Nov 2020 15:28:23 -0500 Subject: [PATCH 007/122] remove big for consistency --- src/lib/array.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 430e4c72aa0..65fe0583ef9 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -75,8 +75,8 @@ var typedArrays = { uint32: typeof Uint32Array !== 'undefined' ? Uint32Array : null, float32: typeof Float32Array !== 'undefined' ? Float32Array : null, float64: typeof Float64Array !== 'undefined' ? Float64Array : null, - bigint64: typeof BigInt64Array !== 'undefined' ? BigInt64Array : null, - biguint64: typeof BigUint64Array !== 'undefined' ? BigUint64Array : null + int64: typeof BigInt64Array !== 'undefined' ? BigInt64Array : null, + uint64: typeof BigUint64Array !== 'undefined' ? BigUint64Array : null }; exports.typedArrays = typedArrays; From 104382711ee192f4de488f3c3e4497901a5c6430 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 28 Nov 2020 16:05:14 -0500 Subject: [PATCH 008/122] Revert "bvals -> buffer" This reverts commit 5079fc79 --- src/lib/array.js | 10 +++++----- src/lib/coerce.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 65fe0583ef9..4cc7be0bb18 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -86,12 +86,12 @@ exports.decodeTypedArraySpec = function(v) { v = coerceTypedArraySpec(v); var T = typedArrays[v.dtype]; var buffer; - if(v.buffer.constructor === ArrayBuffer) { + if(v.bvals.constructor === ArrayBuffer) { // Already an ArrayBuffer - buffer = v.buffer; + buffer = v.bvals; } else { // Decode, assuming a string - buffer = b64.decode(v.buffer); + buffer = b64.decode(v.bvals); } // Check if 1d shape. If so, we're done @@ -143,7 +143,7 @@ exports.decodeTypedArraySpec = function(v) { function isTypedArraySpec(v) { // Assume v has not passed through - return isPlainObject(v) && typedArrays[v.dtype] && v.buffer && ( + return isPlainObject(v) && typedArrays[v.dtype] && v.bvals && ( Number.isInteger(v.shape) || (isArrayOrTypedArray(v.shape) && v.shape.length > 0 && @@ -154,7 +154,7 @@ exports.isTypedArraySpec = isTypedArraySpec; function coerceTypedArraySpec(v) { // Assume isTypedArraySpec passed - var coerced = {dtype: v.dtype, buffer: v.buffer}; + var coerced = {dtype: v.dtype, bvals: v.bvals}; // Normalize shape to a list if(Number.isInteger(v.shape)) { diff --git a/src/lib/coerce.js b/src/lib/coerce.js index c29baeee79a..c2d931f7b3a 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -32,7 +32,7 @@ exports.valObjectMeta = { 'The value could be an {array}', 'noting that typed arrays (e.g. Float32Array) are also supported.', 'It could also be an object in the form of', - 'v: {, dtype: \'float32\', buffer: [/* ... */]}, shape: [dim0 (, dim1, (dim3))]', + 'v: {, dtype: \'float32\', bvals: [/* ... */]}, shape: [dim0 (, dim1, (dim3))]', 'otherwise, it would be ignored.' ].join(' '), requiredOpts: [], @@ -50,7 +50,7 @@ exports.valObjectMeta = { var uid = propOut.obj.uid; var module = propOut.obj._module; - if(v.buffer.constructor === ArrayBuffer || !uid || !module) { + if(v.bvals.constructor === ArrayBuffer || !uid || !module) { // Already an ArrayBuffer // decoding is cheap propOut.set(decodeTypedArraySpec(v)); @@ -59,11 +59,11 @@ exports.valObjectMeta = { var cache = module._b64BufferCache || {}; // Check cache - var cachedBuffer = ((cache[uid] || {})[prop] || {})[v.buffer]; + var cachedBuffer = ((cache[uid] || {})[prop] || {})[v.bvals]; if(cachedBuffer !== undefined) { // Use cached array buffer instead of base64 encoded // string - v.buffer = cachedBuffer; + v.bvals = cachedBuffer; propOut.set(decodeTypedArraySpec(v)); } else { // Not in so cache decode @@ -76,7 +76,7 @@ exports.valObjectMeta = { // Clear any prior cache value (only store one per // trace property cache[uid][prop] = {}; - cache[uid][prop][v.buffer] = decoded.buffer; + cache[uid][prop][v.bvals] = decoded.buffer; module._b64BufferCache = cache; } } From 8d84f3e212c2203825a1a9ce4ca6d73f19520d58 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sat, 28 Nov 2020 16:15:32 -0500 Subject: [PATCH 009/122] Run isArray1D on decoded array not spec --- src/traces/heatmap/xyz_defaults.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/heatmap/xyz_defaults.js b/src/traces/heatmap/xyz_defaults.js index 8be45ca87f0..cb0c912e965 100644 --- a/src/traces/heatmap/xyz_defaults.js +++ b/src/traces/heatmap/xyz_defaults.js @@ -21,7 +21,7 @@ module.exports = function handleXYZDefaults(traceIn, traceOut, coerce, layout, x if(z === undefined || !z.length) return 0; - if(Lib.isArray1D(traceIn.z)) { + if(Lib.isArray1D(z)) { x = coerce(xName); y = coerce(yName); From 2381debfb89d4e7e638165021b4577fe35935c13 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sun, 29 Nov 2020 07:29:27 -0500 Subject: [PATCH 010/122] Plotly.js doesn't currently handle BigInt64/BigUint64 arrays. The fact that elements aren't regular numbers, and aren't compatible with the Math module functions would make this challenging. --- src/lib/array.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 4cc7be0bb18..8c00c51c3fe 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -75,8 +75,6 @@ var typedArrays = { uint32: typeof Uint32Array !== 'undefined' ? Uint32Array : null, float32: typeof Float32Array !== 'undefined' ? Float32Array : null, float64: typeof Float64Array !== 'undefined' ? Float64Array : null, - int64: typeof BigInt64Array !== 'undefined' ? BigInt64Array : null, - uint64: typeof BigUint64Array !== 'undefined' ? BigUint64Array : null }; exports.typedArrays = typedArrays; From 599bc2faf2955c2635d1c10aa3da214b212b6d63 Mon Sep 17 00:00:00 2001 From: archmoj Date: Sun, 29 Nov 2020 15:51:50 -0500 Subject: [PATCH 011/122] no longer support IE9 --- test/jasmine/assets/ie9_mock.js | 9 ------ test/jasmine/bundle_tests/ie9_test.js | 42 --------------------------- test/jasmine/karma.conf.js | 11 ------- 3 files changed, 62 deletions(-) delete mode 100644 test/jasmine/assets/ie9_mock.js delete mode 100644 test/jasmine/bundle_tests/ie9_test.js diff --git a/test/jasmine/assets/ie9_mock.js b/test/jasmine/assets/ie9_mock.js deleted file mode 100644 index ac9dd7a30d8..00000000000 --- a/test/jasmine/assets/ie9_mock.js +++ /dev/null @@ -1,9 +0,0 @@ -delete window.Promise; - -delete window.ArrayBuffer; -delete window.Uint8Array; -delete window.Float32Array; -delete window.Float64Array; -delete window.Int16Array; -delete window.Int32Array; -delete window.DataView; diff --git a/test/jasmine/bundle_tests/ie9_test.js b/test/jasmine/bundle_tests/ie9_test.js deleted file mode 100644 index e35c73bc160..00000000000 --- a/test/jasmine/bundle_tests/ie9_test.js +++ /dev/null @@ -1,42 +0,0 @@ -var Plotly = require('@lib/core'); - -Plotly.register([ - require('@lib/bar'), - require('@lib/box'), - require('@lib/heatmap'), - require('@lib/histogram'), - require('@lib/histogram2d'), - require('@lib/histogram2dcontour'), - require('@lib/pie'), - require('@lib/contour'), - require('@lib/scatterternary'), - require('@lib/ohlc'), - require('@lib/candlestick') -]); - -var createGraphDiv = require('../assets/create_graph_div'); -var destroyGraphDiv = require('../assets/destroy_graph_div'); - -describe('Bundle with IE9 supported trace types:', function() { - afterEach(destroyGraphDiv); - - it('check that ie9_mock.js did its job', function() { - expect(function() { return ArrayBuffer; }) - .toThrow(new ReferenceError('ArrayBuffer is not defined')); - expect(function() { return DataView; }) - .toThrow(new ReferenceError('DataView is not defined')); - expect(function() { return Uint8Array; }) - .toThrow(new ReferenceError('Uint8Array is not defined')); - }); - - it('heatmaps with smoothing should work', function(done) { - var gd = createGraphDiv(); - var data = [{ - type: 'heatmap', - z: [[1, 2, 3], [2, 1, 2]], - zsmooth: 'best' - }]; - - Plotly.plot(gd, data).then(done); - }); -}); diff --git a/test/jasmine/karma.conf.js b/test/jasmine/karma.conf.js index 113f3185ade..d3130a5749f 100644 --- a/test/jasmine/karma.conf.js +++ b/test/jasmine/karma.conf.js @@ -48,9 +48,6 @@ if(argv.info) { 'Run all tests with the `noCI` tag on Firefox in a 1500px wide window:', ' $ npm run test-jasmine -- --tags=noCI --FF --width=1500', '', - 'Run the `ie9_test.js` bundle test with the verbose reporter:', - ' $ npm run test-jasmine -- --bundleTest=ie9 --verbose', - '', 'Arguments:', ' - All non-flagged arguments corresponds to the test suites in `test/jasmine/tests/` to be run.', ' No need to add the `_test.js` suffix, we expand them correctly here.', @@ -122,7 +119,6 @@ if(isFullSuite) { var pathToShortcutPath = path.join(__dirname, '..', '..', 'tasks', 'util', 'shortcut_paths.js'); var pathToStrictD3 = path.join(__dirname, '..', '..', 'tasks', 'util', 'strict_d3.js'); var pathToJQuery = path.join(__dirname, 'assets', 'jquery-1.8.3.min.js'); -var pathToIE9mock = path.join(__dirname, 'assets', 'ie9_mock.js'); var pathToCustomMatchers = path.join(__dirname, 'assets', 'custom_matchers.js'); var pathToUnpolyfill = path.join(__dirname, 'assets', 'unpolyfill.js'); var pathToMathJax = path.join(constants.pathToDist, 'extras', 'mathjax'); @@ -323,13 +319,6 @@ if(isBundleTest) { func.defaultConfig.files.push(constants.pathToPlotlyDistMin); func.defaultConfig.preprocessors[testFileGlob] = ['browserify']; break; - case 'ie9': - // load ie9_mock.js before plotly.js+test bundle - // to catch reference errors that could occur - // when plotly.js is first loaded. - func.defaultConfig.files.push(pathToIE9mock); - func.defaultConfig.preprocessors[testFileGlob] = ['browserify']; - break; case 'plotschema': func.defaultConfig.browserify.ignoreTransform = './tasks/compress_attributes.js'; func.defaultConfig.preprocessors[testFileGlob] = ['browserify']; From a83b11fa1f20e6c2abc6fb7904947e726c1dc5a0 Mon Sep 17 00:00:00 2001 From: Jon Mease Date: Sun, 29 Nov 2020 16:33:44 -0500 Subject: [PATCH 012/122] Remove Big(U)?IntArray from eslint --- .eslintrc | 2 -- 1 file changed, 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index 4cb350f6803..3693c3d138e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,8 +20,6 @@ "Uint16Array": true, "Int32Array": true, "Uint32Array": true, - "BigInt64Array": true, - "BigUint64Array": true, "ArrayBuffer": true, "DataView": true, "SVGElement": false From f3c43f7b9e1eefa5c9eae8854e571d09aa8c4d9b Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 7 Jul 2021 15:22:42 -0400 Subject: [PATCH 013/122] reflect schema changes --- test/plot-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plot-schema.json b/test/plot-schema.json index 2066f2ad50c..b3ed6b0626c 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -468,7 +468,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value MUST be an {array}, or we ignore it. Note that typed arrays (e.g. Float32Array) are supported.", + "description": "An {array} of data. The value could be an {array} noting that typed arrays (e.g. Float32Array) are also supported. It could also be an object in the form of v: {, dtype: 'float32', bvals: [/* ... */]}, shape: [dim0 (, dim1, (dim3))] otherwise, it would be ignored.", "otherOpts": [ "dflt" ], From 960cea399164ca5cd423f406e396718903c4a709 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 7 Jul 2021 15:24:53 -0400 Subject: [PATCH 014/122] provide draft log for PR 5230 --- draftlogs/5230_add.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/5230_add.md diff --git a/draftlogs/5230_add.md b/draftlogs/5230_add.md new file mode 100644 index 00000000000..b846e16184b --- /dev/null +++ b/draftlogs/5230_add.md @@ -0,0 +1 @@ + - Accept objects for encoded typedarrays in data_array valType [[#5230](https://github.com/plotly/plotly.js/pull/5230)] From 9aea69d69a119e4ec1cc3f82ec7c603b66f5dbad Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi <33888540+archmoj@users.noreply.github.com> Date: Fri, 9 Jul 2021 18:14:53 -0400 Subject: [PATCH 015/122] Update src/lib/coerce.js Co-authored-by: Alex Johnson --- src/lib/coerce.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/lib/coerce.js b/src/lib/coerce.js index d32905c6952..e3a251e26ec 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -22,11 +22,16 @@ exports.valObjectMeta = { // You can use *dflt=[] to force said array to exist though. description: [ 'An {array} of data.', - 'The value could be an {array}', - 'noting that typed arrays (e.g. Float32Array) are also supported.', - 'It could also be an object in the form of', - 'v: {, dtype: \'float32\', bvals: [/* ... */]}, shape: [dim0 (, dim1, (dim3))]', - 'otherwise, it would be ignored.' + 'The value must represent an {array} or it will be ignored,', + 'but this array can be provided in several forms:', + '(1) a regular {array} object', + '(2) a typed array (e.g. Float32Array)', + '(3) an object with keys dtype, bvals, and optionally shape.', + 'In this 3rd form, dtype is one of *int8*, *uint8*, *uint8clamped*,', + '*int16*, *uint16*, *int32*, *uint32*, *float32*, or *float64*.', + 'bvals is either a base64-encoded string or an ArrayBuffer,', + 'and for multi-dimensional arrays you must provide its dimensions', + 'as an array of integers.' ].join(' '), requiredOpts: [], otherOpts: ['dflt'], From 18caf3f1013fc2f0f48626e89f177626e854238a Mon Sep 17 00:00:00 2001 From: archmoj Date: Fri, 9 Jul 2021 18:28:30 -0400 Subject: [PATCH 016/122] reflect description change in the schema --- test/plot-schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/plot-schema.json b/test/plot-schema.json index b3ed6b0626c..79677e5a4f8 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -468,7 +468,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value could be an {array} noting that typed arrays (e.g. Float32Array) are also supported. It could also be an object in the form of v: {, dtype: 'float32', bvals: [/* ... */]}, shape: [dim0 (, dim1, (dim3))] otherwise, it would be ignored.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bvals, and optionally shape. In this 3rd form, dtype is one of *int8*, *uint8*, *uint8clamped*, *int16*, *uint16*, *int32*, *uint32*, *float32*, or *float64*. bvals is either a base64-encoded string or an ArrayBuffer, and for multi-dimensional arrays you must provide its dimensions as an array of integers.", "otherOpts": [ "dflt" ], From bbc58482133aacc3d86fa50335035a40f9b9303f Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 3 Feb 2023 09:56:20 -0500 Subject: [PATCH 017/122] uninstall base64-arraybuffer using npm 6 --- package-lock.json | 51 ++++++++++++++++++++++++++++------------------- package.json | 1 - 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index f32057adbd5..c640e8b17f5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,6 @@ "@turf/area": "^6.4.0", "@turf/bbox": "^6.4.0", "@turf/centroid": "^6.0.2", - "base64-arraybuffer": "^0.2.0", "canvas-fit": "^1.5.0", "color-alpha": "1.0.4", "color-normalize": "1.5.0", @@ -976,14 +975,6 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "node_modules/base64-arraybuffer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", - "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", @@ -5864,10 +5855,24 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/jasmine": { + "version": "3.99.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.99.0.tgz", + "integrity": "sha512-YIThBuHzaIIcjxeuLmPD40SjxkEcc8i//sGMDKCgkRMVgIwRJf5qyExtlJpQeh7pkeoBSOe6lQEdg+/9uKg9mw==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.1.6", + "jasmine-core": "~3.99.0" + }, + "bin": { + "jasmine": "bin/jasmine.js" + } + }, "node_modules/jasmine-core": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", - "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", + "version": "3.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz", + "integrity": "sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==", "dev": true }, "node_modules/js-string-escape": { @@ -11246,11 +11251,6 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "base64-arraybuffer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz", - "integrity": "sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==" - }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", @@ -15398,10 +15398,21 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "jasmine": { + "version": "3.99.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.99.0.tgz", + "integrity": "sha512-YIThBuHzaIIcjxeuLmPD40SjxkEcc8i//sGMDKCgkRMVgIwRJf5qyExtlJpQeh7pkeoBSOe6lQEdg+/9uKg9mw==", + "dev": true, + "peer": true, + "requires": { + "glob": "^7.1.6", + "jasmine-core": "~3.99.0" + } + }, "jasmine-core": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.5.0.tgz", - "integrity": "sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==", + "version": "3.99.1", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.99.1.tgz", + "integrity": "sha512-Hu1dmuoGcZ7AfyynN3LsfruwMbxMALMka+YtZeGoLuDEySVmVAPaonkNoBRIw/ectu8b9tVQCJNgp4a4knp+tg==", "dev": true }, "js-string-escape": { diff --git a/package.json b/package.json index a79400e32c8..f67b0d59592 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,6 @@ "@turf/area": "^6.4.0", "@turf/bbox": "^6.4.0", "@turf/centroid": "^6.0.2", - "base64-arraybuffer": "^0.2.0", "canvas-fit": "^1.5.0", "color-alpha": "1.0.4", "color-normalize": "1.5.0", From bdc1f7b36c7b0e27aaeeda65705c004813984540 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 3 Feb 2023 10:01:27 -0500 Subject: [PATCH 018/122] install latest base64-arraybuffer - new major --- package-lock.json | 14 ++++++++++++++ package.json | 1 + 2 files changed, 15 insertions(+) diff --git a/package-lock.json b/package-lock.json index 537fee8a21c..dc39127a73b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@turf/area": "^6.4.0", "@turf/bbox": "^6.4.0", "@turf/centroid": "^6.0.2", + "base64-arraybuffer": "^1.0.2", "canvas-fit": "^1.5.0", "color-alpha": "1.0.4", "color-normalize": "1.5.0", @@ -3152,6 +3153,14 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", @@ -14951,6 +14960,11 @@ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, + "base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==" + }, "base64-js": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", diff --git a/package.json b/package.json index d59ce1b3789..a7d72c7c446 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,7 @@ "@turf/area": "^6.4.0", "@turf/bbox": "^6.4.0", "@turf/centroid": "^6.0.2", + "base64-arraybuffer": "^1.0.2", "canvas-fit": "^1.5.0", "color-alpha": "1.0.4", "color-normalize": "1.5.0", From b94d9945d0752602e1033831da71966d7683ad8b Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 10:08:10 -0500 Subject: [PATCH 019/122] revisit key access --- src/lib/array.js | 56 ++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 5894463731e..aa74633eddd 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -69,36 +69,37 @@ exports.decodeTypedArraySpec = function(v) { v = coerceTypedArraySpec(v); var T = typedArrays[v.dtype]; var buffer; - if(v.bvals.constructor === ArrayBuffer) { + var bvals = v.bvals; + if(bvals.constructor === ArrayBuffer) { // Already an ArrayBuffer - buffer = v.bvals; + buffer = bvals; } else { // Decode, assuming a string - buffer = b64.decode(v.bvals); + buffer = b64.decode(bvals); } - // Check if 1d shape. If so, we're done - if(v.ndims === 1) { - // Construct single Typed array over entire buffer - return new T(buffer); - } else { + var lastDimIndex = v.ndims - 1; + + if(lastDimIndex > 0) { // Reshape into nested plain arrays with innermost // level containing typed arrays // We could eventually adopt an ndarray library + var shape = v.shape; + // Build cumulative product of dimensions - var cumulativeShape = v.shape.map(function(a, i) { - return a * (v.shape[i - 1] || 1); + var cumulativeShape = shape.map(function(a, i) { + return a * (shape[i - 1] || 1); }); // Loop of dimensions in reverse order var nestedArray = []; - for(var dimInd = v.ndims - 1; dimInd > 0; dimInd--) { - var subArrayLength = v.shape[dimInd]; + for(var dimInd = lastDimIndex; dimInd > 0; dimInd--) { + var subArrayLength = shape[dimInd]; var numSubArrays = cumulativeShape[dimInd - 1]; var nextArray = []; - if(dimInd === v.ndims - 1) { + if(dimInd === lastDimIndex) { // First time through, we build the // inner most typed arrays for(var typedInd = 0; typedInd < numSubArrays; typedInd++) { @@ -121,36 +122,45 @@ exports.decodeTypedArraySpec = function(v) { } return nestedArray; + } else { + // Construct single Typed array over entire buffer + return new T(buffer); } }; function isTypedArraySpec(v) { + if(!isPlainObject(v)) return false; + + var shape = v.shape; + // Assume v has not passed through - return isPlainObject(v) && typedArrays[v.dtype] && v.bvals && ( - Number.isInteger(v.shape) || - (isArrayOrTypedArray(v.shape) && - v.shape.length > 0 && - v.shape.every(function(d) { return Number.isInteger(d); })) + return typedArrays[v.dtype] && v.bvals && ( + Number.isInteger(shape) || + (isArrayOrTypedArray(shape) && + shape.length > 0 && + shape.every(function(d) { return Number.isInteger(d); })) ); } exports.isTypedArraySpec = isTypedArraySpec; function coerceTypedArraySpec(v) { + var shape = v.shape; + // Assume isTypedArraySpec passed var coerced = {dtype: v.dtype, bvals: v.bvals}; // Normalize shape to a list - if(Number.isInteger(v.shape)) { - coerced.shape = [v.shape]; + if(Number.isInteger(shape)) { + coerced.shape = [shape]; } else { - coerced.shape = v.shape; + coerced.shape = shape; } // Add length property - coerced.length = v.shape.reduce(function(a, b) { return a * b; }); + coerced.length = shape.reduce(function(a, b) { return a * b; }); // Add ndims - coerced.ndims = v.shape.length; + coerced.ndims = shape.length; return coerced; } From 114deab3d6a979286efecdcf93f353981873a41a Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 10:27:45 -0500 Subject: [PATCH 020/122] declare isInteger --- src/lib/array.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index aa74633eddd..8b4bc98ea76 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -1,9 +1,15 @@ 'use strict'; var b64 = require('base64-arraybuffer'); +var isNumeric = require('fast-isnumeric'); + var isPlainObject = require('./is_plain_object'); var isArray = Array.isArray; +function isInteger(a) { + return isNumeric(a) && (a % 1 === 0); +} + var ab = ArrayBuffer; var dv = DataView; @@ -135,10 +141,10 @@ function isTypedArraySpec(v) { // Assume v has not passed through return typedArrays[v.dtype] && v.bvals && ( - Number.isInteger(shape) || + isInteger(shape) || (isArrayOrTypedArray(shape) && shape.length > 0 && - shape.every(function(d) { return Number.isInteger(d); })) + shape.every(function(d) { return isInteger(d); })) ); } exports.isTypedArraySpec = isTypedArraySpec; @@ -150,7 +156,7 @@ function coerceTypedArraySpec(v) { var coerced = {dtype: v.dtype, bvals: v.bvals}; // Normalize shape to a list - if(Number.isInteger(shape)) { + if(isInteger(shape)) { coerced.shape = [shape]; } else { coerced.shape = shape; From 145c457bf67fd7b555a45c5f64d1155b6bbcdd4b Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 11:06:31 -0500 Subject: [PATCH 021/122] add TODO comments --- src/lib/array.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 8b4bc98ea76..5be68dd89c7 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -150,7 +150,7 @@ function isTypedArraySpec(v) { exports.isTypedArraySpec = isTypedArraySpec; function coerceTypedArraySpec(v) { - var shape = v.shape; + var shape = v.shape; // TODO: could one skip shape for 1d arrays? // Assume isTypedArraySpec passed var coerced = {dtype: v.dtype, bvals: v.bvals}; @@ -158,7 +158,7 @@ function coerceTypedArraySpec(v) { // Normalize shape to a list if(isInteger(shape)) { coerced.shape = [shape]; - } else { + } else { // TODO: should check shape isArrayOrTypedArray(shape) coerced.shape = shape; } From fa229d016345f57d68c513ca775231548adddf78 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 11:09:42 -0500 Subject: [PATCH 022/122] faster access to BYTES_PER_ELEMENT --- src/lib/array.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib/array.js b/src/lib/array.js index 5be68dd89c7..28c5b28889f 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -87,6 +87,8 @@ exports.decodeTypedArraySpec = function(v) { var lastDimIndex = v.ndims - 1; if(lastDimIndex > 0) { + var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; + // Reshape into nested plain arrays with innermost // level containing typed arrays // We could eventually adopt an ndarray library @@ -111,7 +113,7 @@ exports.decodeTypedArraySpec = function(v) { for(var typedInd = 0; typedInd < numSubArrays; typedInd++) { var typedOffset = typedInd * subArrayLength; nextArray.push( - new T(buffer, typedOffset * T.BYTES_PER_ELEMENT, subArrayLength) + new T(buffer, typedOffset * BYTES_PER_ELEMENT, subArrayLength) ); } } else { From 8536fe4f1a2170f61bba1a51d2d2aa6833f8d6ba Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 12:05:46 -0500 Subject: [PATCH 023/122] refactor --- src/lib/array.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 28c5b28889f..eb6f475016c 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -121,7 +121,9 @@ exports.decodeTypedArraySpec = function(v) { // next layer of nested arrays for(var i = 0; i < numSubArrays; i++) { var offset = i * subArrayLength; - nextArray.push(nextArray.slice(offset, offset + subArrayLength - 1)); + nextArray.push( + nextArray.slice(offset, offset + subArrayLength - 1) + ); } } @@ -144,9 +146,11 @@ function isTypedArraySpec(v) { // Assume v has not passed through return typedArrays[v.dtype] && v.bvals && ( isInteger(shape) || - (isArrayOrTypedArray(shape) && + ( + isArrayOrTypedArray(shape) && shape.length > 0 && - shape.every(function(d) { return isInteger(d); })) + shape.every(isInteger) + ) ); } exports.isTypedArraySpec = isTypedArraySpec; From d2b03ac92bd3b575bc79dd5efb97d754f1237546 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 12:24:07 -0500 Subject: [PATCH 024/122] refactor type detection --- src/lib/array.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index eb6f475016c..93173210f4c 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -56,16 +56,20 @@ exports.ensureArray = function(out, n) { return out; }; +function detectType(a) { + return typeof a === 'undefined' ? undefined : a; +} + var typedArrays = { - int8: typeof Int8Array !== 'undefined' ? Int8Array : null, - uint8: typeof Uint8Array !== 'undefined' ? Uint8Array : null, - uint8clamped: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : null, - int16: typeof Int16Array !== 'undefined' ? Int16Array : null, - uint16: typeof Uint16Array !== 'undefined' ? Uint16Array : null, - int32: typeof Int32Array !== 'undefined' ? Int32Array : null, - uint32: typeof Uint32Array !== 'undefined' ? Uint32Array : null, - float32: typeof Float32Array !== 'undefined' ? Float32Array : null, - float64: typeof Float64Array !== 'undefined' ? Float64Array : null, + int8: detectType(Int8Array), + uint8: detectType(Uint8Array), + uint8clamped: detectType(Uint8ClampedArray), + int16: detectType(Int16Array), + uint16: detectType(Uint16Array), + int32: detectType(Int32Array), + uint32: detectType(Uint32Array), + float32: detectType(Float32Array), + float64: detectType(Float64Array) }; exports.typedArrays = typedArrays; From d95640585c9d0e9f66458736702eaad29d4cc212 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 12:57:19 -0500 Subject: [PATCH 025/122] refactor decode bvals --- src/lib/array.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 93173210f4c..064dd53dddd 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -78,15 +78,9 @@ exports.decodeTypedArraySpec = function(v) { // Assume processed by coerceTypedArraySpec v = coerceTypedArraySpec(v); var T = typedArrays[v.dtype]; - var buffer; - var bvals = v.bvals; - if(bvals.constructor === ArrayBuffer) { - // Already an ArrayBuffer - buffer = bvals; - } else { - // Decode, assuming a string - buffer = b64.decode(bvals); - } + + var buffer = v.bvals; + if(buffer.constructor !== ArrayBuffer) buffer = b64.decode(buffer); var lastDimIndex = v.ndims - 1; From dbb717e156662dd432a6fc650c1aafe6fc67ec8b Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 13:14:28 -0500 Subject: [PATCH 026/122] revisit handling integer shapes - do not add ndims and length --- src/lib/array.js | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 064dd53dddd..c15cbc32999 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -82,7 +82,10 @@ exports.decodeTypedArraySpec = function(v) { var buffer = v.bvals; if(buffer.constructor !== ArrayBuffer) buffer = b64.decode(buffer); - var lastDimIndex = v.ndims - 1; + var shape = v.shape; + var ndims = shape ? shape.length : 1; + + var lastDimIndex = ndims - 1; if(lastDimIndex > 0) { var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; @@ -91,8 +94,6 @@ exports.decodeTypedArraySpec = function(v) { // level containing typed arrays // We could eventually adopt an ndarray library - var shape = v.shape; - // Build cumulative product of dimensions var cumulativeShape = shape.map(function(a, i) { return a * (shape[i - 1] || 1); @@ -156,23 +157,14 @@ exports.isTypedArraySpec = isTypedArraySpec; function coerceTypedArraySpec(v) { var shape = v.shape; // TODO: could one skip shape for 1d arrays? - // Assume isTypedArraySpec passed - var coerced = {dtype: v.dtype, bvals: v.bvals}; - // Normalize shape to a list - if(isInteger(shape)) { - coerced.shape = [shape]; - } else { // TODO: should check shape isArrayOrTypedArray(shape) - coerced.shape = shape; - } - - // Add length property - coerced.length = shape.reduce(function(a, b) { return a * b; }); - - // Add ndims - coerced.ndims = shape.length; + if(isInteger(shape)) shape = [shape]; - return coerced; + return { + shape: shape, + dtype: v.dtype, + bvals: v.bvals + }; } exports.coerceTypedArraySpec = coerceTypedArraySpec; From 323bf4d749a26d247f76be304098ca9a6cd7c03b Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Feb 2023 14:32:19 -0500 Subject: [PATCH 027/122] remove module based cache --- src/lib/array.js | 4 +++- src/lib/coerce.js | 32 +++----------------------------- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index c15cbc32999..77ef350db44 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -80,7 +80,9 @@ exports.decodeTypedArraySpec = function(v) { var T = typedArrays[v.dtype]; var buffer = v.bvals; - if(buffer.constructor !== ArrayBuffer) buffer = b64.decode(buffer); + if(buffer.constructor !== ArrayBuffer) { + buffer = b64.decode(buffer); + } var shape = v.shape; var ndims = shape ? shape.length : 1; diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 6c3b2431d90..484c979d3f0 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -44,39 +44,13 @@ exports.valObjectMeta = { // Copy and coerce spec v = coerceTypedArraySpec(v); - // See if caching location is available - var uid = propOut.obj.uid; - var module = propOut.obj._module; - - if(v.bvals.constructor === ArrayBuffer || !uid || !module) { + if(v.bvals.constructor === ArrayBuffer) { // Already an ArrayBuffer // decoding is cheap propOut.set(decodeTypedArraySpec(v)); } else { - var prop = propOut.astr; - var cache = module._b64BufferCache || {}; - - // Check cache - var cachedBuffer = ((cache[uid] || {})[prop] || {})[v.bvals]; - if(cachedBuffer !== undefined) { - // Use cached array buffer instead of base64 encoded - // string - v.bvals = cachedBuffer; - propOut.set(decodeTypedArraySpec(v)); - } else { - // Not in so cache decode - var decoded = decodeTypedArraySpec(v); - propOut.set(decoded); - - // Update cache for next time - cache[uid] = (cache[uid] || {}); - - // Clear any prior cache value (only store one per - // trace property - cache[uid][prop] = {}; - cache[uid][prop][v.bvals] = decoded.buffer; - module._b64BufferCache = cache; - } + var decoded = decodeTypedArraySpec(v); + propOut.set(decoded); } wasSet = true; } From 4f3c0d3b55cee0f980567091d6298a523063bf80 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 7 Feb 2023 10:21:36 -0500 Subject: [PATCH 028/122] rewrite coerce logic --- src/lib/coerce.js | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 484c979d3f0..a5bc3fbc5f9 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -36,25 +36,11 @@ exports.valObjectMeta = { requiredOpts: [], otherOpts: ['dflt'], coerceFunction: function(v, propOut, dflt) { - var wasSet; - if(isArrayOrTypedArray(v)) { - propOut.set(v); - wasSet = true; - } else if(isTypedArraySpec(v)) { - // Copy and coerce spec - v = coerceTypedArraySpec(v); - - if(v.bvals.constructor === ArrayBuffer) { - // Already an ArrayBuffer - // decoding is cheap - propOut.set(decodeTypedArraySpec(v)); - } else { - var decoded = decodeTypedArraySpec(v); - propOut.set(decoded); - } - wasSet = true; - } - if(!wasSet && dflt !== undefined) propOut.set(dflt); + propOut.set( + isArrayOrTypedArray(v) ? v : + isTypedArraySpec(v) ? decodeTypedArraySpec(coerceTypedArraySpec(v)) : + dflt + ); } }, enumerated: { From 014cbe303eefe19e800a2ab6348136a3c4b21e89 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 7 Feb 2023 10:24:47 -0500 Subject: [PATCH 029/122] faster b64 decode calls --- src/lib/array.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 77ef350db44..d84ed6f6008 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -1,5 +1,5 @@ 'use strict'; -var b64 = require('base64-arraybuffer'); +var b64decode = require('base64-arraybuffer').decode; var isNumeric = require('fast-isnumeric'); var isPlainObject = require('./is_plain_object'); @@ -81,7 +81,7 @@ exports.decodeTypedArraySpec = function(v) { var buffer = v.bvals; if(buffer.constructor !== ArrayBuffer) { - buffer = b64.decode(buffer); + buffer = b64decode(buffer); } var shape = v.shape; From 1f260e01e411349cd32adcd81a7e8535d9ed7231 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 7 Feb 2023 10:48:50 -0500 Subject: [PATCH 030/122] no need to extra call coerceTypedArraySpec --- src/lib/array.js | 9 ++------- src/lib/coerce.js | 4 +--- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index d84ed6f6008..502c3438449 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -71,11 +71,8 @@ var typedArrays = { float32: detectType(Float32Array), float64: detectType(Float64Array) }; -exports.typedArrays = typedArrays; - exports.decodeTypedArraySpec = function(v) { - // Assume processed by coerceTypedArraySpec v = coerceTypedArraySpec(v); var T = typedArrays[v.dtype]; @@ -139,7 +136,7 @@ exports.decodeTypedArraySpec = function(v) { } }; -function isTypedArraySpec(v) { +exports.isTypedArraySpec = function(v) { if(!isPlainObject(v)) return false; var shape = v.shape; @@ -153,8 +150,7 @@ function isTypedArraySpec(v) { shape.every(isInteger) ) ); -} -exports.isTypedArraySpec = isTypedArraySpec; +}; function coerceTypedArraySpec(v) { var shape = v.shape; // TODO: could one skip shape for 1d arrays? @@ -168,7 +164,6 @@ function coerceTypedArraySpec(v) { bvals: v.bvals }; } -exports.coerceTypedArraySpec = coerceTypedArraySpec; /* * TypedArray-compatible concatenation of n arrays diff --git a/src/lib/coerce.js b/src/lib/coerce.js index a5bc3fbc5f9..19b05cbcbc4 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -14,8 +14,6 @@ var modHalf = require('./mod').modHalf; var isArrayOrTypedArray = require('./array').isArrayOrTypedArray; var isTypedArraySpec = require('./array').isTypedArraySpec; var decodeTypedArraySpec = require('./array').decodeTypedArraySpec; -var coerceTypedArraySpec = require('./array').coerceTypedArraySpec; - exports.valObjectMeta = { data_array: { @@ -38,7 +36,7 @@ exports.valObjectMeta = { coerceFunction: function(v, propOut, dflt) { propOut.set( isArrayOrTypedArray(v) ? v : - isTypedArraySpec(v) ? decodeTypedArraySpec(coerceTypedArraySpec(v)) : + isTypedArraySpec(v) ? decodeTypedArraySpec(v) : dflt ); } From 5919634df85a6653e155ccf24c35aa2268048b6d Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 7 Feb 2023 13:57:02 -0500 Subject: [PATCH 031/122] attach spec to array for json export --- src/lib/array.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 502c3438449..20d636bd557 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -73,6 +73,7 @@ var typedArrays = { }; exports.decodeTypedArraySpec = function(v) { + var out = []; v = coerceTypedArraySpec(v); var T = typedArrays[v.dtype]; @@ -129,11 +130,18 @@ exports.decodeTypedArraySpec = function(v) { nestedArray = nextArray; } - return nestedArray; + out = nestedArray; } else { // Construct single Typed array over entire buffer - return new T(buffer); + out = new T(buffer); } + + // attach spec to array for json export + out.shape = v.shape; + out.dtype = v.dtype; + out.bvals = v.bvals; + + return out; }; exports.isTypedArraySpec = function(v) { From 4fbefebbd6bad766595b5dcbfc5312660ff69a79 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Feb 2023 14:16:37 -0500 Subject: [PATCH 032/122] export spec in json --- src/plots/plots.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/plots/plots.js b/src/plots/plots.js index 90cfe14eec7..39cc762822e 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2230,6 +2230,17 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi } if(Lib.isTypedArray(d)) { + if(d.dtype) { + return stripObj({ + dtype: d.dtype, + shape: d.shape, + bvals: d.bvals, + + // we could possibly add _vals here + // _vals: Lib.simpleMap(d, Lib.identity) + }, keepFunction); + } + return Lib.simpleMap(d, Lib.identity); } From 21ec1feaf6bf5472873e99a18addc49d3213a3d1 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Feb 2023 15:17:11 -0500 Subject: [PATCH 033/122] add test --- test/jasmine/tests/toimage_test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index bcfa50bd325..787d54feb63 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -291,6 +291,27 @@ describe('Plotly.toImage', function() { .then(done, done.fail); }); + it('export typed arrays as regular arrays', function(done) { + Plotly.newPlot(gd, [{ + type: 'surface', + x: new Float64Array([-1 / 3, 1 / 3]), + y: new Float32Array([-1 / 3, 1 / 3]), + z: [ + new Int16Array([-32768, 32767]), + new Uint16Array([65535, 0]) + ], + }]) + .then(function(gd) { return Plotly.toImage(gd, imgOpts);}) + .then(function(fig) { + fig = JSON.parse(fig); + + expect(fig.data[0].x).toEqual([-0.3333333333333333, 0.3333333333333333]); + expect(fig.data[0].y).toEqual([-0.3333333432674408, 0.3333333432674408]); + expect(fig.data[0].z).toEqual([[-32768, 32767], [65535, 0]]); + }) + .then(done, done.fail); + }); + it('export computed margins', function(done) { Plotly.toImage(pieAutoMargin, imgOpts) .then(function(fig) { From af5abc5a9ef19b52d428ce1267dc440bce723a7d Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Feb 2023 10:57:42 -0500 Subject: [PATCH 034/122] add test --- test/jasmine/tests/toimage_test.js | 42 +++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 787d54feb63..92b63483fb4 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -1,3 +1,8 @@ +var b64 = require('base64-arraybuffer'); +function b64encode(typedArray) { + return b64.encode(typedArray.buffer); +} + var Plotly = require('../../../lib/index'); var Lib = require('../../../src/lib'); @@ -299,12 +304,14 @@ describe('Plotly.toImage', function() { z: [ new Int16Array([-32768, 32767]), new Uint16Array([65535, 0]) - ], + ] }]) .then(function(gd) { return Plotly.toImage(gd, imgOpts);}) .then(function(fig) { fig = JSON.parse(fig); + expect(fig.data[0].visible).toEqual(true); + expect(fig.data[0].x).toEqual([-0.3333333333333333, 0.3333333333333333]); expect(fig.data[0].y).toEqual([-0.3333333432674408, 0.3333333432674408]); expect(fig.data[0].z).toEqual([[-32768, 32767], [65535, 0]]); @@ -312,6 +319,39 @@ describe('Plotly.toImage', function() { .then(done, done.fail); }); + it('import & export typed 1d and 2d arrays', function(done) { + var x = b64encode(new Float64Array([-1 / 3, 0, 1 / 3])); + var y = b64encode(new Float32Array([-1 / 3, 1 / 3])); + var z = b64encode(new Uint8Array([127, 0, 255, 255, 0, 127])); + + Plotly.newPlot(gd, [{ + type: 'surface', + x: {bvals: x, dtype: 'float64', shape: [3]}, + y: {bvals: y, dtype: 'float32', shape: [2]}, + z: {bvals: z, dtype: 'uint8', shape: [2, 3]} + }]) + + .then(function(gd) { return Plotly.toImage(gd, imgOpts);}) + .then(function(fig) { + fig = JSON.parse(fig); + + expect(fig.data[0].visible).toEqual(true); + + expect(fig.data[0].x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); + expect(fig.data[0].y.bvals).toEqual('q6qqvquqqj4='); + expect(fig.data[0].z.bvals).toEqual('fwD//wB/'); + + expect(fig.data[0].x.dtype).toEqual('float64'); + expect(fig.data[0].y.dtype).toEqual('float32'); + expect(fig.data[0].z.dtype).toEqual('uint8'); + + expect(fig.data[0].x._vals).toEqual([-0.3333333333333333, 0.3333333333333333]); + expect(fig.data[0].y._vals).toEqual([-0.3333333432674408, 0.3333333432674408]); + expect(fig.data[0].z._vals).toEqual([[127, 0, 255], [255, 0, 127]]); + }) + .then(done, done.fail); + }); + it('export computed margins', function(done) { Plotly.toImage(pieAutoMargin, imgOpts) .then(function(fig) { From dde03dfac672961c9a4f017d84c0c72645daab09 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Feb 2023 10:59:18 -0500 Subject: [PATCH 035/122] export converted _vals in full-json --- src/plots/plots.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index 39cc762822e..a93e9c2daf0 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2237,7 +2237,7 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi bvals: d.bvals, // we could possibly add _vals here - // _vals: Lib.simpleMap(d, Lib.identity) + _vals: Lib.simpleMap(d, Lib.identity) }, keepFunction); } From 6e7706e94fe1763a724e811cbd5a669281f4e5ab Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Feb 2023 12:52:23 -0500 Subject: [PATCH 036/122] improve test --- test/jasmine/tests/toimage_test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 92b63483fb4..b0bbf0464d8 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -322,13 +322,13 @@ describe('Plotly.toImage', function() { it('import & export typed 1d and 2d arrays', function(done) { var x = b64encode(new Float64Array([-1 / 3, 0, 1 / 3])); var y = b64encode(new Float32Array([-1 / 3, 1 / 3])); - var z = b64encode(new Uint8Array([127, 0, 255, 255, 0, 127])); + var z = b64encode(new Uint16Array([0, 100, 200, 300, 400, 500])); Plotly.newPlot(gd, [{ type: 'surface', x: {bvals: x, dtype: 'float64', shape: [3]}, y: {bvals: y, dtype: 'float32', shape: [2]}, - z: {bvals: z, dtype: 'uint8', shape: [2, 3]} + z: {bvals: z, dtype: 'uint16', shape: [3, 2]} }]) .then(function(gd) { return Plotly.toImage(gd, imgOpts);}) @@ -339,15 +339,15 @@ describe('Plotly.toImage', function() { expect(fig.data[0].x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); expect(fig.data[0].y.bvals).toEqual('q6qqvquqqj4='); - expect(fig.data[0].z.bvals).toEqual('fwD//wB/'); + expect(fig.data[0].z.bvals).toEqual('AABkAMgALAGQAfQB'); expect(fig.data[0].x.dtype).toEqual('float64'); expect(fig.data[0].y.dtype).toEqual('float32'); - expect(fig.data[0].z.dtype).toEqual('uint8'); + expect(fig.data[0].z.dtype).toEqual('uint16'); expect(fig.data[0].x._vals).toEqual([-0.3333333333333333, 0.3333333333333333]); expect(fig.data[0].y._vals).toEqual([-0.3333333432674408, 0.3333333432674408]); - expect(fig.data[0].z._vals).toEqual([[127, 0, 255], [255, 0, 127]]); + expect(fig.data[0].z._vals).toEqual([[0, 100, 200], [300, 400, 500]]); }) .then(done, done.fail); }); From 470b3b7158c1ae40b536c933a09547fa716f64f1 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Feb 2023 13:54:52 -0500 Subject: [PATCH 037/122] improve test --- test/jasmine/tests/toimage_test.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index b0bbf0464d8..3a0554664d1 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -321,7 +321,7 @@ describe('Plotly.toImage', function() { it('import & export typed 1d and 2d arrays', function(done) { var x = b64encode(new Float64Array([-1 / 3, 0, 1 / 3])); - var y = b64encode(new Float32Array([-1 / 3, 1 / 3])); + var y = b64encode(new Float32Array([1 / 3, -1 / 3])); var z = b64encode(new Uint16Array([0, 100, 200, 300, 400, 500])); Plotly.newPlot(gd, [{ @@ -338,16 +338,19 @@ describe('Plotly.toImage', function() { expect(fig.data[0].visible).toEqual(true); expect(fig.data[0].x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); - expect(fig.data[0].y.bvals).toEqual('q6qqvquqqj4='); - expect(fig.data[0].z.bvals).toEqual('AABkAMgALAGQAfQB'); + expect(fig.data[0].y.bvals).toEqual('q6qqPquqqr4='); + // expect(fig.data[0].z.bvals).toEqual('AABkAMgALAGQAfQB'); expect(fig.data[0].x.dtype).toEqual('float64'); expect(fig.data[0].y.dtype).toEqual('float32'); - expect(fig.data[0].z.dtype).toEqual('uint16'); + // expect(fig.data[0].z.dtype).toEqual('uint16'); - expect(fig.data[0].x._vals).toEqual([-0.3333333333333333, 0.3333333333333333]); - expect(fig.data[0].y._vals).toEqual([-0.3333333432674408, 0.3333333432674408]); - expect(fig.data[0].z._vals).toEqual([[0, 100, 200], [300, 400, 500]]); + // expect(fig.data[0].x._vals).toEqual([-0.3333333333333333, 0.3333333333333333]); + // expect(fig.data[0].y._vals).toEqual([0.3333333432674408, -0.3333333432674408]); + // expect(fig.data[0].z._vals).toEqual([[0, 100, 200], [300, 400, 500]]); + + // until we fix maintaining z array in surface + expect(fig.data[0].z).toEqual([[0, 100, 200], [300, 400, 500]]); }) .then(done, done.fail); }); From 61fa9b09e4f76fce764720bb83f3332d959124f3 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Feb 2023 13:55:57 -0500 Subject: [PATCH 038/122] rewrite to simplify handling 2d arrays --- src/lib/array.js | 60 +++++++++++------------------------------------- 1 file changed, 13 insertions(+), 47 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 20d636bd557..3d1304cfa8f 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -85,55 +85,21 @@ exports.decodeTypedArraySpec = function(v) { var shape = v.shape; var ndims = shape ? shape.length : 1; - var lastDimIndex = ndims - 1; - - if(lastDimIndex > 0) { - var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; - - // Reshape into nested plain arrays with innermost - // level containing typed arrays - // We could eventually adopt an ndarray library - - // Build cumulative product of dimensions - var cumulativeShape = shape.map(function(a, i) { - return a * (shape[i - 1] || 1); - }); - - // Loop of dimensions in reverse order - var nestedArray = []; - for(var dimInd = lastDimIndex; dimInd > 0; dimInd--) { - var subArrayLength = shape[dimInd]; - var numSubArrays = cumulativeShape[dimInd - 1]; - var nextArray = []; - - if(dimInd === lastDimIndex) { - // First time through, we build the - // inner most typed arrays - for(var typedInd = 0; typedInd < numSubArrays; typedInd++) { - var typedOffset = typedInd * subArrayLength; - nextArray.push( - new T(buffer, typedOffset * BYTES_PER_ELEMENT, subArrayLength) - ); - } - } else { - // Following times through, build - // next layer of nested arrays - for(var i = 0; i < numSubArrays; i++) { - var offset = i * subArrayLength; - nextArray.push( - nextArray.slice(offset, offset + subArrayLength - 1) - ); - } - } + var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; - // Update nested array with next nesting level - nestedArray = nextArray; - } - - out = nestedArray; - } else { - // Construct single Typed array over entire buffer + if(ndims === 1) { out = new T(buffer); + } else if(ndims === 2) { + var ni = shape[0]; + var nj = shape[1]; + + + out = []; + for(var j = 0; j < nj; j++) { + out[j] = new T(buffer, BYTES_PER_ELEMENT * j * ni, ni); + } + } else if(ndims === 3) { + // TODO: for volume } // attach spec to array for json export From 3d41e4563626db48b5d68514d033a00e92ab56e8 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Feb 2023 14:38:14 -0500 Subject: [PATCH 039/122] fix logic & test --- src/plots/plots.js | 11 ++++++++--- test/jasmine/tests/toimage_test.js | 11 ++--------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index a93e9c2daf0..994673dc424 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2226,6 +2226,14 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi } if(Array.isArray(d)) { + if(d.dtype) { + return stripObj({ + dtype: d.dtype, + shape: d.shape, + bvals: d.bvals, + }, keepFunction); + } + return d.map(function(x) {return stripObj(x, keepFunction);}); } @@ -2235,9 +2243,6 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi dtype: d.dtype, shape: d.shape, bvals: d.bvals, - - // we could possibly add _vals here - _vals: Lib.simpleMap(d, Lib.identity) }, keepFunction); } diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 3a0554664d1..b79ed9addef 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -339,18 +339,11 @@ describe('Plotly.toImage', function() { expect(fig.data[0].x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); expect(fig.data[0].y.bvals).toEqual('q6qqPquqqr4='); - // expect(fig.data[0].z.bvals).toEqual('AABkAMgALAGQAfQB'); + expect(fig.data[0].z.bvals).toEqual('AABkAMgALAGQAfQB'); expect(fig.data[0].x.dtype).toEqual('float64'); expect(fig.data[0].y.dtype).toEqual('float32'); - // expect(fig.data[0].z.dtype).toEqual('uint16'); - - // expect(fig.data[0].x._vals).toEqual([-0.3333333333333333, 0.3333333333333333]); - // expect(fig.data[0].y._vals).toEqual([0.3333333432674408, -0.3333333432674408]); - // expect(fig.data[0].z._vals).toEqual([[0, 100, 200], [300, 400, 500]]); - - // until we fix maintaining z array in surface - expect(fig.data[0].z).toEqual([[0, 100, 200], [300, 400, 500]]); + expect(fig.data[0].z.dtype).toEqual('uint16'); }) .then(done, done.fail); }); From 68d7541f3356188cedc89acd55abd83dad613a69 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Feb 2023 15:23:25 -0500 Subject: [PATCH 040/122] refactor bvals export --- src/plots/plots.js | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index 994673dc424..ba680c1690f 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2225,27 +2225,22 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi return o; } - if(Array.isArray(d)) { - if(d.dtype) { - return stripObj({ - dtype: d.dtype, - shape: d.shape, - bvals: d.bvals, - }, keepFunction); - } + var dIsArray = Array.isArray(d); + var dIsTypedArray = Lib.isTypedArray(d); - return d.map(function(x) {return stripObj(x, keepFunction);}); + if((dIsArray || dIsTypedArray) && d.dtype) { + return stripObj({ + dtype: d.dtype, + shape: d.shape, + bvals: d.bvals, + }, keepFunction); } - if(Lib.isTypedArray(d)) { - if(d.dtype) { - return stripObj({ - dtype: d.dtype, - shape: d.shape, - bvals: d.bvals, - }, keepFunction); - } + if(dIsArray) { + return d.map(function(x) {return stripObj(x, keepFunction);}); + } + if(dIsTypedArray) { return Lib.simpleMap(d, Lib.identity); } From 2cc98fbd4fbd67a7f15f17d5918a36b9aadbbaf0 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Feb 2023 15:36:39 -0500 Subject: [PATCH 041/122] convert ArrayBuffer to b64 in json export --- src/plots/plots.js | 10 +++++++++- test/jasmine/tests/toimage_test.js | 8 ++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index ba680c1690f..0e1ee5dfbde 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -4,6 +4,7 @@ var d3 = require('@plotly/d3'); var timeFormatLocale = require('d3-time-format').timeFormatLocale; var formatLocale = require('d3-format').formatLocale; var isNumeric = require('fast-isnumeric'); +var b64encode = require('base64-arraybuffer'); var Registry = require('../registry'); var PlotSchema = require('../plot_api/plot_schema'); @@ -2229,10 +2230,17 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi var dIsTypedArray = Lib.isTypedArray(d); if((dIsArray || dIsTypedArray) && d.dtype) { + var bvals = d.bvals; return stripObj({ dtype: d.dtype, shape: d.shape, - bvals: d.bvals, + + bvals: + // case of ArrayBuffer + bvals.constructor === ArrayBuffer ? b64encode.encode(bvals) : + // case of b64 string + bvals + }, keepFunction); } diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index b79ed9addef..d24973e4eeb 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -1,5 +1,5 @@ var b64 = require('base64-arraybuffer'); -function b64encode(typedArray) { +function b64encodeTypedArray(typedArray) { return b64.encode(typedArray.buffer); } @@ -320,9 +320,9 @@ describe('Plotly.toImage', function() { }); it('import & export typed 1d and 2d arrays', function(done) { - var x = b64encode(new Float64Array([-1 / 3, 0, 1 / 3])); - var y = b64encode(new Float32Array([1 / 3, -1 / 3])); - var z = b64encode(new Uint16Array([0, 100, 200, 300, 400, 500])); + var x = b64encodeTypedArray(new Float64Array([-1 / 3, 0, 1 / 3])); + var y = b64encodeTypedArray(new Float32Array([1 / 3, -1 / 3])); + var z = b64encodeTypedArray(new Uint16Array([0, 100, 200, 300, 400, 500])); Plotly.newPlot(gd, [{ type: 'surface', From 64e69888a2caaee53e640aea7aaf89b0858d869e Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 10 Feb 2023 09:48:21 -0500 Subject: [PATCH 042/122] improve test --- test/jasmine/tests/toimage_test.js | 48 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index d24973e4eeb..432b50dd604 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -308,21 +308,24 @@ describe('Plotly.toImage', function() { }]) .then(function(gd) { return Plotly.toImage(gd, imgOpts);}) .then(function(fig) { - fig = JSON.parse(fig); + var trace = JSON.parse(fig).data[0]; - expect(fig.data[0].visible).toEqual(true); + expect(trace.visible).toEqual(true); - expect(fig.data[0].x).toEqual([-0.3333333333333333, 0.3333333333333333]); - expect(fig.data[0].y).toEqual([-0.3333333432674408, 0.3333333432674408]); - expect(fig.data[0].z).toEqual([[-32768, 32767], [65535, 0]]); + expect(trace.x).toEqual([-0.3333333333333333, 0.3333333333333333]); + expect(trace.y).toEqual([-0.3333333432674408, 0.3333333432674408]); + expect(trace.z).toEqual([[-32768, 32767], [65535, 0]]); }) .then(done, done.fail); }); it('import & export typed 1d and 2d arrays', function(done) { - var x = b64encodeTypedArray(new Float64Array([-1 / 3, 0, 1 / 3])); - var y = b64encodeTypedArray(new Float32Array([1 / 3, -1 / 3])); - var z = b64encodeTypedArray(new Uint16Array([0, 100, 200, 300, 400, 500])); + var allX = new Float64Array([-1 / 3, 0, 1 / 3]); + var allY = new Float32Array([1 / 3, -1 / 3]); + var allZ = new Uint16Array([0, 100, 200, 300, 400, 500]); + var x = b64encodeTypedArray(allX); + var y = b64encodeTypedArray(allY); + var z = b64encodeTypedArray(allZ); Plotly.newPlot(gd, [{ type: 'surface', @@ -330,20 +333,29 @@ describe('Plotly.toImage', function() { y: {bvals: y, dtype: 'float32', shape: [2]}, z: {bvals: z, dtype: 'uint16', shape: [3, 2]} }]) - - .then(function(gd) { return Plotly.toImage(gd, imgOpts);}) + .then(function(gd) { + var trace = gd._fullData[0]; + expect(trace.x.slice()).toEqual(allX); + expect(trace.y.slice()).toEqual(allY); + expect(trace.z.slice()).toEqual([ + new Uint16Array([0, 100, 200]), + new Uint16Array([300, 400, 500]) + ]); + + return Plotly.toImage(gd, imgOpts); + }) .then(function(fig) { - fig = JSON.parse(fig); + var trace = JSON.parse(fig).data[0]; - expect(fig.data[0].visible).toEqual(true); + expect(trace.visible).toEqual(true); - expect(fig.data[0].x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); - expect(fig.data[0].y.bvals).toEqual('q6qqPquqqr4='); - expect(fig.data[0].z.bvals).toEqual('AABkAMgALAGQAfQB'); + expect(trace.x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); + expect(trace.y.bvals).toEqual('q6qqPquqqr4='); + expect(trace.z.bvals).toEqual('AABkAMgALAGQAfQB'); - expect(fig.data[0].x.dtype).toEqual('float64'); - expect(fig.data[0].y.dtype).toEqual('float32'); - expect(fig.data[0].z.dtype).toEqual('uint16'); + expect(trace.x.dtype).toEqual('float64'); + expect(trace.y.dtype).toEqual('float32'); + expect(trace.z.dtype).toEqual('uint16'); }) .then(done, done.fail); }); From fe5cc08955450bb4788f9b7c16c3d300cc99419f Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 10 Feb 2023 11:30:15 -0500 Subject: [PATCH 043/122] add test for buffer --- test/jasmine/tests/toimage_test.js | 41 ++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 432b50dd604..7977e253ed8 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -360,6 +360,47 @@ describe('Plotly.toImage', function() { .then(done, done.fail); }); + it('import buffer and export b64', function(done) { + var allX = new Float64Array([-1 / 3, 0, 1 / 3]); + var allY = new Float32Array([1 / 3, -1 / 3]); + var allZ = new Uint16Array([0, 100, 200, 300, 400, 500]); + var x = allX.buffer; + var y = allY.buffer; + var z = allZ.buffer; + + Plotly.newPlot(gd, [{ + type: 'surface', + x: {bvals: x, dtype: 'float64', shape: [3]}, + y: {bvals: y, dtype: 'float32', shape: [2]}, + z: {bvals: z, dtype: 'uint16', shape: [3, 2]} + }]) + .then(function(gd) { + var trace = gd._fullData[0]; + expect(trace.x.slice()).toEqual(allX); + expect(trace.y.slice()).toEqual(allY); + expect(trace.z.slice()).toEqual([ + new Uint16Array([0, 100, 200]), + new Uint16Array([300, 400, 500]) + ]); + + return Plotly.toImage(gd, imgOpts); + }) + .then(function(fig) { + var trace = JSON.parse(fig).data[0]; + + expect(trace.visible).toEqual(true); + + expect(trace.x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); + expect(trace.y.bvals).toEqual('q6qqPquqqr4='); + expect(trace.z.bvals).toEqual('AABkAMgALAGQAfQB'); + + expect(trace.x.dtype).toEqual('float64'); + expect(trace.y.dtype).toEqual('float32'); + expect(trace.z.dtype).toEqual('uint16'); + }) + .then(done, done.fail); + }); + it('export computed margins', function(done) { Plotly.toImage(pieAutoMargin, imgOpts) .then(function(fig) { From 9f724b2dfe197437a377dfc9e8507078aaaefbb1 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 10 Feb 2023 12:58:04 -0500 Subject: [PATCH 044/122] simplify decode logic for 2d & 3d - skip 3d for now --- src/lib/array.js | 31 +++++++++++++++++++++++------- test/jasmine/tests/toimage_test.js | 2 +- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 3d1304cfa8f..56c7b83325f 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -85,21 +85,38 @@ exports.decodeTypedArraySpec = function(v) { var shape = v.shape; var ndims = shape ? shape.length : 1; + var j; + var ni = shape[0]; + var nj = shape[1]; + var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; + var rowBites = BYTES_PER_ELEMENT * ni; + var pos = 0; if(ndims === 1) { out = new T(buffer); } else if(ndims === 2) { - var ni = shape[0]; - var nj = shape[1]; + for(j = 0; j < nj; j++) { + out[j] = new T(buffer, pos, ni); + pos += rowBites; + } + /* + // 3d arrays are not supported in traces e.g. volume & isosurface + // once supported we could uncomment this part - out = []; - for(var j = 0; j < nj; j++) { - out[j] = new T(buffer, BYTES_PER_ELEMENT * j * ni, ni); - } } else if(ndims === 3) { - // TODO: for volume + 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 += rowBites; + } + } + */ + } else { + throw new Error('Bad shape: "' + shape + '"'); } // attach spec to array for json export diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 7977e253ed8..0026d88faae 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -319,7 +319,7 @@ describe('Plotly.toImage', function() { .then(done, done.fail); }); - it('import & export typed 1d and 2d arrays', function(done) { + it('import & export 1d and 2d typed arrays', function(done) { var allX = new Float64Array([-1 / 3, 0, 1 / 3]); var allY = new Float32Array([1 / 3, -1 / 3]); var allZ = new Uint16Array([0, 100, 200, 300, 400, 500]); From a35c3fce43907adfd39367e5289ed88ca745d7f6 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 10 Feb 2023 13:09:17 -0500 Subject: [PATCH 045/122] use short forms of types --- src/lib/array.js | 22 +++++++++++++--------- src/lib/coerce.js | 6 +++--- test/jasmine/tests/toimage_test.js | 24 ++++++++++++------------ test/plot-schema.json | 2 +- 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 56c7b83325f..63d04517e5a 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -61,15 +61,19 @@ function detectType(a) { } var typedArrays = { - int8: detectType(Int8Array), - uint8: detectType(Uint8Array), - uint8clamped: detectType(Uint8ClampedArray), - int16: detectType(Int16Array), - uint16: detectType(Uint16Array), - int32: detectType(Int32Array), - uint32: detectType(Uint32Array), - float32: detectType(Float32Array), - float64: detectType(Float64Array) + i8: detectType(Int8Array), + ui8: detectType(Uint8Array), + ui8c: detectType(Uint8ClampedArray), + i16: detectType(Int16Array), + ui16: detectType(Uint16Array), + i32: detectType(Int32Array), + ui32: detectType(Uint32Array), + f32: detectType(Float32Array), + f64: detectType(Float64Array), + + // TODO: potentially add Big Int + // bi64: detectType(BigInt64Array), + // bui64: detectType(BigUint64Array), }; exports.decodeTypedArraySpec = function(v) { diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 19b05cbcbc4..6301bfe7a3b 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -25,9 +25,9 @@ exports.valObjectMeta = { '(1) a regular {array} object', '(2) a typed array (e.g. Float32Array)', '(3) an object with keys dtype, bvals, and optionally shape.', - 'In this 3rd form, dtype is one of *int8*, *uint8*, *uint8clamped*,', - '*int16*, *uint16*, *int32*, *uint32*, *float32*, or *float64*.', - 'bvals is either a base64-encoded string or an ArrayBuffer,', + 'In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*,', + '*i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*.', + 'bvals is either a base64-encoded string or an ArrayBuffer of an integer or float typed array,', 'and for multi-dimensional arrays you must provide its dimensions', 'as an array of integers.' ].join(' '), diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 0026d88faae..1745280b739 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -329,9 +329,9 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {bvals: x, dtype: 'float64', shape: [3]}, - y: {bvals: y, dtype: 'float32', shape: [2]}, - z: {bvals: z, dtype: 'uint16', shape: [3, 2]} + x: {bvals: x, dtype: 'f64', shape: [3]}, + y: {bvals: y, dtype: 'f32', shape: [2]}, + z: {bvals: z, dtype: 'ui16', shape: [3, 2]} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -353,9 +353,9 @@ describe('Plotly.toImage', function() { expect(trace.y.bvals).toEqual('q6qqPquqqr4='); expect(trace.z.bvals).toEqual('AABkAMgALAGQAfQB'); - expect(trace.x.dtype).toEqual('float64'); - expect(trace.y.dtype).toEqual('float32'); - expect(trace.z.dtype).toEqual('uint16'); + expect(trace.x.dtype).toEqual('f64'); + expect(trace.y.dtype).toEqual('f32'); + expect(trace.z.dtype).toEqual('ui16'); }) .then(done, done.fail); }); @@ -370,9 +370,9 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {bvals: x, dtype: 'float64', shape: [3]}, - y: {bvals: y, dtype: 'float32', shape: [2]}, - z: {bvals: z, dtype: 'uint16', shape: [3, 2]} + x: {bvals: x, dtype: 'f64', shape: [3]}, + y: {bvals: y, dtype: 'f32', shape: [2]}, + z: {bvals: z, dtype: 'ui16', shape: [3, 2]} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -394,9 +394,9 @@ describe('Plotly.toImage', function() { expect(trace.y.bvals).toEqual('q6qqPquqqr4='); expect(trace.z.bvals).toEqual('AABkAMgALAGQAfQB'); - expect(trace.x.dtype).toEqual('float64'); - expect(trace.y.dtype).toEqual('float32'); - expect(trace.z.dtype).toEqual('uint16'); + expect(trace.x.dtype).toEqual('f64'); + expect(trace.y.dtype).toEqual('f32'); + expect(trace.z.dtype).toEqual('ui16'); }) .then(done, done.fail); }); diff --git a/test/plot-schema.json b/test/plot-schema.json index 2cc811849b6..ae28c186d8d 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -479,7 +479,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bvals, and optionally shape. In this 3rd form, dtype is one of *int8*, *uint8*, *uint8clamped*, *int16*, *uint16*, *int32*, *uint32*, *float32*, or *float64*. bvals is either a base64-encoded string or an ArrayBuffer, and for multi-dimensional arrays you must provide its dimensions as an array of integers.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bvals, and optionally shape. In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*, *i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*. bvals is either a base64-encoded string or an ArrayBuffer of an integer or float typed array, and for multi-dimensional arrays you must provide its dimensions as an array of integers.", "otherOpts": [ "dflt" ], From f391e7607d557f2fd073186fa6b8ba7dbd8d4781 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 10 Feb 2023 15:36:53 -0500 Subject: [PATCH 046/122] simplify API for typed array spec - avoid shape array --- src/lib/array.js | 65 ++++++++++++------------------ src/lib/coerce.js | 9 +++-- src/lib/index.js | 1 + src/plots/plots.js | 7 ++-- test/jasmine/tests/toimage_test.js | 58 +++++++++++++++++--------- test/plot-schema.json | 2 +- 6 files changed, 76 insertions(+), 66 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 63d04517e5a..a14d62ee502 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -1,15 +1,10 @@ 'use strict'; var b64decode = require('base64-arraybuffer').decode; -var isNumeric = require('fast-isnumeric'); var isPlainObject = require('./is_plain_object'); var isArray = Array.isArray; -function isInteger(a) { - return isNumeric(a) && (a % 1 === 0); -} - var ab = ArrayBuffer; var dv = DataView; @@ -76,22 +71,27 @@ var typedArrays = { // bui64: detectType(BigUint64Array), }; -exports.decodeTypedArraySpec = function(v) { +function isArrayBuffer(a) { + return a.constructor === ArrayBuffer; +} +exports.isArrayBuffer = isArrayBuffer; + +exports.decodeTypedArraySpec = function(vIn) { var out = []; - v = coerceTypedArraySpec(v); - var T = typedArrays[v.dtype]; + var v = coerceTypedArraySpec(vIn); + var shape = v.spec.split('|'); + var dtype = shape.shift(); + var ndims = shape.length; + var T = typedArrays[dtype]; + if(!T) throw new Error('Error in spec: "' + v.spec + '"'); var buffer = v.bvals; - if(buffer.constructor !== ArrayBuffer) { + if(!isArrayBuffer(buffer)) { buffer = b64decode(buffer); } - var shape = v.shape; - var ndims = shape ? shape.length : 1; - - var j; - var ni = shape[0]; - var nj = shape[1]; + var nj, j; + var ni = +shape[0]; var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; var rowBites = BYTES_PER_ELEMENT * ni; @@ -100,6 +100,7 @@ exports.decodeTypedArraySpec = function(v) { if(ndims === 1) { out = new T(buffer); } else if(ndims === 2) { + nj = +shape[1]; for(j = 0; j < nj; j++) { out[j] = new T(buffer, pos, ni); pos += rowBites; @@ -110,7 +111,8 @@ exports.decodeTypedArraySpec = function(v) { // once supported we could uncomment this part } else if(ndims === 3) { - var nk = shape[2]; + nj = +shape[1]; + var nk = +shape[2]; for(var k = 0; k < nk; k++) { out[k] = []; for(j = 0; j < nj; j++) { @@ -120,42 +122,27 @@ exports.decodeTypedArraySpec = function(v) { } */ } else { - throw new Error('Bad shape: "' + shape + '"'); + throw new Error('Error in spec: "' + v.spec + '"'); } - // attach spec to array for json export - out.shape = v.shape; - out.dtype = v.dtype; + // attach spec & bvals to array for json export + out.spec = v.spec; out.bvals = v.bvals; return out; }; exports.isTypedArraySpec = function(v) { - if(!isPlainObject(v)) return false; - - var shape = v.shape; - - // Assume v has not passed through - return typedArrays[v.dtype] && v.bvals && ( - isInteger(shape) || - ( - isArrayOrTypedArray(shape) && - shape.length > 0 && - shape.every(isInteger) - ) + return ( + isPlainObject(v) && + v.hasOwnProperty('spec') && (typeof v.spec === 'string') && + v.hasOwnProperty('bvals') && (typeof v.bvals === 'string' || isArrayBuffer(v.bvals)) ); }; function coerceTypedArraySpec(v) { - var shape = v.shape; // TODO: could one skip shape for 1d arrays? - - // Normalize shape to a list - if(isInteger(shape)) shape = [shape]; - return { - shape: shape, - dtype: v.dtype, + spec: v.spec, bvals: v.bvals }; } diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 6301bfe7a3b..f625855d2b0 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -27,9 +27,12 @@ exports.valObjectMeta = { '(3) an object with keys dtype, bvals, and optionally shape.', 'In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*,', '*i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*.', - 'bvals is either a base64-encoded string or an ArrayBuffer of an integer or float typed array,', - 'and for multi-dimensional arrays you must provide its dimensions', - 'as an array of integers.' + 'bvals is either a base64-encoded string or the ArrayBuffer of', + 'an integer or float typed array.', + 'For either single or multi-dimensional arrays you must also', + 'provide its dimensions separated by pipe at the start of `dtype`.', + 'For exmaple *f64|100|5* declares a 2-D array that has 5 rows and 100 columns', + 'containing float64 values.' ].join(' '), requiredOpts: [], otherOpts: ['dflt'], diff --git a/src/lib/index.js b/src/lib/index.js index b3badf83e25..8e8d6d7b807 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -62,6 +62,7 @@ lib.toLogRange = require('./to_log_range'); lib.relinkPrivateKeys = require('./relink_private'); var arrayModule = require('./array'); +lib.isArrayBuffer = arrayModule.isArrayBuffer; lib.isTypedArray = arrayModule.isTypedArray; lib.isArrayOrTypedArray = arrayModule.isArrayOrTypedArray; lib.isArray1D = arrayModule.isArray1D; diff --git a/src/plots/plots.js b/src/plots/plots.js index 0e1ee5dfbde..69d8041f69a 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2229,15 +2229,14 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi var dIsArray = Array.isArray(d); var dIsTypedArray = Lib.isTypedArray(d); - if((dIsArray || dIsTypedArray) && d.dtype) { + if((dIsArray || dIsTypedArray) && d.spec) { var bvals = d.bvals; return stripObj({ - dtype: d.dtype, - shape: d.shape, + spec: d.spec, bvals: // case of ArrayBuffer - bvals.constructor === ArrayBuffer ? b64encode.encode(bvals) : + Lib.isArrayBuffer(bvals) ? b64encode.encode(bvals) : // case of b64 string bvals diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 1745280b739..95881b7ffe4 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -297,16 +297,30 @@ describe('Plotly.toImage', function() { }); it('export typed arrays as regular arrays', function(done) { + var x = new Float64Array([-1 / 3, 1 / 3]); + var y = new Float32Array([-1 / 3, 1 / 3]); + var z = [ + new Int16Array([-32768, 32767]), + new Uint16Array([65535, 0]) + ]; + Plotly.newPlot(gd, [{ type: 'surface', - x: new Float64Array([-1 / 3, 1 / 3]), - y: new Float32Array([-1 / 3, 1 / 3]), - z: [ - new Int16Array([-32768, 32767]), - new Uint16Array([65535, 0]) - ] + x: x, + y: y, + z: z }]) - .then(function(gd) { return Plotly.toImage(gd, imgOpts);}) + .then(function(gd) { + var trace = gd._fullData[0]; + + expect(trace.visible).toEqual(true); + + expect(trace.x.slice()).toEqual(x); + expect(trace.y.slice()).toEqual(y); + expect(trace.z.slice()).toEqual(z); + + return Plotly.toImage(gd, imgOpts); + }) .then(function(fig) { var trace = JSON.parse(fig).data[0]; @@ -329,12 +343,15 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {bvals: x, dtype: 'f64', shape: [3]}, - y: {bvals: y, dtype: 'f32', shape: [2]}, - z: {bvals: z, dtype: 'ui16', shape: [3, 2]} + x: {bvals: x, spec: 'f64|3'}, + y: {bvals: y, spec: 'f32|2'}, + z: {bvals: z, spec: 'ui16|3|2'} }]) .then(function(gd) { var trace = gd._fullData[0]; + + expect(trace.visible).toEqual(true); + expect(trace.x.slice()).toEqual(allX); expect(trace.y.slice()).toEqual(allY); expect(trace.z.slice()).toEqual([ @@ -353,9 +370,9 @@ describe('Plotly.toImage', function() { expect(trace.y.bvals).toEqual('q6qqPquqqr4='); expect(trace.z.bvals).toEqual('AABkAMgALAGQAfQB'); - expect(trace.x.dtype).toEqual('f64'); - expect(trace.y.dtype).toEqual('f32'); - expect(trace.z.dtype).toEqual('ui16'); + expect(trace.x.spec).toEqual('f64|3'); + expect(trace.y.spec).toEqual('f32|2'); + expect(trace.z.spec).toEqual('ui16|3|2'); }) .then(done, done.fail); }); @@ -370,12 +387,15 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {bvals: x, dtype: 'f64', shape: [3]}, - y: {bvals: y, dtype: 'f32', shape: [2]}, - z: {bvals: z, dtype: 'ui16', shape: [3, 2]} + x: {bvals: x, spec: 'f64|3'}, + y: {bvals: y, spec: 'f32|2'}, + z: {bvals: z, spec: 'ui16|3|2'} }]) .then(function(gd) { var trace = gd._fullData[0]; + + expect(trace.visible).toEqual(true); + expect(trace.x.slice()).toEqual(allX); expect(trace.y.slice()).toEqual(allY); expect(trace.z.slice()).toEqual([ @@ -394,9 +414,9 @@ describe('Plotly.toImage', function() { expect(trace.y.bvals).toEqual('q6qqPquqqr4='); expect(trace.z.bvals).toEqual('AABkAMgALAGQAfQB'); - expect(trace.x.dtype).toEqual('f64'); - expect(trace.y.dtype).toEqual('f32'); - expect(trace.z.dtype).toEqual('ui16'); + expect(trace.x.spec).toEqual('f64|3'); + expect(trace.y.spec).toEqual('f32|2'); + expect(trace.z.spec).toEqual('ui16|3|2'); }) .then(done, done.fail); }); diff --git a/test/plot-schema.json b/test/plot-schema.json index ae28c186d8d..76d358697ee 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -479,7 +479,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bvals, and optionally shape. In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*, *i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*. bvals is either a base64-encoded string or an ArrayBuffer of an integer or float typed array, and for multi-dimensional arrays you must provide its dimensions as an array of integers.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bvals, and optionally shape. In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*, *i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*. bvals is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either single or multi-dimensional arrays you must also provide its dimensions separated by pipe at the start of `dtype`. For exmaple *f64|100|5* declares a 2-D array that has 5 rows and 100 columns containing float64 values.", "otherOpts": [ "dflt" ], From ebd9d790af56824b2a06ad2b75d42e1f364893b5 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 10 Feb 2023 16:21:56 -0500 Subject: [PATCH 047/122] bvals > vals --- src/lib/array.js | 10 +++++----- src/lib/coerce.js | 4 ++-- src/plots/plots.js | 8 ++++---- test/jasmine/tests/toimage_test.js | 24 ++++++++++++------------ test/plot-schema.json | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index a14d62ee502..7e7c30d1022 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -85,7 +85,7 @@ exports.decodeTypedArraySpec = function(vIn) { var T = typedArrays[dtype]; if(!T) throw new Error('Error in spec: "' + v.spec + '"'); - var buffer = v.bvals; + var buffer = v.vals; if(!isArrayBuffer(buffer)) { buffer = b64decode(buffer); } @@ -125,9 +125,9 @@ exports.decodeTypedArraySpec = function(vIn) { throw new Error('Error in spec: "' + v.spec + '"'); } - // attach spec & bvals to array for json export + // attach spec & vals to array for json export out.spec = v.spec; - out.bvals = v.bvals; + out.vals = v.vals; return out; }; @@ -136,14 +136,14 @@ exports.isTypedArraySpec = function(v) { return ( isPlainObject(v) && v.hasOwnProperty('spec') && (typeof v.spec === 'string') && - v.hasOwnProperty('bvals') && (typeof v.bvals === 'string' || isArrayBuffer(v.bvals)) + v.hasOwnProperty('vals') && (typeof v.vals === 'string' || isArrayBuffer(v.vals)) ); }; function coerceTypedArraySpec(v) { return { spec: v.spec, - bvals: v.bvals + vals: v.vals }; } diff --git a/src/lib/coerce.js b/src/lib/coerce.js index f625855d2b0..9744ff3ab85 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -24,10 +24,10 @@ exports.valObjectMeta = { 'but this array can be provided in several forms:', '(1) a regular {array} object', '(2) a typed array (e.g. Float32Array)', - '(3) an object with keys dtype, bvals, and optionally shape.', + '(3) an object with keys dtype, vals, and optionally shape.', 'In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*,', '*i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*.', - 'bvals is either a base64-encoded string or the ArrayBuffer of', + 'vals is either a base64-encoded string or the ArrayBuffer of', 'an integer or float typed array.', 'For either single or multi-dimensional arrays you must also', 'provide its dimensions separated by pipe at the start of `dtype`.', diff --git a/src/plots/plots.js b/src/plots/plots.js index 69d8041f69a..dd2b8183ace 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2230,15 +2230,15 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi var dIsTypedArray = Lib.isTypedArray(d); if((dIsArray || dIsTypedArray) && d.spec) { - var bvals = d.bvals; + var vals = d.vals; return stripObj({ spec: d.spec, - bvals: + vals: // case of ArrayBuffer - Lib.isArrayBuffer(bvals) ? b64encode.encode(bvals) : + Lib.isArrayBuffer(vals) ? b64encode.encode(vals) : // case of b64 string - bvals + vals }, keepFunction); } diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 95881b7ffe4..b49b6241e7f 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -343,9 +343,9 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {bvals: x, spec: 'f64|3'}, - y: {bvals: y, spec: 'f32|2'}, - z: {bvals: z, spec: 'ui16|3|2'} + x: {vals: x, spec: 'f64|3'}, + y: {vals: y, spec: 'f32|2'}, + z: {vals: z, spec: 'ui16|3|2'} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -366,9 +366,9 @@ describe('Plotly.toImage', function() { expect(trace.visible).toEqual(true); - expect(trace.x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); - expect(trace.y.bvals).toEqual('q6qqPquqqr4='); - expect(trace.z.bvals).toEqual('AABkAMgALAGQAfQB'); + expect(trace.x.vals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); + expect(trace.y.vals).toEqual('q6qqPquqqr4='); + expect(trace.z.vals).toEqual('AABkAMgALAGQAfQB'); expect(trace.x.spec).toEqual('f64|3'); expect(trace.y.spec).toEqual('f32|2'); @@ -387,9 +387,9 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {bvals: x, spec: 'f64|3'}, - y: {bvals: y, spec: 'f32|2'}, - z: {bvals: z, spec: 'ui16|3|2'} + x: {vals: x, spec: 'f64|3'}, + y: {vals: y, spec: 'f32|2'}, + z: {vals: z, spec: 'ui16|3|2'} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -410,9 +410,9 @@ describe('Plotly.toImage', function() { expect(trace.visible).toEqual(true); - expect(trace.x.bvals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); - expect(trace.y.bvals).toEqual('q6qqPquqqr4='); - expect(trace.z.bvals).toEqual('AABkAMgALAGQAfQB'); + expect(trace.x.vals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); + expect(trace.y.vals).toEqual('q6qqPquqqr4='); + expect(trace.z.vals).toEqual('AABkAMgALAGQAfQB'); expect(trace.x.spec).toEqual('f64|3'); expect(trace.y.spec).toEqual('f32|2'); diff --git a/test/plot-schema.json b/test/plot-schema.json index 76d358697ee..f2a873bba7d 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -479,7 +479,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bvals, and optionally shape. In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*, *i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*. bvals is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either single or multi-dimensional arrays you must also provide its dimensions separated by pipe at the start of `dtype`. For exmaple *f64|100|5* declares a 2-D array that has 5 rows and 100 columns containing float64 values.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, vals, and optionally shape. In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*, *i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*. vals is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either single or multi-dimensional arrays you must also provide its dimensions separated by pipe at the start of `dtype`. For exmaple *f64|100|5* declares a 2-D array that has 5 rows and 100 columns containing float64 values.", "otherOpts": [ "dflt" ], From 23902dc42a6c9c228f234504528f15129b76bca7 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 24 Feb 2023 16:54:58 -0500 Subject: [PATCH 048/122] add shape and revise dtype naming to match numpy --- src/lib/array.js | 45 +++++++++++++++++------------- src/lib/coerce.js | 11 ++++---- src/plots/plots.js | 5 ++-- test/jasmine/tests/toimage_test.js | 34 ++++++++++++++-------- test/plot-schema.json | 2 +- 5 files changed, 57 insertions(+), 40 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 7e7c30d1022..7ff255bf582 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -56,19 +56,19 @@ function detectType(a) { } var typedArrays = { - i8: detectType(Int8Array), - ui8: detectType(Uint8Array), - ui8c: detectType(Uint8ClampedArray), - i16: detectType(Int16Array), - ui16: detectType(Uint16Array), - i32: detectType(Int32Array), - ui32: detectType(Uint32Array), - f32: detectType(Float32Array), - f64: detectType(Float64Array), + i1: detectType(Int8Array), + u1: detectType(Uint8Array), + c1: detectType(Uint8ClampedArray), // not supported in numpy? + i2: detectType(Int16Array), + u2: detectType(Uint16Array), + i4: detectType(Int32Array), + u4: detectType(Uint32Array), + f4: detectType(Float32Array), + f8: detectType(Float64Array), // TODO: potentially add Big Int - // bi64: detectType(BigInt64Array), - // bui64: detectType(BigUint64Array), + // I8: detectType(BigInt64Array), // not supported in numpy? + // b8: detectType(BigUint64Array), }; function isArrayBuffer(a) { @@ -79,11 +79,13 @@ exports.isArrayBuffer = isArrayBuffer; exports.decodeTypedArraySpec = function(vIn) { var out = []; var v = coerceTypedArraySpec(vIn); - var shape = v.spec.split('|'); - var dtype = shape.shift(); - var ndims = shape.length; + var dtype = v.dtype; + var T = typedArrays[dtype]; - if(!T) throw new Error('Error in spec: "' + v.spec + '"'); + if(!T) throw new Error('Error in dtype: "' + dtype + '"'); + + var shape = ('' + v.shape).split(','); // convert number to string and split to array + var ndims = shape.length; var buffer = v.vals; if(!isArrayBuffer(buffer)) { @@ -122,11 +124,12 @@ exports.decodeTypedArraySpec = function(vIn) { } */ } else { - throw new Error('Error in spec: "' + v.spec + '"'); + throw new Error('Error in shape: "' + v.shape + '"'); } - // attach spec & vals to array for json export - out.spec = v.spec; + // attach dtype, shape & vals to array for json export + out.dtype = v.dtype; + out.shape = v.shape; out.vals = v.vals; return out; @@ -135,14 +138,16 @@ exports.decodeTypedArraySpec = function(vIn) { exports.isTypedArraySpec = function(v) { return ( isPlainObject(v) && - v.hasOwnProperty('spec') && (typeof v.spec === 'string') && + v.hasOwnProperty('dtype') && (typeof v.dtype === 'string') && + v.hasOwnProperty('shape') && (typeof v.shape === 'string' || typeof v.shape === 'number') && v.hasOwnProperty('vals') && (typeof v.vals === 'string' || isArrayBuffer(v.vals)) ); }; function coerceTypedArraySpec(v) { return { - spec: v.spec, + dtype: v.dtype, + shape: v.shape, vals: v.vals }; } diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 9744ff3ab85..c9f2b49abdc 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -25,14 +25,15 @@ exports.valObjectMeta = { '(1) a regular {array} object', '(2) a typed array (e.g. Float32Array)', '(3) an object with keys dtype, vals, and optionally shape.', - 'In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*,', - '*i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*.', + 'In this 3rd form, dtype is one of *i1*, *u1*, *c1*,', + '*i2*, *u2*, *i4*, *u4*, *f4*, or *f8*.', 'vals is either a base64-encoded string or the ArrayBuffer of', 'an integer or float typed array.', 'For either single or multi-dimensional arrays you must also', - 'provide its dimensions separated by pipe at the start of `dtype`.', - 'For exmaple *f64|100|5* declares a 2-D array that has 5 rows and 100 columns', - 'containing float64 values.' + 'provide its dimensions separated by comma via `shape`.', + 'For exmaple using `dtype`: *f4* and `shape`: *100,5* you can', + 'declare a 2-D array that has 5 rows and 100 columns', + 'containing float32 values i.e. 4 bits per value.' ].join(' '), requiredOpts: [], otherOpts: ['dflt'], diff --git a/src/plots/plots.js b/src/plots/plots.js index dd2b8183ace..985004460ed 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2229,10 +2229,11 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi var dIsArray = Array.isArray(d); var dIsTypedArray = Lib.isTypedArray(d); - if((dIsArray || dIsTypedArray) && d.spec) { + if((dIsArray || dIsTypedArray) && d.dtype && d.shape) { var vals = d.vals; return stripObj({ - spec: d.spec, + dtype: d.dtype, + shape: d.shape, vals: // case of ArrayBuffer diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index b49b6241e7f..bee140cc82b 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -343,9 +343,9 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {vals: x, spec: 'f64|3'}, - y: {vals: y, spec: 'f32|2'}, - z: {vals: z, spec: 'ui16|3|2'} + x: {vals: x, dtype: 'f8', shape: '3'}, + y: {vals: y, dtype: 'f4', shape: '2'}, + z: {vals: z, dtype: 'u2', shape: '3,2'} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -370,9 +370,14 @@ describe('Plotly.toImage', function() { expect(trace.y.vals).toEqual('q6qqPquqqr4='); expect(trace.z.vals).toEqual('AABkAMgALAGQAfQB'); - expect(trace.x.spec).toEqual('f64|3'); - expect(trace.y.spec).toEqual('f32|2'); - expect(trace.z.spec).toEqual('ui16|3|2'); + expect(trace.x.dtype).toEqual('f8'); + expect(trace.x.shape).toEqual('3'); + + expect(trace.y.dtype).toEqual('f4'); + expect(trace.y.shape).toEqual('2'); + + expect(trace.z.dtype).toEqual('u2'); + expect(trace.z.shape).toEqual('3,2'); }) .then(done, done.fail); }); @@ -387,9 +392,9 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {vals: x, spec: 'f64|3'}, - y: {vals: y, spec: 'f32|2'}, - z: {vals: z, spec: 'ui16|3|2'} + x: {vals: x, dtype: 'f8', shape: '3'}, + y: {vals: y, dtype: 'f4', shape: '2'}, + z: {vals: z, dtype: 'u2', shape: '3,2'} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -414,9 +419,14 @@ describe('Plotly.toImage', function() { expect(trace.y.vals).toEqual('q6qqPquqqr4='); expect(trace.z.vals).toEqual('AABkAMgALAGQAfQB'); - expect(trace.x.spec).toEqual('f64|3'); - expect(trace.y.spec).toEqual('f32|2'); - expect(trace.z.spec).toEqual('ui16|3|2'); + expect(trace.x.dtype).toEqual('f8'); + expect(trace.x.shape).toEqual('3'); + + expect(trace.y.dtype).toEqual('f4'); + expect(trace.y.shape).toEqual('2'); + + expect(trace.z.dtype).toEqual('u2'); + expect(trace.z.shape).toEqual('3,2'); }) .then(done, done.fail); }); diff --git a/test/plot-schema.json b/test/plot-schema.json index 15336b088aa..49e4b2e5463 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -479,7 +479,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, vals, and optionally shape. In this 3rd form, dtype is one of *i8*, *ui8*, *ui8c*, *i16*, *ui16*, *i32*, *ui32*, *f32*, or *f64*. vals is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either single or multi-dimensional arrays you must also provide its dimensions separated by pipe at the start of `dtype`. For exmaple *f64|100|5* declares a 2-D array that has 5 rows and 100 columns containing float64 values.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, vals, and optionally shape. In this 3rd form, dtype is one of *i1*, *u1*, *c1*, *i2*, *u2*, *i4*, *u4*, *f4*, or *f8*. vals is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either single or multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For exmaple using `dtype`: *f4* and `shape`: *100,5* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value.", "otherOpts": [ "dflt" ], From 819d420b9582fac91a6d9c23d331c273da6421f8 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 24 Feb 2023 19:15:34 -0500 Subject: [PATCH 049/122] make shape optional --- src/lib/array.js | 35 ++++++++++++++++++++++++------ src/lib/coerce.js | 5 +++-- test/jasmine/tests/toimage_test.js | 4 ++-- test/plot-schema.json | 2 +- 4 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 7ff255bf582..36d54456235 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -83,19 +83,36 @@ exports.decodeTypedArraySpec = function(vIn) { var T = typedArrays[dtype]; if(!T) throw new Error('Error in dtype: "' + dtype + '"'); + var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; - var shape = ('' + v.shape).split(','); // convert number to string and split to array - var ndims = shape.length; - + var shape; var buffer = v.vals; if(!isArrayBuffer(buffer)) { + if(v.shape === undefined) { + var len = buffer.length; + var n = 0.75 * len; + if(buffer[len - 2] === '=') { + n -= 2; + } else if(buffer[len - 1] === '=') { + n -= 1; + } + + shape = [n / BYTES_PER_ELEMENT]; + } + buffer = b64decode(buffer); } + if(!shape) { + // convert number to string and split to array + shape = ('' + v.shape).split(','); + } + + var ndims = shape.length; + var nj, j; var ni = +shape[0]; - var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; var rowBites = BYTES_PER_ELEMENT * ni; var pos = 0; @@ -128,9 +145,9 @@ exports.decodeTypedArraySpec = function(vIn) { } // attach dtype, shape & vals to array for json export - out.dtype = v.dtype; - out.shape = v.shape; out.vals = v.vals; + out.dtype = v.dtype; + out.shape = shape.join(','); return out; }; @@ -139,7 +156,11 @@ exports.isTypedArraySpec = function(v) { return ( isPlainObject(v) && v.hasOwnProperty('dtype') && (typeof v.dtype === 'string') && - v.hasOwnProperty('shape') && (typeof v.shape === 'string' || typeof v.shape === 'number') && + + (v.shape === undefined || ( + v.hasOwnProperty('shape') && (typeof v.shape === 'string' || typeof v.shape === 'number') + )) && + v.hasOwnProperty('vals') && (typeof v.vals === 'string' || isArrayBuffer(v.vals)) ); }; diff --git a/src/lib/coerce.js b/src/lib/coerce.js index c9f2b49abdc..dbf8a146468 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -29,11 +29,12 @@ exports.valObjectMeta = { '*i2*, *u2*, *i4*, *u4*, *f4*, or *f8*.', 'vals is either a base64-encoded string or the ArrayBuffer of', 'an integer or float typed array.', - 'For either single or multi-dimensional arrays you must also', + 'For either multi-dimensional arrays you must also', 'provide its dimensions separated by comma via `shape`.', 'For exmaple using `dtype`: *f4* and `shape`: *100,5* you can', 'declare a 2-D array that has 5 rows and 100 columns', - 'containing float32 values i.e. 4 bits per value.' + 'containing float32 values i.e. 4 bits per value.', + '`shape` is optional for one dimensional arrays.' ].join(' '), requiredOpts: [], otherOpts: ['dflt'], diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index bee140cc82b..ba709d0f927 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -343,8 +343,8 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {vals: x, dtype: 'f8', shape: '3'}, - y: {vals: y, dtype: 'f4', shape: '2'}, + x: {vals: x, dtype: 'f8'}, + y: {vals: y, dtype: 'f4'}, z: {vals: z, dtype: 'u2', shape: '3,2'} }]) .then(function(gd) { diff --git a/test/plot-schema.json b/test/plot-schema.json index 49e4b2e5463..b3f936b6f59 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -479,7 +479,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, vals, and optionally shape. In this 3rd form, dtype is one of *i1*, *u1*, *c1*, *i2*, *u2*, *i4*, *u4*, *f4*, or *f8*. vals is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either single or multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For exmaple using `dtype`: *f4* and `shape`: *100,5* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, vals, and optionally shape. In this 3rd form, dtype is one of *i1*, *u1*, *c1*, *i2*, *u2*, *i4*, *u4*, *f4*, or *f8*. vals is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For exmaple using `dtype`: *f4* and `shape`: *100,5* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value. `shape` is optional for one dimensional arrays.", "otherOpts": [ "dflt" ], From 1230ba8215294f730d191866be363bfdd5cfa8e6 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 27 Feb 2023 11:30:08 -0500 Subject: [PATCH 050/122] fix comment for big ints --- src/lib/array.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 36d54456235..789d4f809e4 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -67,8 +67,8 @@ var typedArrays = { f8: detectType(Float64Array), // TODO: potentially add Big Int - // I8: detectType(BigInt64Array), // not supported in numpy? - // b8: detectType(BigUint64Array), + // i8: detectType(BigInt64Array), + // u8: detectType(BigUint64Array), }; function isArrayBuffer(a) { From 8215c9f29c682ef841ef424f7d1aa78a9d5564aa Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 27 Feb 2023 11:46:08 -0500 Subject: [PATCH 051/122] inline detect supported types --- src/lib/array.js | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 789d4f809e4..be6ee5f0d75 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -51,24 +51,20 @@ exports.ensureArray = function(out, n) { return out; }; -function detectType(a) { - return typeof a === 'undefined' ? undefined : a; -} - var typedArrays = { - i1: detectType(Int8Array), - u1: detectType(Uint8Array), - c1: detectType(Uint8ClampedArray), // not supported in numpy? - i2: detectType(Int16Array), - u2: detectType(Uint16Array), - i4: detectType(Int32Array), - u4: detectType(Uint32Array), - f4: detectType(Float32Array), - f8: detectType(Float64Array), + i1: typeof Int8Array === 'undefined' ? undefined : Int8Array, + u1: typeof Uint8Array === 'undefined' ? undefined : Uint8Array, + c1: typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray, // not supported in numpy? + 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: detectType(BigInt64Array), - // u8: detectType(BigUint64Array), + // i8: typeof BigInt64Array === 'undefined' ? undefined : BigInt64Array, + // u8: typeof BigUint64Array === 'undefined' ? undefined : BigUint64Array, }; function isArrayBuffer(a) { From 9ecaf5d3a671ffdc8feebd84611dd2375f9a17d4 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 27 Feb 2023 11:51:01 -0500 Subject: [PATCH 052/122] refactor --- src/lib/array.js | 48 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index be6ee5f0d75..456157d8a25 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -52,19 +52,41 @@ exports.ensureArray = function(out, n) { }; var typedArrays = { - i1: typeof Int8Array === 'undefined' ? undefined : Int8Array, - u1: typeof Uint8Array === 'undefined' ? undefined : Uint8Array, - c1: typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray, // not supported in numpy? - 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, + c1: 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, + */ }; function isArrayBuffer(a) { From 938bb777a29ee6148a545947df1a273ebacd3ccf Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 27 Feb 2023 12:14:39 -0500 Subject: [PATCH 053/122] correct var name rowBytes --- src/lib/array.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 456157d8a25..93b90cceef1 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -131,7 +131,7 @@ exports.decodeTypedArraySpec = function(vIn) { var nj, j; var ni = +shape[0]; - var rowBites = BYTES_PER_ELEMENT * ni; + var rowBytes = BYTES_PER_ELEMENT * ni; var pos = 0; if(ndims === 1) { @@ -140,7 +140,7 @@ exports.decodeTypedArraySpec = function(vIn) { nj = +shape[1]; for(j = 0; j < nj; j++) { out[j] = new T(buffer, pos, ni); - pos += rowBites; + pos += rowBytes; } /* @@ -154,7 +154,7 @@ exports.decodeTypedArraySpec = function(vIn) { out[k] = []; for(j = 0; j < nj; j++) { out[k][j] = new T(buffer, pos, ni); - pos += rowBites; + pos += rowBytes; } } */ From d260168429bbf1e976362593b6f74296f3261b23 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 27 Feb 2023 13:06:57 -0500 Subject: [PATCH 054/122] simplify 1d length detection --- src/lib/array.js | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 93b90cceef1..99d1a15637f 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -103,28 +103,15 @@ exports.decodeTypedArraySpec = function(vIn) { if(!T) throw new Error('Error in dtype: "' + dtype + '"'); var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; - var shape; var buffer = v.vals; if(!isArrayBuffer(buffer)) { - if(v.shape === undefined) { - var len = buffer.length; - var n = 0.75 * len; - if(buffer[len - 2] === '=') { - n -= 2; - } else if(buffer[len - 1] === '=') { - n -= 1; - } - - shape = [n / BYTES_PER_ELEMENT]; - } - buffer = b64decode(buffer); } - - if(!shape) { + var shape = v.shape === undefined ? + // detect 1-d length + [buffer.byteLength / BYTES_PER_ELEMENT] : // convert number to string and split to array - shape = ('' + v.shape).split(','); - } + ('' + v.shape).split(','); var ndims = shape.length; From 78438fa70f69a35c90375210aa549f512081bc65 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 27 Feb 2023 13:32:45 -0500 Subject: [PATCH 055/122] rename vals to bdata --- src/lib/array.js | 16 ++++++++-------- src/lib/coerce.js | 4 ++-- src/plots/plots.js | 8 ++++---- test/jasmine/tests/toimage_test.js | 24 ++++++++++++------------ test/plot-schema.json | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 99d1a15637f..71809a53a1a 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -103,7 +103,7 @@ exports.decodeTypedArraySpec = function(vIn) { if(!T) throw new Error('Error in dtype: "' + dtype + '"'); var BYTES_PER_ELEMENT = T.BYTES_PER_ELEMENT; - var buffer = v.vals; + var buffer = v.bdata; if(!isArrayBuffer(buffer)) { buffer = b64decode(buffer); } @@ -149,8 +149,8 @@ exports.decodeTypedArraySpec = function(vIn) { throw new Error('Error in shape: "' + v.shape + '"'); } - // attach dtype, shape & vals to array for json export - out.vals = v.vals; + // attach bdata, dtype & shape to array for json export + out.bdata = v.bdata; out.dtype = v.dtype; out.shape = shape.join(','); @@ -162,19 +162,19 @@ exports.isTypedArraySpec = function(v) { 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') - )) && - - v.hasOwnProperty('vals') && (typeof v.vals === 'string' || isArrayBuffer(v.vals)) + )) ); }; function coerceTypedArraySpec(v) { return { + bdata: v.bdata, dtype: v.dtype, - shape: v.shape, - vals: v.vals + shape: v.shape }; } diff --git a/src/lib/coerce.js b/src/lib/coerce.js index dbf8a146468..7b3247f9037 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -24,10 +24,10 @@ exports.valObjectMeta = { 'but this array can be provided in several forms:', '(1) a regular {array} object', '(2) a typed array (e.g. Float32Array)', - '(3) an object with keys dtype, vals, and optionally shape.', + '(3) an object with keys dtype, bdata, and optionally shape.', 'In this 3rd form, dtype is one of *i1*, *u1*, *c1*,', '*i2*, *u2*, *i4*, *u4*, *f4*, or *f8*.', - 'vals is either a base64-encoded string or the ArrayBuffer of', + '`bdata` is either a base64-encoded string or the ArrayBuffer of', 'an integer or float typed array.', 'For either multi-dimensional arrays you must also', 'provide its dimensions separated by comma via `shape`.', diff --git a/src/plots/plots.js b/src/plots/plots.js index 985004460ed..15d58287a9b 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -2230,16 +2230,16 @@ plots.graphJson = function(gd, dataonly, mode, output, useDefaults, includeConfi var dIsTypedArray = Lib.isTypedArray(d); if((dIsArray || dIsTypedArray) && d.dtype && d.shape) { - var vals = d.vals; + var bdata = d.bdata; return stripObj({ dtype: d.dtype, shape: d.shape, - vals: + bdata: // case of ArrayBuffer - Lib.isArrayBuffer(vals) ? b64encode.encode(vals) : + Lib.isArrayBuffer(bdata) ? b64encode.encode(bdata) : // case of b64 string - vals + bdata }, keepFunction); } diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index ba709d0f927..76dc73d53e6 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -343,9 +343,9 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {vals: x, dtype: 'f8'}, - y: {vals: y, dtype: 'f4'}, - z: {vals: z, dtype: 'u2', shape: '3,2'} + x: {bdata: x, dtype: 'f8'}, + y: {bdata: y, dtype: 'f4'}, + z: {bdata: z, dtype: 'u2', shape: '3,2'} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -366,9 +366,9 @@ describe('Plotly.toImage', function() { expect(trace.visible).toEqual(true); - expect(trace.x.vals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); - expect(trace.y.vals).toEqual('q6qqPquqqr4='); - expect(trace.z.vals).toEqual('AABkAMgALAGQAfQB'); + expect(trace.x.bdata).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); + expect(trace.y.bdata).toEqual('q6qqPquqqr4='); + expect(trace.z.bdata).toEqual('AABkAMgALAGQAfQB'); expect(trace.x.dtype).toEqual('f8'); expect(trace.x.shape).toEqual('3'); @@ -392,9 +392,9 @@ describe('Plotly.toImage', function() { Plotly.newPlot(gd, [{ type: 'surface', - x: {vals: x, dtype: 'f8', shape: '3'}, - y: {vals: y, dtype: 'f4', shape: '2'}, - z: {vals: z, dtype: 'u2', shape: '3,2'} + x: {bdata: x, dtype: 'f8', shape: '3'}, + y: {bdata: y, dtype: 'f4', shape: '2'}, + z: {bdata: z, dtype: 'u2', shape: '3,2'} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -415,9 +415,9 @@ describe('Plotly.toImage', function() { expect(trace.visible).toEqual(true); - expect(trace.x.vals).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); - expect(trace.y.vals).toEqual('q6qqPquqqr4='); - expect(trace.z.vals).toEqual('AABkAMgALAGQAfQB'); + expect(trace.x.bdata).toEqual('VVVVVVVV1b8AAAAAAAAAAFVVVVVVVdU/'); + expect(trace.y.bdata).toEqual('q6qqPquqqr4='); + expect(trace.z.bdata).toEqual('AABkAMgALAGQAfQB'); expect(trace.x.dtype).toEqual('f8'); expect(trace.x.shape).toEqual('3'); diff --git a/test/plot-schema.json b/test/plot-schema.json index b3f936b6f59..33040f1b1a6 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -479,7 +479,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, vals, and optionally shape. In this 3rd form, dtype is one of *i1*, *u1*, *c1*, *i2*, *u2*, *i4*, *u4*, *f4*, or *f8*. vals is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For exmaple using `dtype`: *f4* and `shape`: *100,5* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value. `shape` is optional for one dimensional arrays.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bdata, and optionally shape. In this 3rd form, dtype is one of *i1*, *u1*, *c1*, *i2*, *u2*, *i4*, *u4*, *f4*, or *f8*. `bdata` is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For exmaple using `dtype`: *f4* and `shape`: *100,5* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value. `shape` is optional for one dimensional arrays.", "otherOpts": [ "dflt" ], From c1c54e88a81011bf4a0c42fee6dbc324f79b315c Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 28 Feb 2023 10:47:58 -0500 Subject: [PATCH 056/122] handle buffer for arrayOk attributes - handle line color when marker color is defined by a buffer --- src/components/colorscale/helpers.js | 2 + src/lib/array.js | 2 + src/lib/coerce.js | 27 ++++++---- src/lib/gl_format_color.js | 2 + src/traces/scatter/defaults.js | 8 +-- src/traces/scatter/line_defaults.js | 1 + src/traces/scatter3d/defaults.js | 8 +-- src/traces/scattercarpet/defaults.js | 8 +-- src/traces/scattergeo/defaults.js | 8 +-- src/traces/scattergl/defaults.js | 10 ++-- src/traces/scattermapbox/defaults.js | 10 ++-- src/traces/scatterpolar/defaults.js | 8 +-- src/traces/scatterpolargl/defaults.js | 8 +-- src/traces/scattersmith/defaults.js | 8 +-- src/traces/scatterternary/defaults.js | 8 +-- test/jasmine/tests/toimage_test.js | 77 +++++++++++++++++++++++++++ 16 files changed, 144 insertions(+), 51 deletions(-) diff --git a/src/components/colorscale/helpers.js b/src/components/colorscale/helpers.js index fed32703b22..bc476e0a9b5 100644 --- a/src/components/colorscale/helpers.js +++ b/src/components/colorscale/helpers.js @@ -13,7 +13,9 @@ function hasColorscale(trace, containerStr, colorKey) { var container = containerStr ? Lib.nestedProperty(trace, containerStr).get() || {} : trace; + var color = container[colorKey || 'color']; + if(color && color._inputArray) color = color._inputArray; var isArrayWithOneNumber = false; if(Lib.isArrayOrTypedArray(color)) { diff --git a/src/lib/array.js b/src/lib/array.js index 71809a53a1a..304669e15cd 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -154,6 +154,8 @@ exports.decodeTypedArraySpec = function(vIn) { out.dtype = v.dtype; out.shape = shape.join(','); + vIn._inputArray = out; + return out; }; diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 7b3247f9037..9c4ee38f1ec 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -385,15 +385,24 @@ exports.coerce = function(containerIn, containerOut, attributes, attribute, dflt if(dflt === undefined) dflt = opts.dflt; - /** - * arrayOk: value MAY be an array, then we do no value checking - * at this point, because it can be more complicated than the - * individual form (eg. some array vals can be numbers, even if the - * single values must be color strings) - */ - if(opts.arrayOk && isArrayOrTypedArray(v)) { - propOut.set(v); - return v; + if(opts.arrayOk) { + if(isArrayOrTypedArray(v)) { + /** + * arrayOk: value MAY be an array, then we do no value checking + * at this point, because it can be more complicated than the + * individual form (eg. some array vals can be numbers, even if the + * single values must be color strings) + */ + + propOut.set(v); + return v; + } else { + if(isTypedArraySpec(v)) { + v = decodeTypedArraySpec(v); + propOut.set(v); + return v; + } + } } var coerceFunction = exports.valObjectMeta[opts.valType].coerceFunction; diff --git a/src/lib/gl_format_color.js b/src/lib/gl_format_color.js index 1de4d7ba460..e42170c3d2d 100644 --- a/src/lib/gl_format_color.js +++ b/src/lib/gl_format_color.js @@ -31,6 +31,8 @@ function validateOpacity(opacityIn) { function formatColor(containerIn, opacityIn, len) { var colorIn = containerIn.color; + if(colorIn && colorIn._inputArray) colorIn = colorIn._inputArray; + var isArrayColorIn = isArrayOrTypedArray(colorIn); var isArrayOpacityIn = isArrayOrTypedArray(opacityIn); var cOpts = Colorscale.extractOpts(containerIn); diff --git a/src/traces/scatter/defaults.js b/src/traces/scatter/defaults.js index 9c980ca69de..ce6a5fab07f 100644 --- a/src/traces/scatter/defaults.js +++ b/src/traces/scatter/defaults.js @@ -44,6 +44,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertext'); coerce('mode', defaultMode); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + if(subTypes.hasLines(traceOut)) { handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); @@ -51,10 +55,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('line.simplify'); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); - } - if(subTypes.hasText(traceOut)) { coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); diff --git a/src/traces/scatter/line_defaults.js b/src/traces/scatter/line_defaults.js index fe70e8f847a..bb70e25feb6 100644 --- a/src/traces/scatter/line_defaults.js +++ b/src/traces/scatter/line_defaults.js @@ -8,6 +8,7 @@ module.exports = function lineDefaults(traceIn, traceOut, defaultColor, layout, if(!opts) opts = {}; var markerColor = (traceIn.marker || {}).color; + if(markerColor && markerColor._inputArray) markerColor = markerColor._inputArray; coerce('line.color', defaultColor); diff --git a/src/traces/scatter3d/defaults.js b/src/traces/scatter3d/defaults.js index 2cb922e5be6..06960264749 100644 --- a/src/traces/scatter3d/defaults.js +++ b/src/traces/scatter3d/defaults.js @@ -30,15 +30,15 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('mode'); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noSelect: true, noAngle: true}); + } + if(subTypes.hasLines(traceOut)) { coerce('connectgaps'); handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noSelect: true, noAngle: true}); - } - if(subTypes.hasText(traceOut)) { coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce, {noSelect: true}); diff --git a/src/traces/scattercarpet/defaults.js b/src/traces/scattercarpet/defaults.js index b21f4e956df..0ca56773e37 100644 --- a/src/traces/scattercarpet/defaults.js +++ b/src/traces/scattercarpet/defaults.js @@ -41,16 +41,16 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'; coerce('mode', defaultMode); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + if(subTypes.hasLines(traceOut)) { handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); - } - if(subTypes.hasText(traceOut)) { handleTextDefaults(traceIn, traceOut, layout, coerce); } diff --git a/src/traces/scattergeo/defaults.js b/src/traces/scattergeo/defaults.js index bee2a5d01fe..6f8adb11d98 100644 --- a/src/traces/scattergeo/defaults.js +++ b/src/traces/scattergeo/defaults.js @@ -50,15 +50,15 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertemplate'); coerce('mode'); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + if(subTypes.hasLines(traceOut)) { handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); coerce('connectgaps'); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); - } - if(subTypes.hasText(traceOut)) { coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); diff --git a/src/traces/scattergl/defaults.js b/src/traces/scattergl/defaults.js index a15ff09e254..b19c27bb117 100644 --- a/src/traces/scattergl/defaults.js +++ b/src/traces/scattergl/defaults.js @@ -39,17 +39,17 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertemplate'); coerce('mode', defaultMode); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true}); + coerce('marker.line.width', isOpen || isBubble ? 1 : 0); + } + if(subTypes.hasLines(traceOut)) { coerce('connectgaps'); handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); coerce('line.shape'); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true}); - coerce('marker.line.width', isOpen || isBubble ? 1 : 0); - } - if(subTypes.hasText(traceOut)) { coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); diff --git a/src/traces/scattermapbox/defaults.js b/src/traces/scattermapbox/defaults.js index 93010cca922..262fea25a58 100644 --- a/src/traces/scattermapbox/defaults.js +++ b/src/traces/scattermapbox/defaults.js @@ -31,11 +31,6 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('mode'); coerce('below'); - if(subTypes.hasLines(traceOut)) { - handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noDash: true}); - coerce('connectgaps'); - } - if(subTypes.hasMarkers(traceOut)) { handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noLine: true, noAngle: true}); @@ -50,6 +45,11 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout } } + if(subTypes.hasLines(traceOut)) { + handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noDash: true}); + coerce('connectgaps'); + } + var clusterMaxzoom = coerce2('cluster.maxzoom'); var clusterStep = coerce2('cluster.step'); var clusterColor = coerce2('cluster.color', (traceOut.marker && traceOut.marker.color) || defaultColor); diff --git a/src/traces/scatterpolar/defaults.js b/src/traces/scatterpolar/defaults.js index 5bf2c5e97b9..ba9e6159e57 100644 --- a/src/traces/scatterpolar/defaults.js +++ b/src/traces/scatterpolar/defaults.js @@ -29,16 +29,16 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { coerce('hovertext'); if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + if(subTypes.hasLines(traceOut)) { handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); - } - if(subTypes.hasText(traceOut)) { coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); diff --git a/src/traces/scatterpolargl/defaults.js b/src/traces/scatterpolargl/defaults.js index 6b64bcb3179..d37497d2efa 100644 --- a/src/traces/scatterpolargl/defaults.js +++ b/src/traces/scatterpolargl/defaults.js @@ -29,15 +29,15 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertext'); if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true}); + } + if(subTypes.hasLines(traceOut)) { handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce); coerce('connectgaps'); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {noAngleRef: true, noStandOff: true}); - } - if(subTypes.hasText(traceOut)) { coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); diff --git a/src/traces/scattersmith/defaults.js b/src/traces/scattersmith/defaults.js index d1001487803..1b3c17fada9 100644 --- a/src/traces/scattersmith/defaults.js +++ b/src/traces/scattersmith/defaults.js @@ -28,16 +28,16 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout coerce('hovertext'); if(traceOut.hoveron !== 'fills') coerce('hovertemplate'); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + if(subTypes.hasLines(traceOut)) { handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); - } - if(subTypes.hasText(traceOut)) { coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); diff --git a/src/traces/scatterternary/defaults.js b/src/traces/scatterternary/defaults.js index cff521b24b9..0b7e3a0e630 100644 --- a/src/traces/scatterternary/defaults.js +++ b/src/traces/scatterternary/defaults.js @@ -55,16 +55,16 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var defaultMode = len < constants.PTS_LINESONLY ? 'lines+markers' : 'lines'; coerce('mode', defaultMode); + if(subTypes.hasMarkers(traceOut)) { + handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); + } + if(subTypes.hasLines(traceOut)) { handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce, {backoff: true}); handleLineShapeDefaults(traceIn, traceOut, coerce); coerce('connectgaps'); } - if(subTypes.hasMarkers(traceOut)) { - handleMarkerDefaults(traceIn, traceOut, defaultColor, layout, coerce, {gradient: true}); - } - if(subTypes.hasText(traceOut)) { coerce('texttemplate'); handleTextDefaults(traceIn, traceOut, layout, coerce); diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index 76dc73d53e6..d01872bbf18 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -431,6 +431,83 @@ describe('Plotly.toImage', function() { .then(done, done.fail); }); + [ + 'scatter3d', + 'scattergl', + 'scatter' + ].forEach(function(type) { + it('import & export arrayOk marker.color and marker.size for ' + type, function(done) { + var is3D = type === 'scatter3d'; + + var allX = new Int16Array([-100, 200, -300, 400]); + var allY = new Uint16Array([100, 200, 300, 400]); + var allZ = new Int8Array([-120, -60, 0, 60]); + var allS = new Uint8ClampedArray([0, 60, 120, 240]); + var allC = new Uint8Array([0, 60, 120, 240]); + + var x = b64encodeTypedArray(allX); + var y = b64encodeTypedArray(allY); + var z = b64encodeTypedArray(allZ); + var s = b64encodeTypedArray(allS); + var c = b64encodeTypedArray(allC); + + Plotly.newPlot(gd, [{ + type: type, + x: {bdata: x, dtype: 'i2'}, + y: {bdata: y, dtype: 'u2'}, + z: {bdata: z, dtype: 'i1'}, + marker: { + color: {bdata: c, dtype: 'u1'}, + size: {bdata: s, dtype: 'c1'} + } + }]) + .then(function(gd) { + var trace = gd._fullData[0]; + + expect(trace.visible).toEqual(true); + + expect(trace.x.slice()).toEqual(allX); + expect(trace.y.slice()).toEqual(allY); + if(is3D) expect(trace.z.slice()).toEqual(allZ); + expect(trace.marker.size.slice()).toEqual(allS); + expect(trace.marker.color.slice()).toEqual(allC); + expect(trace.line.color).toEqual('#1f77b4'); + + return Plotly.toImage(gd, imgOpts); + }) + .then(function(fig) { + var trace = JSON.parse(fig).data[0]; + + expect(trace.visible).toEqual(true); + + expect(trace.x.bdata).toEqual('nP/IANT+kAE='); + expect(trace.x.dtype).toEqual('i2'); + expect(trace.x.shape).toEqual('4'); + + expect(trace.y.bdata).toEqual('ZADIACwBkAE='); + expect(trace.y.dtype).toEqual('u2'); + expect(trace.y.shape).toEqual('4'); + + if(is3D) { + expect(trace.z.bdata).toEqual('iMQAPA=='); + expect(trace.z.dtype).toEqual('i1'); + expect(trace.z.shape).toEqual('4'); + } + + expect(trace.marker.size.bdata).toEqual('ADx48A=='); + expect(trace.marker.size.dtype).toEqual('c1'); + expect(trace.marker.size.shape).toEqual('4'); + + expect(trace.marker.color.bdata).toEqual('ADx48A=='); + expect(trace.marker.color.dtype).toEqual('u1'); + expect(trace.marker.color.shape).toEqual('4'); + + expect(trace.marker.colorscale).toBeDefined(); + }) + .then(done, done.fail); + }); + }); + it('export computed margins', function(done) { Plotly.toImage(pieAutoMargin, imgOpts) .then(function(fig) { From 2df73c437b9bc47155d913b09fb81a08837fbd2e Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 28 Feb 2023 16:42:15 -0500 Subject: [PATCH 057/122] add b64 image test --- .circleci/config.yml | 39 +++++++++++++++ .circleci/env_image.sh | 2 + .circleci/test.sh | 6 +++ test/image/convert_b64.py | 99 +++++++++++++++++++++++++++++++++++++ test/image/make_baseline.js | 16 ++++-- test/image/make_baseline.py | 7 +++ 6 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 test/image/convert_b64.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 33047b16956..85f6b141737 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -253,6 +253,25 @@ jobs: paths: - plotly.js + make-baselines-b64: + parallelism: 4 + docker: + - image: circleci/python:3.8.9 + working_directory: ~/plotly.js + steps: + - attach_workspace: + at: ~/ + - run: + name: Install kaleido, plotly.io and required fonts + command: .circleci/env_image.sh + - run: + name: Create all png files + command: .circleci/test.sh make-baselines-b64 + - persist_to_workspace: + root: ~/ + paths: + - plotly.js + test-baselines: docker: - image: circleci/node:16.9.0 @@ -267,6 +286,20 @@ jobs: path: build destination: / + test-baselines-b64: + docker: + - image: circleci/node:16.9.0 + working_directory: ~/plotly.js + steps: + - attach_workspace: + at: ~/ + - run: + name: Compare pixels + command: .circleci/test.sh test-image ; find build -maxdepth 1 -type f -delete + - store_artifacts: + path: build + destination: / + test-baselines-mathjax3: docker: - image: circleci/node:16.9.0 @@ -450,6 +483,12 @@ workflows: - test-baselines-mathjax3: requires: - make-baselines-mathjax3 + - make-baselines-b64: + requires: + - install-and-cibuild + - test-baselines-b64: + requires: + - make-baselines-b64 - make-baselines: requires: - install-and-cibuild diff --git a/.circleci/env_image.sh b/.circleci/env_image.sh index 87d40fa2889..ad546229cfb 100755 --- a/.circleci/env_image.sh +++ b/.circleci/env_image.sh @@ -6,3 +6,5 @@ sudo cp -r .circleci/fonts/ /usr/share/ && \ sudo fc-cache -f && \ # install kaleido & plotly sudo python3 -m pip install kaleido==0.2.1 plotly==5.5.0 --progress-bar off +# install numpy i.e. to convert arrays to typed arrays +sudo python3 -m pip install numpy==1.24.2 \ No newline at end of file diff --git a/.circleci/test.sh b/.circleci/test.sh index ecb9b0649c7..485a0b2b620 100755 --- a/.circleci/test.sh +++ b/.circleci/test.sh @@ -87,6 +87,12 @@ case $1 in exit $EXIT_STATE ;; + make-baselines-b64) + SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | circleci tests split) + python3 test/image/make_baseline.py b64 $SUITE || EXIT_STATE=$? + exit $EXIT_STATE + ;; + make-baselines) SUITE=$(find $ROOT/test/image/mocks/ -type f -printf "%f\n" | sed 's/\.json$//1' | circleci tests split) python3 test/image/make_baseline.py $SUITE || EXIT_STATE=$? diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py new file mode 100644 index 00000000000..4b0d8d4e64d --- /dev/null +++ b/test/image/convert_b64.py @@ -0,0 +1,99 @@ +from sqlite3 import NotSupportedError +import numpy +import base64 + +plotlyjsTypes = { + 'int8': 'i1', + 'uint8': 'u1', + 'int16': 'i2', + 'uint16': 'u2', + 'int32': 'i4', + 'uint32': 'u4', + 'float32': 'f4', + 'float64': 'f8' +} + +# List mainly including keys with type of 'info_array' +# Simply having two items the b64 option is not supported by plotly.js +# This list also includes cases of type 'any' that we don't to be converted +# e.g. in mapbox layers or geojson +rangeKeys = [ + 'geojson', + 'layers', + 'subplots', + 'xaxes', + 'yaxes', + 'domain', + 'range', + 'dtickrange', + 'constraintrange', + 'bounds', + 'parallels', + 'sector', + 'span', + 'zmin', + 'zmax', + 'args', + 'args2', + 'values', + 'groups', + 'selectedpoints', + 'opacityscale', + 'cameraposition' # deprecated +] + +def arraysToB64(obj, newObj) : + for key, val in obj.items() : + if key in rangeKeys : + newObj[key] = val + elif isinstance(val, dict) : + newObj[key] = dict() + arraysToB64(val, newObj[key]) + elif isinstance(val, list) : + try : + arr = numpy.array(val) + except Exception : + newObj[key] = val + continue + + if arr.dtype == 'object' : + newList = list() + for v in val : + if isinstance(v, dict) : + newList.append(arraysToB64(v, dict())) + else : + newList.append(v) + + newObj[key] = newList + else : + # skip converting arrays with 2 items or less + if(arr.ndim == 1 and arr.shape[0] < 3) : + newObj[key] = val + continue + + # convert Big Ints until we could support them in plotly.js + if str(arr.dtype) == 'int64' : + arr = arr.astype(numpy.int32) + elif str(arr.dtype) == 'uint64' : + arr = arr.astype(numpy.uint32) + + if str(arr.dtype) in plotlyjsTypes : + newObj[key] = { + 'dtype': plotlyjsTypes[str(arr.dtype)], + 'bdata': base64.b64encode(arr).decode('ascii') + } + + if(arr.ndim > 1) : + newObj[key]['shape'] = str(arr.shape)[1:-1] + + #print(val) + #print('____________________') + #print(newObj[key]) + #print('____________________') + else : + newObj[key] = val + + else : + newObj[key] = val + + return newObj diff --git a/test/image/make_baseline.js b/test/image/make_baseline.js index 11ab01dfad0..0666465311b 100644 --- a/test/image/make_baseline.js +++ b/test/image/make_baseline.js @@ -29,14 +29,20 @@ var getMockList = require('./assets/get_mock_list'); * * npm run baseline mathjax3 * - */ + * Generate or (re-generate) baselines using b64 typed arrays: + * + * npm run baseline b64 + * +*/ var argv = minimist(process.argv.slice(2), {}); var allMockList = []; -var mathjax3; +var mathjax3, b64; argv._.forEach(function(pattern) { - if(pattern === 'mathjax3') { + if(pattern === 'b64') { + b64 = true; + } else if(pattern === 'mathjax3') { mathjax3 = true; } else { var mockList = getMockList(pattern); @@ -67,7 +73,9 @@ console.log('Please wait for the process to complete.'); var p = spawn( 'python3', [ path.join('test', 'image', 'make_baseline.py'), - (mathjax3 ? 'mathjax3' : '') + '= ' + allMockList.join(' ') + (mathjax3 ? 'mathjax3' : '') + + (b64 ? 'b64' : '') + + '= ' + allMockList.join(' ') ] ); try { diff --git a/test/image/make_baseline.py b/test/image/make_baseline.py index 58c9d40614a..d1e1c8d3cda 100644 --- a/test/image/make_baseline.py +++ b/test/image/make_baseline.py @@ -2,6 +2,7 @@ import sys import json import plotly.io as pio +from convert_b64 import arraysToB64 args = [] if len(sys.argv) == 2 : @@ -110,6 +111,12 @@ if 'height' in layout : height = layout['height'] + if 'b64' in sys.argv or 'b64=' in sys.argv or 'b64-json' in sys.argv : + newFig = dict() + arraysToB64(fig, newFig) + fig = newFig + if 'b64-json' in sys.argv and attempt == 0 : print(json.dumps(fig, indent = 2)) + try : pio.write_image( fig=fig, From 78abdf1fe717a5c2803d0ba9cbd3be9e81f8d0b2 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 1 Mar 2023 18:59:41 -0500 Subject: [PATCH 058/122] reverse order of shape dimensions to match numpy API --- src/lib/array.js | 3 ++- src/lib/coerce.js | 2 +- test/jasmine/tests/toimage_test.js | 8 ++++---- test/plot-schema.json | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 304669e15cd..e2136f57d14 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -113,6 +113,7 @@ exports.decodeTypedArraySpec = function(vIn) { // convert number to string and split to array ('' + v.shape).split(','); + shape.reverse(); // i.e. to match numpy order var ndims = shape.length; var nj, j; @@ -152,7 +153,7 @@ exports.decodeTypedArraySpec = function(vIn) { // attach bdata, dtype & shape to array for json export out.bdata = v.bdata; out.dtype = v.dtype; - out.shape = shape.join(','); + out.shape = shape.reverse().join(','); vIn._inputArray = out; diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 9c4ee38f1ec..1762c385cb0 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -31,7 +31,7 @@ exports.valObjectMeta = { 'an integer or float typed array.', 'For either multi-dimensional arrays you must also', 'provide its dimensions separated by comma via `shape`.', - 'For exmaple using `dtype`: *f4* and `shape`: *100,5* you can', + 'For exmaple using `dtype`: *f4* and `shape`: *5,100* you can', 'declare a 2-D array that has 5 rows and 100 columns', 'containing float32 values i.e. 4 bits per value.', '`shape` is optional for one dimensional arrays.' diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index d01872bbf18..a10dc10f881 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -345,7 +345,7 @@ describe('Plotly.toImage', function() { type: 'surface', x: {bdata: x, dtype: 'f8'}, y: {bdata: y, dtype: 'f4'}, - z: {bdata: z, dtype: 'u2', shape: '3,2'} + z: {bdata: z, dtype: 'u2', shape: '2,3'} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -377,7 +377,7 @@ describe('Plotly.toImage', function() { expect(trace.y.shape).toEqual('2'); expect(trace.z.dtype).toEqual('u2'); - expect(trace.z.shape).toEqual('3,2'); + expect(trace.z.shape).toEqual('2,3'); }) .then(done, done.fail); }); @@ -394,7 +394,7 @@ describe('Plotly.toImage', function() { type: 'surface', x: {bdata: x, dtype: 'f8', shape: '3'}, y: {bdata: y, dtype: 'f4', shape: '2'}, - z: {bdata: z, dtype: 'u2', shape: '3,2'} + z: {bdata: z, dtype: 'u2', shape: '2,3'} }]) .then(function(gd) { var trace = gd._fullData[0]; @@ -426,7 +426,7 @@ describe('Plotly.toImage', function() { expect(trace.y.shape).toEqual('2'); expect(trace.z.dtype).toEqual('u2'); - expect(trace.z.shape).toEqual('3,2'); + expect(trace.z.shape).toEqual('2,3'); }) .then(done, done.fail); }); diff --git a/test/plot-schema.json b/test/plot-schema.json index 33040f1b1a6..c16e600fa58 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -479,7 +479,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bdata, and optionally shape. In this 3rd form, dtype is one of *i1*, *u1*, *c1*, *i2*, *u2*, *i4*, *u4*, *f4*, or *f8*. `bdata` is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For exmaple using `dtype`: *f4* and `shape`: *100,5* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value. `shape` is optional for one dimensional arrays.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bdata, and optionally shape. In this 3rd form, dtype is one of *i1*, *u1*, *c1*, *i2*, *u2*, *i4*, *u4*, *f4*, or *f8*. `bdata` is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For exmaple using `dtype`: *f4* and `shape`: *5,100* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value. `shape` is optional for one dimensional arrays.", "otherOpts": [ "dflt" ], From 3af205d4577d5cabf6fdbf5913d7926bae5569ba Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 1 Mar 2023 16:17:23 -0500 Subject: [PATCH 059/122] handle typed arrays in smith chart tickvals --- src/plots/smith/layout_defaults.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plots/smith/layout_defaults.js b/src/plots/smith/layout_defaults.js index 70529ebc8e7..540e31ff459 100644 --- a/src/plots/smith/layout_defaults.js +++ b/src/plots/smith/layout_defaults.js @@ -17,6 +17,8 @@ var constants = require('./constants'); var axisNames = constants.axisNames; var makeImagDflt = memoize(function(realTickvals) { + if(Lib.isTypedArray(realTickvals)) realTickvals = Array.from(realTickvals); + return realTickvals.slice().reverse().map(function(x) { return -x; }) .concat([0]) .concat(realTickvals); From bbb2c0fe97ce7c638ae5021777e3d8fc22a70720 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 1 Mar 2023 16:21:36 -0500 Subject: [PATCH 060/122] handle typed arrays in box --- src/traces/box/calc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/traces/box/calc.js b/src/traces/box/calc.js index 83eadb0b1f8..11c74eade6b 100644 --- a/src/traces/box/calc.js +++ b/src/traces/box/calc.js @@ -266,6 +266,7 @@ module.exports = function calc(gd, trace) { } } + if(trace.notched && Lib.isTypedArray(valArray)) valArray = Array.from(valArray); trace._extremes[valAxis._id] = Axes.findExtremes(valAxis, trace.notched ? valArray.concat([minLowerNotch, maxUpperNotch]) : valArray, {padded: true} From bd4d2eb4bf1f4f5f142768e8a50cab1be9ead080 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 1 Mar 2023 17:04:52 -0500 Subject: [PATCH 061/122] handle typed arrays in contour --- src/traces/contour/constraint_mapping.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/traces/contour/constraint_mapping.js b/src/traces/contour/constraint_mapping.js index ba1560ccb7d..abfd601f32a 100644 --- a/src/traces/contour/constraint_mapping.js +++ b/src/traces/contour/constraint_mapping.js @@ -1,5 +1,9 @@ 'use strict'; +var isArrayOrTypedArray = require('../../lib/array').isArrayOrTypedArray; +var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; +var decodeTypedArraySpec = require('../../lib/array').decodeTypedArraySpec; + var filterOps = require('../../constants/filter_ops'); var isNumeric = require('fast-isnumeric'); @@ -16,7 +20,8 @@ module.exports = { // This does not in any way shape or form support calendars. It's adapted from // transforms/filter.js. function coerceValue(operation, value) { - var hasArrayValue = Array.isArray(value); + if(isTypedArraySpec(value)) value = decodeTypedArraySpec(value); + var hasArrayValue = isArrayOrTypedArray(value); var coercedValue; From 60dd1d5ffec3e35af8f67c3dd1ba6c28efbbf5c8 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 1 Mar 2023 21:10:07 -0500 Subject: [PATCH 062/122] handle typed arrays for tickvals --- src/plots/cartesian/tick_value_defaults.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plots/cartesian/tick_value_defaults.js b/src/plots/cartesian/tick_value_defaults.js index 92d83983186..68b9207ee62 100644 --- a/src/plots/cartesian/tick_value_defaults.js +++ b/src/plots/cartesian/tick_value_defaults.js @@ -2,6 +2,8 @@ var cleanTicks = require('./clean_ticks'); var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; +var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; +var decodeTypedArraySpec = require('../../lib/array').decodeTypedArraySpec; module.exports = function handleTickValueDefaults(containerIn, containerOut, coerce, axType, opts) { if(!opts) opts = {}; @@ -12,6 +14,8 @@ module.exports = function handleTickValueDefaults(containerIn, containerOut, coe function readInput(attr) { var v = cIn[attr]; + if(isTypedArraySpec(v)) v = decodeTypedArraySpec(v); + return ( v !== undefined ) ? v : (cOut._template || {})[attr]; From 8309136bb0dd85b761af86d3ffe1d5a396a83136 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 1 Mar 2023 21:23:21 -0500 Subject: [PATCH 063/122] handle typed arrays in bar text --- src/traces/bar/helpers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/bar/helpers.js b/src/traces/bar/helpers.js index 011c866b479..d1f30a23246 100644 --- a/src/traces/bar/helpers.js +++ b/src/traces/bar/helpers.js @@ -53,7 +53,7 @@ exports.coerceEnumerated = function(attributeDefinition, value, defaultValue) { exports.getValue = function(arrayOrScalar, index) { var value; - if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar; + if(!isArrayOrTypedArray(arrayOrScalar)) value = arrayOrScalar; else if(index < arrayOrScalar.length) value = arrayOrScalar[index]; return value; }; From 25d3b4f69cf180bb50bdd153b06f869a4f5bb2d0 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 1 Mar 2023 21:29:42 -0500 Subject: [PATCH 064/122] handle typed arrays in pie --- src/traces/pie/helpers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/traces/pie/helpers.js b/src/traces/pie/helpers.js index 25d35d09e42..1aab36fa8c7 100644 --- a/src/traces/pie/helpers.js +++ b/src/traces/pie/helpers.js @@ -21,7 +21,7 @@ exports.formatPieValue = function formatPieValue(v, separators) { }; exports.getFirstFilled = function getFirstFilled(array, indices) { - if(!Array.isArray(array)) return; + if(!Lib.isArrayOrTypedArray(array)) return; for(var i = 0; i < indices.length; i++) { var v = array[indices[i]]; if(v || v === 0 || v === '') return v; @@ -29,7 +29,7 @@ exports.getFirstFilled = function getFirstFilled(array, indices) { }; exports.castOption = function castOption(item, indices) { - if(Array.isArray(item)) return exports.getFirstFilled(item, indices); + if(Lib.isArrayOrTypedArray(item)) return exports.getFirstFilled(item, indices); else if(item) return item; }; From 2059d2a2b8a093fe3193c25dde2b3a5c69634995 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 2 Mar 2023 09:22:24 -0500 Subject: [PATCH 065/122] handle typed arrays in axes ticktext, hoverlabel & getDataConversions --- src/plots/cartesian/axes.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plots/cartesian/axes.js b/src/plots/cartesian/axes.js index 971bb438cfb..45358b6a51a 100644 --- a/src/plots/cartesian/axes.js +++ b/src/plots/cartesian/axes.js @@ -228,7 +228,7 @@ var getDataConversions = axes.getDataConversions = function(gd, trace, target, t // In the case of an array target, make a mock data array // and call supplyDefaults to the data type and // setup the data-to-calc method. - if(Array.isArray(d2cTarget)) { + if(Lib.isArrayOrTypedArray(d2cTarget)) { ax = { type: autoType(targetArray, undefined, { autotypenumbers: gd._fullLayout.autotypenumbers @@ -1282,7 +1282,7 @@ function arrayTicks(ax) { // without a text array, just format the given values as any other ticks // except with more precision to the numbers - if(!Array.isArray(text)) text = []; + if(!Lib.isArrayOrTypedArray(text)) text = []; for(var i = 0; i < vals.length; i++) { var vali = tickVal2l(vals[i]); @@ -1618,7 +1618,7 @@ axes.tickText = function(ax, x, hover, noSuffixPrefix) { var tickVal2l = axType === 'category' ? ax.d2l_noadd : ax.d2l; var i; - if(arrayMode && Array.isArray(ax.ticktext)) { + if(arrayMode && Lib.isArrayOrTypedArray(ax.ticktext)) { var rng = Lib.simpleMap(ax.range, ax.r2l); var minDiff = (Math.abs(rng[1] - rng[0]) - (ax._lBreaks || 0)) / 10000; @@ -1697,8 +1697,8 @@ axes.tickText = function(ax, x, hover, noSuffixPrefix) { axes.hoverLabelText = function(ax, values, hoverformat) { if(hoverformat) ax = Lib.extendFlat({}, ax, {hoverformat: hoverformat}); - var val = Array.isArray(values) ? values[0] : values; - var val2 = Array.isArray(values) ? values[1] : undefined; + var val = Lib.isArrayOrTypedArray(values) ? values[0] : values; + var val2 = Lib.isArrayOrTypedArray(values) ? values[1] : undefined; if(val2 !== undefined && val2 !== val) { return ( axes.hoverLabelText(ax, val, hoverformat) + ' - ' + From f66010543b4dd7c9237769eb39a91dde39ac6c2c Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 2 Mar 2023 10:00:51 -0500 Subject: [PATCH 066/122] handle typed arrays for bubble charts --- src/traces/scatter/subtypes.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/traces/scatter/subtypes.js b/src/traces/scatter/subtypes.js index 780017eb5fa..ef463a51065 100644 --- a/src/traces/scatter/subtypes.js +++ b/src/traces/scatter/subtypes.js @@ -1,6 +1,7 @@ 'use strict'; var Lib = require('../../lib'); +var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; module.exports = { hasLines: function(trace) { @@ -22,7 +23,10 @@ module.exports = { }, isBubble: function(trace) { - return Lib.isPlainObject(trace.marker) && - Lib.isArrayOrTypedArray(trace.marker.size); + var marker = trace.marker; + return Lib.isPlainObject(marker) && ( + Lib.isArrayOrTypedArray(marker.size) || + isTypedArraySpec(marker.size) + ); } }; From ca07e89889b89e922e62efa9e40bbeaa7367d734 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 2 Mar 2023 10:39:15 -0500 Subject: [PATCH 067/122] handle typed arrays in pie --- src/traces/pie/defaults.js | 4 ++-- src/traces/pie/plot.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/traces/pie/defaults.js b/src/traces/pie/defaults.js index 806897d12dc..69055b8dfd3 100644 --- a/src/traces/pie/defaults.js +++ b/src/traces/pie/defaults.js @@ -7,7 +7,7 @@ var handleDomainDefaults = require('../../plots/domain').defaults; var handleText = require('../bar/defaults').handleText; function handleLabelsAndValues(labels, values) { - var hasLabels = Array.isArray(labels); + var hasLabels = Lib.isArrayOrTypedArray(labels); var hasValues = Lib.isArrayOrTypedArray(values); var len = Math.min( hasLabels ? labels.length : Infinity, @@ -72,7 +72,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { var textData = coerce('text'); var textTemplate = coerce('texttemplate'); var textInfo; - if(!textTemplate) textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent'); + if(!textTemplate) textInfo = coerce('textinfo', Lib.isArrayOrTypedArray(textData) ? 'text+percent' : 'percent'); coerce('hovertext'); coerce('hovertemplate'); diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js index a6b803aed35..359a70acfde 100644 --- a/src/traces/pie/plot.js +++ b/src/traces/pie/plot.js @@ -841,7 +841,7 @@ function getMaxPull(trace) { if(!maxPull) return 0; var j; - if(Array.isArray(maxPull)) { + if(Lib.isArrayOrTypedArray(maxPull)) { maxPull = 0; for(j = 0; j < trace.pull.length; j++) { if(trace.pull[j] > maxPull) maxPull = trace.pull[j]; @@ -874,7 +874,7 @@ function scootLabels(quadrants, trace) { if(newExtraY * yDiffSign > 0) thisPt.labelExtraY = newExtraY; // make sure this label doesn't overlap any slices - if(!Array.isArray(trace.pull)) return; // this can only happen with array pulls + if(!Lib.isArrayOrTypedArray(trace.pull)) return; // this can only happen with array pulls for(i = 0; i < wholeSide.length; i++) { otherPt = wholeSide[i]; From 0f2ef235ce16ec2fba6aae8657dabc879b3b7d63 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 2 Mar 2023 10:46:20 -0500 Subject: [PATCH 068/122] handle typed arrays in parcats --- src/traces/parcats/defaults.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/traces/parcats/defaults.js b/src/traces/parcats/defaults.js index 6a0c436a26f..83246e21f47 100644 --- a/src/traces/parcats/defaults.js +++ b/src/traces/parcats/defaults.js @@ -1,6 +1,7 @@ 'use strict'; var Lib = require('../../lib'); +var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; var hasColorscale = require('../../components/colorscale/helpers').hasColorscale; var colorscaleDefaults = require('../../components/colorscale/defaults'); var handleDomainDefaults = require('../../plots/domain').defaults; @@ -44,7 +45,7 @@ function dimensionDefaults(dimensionIn, dimensionOut) { // Category level var arrayIn = dimensionIn.categoryarray; - var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0); + var isValidArray = (Lib.isArrayOrTypedArray(arrayIn) && arrayIn.length > 0) || isTypedArraySpec(arrayIn); var orderDefault; if(isValidArray) orderDefault = 'array'; From 832af54c5d77810104cf78e088ab5c9aa1d9589d Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 2 Mar 2023 10:54:27 -0500 Subject: [PATCH 069/122] allow 3d arrays used e.g. by image trace --- src/lib/array.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index e2136f57d14..9de165ee682 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -114,7 +114,7 @@ exports.decodeTypedArraySpec = function(vIn) { ('' + v.shape).split(','); shape.reverse(); // i.e. to match numpy order - var ndims = shape.length; + var ndim = shape.length; var nj, j; var ni = +shape[0]; @@ -122,20 +122,15 @@ exports.decodeTypedArraySpec = function(vIn) { var rowBytes = BYTES_PER_ELEMENT * ni; var pos = 0; - if(ndims === 1) { + if(ndim === 1) { out = new T(buffer); - } else if(ndims === 2) { + } else if(ndim === 2) { nj = +shape[1]; for(j = 0; j < nj; j++) { out[j] = new T(buffer, pos, ni); pos += rowBytes; } - /* - - // 3d arrays are not supported in traces e.g. volume & isosurface - // once supported we could uncomment this part - - } else if(ndims === 3) { + } else if(ndim === 3) { nj = +shape[1]; var nk = +shape[2]; for(var k = 0; k < nk; k++) { @@ -145,9 +140,8 @@ exports.decodeTypedArraySpec = function(vIn) { pos += rowBytes; } } - */ } else { - throw new Error('Error in shape: "' + v.shape + '"'); + throw new Error('ndim: ' + ndim + 'is not supported with the shape:"' + v.shape + '"'); } // attach bdata, dtype & shape to array for json export From 142e353a7331273a76932fc2c3beb8f68392591f Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 2 Mar 2023 17:39:45 -0500 Subject: [PATCH 070/122] handle typed arrays in scattergeo --- src/traces/scattergeo/calc.js | 6 +++--- src/traces/scattergeo/hover.js | 4 +++- src/traces/scattergeo/plot.js | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/traces/scattergeo/calc.js b/src/traces/scattergeo/calc.js index cdb811146f6..65d7e6f0b2d 100644 --- a/src/traces/scattergeo/calc.js +++ b/src/traces/scattergeo/calc.js @@ -6,15 +6,15 @@ var BADNUM = require('../../constants/numerical').BADNUM; var calcMarkerColorscale = require('../scatter/colorscale_calc'); var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); var calcSelection = require('../scatter/calc_selection'); - -var _ = require('../../lib')._; +var Lib = require('../../lib'); +var _ = Lib._; function isNonBlankString(v) { return v && typeof v === 'string'; } module.exports = function calc(gd, trace) { - var hasLocationData = Array.isArray(trace.locations); + var hasLocationData = Lib.isArrayOrTypedArray(trace.locations); var len = hasLocationData ? trace.locations.length : trace._length; var calcTrace = new Array(len); diff --git a/src/traces/scattergeo/hover.js b/src/traces/scattergeo/hover.js index b9d54ae6f7d..e35f809fbe0 100644 --- a/src/traces/scattergeo/hover.js +++ b/src/traces/scattergeo/hover.js @@ -1,6 +1,8 @@ 'use strict'; var Fx = require('../../components/fx'); +var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; + var BADNUM = require('../../constants/numerical').BADNUM; var getTraceColor = require('../scatter/get_trace_color'); @@ -76,7 +78,7 @@ function getExtraText(trace, pt, pointData, labels) { attributes.hoverinfo.flags : hoverinfo.split('+'); - var hasLocation = parts.indexOf('location') !== -1 && Array.isArray(trace.locations); + var hasLocation = parts.indexOf('location') !== -1 && isTypedArraySpec(trace.locations); var hasLon = (parts.indexOf('lon') !== -1); var hasLat = (parts.indexOf('lat') !== -1); var hasText = (parts.indexOf('text') !== -1); diff --git a/src/traces/scattergeo/plot.js b/src/traces/scattergeo/plot.js index 29d57b2c95a..9124900e8b3 100644 --- a/src/traces/scattergeo/plot.js +++ b/src/traces/scattergeo/plot.js @@ -72,7 +72,7 @@ function calcGeoJSON(calcTrace, fullLayout) { var len = trace._length; var i, calcPt; - if(Array.isArray(trace.locations)) { + if(Lib.isArrayOrTypedArray(trace.locations)) { var locationmode = trace.locationmode; var features = locationmode === 'geojson-id' ? geoUtils.extractTraceFeature(calcTrace) : From 1b58c43c92e2e6ff6d66d832527fc1977676b351 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 2 Mar 2023 19:27:25 -0500 Subject: [PATCH 071/122] handle typed arrays in heatmap hover --- src/traces/heatmap/hover.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/traces/heatmap/hover.js b/src/traces/heatmap/hover.js index d28c2998c37..b67317b26eb 100644 --- a/src/traces/heatmap/hover.js +++ b/src/traces/heatmap/hover.js @@ -2,6 +2,7 @@ var Fx = require('../../components/fx'); var Lib = require('../../lib'); +var isArrayOrTypedArray = Lib.isArrayOrTypedArray; var Axes = require('../../plots/cartesian/axes'); var extractOpts = require('../../components/colorscale').extractOpts; @@ -96,9 +97,9 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode, opts) { if(zVal === undefined && !trace.hoverongaps) return; var text; - if(Array.isArray(cd0.hovertext) && Array.isArray(cd0.hovertext[ny])) { + if(isArrayOrTypedArray(cd0.hovertext) && isArrayOrTypedArray(cd0.hovertext[ny])) { text = cd0.hovertext[ny][nx]; - } else if(Array.isArray(cd0.text) && Array.isArray(cd0.text[ny])) { + } else if(isArrayOrTypedArray(cd0.text) && isArrayOrTypedArray(cd0.text[ny])) { text = cd0.text[ny][nx]; } From ff305fcf0038b80449e4738f135c737511cb569c Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 2 Mar 2023 19:28:31 -0500 Subject: [PATCH 072/122] handle typed arrays in contour --- src/traces/contour/constraint_defaults.js | 5 +++-- src/traces/contour/plot.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/traces/contour/constraint_defaults.js b/src/traces/contour/constraint_defaults.js index d6804e0876a..2b0178d940f 100644 --- a/src/traces/contour/constraint_defaults.js +++ b/src/traces/contour/constraint_defaults.js @@ -8,6 +8,7 @@ var addOpacity = Color.addOpacity; var opacity = Color.opacity; var filterOps = require('../../constants/filter_ops'); +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var CONSTRAINT_REDUCTION = filterOps.CONSTRAINT_REDUCTION; var COMPARISON_OPS2 = filterOps.COMPARISON_OPS2; @@ -50,7 +51,7 @@ function handleConstraintValueDefaults(coerce, contours) { // Requires an array of two numbers: coerce('contours.value', [0, 1]); - if(!Array.isArray(contours.value)) { + if(!isArrayOrTypedArray(contours.value)) { if(isNumeric(contours.value)) { zvalue = parseFloat(contours.value); contours.value = [zvalue, zvalue + 1]; @@ -73,7 +74,7 @@ function handleConstraintValueDefaults(coerce, contours) { coerce('contours.value', 0); if(!isNumeric(contours.value)) { - if(Array.isArray(contours.value)) { + if(isArrayOrTypedArray(contours.value)) { contours.value = parseFloat(contours.value[0]); } else { contours.value = 0; diff --git a/src/traces/contour/plot.js b/src/traces/contour/plot.js index e1728aca3c4..cf93b835612 100644 --- a/src/traces/contour/plot.js +++ b/src/traces/contour/plot.js @@ -403,7 +403,7 @@ exports.labelFormatter = function(gd, cd0) { } else { if(contours.type === 'constraint') { var value = contours.value; - if(Array.isArray(value)) { + if(Lib.isArrayOrTypedArray(value)) { formatAxis.range = [value[0], value[value.length - 1]]; } else formatAxis.range = [value, value]; } else { From d44d2d23d4e82b27bc7eef9633bbec6dc3806d34 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 3 Mar 2023 11:55:26 -0500 Subject: [PATCH 073/122] handle typed arrays in scattergl convert --- src/traces/scattergl/convert.js | 45 +++++++++++++++++---------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/traces/scattergl/convert.js b/src/traces/scattergl/convert.js index 66c43d753cb..5a477e493e7 100644 --- a/src/traces/scattergl/convert.js +++ b/src/traces/scattergl/convert.js @@ -6,6 +6,7 @@ var rgba = require('color-normalize'); var Registry = require('../../registry'); var Lib = require('../../lib'); +var isArrayOrTypedArray = Lib.isArrayOrTypedArray; var Drawing = require('../../components/drawing'); var AxisIDs = require('../../plots/cartesian/axis_ids'); @@ -54,7 +55,7 @@ function convertStyle(gd, trace) { opts.markerSel = convertMarkerSelection(gd, trace, trace.selected); opts.markerUnsel = convertMarkerSelection(gd, trace, trace.unselected); - if(!trace.unselected && Lib.isArrayOrTypedArray(trace.marker.opacity)) { + if(!trace.unselected && isArrayOrTypedArray(trace.marker.opacity)) { var mo = trace.marker.opacity; opts.markerUnsel.opacity = new Array(mo.length); for(i = 0; i < mo.length; i++) { @@ -102,7 +103,7 @@ function convertTextStyle(gd, trace) { var count = trace._length; var textfontIn = trace.textfont; var textpositionIn = trace.textposition; - var textPos = Array.isArray(textpositionIn) ? textpositionIn : [textpositionIn]; + var textPos = isArrayOrTypedArray(textpositionIn) ? textpositionIn : [textpositionIn]; var tfc = textfontIn.color; var tfs = textfontIn.size; var tff = textfontIn.family; @@ -130,7 +131,7 @@ function convertTextStyle(gd, trace) { optsOut.text.push(Lib.texttemplateString(txt(i), labels, d3locale, pointValues, d, meta)); } } else { - if(Array.isArray(trace.text) && trace.text.length < count) { + if(isArrayOrTypedArray(trace.text) && trace.text.length < count) { // if text array is shorter, we'll need to append to it, so let's slice to prevent mutating optsOut.text = trace.text.slice(); } else { @@ -138,7 +139,7 @@ function convertTextStyle(gd, trace) { } } // pad text array with empty strings - if(Array.isArray(optsOut.text)) { + if(isArrayOrTypedArray(optsOut.text)) { for(i = optsOut.text.length; i < count; i++) { optsOut.text[i] = ''; } @@ -174,7 +175,7 @@ function convertTextStyle(gd, trace) { } } - if(Array.isArray(tfc)) { + if(isArrayOrTypedArray(tfc)) { optsOut.color = new Array(count); for(i = 0; i < count; i++) { optsOut.color[i] = tfc[i]; @@ -183,7 +184,7 @@ function convertTextStyle(gd, trace) { optsOut.color = tfc; } - if(Lib.isArrayOrTypedArray(tfs) || Array.isArray(tff)) { + if(isArrayOrTypedArray(tfs) || isArrayOrTypedArray(tff)) { // if any textfont param is array - make render a batch optsOut.font = new Array(count); for(i = 0; i < count; i++) { @@ -191,12 +192,12 @@ function convertTextStyle(gd, trace) { fonti.size = ( Lib.isTypedArray(tfs) ? tfs[i] : - Array.isArray(tfs) ? ( + isArrayOrTypedArray(tfs) ? ( isNumeric(tfs[i]) ? tfs[i] : 0 ) : tfs ) * plotGlPixelRatio; - fonti.family = Array.isArray(tff) ? tff[i] : tff; + fonti.family = isArrayOrTypedArray(tff) ? tff[i] : tff; } } else { // if both are single values, make render fast single-value @@ -213,13 +214,13 @@ function convertMarkerStyle(gd, trace) { var optsOut = {}; var i; - var multiSymbol = Lib.isArrayOrTypedArray(optsIn.symbol); - var multiAngle = Lib.isArrayOrTypedArray(optsIn.angle); - var multiColor = Lib.isArrayOrTypedArray(optsIn.color); - var multiLineColor = Lib.isArrayOrTypedArray(optsIn.line.color); - var multiOpacity = Lib.isArrayOrTypedArray(optsIn.opacity); - var multiSize = Lib.isArrayOrTypedArray(optsIn.size); - var multiLineWidth = Lib.isArrayOrTypedArray(optsIn.line.width); + var multiSymbol = isArrayOrTypedArray(optsIn.symbol); + var multiAngle = isArrayOrTypedArray(optsIn.angle); + var multiColor = isArrayOrTypedArray(optsIn.color); + var multiLineColor = isArrayOrTypedArray(optsIn.line.color); + var multiOpacity = isArrayOrTypedArray(optsIn.opacity); + var multiSize = isArrayOrTypedArray(optsIn.size); + var multiLineWidth = isArrayOrTypedArray(optsIn.line.width); var isOpen; if(!multiSymbol) isOpen = helpers.isOpenSymbol(optsIn.symbol); @@ -236,28 +237,28 @@ function convertMarkerStyle(gd, trace) { var colors = formatColor(optsIn, optsIn.opacity, count); var borderColors = formatColor(optsIn.line, optsIn.opacity, count); - if(!Array.isArray(borderColors[0])) { + if(!isArrayOrTypedArray(borderColors[0])) { var borderColor = borderColors; borderColors = Array(count); for(i = 0; i < count; i++) { borderColors[i] = borderColor; } } - if(!Array.isArray(colors[0])) { + if(!isArrayOrTypedArray(colors[0])) { var color = colors; colors = Array(count); for(i = 0; i < count; i++) { colors[i] = color; } } - if(!Array.isArray(symbols)) { + if(!isArrayOrTypedArray(symbols)) { var symbol = symbols; symbols = Array(count); for(i = 0; i < count; i++) { symbols[i] = symbol; } } - if(!Array.isArray(angles)) { + if(!isArrayOrTypedArray(angles)) { var angle = angles; angles = Array(count); for(i = 0; i < count; i++) { @@ -642,12 +643,12 @@ function convertTextPosition(gd, trace, textOpts, markerOpts) { for(i = 0; i < count; i++) { var ms = markerOpts.sizes ? markerOpts.sizes[i] : markerOpts.size; - var fs = Array.isArray(fontOpts) ? fontOpts[i].size : fontOpts.size; + var fs = isArrayOrTypedArray(fontOpts) ? fontOpts[i].size : fontOpts.size; - var a = Array.isArray(align) ? + var a = isArrayOrTypedArray(align) ? (align.length > 1 ? align[i] : align[0]) : align; - var b = Array.isArray(baseline) ? + var b = isArrayOrTypedArray(baseline) ? (baseline.length > 1 ? baseline[i] : baseline[0]) : baseline; From 14fed21df0d1572c7b58f386924dc3db86592e84 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 3 Mar 2023 14:25:20 -0500 Subject: [PATCH 074/122] handle typed array fix rendering of contour_transposed-irregular --- src/traces/heatmap/make_bound_array.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/heatmap/make_bound_array.js b/src/traces/heatmap/make_bound_array.js index 817ac4471a1..d6b30629cdb 100644 --- a/src/traces/heatmap/make_bound_array.js +++ b/src/traces/heatmap/make_bound_array.js @@ -22,7 +22,7 @@ module.exports = function makeBoundArray(trace, arrayIn, v0In, dvIn, numbricks, // and extend it linearly based on the last two points if(len <= numbricks) { // contour plots only want the centers - if(isContour || isGL2D) arrayOut = arrayIn.slice(0, numbricks); + if(isContour || isGL2D) arrayOut = Array.from(arrayIn).slice(0, numbricks); else if(numbricks === 1) { arrayOut = [arrayIn[0] - 0.5, arrayIn[0] + 0.5]; } else { From 5902656b4d535dfeeb7dc6c13c271cf66f25987b Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Mar 2023 13:52:50 -0500 Subject: [PATCH 075/122] handle typed arrays in table --- src/traces/table/data_preparation_helper.js | 13 +++++++++---- src/traces/table/plot.js | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/traces/table/data_preparation_helper.js b/src/traces/table/data_preparation_helper.js index 2fc0ee68ad8..c7949b48d41 100644 --- a/src/traces/table/data_preparation_helper.js +++ b/src/traces/table/data_preparation_helper.js @@ -3,6 +3,7 @@ var c = require('./constants'); var extendFlat = require('../../lib/extend').extendFlat; var isNumeric = require('fast-isnumeric'); +var isArrayOrTypedArray = require('../../lib/array').isArrayOrTypedArray; // pure functions, don't alter but passes on `gd` and parts of `trace` without deep copying module.exports = function calc(gd, trace) { @@ -35,9 +36,13 @@ module.exports = function calc(gd, trace) { var headerRowBlocks = makeRowBlock(anchorToHeaderRowBlock, []); var rowBlocks = makeRowBlock(anchorToRowBlock, headerRowBlocks); var uniqueKeys = {}; - var columnOrder = trace._fullInput.columnorder.concat(slicer(cellsValues.map(function(d, i) {return i;}))); + + var columnOrder = trace._fullInput.columnorder; + if(isArrayOrTypedArray(columnOrder)) columnOrder = Array.from(columnOrder); + columnOrder = columnOrder.concat(slicer(cellsValues.map(function(d, i) {return i;}))); + var columnWidths = headerValues.map(function(d, i) { - var value = Array.isArray(trace.columnwidth) ? + var value = isArrayOrTypedArray(trace.columnwidth) ? trace.columnwidth[Math.min(i, trace.columnwidth.length - 1)] : trace.columnwidth; return isNumeric(value) ? Number(value) : 1; @@ -95,7 +100,7 @@ module.exports = function calc(gd, trace) { }; function arrayMax(maybeArray) { - if(Array.isArray(maybeArray)) { + if(isArrayOrTypedArray(maybeArray)) { var max = 0; for(var i = 0; i < maybeArray.length; i++) { max = Math.max(max, arrayMax(maybeArray[i])); @@ -115,7 +120,7 @@ function squareStringMatrix(matrixIn) { var maxLen = 0; var i; for(i = 0; i < matrix.length; i++) { - if(!Array.isArray(matrix[i])) matrix[i] = [matrix[i]]; + if(!isArrayOrTypedArray(matrix[i])) matrix[i] = [matrix[i]]; minLen = Math.min(minLen, matrix[i].length); maxLen = Math.max(maxLen, matrix[i].length); } diff --git a/src/traces/table/plot.js b/src/traces/table/plot.js index 98480fa976a..972e5020903 100644 --- a/src/traces/table/plot.js +++ b/src/traces/table/plot.js @@ -600,9 +600,9 @@ function columnMoved(gd, calcdata, indices) { } function gridPick(spec, col, row) { - if(Array.isArray(spec)) { + if(Lib.isArrayOrTypedArray(spec)) { var column = spec[Math.min(col, spec.length - 1)]; - if(Array.isArray(column)) { + if(Lib.isArrayOrTypedArray(column)) { return column[Math.min(row, column.length - 1)]; } else { return column; From 590ce4f7cd577b7568e44c9b48140288be5b808d Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Mar 2023 15:14:33 -0500 Subject: [PATCH 076/122] do not convert to in32 when not in bounds --- test/image/convert_b64.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 4b0d8d4e64d..ea98bff1d13 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -13,6 +13,9 @@ 'float64': 'f8' } +int32bounds = numpy.iinfo(numpy.int32) +uint32bounds = numpy.iinfo(numpy.uint32) + # List mainly including keys with type of 'info_array' # Simply having two items the b64 option is not supported by plotly.js # This list also includes cases of type 'any' that we don't to be converted @@ -73,8 +76,16 @@ def arraysToB64(obj, newObj) : # convert Big Ints until we could support them in plotly.js if str(arr.dtype) == 'int64' : + if arr.max() > int32bounds.max or arr.min() < int32bounds.min : + newObj[key] = val + continue + arr = arr.astype(numpy.int32) elif str(arr.dtype) == 'uint64' : + if arr.max() > uint32bounds.max or arr.min() < uint32bounds.min : + newObj[key] = val + continue + arr = arr.astype(numpy.uint32) if str(arr.dtype) in plotlyjsTypes : From c7f0e10412825df5f644ba99b99f89cda255f6dc Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Mar 2023 15:44:31 -0500 Subject: [PATCH 077/122] handle typed arrays in parcoords --- src/traces/parcoords/parcoords.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/traces/parcoords/parcoords.js b/src/traces/parcoords/parcoords.js index 8bce2766343..6a2c72d5a67 100644 --- a/src/traces/parcoords/parcoords.js +++ b/src/traces/parcoords/parcoords.js @@ -2,6 +2,7 @@ var d3 = require('@plotly/d3'); var Lib = require('../../lib'); +var isArrayOrTypedArray = Lib.isArrayOrTypedArray; var numberFormat = Lib.numberFormat; var rgba = require('color-rgba'); @@ -237,7 +238,7 @@ function viewModel(state, callbacks, model) { var key = dimension.label + (foundKey ? '__' + foundKey : ''); var specifiedConstraint = dimension.constraintrange; var filterRangeSpecified = specifiedConstraint && specifiedConstraint.length; - if(filterRangeSpecified && !Array.isArray(specifiedConstraint[0])) { + if(filterRangeSpecified && !isArrayOrTypedArray(specifiedConstraint[0])) { specifiedConstraint = [specifiedConstraint]; } var filterRange = filterRangeSpecified ? @@ -265,11 +266,13 @@ function viewModel(state, callbacks, model) { var ticktext; function makeTickItem(v, i) { return {val: v, text: ticktext[i]}; } function sortTickItem(a, b) { return a.val - b.val; } - if(Array.isArray(tickvals) && tickvals.length) { + if(isArrayOrTypedArray(tickvals) && tickvals.length) { + if(Lib.isTypedArray(tickvals)) tickvals = Array.from(tickvals); + ticktext = dimension.ticktext; // ensure ticktext and tickvals have same length - if(!Array.isArray(ticktext) || !ticktext.length) { + if(!isArrayOrTypedArray(ticktext) || !ticktext.length) { ticktext = tickvals.map(numberFormat(dimension.tickformat)); } else if(ticktext.length > tickvals.length) { ticktext = ticktext.slice(0, tickvals.length); From ae04cad582382fc2d2510df3666093dd7b138145 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Mar 2023 18:53:25 -0500 Subject: [PATCH 078/122] handle typed arrays for polar --- src/traces/scatterpolar/defaults.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/traces/scatterpolar/defaults.js b/src/traces/scatterpolar/defaults.js index ba9e6159e57..b42c46fec40 100644 --- a/src/traces/scatterpolar/defaults.js +++ b/src/traces/scatterpolar/defaults.js @@ -70,6 +70,14 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function handleRThetaDefaults(traceIn, traceOut, layout, coerce) { var r = coerce('r'); var theta = coerce('theta'); + + if(Lib.isTypedArray(r)) { + traceOut.r = r = Array.from(r); + } + if(Lib.isTypedArray(theta)) { + traceOut.theta = theta = Array.from(theta); + } + var len; if(r) { From e691a4fe32617d3bcea4c77e1e211a698c6f84d6 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Mar 2023 19:26:15 -0500 Subject: [PATCH 079/122] handle typed arrays for smith charts --- src/plots/smith/layout_defaults.js | 2 ++ src/traces/scattersmith/defaults.js | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/plots/smith/layout_defaults.js b/src/plots/smith/layout_defaults.js index 540e31ff459..b8d606ab044 100644 --- a/src/plots/smith/layout_defaults.js +++ b/src/plots/smith/layout_defaults.js @@ -71,6 +71,8 @@ function handleDefaults(contIn, contOut, coerce, opts) { coerceAxis('tickvals', imagTickvalsDflt); } + if(Lib.isTypedArray(axOut.tickvals)) axOut.tickvals = Array.from(axOut.tickvals); + var dfltColor; var dfltFontColor; var dfltFontSize; diff --git a/src/traces/scattersmith/defaults.js b/src/traces/scattersmith/defaults.js index 1b3c17fada9..6a97e750754 100644 --- a/src/traces/scattersmith/defaults.js +++ b/src/traces/scattersmith/defaults.js @@ -75,6 +75,13 @@ function handleRealImagDefaults(traceIn, traceOut, layout, coerce) { len = Math.min(real.length, imag.length); } + if(Lib.isTypedArray(real)) { + traceOut.real = real = Array.from(real); + } + if(Lib.isTypedArray(imag)) { + traceOut.imag = imag = Array.from(imag); + } + traceOut._length = len; return len; } From a9d21c9dcdc940d3a428899826c4fc091631c8f2 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Mar 2023 19:55:09 -0500 Subject: [PATCH 080/122] no need to support in transforms which is deprecated --- test/image/convert_b64.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index ea98bff1d13..1cae789bacc 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -42,6 +42,7 @@ 'groups', 'selectedpoints', 'opacityscale', + 'transforms', # deprecated 'cameraposition' # deprecated ] From 41c1140404644a3d4a6eef1e5ac6bfc660feee9a Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Mar 2023 20:36:29 -0500 Subject: [PATCH 081/122] handle typed arrays in image trace --- src/traces/image/hover.js | 5 +++-- src/traces/image/plot.js | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/traces/image/hover.js b/src/traces/image/hover.js index 9b4e132d3dc..0c6ec43131d 100644 --- a/src/traces/image/hover.js +++ b/src/traces/image/hover.js @@ -2,6 +2,7 @@ var Fx = require('../../components/fx'); var Lib = require('../../lib'); +var isArrayOrTypedArray = Lib.isArrayOrTypedArray; var constants = require('./constants'); module.exports = function hoverPoints(pointData, xval, yval) { @@ -54,9 +55,9 @@ module.exports = function hoverPoints(pointData, xval, yval) { } var text; - if(Array.isArray(trace.hovertext) && Array.isArray(trace.hovertext[ny])) { + if(isArrayOrTypedArray(trace.hovertext) && isArrayOrTypedArray(trace.hovertext[ny])) { text = trace.hovertext[ny][nx]; - } else if(Array.isArray(trace.text) && Array.isArray(trace.text[ny])) { + } else if(isArrayOrTypedArray(trace.text) && isArrayOrTypedArray(trace.text[ny])) { text = trace.text[ny][nx]; } diff --git a/src/traces/image/plot.js b/src/traces/image/plot.js index a8a9e182fc7..15a5d45be49 100644 --- a/src/traces/image/plot.js +++ b/src/traces/image/plot.js @@ -186,7 +186,11 @@ module.exports = function plot(gd, plotinfo, cdimage, imageLayer) { .then(function() { var href, canvas; if(trace._hasZ) { - canvas = drawMagnifiedPixelsOnCanvas(function(i, j) {return z[j][i];}); + canvas = drawMagnifiedPixelsOnCanvas(function(i, j) { + var _z = z[j][i]; + if(Lib.isTypedArray(_z)) _z = Array.from(_z); + return _z; + }); href = canvas.toDataURL('image/png'); } else if(trace._hasSource) { if(realImage) { From 4b50e16f62fced1a9e212c0d22c772e8487c5bae Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Mar 2023 21:02:04 -0500 Subject: [PATCH 082/122] handle typed arrays various traces --- src/traces/bar/cross_trace_calc.js | 10 +++++----- src/traces/cone/convert.js | 3 ++- src/traces/funnel/defaults.js | 2 +- src/traces/icicle/defaults.js | 2 +- src/traces/isosurface/convert.js | 3 ++- src/traces/mesh3d/convert.js | 3 ++- src/traces/ohlc/calc.js | 4 ++-- src/traces/pointcloud/convert.js | 3 ++- src/traces/scatter/hover.js | 2 +- src/traces/scatter3d/convert.js | 4 ++-- src/traces/scattergl/edit_style.js | 2 +- src/traces/sunburst/defaults.js | 2 +- src/traces/surface/convert.js | 2 +- src/traces/treemap/defaults.js | 2 +- src/traces/volume/convert.js | 3 ++- 15 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/traces/bar/cross_trace_calc.js b/src/traces/bar/cross_trace_calc.js index a63b4cc248b..6af4eb73a41 100644 --- a/src/traces/bar/cross_trace_calc.js +++ b/src/traces/bar/cross_trace_calc.js @@ -432,9 +432,9 @@ function setBarCenterAndWidth(pa, sieve) { var calcTrace = calcTraces[i]; var t = calcTrace[0].t; var poffset = t.poffset; - var poffsetIsArray = Array.isArray(poffset); + var poffsetIsArray = isArrayOrTypedArray(poffset); var barwidth = t.barwidth; - var barwidthIsArray = Array.isArray(barwidth); + var barwidthIsArray = isArrayOrTypedArray(barwidth); for(var j = 0; j < calcTrace.length; j++) { var calcBar = calcTrace[j]; @@ -478,8 +478,8 @@ function updatePositionAxis(pa, sieve, allowMinDtick) { var t = calcTrace0.t; var poffset = t.poffset; var barwidth = t.barwidth; - var poffsetIsArray = Array.isArray(poffset); - var barwidthIsArray = Array.isArray(barwidth); + var poffsetIsArray = isArrayOrTypedArray(poffset); + var barwidthIsArray = isArrayOrTypedArray(barwidth); for(j = 0; j < calcTrace.length; j++) { bar = calcTrace[j]; @@ -750,7 +750,7 @@ function collectExtents(calcTraces, pa) { cd[0].t.extents = extents; var poffset = cd[0].t.poffset; - var poffsetIsArray = Array.isArray(poffset); + var poffsetIsArray = isArrayOrTypedArray(poffset); for(j = 0; j < cd.length; j++) { var di = cd[j]; diff --git a/src/traces/cone/convert.js b/src/traces/cone/convert.js index 42e1974c2b1..75a35c32477 100644 --- a/src/traces/cone/convert.js +++ b/src/traces/cone/convert.js @@ -6,6 +6,7 @@ var createConeMesh = require('../../../stackgl_modules').gl_cone3d.createConeMes var simpleMap = require('../../lib').simpleMap; var parseColorScale = require('../../lib/gl_format_color').parseColorScale; var extractOpts = require('../../components/colorscale').extractOpts; +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var zip3 = require('../../plots/gl3d/zip3'); function Cone(scene, uid) { @@ -34,7 +35,7 @@ proto.handlePick = function(selection) { ]; var text = this.data.hovertext || this.data.text; - if(Array.isArray(text) && text[selectIndex] !== undefined) { + if(isArrayOrTypedArray(text) && text[selectIndex] !== undefined) { selection.textLabel = text[selectIndex]; } else if(text) { selection.textLabel = text; diff --git a/src/traces/funnel/defaults.js b/src/traces/funnel/defaults.js index f729bc21639..80f9745fac9 100644 --- a/src/traces/funnel/defaults.js +++ b/src/traces/funnel/defaults.js @@ -44,7 +44,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { }); if(traceOut.textposition !== 'none' && !traceOut.texttemplate) { - coerce('textinfo', Array.isArray(text) ? 'text+value' : 'value'); + coerce('textinfo', Lib.isArrayOrTypedArray(text) ? 'text+value' : 'value'); } var markerColor = coerce('marker.color', defaultColor); diff --git a/src/traces/icicle/defaults.js b/src/traces/icicle/defaults.js index a549f1eac6e..bd86fc83fe8 100644 --- a/src/traces/icicle/defaults.js +++ b/src/traces/icicle/defaults.js @@ -40,7 +40,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var text = coerce('text'); coerce('texttemplate'); - if(!traceOut.texttemplate) coerce('textinfo', Array.isArray(text) ? 'text+label' : 'label'); + if(!traceOut.texttemplate) coerce('textinfo', Lib.isArrayOrTypedArray(text) ? 'text+label' : 'label'); coerce('hovertext'); coerce('hovertemplate'); diff --git a/src/traces/isosurface/convert.js b/src/traces/isosurface/convert.js index 5027cdc1d5b..94b9ef2b891 100644 --- a/src/traces/isosurface/convert.js +++ b/src/traces/isosurface/convert.js @@ -2,6 +2,7 @@ var createMesh = require('../../../stackgl_modules').gl_mesh3d; var parseColorScale = require('../../lib/gl_format_color').parseColorScale; +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var str2RgbaArray = require('../../lib/str2rgbarray'); var extractOpts = require('../../components/colorscale').extractOpts; var zip3 = require('../../plots/gl3d/zip3'); @@ -59,7 +60,7 @@ proto.handlePick = function(selection) { ]; var text = this.data.hovertext || this.data.text; - if(Array.isArray(text) && text[selectIndex] !== undefined) { + if(isArrayOrTypedArray(text) && text[selectIndex] !== undefined) { selection.textLabel = text[selectIndex]; } else if(text) { selection.textLabel = text; diff --git a/src/traces/mesh3d/convert.js b/src/traces/mesh3d/convert.js index 3c38b148f85..7adddd93cb3 100644 --- a/src/traces/mesh3d/convert.js +++ b/src/traces/mesh3d/convert.js @@ -6,6 +6,7 @@ var alphaShape = require('../../../stackgl_modules').alpha_shape; var convexHull = require('../../../stackgl_modules').convex_hull; var parseColorScale = require('../../lib/gl_format_color').parseColorScale; +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var str2RgbaArray = require('../../lib/str2rgbarray'); var extractOpts = require('../../components/colorscale').extractOpts; var zip3 = require('../../plots/gl3d/zip3'); @@ -37,7 +38,7 @@ proto.handlePick = function(selection) { } var text = this.data.hovertext || this.data.text; - if(Array.isArray(text) && text[selectIndex] !== undefined) { + if(isArrayOrTypedArray(text) && text[selectIndex] !== undefined) { selection.textLabel = text[selectIndex]; } else if(text) { selection.textLabel = text; diff --git a/src/traces/ohlc/calc.js b/src/traces/ohlc/calc.js index 831518652c5..c76a108e8bf 100644 --- a/src/traces/ohlc/calc.js +++ b/src/traces/ohlc/calc.js @@ -50,8 +50,8 @@ function calcCommon(gd, trace, origX, x, ya, ptFunc) { var l = ya.makeCalcdata(trace, 'low'); var c = ya.makeCalcdata(trace, 'close'); - var hasTextArray = Array.isArray(trace.text); - var hasHovertextArray = Array.isArray(trace.hovertext); + var hasTextArray = Lib.isArrayOrTypedArray(trace.text); + var hasHovertextArray = Lib.isArrayOrTypedArray(trace.hovertext); // we're optimists - before we have any changing data, assume increasing var increasing = true; diff --git a/src/traces/pointcloud/convert.js b/src/traces/pointcloud/convert.js index 7ed0b37c140..3f1ebb942f0 100644 --- a/src/traces/pointcloud/convert.js +++ b/src/traces/pointcloud/convert.js @@ -2,6 +2,7 @@ var createPointCloudRenderer = require('../../../stackgl_modules').gl_pointcloud2d; +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var str2RGBArray = require('../../lib/str2rgbarray'); var findExtremes = require('../../plots/cartesian/autorange').findExtremes; var getTraceColor = require('../scatter/get_trace_color'); @@ -48,7 +49,7 @@ proto.handlePick = function(pickResult) { traceCoord: this.pickXYData ? [this.pickXYData[index * 2], this.pickXYData[index * 2 + 1]] : [this.pickXData[index], this.pickYData[index]], - textLabel: Array.isArray(this.textLabels) ? + textLabel: isArrayOrTypedArray(this.textLabels) ? this.textLabels[index] : this.textLabels, color: this.color, diff --git a/src/traces/scatter/hover.js b/src/traces/scatter/hover.js index c86a5b902f8..e6fb76cdb36 100644 --- a/src/traces/scatter/hover.js +++ b/src/traces/scatter/hover.js @@ -199,7 +199,7 @@ module.exports = function hoverPoints(pointData, xval, yval, hovermode) { delete pointData.index; - if(trace.text && !Array.isArray(trace.text)) { + if(trace.text && !Lib.isArrayOrTypedArray(trace.text)) { pointData.text = String(trace.text); } else pointData.text = trace.name; diff --git a/src/traces/scatter3d/convert.js b/src/traces/scatter3d/convert.js index e186b1949f6..d3f846fb9cf 100644 --- a/src/traces/scatter3d/convert.js +++ b/src/traces/scatter3d/convert.js @@ -58,7 +58,7 @@ proto.handlePick = function(selection) { selection.textLabel = ''; if(this.textLabels) { - if(Array.isArray(this.textLabels)) { + if(Lib.isArrayOrTypedArray(this.textLabels)) { if(this.textLabels[ind] || this.textLabels[ind] === 0) { selection.textLabel = this.textLabels[ind]; } @@ -227,7 +227,7 @@ function convertPlotlyOptions(scene, data) { } // convert text - if(Array.isArray(data.text)) text = data.text; + if(Lib.isArrayOrTypedArray(data.text)) text = data.text; else if(data.text !== undefined) { text = new Array(len); for(i = 0; i < len; i++) text[i] = data.text; diff --git a/src/traces/scattergl/edit_style.js b/src/traces/scattergl/edit_style.js index 7246fa1e641..961dee97cbc 100644 --- a/src/traces/scattergl/edit_style.js +++ b/src/traces/scattergl/edit_style.js @@ -23,7 +23,7 @@ function styleTextSelection(cd) { var stc = selOpts.color; var utc = unselOpts.color; var base = baseOpts.color; - var hasArrayBase = Array.isArray(base); + var hasArrayBase = Lib.isArrayOrTypedArray(base); opts.color = new Array(trace._length); for(i = 0; i < els.length; i++) { diff --git a/src/traces/sunburst/defaults.js b/src/traces/sunburst/defaults.js index 8f40be23116..6848f7f0ebf 100644 --- a/src/traces/sunburst/defaults.js +++ b/src/traces/sunburst/defaults.js @@ -48,7 +48,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var text = coerce('text'); coerce('texttemplate'); - if(!traceOut.texttemplate) coerce('textinfo', Array.isArray(text) ? 'text+label' : 'label'); + if(!traceOut.texttemplate) coerce('textinfo', Lib.isArrayOrTypedArray(text) ? 'text+label' : 'label'); coerce('hovertext'); coerce('hovertemplate'); diff --git a/src/traces/surface/convert.js b/src/traces/surface/convert.js index c66689a8672..d2433c382bb 100644 --- a/src/traces/surface/convert.js +++ b/src/traces/surface/convert.js @@ -96,7 +96,7 @@ proto.handlePick = function(selection) { } var text = this.data.hovertext || this.data.text; - if(Array.isArray(text) && text[k] && text[k][j] !== undefined) { + if(isArrayOrTypedArray(text) && text[k] && text[k][j] !== undefined) { selection.textLabel = text[k][j]; } else if(text) { selection.textLabel = text; diff --git a/src/traces/treemap/defaults.js b/src/traces/treemap/defaults.js index 6c428979514..4c72f813b4b 100644 --- a/src/traces/treemap/defaults.js +++ b/src/traces/treemap/defaults.js @@ -44,7 +44,7 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout var text = coerce('text'); coerce('texttemplate'); - if(!traceOut.texttemplate) coerce('textinfo', Array.isArray(text) ? 'text+label' : 'label'); + if(!traceOut.texttemplate) coerce('textinfo', Lib.isArrayOrTypedArray(text) ? 'text+label' : 'label'); coerce('hovertext'); coerce('hovertemplate'); diff --git a/src/traces/volume/convert.js b/src/traces/volume/convert.js index 7504a493d1f..c749a1eeb4b 100644 --- a/src/traces/volume/convert.js +++ b/src/traces/volume/convert.js @@ -3,6 +3,7 @@ var createMesh = require('../../../stackgl_modules').gl_mesh3d; var parseColorScale = require('../../lib/gl_format_color').parseColorScale; +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var str2RgbaArray = require('../../lib/str2rgbarray'); var extractOpts = require('../../components/colorscale').extractOpts; var zip3 = require('../../plots/gl3d/zip3'); @@ -46,7 +47,7 @@ proto.handlePick = function(selection) { ]; var text = this.data.hovertext || this.data.text; - if(Array.isArray(text) && text[selectIndex] !== undefined) { + if(isArrayOrTypedArray(text) && text[selectIndex] !== undefined) { selection.textLabel = text[selectIndex]; } else if(text) { selection.textLabel = text; From 8c4202b6ebeefc871f874e0ab292b70fd0d52940 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 7 Mar 2023 10:45:01 -0500 Subject: [PATCH 083/122] handle typed array in scattergl text --- src/traces/scattergl/hover.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/traces/scattergl/hover.js b/src/traces/scattergl/hover.js index 74efc4776b3..7d8a922f387 100644 --- a/src/traces/scattergl/hover.js +++ b/src/traces/scattergl/hover.js @@ -122,7 +122,7 @@ function calcHover(pointData, x, y, trace) { }; // that is single-item arrays_to_calcdata excerpt, since we are doing it for a single point and we don't have to do it beforehead for 1e6 points - di.tx = Array.isArray(trace.text) ? trace.text[id] : trace.text; + di.tx = Lib.isArrayOrTypedArray(trace.text) ? trace.text[id] : trace.text; di.htx = Array.isArray(trace.hovertext) ? trace.hovertext[id] : trace.hovertext; di.data = Array.isArray(trace.customdata) ? trace.customdata[id] : trace.customdata; di.tp = Array.isArray(trace.textposition) ? trace.textposition[id] : trace.textposition; From 59557c1fd16ec8ffdf233961a4709a311e9ae6f2 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 12:25:33 -0500 Subject: [PATCH 084/122] revise dtypes --- src/lib/array.js | 14 ++++++++++++-- src/lib/coerce.js | 14 +++++++++++--- test/jasmine/tests/toimage_test.js | 4 ++-- test/plot-schema.json | 2 +- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/lib/array.js b/src/lib/array.js index 9de165ee682..6c5db031f09 100644 --- a/src/lib/array.js +++ b/src/lib/array.js @@ -52,8 +52,8 @@ exports.ensureArray = function(out, n) { }; var typedArrays = { - c1: typeof Uint8ClampedArray === 'undefined' ? undefined : - Uint8ClampedArray, // not supported in numpy? + u1c: typeof Uint8ClampedArray === 'undefined' ? undefined : + Uint8ClampedArray, // not supported in numpy? i1: typeof Int8Array === 'undefined' ? undefined : Int8Array, @@ -89,6 +89,16 @@ var typedArrays = { */ }; +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; } diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 1762c385cb0..70707dce914 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -25,13 +25,21 @@ exports.valObjectMeta = { '(1) a regular {array} object', '(2) a typed array (e.g. Float32Array)', '(3) an object with keys dtype, bdata, and optionally shape.', - 'In this 3rd form, dtype is one of *i1*, *u1*, *c1*,', - '*i2*, *u2*, *i4*, *u4*, *f4*, or *f8*.', + 'In this 3rd form, dtype is one of', + '*f8*, *f4*.', + '*i4*, *u4*,', + '*i2*, *u2*,', + '*i1*, *u1* or *u1c* for Uint8ClampedArray.', + 'In addition to shorthand `dtype` above one could also use the following forms:', + '*float64*, *float32*,', + '*int32*, *uint32*,', + '*int16*, *uint16*,', + '*int8*, *uint8* or *uint8c* for Uint8ClampedArray.', '`bdata` is either a base64-encoded string or the ArrayBuffer of', 'an integer or float typed array.', 'For either multi-dimensional arrays you must also', 'provide its dimensions separated by comma via `shape`.', - 'For exmaple using `dtype`: *f4* and `shape`: *5,100* you can', + 'For example using `dtype`: *f4* and `shape`: *5,100* you can', 'declare a 2-D array that has 5 rows and 100 columns', 'containing float32 values i.e. 4 bits per value.', '`shape` is optional for one dimensional arrays.' diff --git a/test/jasmine/tests/toimage_test.js b/test/jasmine/tests/toimage_test.js index a10dc10f881..da4ffd5aba6 100644 --- a/test/jasmine/tests/toimage_test.js +++ b/test/jasmine/tests/toimage_test.js @@ -458,7 +458,7 @@ describe('Plotly.toImage', function() { z: {bdata: z, dtype: 'i1'}, marker: { color: {bdata: c, dtype: 'u1'}, - size: {bdata: s, dtype: 'c1'} + size: {bdata: s, dtype: 'u1c'} } }]) .then(function(gd) { @@ -495,7 +495,7 @@ describe('Plotly.toImage', function() { } expect(trace.marker.size.bdata).toEqual('ADx48A=='); - expect(trace.marker.size.dtype).toEqual('c1'); + expect(trace.marker.size.dtype).toEqual('u1c'); expect(trace.marker.size.shape).toEqual('4'); expect(trace.marker.color.bdata).toEqual('ADx48A=='); diff --git a/test/plot-schema.json b/test/plot-schema.json index c16e600fa58..a2fed88f4ab 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -479,7 +479,7 @@ "requiredOpts": [] }, "data_array": { - "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bdata, and optionally shape. In this 3rd form, dtype is one of *i1*, *u1*, *c1*, *i2*, *u2*, *i4*, *u4*, *f4*, or *f8*. `bdata` is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For exmaple using `dtype`: *f4* and `shape`: *5,100* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value. `shape` is optional for one dimensional arrays.", + "description": "An {array} of data. The value must represent an {array} or it will be ignored, but this array can be provided in several forms: (1) a regular {array} object (2) a typed array (e.g. Float32Array) (3) an object with keys dtype, bdata, and optionally shape. In this 3rd form, dtype is one of *f8*, *f4*. *i4*, *u4*, *i2*, *u2*, *i1*, *u1* or *u1c* for Uint8ClampedArray. In addition to shorthand `dtype` above one could also use the following forms: *float64*, *float32*, *int32*, *uint32*, *int16*, *uint16*, *int8*, *uint8* or *uint8c* for Uint8ClampedArray. `bdata` is either a base64-encoded string or the ArrayBuffer of an integer or float typed array. For either multi-dimensional arrays you must also provide its dimensions separated by comma via `shape`. For example using `dtype`: *f4* and `shape`: *5,100* you can declare a 2-D array that has 5 rows and 100 columns containing float32 values i.e. 4 bits per value. `shape` is optional for one dimensional arrays.", "otherOpts": [ "dflt" ], From fe9b404709020918dd2f3d794cf266801af06873 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 13:37:09 -0500 Subject: [PATCH 085/122] add TODO comments to avoid casting to Array in supply defaults --- src/plots/smith/layout_defaults.js | 2 ++ src/traces/scatterpolar/defaults.js | 1 + src/traces/scattersmith/defaults.js | 1 + 3 files changed, 4 insertions(+) diff --git a/src/plots/smith/layout_defaults.js b/src/plots/smith/layout_defaults.js index b8d606ab044..6eb26adc977 100644 --- a/src/plots/smith/layout_defaults.js +++ b/src/plots/smith/layout_defaults.js @@ -17,6 +17,7 @@ var constants = require('./constants'); var axisNames = constants.axisNames; var makeImagDflt = memoize(function(realTickvals) { + // TODO: handle this case outside supply defaults step if(Lib.isTypedArray(realTickvals)) realTickvals = Array.from(realTickvals); return realTickvals.slice().reverse().map(function(x) { return -x; }) @@ -71,6 +72,7 @@ function handleDefaults(contIn, contOut, coerce, opts) { coerceAxis('tickvals', imagTickvalsDflt); } + // TODO: handle this case outside supply defaults step if(Lib.isTypedArray(axOut.tickvals)) axOut.tickvals = Array.from(axOut.tickvals); var dfltColor; diff --git a/src/traces/scatterpolar/defaults.js b/src/traces/scatterpolar/defaults.js index b42c46fec40..15b529f3c0e 100644 --- a/src/traces/scatterpolar/defaults.js +++ b/src/traces/scatterpolar/defaults.js @@ -71,6 +71,7 @@ function handleRThetaDefaults(traceIn, traceOut, layout, coerce) { var r = coerce('r'); var theta = coerce('theta'); + // TODO: handle this case outside supply defaults step if(Lib.isTypedArray(r)) { traceOut.r = r = Array.from(r); } diff --git a/src/traces/scattersmith/defaults.js b/src/traces/scattersmith/defaults.js index 6a97e750754..622901b83c5 100644 --- a/src/traces/scattersmith/defaults.js +++ b/src/traces/scattersmith/defaults.js @@ -75,6 +75,7 @@ function handleRealImagDefaults(traceIn, traceOut, layout, coerce) { len = Math.min(real.length, imag.length); } + // TODO: handle this case outside supply defaults step if(Lib.isTypedArray(real)) { traceOut.real = real = Array.from(real); } From 662294e42290586aeed7053dab6add6765eb2052 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 15:56:38 -0500 Subject: [PATCH 086/122] handle typed arrays in categoryarray --- src/plots/cartesian/category_order_defaults.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plots/cartesian/category_order_defaults.js b/src/plots/cartesian/category_order_defaults.js index b75fa57898b..76c1e5c261a 100644 --- a/src/plots/cartesian/category_order_defaults.js +++ b/src/plots/cartesian/category_order_defaults.js @@ -1,5 +1,7 @@ 'use strict'; +var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; + function findCategories(ax, opts) { var dataAttr = opts.dataAttr || ax._id.charAt(0); var lookup = {}; @@ -49,7 +51,8 @@ module.exports = function handleCategoryOrderDefaults(containerIn, containerOut, if(containerOut.type !== 'category') return; var arrayIn = containerIn.categoryarray; - var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0); + var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0) || + isTypedArraySpec(arrayIn); // override default 'categoryorder' value when non-empty array is supplied var orderDefault; From 281220a8387e643077411cb127aefdcb578b893e Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 17:31:10 -0500 Subject: [PATCH 087/122] fix polar set_convert --- src/plots/polar/set_convert.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/plots/polar/set_convert.js b/src/plots/polar/set_convert.js index d150e61af5c..cc16cfc2b74 100644 --- a/src/plots/polar/set_convert.js +++ b/src/plots/polar/set_convert.js @@ -109,14 +109,6 @@ function setConvertAngular(ax, polarLayout) { var _d2c = function(v) { return ax.d2c(v, trace.thetaunit); }; if(arrayIn) { - if(Lib.isTypedArray(arrayIn) && axType === 'linear') { - if(len === arrayIn.length) { - return arrayIn; - } else if(arrayIn.subarray) { - return arrayIn.subarray(0, len); - } - } - arrayOut = new Array(len); for(i = 0; i < len; i++) { arrayOut[i] = _d2c(arrayIn[i]); From 1300c2afb6b783c36ec3327ba9e72fd494ffce27 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 17:47:10 -0500 Subject: [PATCH 088/122] handle typed arrays in barpolar --- src/traces/barpolar/calc.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/traces/barpolar/calc.js b/src/traces/barpolar/calc.js index b9f605a4b92..1a532ab210c 100644 --- a/src/traces/barpolar/calc.js +++ b/src/traces/barpolar/calc.js @@ -2,6 +2,7 @@ var hasColorscale = require('../../components/colorscale/helpers').hasColorscale; var colorscaleCalc = require('../../components/colorscale/calc'); +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var arraysToCalcdata = require('../bar/arrays_to_calcdata'); var setGroupPositions = require('../bar/cross_trace_calc').setGroupPositions; var calcSelection = require('../scatter/calc_selection'); @@ -33,7 +34,7 @@ function calc(gd, trace) { function d2c(attr) { var val = trace[attr]; if(val !== undefined) { - trace['_' + attr] = Array.isArray(val) ? + trace['_' + attr] = isArrayOrTypedArray(val) ? angularAxis.makeCalcdata(trace, attr) : angularAxis.d2c(val, trace.thetaunit); } From 0fe1414eb65cf2c61667987ee7380e1a4d2cbe12 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 18:53:49 -0500 Subject: [PATCH 089/122] handle typed arrays in transform - fix rendering of point-selection case --- src/lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/index.js b/src/lib/index.js index 8e8d6d7b807..d0c37a0e22f 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -685,7 +685,7 @@ lib.getTargetArray = function(trace, transformOpts) { if(typeof target === 'string' && target) { var array = lib.nestedProperty(trace, target).get(); - return Array.isArray(array) ? array : false; + return lib.isArrayOrTypedArray(array) ? array : false; } else if(Array.isArray(target)) { return target; } From d9dc5933775686302bc99deedda800f38213c05c Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 19:07:56 -0500 Subject: [PATCH 090/122] handle typed arrays in scatter3d text --- src/traces/scatter3d/convert.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/traces/scatter3d/convert.js b/src/traces/scatter3d/convert.js index d3f846fb9cf..e2a9de0e907 100644 --- a/src/traces/scatter3d/convert.js +++ b/src/traces/scatter3d/convert.js @@ -227,8 +227,11 @@ function convertPlotlyOptions(scene, data) { } // convert text - if(Lib.isArrayOrTypedArray(data.text)) text = data.text; - else if(data.text !== undefined) { + if(Array.isArray(data.text)) { + text = data.text; + } else if(Lib.isTypedArray(data.text)) { + text = Array.from(data.text); + } else if(data.text !== undefined) { text = new Array(len); for(i = 0; i < len; i++) text[i] = data.text; } From 73650935301004f4a23f5edb59da4fc70809fbaf Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 19:23:35 -0500 Subject: [PATCH 091/122] short dtype is now optional - both i1 & int8 are supported --- test/image/convert_b64.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 1cae789bacc..1b8645a6a86 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -2,7 +2,7 @@ import numpy import base64 -plotlyjsTypes = { +plotlyjsShortTypes = { 'int8': 'i1', 'uint8': 'u1', 'int16': 'i2', @@ -89,9 +89,9 @@ def arraysToB64(obj, newObj) : arr = arr.astype(numpy.uint32) - if str(arr.dtype) in plotlyjsTypes : + if str(arr.dtype) in plotlyjsShortTypes : newObj[key] = { - 'dtype': plotlyjsTypes[str(arr.dtype)], + 'dtype': plotlyjsShortTypes[str(arr.dtype)], 'bdata': base64.b64encode(arr).decode('ascii') } From e92352bc338bc253cdb7dcb6a7921168aeefae97 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 19:25:07 -0500 Subject: [PATCH 092/122] remove unused import --- test/image/convert_b64.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 1b8645a6a86..740189c4540 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -1,4 +1,3 @@ -from sqlite3 import NotSupportedError import numpy import base64 From db62bb1ca1d0efda90bd6c4c542be486e8518870 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 19:26:37 -0500 Subject: [PATCH 093/122] allow transforms --- test/image/convert_b64.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 740189c4540..31c3b22f317 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -41,7 +41,6 @@ 'groups', 'selectedpoints', 'opacityscale', - 'transforms', # deprecated 'cameraposition' # deprecated ] From ed8cea13c74ea28654e8050c0119996ec578dab5 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 19:44:33 -0500 Subject: [PATCH 094/122] handle typed arrays in transform target --- src/lib/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/index.js b/src/lib/index.js index d0c37a0e22f..837d565f0f2 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -686,7 +686,7 @@ lib.getTargetArray = function(trace, transformOpts) { if(typeof target === 'string' && target) { var array = lib.nestedProperty(trace, target).get(); return lib.isArrayOrTypedArray(array) ? array : false; - } else if(Array.isArray(target)) { + } else if(lib.isArrayOrTypedArray(target)) { return target; } From 586088650b522e010df74dbff8e57eb1e10ef340 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 19:57:04 -0500 Subject: [PATCH 095/122] allow groups --- test/image/convert_b64.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 31c3b22f317..4a523e64b6c 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -38,7 +38,6 @@ 'args', 'args2', 'values', - 'groups', 'selectedpoints', 'opacityscale', 'cameraposition' # deprecated From 560fadfc86830058d16844d5ed71c9a276322397 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 21:40:50 -0500 Subject: [PATCH 096/122] handle typed arrays & b64 in info_array e.g. sankey.groups --- src/lib/coerce.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/lib/coerce.js b/src/lib/coerce.js index 70707dce914..43c0c9705ab 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -293,17 +293,19 @@ exports.valObjectMeta = { return out; } - var twoD = opts.dimensions === 2 || (opts.dimensions === '1-2' && Array.isArray(v) && Array.isArray(v[0])); + if(isTypedArraySpec(v)) v = decodeTypedArraySpec(v); - if(!Array.isArray(v)) { + if(!isArrayOrTypedArray(v)) { propOut.set(dflt); return; } + var twoD = opts.dimensions === 2 || (opts.dimensions === '1-2' && Array.isArray(v) && isArrayOrTypedArray(v[0])); + var items = opts.items; var vOut = []; var arrayItems = Array.isArray(items); - var arrayItems2D = arrayItems && twoD && Array.isArray(items[0]); + var arrayItems2D = arrayItems && twoD && isArrayOrTypedArray(items[0]); var innerItemsOnly = twoD && arrayItems && !arrayItems2D; var len = (arrayItems && !innerItemsOnly) ? items.length : v.length; @@ -314,7 +316,7 @@ exports.valObjectMeta = { if(twoD) { for(i = 0; i < len; i++) { vOut[i] = []; - row = Array.isArray(v[i]) ? v[i] : []; + row = isArrayOrTypedArray(v[i]) ? v[i] : []; if(innerItemsOnly) len2 = items.length; else if(arrayItems) len2 = items[i].length; else len2 = row.length; @@ -338,7 +340,7 @@ exports.valObjectMeta = { propOut.set(vOut); }, validateFunction: function(v, opts) { - if(!Array.isArray(v)) return false; + if(!isArrayOrTypedArray(v)) return false; var items = opts.items; var arrayItems = Array.isArray(items); @@ -350,7 +352,7 @@ exports.valObjectMeta = { // valid when all input items are valid for(var i = 0; i < v.length; i++) { if(twoD) { - if(!Array.isArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) { + if(!isArrayOrTypedArray(v[i]) || (!opts.freeLength && v[i].length !== items[i].length)) { return false; } for(var j = 0; j < v[i].length; j++) { From ea98440a87addc5efa7918989967f0a9393ea5d1 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 22:00:35 -0500 Subject: [PATCH 097/122] allow values --- test/image/convert_b64.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 4a523e64b6c..6b0e5685b84 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -37,7 +37,6 @@ 'zmax', 'args', 'args2', - 'values', 'selectedpoints', 'opacityscale', 'cameraposition' # deprecated From 0fc0d70776acb0c642fb8fc3f335b4a13b9ce6c0 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 22:46:23 -0500 Subject: [PATCH 098/122] handle typed arrays in table values --- src/traces/table/data_preparation_helper.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/traces/table/data_preparation_helper.js b/src/traces/table/data_preparation_helper.js index c7949b48d41..fb737366dd3 100644 --- a/src/traces/table/data_preparation_helper.js +++ b/src/traces/table/data_preparation_helper.js @@ -3,6 +3,7 @@ var c = require('./constants'); var extendFlat = require('../../lib/extend').extendFlat; var isNumeric = require('fast-isnumeric'); +var isTypedArray = require('../../lib/array').isTypedArray; var isArrayOrTypedArray = require('../../lib/array').isArrayOrTypedArray; // pure functions, don't alter but passes on `gd` and parts of `trace` without deep copying @@ -120,7 +121,8 @@ function squareStringMatrix(matrixIn) { var maxLen = 0; var i; for(i = 0; i < matrix.length; i++) { - if(!isArrayOrTypedArray(matrix[i])) matrix[i] = [matrix[i]]; + if(isTypedArray(matrix[i])) matrix[i] = Array.from(matrix[i]); + else if(!isArrayOrTypedArray(matrix[i])) matrix[i] = [matrix[i]]; minLen = Math.min(minLen, matrix[i].length); maxLen = Math.max(maxLen, matrix[i].length); } From a4147151c05d8db0740f2ed7534ed270b281b757 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 22:58:31 -0500 Subject: [PATCH 099/122] allow args and args2 --- test/image/convert_b64.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 6b0e5685b84..f1d3db1d9a4 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -35,8 +35,6 @@ 'span', 'zmin', 'zmax', - 'args', - 'args2', 'selectedpoints', 'opacityscale', 'cameraposition' # deprecated From 6852b4d4b2608abe00e7f85359c0bc3f89a98850 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 23:09:18 -0500 Subject: [PATCH 100/122] allow more keys --- test/image/convert_b64.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index f1d3db1d9a4..fb390910712 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -30,14 +30,8 @@ 'dtickrange', 'constraintrange', 'bounds', - 'parallels', - 'sector', - 'span', - 'zmin', - 'zmax', 'selectedpoints', - 'opacityscale', - 'cameraposition' # deprecated + 'opacityscale' ] def arraysToB64(obj, newObj) : From bd381f597b930a24d81a43d5a1b6a32a75654c10 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 23:35:28 -0500 Subject: [PATCH 101/122] allow dtickrange, constraintrange and bounds --- test/image/convert_b64.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index fb390910712..19bbf10460e 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -27,9 +27,6 @@ 'yaxes', 'domain', 'range', - 'dtickrange', - 'constraintrange', - 'bounds', 'selectedpoints', 'opacityscale' ] From 2d4d47f5991c5e310179f6a9f4e50b74259d8b54 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 8 Mar 2023 23:36:27 -0500 Subject: [PATCH 102/122] allow subplots, xaxes, yaxes & domain --- test/image/convert_b64.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 19bbf10460e..6f48775f649 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -22,10 +22,6 @@ rangeKeys = [ 'geojson', 'layers', - 'subplots', - 'xaxes', - 'yaxes', - 'domain', 'range', 'selectedpoints', 'opacityscale' From d33dc30198b9f09aaa70a05672666ac0b11c5fb5 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Mar 2023 09:42:43 -0500 Subject: [PATCH 103/122] allow opacityscale --- test/image/convert_b64.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 6f48775f649..87a106a9da0 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -23,8 +23,7 @@ 'geojson', 'layers', 'range', - 'selectedpoints', - 'opacityscale' + 'selectedpoints' ] def arraysToB64(obj, newObj) : From 60697dc0c916866928e0018e3a9bf4ae7686fd3c Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Mar 2023 09:55:52 -0500 Subject: [PATCH 104/122] handle typed arrays in opacityscale --- src/traces/surface/defaults.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/traces/surface/defaults.js b/src/traces/surface/defaults.js index 3330882bf59..bd3b43a7ecf 100644 --- a/src/traces/surface/defaults.js +++ b/src/traces/surface/defaults.js @@ -2,6 +2,8 @@ var Registry = require('../../registry'); var Lib = require('../../lib'); +var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; +var decodeTypedArraySpec = require('../../lib/array').decodeTypedArraySpec; var colorscaleDefaults = require('../../components/colorscale/defaults'); var attributes = require('./attributes'); @@ -25,7 +27,7 @@ function createWave(n, minOpacity) { function isValidScaleArray(scl) { var highestVal = 0; - if(!Array.isArray(scl) || scl.length < 2) return false; + if(!Lib.isArrayOrTypedArray(scl) || scl.length < 2) return false; if(!scl[0] || !scl[scl.length - 1]) return false; @@ -144,6 +146,11 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function opacityscaleDefaults(traceIn, traceOut, layout, coerce) { var opacityscale = coerce('opacityscale'); + + if(isTypedArraySpec(opacityscale)) { + traceOut.opacityscale = opacityscale = decodeTypedArraySpec(opacityscale); + } + if(opacityscale === 'max') { traceOut.opacityscale = [[0, MIN], [1, 1]]; } else if(opacityscale === 'min') { From 11e0bf0e05c4e02308dfbd40417132ac6f5e7c5a Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Mar 2023 10:40:11 -0500 Subject: [PATCH 105/122] allow selectedpoints --- test/image/convert_b64.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 87a106a9da0..8007a20f8fd 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -15,20 +15,15 @@ int32bounds = numpy.iinfo(numpy.int32) uint32bounds = numpy.iinfo(numpy.uint32) -# List mainly including keys with type of 'info_array' -# Simply having two items the b64 option is not supported by plotly.js -# This list also includes cases of type 'any' that we don't to be converted -# e.g. in mapbox layers or geojson -rangeKeys = [ +skipKeys = [ 'geojson', 'layers', - 'range', - 'selectedpoints' + 'range' ] def arraysToB64(obj, newObj) : for key, val in obj.items() : - if key in rangeKeys : + if key in skipKeys : newObj[key] = val elif isinstance(val, dict) : newObj[key] = dict() From 48105853f4aced2b162929c071b596abd044e3a3 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Mar 2023 15:39:13 -0500 Subject: [PATCH 106/122] handle typed arrays in selectedpoints --- src/plots/plots.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index 15d58287a9b..d64e7354d4b 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -25,6 +25,9 @@ var getModuleCalcData = require('../plots/get_data').getModuleCalcData; var relinkPrivateKeys = Lib.relinkPrivateKeys; var _ = Lib._; +var isTypedArraySpec = require('../lib/array').isTypedArraySpec; +var decodeTypedArraySpec = require('../lib/array').decodeTypedArraySpec; + var plots = module.exports = {}; // Expose registry methods on Plots for backward-compatibility @@ -1356,7 +1359,10 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac } if(_module && _module.selectPoints) { - coerce('selectedpoints'); + var selectedpoints = coerce('selectedpoints'); + if(isTypedArraySpec(selectedpoints)) { + traceOut.selectedpoints = Array.from(decodeTypedArraySpec(selectedpoints)); + } } plots.supplyTransformDefaults(traceIn, traceOut, layout); From 8bba7f814db3c78c5b8dda024f8613b7a099ce58 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Thu, 9 Mar 2023 16:21:42 -0500 Subject: [PATCH 107/122] allow range --- test/image/convert_b64.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 8007a20f8fd..d0f6bc456b1 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -17,8 +17,7 @@ skipKeys = [ 'geojson', - 'layers', - 'range' + 'layers' ] def arraysToB64(obj, newObj) : From bb53b1ecd10bda3c19f756928576cae0d93785a4 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 11:05:34 -0400 Subject: [PATCH 108/122] handle array spec in type any --- src/lib/coerce.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/lib/coerce.js b/src/lib/coerce.js index eca3118ac57..5ad6dc61a2d 100644 --- a/src/lib/coerce.js +++ b/src/lib/coerce.js @@ -265,8 +265,14 @@ exports.valObjectMeta = { requiredOpts: [], otherOpts: ['dflt', 'values', 'arrayOk'], coerceFunction: function(v, propOut, dflt) { - if(v === undefined) propOut.set(dflt); - else propOut.set(v); + if(v === undefined) { + propOut.set(dflt); + } else { + propOut.set( + isTypedArraySpec(v) ? decodeTypedArraySpec(v) : + v + ); + } } }, info_array: { From 1a38f373afba346b14193df0e0f3edc90bb255c0 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 11:25:58 -0400 Subject: [PATCH 109/122] fix handling direct typedarrays in selectedpoints --- src/plots/plots.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/plots/plots.js b/src/plots/plots.js index 0f1a805e9bc..9e9f0e3082c 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -25,9 +25,6 @@ var getModuleCalcData = require('../plots/get_data').getModuleCalcData; var relinkPrivateKeys = Lib.relinkPrivateKeys; var _ = Lib._; -var isTypedArraySpec = require('../lib/array').isTypedArraySpec; -var decodeTypedArraySpec = require('../lib/array').decodeTypedArraySpec; - var plots = module.exports = {}; // Expose registry methods on Plots for backward-compatibility @@ -1360,8 +1357,8 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac if(_module && _module.selectPoints) { var selectedpoints = coerce('selectedpoints'); - if(isTypedArraySpec(selectedpoints)) { - traceOut.selectedpoints = Array.from(decodeTypedArraySpec(selectedpoints)); + if(Lib.isTypedArray(selectedpoints)) { + traceOut.selectedpoints = Array.from(selectedpoints); } } From 534777469f95eb4330b4faf2f6a55387059b23f0 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 11:53:42 -0400 Subject: [PATCH 110/122] Revert "handle typed arrays in contour" This reverts commit bd4d2eb4bf1f4f5f142768e8a50cab1be9ead080. --- src/traces/contour/constraint_mapping.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/traces/contour/constraint_mapping.js b/src/traces/contour/constraint_mapping.js index abfd601f32a..ba1560ccb7d 100644 --- a/src/traces/contour/constraint_mapping.js +++ b/src/traces/contour/constraint_mapping.js @@ -1,9 +1,5 @@ 'use strict'; -var isArrayOrTypedArray = require('../../lib/array').isArrayOrTypedArray; -var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; -var decodeTypedArraySpec = require('../../lib/array').decodeTypedArraySpec; - var filterOps = require('../../constants/filter_ops'); var isNumeric = require('fast-isnumeric'); @@ -20,8 +16,7 @@ module.exports = { // This does not in any way shape or form support calendars. It's adapted from // transforms/filter.js. function coerceValue(operation, value) { - if(isTypedArraySpec(value)) value = decodeTypedArraySpec(value); - var hasArrayValue = isArrayOrTypedArray(value); + var hasArrayValue = Array.isArray(value); var coercedValue; From 5dfe2c907fdcdb0dbc228daf78292b74f27fcbed Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 11:54:15 -0400 Subject: [PATCH 111/122] Revert "handle typed arrays in parcats" This reverts commit 0f2ef235ce16ec2fba6aae8657dabc879b3b7d63. --- src/traces/parcats/defaults.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/traces/parcats/defaults.js b/src/traces/parcats/defaults.js index 83246e21f47..6a0c436a26f 100644 --- a/src/traces/parcats/defaults.js +++ b/src/traces/parcats/defaults.js @@ -1,7 +1,6 @@ 'use strict'; var Lib = require('../../lib'); -var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; var hasColorscale = require('../../components/colorscale/helpers').hasColorscale; var colorscaleDefaults = require('../../components/colorscale/defaults'); var handleDomainDefaults = require('../../plots/domain').defaults; @@ -45,7 +44,7 @@ function dimensionDefaults(dimensionIn, dimensionOut) { // Category level var arrayIn = dimensionIn.categoryarray; - var isValidArray = (Lib.isArrayOrTypedArray(arrayIn) && arrayIn.length > 0) || isTypedArraySpec(arrayIn); + var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0); var orderDefault; if(isValidArray) orderDefault = 'array'; From 9767454bffb69b49290ece8c91a60f35d6cc8466 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 11:55:25 -0400 Subject: [PATCH 112/122] Revert "handle typed arrays in scattergeo" This reverts commit 142e353a7331273a76932fc2c3beb8f68392591f. --- src/traces/scattergeo/calc.js | 6 +++--- src/traces/scattergeo/hover.js | 4 +--- src/traces/scattergeo/plot.js | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/traces/scattergeo/calc.js b/src/traces/scattergeo/calc.js index 65d7e6f0b2d..cdb811146f6 100644 --- a/src/traces/scattergeo/calc.js +++ b/src/traces/scattergeo/calc.js @@ -6,15 +6,15 @@ var BADNUM = require('../../constants/numerical').BADNUM; var calcMarkerColorscale = require('../scatter/colorscale_calc'); var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); var calcSelection = require('../scatter/calc_selection'); -var Lib = require('../../lib'); -var _ = Lib._; + +var _ = require('../../lib')._; function isNonBlankString(v) { return v && typeof v === 'string'; } module.exports = function calc(gd, trace) { - var hasLocationData = Lib.isArrayOrTypedArray(trace.locations); + var hasLocationData = Array.isArray(trace.locations); var len = hasLocationData ? trace.locations.length : trace._length; var calcTrace = new Array(len); diff --git a/src/traces/scattergeo/hover.js b/src/traces/scattergeo/hover.js index e35f809fbe0..b9d54ae6f7d 100644 --- a/src/traces/scattergeo/hover.js +++ b/src/traces/scattergeo/hover.js @@ -1,8 +1,6 @@ 'use strict'; var Fx = require('../../components/fx'); -var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; - var BADNUM = require('../../constants/numerical').BADNUM; var getTraceColor = require('../scatter/get_trace_color'); @@ -78,7 +76,7 @@ function getExtraText(trace, pt, pointData, labels) { attributes.hoverinfo.flags : hoverinfo.split('+'); - var hasLocation = parts.indexOf('location') !== -1 && isTypedArraySpec(trace.locations); + var hasLocation = parts.indexOf('location') !== -1 && Array.isArray(trace.locations); var hasLon = (parts.indexOf('lon') !== -1); var hasLat = (parts.indexOf('lat') !== -1); var hasText = (parts.indexOf('text') !== -1); diff --git a/src/traces/scattergeo/plot.js b/src/traces/scattergeo/plot.js index 9124900e8b3..29d57b2c95a 100644 --- a/src/traces/scattergeo/plot.js +++ b/src/traces/scattergeo/plot.js @@ -72,7 +72,7 @@ function calcGeoJSON(calcTrace, fullLayout) { var len = trace._length; var i, calcPt; - if(Lib.isArrayOrTypedArray(trace.locations)) { + if(Array.isArray(trace.locations)) { var locationmode = trace.locationmode; var features = locationmode === 'geojson-id' ? geoUtils.extractTraceFeature(calcTrace) : From dfa65dbd5570c05e1d6655843aa6f5aa773019c3 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 11:56:02 -0400 Subject: [PATCH 113/122] Revert "handle typed arrays in opacityscale" This reverts commit 60697dc0c916866928e0018e3a9bf4ae7686fd3c. --- src/traces/surface/defaults.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/traces/surface/defaults.js b/src/traces/surface/defaults.js index bd3b43a7ecf..3330882bf59 100644 --- a/src/traces/surface/defaults.js +++ b/src/traces/surface/defaults.js @@ -2,8 +2,6 @@ var Registry = require('../../registry'); var Lib = require('../../lib'); -var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; -var decodeTypedArraySpec = require('../../lib/array').decodeTypedArraySpec; var colorscaleDefaults = require('../../components/colorscale/defaults'); var attributes = require('./attributes'); @@ -27,7 +25,7 @@ function createWave(n, minOpacity) { function isValidScaleArray(scl) { var highestVal = 0; - if(!Lib.isArrayOrTypedArray(scl) || scl.length < 2) return false; + if(!Array.isArray(scl) || scl.length < 2) return false; if(!scl[0] || !scl[scl.length - 1]) return false; @@ -146,11 +144,6 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) { function opacityscaleDefaults(traceIn, traceOut, layout, coerce) { var opacityscale = coerce('opacityscale'); - - if(isTypedArraySpec(opacityscale)) { - traceOut.opacityscale = opacityscale = decodeTypedArraySpec(opacityscale); - } - if(opacityscale === 'max') { traceOut.opacityscale = [[0, MIN], [1, 1]]; } else if(opacityscale === 'min') { From 43d07808e7c5e9e6b26ff3901ad17fcea21a11e0 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 13:10:09 -0400 Subject: [PATCH 114/122] improve packing integers in b64 image test --- test/image/convert_b64.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index d0f6bc456b1..fd847f25eb6 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -12,7 +12,11 @@ 'float64': 'f8' } +int8bounds = numpy.iinfo(numpy.int8) +int16bounds = numpy.iinfo(numpy.int16) int32bounds = numpy.iinfo(numpy.int32) +uint8bounds = numpy.iinfo(numpy.uint8) +uint16bounds = numpy.iinfo(numpy.uint16) uint32bounds = numpy.iinfo(numpy.uint32) skipKeys = [ @@ -49,20 +53,31 @@ def arraysToB64(obj, newObj) : newObj[key] = val continue - # convert Big Ints until we could support them in plotly.js + # convert default Big Ints until we could support them in plotly.js if str(arr.dtype) == 'int64' : - if arr.max() > int32bounds.max or arr.min() < int32bounds.min : + max = arr.max() + min = arr.min() + if max <= int8bounds.max and min >= int8bounds.min : + arr = arr.astype(numpy.int8) + elif max <= int16bounds.max and min >= int16bounds.min : + arr = arr.astype(numpy.int16) + elif max <= int32bounds.max and min >= int32bounds.min : + arr = arr.astype(numpy.int32) + else : newObj[key] = val continue - arr = arr.astype(numpy.int32) elif str(arr.dtype) == 'uint64' : - if arr.max() > uint32bounds.max or arr.min() < uint32bounds.min : + if max <= uint8bounds.max and min >= uint8bounds.min : + arr = arr.astype(numpy.uint8) + elif max <= uint16bounds.max and min >= uint16bounds.min : + arr = arr.astype(numpy.uint16) + elif max <= uint32bounds.max and min >= uint32bounds.min : + arr = arr.astype(numpy.uint32) + else : newObj[key] = val continue - arr = arr.astype(numpy.uint32) - if str(arr.dtype) in plotlyjsShortTypes : newObj[key] = { 'dtype': plotlyjsShortTypes[str(arr.dtype)], @@ -73,7 +88,6 @@ def arraysToB64(obj, newObj) : newObj[key]['shape'] = str(arr.shape)[1:-1] #print(val) - #print('____________________') #print(newObj[key]) #print('____________________') else : From b93033fb49328ce3f870a1f7ddb7602fe12f3a68 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 13:40:54 -0400 Subject: [PATCH 115/122] handle typed arrays in transform filter value --- src/transforms/filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transforms/filter.js b/src/transforms/filter.js index e852a2a147f..6a05527db34 100644 --- a/src/transforms/filter.js +++ b/src/transforms/filter.js @@ -222,7 +222,7 @@ exports.calcTransform = function(gd, trace, opts) { function getFilterFunc(opts, d2c, targetCalendar) { var operation = opts.operation; var value = opts.value; - var hasArrayValue = Array.isArray(value); + var hasArrayValue = Lib.isArrayOrTypedArray(value); function isOperationIn(array) { return array.indexOf(operation) !== -1; From 6e3f987299043e95592531a5388cd4aad1739bf1 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 13:53:21 -0400 Subject: [PATCH 116/122] improve b64 test coverage include 2 item arrays too --- test/image/convert_b64.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index fd847f25eb6..524237c8d54 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -21,7 +21,8 @@ skipKeys = [ 'geojson', - 'layers' + 'layers', + 'range' ] def arraysToB64(obj, newObj) : @@ -48,8 +49,13 @@ def arraysToB64(obj, newObj) : newObj[key] = newList else : - # skip converting arrays with 2 items or less - if(arr.ndim == 1 and arr.shape[0] < 3) : + # In a real application one does not need to convert + # small arrays like those that only have 2 items. + # But here we convert 2 item array to test typed arrays + # in more places. + + # skip converting arrays with 1 item or less + if(arr.ndim == 1 and arr.shape[0] < 2) : newObj[key] = val continue From 4bab8dea9c1a22949c1c7544b3ddadf291fb556e Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 15:02:52 -0400 Subject: [PATCH 117/122] handle typed arrays in parcats categoryarray --- src/traces/parcats/defaults.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/traces/parcats/defaults.js b/src/traces/parcats/defaults.js index 6a0c436a26f..5d3319e4c3e 100644 --- a/src/traces/parcats/defaults.js +++ b/src/traces/parcats/defaults.js @@ -8,6 +8,7 @@ var handleArrayContainerDefaults = require('../../plots/array_container_defaults var attributes = require('./attributes'); var mergeLength = require('../parcoords/merge_length'); +var isTypedArraySpec = require('../../lib/array').isTypedArraySpec; function handleLineDefaults(traceIn, traceOut, defaultColor, layout, coerce) { coerce('line.shape'); @@ -44,7 +45,8 @@ function dimensionDefaults(dimensionIn, dimensionOut) { // Category level var arrayIn = dimensionIn.categoryarray; - var isValidArray = (Array.isArray(arrayIn) && arrayIn.length > 0); + var isValidArray = (Lib.isArrayOrTypedArray(arrayIn) && arrayIn.length > 0) || + isTypedArraySpec(arrayIn); var orderDefault; if(isValidArray) orderDefault = 'array'; From 9b0be90548c69ef0dad0987c1fa94779790e2dd1 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 15:23:53 -0400 Subject: [PATCH 118/122] handle typed arrays in scattergeo locations --- src/traces/scattergeo/calc.js | 3 ++- src/traces/scattergeo/plot.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/traces/scattergeo/calc.js b/src/traces/scattergeo/calc.js index cdb811146f6..da4e48c4817 100644 --- a/src/traces/scattergeo/calc.js +++ b/src/traces/scattergeo/calc.js @@ -6,6 +6,7 @@ var BADNUM = require('../../constants/numerical').BADNUM; var calcMarkerColorscale = require('../scatter/colorscale_calc'); var arraysToCalcdata = require('../scatter/arrays_to_calcdata'); var calcSelection = require('../scatter/calc_selection'); +var isArrayOrTypedArray = require('../../lib').isArrayOrTypedArray; var _ = require('../../lib')._; @@ -14,7 +15,7 @@ function isNonBlankString(v) { } module.exports = function calc(gd, trace) { - var hasLocationData = Array.isArray(trace.locations); + var hasLocationData = isArrayOrTypedArray(trace.locations); var len = hasLocationData ? trace.locations.length : trace._length; var calcTrace = new Array(len); diff --git a/src/traces/scattergeo/plot.js b/src/traces/scattergeo/plot.js index 29d57b2c95a..9124900e8b3 100644 --- a/src/traces/scattergeo/plot.js +++ b/src/traces/scattergeo/plot.js @@ -72,7 +72,7 @@ function calcGeoJSON(calcTrace, fullLayout) { var len = trace._length; var i, calcPt; - if(Array.isArray(trace.locations)) { + if(Lib.isArrayOrTypedArray(trace.locations)) { var locationmode = trace.locationmode; var features = locationmode === 'geojson-id' ? geoUtils.extractTraceFeature(calcTrace) : From 509f7dfd16d0b2a0b4abcab09731831112964182 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 13 Mar 2023 15:29:23 -0400 Subject: [PATCH 119/122] test b64 on arrays with only 1 item as well --- test/image/convert_b64.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/image/convert_b64.py b/test/image/convert_b64.py index 524237c8d54..18bc4a0c197 100644 --- a/test/image/convert_b64.py +++ b/test/image/convert_b64.py @@ -54,8 +54,8 @@ def arraysToB64(obj, newObj) : # But here we convert 2 item array to test typed arrays # in more places. - # skip converting arrays with 1 item or less - if(arr.ndim == 1 and arr.shape[0] < 2) : + # skip converting arrays with no items + if(arr.ndim == 1 and arr.shape[0] < 1) : newObj[key] = val continue From 25e8678045960d3531a1c8dfa667478e6a0082cd Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Tue, 14 Mar 2023 12:20:21 -0400 Subject: [PATCH 120/122] add b64 test dashboard - add script to convert mocks to b64 --- devtools/test_dashboard/server.js | 6 ++-- package.json | 1 + test/image/b64/.gitignore | 1 + test/image/generate_b64_mocks.py | 50 +++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 test/image/b64/.gitignore create mode 100644 test/image/generate_b64_mocks.py diff --git a/devtools/test_dashboard/server.js b/devtools/test_dashboard/server.js index b8e6103eba3..cc954a25d2c 100644 --- a/devtools/test_dashboard/server.js +++ b/devtools/test_dashboard/server.js @@ -12,6 +12,7 @@ config.optimization = { minimize: false }; var args = minimist(process.argv.slice(2), {}); var PORT = args.port || 3000; +var b64 = args.b64; var strict = args.strict; var mathjax3 = args.mathjax3; var mathjax3chtml = args.mathjax3chtml; @@ -23,6 +24,7 @@ if(!strict) { config.devtool = 'eval'; } +var mockFolder = path.join(constants.pathToImageTest, b64 ? 'b64' : 'mocks'); // mock list getMockFiles() @@ -104,7 +106,7 @@ compiler.run(function(devtoolsErr, devtoolsStats) { function getMockFiles() { return new Promise(function(resolve, reject) { - fs.readdir(constants.pathToTestImageMocks, function(err, files) { + fs.readdir(mockFolder, function(err, files) { if(err) { reject(err); } else { @@ -116,7 +118,7 @@ function getMockFiles() { function readFiles(files) { var promises = files.map(function(file) { - var filePath = path.join(constants.pathToTestImageMocks, file); + var filePath = path.join(mockFolder, file); return readFilePromise(filePath); }); diff --git a/package.json b/package.json index aa49684b62a..efa0f34de9d 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "test-requirejs": "node tasks/test_requirejs.js", "test-plain-obj": "node tasks/test_plain_obj.js", "test": "npm run test-jasmine -- --nowatch && npm run test-bundle && npm run test-image && npm run test-export && npm run test-syntax && npm run lint", + "b64": "python3 test/image/generate_b64_mocks.py && node devtools/test_dashboard/server.js --b64", "mathjax3": "node devtools/test_dashboard/server.js --mathjax3", "mathjax3chtml": "node devtools/test_dashboard/server.js --mathjax3chtml", "strict": "node devtools/test_dashboard/server.js --strict", diff --git a/test/image/b64/.gitignore b/test/image/b64/.gitignore new file mode 100644 index 00000000000..a6c57f5fb2f --- /dev/null +++ b/test/image/b64/.gitignore @@ -0,0 +1 @@ +*.json diff --git a/test/image/generate_b64_mocks.py b/test/image/generate_b64_mocks.py new file mode 100644 index 00000000000..11cefed542f --- /dev/null +++ b/test/image/generate_b64_mocks.py @@ -0,0 +1,50 @@ +import os +import sys +import json +import plotly.io as pio +from convert_b64 import arraysToB64 + +args = [] +if len(sys.argv) == 2 : + args = sys.argv[1].split() +elif len(sys.argv) > 1 : + args = sys.argv + +root = os.getcwd() +dirIn = os.path.join(root, 'test', 'image', 'mocks') +dirOut = os.path.join(root, 'test', 'image', 'b64') + +print('output to', dirOut) + +ALL_MOCKS = [os.path.splitext(a)[0] for a in os.listdir(dirIn) if a.endswith('.json')] +ALL_MOCKS.sort() + +if len(args) > 0 : + allNames = [a for a in args if a in ALL_MOCKS] +else : + allNames = ALL_MOCKS + +if len(allNames) == 0 : + print('error: Nothing to create!') + sys.exit(1) + +for name in allNames : + outName = name + + with open(os.path.join(dirIn, name + '.json'), 'r') as _in : + fig = json.load(_in) + + before = json.dumps(fig, indent = 2) + + newFig = dict() + arraysToB64(fig, newFig) + fig = newFig + + after = json.dumps(fig, indent = 2) + + if before != after : + print(outName) + + _out = open(os.path.join(dirOut, outName + '.json'), 'w') + _out.write(json.dumps(fig, indent = 2)) + _out.close() From e8d82f42607cac6ca14778e0f717fde6a5708f69 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 15 Mar 2023 10:11:35 -0400 Subject: [PATCH 121/122] place b64 mocks in the same folder so that devtools.js finds them --- devtools/test_dashboard/server.js | 3 +-- package.json | 2 +- test/image/b64/.gitignore | 1 - test/image/generate_b64_mocks.py | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) delete mode 100644 test/image/b64/.gitignore diff --git a/devtools/test_dashboard/server.js b/devtools/test_dashboard/server.js index cc954a25d2c..4c069e88379 100644 --- a/devtools/test_dashboard/server.js +++ b/devtools/test_dashboard/server.js @@ -12,7 +12,6 @@ config.optimization = { minimize: false }; var args = minimist(process.argv.slice(2), {}); var PORT = args.port || 3000; -var b64 = args.b64; var strict = args.strict; var mathjax3 = args.mathjax3; var mathjax3chtml = args.mathjax3chtml; @@ -24,7 +23,7 @@ if(!strict) { config.devtool = 'eval'; } -var mockFolder = path.join(constants.pathToImageTest, b64 ? 'b64' : 'mocks'); +var mockFolder = constants.pathToTestImageMocks; // mock list getMockFiles() diff --git a/package.json b/package.json index efa0f34de9d..659cce65acf 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "test-requirejs": "node tasks/test_requirejs.js", "test-plain-obj": "node tasks/test_plain_obj.js", "test": "npm run test-jasmine -- --nowatch && npm run test-bundle && npm run test-image && npm run test-export && npm run test-syntax && npm run lint", - "b64": "python3 test/image/generate_b64_mocks.py && node devtools/test_dashboard/server.js --b64", + "b64": "python3 test/image/generate_b64_mocks.py && node devtools/test_dashboard/server.js", "mathjax3": "node devtools/test_dashboard/server.js --mathjax3", "mathjax3chtml": "node devtools/test_dashboard/server.js --mathjax3chtml", "strict": "node devtools/test_dashboard/server.js --strict", diff --git a/test/image/b64/.gitignore b/test/image/b64/.gitignore deleted file mode 100644 index a6c57f5fb2f..00000000000 --- a/test/image/b64/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.json diff --git a/test/image/generate_b64_mocks.py b/test/image/generate_b64_mocks.py index 11cefed542f..923f3af5669 100644 --- a/test/image/generate_b64_mocks.py +++ b/test/image/generate_b64_mocks.py @@ -12,7 +12,7 @@ root = os.getcwd() dirIn = os.path.join(root, 'test', 'image', 'mocks') -dirOut = os.path.join(root, 'test', 'image', 'b64') +dirOut = dirIn print('output to', dirOut) From e9e221ee131945ffe3bbd39d685653641e6e47fd Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Wed, 15 Mar 2023 10:43:44 -0400 Subject: [PATCH 122/122] lint env_image script --- .circleci/env_image.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/env_image.sh b/.circleci/env_image.sh index ad546229cfb..4b684aa1599 100755 --- a/.circleci/env_image.sh +++ b/.circleci/env_image.sh @@ -7,4 +7,4 @@ sudo fc-cache -f && \ # install kaleido & plotly sudo python3 -m pip install kaleido==0.2.1 plotly==5.5.0 --progress-bar off # install numpy i.e. to convert arrays to typed arrays -sudo python3 -m pip install numpy==1.24.2 \ No newline at end of file +sudo python3 -m pip install numpy==1.24.2