diff --git a/lib/index.js b/lib/index.js
index 36083aa498b..7dfb08a60d3 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -26,6 +26,7 @@ Plotly.register([
require('./pie'),
require('./sunburst'),
+ require('./treemap'),
require('./funnelarea'),
require('./scatter3d'),
diff --git a/lib/treemap.js b/lib/treemap.js
new file mode 100644
index 00000000000..040cec276f7
--- /dev/null
+++ b/lib/treemap.js
@@ -0,0 +1,11 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+module.exports = require('../src/traces/treemap');
diff --git a/src/lib/index.js b/src/lib/index.js
index 0073533aab1..7a43f10b300 100644
--- a/src/lib/index.js
+++ b/src/lib/index.js
@@ -1159,6 +1159,10 @@ lib.isValidTextValue = function(v) {
return v || v === 0;
};
+/**
+ * @param {number} ratio
+ * @param {number} n (number of decimal places)
+ */
lib.formatPercent = function(ratio, n) {
n = n || 0;
var str = (Math.round(100 * ratio * Math.pow(10, n)) * Math.pow(0.1, n)).toFixed(n) + '%';
@@ -1175,3 +1179,32 @@ lib.isHidden = function(gd) {
var display = window.getComputedStyle(gd).display;
return !display || display === 'none';
};
+
+lib.getTextTransform = function(opts) {
+ var textX = opts.textX;
+ var textY = opts.textY;
+ var targetX = opts.targetX;
+ var targetY = opts.targetY;
+ var scale = opts.scale;
+ var rotate = opts.rotate;
+
+ var transformScale;
+ var transformRotate;
+ var transformTranslate;
+
+ if(scale < 1) transformScale = 'scale(' + scale + ') ';
+ else {
+ scale = 1;
+ transformScale = '';
+ }
+
+ transformRotate = (rotate) ?
+ 'rotate(' + rotate + ' ' + textX + ' ' + textY + ') ' : '';
+
+ // Note that scaling also affects the center of the text box
+ var translateX = (targetX - scale * textX);
+ var translateY = (targetY - scale * textY);
+ transformTranslate = 'translate(' + translateX + ' ' + translateY + ')';
+
+ return transformTranslate + transformScale + transformRotate;
+};
diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index 241f437ca3b..cd8353a81b8 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2450,7 +2450,7 @@ var traceUIControlPatterns = [
{pattern: /(^|value\.)visible$/, attr: 'legend.uirevision'},
{pattern: /^dimensions\[\d+\]\.constraintrange/},
{pattern: /^node\.(x|y|groups)/}, // for Sankey nodes
- {pattern: /^level$/}, // for Sunburst traces
+ {pattern: /^level$/}, // for Sunburst & Treemap traces
// below this you must be in editable: true mode
// TODO: I still put name and title with `trace.uirevision`
@@ -3780,6 +3780,9 @@ function makePlotFramework(gd) {
// single pie layer for the whole plot
fullLayout._pielayer = fullLayout._paper.append('g').classed('pielayer', true);
+ // single treemap layer for the whole plot
+ fullLayout._treemaplayer = fullLayout._paper.append('g').classed('treemaplayer', true);
+
// single sunburst layer for the whole plot
fullLayout._sunburstlayer = fullLayout._paper.append('g').classed('sunburstlayer', true);
diff --git a/src/plots/plots.js b/src/plots/plots.js
index c9aa68b131d..80ce2f2b7e3 100644
--- a/src/plots/plots.js
+++ b/src/plots/plots.js
@@ -23,6 +23,8 @@ var axisIDs = require('./cartesian/axis_ids');
var animationAttrs = require('./animation_attributes');
var frameAttrs = require('./frame_attributes');
+var getModuleCalcData = require('../plots/get_data').getModuleCalcData;
+
var relinkPrivateKeys = Lib.relinkPrivateKeys;
var _ = Lib._;
@@ -2771,9 +2773,10 @@ plots.doCalcdata = function(gd, traces) {
gd._hmpixcount = 0;
gd._hmlumcount = 0;
- // for sharing colors across pies / sunbursts / funnelarea (and for legend)
+ // for sharing colors across pies / sunbursts / treemap / funnelarea (and for legend)
fullLayout._piecolormap = {};
fullLayout._sunburstcolormap = {};
+ fullLayout._treemapcolormap = {};
fullLayout._funnelareacolormap = {};
// If traces were specified and this trace was not included,
@@ -3203,3 +3206,18 @@ plots.generalUpdatePerTraceModule = function(gd, subplot, subplotCalcData, subpl
// update moduleName -> calcData hash
subplot.traceHash = traceHash;
};
+
+plots.plotBasePlot = function(desiredType, gd, traces, transitionOpts, makeOnCompleteCallback) {
+ var _module = Registry.getModule(desiredType);
+ var cdmodule = getModuleCalcData(gd.calcdata, _module)[0];
+ _module.plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback);
+};
+
+plots.cleanBasePlot = function(desiredType, newFullData, newFullLayout, oldFullData, oldFullLayout) {
+ var had = (oldFullLayout._has && oldFullLayout._has(desiredType));
+ var has = (newFullLayout._has && newFullLayout._has(desiredType));
+
+ if(had && !has) {
+ oldFullLayout['_' + desiredType + 'layer'].selectAll('g.trace').remove();
+ }
+};
diff --git a/src/traces/bar/attributes.js b/src/traces/bar/attributes.js
index c800d8bc30a..206e1619ebc 100644
--- a/src/traces/bar/attributes.js
+++ b/src/traces/bar/attributes.js
@@ -13,7 +13,7 @@ var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplat
var texttemplateAttrs = require('../../plots/template_attributes').texttemplateAttrs;
var colorScaleAttrs = require('../../components/colorscale/attributes');
var fontAttrs = require('../../plots/font_attributes');
-var constants = require('./constants.js');
+var constants = require('./constants');
var extendFlat = require('../../lib/extend').extendFlat;
diff --git a/src/traces/bar/constants.js b/src/traces/bar/constants.js
index 89c9c2ba941..608bcc22041 100644
--- a/src/traces/bar/constants.js
+++ b/src/traces/bar/constants.js
@@ -10,5 +10,6 @@
'use strict';
module.exports = {
+ TEXTPAD: 3, // padding in pixels around text
eventDataKeys: []
};
diff --git a/src/traces/bar/plot.js b/src/traces/bar/plot.js
index 11d8ad24f68..2a513d4ee23 100644
--- a/src/traces/bar/plot.js
+++ b/src/traces/bar/plot.js
@@ -21,6 +21,7 @@ var tickText = require('../../plots/cartesian/axes').tickText;
var style = require('./style');
var helpers = require('./helpers');
+var constants = require('./constants');
var attributes = require('./attributes');
var attributeText = attributes.text;
@@ -28,8 +29,7 @@ var attributeTextPosition = attributes.textposition;
var appendArrayPointValue = require('../../components/fx/helpers').appendArrayPointValue;
-// padding in pixels around text
-var TEXTPAD = 3;
+var TEXTPAD = constants.TEXTPAD;
function dirSign(a, b) {
return (a < b) ? 1 : -1;
@@ -342,7 +342,7 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) {
trace.constraintext === 'both' ||
trace.constraintext === 'outside';
- transform = getTransform(toMoveOutsideBar(x0, x1, y0, y1, textBB, {
+ transform = Lib.getTextTransform(toMoveOutsideBar(x0, x1, y0, y1, textBB, {
isHorizontal: isHorizontal,
constrained: constrained,
angle: trace.textangle
@@ -352,7 +352,7 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) {
trace.constraintext === 'both' ||
trace.constraintext === 'inside';
- transform = getTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, {
+ transform = Lib.getTextTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, {
isHorizontal: isHorizontal,
constrained: constrained,
angle: trace.textangle,
@@ -510,35 +510,6 @@ function toMoveOutsideBar(x0, x1, y0, y1, textBB, opts) {
};
}
-function getTransform(opts) {
- var textX = opts.textX;
- var textY = opts.textY;
- var targetX = opts.targetX;
- var targetY = opts.targetY;
- var scale = opts.scale;
- var rotate = opts.rotate;
-
- var transformScale;
- var transformRotate;
- var transformTranslate;
-
- if(scale < 1) transformScale = 'scale(' + scale + ') ';
- else {
- scale = 1;
- transformScale = '';
- }
-
- transformRotate = (rotate) ?
- 'rotate(' + rotate + ' ' + textX + ' ' + textY + ') ' : '';
-
- // Note that scaling also affects the center of the text box
- var translateX = (targetX - scale * textX);
- var translateY = (targetY - scale * textY);
- transformTranslate = 'translate(' + translateX + ' ' + translateY + ')';
-
- return transformTranslate + transformScale + transformRotate;
-}
-
function getText(fullLayout, calcTrace, index, xa, ya) {
var trace = calcTrace[0].trace;
var texttemplate = trace.texttemplate;
@@ -695,7 +666,6 @@ function calcTextinfo(calcTrace, index, xa, ya) {
module.exports = {
plot: plot,
- getTransform: getTransform,
toMoveInsideBar: toMoveInsideBar,
toMoveOutsideBar: toMoveOutsideBar
};
diff --git a/src/traces/funnelarea/base_plot.js b/src/traces/funnelarea/base_plot.js
index 739fbc3050e..3dcfab8563c 100644
--- a/src/traces/funnelarea/base_plot.js
+++ b/src/traces/funnelarea/base_plot.js
@@ -8,22 +8,14 @@
'use strict';
-var Registry = require('../../registry');
-var getModuleCalcData = require('../../plots/get_data').getModuleCalcData;
+var plots = require('../../plots/plots');
exports.name = 'funnelarea';
-exports.plot = function(gd) {
- var Funnelarea = Registry.getModule('funnelarea');
- var cdFunnelarea = getModuleCalcData(gd.calcdata, Funnelarea)[0];
- Funnelarea.plot(gd, cdFunnelarea);
+exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
+ plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback);
};
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var hadFunnelarea = (oldFullLayout._has && oldFullLayout._has('funnelarea'));
- var hasFunnelarea = (newFullLayout._has && newFullLayout._has('funnelarea'));
-
- if(hadFunnelarea && !hasFunnelarea) {
- oldFullLayout._funnelarealayer.selectAll('g.trace').remove();
- }
+ plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout);
};
diff --git a/src/traces/funnelarea/plot.js b/src/traces/funnelarea/plot.js
index 9e1091e2d66..29ce263c1d1 100644
--- a/src/traces/funnelarea/plot.js
+++ b/src/traces/funnelarea/plot.js
@@ -15,7 +15,6 @@ var Lib = require('../../lib');
var svgTextUtils = require('../../lib/svg_text_utils');
var barPlot = require('../bar/plot');
-var getTransform = barPlot.getTransform;
var toMoveInsideBar = barPlot.toMoveInsideBar;
var pieHelpers = require('../pie/helpers');
@@ -115,7 +114,7 @@ module.exports = function plot(gd, cdModule) {
x0 = Math.max(pt.TL[0], pt.BL[0]);
x1 = Math.min(pt.TR[0], pt.BR[0]);
- transform = getTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, {
+ transform = Lib.getTextTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, {
isHorizontal: true,
constrained: true,
angle: 0,
diff --git a/src/traces/indicator/base_plot.js b/src/traces/indicator/base_plot.js
index 07a0e8b35de..1d8f5c48c88 100644
--- a/src/traces/indicator/base_plot.js
+++ b/src/traces/indicator/base_plot.js
@@ -8,22 +8,14 @@
'use strict';
-var Registry = require('../../registry');
-var getModuleCalcData = require('../../plots/get_data').getModuleCalcData;
+var plots = require('../../plots/plots');
-var name = exports.name = 'indicator';
+exports.name = 'indicator';
exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
- var _module = Registry.getModule(name);
- var cdmodule = getModuleCalcData(gd.calcdata, _module)[0];
- _module.plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback);
+ plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback);
};
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var had = (oldFullLayout._has && oldFullLayout._has(name));
- var has = (newFullLayout._has && newFullLayout._has(name));
-
- if(had && !has) {
- oldFullLayout._indicatorlayer.selectAll('g.trace').remove();
- }
+ plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout);
};
diff --git a/src/traces/pie/base_plot.js b/src/traces/pie/base_plot.js
index af1d15543a1..4842d10e652 100644
--- a/src/traces/pie/base_plot.js
+++ b/src/traces/pie/base_plot.js
@@ -8,22 +8,14 @@
'use strict';
-var Registry = require('../../registry');
-var getModuleCalcData = require('../../plots/get_data').getModuleCalcData;
+var plots = require('../../plots/plots');
exports.name = 'pie';
-exports.plot = function(gd) {
- var Pie = Registry.getModule('pie');
- var cdPie = getModuleCalcData(gd.calcdata, Pie)[0];
- Pie.plot(gd, cdPie);
+exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
+ plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback);
};
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var hadPie = (oldFullLayout._has && oldFullLayout._has('pie'));
- var hasPie = (newFullLayout._has && newFullLayout._has('pie'));
-
- if(hadPie && !hasPie) {
- oldFullLayout._pielayer.selectAll('g.trace').remove();
- }
+ plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout);
};
diff --git a/src/traces/sunburst/attributes.js b/src/traces/sunburst/attributes.js
index f36294917aa..2a3d60c2c71 100644
--- a/src/traces/sunburst/attributes.js
+++ b/src/traces/sunburst/attributes.js
@@ -11,9 +11,11 @@
var plotAttrs = require('../../plots/attributes');
var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
var texttemplateAttrs = require('../../plots/template_attributes').texttemplateAttrs;
+
+var colorScaleAttrs = require('../../components/colorscale/attributes');
var domainAttrs = require('../../plots/domain').attributes;
var pieAttrs = require('../pie/attributes');
-
+var constants = require('./constants');
var extendFlat = require('../../lib/extend').extendFlat;
module.exports = {
@@ -21,14 +23,14 @@ module.exports = {
valType: 'data_array',
editType: 'calc',
description: [
- 'Sets the labels of each of the sunburst sectors.'
+ 'Sets the labels of each of the sectors.'
].join(' ')
},
parents: {
valType: 'data_array',
editType: 'calc',
description: [
- 'Sets the parent sectors for each of the sunburst sectors.',
+ 'Sets the parent sectors for each of the sectors.',
'Empty string items \'\' are understood to reference',
'the root node in the hierarchy.',
'If `ids` is filled, `parents` items are understood to be "ids" themselves.',
@@ -41,7 +43,7 @@ module.exports = {
valType: 'data_array',
editType: 'calc',
description: [
- 'Sets the values associated with each of the sunburst sectors.',
+ 'Sets the values associated with each of the sectors.',
'Use with `branchvalues` to determine how the values are summed.'
].join(' ')
},
@@ -58,6 +60,20 @@ module.exports = {
'are taken to be the extra part not part of the sum of the values at their leaves.'
].join(' ')
},
+ count: {
+ valType: 'flaglist',
+ flags: [
+ 'branches',
+ 'leaves'
+ ],
+ dflt: 'leaves',
+ editType: 'calc',
+ role: 'info',
+ description: [
+ 'Determines counting the number of *leaves* and/or *branches*,',
+ 'when a `values` array is not provided.'
+ ].join(' ')
+ },
level: {
valType: 'any',
@@ -65,8 +81,8 @@ module.exports = {
anim: true,
role: 'info',
description: [
- 'Sets the level from which this sunburst trace hierarchy is rendered.',
- 'Set `level` to `\'\'` to start the sunburst from the root node in the hierarchy.',
+ 'Sets the level from which this trace hierarchy is rendered.',
+ 'Set `level` to `\'\'` to start from the root node in the hierarchy.',
'Must be an "id" if `ids` is filled in, otherwise plotly attempts to find a matching',
'item in `labels`.'
].join(' ')
@@ -77,17 +93,17 @@ module.exports = {
role: 'info',
dflt: -1,
description: [
- 'Sets the number of rendered sunburst rings from any given `level`.',
+ 'Sets the number of rendered sectors from any given `level`.',
'Set `maxdepth` to *-1* to render all the levels in the hierarchy.'
].join(' ')
},
- marker: {
+ marker: extendFlat({
colors: {
valType: 'data_array',
editType: 'calc',
description: [
- 'Sets the color of each sector of this sunburst chart.',
+ 'Sets the color of each sector of this trace.',
'If not specified, the default trace color set is used',
'to pick the sector colors.'
].join(' ')
@@ -111,6 +127,11 @@ module.exports = {
},
editType: 'calc'
},
+ colorScaleAttrs('marker', {
+ colorAttr: 'colors',
+ anim: false // TODO: set to anim: true?
+ })
+ ),
leaf: {
opacity: {
@@ -119,28 +140,58 @@ module.exports = {
role: 'style',
min: 0,
max: 1,
- dflt: 0.7,
- description: 'Sets the opacity of the leaves.'
+ description: [
+ 'Sets the opacity of the leaves. With colorscale',
+ 'it is defaulted to 1; otherwise it is defaulted to 0.7'
+ ].join(' ')
},
editType: 'plot'
},
text: pieAttrs.text,
- textinfo: extendFlat({}, pieAttrs.textinfo, {
+ textinfo: {
+ valType: 'flaglist',
+ role: 'info',
+ flags: [
+ 'label',
+ 'text',
+ 'value',
+ 'current path',
+ 'percent root',
+ 'percent entry',
+ 'percent parent'
+ ],
+ extras: ['none'],
editType: 'plot',
- flags: ['label', 'text', 'value']
- }),
+ description: [
+ 'Determines which trace information appear on the graph.'
+ ].join(' ')
+ },
+
+ // TODO: incorporate `label` and `value` in the eventData
texttemplate: texttemplateAttrs({editType: 'plot'}, {
- keys: ['label', 'text', 'value', 'color']
+ keys: constants.eventDataKeys.concat(['label', 'value'])
}),
- textfont: pieAttrs.textfont,
hovertext: pieAttrs.hovertext,
hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {
- flags: ['label', 'text', 'value', 'name']
+ flags: [
+ 'label',
+ 'text',
+ 'value',
+ 'name',
+ 'current path',
+ 'percent root',
+ 'percent entry',
+ 'percent parent'
+ ],
+ dflt: 'label+text+value+name'
+ }),
+ hovertemplate: hovertemplateAttrs({}, {
+ keys: constants.eventDataKeys
}),
- hovertemplate: hovertemplateAttrs(),
+ textfont: pieAttrs.textfont,
insidetextfont: pieAttrs.insidetextfont,
outsidetextfont: pieAttrs.outsidetextfont,
diff --git a/src/traces/sunburst/base_plot.js b/src/traces/sunburst/base_plot.js
index 82dddc89d59..3ae714a1689 100644
--- a/src/traces/sunburst/base_plot.js
+++ b/src/traces/sunburst/base_plot.js
@@ -8,22 +8,14 @@
'use strict';
-var Registry = require('../../registry');
-var getModuleCalcData = require('../../plots/get_data').getModuleCalcData;
+var plots = require('../../plots/plots');
-var name = exports.name = 'sunburst';
+exports.name = 'sunburst';
exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
- var _module = Registry.getModule(name);
- var cdmodule = getModuleCalcData(gd.calcdata, _module)[0];
- _module.plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback);
+ plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback);
};
exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
- var had = (oldFullLayout._has && oldFullLayout._has(name));
- var has = (newFullLayout._has && newFullLayout._has(name));
-
- if(had && !has) {
- oldFullLayout._sunburstlayer.selectAll('g.trace').remove();
- }
+ plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout);
};
diff --git a/src/traces/sunburst/calc.js b/src/traces/sunburst/calc.js
index a864e9bd708..04d8a196106 100644
--- a/src/traces/sunburst/calc.js
+++ b/src/traces/sunburst/calc.js
@@ -12,21 +12,25 @@ var d3Hierarchy = require('d3-hierarchy');
var isNumeric = require('fast-isnumeric');
var Lib = require('../../lib');
+var makeColorScaleFn = require('../../components/colorscale').makeColorScaleFuncFromTrace;
var makePullColorFn = require('../pie/calc').makePullColorFn;
var generateExtendedColors = require('../pie/calc').generateExtendedColors;
-var isArrayOrTypedArray = Lib.isArrayOrTypedArray;
+var Colorscale = require('../../components/colorscale');
+var hasColorscale = Colorscale.hasColorscale;
+var colorscaleCalc = Colorscale.calc;
var sunburstExtendedColorWays = {};
+var treemapExtendedColorWays = {};
exports.calc = function(gd, trace) {
var fullLayout = gd._fullLayout;
var ids = trace.ids;
- var hasIds = isArrayOrTypedArray(ids);
+ var hasIds = Lib.isArrayOrTypedArray(ids);
var labels = trace.labels;
var parents = trace.parents;
- var vals = trace.values;
- var hasVals = isArrayOrTypedArray(vals);
+ var values = trace.values;
+ var hasValues = Lib.isArrayOrTypedArray(values);
var cd = [];
var parent2children = {};
@@ -43,7 +47,7 @@ exports.calc = function(gd, trace) {
};
var isValidVal = function(i) {
- return !hasVals || (isNumeric(vals[i]) && vals[i] >= 0);
+ return !hasValues || (isNumeric(values[i]) && values[i] >= 0);
};
var len;
@@ -67,7 +71,7 @@ exports.calc = function(gd, trace) {
getId = function(i) { return String(labels[i]); };
}
- if(hasVals) len = Math.min(len, vals.length);
+ if(hasValues) len = Math.min(len, values.length);
for(var i = 0; i < len; i++) {
if(isValid(i)) {
@@ -81,7 +85,7 @@ exports.calc = function(gd, trace) {
label: isValidKey(labels[i]) ? String(labels[i]) : ''
};
- if(hasVals) cdi.v = +vals[i];
+ if(hasValues) cdi.v = +values[i];
cd.push(cdi);
addToLookup(pid, id);
}
@@ -107,7 +111,7 @@ exports.calc = function(gd, trace) {
label: k
});
} else {
- return Lib.warn('Multiple implied roots, cannot build sunburst hierarchy.');
+ return Lib.warn('Multiple implied roots, cannot build ' + trace.type + ' hierarchy.');
}
} else if(parent2children[''].length > 1) {
var dummyId = Lib.randstr();
@@ -135,13 +139,13 @@ exports.calc = function(gd, trace) {
.id(function(d) { return d.id; })
.parentId(function(d) { return d.pid; })(cd);
} catch(e) {
- return Lib.warn('Failed to build sunburst hierarchy. Error: ' + e.message);
+ return Lib.warn('Failed to build ' + trace.type + ' hierarchy. Error: ' + e.message);
}
var hierarchy = d3Hierarchy.hierarchy(root);
var failed = false;
- if(hasVals) {
+ if(hasValues) {
switch(trace.branchvalues) {
case 'remainder':
hierarchy.sum(function(d) { return d.data.v; });
@@ -158,7 +162,9 @@ exports.calc = function(gd, trace) {
failed = true;
return Lib.warn([
'Total value for node', d.data.data.id,
- 'is smaller than the sum of its children.'
+ 'is smaller than the sum of its children.',
+ '\nparent value =', v,
+ '\nchildren sum =', partialSum
].join(' '));
}
}
@@ -168,7 +174,10 @@ exports.calc = function(gd, trace) {
break;
}
} else {
- hierarchy.count();
+ countDescendants(hierarchy, trace, {
+ branches: trace.count.indexOf('branches') !== -1,
+ leaves: trace.count.indexOf('leaves') !== -1
+ });
}
if(failed) return;
@@ -176,16 +185,34 @@ exports.calc = function(gd, trace) {
// TODO add way to sort by height also?
hierarchy.sort(function(a, b) { return b.value - a.value; });
+ var pullColor;
+ var scaleColor;
var colors = trace.marker.colors || [];
- var pullColor = makePullColorFn(fullLayout._sunburstcolormap);
+ trace._hasColorscale = hasColorscale(trace, 'marker');
+ if(trace._hasColorscale) {
+ if(!colors.length) {
+ colors = hasValues ? trace.values : trace._values;
+ }
+
+ colorscaleCalc(gd, trace, {
+ vals: colors,
+ containerStr: 'marker',
+ cLetter: 'c'
+ });
+
+ scaleColor = makeColorScaleFn(trace.marker);
+ } else {
+ pullColor = makePullColorFn(fullLayout['_' + trace.type + 'colormap']);
+ }
// TODO keep track of 'root-children' (i.e. branch) for hover info etc.
hierarchy.each(function(d) {
var cdi = d.data.data;
- var id = cdi.id;
// N.B. this mutates items in `cd`
- cdi.color = pullColor(colors[cdi.i], id);
+ cdi.color = trace._hasColorscale ?
+ scaleColor(colors[cdi.i]) :
+ pullColor(colors[cdi.i], cdi.id);
});
cd[0].hierarchy = hierarchy;
@@ -200,14 +227,16 @@ exports.calc = function(gd, trace) {
* This is done after sorting, so we pick defaults
* in the order slices will be displayed
*/
-exports.crossTraceCalc = function(gd) {
+exports._runCrossTraceCalc = function(desiredType, gd) {
var fullLayout = gd._fullLayout;
var calcdata = gd.calcdata;
- var colorWay = fullLayout.sunburstcolorway;
- var colorMap = fullLayout._sunburstcolormap;
+ var colorWay = fullLayout[desiredType + 'colorway'];
+ var colorMap = fullLayout['_' + desiredType + 'colormap'];
- if(fullLayout.extendsunburstcolors) {
- colorWay = generateExtendedColors(colorWay, sunburstExtendedColorWays);
+ if(fullLayout['extend' + desiredType + 'colors']) {
+ colorWay = generateExtendedColors(colorWay,
+ desiredType === 'treemap' ? treemapExtendedColorWays : sunburstExtendedColorWays
+ );
}
var dfltColorCount = 0;
@@ -238,8 +267,38 @@ exports.crossTraceCalc = function(gd) {
for(var i = 0; i < calcdata.length; i++) {
var cd = calcdata[i];
var cd0 = cd[0];
- if(cd0.trace.type === 'sunburst' && cd0.hierarchy) {
+ if(cd0.trace.type === desiredType && cd0.hierarchy) {
cd0.hierarchy.each(pickColor);
}
}
};
+
+exports.crossTraceCalc = function(gd) {
+ return exports._runCrossTraceCalc('sunburst', gd);
+};
+
+function countDescendants(node, trace, opts) {
+ var nChild = 0;
+
+ var children = node.children;
+ if(children) {
+ var len = children.length;
+
+ for(var i = 0; i < len; i++) {
+ nChild += countDescendants(children[i], trace, opts);
+ }
+
+ if(opts.branches) nChild++; // count this branch
+ } else {
+ if(opts.leaves) nChild++; // count this leaf
+ }
+
+ // save to the node
+ node.value = node.data.data.value = nChild;
+
+ // save to the trace
+ if(!trace._values) trace._values = [];
+ trace._values[node.data.data.i] = nChild;
+
+ return nChild;
+}
diff --git a/src/traces/sunburst/constants.js b/src/traces/sunburst/constants.js
index fafc4c52c0d..753f2b6986f 100644
--- a/src/traces/sunburst/constants.js
+++ b/src/traces/sunburst/constants.js
@@ -10,5 +10,17 @@
module.exports = {
CLICK_TRANSITION_TIME: 750,
- CLICK_TRANSITION_EASING: 'linear'
+ CLICK_TRANSITION_EASING: 'linear',
+ eventDataKeys: [
+ // string
+ 'currentPath',
+ 'root',
+ 'entry',
+ // no need to add 'parent' here
+
+ // percentages i.e. ratios
+ 'percentRoot',
+ 'percentEntry',
+ 'percentParent'
+ ]
};
diff --git a/src/traces/sunburst/defaults.js b/src/traces/sunburst/defaults.js
index c34e27b019e..a8f758cbcff 100644
--- a/src/traces/sunburst/defaults.js
+++ b/src/traces/sunburst/defaults.js
@@ -13,6 +13,10 @@ var attributes = require('./attributes');
var handleDomainDefaults = require('../../plots/domain').defaults;
var handleText = require('../bar/defaults').handleText;
+var Colorscale = require('../../components/colorscale');
+var hasColorscale = Colorscale.hasColorscale;
+var colorscaleDefaults = Colorscale.handleDefaults;
+
module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
function coerce(attr, dflt) {
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
@@ -27,7 +31,11 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
}
var vals = coerce('values');
- if(vals && vals.length) coerce('branchvalues');
+ if(vals && vals.length) {
+ coerce('branchvalues');
+ } else {
+ coerce('count');
+ }
coerce('level');
coerce('maxdepth');
@@ -36,8 +44,12 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
if(lineWidth) coerce('marker.line.color', layout.paper_bgcolor);
coerce('marker.colors');
+ var withColorscale = hasColorscale(traceIn, 'marker');
+ if(withColorscale) {
+ colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'});
+ }
- coerce('leaf.opacity');
+ coerce('leaf.opacity', withColorscale ? 1 : 0.7);
var text = coerce('text');
coerce('texttemplate');
diff --git a/src/traces/sunburst/fx.js b/src/traces/sunburst/fx.js
new file mode 100644
index 00000000000..25f4eceb1a1
--- /dev/null
+++ b/src/traces/sunburst/fx.js
@@ -0,0 +1,315 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var d3 = require('d3');
+var Registry = require('../../registry');
+var appendArrayPointValue = require('../../components/fx/helpers').appendArrayPointValue;
+var Fx = require('../../components/fx');
+var Lib = require('../../lib');
+var Events = require('../../lib/events');
+
+var helpers = require('./helpers');
+var pieHelpers = require('../pie/helpers');
+
+var formatValue = pieHelpers.formatPieValue;
+
+module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
+ var cd0 = cd[0];
+ var trace = cd0.trace;
+ var hierarchy = cd0.hierarchy;
+ var rootLabel = hierarchy.data.data.pid;
+ var readLabel = function(refPt) {
+ var l = helpers.getPtLabel(refPt);
+ return l === undefined ? rootLabel : l;
+ };
+
+ var isSunburst = trace.type === 'sunburst';
+ var isTreemap = trace.type === 'treemap';
+
+ // hover state vars
+ // have we drawn a hover label, so it should be cleared later
+ if(!('_hasHoverLabel' in trace)) trace._hasHoverLabel = false;
+ // have we emitted a hover event, so later an unhover event should be emitted
+ // note that click events do not depend on this - you can still get them
+ // with hovermode: false or if you were earlier dragging, then clicked
+ // in the same slice that you moused up in
+ if(!('_hasHoverEvent' in trace)) trace._hasHoverEvent = false;
+
+ var onMouseOver = function(pt) {
+ var fullLayoutNow = gd._fullLayout;
+
+ if(gd._dragging || fullLayoutNow.hovermode === false) return;
+
+ var traceNow = gd._fullData[trace.index];
+ var cdi = pt.data.data;
+ var ptNumber = cdi.i;
+ var isRoot = helpers.isHierarchyRoot(pt);
+ var parent = helpers.getParent(hierarchy, pt);
+
+ var val = helpers.getVal(pt);
+
+ var _cast = function(astr) {
+ return Lib.castOption(traceNow, ptNumber, astr);
+ };
+
+ var hovertemplate = _cast('hovertemplate');
+ var hoverinfo = Fx.castHoverinfo(traceNow, fullLayoutNow, ptNumber);
+ var separators = fullLayoutNow.separators;
+
+ if(hovertemplate || (hoverinfo && hoverinfo !== 'none' && hoverinfo !== 'skip')) {
+ var hoverCenterX;
+ var hoverCenterY;
+ if(isSunburst) {
+ hoverCenterX = cd0.cx + pt.pxmid[0] * (1 - pt.rInscribed);
+ hoverCenterY = cd0.cy + pt.pxmid[1] * (1 - pt.rInscribed);
+ }
+ if(isTreemap) {
+ hoverCenterX = pt._hoverX;
+ hoverCenterY = pt._hoverY;
+ }
+
+ var hoverPt = {};
+ var parts = [];
+ var thisText = [];
+ var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
+
+ if(hoverinfo) {
+ parts = hoverinfo === 'all' ?
+ traceNow._module.attributes.hoverinfo.flags :
+ hoverinfo.split('+');
+ }
+
+ hoverPt.label = cdi.label;
+ if(hasFlag('label') && hoverPt.label) thisText.push(hoverPt.label);
+
+ if(cdi.hasOwnProperty('v')) {
+ hoverPt.value = cdi.v;
+ hoverPt.valueLabel = formatValue(hoverPt.value, separators);
+ if(hasFlag('value')) thisText.push(hoverPt.valueLabel);
+ }
+
+ hoverPt.currentPath = pt.currentPath = helpers.getPath(pt.data);
+ if(hasFlag('current path') && !isRoot) {
+ thisText.push(hoverPt.currentPath);
+ }
+
+ var tx;
+ var allPercents = [];
+ var insertPercent = function() {
+ if(allPercents.indexOf(tx) === -1) { // no need to add redundant info
+ thisText.push(tx);
+ allPercents.push(tx);
+ }
+ };
+
+ if(parent) {
+ hoverPt.percentParent = pt.percentParent = val / helpers.getVal(parent);
+ hoverPt.parent = pt.parentString = readLabel(parent);
+ if(hasFlag('percent parent')) {
+ tx = helpers.formatPercent(hoverPt.percentParent, separators) + ' of ' + hoverPt.parent;
+ insertPercent();
+ }
+ }
+
+ hoverPt.percentEntry = pt.percentEntry = val / helpers.getVal(entry);
+ hoverPt.entry = pt.entry = readLabel(entry);
+ if(hasFlag('percent entry') && !isRoot && !pt.onPathbar) {
+ tx = helpers.formatPercent(hoverPt.percentEntry, separators) + ' of ' + hoverPt.entry;
+ insertPercent();
+ }
+
+ hoverPt.percentRoot = pt.percentRoot = val / helpers.getVal(hierarchy);
+ hoverPt.root = pt.root = readLabel(hierarchy);
+ if(hasFlag('percent root') && !isRoot) {
+ tx = helpers.formatPercent(hoverPt.percentRoot, separators) + ' of ' + hoverPt.root;
+ insertPercent();
+ }
+
+ hoverPt.text = _cast('hovertext') || _cast('text');
+ if(hasFlag('text')) {
+ tx = hoverPt.text;
+ if(Lib.isValidTextValue(tx)) thisText.push(tx);
+ }
+
+ var hoverItems = {
+ trace: traceNow,
+ y: hoverCenterY,
+ text: thisText.join('
'),
+ name: (hovertemplate || hasFlag('name')) ? traceNow.name : undefined,
+ color: _cast('hoverlabel.bgcolor') || cdi.color,
+ borderColor: _cast('hoverlabel.bordercolor'),
+ fontFamily: _cast('hoverlabel.font.family'),
+ fontSize: _cast('hoverlabel.font.size'),
+ fontColor: _cast('hoverlabel.font.color'),
+ nameLength: _cast('hoverlabel.namelength'),
+ textAlign: _cast('hoverlabel.align'),
+ hovertemplate: hovertemplate,
+ hovertemplateLabels: hoverPt,
+ eventData: [makeEventData(pt, traceNow, opts.eventDataKeys)]
+ };
+
+ if(isSunburst) {
+ hoverItems.x0 = hoverCenterX - pt.rInscribed * pt.rpx1;
+ hoverItems.x1 = hoverCenterX + pt.rInscribed * pt.rpx1;
+ hoverItems.idealAlign = pt.pxmid[0] < 0 ? 'left' : 'right';
+ }
+ if(isTreemap) {
+ hoverItems.x = hoverCenterX;
+ hoverItems.idealAlign = hoverCenterX < 0 ? 'left' : 'right';
+ }
+
+ Fx.loneHover(hoverItems, {
+ container: fullLayoutNow._hoverlayer.node(),
+ outerContainer: fullLayoutNow._paper.node(),
+ gd: gd
+ });
+
+ trace._hasHoverLabel = true;
+ }
+
+ if(isTreemap) {
+ var slice = sliceTop.select('path.surface');
+ opts.styleOne(slice, pt, traceNow, {
+ hovered: true
+ });
+ }
+
+ trace._hasHoverEvent = true;
+ gd.emit('plotly_hover', {
+ points: [makeEventData(pt, traceNow, opts.eventDataKeys)],
+ event: d3.event
+ });
+ };
+
+ var onMouseOut = function(evt) {
+ var fullLayoutNow = gd._fullLayout;
+ var traceNow = gd._fullData[trace.index];
+ var pt = d3.select(this).datum();
+
+ if(trace._hasHoverEvent) {
+ evt.originalEvent = d3.event;
+ gd.emit('plotly_unhover', {
+ points: [makeEventData(pt, traceNow, opts.eventDataKeys)],
+ event: d3.event
+ });
+ trace._hasHoverEvent = false;
+ }
+
+ if(trace._hasHoverLabel) {
+ Fx.loneUnhover(fullLayoutNow._hoverlayer.node());
+ trace._hasHoverLabel = false;
+ }
+
+ if(isTreemap) {
+ var slice = sliceTop.select('path.surface');
+ opts.styleOne(slice, pt, traceNow, {
+ hovered: false
+ });
+ }
+ };
+
+ var onClick = function(pt) {
+ // TODO: this does not support right-click. If we want to support it, we
+ // would likely need to change pie to use dragElement instead of straight
+ // mapbox event binding. Or perhaps better, make a simple wrapper with the
+ // right mousedown, mousemove, and mouseup handlers just for a left/right click
+ // mapbox would use this too.
+ var fullLayoutNow = gd._fullLayout;
+ var traceNow = gd._fullData[trace.index];
+
+ var clickVal = Events.triggerHandler(gd, 'plotly_' + trace.type + 'click', {
+ points: [makeEventData(pt, traceNow, opts.eventDataKeys)],
+ event: d3.event
+ });
+
+ // 'regular' click event when sunburst/treemap click is disabled or when
+ // clicking on leaves or the hierarchy root
+ if(
+ clickVal === false ||
+ isSunburst && (
+ helpers.isHierarchyRoot(pt) ||
+ helpers.isLeaf(pt)
+ )
+ ) {
+ if(fullLayoutNow.hovermode) {
+ gd._hoverdata = [makeEventData(pt, traceNow, opts.eventDataKeys)];
+ Fx.click(gd, d3.event);
+ }
+ return;
+ }
+
+ // skip if triggered from dragging a nearby cartesian subplot
+ if(gd._dragging) return;
+
+ // skip during transitions, to avoid potential bugs
+ // we could remove this check later
+ if(gd._transitioning) return;
+
+ // store 'old' level in guiEdit stash, so that subsequent Plotly.react
+ // calls with the same uirevision can start from the same entry
+ Registry.call('_storeDirectGUIEdit', traceNow, fullLayoutNow._tracePreGUI[traceNow.uid], {
+ level: traceNow.level
+ });
+
+ var id = helpers.getPtId(pt);
+ var nextEntry = helpers.isEntry(pt) ?
+ helpers.findEntryWithChild(hierarchy, id) :
+ helpers.findEntryWithLevel(hierarchy, id);
+
+ var frame = {
+ data: [{level: helpers.getPtId(nextEntry)}],
+ traces: [trace.index]
+ };
+
+ var animOpts = {
+ frame: {
+ redraw: false,
+ duration: opts.transitionTime
+ },
+ transition: {
+ duration: opts.transitionTime,
+ easing: opts.transitionEasing
+ },
+ mode: 'immediate',
+ fromcurrent: true
+ };
+
+ Fx.loneUnhover(fullLayoutNow._hoverlayer.node());
+ Registry.call('animate', gd, frame, animOpts);
+ };
+
+ sliceTop.on('mouseover', onMouseOver);
+ sliceTop.on('mouseout', onMouseOut);
+ sliceTop.on('click', onClick);
+};
+
+function makeEventData(pt, trace, keys) {
+ var cdi = pt.data.data;
+
+ var out = {
+ curveNumber: trace.index,
+ pointNumber: cdi.i,
+ data: trace._input,
+ fullData: trace,
+
+ // TODO more things like 'children', 'siblings', 'hierarchy?
+ };
+
+ for(var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ if(key in pt) out[key] = pt[key];
+ }
+ // handle special case of parent
+ if('parentString' in pt) out.parent = pt.parentString;
+
+ appendArrayPointValue(out, trace, cdi.i);
+
+ return out;
+}
diff --git a/src/traces/sunburst/helpers.js b/src/traces/sunburst/helpers.js
index a475e31a3f8..269dfcbbeec 100644
--- a/src/traces/sunburst/helpers.js
+++ b/src/traces/sunburst/helpers.js
@@ -11,24 +11,7 @@
var Lib = require('../../lib');
var Color = require('../../components/color');
var setCursor = require('../../lib/setcursor');
-var appendArrayPointValue = require('../../components/fx/helpers').appendArrayPointValue;
-
-exports.makeEventData = function(pt, trace) {
- var cdi = pt.data.data;
-
- var out = {
- curveNumber: trace.index,
- pointNumber: cdi.i,
- data: trace._input,
- fullData: trace,
-
- // TODO more things like 'children', 'siblings', 'hierarchy?
- };
-
- appendArrayPointValue(out, trace, cdi.i);
-
- return out;
-};
+var pieHelpers = require('../pie/helpers');
exports.findEntryWithLevel = function(hierarchy, level) {
var out;
@@ -56,11 +39,6 @@ exports.findEntryWithChild = function(hierarchy, childId) {
return out || hierarchy;
};
-exports.isHierachyRoot = function(pt) {
- var cdi = pt.data.data;
- return cdi.pid === '';
-};
-
exports.isEntry = function(pt) {
return !pt.parent;
};
@@ -70,40 +48,42 @@ exports.isLeaf = function(pt) {
};
exports.getPtId = function(pt) {
- var cdi = pt.data.data;
- return cdi.id;
+ return pt.data.data.id;
};
-exports.setSliceCursor = function(sliceTop, gd, opts) {
- var pt = sliceTop.datum();
- var isTransitioning = (opts || {}).isTransitioning;
- setCursor(sliceTop, (isTransitioning || exports.isLeaf(pt) || exports.isHierachyRoot(pt)) ? null : 'pointer');
+exports.getPtLabel = function(pt) {
+ return pt.data.data.label;
};
-exports.determineOutsideTextFont = function(trace, pt, layoutFont) {
- var cdi = pt.data.data;
- var ptNumber = cdi.i;
-
- var color = Lib.castOption(trace, ptNumber, 'outsidetextfont.color') ||
- Lib.castOption(trace, ptNumber, 'textfont.color') ||
- layoutFont.color;
+exports.getVal = function(d) {
+ return d.value;
+};
- var family = Lib.castOption(trace, ptNumber, 'outsidetextfont.family') ||
- Lib.castOption(trace, ptNumber, 'textfont.family') ||
- layoutFont.family;
+exports.isHierarchyRoot = function(pt) {
+ return getParentId(pt) === '';
+};
- var size = Lib.castOption(trace, ptNumber, 'outsidetextfont.size') ||
- Lib.castOption(trace, ptNumber, 'textfont.size') ||
- layoutFont.size;
+exports.setSliceCursor = function(sliceTop, gd, opts) {
+ var hide = opts.isTransitioning;
+ if(!hide) {
+ var pt = sliceTop.datum();
+ hide = (
+ (opts.hideOnRoot && exports.isHierarchyRoot(pt)) ||
+ (opts.hideOnLeaves && exports.isLeaf(pt))
+ );
+ }
+ setCursor(sliceTop, hide ? null : 'pointer');
+};
+function determineOutsideTextFont(trace, pt, layoutFont) {
return {
- color: color,
- family: family,
- size: size
+ color: exports.getOutsideTextFontKey('color', trace, pt, layoutFont),
+ family: exports.getOutsideTextFontKey('family', trace, pt, layoutFont),
+ size: exports.getOutsideTextFontKey('size', trace, pt, layoutFont)
};
-};
+}
-exports.determineInsideTextFont = function(trace, pt, layoutFont) {
+function determineInsideTextFont(trace, pt, layoutFont, cont) {
var cdi = pt.data.data;
var ptNumber = cdi.i;
@@ -116,17 +96,84 @@ exports.determineInsideTextFont = function(trace, pt, layoutFont) {
customColor = Lib.castOption(trace._input, ptNumber, 'textfont.color');
}
- var family = Lib.castOption(trace, ptNumber, 'insidetextfont.family') ||
- Lib.castOption(trace, ptNumber, 'textfont.family') ||
- layoutFont.family;
-
- var size = Lib.castOption(trace, ptNumber, 'insidetextfont.size') ||
- Lib.castOption(trace, ptNumber, 'textfont.size') ||
- layoutFont.size;
-
return {
color: customColor || Color.contrast(cdi.color),
- family: family,
- size: size
+ family: exports.getInsideTextFontKey('family', cont || trace, pt, layoutFont),
+ size: exports.getInsideTextFontKey('size', cont || trace, pt, layoutFont)
};
+}
+
+exports.getInsideTextFontKey = function(keyStr, trace, pt, layoutFont) {
+ var ptNumber = pt.data.data.i;
+
+ return (
+ Lib.castOption(trace, ptNumber, 'insidetextfont.' + keyStr) ||
+ Lib.castOption(trace, ptNumber, 'textfont.' + keyStr) ||
+ layoutFont.size
+ );
+};
+
+exports.getOutsideTextFontKey = function(keyStr, trace, pt, layoutFont) {
+ var ptNumber = pt.data.data.i;
+
+ return (
+ Lib.castOption(trace, ptNumber, 'outsidetextfont.' + keyStr) ||
+ Lib.castOption(trace, ptNumber, 'textfont.' + keyStr) ||
+ layoutFont.size
+ );
+};
+
+exports.isOutsideText = function(trace, pt) {
+ return !trace._hasColorscale && exports.isHierarchyRoot(pt);
+};
+
+exports.determineTextFont = function(trace, pt, layoutFont, cont) {
+ return exports.isOutsideText(trace, pt) ?
+ determineOutsideTextFont(trace, pt, layoutFont) :
+ determineInsideTextFont(trace, pt, layoutFont, cont);
+};
+
+exports.hasTransition = function(transitionOpts) {
+ // We could optimize hasTransition per trace,
+ // as sunburst & treemap have no cross-trace logic!
+ return !!(transitionOpts && transitionOpts.duration > 0);
+};
+
+exports.getMaxDepth = function(trace) {
+ return trace.maxdepth >= 0 ? trace.maxdepth : Infinity;
+};
+
+exports.isHeader = function(pt, trace) { // it is only used in treemap.
+ return !(exports.isLeaf(pt) || pt.depth === trace._maxDepth - 1);
+};
+
+function getParentId(pt) {
+ return pt.data.data.pid;
+}
+
+exports.getParent = function(hierarchy, pt) {
+ var parentId = getParentId(pt);
+ return parentId === '' ?
+ undefined :
+ exports.findEntryWithLevel(hierarchy, parentId);
+};
+
+exports.listPath = function(d, keyStr) {
+ var parent = d.parent;
+ if(!parent) return [];
+ var list = keyStr ? [parent.data[keyStr]] : [parent];
+ return exports.listPath(parent, keyStr).concat(list);
+};
+
+exports.getPath = function(d) {
+ return exports.listPath(d, 'label').join('/') + '/';
+};
+
+exports.formatValue = pieHelpers.formatPieValue;
+
+// TODO: should combine the two in a separate PR - Also please note Lib.formatPercent should support separators.
+exports.formatPercent = function(v, separators) {
+ var tx = Lib.formatPercent(v, 0); // use funnel(area) version
+ if(tx === '0%') tx = pieHelpers.formatPiePercent(v, separators); // use pie version
+ return tx;
};
diff --git a/src/traces/sunburst/index.js b/src/traces/sunburst/index.js
index 05246586994..83f7dd70c2d 100644
--- a/src/traces/sunburst/index.js
+++ b/src/traces/sunburst/index.js
@@ -23,9 +23,11 @@ module.exports = {
calc: require('./calc').calc,
crossTraceCalc: require('./calc').crossTraceCalc,
- plot: require('./plot'),
+ plot: require('./plot').plot,
style: require('./style').style,
+ colorbar: require('../scatter/marker_colorbar'),
+
meta: {
description: [
'Visualize hierarchal data spanning outward radially from root to leaves.',
diff --git a/src/traces/sunburst/plot.js b/src/traces/sunburst/plot.js
index d8bedac6df0..89028f8bbe7 100644
--- a/src/traces/sunburst/plot.js
+++ b/src/traces/sunburst/plot.js
@@ -11,21 +11,18 @@
var d3 = require('d3');
var d3Hierarchy = require('d3-hierarchy');
-var Registry = require('../../registry');
-var Fx = require('../../components/fx');
var Drawing = require('../../components/drawing');
var Lib = require('../../lib');
-var Events = require('../../lib/events');
var svgTextUtils = require('../../lib/svg_text_utils');
var transformInsideText = require('../pie/plot').transformInsideText;
-var formatPieValue = require('../pie/helpers').formatPieValue;
var styleOne = require('./style').styleOne;
+var attachFxHandlers = require('./fx');
var constants = require('./constants');
var helpers = require('./helpers');
-module.exports = function(gd, cdmodule, transitionOpts, makeOnCompleteCallback) {
+exports.plot = function(gd, cdmodule, transitionOpts, makeOnCompleteCallback) {
var fullLayout = gd._fullLayout;
var layer = fullLayout._sunburstlayer;
var join, onComplete;
@@ -33,7 +30,7 @@ module.exports = function(gd, cdmodule, transitionOpts, makeOnCompleteCallback)
// If transition config is provided, then it is only a partial replot and traces not
// updated are removed.
var isFullReplot = !transitionOpts;
- var hasTransition = transitionOpts && transitionOpts.duration > 0;
+ var hasTransition = helpers.hasTransition(transitionOpts);
join = layer.selectAll('g.trace.sunburst')
.data(cdmodule, function(cd) { return cd[0].trace.uid; });
@@ -80,9 +77,7 @@ module.exports = function(gd, cdmodule, transitionOpts, makeOnCompleteCallback)
function plotOne(gd, cd, element, transitionOpts) {
var fullLayout = gd._fullLayout;
- // We could optimize hasTransition per trace,
- // as sunburst has no cross-trace logic!
- var hasTransition = transitionOpts && transitionOpts.duration > 0;
+ var hasTransition = helpers.hasTransition(transitionOpts);
var gTrace = d3.select(element);
var slices = gTrace.selectAll('g.slice');
@@ -90,8 +85,8 @@ function plotOne(gd, cd, element, transitionOpts) {
var cd0 = cd[0];
var trace = cd0.trace;
var hierarchy = cd0.hierarchy;
- var entry = findEntryWithLevel(hierarchy, trace.level);
- var maxDepth = trace.maxdepth >= 0 ? trace.maxdepth : Infinity;
+ var entry = helpers.findEntryWithLevel(hierarchy, trace.level);
+ var maxDepth = helpers.getMaxDepth(trace);
var gs = fullLayout._size;
var domain = trace.domain;
@@ -130,12 +125,12 @@ function plotOne(gd, cd, element, transitionOpts) {
// N.B. slice data isn't the calcdata,
// grab corresponding calcdata item in sliceData[i].data.data
var sliceData = partition(entry).descendants();
+
var maxHeight = entry.height + 1;
var yOffset = 0;
var cutoff = maxDepth;
-
// N.B. handle multiple-root special case
- if(cd0.hasMultipleRoots && helpers.isHierachyRoot(entry)) {
+ if(cd0.hasMultipleRoots && helpers.isHierarchyRoot(entry)) {
sliceData = sliceData.slice(1);
maxHeight -= 1;
yOffset = 1;
@@ -197,9 +192,13 @@ function plotOne(gd, cd, element, transitionOpts) {
if(hasTransition) {
updateSlices = updateSlices.transition().each('end', function() {
// N.B. gd._transitioning is (still) *true* by the time
- // transition updates get hare
+ // transition updates get here
var sliceTop = d3.select(this);
- helpers.setSliceCursor(sliceTop, gd, {isTransitioning: false});
+ helpers.setSliceCursor(sliceTop, gd, {
+ hideOnRoot: true,
+ hideOnLeaves: true,
+ isTransitioning: false
+ });
});
}
@@ -221,7 +220,7 @@ function plotOne(gd, cd, element, transitionOpts) {
if(hasTransition) {
slicePath.transition().attrTween('d', function(pt2) {
- var interp = makeUpdateSliceIntepolator(pt2);
+ var interp = makeUpdateSliceInterpolator(pt2);
return function(t) { return pathSlice(interp(t)); };
});
} else {
@@ -229,8 +228,16 @@ function plotOne(gd, cd, element, transitionOpts) {
}
sliceTop
- .call(attachFxHandlers, gd, cd)
- .call(helpers.setSliceCursor, gd, {isTransitioning: gd._transitioning});
+ .call(attachFxHandlers, entry, gd, cd, {
+ eventDataKeys: constants.eventDataKeys,
+ transitionTime: constants.CLICK_TRANSITION_TIME,
+ transitionEasing: constants.CLICK_TRANSITION_EASING
+ })
+ .call(helpers.setSliceCursor, gd, {
+ hideOnRoot: true,
+ hideOnLeaves: true,
+ isTransitioning: gd._transitioning
+ });
slicePath.call(styleOne, pt, trace);
@@ -241,12 +248,10 @@ function plotOne(gd, cd, element, transitionOpts) {
s.attr('data-notex', 1);
});
- sliceText.text(formatSliceLabel(pt, trace, fullLayout))
+ sliceText.text(exports.formatSliceLabel(pt, entry, trace, cd, fullLayout))
.classed('slicetext', true)
.attr('text-anchor', 'middle')
- .call(Drawing.font, helpers.isHierachyRoot(pt) ?
- helpers.determineOutsideTextFont(trace, pt, fullLayout.font) :
- helpers.determineInsideTextFont(trace, pt, fullLayout.font))
+ .call(Drawing.font, helpers.determineTextFont(trace, pt, fullLayout.font))
.call(svgTextUtils.convertToTspans, gd);
// position the text relative to the slice
@@ -267,7 +272,7 @@ function plotOne(gd, cd, element, transitionOpts) {
if(hasTransition) {
sliceText.transition().attrTween('transform', function(pt2) {
- var interp = makeUpdateTextInterpolar(pt2);
+ var interp = makeUpdateTextInterpolator(pt2);
return function(t) { return strTransform(interp(t), textBB); };
});
} else {
@@ -315,7 +320,7 @@ function plotOne(gd, cd, element, transitionOpts) {
return d3.interpolate(prev, next);
}
- function makeUpdateSliceIntepolator(pt) {
+ function makeUpdateSliceInterpolator(pt) {
var prev0 = prevLookup[helpers.getPtId(pt)];
var prev;
var next = {x0: pt.x0, x1: pt.x1, rpx0: pt.rpx0, rpx1: pt.rpx1};
@@ -354,7 +359,7 @@ function plotOne(gd, cd, element, transitionOpts) {
return d3.interpolate(prev, next);
}
- function makeUpdateTextInterpolar(pt) {
+ function makeUpdateTextInterpolator(pt) {
var prev0 = prevLookup[helpers.getPtId(pt)];
var prev;
var transform = pt.transform;
@@ -471,211 +476,7 @@ function partition(entry) {
.size([2 * Math.PI, entry.height + 1])(entry);
}
-function findEntryWithLevel(hierarchy, level) {
- var out;
- if(level) {
- hierarchy.eachAfter(function(pt) {
- if(helpers.getPtId(pt) === level) {
- return out = pt.copy();
- }
- });
- }
- return out || hierarchy;
-}
-
-function findEntryWithChild(hierarchy, childId) {
- var out;
- hierarchy.eachAfter(function(pt) {
- var children = pt.children || [];
- for(var i = 0; i < children.length; i++) {
- var child = children[i];
- if(helpers.getPtId(child) === childId) {
- return out = pt.copy();
- }
- }
- });
- return out || hierarchy;
-}
-
-function attachFxHandlers(sliceTop, gd, cd) {
- var cd0 = cd[0];
- var trace = cd0.trace;
-
- // hover state vars
- // have we drawn a hover label, so it should be cleared later
- if(!('_hasHoverLabel' in trace)) trace._hasHoverLabel = false;
- // have we emitted a hover event, so later an unhover event should be emitted
- // note that click events do not depend on this - you can still get them
- // with hovermode: false or if you were earlier dragging, then clicked
- // in the same slice that you moused up in
- if(!('_hasHoverEvent' in trace)) trace._hasHoverEvent = false;
-
- sliceTop.on('mouseover', function(pt) {
- var fullLayoutNow = gd._fullLayout;
-
- if(gd._dragging || fullLayoutNow.hovermode === false) return;
-
- var traceNow = gd._fullData[trace.index];
- var cdi = pt.data.data;
- var ptNumber = cdi.i;
-
- var _cast = function(astr) {
- return Lib.castOption(traceNow, ptNumber, astr);
- };
-
- var hovertemplate = _cast('hovertemplate');
- var hoverinfo = Fx.castHoverinfo(traceNow, fullLayoutNow, ptNumber);
- var separators = fullLayoutNow.separators;
-
- if(hovertemplate || (hoverinfo && hoverinfo !== 'none' && hoverinfo !== 'skip')) {
- var rInscribed = pt.rInscribed;
- var hoverCenterX = cd0.cx + pt.pxmid[0] * (1 - rInscribed);
- var hoverCenterY = cd0.cy + pt.pxmid[1] * (1 - rInscribed);
- var hoverPt = {};
- var parts = [];
- var thisText = [];
- var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
-
- if(hoverinfo) {
- parts = hoverinfo === 'all' ?
- traceNow._module.attributes.hoverinfo.flags :
- hoverinfo.split('+');
- }
-
- hoverPt.label = cdi.label;
- if(hasFlag('label') && hoverPt.label) thisText.push(hoverPt.label);
-
- if(cdi.hasOwnProperty('v')) {
- hoverPt.value = cdi.v;
- hoverPt.valueLabel = formatPieValue(hoverPt.value, separators);
- if(hasFlag('value')) thisText.push(hoverPt.valueLabel);
- }
-
- hoverPt.text = _cast('hovertext') || _cast('text');
- if(hasFlag('text')) {
- var tx = hoverPt.text;
- if(Lib.isValidTextValue(tx)) thisText.push(tx);
- }
-
- Fx.loneHover({
- trace: traceNow,
- x0: hoverCenterX - rInscribed * pt.rpx1,
- x1: hoverCenterX + rInscribed * pt.rpx1,
- y: hoverCenterY,
- idealAlign: pt.pxmid[0] < 0 ? 'left' : 'right',
- text: thisText.join('
'),
- name: (hovertemplate || hasFlag('name')) ? traceNow.name : undefined,
- color: _cast('hoverlabel.bgcolor') || cdi.color,
- borderColor: _cast('hoverlabel.bordercolor'),
- fontFamily: _cast('hoverlabel.font.family'),
- fontSize: _cast('hoverlabel.font.size'),
- fontColor: _cast('hoverlabel.font.color'),
- nameLength: _cast('hoverlabel.namelength'),
- textAlign: _cast('hoverlabel.align'),
- hovertemplate: hovertemplate,
- hovertemplateLabels: hoverPt,
- eventData: [helpers.makeEventData(pt, traceNow)]
- }, {
- container: fullLayoutNow._hoverlayer.node(),
- outerContainer: fullLayoutNow._paper.node(),
- gd: gd
- });
-
- trace._hasHoverLabel = true;
- }
-
- trace._hasHoverEvent = true;
- gd.emit('plotly_hover', {
- points: [helpers.makeEventData(pt, traceNow)],
- event: d3.event
- });
- });
-
- sliceTop.on('mouseout', function(evt) {
- var fullLayoutNow = gd._fullLayout;
- var traceNow = gd._fullData[trace.index];
- var pt = d3.select(this).datum();
-
- if(trace._hasHoverEvent) {
- evt.originalEvent = d3.event;
- gd.emit('plotly_unhover', {
- points: [helpers.makeEventData(pt, traceNow)],
- event: d3.event
- });
- trace._hasHoverEvent = false;
- }
-
- if(trace._hasHoverLabel) {
- Fx.loneUnhover(fullLayoutNow._hoverlayer.node());
- trace._hasHoverLabel = false;
- }
- });
-
- sliceTop.on('click', function(pt) {
- // TODO: this does not support right-click. If we want to support it, we
- // would likely need to change pie to use dragElement instead of straight
- // mapbox event binding. Or perhaps better, make a simple wrapper with the
- // right mousedown, mousemove, and mouseup handlers just for a left/right click
- // mapbox would use this too.
- var fullLayoutNow = gd._fullLayout;
- var traceNow = gd._fullData[trace.index];
-
- var clickVal = Events.triggerHandler(gd, 'plotly_sunburstclick', {
- points: [helpers.makeEventData(pt, traceNow)],
- event: d3.event
- });
-
- // 'regular' click event when sunburstclick is disabled or when
- // clikcin on leaves or the hierarchy root
- if(clickVal === false || helpers.isLeaf(pt) || helpers.isHierachyRoot(pt)) {
- if(fullLayoutNow.hovermode) {
- gd._hoverdata = [helpers.makeEventData(pt, traceNow)];
- Fx.click(gd, d3.event);
- }
- return;
- }
-
- // skip if triggered from dragging a nearby cartesian subplot
- if(gd._dragging) return;
-
- // skip during transitions, to avoid potential bugs
- // we could remove this check later
- if(gd._transitioning) return;
-
- // store 'old' level in guiEdit stash, so that subsequent Plotly.react
- // calls with the same uirevision can start from the same entry
- Registry.call('_storeDirectGUIEdit', traceNow, fullLayoutNow._tracePreGUI[traceNow.uid], {level: traceNow.level});
-
- var hierarchy = cd0.hierarchy;
- var id = helpers.getPtId(pt);
- var nextEntry = helpers.isEntry(pt) ?
- findEntryWithChild(hierarchy, id) :
- findEntryWithLevel(hierarchy, id);
-
- var frame = {
- data: [{level: helpers.getPtId(nextEntry)}],
- traces: [trace.index]
- };
-
- var animOpts = {
- frame: {
- redraw: false,
- duration: constants.CLICK_TRANSITION_TIME
- },
- transition: {
- duration: constants.CLICK_TRANSITION_TIME,
- easing: constants.CLICK_TRANSITION_EASING
- },
- mode: 'immediate',
- fromcurrent: true
- };
-
- Fx.loneUnhover(fullLayoutNow._hoverlayer.node());
- Registry.call('animate', gd, frame, animOpts);
- });
-}
-
-function formatSliceLabel(pt, trace, fullLayout) {
+exports.formatSliceLabel = function(pt, entry, trace, cd, fullLayout) {
var texttemplate = trace.texttemplate;
var textinfo = trace.textinfo;
@@ -683,23 +484,71 @@ function formatSliceLabel(pt, trace, fullLayout) {
return '';
}
- var cdi = pt.data.data;
var separators = fullLayout.separators;
+ var cd0 = cd[0];
+ var cdi = pt.data.data;
+ var hierarchy = cd0.hierarchy;
+ var rootLabel = hierarchy.data.data.pid;
+ var readLabel = function(refPt) {
+ var l = helpers.getPtLabel(refPt);
+ return l === undefined ? rootLabel : l;
+ };
+
+ var isRoot = helpers.isHierarchyRoot(pt);
+ var parent = helpers.getParent(hierarchy, pt);
+ var val = helpers.getVal(pt);
+
if(!texttemplate) {
var parts = textinfo.split('+');
var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
var thisText = [];
+ var tx;
if(hasFlag('label') && cdi.label) {
thisText.push(cdi.label);
}
if(cdi.hasOwnProperty('v') && hasFlag('value')) {
- thisText.push(formatPieValue(cdi.v, separators));
+ thisText.push(helpers.formatValue(cdi.v, separators));
+ }
+
+ if(!isRoot) {
+ if(hasFlag('current path')) {
+ thisText.push(helpers.getPath(pt.data));
+ }
+
+ var nPercent = 0;
+ if(hasFlag('percent parent')) nPercent++;
+ if(hasFlag('percent entry')) nPercent++;
+ if(hasFlag('percent root')) nPercent++;
+ var hasMultiplePercents = nPercent > 1;
+
+ if(nPercent) {
+ var percent;
+ var addPercent = function(key) {
+ tx = helpers.formatPercent(percent, separators);
+
+ if(hasMultiplePercents) tx += ' of ' + key;
+ thisText.push(tx);
+ };
+
+ if(hasFlag('percent parent') && parent) {
+ percent = val / helpers.getVal(parent);
+ addPercent('parent');
+ }
+ if(hasFlag('percent entry')) {
+ percent = val / helpers.getVal(entry);
+ addPercent('entry');
+ }
+ if(hasFlag('percent root')) {
+ percent = val / helpers.getVal(hierarchy);
+ addPercent('root');
+ }
+ }
}
if(hasFlag('text')) {
- var tx = Lib.castOption(trace, cdi.i, 'text');
+ tx = Lib.castOption(trace, cdi.i, 'text');
if(Lib.isValidTextValue(tx)) thisText.push(tx);
}
@@ -712,8 +561,31 @@ function formatSliceLabel(pt, trace, fullLayout) {
if(cdi.label) obj.label = cdi.label;
if(cdi.hasOwnProperty('v')) {
obj.value = cdi.v;
- obj.valueLabel = formatPieValue(cdi.v, separators);
+ obj.valueLabel = helpers.formatValue(cdi.v, separators);
+ }
+
+ obj.currentPath = helpers.getPath(pt.data);
+
+ if(parent) {
+ obj.percentParent = val / helpers.getVal(parent);
+ obj.percentParentLabel = helpers.formatPercent(
+ obj.percentParent, separators
+ );
+ obj.parent = readLabel(parent);
}
+
+ obj.percentEntry = val / helpers.getVal(entry);
+ obj.percentEntryLabel = helpers.formatPercent(
+ obj.percentEntry, separators
+ );
+ obj.entry = readLabel(entry);
+
+ obj.percentRoot = val / helpers.getVal(hierarchy);
+ obj.percentRootLabel = helpers.formatPercent(
+ obj.percentRoot, separators
+ );
+ obj.root = readLabel(hierarchy);
+
if(cdi.hasOwnProperty('color')) {
obj.color = cdi.color;
}
@@ -721,7 +593,7 @@ function formatSliceLabel(pt, trace, fullLayout) {
if(Lib.isValidTextValue(ptTx) || ptTx === '') obj.text = ptTx;
obj.customdata = Lib.castOption(trace, cdi.i, 'customdata');
return Lib.texttemplateString(txt, obj, fullLayout._d3locale, obj, trace._meta || {});
-}
+};
function getInscribedRadiusFraction(pt) {
if(pt.rpx0 === 0 && Lib.isFullCircle([pt.x0, pt.x1])) {
diff --git a/src/traces/treemap/attributes.js b/src/traces/treemap/attributes.js
new file mode 100644
index 00000000000..58fc9c80a23
--- /dev/null
+++ b/src/traces/treemap/attributes.js
@@ -0,0 +1,286 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var hovertemplateAttrs = require('../../plots/template_attributes').hovertemplateAttrs;
+var texttemplateAttrs = require('../../plots/template_attributes').texttemplateAttrs;
+
+var colorScaleAttrs = require('../../components/colorscale/attributes');
+var domainAttrs = require('../../plots/domain').attributes;
+var pieAttrs = require('../pie/attributes');
+var sunburstAttrs = require('../sunburst/attributes');
+var constants = require('./constants');
+var extendFlat = require('../../lib/extend').extendFlat;
+
+module.exports = {
+ labels: sunburstAttrs.labels,
+ parents: sunburstAttrs.parents,
+
+ values: sunburstAttrs.values,
+ branchvalues: sunburstAttrs.branchvalues,
+ count: sunburstAttrs.count,
+
+ level: sunburstAttrs.level,
+ maxdepth: sunburstAttrs.maxdepth,
+
+ tiling: {
+ packing: {
+ valType: 'enumerated',
+ values: [
+ 'squarify',
+ 'binary',
+ 'dice',
+ 'slice',
+ 'slice-dice',
+ 'dice-slice'
+ ],
+ dflt: 'squarify',
+ role: 'info',
+ editType: 'plot',
+ description: [
+ 'Determines d3 treemap solver.',
+ 'For more info please refer to https://github.com/d3/d3-hierarchy#treemap-tiling'
+ ].join(' ')
+ },
+
+ squarifyratio: {
+ valType: 'number',
+ role: 'info',
+ min: 1,
+ dflt: 1,
+ editType: 'plot',
+ description: [
+ 'When using *squarify* `packing` algorithm, according to https://github.com/d3/d3-hierarchy/blob/master/README.md#squarify_ratio',
+ 'this option specifies the desired aspect ratio of the generated rectangles.',
+ 'The ratio must be specified as a number greater than or equal to one.',
+ 'Note that the orientation of the generated rectangles (tall or wide)',
+ 'is not implied by the ratio; for example, a ratio of two will attempt',
+ 'to produce a mixture of rectangles whose width:height ratio is either 2:1 or 1:2.',
+ 'When using *squarify*, unlike d3 which uses the Golden Ratio i.e. 1.618034,',
+ 'Plotly applies 1 to increase squares in treemap layouts.'
+ ].join(' ')
+ },
+
+ flip: {
+ valType: 'flaglist',
+ role: 'info',
+ flags: [
+ 'x',
+ 'y'
+ ],
+ dflt: '',
+ editType: 'plot',
+ description: [
+ 'Determines if the positions obtained from solver are flipped on each axis.'
+ ].join(' ')
+ },
+
+ pad: {
+ valType: 'number',
+ role: 'style',
+ min: 0,
+ dflt: 3,
+ editType: 'plot',
+ description: [
+ 'Sets the inner padding (in px).'
+ ].join(' ')
+ },
+
+ editType: 'calc',
+ },
+
+ marker: extendFlat({
+ pad: {
+ t: {
+ valType: 'number',
+ role: 'style',
+ min: 0,
+ editType: 'plot',
+ description: [
+ 'Sets the padding form the top (in px).'
+ ].join(' ')
+ },
+ l: {
+ valType: 'number',
+ role: 'style',
+ min: 0,
+ editType: 'plot',
+ description: [
+ 'Sets the padding form the left (in px).'
+ ].join(' ')
+ },
+ r: {
+ valType: 'number',
+ role: 'style',
+ min: 0,
+ editType: 'plot',
+ description: [
+ 'Sets the padding form the right (in px).'
+ ].join(' ')
+ },
+ b: {
+ valType: 'number',
+ role: 'style',
+ min: 0,
+ editType: 'plot',
+ description: [
+ 'Sets the padding form the bottom (in px).'
+ ].join(' ')
+ },
+
+ editType: 'calc'
+ },
+
+ colors: sunburstAttrs.marker.colors,
+
+ opacitybase: {
+ valType: 'number',
+ editType: 'style',
+ role: 'style',
+ min: 0,
+ max: 1,
+ dflt: 0.5,
+ description: [
+ 'Sets the base opacity of the headers based on the depth from the entry.',
+ 'This options is not available when having a `colorscale`.'
+ ].join(' ')
+ },
+
+ opacitystep: {
+ valType: 'number',
+ editType: 'style',
+ role: 'style',
+ min: 0,
+ max: 1,
+ dflt: 0.5,
+ description: [
+ 'Sets the increment for opacity of the headers based on the depth from the entry.',
+ 'This options is not available when having a `colorscale`.'
+ ].join(' ')
+ },
+
+ line: sunburstAttrs.marker.line,
+
+ editType: 'calc'
+ },
+ colorScaleAttrs('marker', {
+ colorAttr: 'colors',
+ anim: false // TODO: set to anim: true?
+ })
+ ),
+
+ pathbar: {
+ visible: {
+ valType: 'boolean',
+ dflt: true,
+ role: 'info',
+ editType: 'plot',
+ description: [
+ 'Determines if the path bar is drawn',
+ 'i.e. outside the trace `domain` and',
+ 'with one pixel gap.'
+ ].join(' ')
+ },
+
+ side: {
+ valType: 'enumerated',
+ values: [
+ 'top',
+ 'bottom'
+ ],
+ dflt: 'top',
+ role: 'info',
+ editType: 'plot',
+ description: [
+ 'Determines on which side of the the treemap the',
+ '`pathbar` should be presented.'
+ ].join(' ')
+ },
+
+ edgeshape: {
+ valType: 'enumerated',
+ values: [
+ '>',
+ '<',
+ '|',
+ '/',
+ '\\'
+ ],
+ dflt: '>',
+ role: 'style',
+ editType: 'plot',
+ description: [
+ 'Determines which shape is used for edges between `barpath` labels.'
+ ].join(' ')
+ },
+
+ thickness: {
+ valType: 'number',
+ min: 12,
+ role: 'info',
+ editType: 'plot',
+ description: [
+ 'Sets the thickness of `pathbar` (in px). If not specified the `pathbar.textfont.size` is used',
+ 'with 3 pixles extra padding on each side.'
+ ].join(' ')
+ },
+
+ opacity: {
+ valType: 'number',
+ editType: 'style',
+ role: 'style',
+ min: 0,
+ dflt: 0.5,
+ description: [
+ 'Sets the opacity of the pathbar',
+ 'This options is not available when having a `colorscale`.'
+ ].join(' ')
+ },
+
+ textfont: extendFlat({}, pieAttrs.textfont, {
+ description: 'Sets the font used inside `pathbar`.'
+ }),
+
+ editType: 'calc'
+ },
+
+ text: pieAttrs.text,
+ textinfo: sunburstAttrs.textinfo,
+ // TODO: incorporate `label` and `value` in the eventData
+ texttemplate: texttemplateAttrs({editType: 'plot'}, {
+ keys: constants.eventDataKeys.concat(['label', 'value'])
+ }),
+
+ hovertext: pieAttrs.hovertext,
+ hoverinfo: sunburstAttrs.hoverinfo,
+ hovertemplate: hovertemplateAttrs({}, {
+ keys: constants.eventDataKeys
+ }),
+
+ textfont: pieAttrs.textfont,
+ insidetextfont: pieAttrs.insidetextfont,
+ outsidetextfont: pieAttrs.outsidetextfont,
+
+ textposition: {
+ valType: 'enumerated',
+ values: [
+ 'top left', 'top center', 'top right',
+ 'middle left', 'middle center', 'middle right',
+ 'bottom left', 'bottom center', 'bottom right'
+ ],
+ dflt: 'top left',
+ role: 'style',
+ editType: 'plot',
+ description: [
+ 'Sets the positions of the `text` elements.'
+ ].join(' ')
+ },
+
+ domain: domainAttrs({name: 'treemap', trace: true, editType: 'calc'}),
+};
diff --git a/src/traces/treemap/base_plot.js b/src/traces/treemap/base_plot.js
new file mode 100644
index 00000000000..3573a6e537b
--- /dev/null
+++ b/src/traces/treemap/base_plot.js
@@ -0,0 +1,21 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var plots = require('../../plots/plots');
+
+exports.name = 'treemap';
+
+exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
+ plots.plotBasePlot(exports.name, gd, traces, transitionOpts, makeOnCompleteCallback);
+};
+
+exports.clean = function(newFullData, newFullLayout, oldFullData, oldFullLayout) {
+ plots.cleanBasePlot(exports.name, newFullData, newFullLayout, oldFullData, oldFullLayout);
+};
diff --git a/src/traces/treemap/calc.js b/src/traces/treemap/calc.js
new file mode 100644
index 00000000000..0d2469ecac2
--- /dev/null
+++ b/src/traces/treemap/calc.js
@@ -0,0 +1,19 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var calc = require('../sunburst/calc');
+
+exports.calc = function(gd, trace) {
+ return calc.calc(gd, trace);
+};
+
+exports.crossTraceCalc = function(gd) {
+ return calc._runCrossTraceCalc('treemap', gd);
+};
diff --git a/src/traces/treemap/constants.js b/src/traces/treemap/constants.js
new file mode 100644
index 00000000000..388ad69ca49
--- /dev/null
+++ b/src/traces/treemap/constants.js
@@ -0,0 +1,27 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+module.exports = {
+ CLICK_TRANSITION_TIME: 750,
+ CLICK_TRANSITION_EASING: 'poly',
+ eventDataKeys: [
+ // string
+ 'currentPath',
+ 'root',
+ 'entry',
+ // no need to add 'parent' here
+
+ // percentages i.e. ratios
+ 'percentRoot',
+ 'percentEntry',
+ 'percentParent'
+ ],
+ gapWithPathbar: 1 // i.e. one pixel
+};
diff --git a/src/traces/treemap/defaults.js b/src/traces/treemap/defaults.js
new file mode 100644
index 00000000000..ef53c076af3
--- /dev/null
+++ b/src/traces/treemap/defaults.js
@@ -0,0 +1,118 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Lib = require('../../lib');
+var attributes = require('./attributes');
+var Color = require('../../components/color');
+var handleDomainDefaults = require('../../plots/domain').defaults;
+var handleText = require('../bar/defaults').handleText;
+var TEXTPAD = require('../bar/constants').TEXTPAD;
+
+var Colorscale = require('../../components/colorscale');
+var hasColorscale = Colorscale.hasColorscale;
+var colorscaleDefaults = Colorscale.handleDefaults;
+
+module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
+ function coerce(attr, dflt) {
+ return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
+ }
+
+ var labels = coerce('labels');
+ var parents = coerce('parents');
+
+ if(!labels || !labels.length || !parents || !parents.length) {
+ traceOut.visible = false;
+ return;
+ }
+
+ var vals = coerce('values');
+ if(vals && vals.length) {
+ coerce('branchvalues');
+ } else {
+ coerce('count');
+ }
+
+ coerce('level');
+ coerce('maxdepth');
+
+ var packing = coerce('tiling.packing');
+ if(packing === 'squarify') {
+ coerce('tiling.squarifyratio');
+ }
+
+ coerce('tiling.flip');
+ coerce('tiling.pad');
+
+ var text = coerce('text');
+ coerce('texttemplate');
+ if(!traceOut.texttemplate) coerce('textinfo', Array.isArray(text) ? 'text+label' : 'label');
+
+ coerce('hovertext');
+ coerce('hovertemplate');
+
+ var textposition = 'auto';
+ handleText(traceIn, traceOut, layout, coerce, textposition, {
+ moduleHasSelected: false,
+ moduleHasUnselected: false,
+ moduleHasConstrain: false,
+ moduleHasCliponaxis: false,
+ moduleHasTextangle: false,
+ moduleHasInsideanchor: false
+ });
+ coerce('textposition');
+ var bottomText = traceOut.textposition.indexOf('bottom') !== -1;
+
+ var lineWidth = coerce('marker.line.width');
+ if(lineWidth) coerce('marker.line.color', layout.paper_bgcolor);
+
+ coerce('marker.colors');
+ var withColorscale = hasColorscale(traceIn, 'marker');
+
+ var headerSize = traceOut.textfont.size * 2;
+
+ coerce('marker.pad.t', bottomText ? headerSize / 4 : headerSize);
+ coerce('marker.pad.l', headerSize / 4);
+ coerce('marker.pad.r', headerSize / 4);
+ coerce('marker.pad.b', bottomText ? headerSize : headerSize / 4);
+
+ if(withColorscale) {
+ colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'});
+ } else {
+ coerce('marker.opacitybase');
+ coerce('marker.opacitystep');
+ coerce('pathbar.opacity');
+ }
+
+ traceOut._hovered = {
+ marker: {
+ opacity: 1,
+ line: {
+ width: 2,
+ color: Color.contrast(layout.paper_bgcolor)
+ }
+ }
+ };
+
+ var hasPathbar = coerce('pathbar.visible');
+ if(hasPathbar) {
+ Lib.coerceFont(coerce, 'pathbar.textfont', layout.font);
+
+ // This works even for multi-line labels as treemap pathbar trim out line breaks
+ coerce('pathbar.thickness', traceOut.pathbar.textfont.size + 2 * TEXTPAD);
+
+ coerce('pathbar.side');
+ coerce('pathbar.edgeshape');
+ }
+
+ handleDomainDefaults(traceOut, layout, coerce);
+
+ // do not support transforms for now
+ traceOut._length = null;
+};
diff --git a/src/traces/treemap/draw_ancestors.js b/src/traces/treemap/draw_ancestors.js
new file mode 100644
index 00000000000..87b87451f36
--- /dev/null
+++ b/src/traces/treemap/draw_ancestors.js
@@ -0,0 +1,179 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var d3 = require('d3');
+var Lib = require('../../lib');
+var Drawing = require('../../components/drawing');
+var svgTextUtils = require('../../lib/svg_text_utils');
+
+var partition = require('./partition');
+var styleOne = require('./style').styleOne;
+var constants = require('./constants');
+var helpers = require('../sunburst/helpers');
+var attachFxHandlers = require('../sunburst/fx');
+
+var onPathbar = true; // for Ancestors
+
+module.exports = function drawAncestors(gd, cd, entry, slices, opts) {
+ var barDifY = opts.barDifY;
+ var width = opts.width;
+ var height = opts.height;
+ var viewX = opts.viewX;
+ var viewY = opts.viewY;
+ var pathSlice = opts.pathSlice;
+ var toMoveInsideSlice = opts.toMoveInsideSlice;
+ var strTransform = opts.strTransform;
+ var hasTransition = opts.hasTransition;
+ var handleSlicesExit = opts.handleSlicesExit;
+ var makeUpdateSliceInterpolator = opts.makeUpdateSliceInterpolator;
+ var makeUpdateTextInterpolator = opts.makeUpdateTextInterpolator;
+ var refRect = {};
+
+ var fullLayout = gd._fullLayout;
+ var cd0 = cd[0];
+ var trace = cd0.trace;
+ var hierarchy = cd0.hierarchy;
+
+ var eachWidth = width / trace._entryDepth;
+
+ var pathIds = helpers.listPath(entry.data, 'id');
+
+ var sliceData = partition(hierarchy.copy(), [width, height], {
+ packing: 'dice',
+ pad: {
+ inner: 0,
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0
+ }
+ }).descendants();
+
+ // edit slices that show up on graph
+ sliceData = sliceData.filter(function(pt) {
+ var level = pathIds.indexOf(pt.data.id);
+ if(level === -1) return false;
+
+ pt.x0 = eachWidth * level;
+ pt.x1 = eachWidth * (level + 1);
+ pt.y0 = barDifY;
+ pt.y1 = barDifY + height;
+
+ pt.onPathbar = true;
+
+ return true;
+ });
+
+ sliceData.reverse();
+
+ slices = slices.data(sliceData, function(pt) { return helpers.getPtId(pt); });
+
+ slices.enter().append('g')
+ .classed('pathbar', true);
+
+ handleSlicesExit(slices, onPathbar, refRect, [width, height], pathSlice);
+
+ slices.order();
+
+ var updateSlices = slices;
+ if(hasTransition) {
+ updateSlices = updateSlices.transition().each('end', function() {
+ // N.B. gd._transitioning is (still) *true* by the time
+ // transition updates get here
+ var sliceTop = d3.select(this);
+ helpers.setSliceCursor(sliceTop, gd, {
+ hideOnRoot: false,
+ hideOnLeaves: false,
+ isTransitioning: false
+ });
+ });
+ }
+
+ updateSlices.each(function(pt) {
+ pt._hoverX = viewX(pt.x1 - height / 2);
+ pt._hoverY = viewY(pt.y1 - height / 2);
+
+ var sliceTop = d3.select(this);
+
+ var slicePath = Lib.ensureSingle(sliceTop, 'path', 'surface', function(s) {
+ s.style('pointer-events', 'all');
+ });
+
+ if(hasTransition) {
+ slicePath.transition().attrTween('d', function(pt2) {
+ var interp = makeUpdateSliceInterpolator(pt2, onPathbar, refRect, [width, height]);
+ return function(t) { return pathSlice(interp(t)); };
+ });
+ } else {
+ slicePath.attr('d', pathSlice);
+ }
+
+ sliceTop
+ .call(attachFxHandlers, entry, gd, cd, {
+ styleOne: styleOne,
+ eventDataKeys: constants.eventDataKeys,
+ transitionTime: constants.CLICK_TRANSITION_TIME,
+ transitionEasing: constants.CLICK_TRANSITION_EASING
+ })
+ .call(helpers.setSliceCursor, gd, {
+ hideOnRoot: false,
+ hideOnLeaves: false,
+ isTransitioning: gd._transitioning
+ });
+
+ slicePath.call(styleOne, pt, trace, {
+ hovered: false
+ });
+
+ var sliceTextGroup = Lib.ensureSingle(sliceTop, 'g', 'slicetext');
+ var sliceText = Lib.ensureSingle(sliceTextGroup, 'text', '', function(s) {
+ // prohibit tex interpretation until we can handle
+ // tex and regular text together
+ s.attr('data-notex', 1);
+ });
+
+ var tx = (helpers.getPtLabel(pt) || ' ').split('
').join(' ');
+
+ sliceText.text(tx)
+ .classed('slicetext', true)
+ .attr('text-anchor', 'start')
+ .call(Drawing.font, helpers.determineTextFont(trace, pt, fullLayout.font, trace.pathdir))
+ .call(svgTextUtils.convertToTspans, gd);
+
+ pt.textBB = Drawing.bBox(sliceText.node());
+ pt.transform = toMoveInsideSlice(
+ pt.x0,
+ pt.x1,
+ pt.y0,
+ pt.y1,
+ pt.textBB,
+ {
+ onPathbar: true
+ }
+ );
+
+ if(helpers.isOutsideText(trace, pt)) {
+ // consider in/out diff font sizes
+ pt.transform.targetY -= (
+ helpers.getOutsideTextFontKey('size', trace, pt, fullLayout.font) -
+ helpers.getInsideTextFontKey('size', trace, pt, fullLayout.font)
+ );
+ }
+
+ if(hasTransition) {
+ sliceText.transition().attrTween('transform', function(pt2) {
+ var interp = makeUpdateTextInterpolator(pt2, onPathbar, refRect, [width, height]);
+ return function(t) { return strTransform(interp(t)); };
+ });
+ } else {
+ sliceText.attr('transform', strTransform(pt));
+ }
+ });
+};
diff --git a/src/traces/treemap/draw_descendants.js b/src/traces/treemap/draw_descendants.js
new file mode 100644
index 00000000000..88e481ae0ce
--- /dev/null
+++ b/src/traces/treemap/draw_descendants.js
@@ -0,0 +1,213 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var d3 = require('d3');
+var Lib = require('../../lib');
+var Drawing = require('../../components/drawing');
+var svgTextUtils = require('../../lib/svg_text_utils');
+
+var partition = require('./partition');
+var styleOne = require('./style').styleOne;
+var constants = require('./constants');
+var helpers = require('../sunburst/helpers');
+var attachFxHandlers = require('../sunburst/fx');
+var formatSliceLabel = require('../sunburst/plot').formatSliceLabel;
+
+var onPathbar = false; // for Descendants
+
+module.exports = function drawDescendants(gd, cd, entry, slices, opts) {
+ var width = opts.width;
+ var height = opts.height;
+ var viewX = opts.viewX;
+ var viewY = opts.viewY;
+ var pathSlice = opts.pathSlice;
+ var toMoveInsideSlice = opts.toMoveInsideSlice;
+ var strTransform = opts.strTransform;
+ var hasTransition = opts.hasTransition;
+ var handleSlicesExit = opts.handleSlicesExit;
+ var makeUpdateSliceInterpolator = opts.makeUpdateSliceInterpolator;
+ var makeUpdateTextInterpolator = opts.makeUpdateTextInterpolator;
+ var prevEntry = opts.prevEntry;
+ var refRect = {};
+
+ var fullLayout = gd._fullLayout;
+ var cd0 = cd[0];
+ var trace = cd0.trace;
+
+ var hasLeft = trace.textposition.indexOf('left') !== -1;
+ var hasRight = trace.textposition.indexOf('right') !== -1;
+ var hasBottom = trace.textposition.indexOf('bottom') !== -1;
+
+ var noRoomForHeader = (!hasBottom && !trace.marker.pad.t) || (hasBottom && !trace.marker.pad.b);
+
+ // N.B. slice data isn't the calcdata,
+ // grab corresponding calcdata item in sliceData[i].data.data
+ var allData = partition(entry, [width, height], {
+ packing: trace.tiling.packing,
+ squarifyratio: trace.tiling.squarifyratio,
+ flipX: trace.tiling.flip.indexOf('x') > -1,
+ flipY: trace.tiling.flip.indexOf('y') > -1,
+ pad: {
+ inner: trace.tiling.pad,
+ top: trace.marker.pad.t,
+ left: trace.marker.pad.l,
+ right: trace.marker.pad.r,
+ bottom: trace.marker.pad.b,
+ }
+ });
+
+ var sliceData = allData.descendants();
+
+ slices = slices.data(sliceData, function(pt) {
+ // hide slices that won't show up on graph
+ if(pt.depth >= trace._maxDepth) {
+ pt.x0 = pt.x1 = (pt.x0 + pt.x1) / 2;
+ pt.y0 = pt.y1 = (pt.y0 + pt.y1) / 2;
+ }
+
+ return helpers.getPtId(pt);
+ });
+
+ slices.enter().append('g')
+ .classed('slice', true);
+
+ handleSlicesExit(slices, onPathbar, refRect, [width, height], pathSlice);
+
+ slices.order();
+
+ // next coords of previous entry
+ var nextOfPrevEntry = null;
+ if(hasTransition && prevEntry) {
+ var prevEntryId = helpers.getPtId(prevEntry);
+ slices.each(function(pt) {
+ if(nextOfPrevEntry === null && (helpers.getPtId(pt) === prevEntryId)) {
+ nextOfPrevEntry = {
+ x0: pt.x0,
+ x1: pt.x1,
+ y0: pt.y0,
+ y1: pt.y1
+ };
+ }
+ });
+ }
+
+ var getRefRect = function() {
+ return nextOfPrevEntry || {
+ x0: 0,
+ x1: width,
+ y0: 0,
+ y1: height
+ };
+ };
+
+ var updateSlices = slices;
+ if(hasTransition) {
+ updateSlices = updateSlices.transition().each('end', function() {
+ // N.B. gd._transitioning is (still) *true* by the time
+ // transition updates get here
+ var sliceTop = d3.select(this);
+ helpers.setSliceCursor(sliceTop, gd, {
+ hideOnRoot: true,
+ hideOnLeaves: false,
+ isTransitioning: false
+ });
+ });
+ }
+
+ updateSlices.each(function(pt) {
+ var isHeader = helpers.isHeader(pt, trace);
+
+ pt._hoverX = viewX(pt.x1 - trace.marker.pad.r),
+ pt._hoverY = hasBottom ?
+ viewY(pt.y1 - trace.marker.pad.b / 2) :
+ viewY(pt.y0 + trace.marker.pad.t / 2);
+
+ var sliceTop = d3.select(this);
+
+ var slicePath = Lib.ensureSingle(sliceTop, 'path', 'surface', function(s) {
+ s.style('pointer-events', 'all');
+ });
+
+ if(hasTransition) {
+ slicePath.transition().attrTween('d', function(pt2) {
+ var interp = makeUpdateSliceInterpolator(pt2, onPathbar, getRefRect(), [width, height]);
+ return function(t) { return pathSlice(interp(t)); };
+ });
+ } else {
+ slicePath.attr('d', pathSlice);
+ }
+
+ sliceTop
+ .call(attachFxHandlers, entry, gd, cd, {
+ styleOne: styleOne,
+ eventDataKeys: constants.eventDataKeys,
+ transitionTime: constants.CLICK_TRANSITION_TIME,
+ transitionEasing: constants.CLICK_TRANSITION_EASING
+ })
+ .call(helpers.setSliceCursor, gd, { isTransitioning: gd._transitioning });
+
+ slicePath.call(styleOne, pt, trace, {
+ hovered: false
+ });
+
+ var sliceTextGroup = Lib.ensureSingle(sliceTop, 'g', 'slicetext');
+ var sliceText = Lib.ensureSingle(sliceTextGroup, 'text', '', function(s) {
+ // prohibit tex interpretation until we can handle
+ // tex and regular text together
+ s.attr('data-notex', 1);
+ });
+
+ var tx;
+ if(isHeader) {
+ if(noRoomForHeader) return;
+
+ tx = helpers.getPtLabel(pt);
+ } else {
+ tx = formatSliceLabel(pt, entry, trace, cd, fullLayout) || ' ';
+ }
+
+ sliceText.text(tx)
+ .classed('slicetext', true)
+ .attr('text-anchor', hasRight ? 'end' : (hasLeft || isHeader) ? 'start' : 'middle')
+ .call(Drawing.font, helpers.determineTextFont(trace, pt, fullLayout.font))
+ .call(svgTextUtils.convertToTspans, gd);
+
+ pt.textBB = Drawing.bBox(sliceText.node());
+ pt.transform = toMoveInsideSlice(
+ pt.x0,
+ pt.x1,
+ pt.y0,
+ pt.y1,
+ pt.textBB,
+ {
+ isHeader: isHeader
+ }
+ );
+
+ if(helpers.isOutsideText(trace, pt)) {
+ // consider in/out diff font sizes
+ pt.transform.targetY -= (
+ helpers.getOutsideTextFontKey('size', trace, pt, fullLayout.font) -
+ helpers.getInsideTextFontKey('size', trace, pt, fullLayout.font)
+ );
+ }
+
+ if(hasTransition) {
+ sliceText.transition().attrTween('transform', function(pt2) {
+ var interp = makeUpdateTextInterpolator(pt2, onPathbar, getRefRect(), [width, height]);
+ return function(t) { return strTransform(interp(t)); };
+ });
+ } else {
+ sliceText.attr('transform', strTransform(pt));
+ }
+ });
+
+ return nextOfPrevEntry;
+};
diff --git a/src/traces/treemap/index.js b/src/traces/treemap/index.js
new file mode 100644
index 00000000000..28e57f4c644
--- /dev/null
+++ b/src/traces/treemap/index.js
@@ -0,0 +1,38 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+module.exports = {
+ moduleType: 'trace',
+ name: 'treemap',
+ basePlotModule: require('./base_plot'),
+ categories: [],
+ animatable: true,
+
+ attributes: require('./attributes'),
+ layoutAttributes: require('./layout_attributes'),
+ supplyDefaults: require('./defaults'),
+ supplyLayoutDefaults: require('./layout_defaults'),
+
+ calc: require('./calc').calc,
+ crossTraceCalc: require('./calc').crossTraceCalc,
+
+ plot: require('./plot'),
+ style: require('./style').style,
+
+ colorbar: require('../scatter/marker_colorbar'),
+
+ meta: {
+ description: [
+ 'Visualize hierarchal data from leaves (and/or outer branches) towards root',
+ 'with rectangles. The treemap sectors are determined by the entries in',
+ '*labels* or *ids* and in *parents*.'
+ ].join(' ')
+ }
+};
diff --git a/src/traces/treemap/layout_attributes.js b/src/traces/treemap/layout_attributes.js
new file mode 100644
index 00000000000..93ac90ebaae
--- /dev/null
+++ b/src/traces/treemap/layout_attributes.js
@@ -0,0 +1,39 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+module.exports = {
+ treemapcolorway: {
+ valType: 'colorlist',
+ role: 'style',
+ editType: 'calc',
+ description: [
+ 'Sets the default treemap slice colors. Defaults to the main',
+ '`colorway` used for trace colors. If you specify a new',
+ 'list here it can still be extended with lighter and darker',
+ 'colors, see `extendtreemapcolors`.'
+ ].join(' ')
+ },
+ extendtreemapcolors: {
+ valType: 'boolean',
+ dflt: true,
+ role: 'style',
+ editType: 'calc',
+ description: [
+ 'If `true`, the treemap slice colors (whether given by `treemapcolorway` or',
+ 'inherited from `colorway`) will be extended to three times its',
+ 'original length by first repeating every color 20% lighter then',
+ 'each color 20% darker. This is intended to reduce the likelihood',
+ 'of reusing the same color when you have many slices, but you can',
+ 'set `false` to disable.',
+ 'Colors provided in the trace, using `marker.colors`, are never',
+ 'extended.'
+ ].join(' ')
+ }
+};
diff --git a/src/traces/treemap/layout_defaults.js b/src/traces/treemap/layout_defaults.js
new file mode 100644
index 00000000000..e1ccbb37c7a
--- /dev/null
+++ b/src/traces/treemap/layout_defaults.js
@@ -0,0 +1,20 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Lib = require('../../lib');
+var layoutAttributes = require('./layout_attributes');
+
+module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) {
+ function coerce(attr, dflt) {
+ return Lib.coerce(layoutIn, layoutOut, layoutAttributes, attr, dflt);
+ }
+ coerce('treemapcolorway', layoutOut.colorway);
+ coerce('extendtreemapcolors');
+};
diff --git a/src/traces/treemap/partition.js b/src/traces/treemap/partition.js
new file mode 100644
index 00000000000..182e368cf28
--- /dev/null
+++ b/src/traces/treemap/partition.js
@@ -0,0 +1,104 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var d3Hierarchy = require('d3-hierarchy');
+
+module.exports = function partition(entry, size, opts) {
+ var flipX = opts.flipX;
+ var flipY = opts.flipY;
+ var swapXY = opts.packing === 'dice-slice';
+
+ var top = opts.pad[flipY ? 'bottom' : 'top'];
+ var left = opts.pad[flipX ? 'right' : 'left'];
+ var right = opts.pad[flipX ? 'left' : 'right'];
+ var bottom = opts.pad[flipY ? 'top' : 'bottom'];
+
+ var tmp;
+ if(swapXY) {
+ tmp = left;
+ left = top;
+ top = tmp;
+
+ tmp = right;
+ right = bottom;
+ bottom = tmp;
+ }
+
+ var result = d3Hierarchy
+ .treemap()
+ .tile(getTilingMethod(opts.packing, opts.squarifyratio))
+ .paddingInner(opts.pad.inner)
+ .paddingLeft(left)
+ .paddingRight(right)
+ .paddingTop(top)
+ .paddingBottom(bottom)
+ .size(
+ swapXY ? [size[1], size[0]] : size
+ )(entry);
+
+ if(swapXY || flipX || flipY) {
+ flipTree(result, size, {
+ swapXY: swapXY,
+ flipX: flipX,
+ flipY: flipY
+ });
+ }
+ return result;
+};
+
+function getTilingMethod(key, squarifyratio) {
+ switch(key) {
+ case 'squarify':
+ return d3Hierarchy.treemapSquarify.ratio(squarifyratio);
+ case 'binary':
+ return d3Hierarchy.treemapBinary;
+ case 'dice':
+ return d3Hierarchy.treemapDice;
+ case 'slice':
+ return d3Hierarchy.treemapSlice;
+ default: // i.e. 'slice-dice' | 'dice-slice'
+ return d3Hierarchy.treemapSliceDice;
+ }
+}
+
+function flipTree(node, size, opts) {
+ var tmp;
+
+ if(opts.swapXY) {
+ // swap x0 and y0
+ tmp = node.x0;
+ node.x0 = node.y0;
+ node.y0 = tmp;
+
+ // swap x1 and y1
+ tmp = node.x1;
+ node.x1 = node.y1;
+ node.y1 = tmp;
+ }
+
+ if(opts.flipX) {
+ tmp = node.x0;
+ node.x0 = size[0] - node.x1;
+ node.x1 = size[0] - tmp;
+ }
+
+ if(opts.flipY) {
+ tmp = node.y0;
+ node.y0 = size[1] - node.y1;
+ node.y1 = size[1] - tmp;
+ }
+
+ var children = node.children;
+ if(children) {
+ for(var i = 0; i < children.length; i++) {
+ flipTree(children[i], size, opts);
+ }
+ }
+}
diff --git a/src/traces/treemap/plot.js b/src/traces/treemap/plot.js
new file mode 100644
index 00000000000..a72f03d6c8c
--- /dev/null
+++ b/src/traces/treemap/plot.js
@@ -0,0 +1,597 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var d3 = require('d3');
+
+var hasTransition = require('../sunburst/helpers').hasTransition;
+var helpers = require('../sunburst/helpers');
+
+var Lib = require('../../lib');
+var TEXTPAD = require('../bar/constants').TEXTPAD;
+var toMoveInsideBar = require('../bar/plot').toMoveInsideBar;
+
+var constants = require('./constants');
+var drawDescendants = require('./draw_descendants');
+var drawAncestors = require('./draw_ancestors');
+
+module.exports = function(gd, cdmodule, transitionOpts, makeOnCompleteCallback) {
+ var fullLayout = gd._fullLayout;
+ var layer = fullLayout._treemaplayer;
+ var join, onComplete;
+
+ // If transition config is provided, then it is only a partial replot and traces not
+ // updated are removed.
+ var isFullReplot = !transitionOpts;
+
+ join = layer.selectAll('g.trace.treemap')
+ .data(cdmodule, function(cd) { return cd[0].trace.uid; });
+
+ join.enter().append('g')
+ .classed('trace', true)
+ .classed('treemap', true);
+
+ join.order();
+
+ if(hasTransition(transitionOpts)) {
+ if(makeOnCompleteCallback) {
+ // If it was passed a callback to register completion, make a callback. If
+ // this is created, then it must be executed on completion, otherwise the
+ // pos-transition redraw will not execute:
+ onComplete = makeOnCompleteCallback();
+ }
+
+ var transition = d3.transition()
+ .duration(transitionOpts.duration)
+ .ease(transitionOpts.easing)
+ .each('end', function() { onComplete && onComplete(); })
+ .each('interrupt', function() { onComplete && onComplete(); });
+
+ transition.each(function() {
+ // Must run the selection again since otherwise enters/updates get grouped together
+ // and these get executed out of order. Except we need them in order!
+ layer.selectAll('g.trace').each(function(cd) {
+ plotOne(gd, cd, this, transitionOpts);
+ });
+ });
+ } else {
+ join.each(function(cd) {
+ plotOne(gd, cd, this, transitionOpts);
+ });
+ }
+
+ if(isFullReplot) {
+ join.exit().remove();
+ }
+};
+
+function getKey(pt) {
+ return helpers.isHierarchyRoot(pt) ?
+ '' : // don't use the dummyId
+ helpers.getPtId(pt);
+}
+
+function plotOne(gd, cd, element, transitionOpts) {
+ var fullLayout = gd._fullLayout;
+ var cd0 = cd[0];
+ var trace = cd0.trace;
+ var hierarchy = cd0.hierarchy;
+ var hasTransition = helpers.hasTransition(transitionOpts);
+ var entry = helpers.findEntryWithLevel(hierarchy, trace.level);
+ var maxDepth = helpers.getMaxDepth(trace);
+ var hasVisibleDepth = function(pt) {
+ return pt.data.depth - entry.data.depth < maxDepth;
+ };
+
+ var gs = fullLayout._size;
+ var domain = trace.domain;
+
+ var vpw = gs.w * (domain.x[1] - domain.x[0]);
+ var vph = gs.h * (domain.y[1] - domain.y[0]);
+ var barW = vpw;
+ var barH = trace.pathbar.thickness;
+ var barPad = trace.marker.line.width + constants.gapWithPathbar;
+ var barDifY = !trace.pathbar.visible ? 0 :
+ trace.pathbar.side.indexOf('bottom') > -1 ? vph + barPad : -(barH + barPad);
+
+ var pathbarOrigin = {
+ x0: barW, // slide to the right
+ x1: barW,
+ y0: barDifY,
+ y1: barDifY + barH
+ };
+
+ var findClosestEdge = function(pt, ref, size) {
+ var e = trace.tiling.pad;
+ var isLeftOfRect = function(x) { return x - e <= ref.x0; };
+ var isRightOfRect = function(x) { return x + e >= ref.x1; };
+ var isBottomOfRect = function(y) { return y - e <= ref.y0; };
+ var isTopOfRect = function(y) { return y + e >= ref.y1; };
+
+ return {
+ x0: isLeftOfRect(pt.x0 - e) ? 0 : isRightOfRect(pt.x0 - e) ? size[0] : pt.x0,
+ x1: isLeftOfRect(pt.x1 + e) ? 0 : isRightOfRect(pt.x1 + e) ? size[0] : pt.x1,
+ y0: isBottomOfRect(pt.y0 - e) ? 0 : isTopOfRect(pt.y0 - e) ? size[1] : pt.y0,
+ y1: isBottomOfRect(pt.y1 + e) ? 0 : isTopOfRect(pt.y1 + e) ? size[1] : pt.y1
+ };
+ };
+
+ // stash of 'previous' position data used by tweening functions
+ var prevEntry = null;
+ var prevLookupPathbar = {};
+ var prevLookupSlices = {};
+ var nextOfPrevEntry = null;
+ var getPrev = function(pt, onPathbar) {
+ return onPathbar ?
+ prevLookupPathbar[getKey(pt)] :
+ prevLookupSlices[getKey(pt)];
+ };
+
+ var getOrigin = function(pt, onPathbar, refRect, size) {
+ if(onPathbar) {
+ return prevLookupPathbar[getKey(hierarchy)] || pathbarOrigin;
+ } else {
+ var ref = prevLookupSlices[trace.level] || refRect;
+
+ if(hasVisibleDepth(pt)) { // case of an empty object - happens when maxdepth is set
+ return findClosestEdge(pt, ref, size);
+ }
+ }
+ return {};
+ };
+
+ var isRoot = helpers.isHierarchyRoot(entry);
+
+ trace._entryDepth = entry.data.depth;
+ if(isRoot) {
+ trace._entryDepth++;
+ }
+
+ // N.B. handle multiple-root special case
+ if(cd0.hasMultipleRoots && isRoot) {
+ maxDepth++;
+ }
+ trace._maxDepth = maxDepth;
+
+ var cenX = -vpw / 2 + gs.l + gs.w * (domain.x[1] + domain.x[0]) / 2;
+ var cenY = -vph / 2 + gs.t + gs.h * (1 - (domain.y[1] + domain.y[0]) / 2);
+
+ var viewMapX = function(x) { return cenX + x; };
+ var viewMapY = function(y) { return cenY + y; };
+
+ var barY0 = viewMapY(0);
+ var barX0 = viewMapX(0);
+
+ var viewBarX = function(x) { return barX0 + x; };
+ var viewBarY = function(y) { return barY0 + y; };
+
+ function pos(x, y) {
+ return x + ',' + y;
+ }
+
+ var xStart = viewBarX(0);
+ var limitX0 = function(p) { p.x = Math.max(xStart, p.x); };
+
+ var edgeshape = trace.pathbar.edgeshape;
+
+ // pathbar(directory) path generation fn
+ var pathAncestor = function(d) {
+ var _x0 = viewBarX(Math.max(Math.min(d.x0, d.x0), 0));
+ var _x1 = viewBarX(Math.min(Math.max(d.x1, d.x1), barW));
+ var _y0 = viewBarY(d.y0);
+ var _y1 = viewBarY(d.y1);
+
+ var halfH = barH / 2;
+
+ var pL = {};
+ var pR = {};
+
+ pL.x = _x0;
+ pR.x = _x1;
+
+ pL.y = pR.y = (_y0 + _y1) / 2;
+
+ var pA = {x: _x0, y: _y0};
+ var pB = {x: _x1, y: _y0};
+ var pC = {x: _x1, y: _y1};
+ var pD = {x: _x0, y: _y1};
+
+ if(edgeshape === '>') {
+ pA.x -= halfH;
+ pB.x -= halfH;
+ pC.x -= halfH;
+ pD.x -= halfH;
+ } else if(edgeshape === '/') {
+ pC.x -= halfH;
+ pD.x -= halfH;
+ pL.x -= halfH / 2;
+ pR.x -= halfH / 2;
+ } else if(edgeshape === '\\') {
+ pA.x -= halfH;
+ pB.x -= halfH;
+ pL.x -= halfH / 2;
+ pR.x -= halfH / 2;
+ } else if(edgeshape === '<') {
+ pL.x -= halfH;
+ pR.x -= halfH;
+ }
+
+ limitX0(pA);
+ limitX0(pD);
+ limitX0(pL);
+
+ limitX0(pB);
+ limitX0(pC);
+ limitX0(pR);
+
+ return (
+ 'M' + pos(pA.x, pA.y) +
+ 'L' + pos(pB.x, pB.y) +
+ 'L' + pos(pR.x, pR.y) +
+ 'L' + pos(pC.x, pC.y) +
+ 'L' + pos(pD.x, pD.y) +
+ 'L' + pos(pL.x, pL.y) +
+ 'Z'
+ );
+ };
+
+ // slice path generation fn
+ var pathDescendant = function(d) {
+ var _x0 = viewMapX(d.x0);
+ var _x1 = viewMapX(d.x1);
+ var _y0 = viewMapY(d.y0);
+ var _y1 = viewMapY(d.y1);
+
+ var dx = _x1 - _x0;
+ var dy = _y1 - _y0;
+ if(!dx || !dy) return '';
+
+ var FILLET = 0; // TODO: may expose this constant
+
+ var r = (
+ dx > 2 * FILLET &&
+ dy > 2 * FILLET
+ ) ? FILLET : 0;
+
+ var arc = function(rx, ry) { return r ? 'a' + pos(r, r) + ' 0 0 1 ' + pos(rx, ry) : ''; };
+
+ return (
+ 'M' + pos(_x0, _y0 + r) +
+ arc(r, -r) +
+ 'L' + pos(_x1 - r, _y0) +
+ arc(r, r) +
+ 'L' + pos(_x1, _y1 - r) +
+ arc(-r, r) +
+ 'L' + pos(_x0 + r, _y1) +
+ arc(-r, -r) + 'Z'
+ );
+ };
+
+ var toMoveInsideSlice = function(x0, x1, y0, y1, textBB, opts) {
+ var hasFlag = function(f) { return trace.textposition.indexOf(f) !== -1; };
+
+ var hasBottom = hasFlag('bottom');
+ var hasTop = hasFlag('top') || (opts.isHeader && !hasBottom);
+
+ var anchor =
+ hasTop ? 'start' :
+ hasBottom ? 'end' : 'middle';
+
+ var hasRight = hasFlag('right');
+ var hasLeft = hasFlag('left') || opts.onPathbar;
+
+ var offsetDir =
+ hasLeft ? 'left' :
+ hasRight ? 'right' : 'center';
+
+ if(opts.onPathbar || !opts.isHeader) {
+ x0 += hasLeft ? TEXTPAD : 0;
+ x1 -= hasRight ? TEXTPAD : 0;
+ }
+
+ var pad = trace.marker.pad;
+ if(opts.isHeader) {
+ x0 += pad.l - TEXTPAD;
+ x1 -= pad.r - TEXTPAD;
+
+ // limit the drawing area for headers
+ var limY;
+ if(hasBottom) {
+ limY = y1 - pad.b;
+ if(y0 < limY && limY < y1) y0 = limY;
+ } else {
+ limY = y0 + pad.t;
+ if(y0 < limY && limY < y1) y1 = limY;
+ }
+ }
+
+ // position the text relative to the slice
+ var transform = toMoveInsideBar(x0, x1, y0, y1, textBB, {
+ isHorizontal: false,
+ constrained: true,
+ angle: 0,
+ anchor: anchor
+ });
+
+ if(offsetDir !== 'center') {
+ var deltaX = (x1 - x0) / 2 - transform.scale * (textBB.right - textBB.left) / 2;
+ if(opts.isHeader) deltaX -= TEXTPAD;
+
+ if(offsetDir === 'left') transform.targetX -= deltaX;
+ else if(offsetDir === 'right') transform.targetX += deltaX;
+ }
+
+ transform.targetX = viewMapX(transform.targetX);
+ transform.targetY = viewMapY(transform.targetY);
+
+ if(isNaN(transform.targetX) || isNaN(transform.targetY)) {
+ return {};
+ }
+
+ return {
+ scale: transform.scale,
+ rotate: transform.rotate,
+ textX: transform.textX,
+ textY: transform.textY,
+ targetX: transform.targetX,
+ targetY: transform.targetY
+ };
+ };
+
+ var interpFromParent = function(pt, onPathbar) {
+ var parentPrev;
+ var i = 0;
+ var Q = pt;
+ while(!parentPrev && i < maxDepth) { // loop to find a parent/grandParent on the previous graph
+ i++;
+ Q = Q.parent;
+ if(Q) {
+ parentPrev = getPrev(Q, onPathbar);
+ } else i = maxDepth;
+ }
+ return parentPrev || {};
+ };
+
+ var makeExitSliceInterpolator = function(pt, onPathbar, refRect, size) {
+ var prev = getPrev(pt, onPathbar);
+ var next;
+
+ if(onPathbar) {
+ next = pathbarOrigin;
+ } else {
+ var entryPrev = getPrev(entry, onPathbar);
+ if(entryPrev) {
+ // 'entryPrev' is here has the previous coordinates of the entry
+ // node, which corresponds to the last "clicked" node when zooming in
+ next = findClosestEdge(pt, entryPrev, size);
+ } else {
+ // this happens when maxdepth is set, when leaves must
+ // be removed and the entry is new (i.e. does not have a 'prev' object)
+ next = {};
+ }
+ }
+
+ return d3.interpolate(prev, next);
+ };
+
+ var makeUpdateSliceInterpolator = function(pt, onPathbar, refRect, size) {
+ var prev0 = getPrev(pt, onPathbar);
+ var prev;
+
+ if(prev0) {
+ // if pt already on graph, this is easy
+ prev = prev0;
+ } else {
+ // for new pts:
+ if(onPathbar) {
+ prev = pathbarOrigin;
+ } else {
+ if(prevEntry) {
+ // if trace was visible before
+ if(pt.parent) {
+ var ref = nextOfPrevEntry || refRect;
+
+ if(ref && !onPathbar) {
+ prev = findClosestEdge(pt, ref, size);
+ } else {
+ // if new leaf (when maxdepth is set),
+ // grow it from its parent node
+ prev = {};
+ Lib.extendFlat(prev, interpFromParent(pt, onPathbar));
+ }
+ } else {
+ prev = pt;
+ }
+ } else {
+ prev = {};
+ }
+ }
+ }
+
+ return d3.interpolate(prev, {
+ x0: pt.x0,
+ x1: pt.x1,
+ y0: pt.y0,
+ y1: pt.y1
+ });
+ };
+
+ var makeUpdateTextInterpolator = function(pt, onPathbar, refRect, size) {
+ var prev0 = getPrev(pt, onPathbar);
+ var prev = {};
+ var origin = getOrigin(pt, onPathbar, refRect, size);
+
+ Lib.extendFlat(prev, {
+ transform: toMoveInsideSlice(
+ origin.x0,
+ origin.x1,
+ origin.y0,
+ origin.y1,
+ pt.textBB,
+ {
+ isHeader: helpers.isHeader(pt, trace)
+ }
+ )
+ });
+
+ if(prev0) {
+ // if pt already on graph, this is easy
+ prev = prev0;
+ } else {
+ // for new pts:
+ if(pt.parent) {
+ Lib.extendFlat(prev, interpFromParent(pt, onPathbar));
+ }
+ }
+
+ return d3.interpolate(prev, {
+ transform: {
+ scale: pt.transform.scale,
+ rotate: pt.transform.rotate,
+ textX: pt.transform.textX,
+ textY: pt.transform.textY,
+ targetX: pt.transform.targetX,
+ targetY: pt.transform.targetY
+ }
+ });
+ };
+
+ var handleSlicesExit = function(slices, onPathbar, refRect, size, pathSlice) {
+ var width = size[0];
+ var height = size[1];
+
+ if(hasTransition) {
+ slices.exit().transition()
+ .each(function() {
+ var sliceTop = d3.select(this);
+
+ var slicePath = sliceTop.select('path.surface');
+ slicePath.transition().attrTween('d', function(pt2) {
+ var interp = makeExitSliceInterpolator(pt2, onPathbar, refRect, [width, height]);
+ return function(t) { return pathSlice(interp(t)); };
+ });
+
+ var sliceTextGroup = sliceTop.select('g.slicetext');
+ sliceTextGroup.attr('opacity', 0);
+ })
+ .remove();
+ } else {
+ slices.exit().remove();
+ }
+ };
+
+ var strTransform = function(d) {
+ return Lib.getTextTransform({
+ textX: d.transform.textX,
+ textY: d.transform.textY,
+ targetX: d.transform.targetX,
+ targetY: d.transform.targetY,
+ scale: d.transform.scale,
+ rotate: d.transform.rotate
+ });
+ };
+
+ var gTrace = d3.select(element);
+ var selAncestors = gTrace.selectAll('g.pathbar');
+ var selDescendants = gTrace.selectAll('g.slice');
+
+ if(!entry) {
+ selAncestors.remove();
+ selDescendants.remove();
+ return;
+ }
+
+ if(hasTransition) {
+ // Important: do this before binding new sliceData!
+
+ selAncestors.each(function(pt) {
+ prevLookupPathbar[getKey(pt)] = {
+ x0: pt.x0,
+ x1: pt.x1,
+ y0: pt.y0,
+ y1: pt.y1
+ };
+
+ if(pt.transform) {
+ prevLookupPathbar[getKey(pt)].transform = {
+ textX: pt.transform.textX,
+ textY: pt.transform.textY,
+ targetX: pt.transform.targetX,
+ targetY: pt.transform.targetY,
+ scale: pt.transform.scale,
+ rotate: pt.transform.rotate
+ };
+ }
+ });
+
+ selDescendants.each(function(pt) {
+ prevLookupSlices[getKey(pt)] = {
+ x0: pt.x0,
+ x1: pt.x1,
+ y0: pt.y0,
+ y1: pt.y1
+ };
+
+ if(pt.transform) {
+ prevLookupSlices[getKey(pt)].transform = {
+ textX: pt.transform.textX,
+ textY: pt.transform.textY,
+ targetX: pt.transform.targetX,
+ targetY: pt.transform.targetY,
+ scale: pt.transform.scale,
+ rotate: pt.transform.rotate
+ };
+ }
+
+ if(!prevEntry && helpers.isEntry(pt)) {
+ prevEntry = pt;
+ }
+ });
+ }
+
+ nextOfPrevEntry = drawDescendants(gd, cd, entry, selDescendants, {
+ width: vpw,
+ height: vph,
+
+ viewX: viewMapX,
+ viewY: viewMapY,
+
+ pathSlice: pathDescendant,
+ toMoveInsideSlice: toMoveInsideSlice,
+
+ prevEntry: prevEntry,
+ makeUpdateSliceInterpolator: makeUpdateSliceInterpolator,
+ makeUpdateTextInterpolator: makeUpdateTextInterpolator,
+
+ handleSlicesExit: handleSlicesExit,
+ hasTransition: hasTransition,
+ strTransform: strTransform
+ });
+
+ if(trace.pathbar.visible) {
+ drawAncestors(gd, cd, entry, selAncestors, {
+ barDifY: barDifY,
+ width: barW,
+ height: barH,
+
+ viewX: viewBarX,
+ viewY: viewBarY,
+
+ pathSlice: pathAncestor,
+ toMoveInsideSlice: toMoveInsideSlice,
+
+ makeUpdateSliceInterpolator: makeUpdateSliceInterpolator,
+ makeUpdateTextInterpolator: makeUpdateTextInterpolator,
+
+ handleSlicesExit: handleSlicesExit,
+ hasTransition: hasTransition,
+ strTransform: strTransform
+ });
+ }
+}
diff --git a/src/traces/treemap/style.js b/src/traces/treemap/style.js
new file mode 100644
index 00000000000..cecc78c3c93
--- /dev/null
+++ b/src/traces/treemap/style.js
@@ -0,0 +1,76 @@
+/**
+* Copyright 2012-2019, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var d3 = require('d3');
+var Color = require('../../components/color');
+var Lib = require('../../lib');
+var helpers = require('../sunburst/helpers');
+
+function style(gd) {
+ gd._fullLayout._treemaplayer.selectAll('.trace').each(function(cd) {
+ var gTrace = d3.select(this);
+ var cd0 = cd[0];
+ var trace = cd0.trace;
+
+ gTrace.style('opacity', trace.opacity);
+
+ gTrace.selectAll('path.surface').each(function(pt) {
+ d3.select(this).call(styleOne, pt, trace, {
+ hovered: false
+ });
+ });
+ });
+}
+
+function styleOne(s, pt, trace, opts) {
+ var hovered = (opts || {}).hovered;
+ var cdi = pt.data.data;
+ var ptNumber = cdi.i;
+ var lineColor;
+ var lineWidth;
+ var opacity;
+
+ var depthFade = function(n) {
+ var base = trace.marker.opacitybase;
+ var step = trace.marker.opacitystep;
+
+ return n === 0 ? base :
+ Math.max(0, Math.min(1, n * step));
+ };
+
+ if(hovered) {
+ lineColor = trace._hovered.marker.line.color;
+ lineWidth = trace._hovered.marker.line.width;
+ opacity = trace._hovered.marker.opacity;
+ } else {
+ if(helpers.isHierarchyRoot(pt)) {
+ lineColor = 'rgba(0,0,0,0)';
+ lineWidth = 0;
+ } else {
+ lineColor = Lib.castOption(trace, ptNumber, 'marker.line.color') || Color.defaultLine;
+ lineWidth = Lib.castOption(trace, ptNumber, 'marker.line.width') || 0;
+ }
+
+ opacity =
+ trace._hasColorscale || helpers.isLeaf(pt) ? 1 :
+ pt.onPathbar ? trace.pathbar.opacity :
+ depthFade(pt.data.depth - trace._entryDepth);
+ }
+
+ s.style('stroke-width', lineWidth)
+ .call(Color.fill, cdi.color)
+ .call(Color.stroke, lineColor)
+ .style('opacity', opacity);
+}
+
+module.exports = {
+ style: style,
+ styleOne: styleOne
+};
diff --git a/tasks/test_syntax.js b/tasks/test_syntax.js
index e3018d5460b..3e77d91e242 100644
--- a/tasks/test_syntax.js
+++ b/tasks/test_syntax.js
@@ -140,12 +140,15 @@ function assertSrcContents() {
} else if(IE_BLACK_LIST.indexOf(lastPart) !== -1) {
logs.push(file + ' : contains .' + lastPart + ' (IE failure)');
} else if(IE_SVG_BLACK_LIST.indexOf(lastPart) !== -1) {
- // add special case for sunburst where we use 'children'
+ // add special case for sunburst and treemap where we use 'children'
// off the d3-hierarchy output
var dirParts = path.dirname(file).split(path.sep);
- var isSunburstFile = dirParts[dirParts.length - 1] === 'sunburst';
- var isLinkedToObject = ['pt', 'd', 'parent'].indexOf(parts[parts.length - 2]) !== -1;
- if(!(isSunburstFile && isLinkedToObject)) {
+ var filename = dirParts[dirParts.length - 1];
+ var isSunburstOrTreemap =
+ filename === 'sunburst' ||
+ filename === 'treemap';
+ var isLinkedToObject = ['pt', 'd', 'parent', 'node'].indexOf(parts[parts.length - 2]) !== -1;
+ if(!(isSunburstOrTreemap && isLinkedToObject)) {
logs.push(file + ' : contains .' + lastPart + ' (IE failure in SVG)');
}
} else if(FF_BLACK_LIST.indexOf(lastPart) !== -1) {
diff --git a/test/image/baselines/sunburst_count_branches.png b/test/image/baselines/sunburst_count_branches.png
new file mode 100644
index 00000000000..a15651fe553
Binary files /dev/null and b/test/image/baselines/sunburst_count_branches.png differ
diff --git a/test/image/baselines/sunburst_first.png b/test/image/baselines/sunburst_first.png
index b1ba840f2e8..9e2c4013b62 100644
Binary files a/test/image/baselines/sunburst_first.png and b/test/image/baselines/sunburst_first.png differ
diff --git a/test/image/baselines/sunburst_packages_colorscale_novalue.png b/test/image/baselines/sunburst_packages_colorscale_novalue.png
new file mode 100644
index 00000000000..02e7bcbbbc7
Binary files /dev/null and b/test/image/baselines/sunburst_packages_colorscale_novalue.png differ
diff --git a/test/image/baselines/sunburst_values.png b/test/image/baselines/sunburst_values.png
index e3d36289c66..8e7f8b6f8da 100644
Binary files a/test/image/baselines/sunburst_values.png and b/test/image/baselines/sunburst_values.png differ
diff --git a/test/image/baselines/sunburst_values_colorscale.png b/test/image/baselines/sunburst_values_colorscale.png
new file mode 100644
index 00000000000..94d1e7e69e4
Binary files /dev/null and b/test/image/baselines/sunburst_values_colorscale.png differ
diff --git a/test/image/baselines/sunburst_with-without_values.png b/test/image/baselines/sunburst_with-without_values.png
new file mode 100644
index 00000000000..80263d41d03
Binary files /dev/null and b/test/image/baselines/sunburst_with-without_values.png differ
diff --git a/test/image/baselines/treemap_coffee-maxdepth3.png b/test/image/baselines/treemap_coffee-maxdepth3.png
new file mode 100644
index 00000000000..7a3c014df04
Binary files /dev/null and b/test/image/baselines/treemap_coffee-maxdepth3.png differ
diff --git a/test/image/baselines/treemap_coffee.png b/test/image/baselines/treemap_coffee.png
new file mode 100644
index 00000000000..f6c02683aca
Binary files /dev/null and b/test/image/baselines/treemap_coffee.png differ
diff --git a/test/image/baselines/treemap_first.png b/test/image/baselines/treemap_first.png
new file mode 100644
index 00000000000..e1a0a76d836
Binary files /dev/null and b/test/image/baselines/treemap_first.png differ
diff --git a/test/image/baselines/treemap_flare.png b/test/image/baselines/treemap_flare.png
new file mode 100644
index 00000000000..6522ebaf127
Binary files /dev/null and b/test/image/baselines/treemap_flare.png differ
diff --git a/test/image/baselines/treemap_level-depth.png b/test/image/baselines/treemap_level-depth.png
new file mode 100644
index 00000000000..a254f815c17
Binary files /dev/null and b/test/image/baselines/treemap_level-depth.png differ
diff --git a/test/image/baselines/treemap_packages_colorscale_allone.png b/test/image/baselines/treemap_packages_colorscale_allone.png
new file mode 100644
index 00000000000..46daa70c4ea
Binary files /dev/null and b/test/image/baselines/treemap_packages_colorscale_allone.png differ
diff --git a/test/image/baselines/treemap_packages_colorscale_novalue.png b/test/image/baselines/treemap_packages_colorscale_novalue.png
new file mode 100644
index 00000000000..28261b046df
Binary files /dev/null and b/test/image/baselines/treemap_packages_colorscale_novalue.png differ
diff --git a/test/image/baselines/treemap_packings.png b/test/image/baselines/treemap_packings.png
new file mode 100644
index 00000000000..4664a239293
Binary files /dev/null and b/test/image/baselines/treemap_packings.png differ
diff --git a/test/image/baselines/treemap_pad_mirror.png b/test/image/baselines/treemap_pad_mirror.png
new file mode 100644
index 00000000000..e7ff59336e6
Binary files /dev/null and b/test/image/baselines/treemap_pad_mirror.png differ
diff --git a/test/image/baselines/treemap_pad_transpose.png b/test/image/baselines/treemap_pad_transpose.png
new file mode 100644
index 00000000000..b07265c6bc9
Binary files /dev/null and b/test/image/baselines/treemap_pad_transpose.png differ
diff --git a/test/image/baselines/treemap_textfit.png b/test/image/baselines/treemap_textfit.png
new file mode 100644
index 00000000000..a56cbff85f3
Binary files /dev/null and b/test/image/baselines/treemap_textfit.png differ
diff --git a/test/image/baselines/treemap_textposition.png b/test/image/baselines/treemap_textposition.png
new file mode 100644
index 00000000000..7ecbc57290f
Binary files /dev/null and b/test/image/baselines/treemap_textposition.png differ
diff --git a/test/image/baselines/treemap_transpose_nopad.png b/test/image/baselines/treemap_transpose_nopad.png
new file mode 100644
index 00000000000..43b9ef13a96
Binary files /dev/null and b/test/image/baselines/treemap_transpose_nopad.png differ
diff --git a/test/image/baselines/treemap_values.png b/test/image/baselines/treemap_values.png
new file mode 100644
index 00000000000..00dd12be445
Binary files /dev/null and b/test/image/baselines/treemap_values.png differ
diff --git a/test/image/baselines/treemap_values_colorscale.png b/test/image/baselines/treemap_values_colorscale.png
new file mode 100644
index 00000000000..f89fb5b7fef
Binary files /dev/null and b/test/image/baselines/treemap_values_colorscale.png differ
diff --git a/test/image/baselines/treemap_with-without_values.png b/test/image/baselines/treemap_with-without_values.png
new file mode 100644
index 00000000000..2aaeeba3c73
Binary files /dev/null and b/test/image/baselines/treemap_with-without_values.png differ
diff --git a/test/image/baselines/treemap_with-without_values_template.png b/test/image/baselines/treemap_with-without_values_template.png
new file mode 100644
index 00000000000..e0552a8717a
Binary files /dev/null and b/test/image/baselines/treemap_with-without_values_template.png differ
diff --git a/test/image/mocks/sunburst_count_branches.json b/test/image/mocks/sunburst_count_branches.json
new file mode 100644
index 00000000000..f4566094ae3
--- /dev/null
+++ b/test/image/mocks/sunburst_count_branches.json
@@ -0,0 +1,78 @@
+{
+ "data": [
+ {
+ "type": "sunburst",
+ "count": "leaves+branches",
+ "textinfo": "label+percent parent",
+ "marker": {
+ "line": {
+ "color": "#777"
+ },
+ "colorscale": "Blackbody",
+ "reversescale": true,
+ "showscale": true
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ]
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 800
+ }
+}
diff --git a/test/image/mocks/sunburst_first.json b/test/image/mocks/sunburst_first.json
index 3e5d343c1e0..984de005d9c 100644
--- a/test/image/mocks/sunburst_first.json
+++ b/test/image/mocks/sunburst_first.json
@@ -2,18 +2,37 @@
"data": [
{
"type": "sunburst",
+ "textinfo": "label+percent parent",
"labels": ["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
"parents": ["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
"domain": {"x": [0, 0.5]}
},
{
"type": "sunburst",
+ "textinfo": "label+percent root",
"labels": ["Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
"parents": ["Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
"domain": {"x": [0.5, 1]}
}
],
"layout": {
-
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "percent parent",
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "percent root",
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ }
+ ]
}
}
diff --git a/test/image/mocks/sunburst_flare.json b/test/image/mocks/sunburst_flare.json
index a1429b53aa3..5c396c75132 100644
--- a/test/image/mocks/sunburst_flare.json
+++ b/test/image/mocks/sunburst_flare.json
@@ -2,6 +2,7 @@
"data": [
{
"type": "sunburst",
+ "hoverinfo": "all",
"ids": [
"flare",
"flare-analytics",
diff --git a/test/image/mocks/sunburst_packages_colorscale_novalue.json b/test/image/mocks/sunburst_packages_colorscale_novalue.json
new file mode 100644
index 00000000000..f80181f786f
--- /dev/null
+++ b/test/image/mocks/sunburst_packages_colorscale_novalue.json
@@ -0,0 +1,2328 @@
+{
+ "data": [
+ {
+ "type": "sunburst",
+ "maxdepth": 2,
+ "textinfo": "label+percent parent",
+ "marker": {
+ "line": {
+ "color": "#777"
+ },
+ "colorscale": [[0, "#FFF"], [0.01, "#FF0"], [0.1, "#F00"], [1, "#000"]],
+ "showscale": true
+ },
+ "ids": [
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. array-range",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. country-regex",
+ "1. d3",
+ "1. d3-force",
+ "1. d3-hierarchy",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. es6-promise",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-mat4",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-spikes2d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-event-offset",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. polybooljs",
+ "1. regl",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. right-now",
+ "1. robust-orientation",
+ "1. sane-topojson",
+ "1. strongly-connected-components",
+ "1. superscript-text",
+ "1. svg-path-sdf",
+ "1. tinycolor2",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. 3d-view",
+ "2. @mapbox/gl-matrix",
+ "2. @mapbox/jsonlint-lines-primitives",
+ "2. @mapbox/mapbox-gl-supported",
+ "2. @mapbox/point-geometry",
+ "2. @mapbox/shelf-pack",
+ "2. @mapbox/tiny-sdf",
+ "2. @mapbox/unitbezier",
+ "2. @mapbox/vector-tile",
+ "2. @mapbox/whoots-js",
+ "2. a-big-triangle",
+ "2. affine-hull",
+ "2. alpha-complex",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. array-range",
+ "2. array-rearrange",
+ "2. barycentric",
+ "2. binary-search-bounds",
+ "2. bit-twiddle",
+ "2. bitmap-sdf",
+ "2. bl",
+ "2. brfs",
+ "2. bubleify",
+ "2. cdt2d",
+ "2. clamp",
+ "2. clean-pslg",
+ "2. color-alpha",
+ "2. color-id",
+ "2. color-normalize",
+ "2. color-rgba",
+ "2. colormap",
+ "2. commander",
+ "2. concat-stream",
+ "2. css-font",
+ "2. csscolorparser",
+ "2. cwise",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-color",
+ "2. d3-dispatch",
+ "2. d3-quadtree",
+ "2. d3-shape",
+ "2. d3-timer",
+ "2. defined",
+ "2. detect-kerning",
+ "2. draw-svg-path",
+ "2. dtype",
+ "2. dup",
+ "2. duplexify",
+ "2. earcut",
+ "2. element-size",
+ "2. elementary-circuits-directed-graph",
+ "2. es6-weak-map",
+ "2. falafel",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. from2",
+ "2. geojson-rewind",
+ "2. geojson-vt",
+ "2. get-canvas-context",
+ "2. gl-axes3d",
+ "2. gl-buffer",
+ "2. gl-fbo",
+ "2. gl-mat4",
+ "2. gl-matrix-invert",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. gl-spikes3d",
+ "2. gl-texture2d",
+ "2. gl-util",
+ "2. gl-vao",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glsl-read-float",
+ "2. glsl-resolve",
+ "2. glsl-specular-beckmann",
+ "2. glsl-specular-cook-torrance",
+ "2. glsl-token-whitespace-trim",
+ "2. glslify",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. gray-matter",
+ "2. grid-index",
+ "2. has-passive-events",
+ "2. image-palette",
+ "2. incremental-convex-hull",
+ "2. iota-array",
+ "2. is-browser",
+ "2. is-buffer",
+ "2. is-iexplorer",
+ "2. is-mobile",
+ "2. is-obj",
+ "2. is-plain-obj",
+ "2. is-string-blank",
+ "2. is-svg-path",
+ "2. left-pad",
+ "2. mat4-interpolate",
+ "2. math-log2",
+ "2. minimist",
+ "2. monotone-convex-hull-2d",
+ "2. mouse-change",
+ "2. mouse-event",
+ "2. mouse-event-offset",
+ "2. mouse-wheel",
+ "2. ndarray",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. ndarray-warp",
+ "2. normals",
+ "2. object-assign",
+ "2. optical-properties",
+ "2. parse-rect",
+ "2. parse-svg-path",
+ "2. parse-unit",
+ "2. pbf",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. polytope-closest-point",
+ "2. quickselect",
+ "2. raf",
+ "2. regl",
+ "2. regl-scatter2d",
+ "2. resolve",
+ "2. right-now",
+ "2. robust-scale",
+ "2. robust-subtract",
+ "2. robust-sum",
+ "2. rw",
+ "2. shuffle-seed",
+ "2. signum",
+ "2. simplicial-complex-boundary",
+ "2. simplicial-complex-contour",
+ "2. sort-object",
+ "2. stack-trace",
+ "2. static-eval",
+ "2. supercluster",
+ "2. surface-nets",
+ "2. svg-path-bounds",
+ "2. text-cache",
+ "2. through2",
+ "2. tiny-sdf",
+ "2. tinyqueue",
+ "2. to-float32",
+ "2. to-px",
+ "2. two-product",
+ "2. typedarray-pool",
+ "2. uniq",
+ "2. update-diff",
+ "2. vectorize-text",
+ "2. vt-pbf",
+ "2. xtend",
+ "3. @choojs/findup",
+ "3. @mapbox/geojson-area",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/vector-tile",
+ "3. abs-svg-path",
+ "3. acorn",
+ "3. array-bounds",
+ "3. big-rat",
+ "3. binary-search-bounds",
+ "3. bit-twiddle",
+ "3. boundary-cells",
+ "3. box-intersect",
+ "3. buble",
+ "3. buffer-from",
+ "3. cdt2d",
+ "3. circumradius",
+ "3. clamp",
+ "3. clean-pslg",
+ "3. color-id",
+ "3. color-parse",
+ "3. color-space",
+ "3. concat-stream",
+ "3. css-font",
+ "3. css-font-size-keywords",
+ "3. css-font-stretch-keywords",
+ "3. css-font-style-keywords",
+ "3. css-font-weight-keywords",
+ "3. css-global-keywords",
+ "3. css-system-font-keywords",
+ "3. cwise",
+ "3. cwise-compiler",
+ "3. cwise-parser",
+ "3. d",
+ "3. d3-path",
+ "3. delaunay-triangulate",
+ "3. dtype",
+ "3. dup",
+ "3. end-of-stream",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. es6-weak-map",
+ "3. escodegen",
+ "3. events",
+ "3. extend-shallow",
+ "3. extract-frustum-planes",
+ "3. foreach",
+ "3. gl-buffer",
+ "3. gl-fbo",
+ "3. gl-format-compiler-error",
+ "3. gl-mat2",
+ "3. gl-mat3",
+ "3. gl-mat4",
+ "3. gl-shader",
+ "3. gl-state",
+ "3. gl-texture2d",
+ "3. gl-vao",
+ "3. gl-vec3",
+ "3. gl-vec4",
+ "3. glsl-inject-defines",
+ "3. glsl-resolve",
+ "3. glsl-specular-beckmann",
+ "3. glsl-token-defines",
+ "3. glsl-token-depth",
+ "3. glsl-token-descope",
+ "3. glsl-token-scope",
+ "3. glsl-token-string",
+ "3. glsl-token-whitespace-trim",
+ "3. glsl-tokenizer",
+ "3. glslify",
+ "3. graceful-fs",
+ "3. ieee754",
+ "3. inherits",
+ "3. is-browser",
+ "3. is-firefox",
+ "3. is-plain-obj",
+ "3. is-svg-path",
+ "3. isarray",
+ "3. js-yaml",
+ "3. kdbush",
+ "3. kind-of",
+ "3. lerp",
+ "3. map-limit",
+ "3. marching-simplex-table",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. matrix-camera-controller",
+ "3. minimist",
+ "3. murmurhash-js",
+ "3. ndarray",
+ "3. ndarray-extract-contour",
+ "3. ndarray-linear-interpolate",
+ "3. ndarray-ops",
+ "3. ndarray-sort",
+ "3. nextafter",
+ "3. normalize-svg-path",
+ "3. number-is-integer",
+ "3. numeric",
+ "3. object-assign",
+ "3. object-keys",
+ "3. orbit-camera-controller",
+ "3. parse-svg-path",
+ "3. parse-unit",
+ "3. path-parse",
+ "3. pbf",
+ "3. performance-now",
+ "3. pick-by-alias",
+ "3. planar-graph-to-polyline",
+ "3. pxls",
+ "3. quantize",
+ "3. quat-slerp",
+ "3. quote-stream",
+ "3. rat-vec",
+ "3. readable-stream",
+ "3. reduce-simplicial-complex",
+ "3. resolve",
+ "3. resolve-protobuf-schema",
+ "3. robust-in-sphere",
+ "3. robust-linear-solve",
+ "3. robust-orientation",
+ "3. robust-segment-intersect",
+ "3. safe-buffer",
+ "3. seedrandom",
+ "3. shallow-copy",
+ "3. sharkdown",
+ "3. simplicial-complex",
+ "3. simplify-planar-graph",
+ "3. sort-asc",
+ "3. sort-desc",
+ "3. split-polygon",
+ "3. static-module",
+ "3. stream-shift",
+ "3. string-split-by",
+ "3. strip-bom-string",
+ "3. strongly-connected-components",
+ "3. surface-nets",
+ "3. through2",
+ "3. triangulate-hypercube",
+ "3. triangulate-polyline",
+ "3. turntable-camera-controller",
+ "3. two-product",
+ "3. two-sum",
+ "3. typedarray",
+ "3. typedarray-pool",
+ "3. uglify-js",
+ "3. union-find",
+ "3. uniq",
+ "3. unquote",
+ "3. vectorize-text",
+ "3. weak-map",
+ "3. weakmap-shim",
+ "3. xtend",
+ "3. zero-crossings",
+ "4. acorn",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. add-line-numbers",
+ "4. argparse",
+ "4. arr-flatten",
+ "4. big-rat",
+ "4. bit-twiddle",
+ "4. bn.js",
+ "4. buffer-equal",
+ "4. cardinal",
+ "4. cdt2d",
+ "4. cell-orientation",
+ "4. chalk",
+ "4. circumcenter",
+ "4. color-name",
+ "4. commander",
+ "4. compare-cell",
+ "4. compare-oriented-cell",
+ "4. compute-dims",
+ "4. concat-stream",
+ "4. convert-source-map",
+ "4. convex-hull",
+ "4. core-util-is",
+ "4. cwise-compiler",
+ "4. d",
+ "4. defined",
+ "4. double-bits",
+ "4. duplexer2",
+ "4. edges-to-adjacency-list",
+ "4. es5-ext",
+ "4. es6-iterator",
+ "4. es6-symbol",
+ "4. escodegen",
+ "4. esprima",
+ "4. estraverse",
+ "4. esutils",
+ "4. expect.js",
+ "4. falafel",
+ "4. filtered-vector",
+ "4. flip-pixels",
+ "4. gamma",
+ "4. gl-constants",
+ "4. gl-mat4",
+ "4. gl-quat",
+ "4. gl-vec3",
+ "4. glsl-shader-name",
+ "4. glsl-token-assignments",
+ "4. glsl-token-depth",
+ "4. glsl-token-inject-block",
+ "4. glsl-token-properties",
+ "4. glsl-token-scope",
+ "4. glsl-token-string",
+ "4. glsl-tokenizer",
+ "4. has",
+ "4. hsluv",
+ "4. inherits",
+ "4. is-browser",
+ "4. is-buffer",
+ "4. is-extendable",
+ "4. is-finite",
+ "4. is-plain-obj",
+ "4. isarray",
+ "4. magic-string",
+ "4. merge-source-map",
+ "4. minimist",
+ "4. mumath",
+ "4. next-tick",
+ "4. object-inspect",
+ "4. once",
+ "4. optionator",
+ "4. os-homedir",
+ "4. parenthesis",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. process-nextick-args",
+ "4. protocol-buffers-schema",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. robust-determinant",
+ "4. robust-dot-product",
+ "4. robust-orientation",
+ "4. robust-scale",
+ "4. robust-subtract",
+ "4. robust-sum",
+ "4. safe-buffer",
+ "4. shallow-copy",
+ "4. simplicial-complex",
+ "4. source-map",
+ "4. split",
+ "4. sprintf-js",
+ "4. static-eval",
+ "4. stream-spigot",
+ "4. string_decoder",
+ "4. svg-arc-to-cubic-bezier",
+ "4. tape",
+ "4. through",
+ "4. through2",
+ "4. to-uint8",
+ "4. two-product",
+ "4. typedarray-pool",
+ "4. uglify-to-browserify",
+ "4. union-find",
+ "4. uniq",
+ "4. util-deprecate",
+ "4. vlq",
+ "4. wgs84",
+ "4. yargs",
+ "5. acorn",
+ "5. almost-equal",
+ "5. ansi-styles",
+ "5. ansicolors",
+ "5. arr-flatten",
+ "5. atob-lite",
+ "5. binary-search-bounds",
+ "5. bit-twiddle",
+ "5. camelcase",
+ "5. cell-orientation",
+ "5. clamp",
+ "5. cliui",
+ "5. compare-angle",
+ "5. compare-cell",
+ "5. core-util-is",
+ "5. cubic-hermite",
+ "5. decamelize",
+ "5. deep-equal",
+ "5. deep-is",
+ "5. defined",
+ "5. dup",
+ "5. escape-string-regexp",
+ "5. escodegen",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. fast-levenshtein",
+ "5. for-each",
+ "5. function-bind",
+ "5. gl-mat3",
+ "5. gl-vec3",
+ "5. gl-vec4",
+ "5. glob",
+ "5. glsl-tokenizer",
+ "5. has",
+ "5. inherits",
+ "5. interval-tree-1d",
+ "5. invert-permutation",
+ "5. is-base64",
+ "5. is-float-array",
+ "5. isarray",
+ "5. levn",
+ "5. minimist",
+ "5. number-is-nan",
+ "5. object-inspect",
+ "5. optionator",
+ "5. pad-left",
+ "5. prelude-ls",
+ "5. readable-stream",
+ "5. redeyed",
+ "5. resolve",
+ "5. resumer",
+ "5. robust-compress",
+ "5. robust-linear-solve",
+ "5. robust-orientation",
+ "5. robust-scale",
+ "5. robust-sum",
+ "5. safe-buffer",
+ "5. slab-decomposition",
+ "5. source-map",
+ "5. sprintf-js",
+ "5. string.prototype.trim",
+ "5. string_decoder",
+ "5. supports-color",
+ "5. through",
+ "5. through2",
+ "5. to-array-buffer",
+ "5. two-product",
+ "5. type-check",
+ "5. typedarray-pool",
+ "5. union-find",
+ "5. uniq",
+ "5. utils-copy",
+ "5. validate.io-array",
+ "5. validate.io-matrix-like",
+ "5. validate.io-ndarray-like",
+ "5. validate.io-positive-integer",
+ "5. vlq",
+ "5. window-size",
+ "5. wordwrap",
+ "5. wrappy",
+ "5. xtend",
+ "6. amdefine",
+ "6. binary-search-bounds",
+ "6. center-align",
+ "6. color-convert",
+ "6. const-pinf-float64",
+ "6. core-util-is",
+ "6. define-properties",
+ "6. es-abstract",
+ "6. esprima",
+ "6. estraverse",
+ "6. flatten-vertex-data",
+ "6. fs.realpath",
+ "6. function-bind",
+ "6. functional-red-black-tree",
+ "6. has-flag",
+ "6. inflight",
+ "6. inherits",
+ "6. is-blob",
+ "6. is-callable",
+ "6. isarray",
+ "6. minimatch",
+ "6. object-keys",
+ "6. once",
+ "6. path-is-absolute",
+ "6. prelude-ls",
+ "6. readable-stream",
+ "6. repeat-string",
+ "6. right-align",
+ "6. robust-orientation",
+ "6. robust-product",
+ "6. robust-sum",
+ "6. signum",
+ "6. source-map",
+ "6. string-to-arraybuffer",
+ "6. string_decoder",
+ "6. through",
+ "6. two-sum",
+ "6. type-check",
+ "6. type-name",
+ "6. utils-copy-error",
+ "6. utils-indexof",
+ "6. utils-regex-from-string",
+ "6. validate.io-array",
+ "6. validate.io-buffer",
+ "6. validate.io-integer",
+ "6. validate.io-nonnegative-integer",
+ "6. wordwrap",
+ "6. xtend"
+ ],
+ "parents": [
+ "",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. @plotly/d3-sankey",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. alpha-shape",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. color-normalize",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. convex-hull",
+ "1. convex-hull",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. delaunay-triangulate",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-select-box",
+ "1. gl-select-box",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-wheel",
+ "1. mouse-wheel",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. d3-shape",
+ "2. alpha-complex",
+ "2. alpha-complex",
+ "2. simplicial-complex-boundary",
+ "2. simplicial-complex-boundary",
+ "2. color-rgba",
+ "2. color-rgba",
+ "2. color-rgba",
+ "2. affine-hull",
+ "2. incremental-convex-hull",
+ "2. incremental-convex-hull",
+ "2. monotone-convex-hull-2d",
+ "2. elementary-circuits-directed-graph",
+ "2. gl-shader",
+ "2. gl-shader",
+ "2. cdt2d",
+ "2. cdt2d",
+ "2. cdt2d",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. gl-buffer",
+ "2. gl-buffer",
+ "2. gl-buffer",
+ "2. surface-nets",
+ "2. surface-nets",
+ "2. surface-nets",
+ "2. typedarray-pool",
+ "2. typedarray-pool",
+ "2. gl-texture2d",
+ "2. gl-texture2d",
+ "2. gl-texture2d",
+ "2. barycentric",
+ "2. colormap",
+ "2. glsl-specular-cook-torrance",
+ "2. polytope-closest-point",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. text-cache",
+ "2. 3d-view",
+ "2. 3d-view",
+ "2. 3d-view",
+ "2. a-big-triangle",
+ "2. a-big-triangle",
+ "2. a-big-triangle",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-fbo",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. ndarray-gradient",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. ndarray-scratch",
+ "2. ndarray-scratch",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. parse-rect",
+ "2. to-px",
+ "2. bl",
+ "2. bl",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. duplexify",
+ "2. duplexify",
+ "2. duplexify",
+ "2. duplexify",
+ "2. falafel",
+ "2. falafel",
+ "2. falafel",
+ "2. falafel",
+ "2. from2",
+ "2. from2",
+ "2. glsl-resolve",
+ "2. glsl-resolve",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. through2",
+ "2. through2",
+ "2. resolve",
+ "2. static-eval",
+ "2. @mapbox/vector-tile",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. brfs",
+ "2. brfs",
+ "2. brfs",
+ "2. brfs",
+ "2. through2",
+ "2. through2",
+ "2. pbf",
+ "2. pbf",
+ "2. shuffle-seed",
+ "2. sort-object",
+ "2. sort-object",
+ "2. supercluster",
+ "2. vt-pbf",
+ "2. vt-pbf",
+ "2. vt-pbf",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. cwise",
+ "2. cwise",
+ "2. cwise",
+ "2. cwise",
+ "2. gl-matrix-invert",
+ "2. gl-matrix-invert",
+ "2. gl-matrix-invert",
+ "2. ndarray-warp",
+ "2. ndarray-warp",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. color-id",
+ "2. image-palette",
+ "2. image-palette",
+ "2. image-palette",
+ "2. color-alpha",
+ "2. raf",
+ "2. robust-scale",
+ "2. robust-scale",
+ "2. bitmap-sdf",
+ "2. draw-svg-path",
+ "2. draw-svg-path",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "3. circumradius",
+ "3. boundary-cells",
+ "3. reduce-simplicial-complex",
+ "3. reduce-simplicial-complex",
+ "3. reduce-simplicial-complex",
+ "3. color-parse",
+ "3. color-parse",
+ "3. color-parse",
+ "3. color-space",
+ "3. color-space",
+ "3. simplicial-complex",
+ "3. simplicial-complex",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. big-rat",
+ "3. big-rat",
+ "3. big-rat",
+ "3. box-intersect",
+ "3. box-intersect",
+ "3. nextafter",
+ "3. rat-vec",
+ "3. robust-segment-intersect",
+ "3. ndarray-extract-contour",
+ "3. triangulate-hypercube",
+ "3. triangulate-hypercube",
+ "3. triangulate-hypercube",
+ "3. zero-crossings",
+ "3. robust-linear-solve",
+ "3. marching-simplex-table",
+ "3. ndarray-sort",
+ "3. orbit-camera-controller",
+ "3. orbit-camera-controller",
+ "3. turntable-camera-controller",
+ "3. turntable-camera-controller",
+ "3. turntable-camera-controller",
+ "3. gl-state",
+ "3. split-polygon",
+ "3. split-polygon",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. simplify-planar-graph",
+ "3. simplify-planar-graph",
+ "3. triangulate-polyline",
+ "3. cwise-compiler",
+ "3. string-split-by",
+ "3. d",
+ "3. es5-ext",
+ "3. es5-ext",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-iterator",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. es6-symbol",
+ "3. number-is-integer",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. end-of-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. glsl-inject-defines",
+ "3. glsl-inject-defines",
+ "3. glsl-inject-defines",
+ "3. glsl-token-defines",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-tokenizer",
+ "3. @choojs/findup",
+ "3. map-limit",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. @mapbox/geojson-area",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. extend-shallow",
+ "3. js-yaml",
+ "3. js-yaml",
+ "3. quote-stream",
+ "3. quote-stream",
+ "3. quote-stream",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. resolve-protobuf-schema",
+ "3. mat4-decompose",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. quat-slerp",
+ "3. cwise-parser",
+ "3. cwise-parser",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. uglify-js",
+ "3. uglify-js",
+ "3. uglify-js",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. normalize-svg-path",
+ "4. circumcenter",
+ "4. circumcenter",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. compare-oriented-cell",
+ "4. compare-oriented-cell",
+ "4. mumath",
+ "4. add-line-numbers",
+ "4. glsl-shader-name",
+ "4. glsl-shader-name",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. permutation-rank",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. filtered-vector",
+ "4. filtered-vector",
+ "4. robust-dot-product",
+ "4. robust-dot-product",
+ "4. edges-to-adjacency-list",
+ "4. planar-dual",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. simplicial-complex",
+ "4. simplicial-complex",
+ "4. is-finite",
+ "4. string_decoder",
+ "4. string_decoder",
+ "4. string_decoder",
+ "4. once",
+ "4. string_decoder",
+ "4. through2",
+ "4. through2",
+ "4. once",
+ "4. string_decoder",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. cardinal",
+ "4. cardinal",
+ "4. split",
+ "4. stream-spigot",
+ "4. argparse",
+ "4. has",
+ "4. magic-string",
+ "4. duplexer2",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. merge-source-map",
+ "4. string_decoder",
+ "4. gl-quat",
+ "4. gl-quat",
+ "4. gl-quat",
+ "4. duplexer2",
+ "4. quote-stream",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. static-eval",
+ "4. through2",
+ "4. through2",
+ "4. yargs",
+ "4. yargs",
+ "4. yargs",
+ "4. yargs",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. chalk",
+ "4. chalk",
+ "4. chalk",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8"
+ ],
+ "labels": [
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. array-range",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. country-regex",
+ "1. d3",
+ "1. d3-force",
+ "1. d3-hierarchy",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. es6-promise",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-mat4",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-spikes2d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-event-offset",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. polybooljs",
+ "1. regl",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. right-now",
+ "1. robust-orientation",
+ "1. sane-topojson",
+ "1. strongly-connected-components",
+ "1. superscript-text",
+ "1. svg-path-sdf",
+ "1. tinycolor2",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-shape",
+ "2. alpha-complex",
+ "2. simplicial-complex-boundary",
+ "2. element-size",
+ "2. clamp",
+ "2. color-rgba",
+ "2. dtype",
+ "2. affine-hull",
+ "2. incremental-convex-hull",
+ "2. monotone-convex-hull-2d",
+ "2. d3-collection",
+ "2. d3-dispatch",
+ "2. d3-quadtree",
+ "2. d3-timer",
+ "2. d3-color",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-shape",
+ "2. elementary-circuits-directed-graph",
+ "2. incremental-convex-hull",
+ "2. uniq",
+ "2. is-string-blank",
+ "2. optical-properties",
+ "2. tiny-sdf",
+ "2. gl-shader",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. cdt2d",
+ "2. clean-pslg",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. glslify",
+ "2. iota-array",
+ "2. ndarray",
+ "2. surface-nets",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. glslify",
+ "2. iota-array",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-read-float",
+ "2. glslify",
+ "2. ndarray",
+ "2. barycentric",
+ "2. colormap",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-specular-cook-torrance",
+ "2. glslify",
+ "2. ndarray",
+ "2. normals",
+ "2. polytope-closest-point",
+ "2. simplicial-complex-contour",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. glsl-inverse",
+ "2. glslify",
+ "2. text-cache",
+ "2. 3d-view",
+ "2. a-big-triangle",
+ "2. gl-axes3d",
+ "2. gl-fbo",
+ "2. gl-mat4",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. gl-spikes3d",
+ "2. glslify",
+ "2. has-passive-events",
+ "2. is-mobile",
+ "2. mouse-change",
+ "2. mouse-event-offset",
+ "2. mouse-wheel",
+ "2. ndarray",
+ "2. right-now",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. glslify",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. is-string-blank",
+ "2. typedarray-pool",
+ "2. vectorize-text",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. glslify",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. bit-twiddle",
+ "2. colormap",
+ "2. dup",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. binary-search-bounds",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-specular-beckmann",
+ "2. glslify",
+ "2. ndarray",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. surface-nets",
+ "2. typedarray-pool",
+ "2. bit-twiddle",
+ "2. color-normalize",
+ "2. css-font",
+ "2. detect-kerning",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. gl-util",
+ "2. is-plain-obj",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. parse-unit",
+ "2. pick-by-alias",
+ "2. regl",
+ "2. to-px",
+ "2. typedarray-pool",
+ "2. bl",
+ "2. concat-stream",
+ "2. duplexify",
+ "2. falafel",
+ "2. from2",
+ "2. glsl-resolve",
+ "2. glsl-token-whitespace-trim",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. through2",
+ "2. minimist",
+ "2. resolve",
+ "2. stack-trace",
+ "2. static-eval",
+ "2. xtend",
+ "2. is-browser",
+ "2. is-browser",
+ "2. @mapbox/gl-matrix",
+ "2. @mapbox/jsonlint-lines-primitives",
+ "2. @mapbox/mapbox-gl-supported",
+ "2. @mapbox/point-geometry",
+ "2. @mapbox/shelf-pack",
+ "2. @mapbox/tiny-sdf",
+ "2. @mapbox/unitbezier",
+ "2. @mapbox/vector-tile",
+ "2. @mapbox/whoots-js",
+ "2. csscolorparser",
+ "2. earcut",
+ "2. geojson-rewind",
+ "2. geojson-vt",
+ "2. gray-matter",
+ "2. grid-index",
+ "2. brfs",
+ "2. minimist",
+ "2. through2",
+ "2. pbf",
+ "2. quickselect",
+ "2. rw",
+ "2. shuffle-seed",
+ "2. sort-object",
+ "2. supercluster",
+ "2. tinyqueue",
+ "2. vt-pbf",
+ "2. binary-search-bounds",
+ "2. gl-mat4",
+ "2. gl-vec3",
+ "2. mat4-interpolate",
+ "2. mouse-event",
+ "2. signum",
+ "2. right-now",
+ "2. to-px",
+ "2. iota-array",
+ "2. is-buffer",
+ "2. cwise",
+ "2. gl-matrix-invert",
+ "2. ndarray-warp",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. clamp",
+ "2. dtype",
+ "2. flatten-vertex-data",
+ "2. is-obj",
+ "2. math-log2",
+ "2. parse-rect",
+ "2. binary-search-bounds",
+ "2. array-bounds",
+ "2. bubleify",
+ "2. color-normalize",
+ "2. flatten-vertex-data",
+ "2. object-assign",
+ "2. pick-by-alias",
+ "2. to-float32",
+ "2. update-diff",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. color-normalize",
+ "2. earcut",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. glslify",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. to-float32",
+ "2. array-range",
+ "2. array-rearrange",
+ "2. clamp",
+ "2. color-id",
+ "2. color-normalize",
+ "2. color-rgba",
+ "2. flatten-vertex-data",
+ "2. glslify",
+ "2. image-palette",
+ "2. is-iexplorer",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. to-float32",
+ "2. update-diff",
+ "2. array-bounds",
+ "2. array-range",
+ "2. bubleify",
+ "2. color-alpha",
+ "2. defined",
+ "2. flatten-vertex-data",
+ "2. left-pad",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. raf",
+ "2. regl-scatter2d",
+ "2. robust-scale",
+ "2. robust-subtract",
+ "2. robust-sum",
+ "2. two-product",
+ "2. bitmap-sdf",
+ "2. draw-svg-path",
+ "2. is-svg-path",
+ "2. parse-svg-path",
+ "2. svg-path-bounds",
+ "2. commander",
+ "2. get-canvas-context",
+ "2. object-assign",
+ "3. d3-path",
+ "3. circumradius",
+ "3. delaunay-triangulate",
+ "3. boundary-cells",
+ "3. reduce-simplicial-complex",
+ "3. clamp",
+ "3. color-parse",
+ "3. color-space",
+ "3. robust-orientation",
+ "3. robust-orientation",
+ "3. simplicial-complex",
+ "3. robust-orientation",
+ "3. strongly-connected-components",
+ "3. gl-format-compiler-error",
+ "3. weakmap-shim",
+ "3. binary-search-bounds",
+ "3. robust-in-sphere",
+ "3. robust-orientation",
+ "3. big-rat",
+ "3. box-intersect",
+ "3. nextafter",
+ "3. rat-vec",
+ "3. robust-segment-intersect",
+ "3. union-find",
+ "3. uniq",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. ndarray-extract-contour",
+ "3. triangulate-hypercube",
+ "3. zero-crossings",
+ "3. bit-twiddle",
+ "3. dup",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. robust-linear-solve",
+ "3. lerp",
+ "3. glsl-specular-beckmann",
+ "3. numeric",
+ "3. marching-simplex-table",
+ "3. ndarray",
+ "3. ndarray-sort",
+ "3. typedarray-pool",
+ "3. bit-twiddle",
+ "3. cwise",
+ "3. gl-fbo",
+ "3. ndarray",
+ "3. typedarray-pool",
+ "3. vectorize-text",
+ "3. matrix-camera-controller",
+ "3. orbit-camera-controller",
+ "3. turntable-camera-controller",
+ "3. gl-buffer",
+ "3. gl-vao",
+ "3. weak-map",
+ "3. bit-twiddle",
+ "3. dup",
+ "3. extract-frustum-planes",
+ "3. gl-buffer",
+ "3. gl-mat4",
+ "3. gl-shader",
+ "3. gl-state",
+ "3. gl-vao",
+ "3. gl-vec4",
+ "3. glslify",
+ "3. robust-orientation",
+ "3. split-polygon",
+ "3. vectorize-text",
+ "3. gl-texture2d",
+ "3. gl-buffer",
+ "3. gl-shader",
+ "3. gl-vao",
+ "3. glslify",
+ "3. cdt2d",
+ "3. clean-pslg",
+ "3. ndarray",
+ "3. planar-graph-to-polyline",
+ "3. simplify-planar-graph",
+ "3. surface-nets",
+ "3. triangulate-polyline",
+ "3. cwise-compiler",
+ "3. dup",
+ "3. cwise-compiler",
+ "3. cwise-compiler",
+ "3. ndarray",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. css-font-size-keywords",
+ "3. css-font-stretch-keywords",
+ "3. css-font-style-keywords",
+ "3. css-font-weight-keywords",
+ "3. css-global-keywords",
+ "3. css-system-font-keywords",
+ "3. pick-by-alias",
+ "3. string-split-by",
+ "3. unquote",
+ "3. d",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. dtype",
+ "3. css-font",
+ "3. css-font",
+ "3. es6-weak-map",
+ "3. is-browser",
+ "3. is-firefox",
+ "3. is-plain-obj",
+ "3. number-is-integer",
+ "3. object-assign",
+ "3. pick-by-alias",
+ "3. pick-by-alias",
+ "3. parse-unit",
+ "3. readable-stream",
+ "3. safe-buffer",
+ "3. buffer-from",
+ "3. readable-stream",
+ "3. inherits",
+ "3. typedarray",
+ "3. readable-stream",
+ "3. end-of-stream",
+ "3. inherits",
+ "3. stream-shift",
+ "3. acorn",
+ "3. foreach",
+ "3. isarray",
+ "3. object-keys",
+ "3. readable-stream",
+ "3. inherits",
+ "3. resolve",
+ "3. xtend",
+ "3. glsl-inject-defines",
+ "3. glsl-token-defines",
+ "3. glsl-token-depth",
+ "3. glsl-token-descope",
+ "3. glsl-token-scope",
+ "3. glsl-token-string",
+ "3. glsl-token-whitespace-trim",
+ "3. glsl-tokenizer",
+ "3. murmurhash-js",
+ "3. shallow-copy",
+ "3. @choojs/findup",
+ "3. events",
+ "3. glsl-resolve",
+ "3. glsl-tokenizer",
+ "3. graceful-fs",
+ "3. inherits",
+ "3. map-limit",
+ "3. resolve",
+ "3. readable-stream",
+ "3. xtend",
+ "3. path-parse",
+ "3. escodegen",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/geojson-area",
+ "3. concat-stream",
+ "3. minimist",
+ "3. sharkdown",
+ "3. extend-shallow",
+ "3. kind-of",
+ "3. js-yaml",
+ "3. strip-bom-string",
+ "3. quote-stream",
+ "3. static-module",
+ "3. through2",
+ "3. resolve",
+ "3. readable-stream",
+ "3. xtend",
+ "3. ieee754",
+ "3. resolve-protobuf-schema",
+ "3. seedrandom",
+ "3. sort-asc",
+ "3. sort-desc",
+ "3. kdbush",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/vector-tile",
+ "3. pbf",
+ "3. gl-mat4",
+ "3. gl-vec3",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. quat-slerp",
+ "3. cwise-compiler",
+ "3. cwise-parser",
+ "3. static-module",
+ "3. uglify-js",
+ "3. gl-mat2",
+ "3. gl-mat3",
+ "3. gl-mat4",
+ "3. cwise",
+ "3. ndarray-linear-interpolate",
+ "3. array-bounds",
+ "3. buble",
+ "3. clamp",
+ "3. color-id",
+ "3. pxls",
+ "3. quantize",
+ "3. color-parse",
+ "3. performance-now",
+ "3. two-product",
+ "3. two-sum",
+ "3. clamp",
+ "3. abs-svg-path",
+ "3. normalize-svg-path",
+ "3. abs-svg-path",
+ "3. is-svg-path",
+ "3. parse-svg-path",
+ "3. normalize-svg-path",
+ "4. circumcenter",
+ "4. tape",
+ "4. cell-orientation",
+ "4. compare-cell",
+ "4. compare-oriented-cell",
+ "4. color-name",
+ "4. defined",
+ "4. is-plain-obj",
+ "4. hsluv",
+ "4. mumath",
+ "4. bit-twiddle",
+ "4. union-find",
+ "4. add-line-numbers",
+ "4. gl-constants",
+ "4. glsl-shader-name",
+ "4. sprintf-js",
+ "4. robust-scale",
+ "4. robust-subtract",
+ "4. robust-sum",
+ "4. two-product",
+ "4. bit-twiddle",
+ "4. bn.js",
+ "4. double-bits",
+ "4. bit-twiddle",
+ "4. typedarray-pool",
+ "4. double-bits",
+ "4. big-rat",
+ "4. robust-orientation",
+ "4. typedarray-pool",
+ "4. gamma",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. cwise-compiler",
+ "4. robust-determinant",
+ "4. convex-hull",
+ "4. typedarray-pool",
+ "4. filtered-vector",
+ "4. gl-mat4",
+ "4. filtered-vector",
+ "4. gl-mat4",
+ "4. gl-vec3",
+ "4. uniq",
+ "4. robust-dot-product",
+ "4. robust-sum",
+ "4. edges-to-adjacency-list",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. robust-orientation",
+ "4. robust-sum",
+ "4. two-product",
+ "4. uniq",
+ "4. robust-orientation",
+ "4. simplicial-complex",
+ "4. cdt2d",
+ "4. uniq",
+ "4. parenthesis",
+ "4. es5-ext",
+ "4. es6-iterator",
+ "4. es6-symbol",
+ "4. next-tick",
+ "4. d",
+ "4. es5-ext",
+ "4. es6-symbol",
+ "4. d",
+ "4. es5-ext",
+ "4. is-finite",
+ "4. isarray",
+ "4. string_decoder",
+ "4. core-util-is",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. isarray",
+ "4. string_decoder",
+ "4. core-util-is",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. once",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. glsl-token-inject-block",
+ "4. glsl-token-string",
+ "4. glsl-tokenizer",
+ "4. glsl-tokenizer",
+ "4. glsl-token-assignments",
+ "4. glsl-token-depth",
+ "4. glsl-token-properties",
+ "4. glsl-token-scope",
+ "4. through2",
+ "4. commander",
+ "4. once",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. esprima",
+ "4. estraverse",
+ "4. esutils",
+ "4. optionator",
+ "4. source-map",
+ "4. wgs84",
+ "4. cardinal",
+ "4. expect.js",
+ "4. minimist",
+ "4. split",
+ "4. stream-spigot",
+ "4. through",
+ "4. is-extendable",
+ "4. argparse",
+ "4. esprima",
+ "4. buffer-equal",
+ "4. minimist",
+ "4. through2",
+ "4. concat-stream",
+ "4. convert-source-map",
+ "4. falafel",
+ "4. has",
+ "4. magic-string",
+ "4. duplexer2",
+ "4. escodegen",
+ "4. object-inspect",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. through2",
+ "4. merge-source-map",
+ "4. shallow-copy",
+ "4. static-eval",
+ "4. core-util-is",
+ "4. inherits",
+ "4. isarray",
+ "4. string_decoder",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. protocol-buffers-schema",
+ "4. gl-mat4",
+ "4. gl-vec3",
+ "4. gl-mat4",
+ "4. gl-quat",
+ "4. esprima",
+ "4. uniq",
+ "4. concat-stream",
+ "4. duplexer2",
+ "4. falafel",
+ "4. has",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. shallow-copy",
+ "4. escodegen",
+ "4. object-inspect",
+ "4. static-eval",
+ "4. through2",
+ "4. source-map",
+ "4. uglify-to-browserify",
+ "4. yargs",
+ "4. acorn",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. vlq",
+ "4. chalk",
+ "4. magic-string",
+ "4. minimist",
+ "4. os-homedir",
+ "4. arr-flatten",
+ "4. compute-dims",
+ "4. flip-pixels",
+ "4. is-browser",
+ "4. is-buffer",
+ "4. to-uint8",
+ "4. svg-arc-to-cubic-bezier",
+ "5. dup",
+ "5. robust-linear-solve",
+ "5. deep-equal",
+ "5. defined",
+ "5. for-each",
+ "5. function-bind",
+ "5. glob",
+ "5. has",
+ "5. inherits",
+ "5. minimist",
+ "5. object-inspect",
+ "5. resolve",
+ "5. resumer",
+ "5. string.prototype.trim",
+ "5. through",
+ "5. cell-orientation",
+ "5. compare-cell",
+ "5. almost-equal",
+ "5. pad-left",
+ "5. atob-lite",
+ "5. glsl-tokenizer",
+ "5. typedarray-pool",
+ "5. invert-permutation",
+ "5. typedarray-pool",
+ "5. robust-compress",
+ "5. robust-scale",
+ "5. robust-sum",
+ "5. two-product",
+ "5. binary-search-bounds",
+ "5. cubic-hermite",
+ "5. robust-sum",
+ "5. two-product",
+ "5. uniq",
+ "5. compare-angle",
+ "5. dup",
+ "5. binary-search-bounds",
+ "5. interval-tree-1d",
+ "5. robust-orientation",
+ "5. slab-decomposition",
+ "5. bit-twiddle",
+ "5. union-find",
+ "5. number-is-nan",
+ "5. safe-buffer",
+ "5. safe-buffer",
+ "5. safe-buffer",
+ "5. wrappy",
+ "5. safe-buffer",
+ "5. readable-stream",
+ "5. xtend",
+ "5. wrappy",
+ "5. safe-buffer",
+ "5. deep-is",
+ "5. fast-levenshtein",
+ "5. levn",
+ "5. prelude-ls",
+ "5. type-check",
+ "5. wordwrap",
+ "5. ansicolors",
+ "5. redeyed",
+ "5. through",
+ "5. readable-stream",
+ "5. sprintf-js",
+ "5. function-bind",
+ "5. vlq",
+ "5. readable-stream",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. optionator",
+ "5. source-map",
+ "5. source-map",
+ "5. safe-buffer",
+ "5. gl-mat3",
+ "5. gl-vec3",
+ "5. gl-vec4",
+ "5. readable-stream",
+ "5. minimist",
+ "5. through2",
+ "5. core-util-is",
+ "5. inherits",
+ "5. isarray",
+ "5. string_decoder",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. source-map",
+ "5. escodegen",
+ "5. readable-stream",
+ "5. xtend",
+ "5. camelcase",
+ "5. cliui",
+ "5. decamelize",
+ "5. window-size",
+ "5. acorn",
+ "5. acorn",
+ "5. ansi-styles",
+ "5. escape-string-regexp",
+ "5. supports-color",
+ "5. utils-copy",
+ "5. validate.io-array",
+ "5. validate.io-matrix-like",
+ "5. validate.io-ndarray-like",
+ "5. validate.io-positive-integer",
+ "5. arr-flatten",
+ "5. clamp",
+ "5. is-base64",
+ "5. is-float-array",
+ "5. to-array-buffer"
+ ]
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 800
+ }
+}
diff --git a/test/image/mocks/sunburst_values.json b/test/image/mocks/sunburst_values.json
index 940fe3e2d15..3fa7f87b325 100644
--- a/test/image/mocks/sunburst_values.json
+++ b/test/image/mocks/sunburst_values.json
@@ -6,6 +6,7 @@
"labels": ["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
"parents": ["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
"values": [10, 14, 12, 10, 2, 6, 6, 1, 4],
+ "textinfo": "label+value+percent parent+percent root",
"domain": {"x": [0, 0.48]},
"outsidetextfont": {"size": 20, "color": "#377eb8"},
"leaf": {"opacity": 0.4},
@@ -19,6 +20,7 @@
"parents": ["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
"domain": {"x": [0.52, 1]},
"values": [65, 14, 12, 10, 2, 6, 6, 1, 4],
+ "textinfo": "label+value+percent parent+percent root",
"outsidetextfont": {"size": 20, "color": "#377eb8"},
"leaf": {"opacity": 0.4},
"marker": {"line": {"width": 2}}
diff --git a/test/image/mocks/sunburst_values_colorscale.json b/test/image/mocks/sunburst_values_colorscale.json
new file mode 100644
index 00000000000..920119fc0c8
--- /dev/null
+++ b/test/image/mocks/sunburst_values_colorscale.json
@@ -0,0 +1,144 @@
+{
+ "data": [
+ {
+ "type": "sunburst",
+ "name": "with branchvalues:remainder",
+ "labels": [
+ "Eve",
+ "Cain",
+ "Seth",
+ "Enos",
+ "Noam",
+ "Abel",
+ "Awan",
+ "Enoch",
+ "Azura"
+ ],
+ "parents": [
+ "",
+ "Eve",
+ "Eve",
+ "Seth",
+ "Seth",
+ "Eve",
+ "Eve",
+ "Awan",
+ "Eve"
+ ],
+ "values": [
+ 10,
+ 14,
+ 12,
+ 10,
+ 2,
+ 6,
+ 6,
+ 1,
+ 4
+ ],
+ "domain": {
+ "x": [
+ 0,
+ 0.48
+ ]
+ },
+ "marker": {
+ "colorscale": "Portland",
+ "colors": [
+ 10,
+ 14,
+ 12,
+ 10,
+ 2,
+ 6,
+ 6,
+ 1,
+ 4
+ ],
+ "showscale": true,
+ "colorbar": {
+ "title": "trace A",
+ "x": 0,
+ "xanchor": "right"
+ }
+ }
+ },
+ {
+ "type": "sunburst",
+ "name": "with branchvalues:total",
+ "branchvalues": "total",
+ "labels": [
+ "Eve",
+ "Cain",
+ "Seth",
+ "Enos",
+ "Noam",
+ "Abel",
+ "Awan",
+ "Enoch",
+ "Azura"
+ ],
+ "parents": [
+ "",
+ "Eve",
+ "Eve",
+ "Seth",
+ "Seth",
+ "Eve",
+ "Eve",
+ "Awan",
+ "Eve"
+ ],
+ "domain": {
+ "x": [
+ 0.52,
+ 1
+ ]
+ },
+ "values": [
+ 65,
+ 14,
+ 12,
+ 10,
+ 2,
+ 6,
+ 6,
+ 1,
+ 4
+ ],
+ "marker": {
+ "cmin": 0,
+ "cmax": 25,
+ "colorscale": "Electric",
+ "reversescale": true,
+ "showscale": true,
+ "colorbar": {
+ "title": "trace B"
+ }
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "branchvalues: remainder",
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 1.1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "branchvalues: total
used as input to marker.color",
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 1.1,
+ "yanchor": "bottom"
+ }
+ ],
+ "paper_bgcolor": "#d3d3d3"
+ }
+}
diff --git a/test/image/mocks/sunburst_with-without_values.json b/test/image/mocks/sunburst_with-without_values.json
new file mode 100644
index 00000000000..2f3d1710046
--- /dev/null
+++ b/test/image/mocks/sunburst_with-without_values.json
@@ -0,0 +1,430 @@
+{
+ "data": [
+ {
+ "type": "sunburst",
+ "textinfo": "label+value+percent parent+percent entry+percent root+text+current path",
+ "hoverinfo": "all",
+ "name": "without values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "domain": {
+ "x": [
+ 0.01,
+ 0.33
+ ]
+ }
+ },
+ {
+ "type": "sunburst",
+ "textinfo": "label+value+percent parent+percent entry+percent root+text+current path",
+ "hoverinfo": "all",
+ "name": "with total values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "branchvalues": "total",
+ "values": [
+ 40,
+ 2,
+ 38,
+ 1.5,
+ 2.5,
+ 34,
+ 1,
+ 2,
+ 3,
+ 28,
+ 1.25,
+ 1.75,
+ 2.25,
+ 2.75,
+ 20,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3,
+ 10,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3
+ ],
+ "text": [
+ "forty",
+ "two",
+ "thirty-eight",
+ "one and a half",
+ "two and a half",
+ "thirty-four",
+ "one",
+ "two",
+ "three",
+ "twenty-eight",
+ "one and twenty-five hundredths",
+ "one and seventy-five hundredths",
+ "two and twenty-five hundredths",
+ "two and seventy-five hundredths",
+ "twenty",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three",
+ "ten",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": [
+ [
+ 0,
+ "#FFF"
+ ],
+ [
+ 0.01,
+ "#0FF"
+ ],
+ [
+ 0.1,
+ "#00F"
+ ],
+ [
+ 1,
+ "#000"
+ ]
+ ]
+ },
+ "domain": {
+ "x": [
+ 0.34,
+ 0.66
+ ]
+ }
+ },
+ {
+ "type": "sunburst",
+ "textinfo": "label+value+percent parent+percent entry+percent root+text+current path",
+ "hoverinfo": "all",
+ "name": "with remainder values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "branchvalues": "remainder",
+ "values": [
+ 0,
+ 2,
+ 0,
+ 1.5,
+ 2.5,
+ 0,
+ 1,
+ 2,
+ 3,
+ 0,
+ 1.25,
+ 1.75,
+ 2.25,
+ 2.75,
+ 0,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3,
+ 0,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3
+ ],
+ "text": [
+ "zero",
+ "two",
+ "zero",
+ "one and a half",
+ "two and a half",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "zero",
+ "one and twenty-five hundredths",
+ "one and seventy-five hundredths",
+ "two and twenty-five hundredths",
+ "two and seventy-five hundredths",
+ "zero",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three",
+ "zero",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": [
+ [
+ 0,
+ "#FFF"
+ ],
+ [
+ 0.01,
+ "#0FF"
+ ],
+ [
+ 0.1,
+ "#00F"
+ ],
+ [
+ 1,
+ "#000"
+ ]
+ ]
+ },
+ "domain": {
+ "x": [
+ 0.67,
+ 0.99
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "margin" : {
+ "t": 50,
+ "l": 0,
+ "r": 0,
+ "b": 25
+ },
+ "width": 1500,
+ "height": 600,
+ "shapes": [
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.01,
+ "x1": 0.33,
+ "y0": 0,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.34,
+ "x1": 0.66,
+ "y0": 0,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.67,
+ "x1": 0.99,
+ "y0": 0,
+ "y1": 1
+ }
+ ],
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "with counted leaves
",
+ "x": 0.17,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "with values and branchvalues: total
",
+ "x": 0.5,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "with values and branchvalues: remainder
",
+ "x": 0.83,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_coffee-maxdepth3.json b/test/image/mocks/treemap_coffee-maxdepth3.json
new file mode 100644
index 00000000000..90d45aff3f0
--- /dev/null
+++ b/test/image/mocks/treemap_coffee-maxdepth3.json
@@ -0,0 +1,314 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "pathbar": {
+ "visible": false
+ },
+ "maxdepth": 3,
+ "textinfo": "label+percent parent",
+ "ids": [
+ "Aromas",
+ "Tastes",
+ "Aromas-Enzymatic",
+ "Aromas-Sugar Browning",
+ "Aromas-Dry Distillation",
+ "Tastes-Bitter",
+ "Tastes-Salt",
+ "Tastes-Sweet",
+ "Tastes-Sour",
+ "Enzymatic-Flowery",
+ "Enzymatic-Fruity",
+ "Enzymatic-Herby",
+ "Sugar Browning-Nutty",
+ "Sugar Browning-Carmelly",
+ "Sugar Browning-Chocolatey",
+ "Dry Distillation-Resinous",
+ "Dry Distillation-Spicy",
+ "Dry Distillation-Carbony",
+ "Bitter-Pungent",
+ "Bitter-Harsh",
+ "Salt-Sharp",
+ "Salt-Bland",
+ "Sweet-Mellow",
+ "Sweet-Acidy",
+ "Sour-Winey",
+ "Sour-Soury",
+ "Flowery-Floral",
+ "Flowery-Fragrant",
+ "Fruity-Citrus",
+ "Fruity-Berry-like",
+ "Herby-Alliaceous",
+ "Herby-Leguminous",
+ "Nutty-Nut-like",
+ "Nutty-Malt-like",
+ "Carmelly-Candy-like",
+ "Carmelly-Syrup-like",
+ "Chocolatey-Chocolate-like",
+ "Chocolatey-Vanilla-like",
+ "Resinous-Turpeny",
+ "Resinous-Medicinal",
+ "Spicy-Warming",
+ "Spicy-Pungent",
+ "Carbony-Smokey",
+ "Carbony-Ashy",
+ "Pungent-Creosol",
+ "Pungent-Phenolic",
+ "Harsh-Caustic",
+ "Harsh-Alkaline",
+ "Sharp-Astringent",
+ "Sharp-Rough",
+ "Bland-Neutral",
+ "Bland-Soft",
+ "Mellow-Delicate",
+ "Mellow-Mild",
+ "Acidy-Nippy",
+ "Acidy-Piquant",
+ "Winey-Tangy",
+ "Winey-Tart",
+ "Soury-Hard",
+ "Soury-Acrid",
+ "Floral-Coffee Blossom",
+ "Floral-Tea Rose",
+ "Fragrant-Cardamon Caraway",
+ "Fragrant-Coriander Seeds",
+ "Citrus-Lemon",
+ "Citrus-Apple",
+ "Berry-like-Apricot",
+ "Berry-like-Blackberry",
+ "Alliaceous-Onion",
+ "Alliaceous-Garlic",
+ "Leguminous-Cucumber",
+ "Leguminous-Garden Peas",
+ "Nut-like-Roasted Peanuts",
+ "Nut-like-Walnuts",
+ "Malt-like-Balsamic Rice",
+ "Malt-like-Toast",
+ "Candy-like-Roasted Hazelnut",
+ "Candy-like-Roasted Almond",
+ "Syrup-like-Honey",
+ "Syrup-like-Maple Syrup",
+ "Chocolate-like-Bakers",
+ "Chocolate-like-Dark Chocolate",
+ "Vanilla-like-Swiss",
+ "Vanilla-like-Butter",
+ "Turpeny-Piney",
+ "Turpeny-Blackcurrant-like",
+ "Medicinal-Camphoric",
+ "Medicinal-Cineolic",
+ "Warming-Cedar",
+ "Warming-Pepper",
+ "Pungent-Clove",
+ "Pungent-Thyme",
+ "Smokey-Tarry",
+ "Smokey-Pipe Tobacco",
+ "Ashy-Burnt",
+ "Ashy-Charred"
+ ],
+ "labels": [
+ "Aromas",
+ "Tastes",
+ "Enzymatic",
+ "Sugar Browning",
+ "Dry Distillation",
+ "Bitter",
+ "Salt",
+ "Sweet",
+ "Sour",
+ "Flowery",
+ "Fruity",
+ "Herby",
+ "Nutty",
+ "Carmelly",
+ "Chocolatey",
+ "Resinous",
+ "Spicy",
+ "Carbony",
+ "Pungent",
+ "Harsh",
+ "Sharp",
+ "Bland",
+ "Mellow",
+ "Acidy",
+ "Winey",
+ "Soury",
+ "Floral",
+ "Fragrant",
+ "Citrus",
+ "Berry-like",
+ "Alliaceous",
+ "Leguminous",
+ "Nut-like",
+ "Malt-like",
+ "Candy-like",
+ "Syrup-like",
+ "Chocolate-like",
+ "Vanilla-like",
+ "Turpeny",
+ "Medicinal",
+ "Warming",
+ "Pungent",
+ "Smokey",
+ "Ashy",
+ "Creosol",
+ "Phenolic",
+ "Caustic",
+ "Alkaline",
+ "Astringent",
+ "Rough",
+ "Neutral",
+ "Soft",
+ "Delicate",
+ "Mild",
+ "Nippy",
+ "Piquant",
+ "Tangy",
+ "Tart",
+ "Hard",
+ "Acrid",
+ "Coffee Blossom",
+ "Tea Rose",
+ "Cardamon Caraway",
+ "Coriander Seeds",
+ "Lemon",
+ "Apple",
+ "Apricot",
+ "Blackberry",
+ "Onion",
+ "Garlic",
+ "Cucumber",
+ "Garden Peas",
+ "Roasted Peanuts",
+ "Walnuts",
+ "Balsamic Rice",
+ "Toast",
+ "Roasted Hazelnut",
+ "Roasted Almond",
+ "Honey",
+ "Maple Syrup",
+ "Bakers",
+ "Dark Chocolate",
+ "Swiss",
+ "Butter",
+ "Piney",
+ "Blackcurrant-like",
+ "Camphoric",
+ "Cineolic",
+ "Cedar",
+ "Pepper",
+ "Clove",
+ "Thyme",
+ "Tarry",
+ "Pipe Tobacco",
+ "Burnt",
+ "Charred"
+ ],
+ "parents": [
+ "",
+ "",
+ "Aromas",
+ "Aromas",
+ "Aromas",
+ "Tastes",
+ "Tastes",
+ "Tastes",
+ "Tastes",
+ "Aromas-Enzymatic",
+ "Aromas-Enzymatic",
+ "Aromas-Enzymatic",
+ "Aromas-Sugar Browning",
+ "Aromas-Sugar Browning",
+ "Aromas-Sugar Browning",
+ "Aromas-Dry Distillation",
+ "Aromas-Dry Distillation",
+ "Aromas-Dry Distillation",
+ "Tastes-Bitter",
+ "Tastes-Bitter",
+ "Tastes-Salt",
+ "Tastes-Salt",
+ "Tastes-Sweet",
+ "Tastes-Sweet",
+ "Tastes-Sour",
+ "Tastes-Sour",
+ "Enzymatic-Flowery",
+ "Enzymatic-Flowery",
+ "Enzymatic-Fruity",
+ "Enzymatic-Fruity",
+ "Enzymatic-Herby",
+ "Enzymatic-Herby",
+ "Sugar Browning-Nutty",
+ "Sugar Browning-Nutty",
+ "Sugar Browning-Carmelly",
+ "Sugar Browning-Carmelly",
+ "Sugar Browning-Chocolatey",
+ "Sugar Browning-Chocolatey",
+ "Dry Distillation-Resinous",
+ "Dry Distillation-Resinous",
+ "Dry Distillation-Spicy",
+ "Dry Distillation-Spicy",
+ "Dry Distillation-Carbony",
+ "Dry Distillation-Carbony",
+ "Bitter-Pungent",
+ "Bitter-Pungent",
+ "Bitter-Harsh",
+ "Bitter-Harsh",
+ "Salt-Sharp",
+ "Salt-Sharp",
+ "Salt-Bland",
+ "Salt-Bland",
+ "Sweet-Mellow",
+ "Sweet-Mellow",
+ "Sweet-Acidy",
+ "Sweet-Acidy",
+ "Sour-Winey",
+ "Sour-Winey",
+ "Sour-Soury",
+ "Sour-Soury",
+ "Flowery-Floral",
+ "Flowery-Floral",
+ "Flowery-Fragrant",
+ "Flowery-Fragrant",
+ "Fruity-Citrus",
+ "Fruity-Citrus",
+ "Fruity-Berry-like",
+ "Fruity-Berry-like",
+ "Herby-Alliaceous",
+ "Herby-Alliaceous",
+ "Herby-Leguminous",
+ "Herby-Leguminous",
+ "Nutty-Nut-like",
+ "Nutty-Nut-like",
+ "Nutty-Malt-like",
+ "Nutty-Malt-like",
+ "Carmelly-Candy-like",
+ "Carmelly-Candy-like",
+ "Carmelly-Syrup-like",
+ "Carmelly-Syrup-like",
+ "Chocolatey-Chocolate-like",
+ "Chocolatey-Chocolate-like",
+ "Chocolatey-Vanilla-like",
+ "Chocolatey-Vanilla-like",
+ "Resinous-Turpeny",
+ "Resinous-Turpeny",
+ "Resinous-Medicinal",
+ "Resinous-Medicinal",
+ "Spicy-Warming",
+ "Spicy-Warming",
+ "Spicy-Pungent",
+ "Spicy-Pungent",
+ "Carbony-Smokey",
+ "Carbony-Smokey",
+ "Carbony-Ashy",
+ "Carbony-Ashy"
+ ]
+ }
+ ],
+ "layout": {
+ "margin": {"l": 0, "r": 0, "b": 0, "t": 0},
+ "width": 500,
+ "height": 500,
+ "shapes": [
+ { "type": "rect", "layer": "below", "x0": 0, "x1": 1, "y0": 0, "y1": 1 }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_coffee.json b/test/image/mocks/treemap_coffee.json
new file mode 100644
index 00000000000..cf9db2ad129
--- /dev/null
+++ b/test/image/mocks/treemap_coffee.json
@@ -0,0 +1,313 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "pathbar": {
+ "visible": false
+ },
+ "textinfo": "label+percent parent",
+ "ids": [
+ "Aromas",
+ "Tastes",
+ "Aromas-Enzymatic",
+ "Aromas-Sugar Browning",
+ "Aromas-Dry Distillation",
+ "Tastes-Bitter",
+ "Tastes-Salt",
+ "Tastes-Sweet",
+ "Tastes-Sour",
+ "Enzymatic-Flowery",
+ "Enzymatic-Fruity",
+ "Enzymatic-Herby",
+ "Sugar Browning-Nutty",
+ "Sugar Browning-Carmelly",
+ "Sugar Browning-Chocolatey",
+ "Dry Distillation-Resinous",
+ "Dry Distillation-Spicy",
+ "Dry Distillation-Carbony",
+ "Bitter-Pungent",
+ "Bitter-Harsh",
+ "Salt-Sharp",
+ "Salt-Bland",
+ "Sweet-Mellow",
+ "Sweet-Acidy",
+ "Sour-Winey",
+ "Sour-Soury",
+ "Flowery-Floral",
+ "Flowery-Fragrant",
+ "Fruity-Citrus",
+ "Fruity-Berry-like",
+ "Herby-Alliaceous",
+ "Herby-Leguminous",
+ "Nutty-Nut-like",
+ "Nutty-Malt-like",
+ "Carmelly-Candy-like",
+ "Carmelly-Syrup-like",
+ "Chocolatey-Chocolate-like",
+ "Chocolatey-Vanilla-like",
+ "Resinous-Turpeny",
+ "Resinous-Medicinal",
+ "Spicy-Warming",
+ "Spicy-Pungent",
+ "Carbony-Smokey",
+ "Carbony-Ashy",
+ "Pungent-Creosol",
+ "Pungent-Phenolic",
+ "Harsh-Caustic",
+ "Harsh-Alkaline",
+ "Sharp-Astringent",
+ "Sharp-Rough",
+ "Bland-Neutral",
+ "Bland-Soft",
+ "Mellow-Delicate",
+ "Mellow-Mild",
+ "Acidy-Nippy",
+ "Acidy-Piquant",
+ "Winey-Tangy",
+ "Winey-Tart",
+ "Soury-Hard",
+ "Soury-Acrid",
+ "Floral-Coffee Blossom",
+ "Floral-Tea Rose",
+ "Fragrant-Cardamon Caraway",
+ "Fragrant-Coriander Seeds",
+ "Citrus-Lemon",
+ "Citrus-Apple",
+ "Berry-like-Apricot",
+ "Berry-like-Blackberry",
+ "Alliaceous-Onion",
+ "Alliaceous-Garlic",
+ "Leguminous-Cucumber",
+ "Leguminous-Garden Peas",
+ "Nut-like-Roasted Peanuts",
+ "Nut-like-Walnuts",
+ "Malt-like-Balsamic Rice",
+ "Malt-like-Toast",
+ "Candy-like-Roasted Hazelnut",
+ "Candy-like-Roasted Almond",
+ "Syrup-like-Honey",
+ "Syrup-like-Maple Syrup",
+ "Chocolate-like-Bakers",
+ "Chocolate-like-Dark Chocolate",
+ "Vanilla-like-Swiss",
+ "Vanilla-like-Butter",
+ "Turpeny-Piney",
+ "Turpeny-Blackcurrant-like",
+ "Medicinal-Camphoric",
+ "Medicinal-Cineolic",
+ "Warming-Cedar",
+ "Warming-Pepper",
+ "Pungent-Clove",
+ "Pungent-Thyme",
+ "Smokey-Tarry",
+ "Smokey-Pipe Tobacco",
+ "Ashy-Burnt",
+ "Ashy-Charred"
+ ],
+ "labels": [
+ "Aromas",
+ "Tastes",
+ "Enzymatic",
+ "Sugar Browning",
+ "Dry Distillation",
+ "Bitter",
+ "Salt",
+ "Sweet",
+ "Sour",
+ "Flowery",
+ "Fruity",
+ "Herby",
+ "Nutty",
+ "Carmelly",
+ "Chocolatey",
+ "Resinous",
+ "Spicy",
+ "Carbony",
+ "Pungent",
+ "Harsh",
+ "Sharp",
+ "Bland",
+ "Mellow",
+ "Acidy",
+ "Winey",
+ "Soury",
+ "Floral",
+ "Fragrant",
+ "Citrus",
+ "Berry-like",
+ "Alliaceous",
+ "Leguminous",
+ "Nut-like",
+ "Malt-like",
+ "Candy-like",
+ "Syrup-like",
+ "Chocolate-like",
+ "Vanilla-like",
+ "Turpeny",
+ "Medicinal",
+ "Warming",
+ "Pungent",
+ "Smokey",
+ "Ashy",
+ "Creosol",
+ "Phenolic",
+ "Caustic",
+ "Alkaline",
+ "Astringent",
+ "Rough",
+ "Neutral",
+ "Soft",
+ "Delicate",
+ "Mild",
+ "Nippy",
+ "Piquant",
+ "Tangy",
+ "Tart",
+ "Hard",
+ "Acrid",
+ "Coffee Blossom",
+ "Tea Rose",
+ "Cardamon Caraway",
+ "Coriander Seeds",
+ "Lemon",
+ "Apple",
+ "Apricot",
+ "Blackberry",
+ "Onion",
+ "Garlic",
+ "Cucumber",
+ "Garden Peas",
+ "Roasted Peanuts",
+ "Walnuts",
+ "Balsamic Rice",
+ "Toast",
+ "Roasted Hazelnut",
+ "Roasted Almond",
+ "Honey",
+ "Maple Syrup",
+ "Bakers",
+ "Dark Chocolate",
+ "Swiss",
+ "Butter",
+ "Piney",
+ "Blackcurrant-like",
+ "Camphoric",
+ "Cineolic",
+ "Cedar",
+ "Pepper",
+ "Clove",
+ "Thyme",
+ "Tarry",
+ "Pipe Tobacco",
+ "Burnt",
+ "Charred"
+ ],
+ "parents": [
+ "",
+ "",
+ "Aromas",
+ "Aromas",
+ "Aromas",
+ "Tastes",
+ "Tastes",
+ "Tastes",
+ "Tastes",
+ "Aromas-Enzymatic",
+ "Aromas-Enzymatic",
+ "Aromas-Enzymatic",
+ "Aromas-Sugar Browning",
+ "Aromas-Sugar Browning",
+ "Aromas-Sugar Browning",
+ "Aromas-Dry Distillation",
+ "Aromas-Dry Distillation",
+ "Aromas-Dry Distillation",
+ "Tastes-Bitter",
+ "Tastes-Bitter",
+ "Tastes-Salt",
+ "Tastes-Salt",
+ "Tastes-Sweet",
+ "Tastes-Sweet",
+ "Tastes-Sour",
+ "Tastes-Sour",
+ "Enzymatic-Flowery",
+ "Enzymatic-Flowery",
+ "Enzymatic-Fruity",
+ "Enzymatic-Fruity",
+ "Enzymatic-Herby",
+ "Enzymatic-Herby",
+ "Sugar Browning-Nutty",
+ "Sugar Browning-Nutty",
+ "Sugar Browning-Carmelly",
+ "Sugar Browning-Carmelly",
+ "Sugar Browning-Chocolatey",
+ "Sugar Browning-Chocolatey",
+ "Dry Distillation-Resinous",
+ "Dry Distillation-Resinous",
+ "Dry Distillation-Spicy",
+ "Dry Distillation-Spicy",
+ "Dry Distillation-Carbony",
+ "Dry Distillation-Carbony",
+ "Bitter-Pungent",
+ "Bitter-Pungent",
+ "Bitter-Harsh",
+ "Bitter-Harsh",
+ "Salt-Sharp",
+ "Salt-Sharp",
+ "Salt-Bland",
+ "Salt-Bland",
+ "Sweet-Mellow",
+ "Sweet-Mellow",
+ "Sweet-Acidy",
+ "Sweet-Acidy",
+ "Sour-Winey",
+ "Sour-Winey",
+ "Sour-Soury",
+ "Sour-Soury",
+ "Flowery-Floral",
+ "Flowery-Floral",
+ "Flowery-Fragrant",
+ "Flowery-Fragrant",
+ "Fruity-Citrus",
+ "Fruity-Citrus",
+ "Fruity-Berry-like",
+ "Fruity-Berry-like",
+ "Herby-Alliaceous",
+ "Herby-Alliaceous",
+ "Herby-Leguminous",
+ "Herby-Leguminous",
+ "Nutty-Nut-like",
+ "Nutty-Nut-like",
+ "Nutty-Malt-like",
+ "Nutty-Malt-like",
+ "Carmelly-Candy-like",
+ "Carmelly-Candy-like",
+ "Carmelly-Syrup-like",
+ "Carmelly-Syrup-like",
+ "Chocolatey-Chocolate-like",
+ "Chocolatey-Chocolate-like",
+ "Chocolatey-Vanilla-like",
+ "Chocolatey-Vanilla-like",
+ "Resinous-Turpeny",
+ "Resinous-Turpeny",
+ "Resinous-Medicinal",
+ "Resinous-Medicinal",
+ "Spicy-Warming",
+ "Spicy-Warming",
+ "Spicy-Pungent",
+ "Spicy-Pungent",
+ "Carbony-Smokey",
+ "Carbony-Smokey",
+ "Carbony-Ashy",
+ "Carbony-Ashy"
+ ]
+ }
+ ],
+ "layout": {
+ "margin": {"l": 0, "r": 0, "b": 0, "t": 0},
+ "width": 500,
+ "height": 500,
+ "shapes": [
+ { "type": "rect", "layer": "below", "x0": 0, "x1": 1, "y0": 0, "y1": 1 }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_first.json b/test/image/mocks/treemap_first.json
new file mode 100644
index 00000000000..a8fb85a2100
--- /dev/null
+++ b/test/image/mocks/treemap_first.json
@@ -0,0 +1,92 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "pathbar": {
+ "visible": false
+ },
+ "textinfo": "label+percent parent",
+ "labels": [
+ "Eve",
+ "Cain",
+ "Seth",
+ "Enos",
+ "Noam",
+ "Abel",
+ "Awan",
+ "Enoch",
+ "Azura"
+ ],
+ "parents": [
+ "",
+ "Eve",
+ "Eve",
+ "Seth",
+ "Seth",
+ "Eve",
+ "Eve",
+ "Awan",
+ "Eve"
+ ],
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "pathbar": {
+ "visible": false
+ },
+ "textinfo": "label+percent entry",
+ "labels": [
+ "Cain",
+ "Seth",
+ "Enos",
+ "Noam",
+ "Abel",
+ "Awan",
+ "Enoch",
+ "Azura"
+ ],
+ "parents": [
+ "Eve",
+ "Eve",
+ "Seth",
+ "Seth",
+ "Eve",
+ "Eve",
+ "Awan",
+ "Eve"
+ ],
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "percent parent",
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "percent entry",
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_flare.json b/test/image/mocks/treemap_flare.json
new file mode 100644
index 00000000000..f049539cc80
--- /dev/null
+++ b/test/image/mocks/treemap_flare.json
@@ -0,0 +1,774 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "hoverinfo": "all",
+ "ids": [
+ "flare",
+ "flare-analytics",
+ "flare-animate",
+ "flare-data",
+ "flare-display",
+ "flare-flex",
+ "flare-physics",
+ "flare-query",
+ "flare-scale",
+ "flare-util",
+ "flare-vis",
+ "analytics-cluster",
+ "analytics-graph",
+ "analytics-optimization",
+ "animate-Easing",
+ "animate-FunctionSequence",
+ "animate-interpolate",
+ "animate-ISchedulable",
+ "animate-Parallel",
+ "animate-Pause",
+ "animate-Scheduler",
+ "animate-Sequence",
+ "animate-Transition",
+ "animate-Transitioner",
+ "animate-TransitionEvent",
+ "animate-Tween",
+ "data-converters",
+ "data-DataField",
+ "data-DataSchema",
+ "data-DataSet",
+ "data-DataSource",
+ "data-DataTable",
+ "data-DataUtil",
+ "display-DirtySprite",
+ "display-LineSprite",
+ "display-RectSprite",
+ "display-TextSprite",
+ "flex-FlareVis",
+ "physics-DragForce",
+ "physics-GravityForce",
+ "physics-IForce",
+ "physics-NBodyForce",
+ "physics-Particle",
+ "physics-Simulation",
+ "physics-Spring",
+ "physics-SpringForce",
+ "query-AggregateExpression",
+ "query-And",
+ "query-Arithmetic",
+ "query-Average",
+ "query-BinaryExpression",
+ "query-Comparison",
+ "query-CompositeExpression",
+ "query-Count",
+ "query-DateUtil",
+ "query-Distinct",
+ "query-Expression",
+ "query-ExpressionIterator",
+ "query-Fn",
+ "query-If",
+ "query-IsA",
+ "query-Literal",
+ "query-Match",
+ "query-Maximum",
+ "query-methods",
+ "query-Minimum",
+ "query-Not",
+ "query-Or",
+ "query-Query",
+ "query-Range",
+ "query-StringUtil",
+ "query-Sum",
+ "query-Variable",
+ "query-Variance",
+ "query-Xor",
+ "scale-IScaleMap",
+ "scale-LinearScale",
+ "scale-LogScale",
+ "scale-OrdinalScale",
+ "scale-QuantileScale",
+ "scale-QuantitativeScale",
+ "scale-RootScale",
+ "scale-Scale",
+ "scale-ScaleType",
+ "scale-TimeScale",
+ "util-Arrays",
+ "util-Colors",
+ "util-Dates",
+ "util-Displays",
+ "util-Filter",
+ "util-Geometry",
+ "util-heap",
+ "util-IEvaluable",
+ "util-IPredicate",
+ "util-IValueProxy",
+ "util-math",
+ "util-Maths",
+ "util-Orientation",
+ "util-palette",
+ "util-Property",
+ "util-Shapes",
+ "util-Sort",
+ "util-Stats",
+ "util-Strings",
+ "vis-axis",
+ "vis-controls",
+ "vis-data",
+ "vis-events",
+ "vis-legend",
+ "vis-operator",
+ "vis-Visualization",
+ "cluster-AgglomerativeCluster",
+ "cluster-CommunityStructure",
+ "cluster-HierarchicalCluster",
+ "cluster-MergeEdge",
+ "graph-BetweennessCentrality",
+ "graph-LinkDistance",
+ "graph-MaxFlowMinCut",
+ "graph-ShortestPaths",
+ "graph-SpanningTree",
+ "optimization-AspectRatioBanker",
+ "interpolate-ArrayInterpolator",
+ "interpolate-ColorInterpolator",
+ "interpolate-DateInterpolator",
+ "interpolate-Interpolator",
+ "interpolate-MatrixInterpolator",
+ "interpolate-NumberInterpolator",
+ "interpolate-ObjectInterpolator",
+ "interpolate-PointInterpolator",
+ "interpolate-RectangleInterpolator",
+ "converters-Converters",
+ "converters-DelimitedTextConverter",
+ "converters-GraphMLConverter",
+ "converters-IDataConverter",
+ "converters-JSONConverter",
+ "methods-add",
+ "methods-and",
+ "methods-average",
+ "methods-count",
+ "methods-distinct",
+ "methods-div",
+ "methods-eq",
+ "methods-fn",
+ "methods-gt",
+ "methods-gte",
+ "methods-iff",
+ "methods-isa",
+ "methods-lt",
+ "methods-lte",
+ "methods-max",
+ "methods-min",
+ "methods-mod",
+ "methods-mul",
+ "methods-neq",
+ "methods-not",
+ "methods-or",
+ "methods-orderby",
+ "methods-range",
+ "methods-select",
+ "methods-stddev",
+ "methods-sub",
+ "methods-sum",
+ "methods-update",
+ "methods-variance",
+ "methods-where",
+ "methods-xor",
+ "methods-_",
+ "heap-FibonacciHeap",
+ "heap-HeapNode",
+ "math-DenseMatrix",
+ "math-IMatrix",
+ "math-SparseMatrix",
+ "palette-ColorPalette",
+ "palette-Palette",
+ "palette-ShapePalette",
+ "palette-SizePalette",
+ "axis-Axes",
+ "axis-Axis",
+ "axis-AxisGridLine",
+ "axis-AxisLabel",
+ "axis-CartesianAxes",
+ "controls-AnchorControl",
+ "controls-ClickControl",
+ "controls-Control",
+ "controls-ControlList",
+ "controls-DragControl",
+ "controls-ExpandControl",
+ "controls-HoverControl",
+ "controls-IControl",
+ "controls-PanZoomControl",
+ "controls-SelectionControl",
+ "controls-TooltipControl",
+ "data-Data",
+ "data-DataList",
+ "data-DataSprite",
+ "data-EdgeSprite",
+ "data-NodeSprite",
+ "data-render",
+ "data-ScaleBinding",
+ "data-Tree",
+ "data-TreeBuilder",
+ "events-DataEvent",
+ "events-SelectionEvent",
+ "events-TooltipEvent",
+ "events-VisualizationEvent",
+ "legend-Legend",
+ "legend-LegendItem",
+ "legend-LegendRange",
+ "operator-distortion",
+ "operator-encoder",
+ "operator-filter",
+ "operator-IOperator",
+ "operator-label",
+ "operator-layout",
+ "operator-Operator",
+ "operator-OperatorList",
+ "operator-OperatorSequence",
+ "operator-OperatorSwitch",
+ "operator-SortOperator",
+ "render-ArrowType",
+ "render-EdgeRenderer",
+ "render-IRenderer",
+ "render-ShapeRenderer",
+ "distortion-BifocalDistortion",
+ "distortion-Distortion",
+ "distortion-FisheyeDistortion",
+ "encoder-ColorEncoder",
+ "encoder-Encoder",
+ "encoder-PropertyEncoder",
+ "encoder-ShapeEncoder",
+ "encoder-SizeEncoder",
+ "filter-FisheyeTreeFilter",
+ "filter-GraphDistanceFilter",
+ "filter-VisibilityFilter",
+ "label-Labeler",
+ "label-RadialLabeler",
+ "label-StackedAreaLabeler",
+ "layout-AxisLayout",
+ "layout-BundledEdgeRouter",
+ "layout-CircleLayout",
+ "layout-CirclePackingLayout",
+ "layout-DendrogramLayout",
+ "layout-ForceDirectedLayout",
+ "layout-IcicleTreeLayout",
+ "layout-IndentedTreeLayout",
+ "layout-Layout",
+ "layout-NodeLinkTreeLayout",
+ "layout-PieLayout",
+ "layout-RadialTreeLayout",
+ "layout-RandomLayout",
+ "layout-StackedAreaLayout",
+ "layout-TreeMapLayout"
+ ],
+ "labels": [
+ "flare",
+ "analytics",
+ "animate",
+ "data",
+ "display",
+ "flex",
+ "physics",
+ "query",
+ "scale",
+ "util",
+ "vis",
+ "cluster",
+ "graph",
+ "optimization",
+ "Easing",
+ "FunctionSequence",
+ "interpolate",
+ "ISchedulable",
+ "Parallel",
+ "Pause",
+ "Scheduler",
+ "Sequence",
+ "Transition",
+ "Transitioner",
+ "TransitionEvent",
+ "Tween",
+ "converters",
+ "DataField",
+ "DataSchema",
+ "DataSet",
+ "DataSource",
+ "DataTable",
+ "DataUtil",
+ "DirtySprite",
+ "LineSprite",
+ "RectSprite",
+ "TextSprite",
+ "FlareVis",
+ "DragForce",
+ "GravityForce",
+ "IForce",
+ "NBodyForce",
+ "Particle",
+ "Simulation",
+ "Spring",
+ "SpringForce",
+ "AggregateExpression",
+ "And",
+ "Arithmetic",
+ "Average",
+ "BinaryExpression",
+ "Comparison",
+ "CompositeExpression",
+ "Count",
+ "DateUtil",
+ "Distinct",
+ "Expression",
+ "ExpressionIterator",
+ "Fn",
+ "If",
+ "IsA",
+ "Literal",
+ "Match",
+ "Maximum",
+ "methods",
+ "Minimum",
+ "Not",
+ "Or",
+ "Query",
+ "Range",
+ "StringUtil",
+ "Sum",
+ "Variable",
+ "Variance",
+ "Xor",
+ "IScaleMap",
+ "LinearScale",
+ "LogScale",
+ "OrdinalScale",
+ "QuantileScale",
+ "QuantitativeScale",
+ "RootScale",
+ "Scale",
+ "ScaleType",
+ "TimeScale",
+ "Arrays",
+ "Colors",
+ "Dates",
+ "Displays",
+ "Filter",
+ "Geometry",
+ "heap",
+ "IEvaluable",
+ "IPredicate",
+ "IValueProxy",
+ "math",
+ "Maths",
+ "Orientation",
+ "palette",
+ "Property",
+ "Shapes",
+ "Sort",
+ "Stats",
+ "Strings",
+ "axis",
+ "controls",
+ "data",
+ "events",
+ "legend",
+ "operator",
+ "Visualization",
+ "AgglomerativeCluster",
+ "CommunityStructure",
+ "HierarchicalCluster",
+ "MergeEdge",
+ "BetweennessCentrality",
+ "LinkDistance",
+ "MaxFlowMinCut",
+ "ShortestPaths",
+ "SpanningTree",
+ "AspectRatioBanker",
+ "ArrayInterpolator",
+ "ColorInterpolator",
+ "DateInterpolator",
+ "Interpolator",
+ "MatrixInterpolator",
+ "NumberInterpolator",
+ "ObjectInterpolator",
+ "PointInterpolator",
+ "RectangleInterpolator",
+ "Converters",
+ "DelimitedTextConverter",
+ "GraphMLConverter",
+ "IDataConverter",
+ "JSONConverter",
+ "add",
+ "and",
+ "average",
+ "count",
+ "distinct",
+ "div",
+ "eq",
+ "fn",
+ "gt",
+ "gte",
+ "iff",
+ "isa",
+ "lt",
+ "lte",
+ "max",
+ "min",
+ "mod",
+ "mul",
+ "neq",
+ "not",
+ "or",
+ "orderby",
+ "range",
+ "select",
+ "stddev",
+ "sub",
+ "sum",
+ "update",
+ "variance",
+ "where",
+ "xor",
+ "_",
+ "FibonacciHeap",
+ "HeapNode",
+ "DenseMatrix",
+ "IMatrix",
+ "SparseMatrix",
+ "ColorPalette",
+ "Palette",
+ "ShapePalette",
+ "SizePalette",
+ "Axes",
+ "Axis",
+ "AxisGridLine",
+ "AxisLabel",
+ "CartesianAxes",
+ "AnchorControl",
+ "ClickControl",
+ "Control",
+ "ControlList",
+ "DragControl",
+ "ExpandControl",
+ "HoverControl",
+ "IControl",
+ "PanZoomControl",
+ "SelectionControl",
+ "TooltipControl",
+ "Data",
+ "DataList",
+ "DataSprite",
+ "EdgeSprite",
+ "NodeSprite",
+ "render",
+ "ScaleBinding",
+ "Tree",
+ "TreeBuilder",
+ "DataEvent",
+ "SelectionEvent",
+ "TooltipEvent",
+ "VisualizationEvent",
+ "Legend",
+ "LegendItem",
+ "LegendRange",
+ "distortion",
+ "encoder",
+ "filter",
+ "IOperator",
+ "label",
+ "layout",
+ "Operator",
+ "OperatorList",
+ "OperatorSequence",
+ "OperatorSwitch",
+ "SortOperator",
+ "ArrowType",
+ "EdgeRenderer",
+ "IRenderer",
+ "ShapeRenderer",
+ "BifocalDistortion",
+ "Distortion",
+ "FisheyeDistortion",
+ "ColorEncoder",
+ "Encoder",
+ "PropertyEncoder",
+ "ShapeEncoder",
+ "SizeEncoder",
+ "FisheyeTreeFilter",
+ "GraphDistanceFilter",
+ "VisibilityFilter",
+ "Labeler",
+ "RadialLabeler",
+ "StackedAreaLabeler",
+ "AxisLayout",
+ "BundledEdgeRouter",
+ "CircleLayout",
+ "CirclePackingLayout",
+ "DendrogramLayout",
+ "ForceDirectedLayout",
+ "IcicleTreeLayout",
+ "IndentedTreeLayout",
+ "Layout",
+ "NodeLinkTreeLayout",
+ "PieLayout",
+ "RadialTreeLayout",
+ "RandomLayout",
+ "StackedAreaLayout",
+ "TreeMapLayout"
+ ],
+ "parents": [
+ "",
+ "flare",
+ "flare",
+ "flare",
+ "flare",
+ "flare",
+ "flare",
+ "flare",
+ "flare",
+ "flare",
+ "flare",
+ "flare-analytics",
+ "flare-analytics",
+ "flare-analytics",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-animate",
+ "flare-data",
+ "flare-data",
+ "flare-data",
+ "flare-data",
+ "flare-data",
+ "flare-data",
+ "flare-data",
+ "flare-display",
+ "flare-display",
+ "flare-display",
+ "flare-display",
+ "flare-flex",
+ "flare-physics",
+ "flare-physics",
+ "flare-physics",
+ "flare-physics",
+ "flare-physics",
+ "flare-physics",
+ "flare-physics",
+ "flare-physics",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-query",
+ "flare-scale",
+ "flare-scale",
+ "flare-scale",
+ "flare-scale",
+ "flare-scale",
+ "flare-scale",
+ "flare-scale",
+ "flare-scale",
+ "flare-scale",
+ "flare-scale",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-util",
+ "flare-vis",
+ "flare-vis",
+ "flare-vis",
+ "flare-vis",
+ "flare-vis",
+ "flare-vis",
+ "flare-vis",
+ "analytics-cluster",
+ "analytics-cluster",
+ "analytics-cluster",
+ "analytics-cluster",
+ "analytics-graph",
+ "analytics-graph",
+ "analytics-graph",
+ "analytics-graph",
+ "analytics-graph",
+ "analytics-optimization",
+ "animate-interpolate",
+ "animate-interpolate",
+ "animate-interpolate",
+ "animate-interpolate",
+ "animate-interpolate",
+ "animate-interpolate",
+ "animate-interpolate",
+ "animate-interpolate",
+ "animate-interpolate",
+ "data-converters",
+ "data-converters",
+ "data-converters",
+ "data-converters",
+ "data-converters",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "query-methods",
+ "util-heap",
+ "util-heap",
+ "util-math",
+ "util-math",
+ "util-math",
+ "util-palette",
+ "util-palette",
+ "util-palette",
+ "util-palette",
+ "vis-axis",
+ "vis-axis",
+ "vis-axis",
+ "vis-axis",
+ "vis-axis",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-controls",
+ "vis-data",
+ "vis-data",
+ "vis-data",
+ "vis-data",
+ "vis-data",
+ "vis-data",
+ "vis-data",
+ "vis-data",
+ "vis-data",
+ "vis-events",
+ "vis-events",
+ "vis-events",
+ "vis-events",
+ "vis-legend",
+ "vis-legend",
+ "vis-legend",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "vis-operator",
+ "data-render",
+ "data-render",
+ "data-render",
+ "data-render",
+ "operator-distortion",
+ "operator-distortion",
+ "operator-distortion",
+ "operator-encoder",
+ "operator-encoder",
+ "operator-encoder",
+ "operator-encoder",
+ "operator-encoder",
+ "operator-filter",
+ "operator-filter",
+ "operator-filter",
+ "operator-label",
+ "operator-label",
+ "operator-label",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout",
+ "operator-layout"
+ ]
+ }
+ ],
+ "layout": {
+ "width": 600,
+ "height": 600
+ }
+}
diff --git a/test/image/mocks/treemap_level-depth.json b/test/image/mocks/treemap_level-depth.json
new file mode 100644
index 00000000000..8cc81a08320
--- /dev/null
+++ b/test/image/mocks/treemap_level-depth.json
@@ -0,0 +1,562 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "maxdepth": 3,
+ "texttemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "hovertemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U",
+ ""
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu",
+ 0
+ ],
+ "values": [
+ 4000000,
+ 3000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 3000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Alpha",
+ "Alpha",
+ "Alpha",
+ "Alpha",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Charlie",
+ "Charlie",
+ "Delta",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Golf",
+ "Golf",
+ "Hotel",
+ "Papa",
+ "Papa",
+ "Quebec",
+ "Victor",
+ "Victor",
+ "Yankee"
+ ],
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0,
+ 0.3
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "level": "Victor",
+ "texttemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "hovertemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U",
+ ""
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu",
+ 0
+ ],
+ "values": [
+ 4000000,
+ 3000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 3000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Alpha",
+ "Alpha",
+ "Alpha",
+ "Alpha",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Charlie",
+ "Charlie",
+ "Delta",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Golf",
+ "Golf",
+ "Hotel",
+ "Papa",
+ "Papa",
+ "Quebec",
+ "Victor",
+ "Victor",
+ "Yankee"
+ ],
+ "marker": {
+ "line": {
+ "color": "black",
+ "width": 2
+ }
+ },
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.3
+ ]
+ }
+ },
+ {
+ "type": "sunburst",
+ "maxdepth": 3,
+ "texttemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "hovertemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U",
+ ""
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu",
+ 0
+ ],
+ "values": [
+ 4000000,
+ 3000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 3000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Alpha",
+ "Alpha",
+ "Alpha",
+ "Alpha",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Charlie",
+ "Charlie",
+ "Delta",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Golf",
+ "Golf",
+ "Hotel",
+ "Papa",
+ "Papa",
+ "Quebec",
+ "Victor",
+ "Victor",
+ "Yankee"
+ ],
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0.2,
+ 1
+ ]
+ }
+ },
+ {
+ "type": "sunburst",
+ "level": "Victor",
+ "texttemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "hovertemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U",
+ ""
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu",
+ 0
+ ],
+ "values": [
+ 4000000,
+ 3000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 3000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 2000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000,
+ 1000000
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Alpha",
+ "Alpha",
+ "Alpha",
+ "Alpha",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Charlie",
+ "Charlie",
+ "Delta",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Golf",
+ "Golf",
+ "Hotel",
+ "Papa",
+ "Papa",
+ "Quebec",
+ "Victor",
+ "Victor",
+ "Yankee"
+ ],
+ "marker": {
+ "line": {
+ "color": "black",
+ "width": 2
+ }
+ },
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0.2,
+ 1
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 1200,
+ "height": 800,
+ "treemapcolorway": [
+ "#8dd3c7",
+ "#ffffb3",
+ "#bebada",
+ "#fb8072",
+ "#80b1d3"
+ ],
+ "sunburstcolorway": [
+ "#8dd3c7",
+ "#ffffb3",
+ "#bebada",
+ "#fb8072",
+ "#80b1d3"
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_packages_colorscale_allone.json b/test/image/mocks/treemap_packages_colorscale_allone.json
new file mode 100644
index 00000000000..e8187fb9661
--- /dev/null
+++ b/test/image/mocks/treemap_packages_colorscale_allone.json
@@ -0,0 +1,2354 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "count": "leaves+branches",
+ "maxdepth": 3,
+ "textinfo": "label+percent parent",
+ "hoverinfo": "all",
+ "tiling": {
+ "packing": "slice-dice"
+ },
+ "pathbar": {
+ "side": "bottom"
+ },
+ "marker": {
+ "line": {
+ "color": "#777"
+ },
+ "colorscale": [
+ [
+ 0,
+ "#FFF"
+ ],
+ [
+ 0.01,
+ "#FF0"
+ ],
+ [
+ 0.1,
+ "#F00"
+ ],
+ [
+ 1,
+ "#000"
+ ]
+ ],
+ "showscale": true
+ },
+ "level": "1. gl-mesh3d",
+ "ids": [
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. array-range",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. country-regex",
+ "1. d3",
+ "1. d3-force",
+ "1. d3-hierarchy",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. es6-promise",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-mat4",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-spikes2d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-event-offset",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. polybooljs",
+ "1. regl",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. right-now",
+ "1. robust-orientation",
+ "1. sane-topojson",
+ "1. strongly-connected-components",
+ "1. superscript-text",
+ "1. svg-path-sdf",
+ "1. tinycolor2",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. 3d-view",
+ "2. @mapbox/gl-matrix",
+ "2. @mapbox/jsonlint-lines-primitives",
+ "2. @mapbox/mapbox-gl-supported",
+ "2. @mapbox/point-geometry",
+ "2. @mapbox/shelf-pack",
+ "2. @mapbox/tiny-sdf",
+ "2. @mapbox/unitbezier",
+ "2. @mapbox/vector-tile",
+ "2. @mapbox/whoots-js",
+ "2. a-big-triangle",
+ "2. affine-hull",
+ "2. alpha-complex",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. array-range",
+ "2. array-rearrange",
+ "2. barycentric",
+ "2. binary-search-bounds",
+ "2. bit-twiddle",
+ "2. bitmap-sdf",
+ "2. bl",
+ "2. brfs",
+ "2. bubleify",
+ "2. cdt2d",
+ "2. clamp",
+ "2. clean-pslg",
+ "2. color-alpha",
+ "2. color-id",
+ "2. color-normalize",
+ "2. color-rgba",
+ "2. colormap",
+ "2. commander",
+ "2. concat-stream",
+ "2. css-font",
+ "2. csscolorparser",
+ "2. cwise",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-color",
+ "2. d3-dispatch",
+ "2. d3-quadtree",
+ "2. d3-shape",
+ "2. d3-timer",
+ "2. defined",
+ "2. detect-kerning",
+ "2. draw-svg-path",
+ "2. dtype",
+ "2. dup",
+ "2. duplexify",
+ "2. earcut",
+ "2. element-size",
+ "2. elementary-circuits-directed-graph",
+ "2. es6-weak-map",
+ "2. falafel",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. from2",
+ "2. geojson-rewind",
+ "2. geojson-vt",
+ "2. get-canvas-context",
+ "2. gl-axes3d",
+ "2. gl-buffer",
+ "2. gl-fbo",
+ "2. gl-mat4",
+ "2. gl-matrix-invert",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. gl-spikes3d",
+ "2. gl-texture2d",
+ "2. gl-util",
+ "2. gl-vao",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glsl-read-float",
+ "2. glsl-resolve",
+ "2. glsl-specular-beckmann",
+ "2. glsl-specular-cook-torrance",
+ "2. glsl-token-whitespace-trim",
+ "2. glslify",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. gray-matter",
+ "2. grid-index",
+ "2. has-passive-events",
+ "2. image-palette",
+ "2. incremental-convex-hull",
+ "2. iota-array",
+ "2. is-browser",
+ "2. is-buffer",
+ "2. is-iexplorer",
+ "2. is-mobile",
+ "2. is-obj",
+ "2. is-plain-obj",
+ "2. is-string-blank",
+ "2. is-svg-path",
+ "2. left-pad",
+ "2. mat4-interpolate",
+ "2. math-log2",
+ "2. minimist",
+ "2. monotone-convex-hull-2d",
+ "2. mouse-change",
+ "2. mouse-event",
+ "2. mouse-event-offset",
+ "2. mouse-wheel",
+ "2. ndarray",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. ndarray-warp",
+ "2. normals",
+ "2. object-assign",
+ "2. optical-properties",
+ "2. parse-rect",
+ "2. parse-svg-path",
+ "2. parse-unit",
+ "2. pbf",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. polytope-closest-point",
+ "2. quickselect",
+ "2. raf",
+ "2. regl",
+ "2. regl-scatter2d",
+ "2. resolve",
+ "2. right-now",
+ "2. robust-scale",
+ "2. robust-subtract",
+ "2. robust-sum",
+ "2. rw",
+ "2. shuffle-seed",
+ "2. signum",
+ "2. simplicial-complex-boundary",
+ "2. simplicial-complex-contour",
+ "2. sort-object",
+ "2. stack-trace",
+ "2. static-eval",
+ "2. supercluster",
+ "2. surface-nets",
+ "2. svg-path-bounds",
+ "2. text-cache",
+ "2. through2",
+ "2. tiny-sdf",
+ "2. tinyqueue",
+ "2. to-float32",
+ "2. to-px",
+ "2. two-product",
+ "2. typedarray-pool",
+ "2. uniq",
+ "2. update-diff",
+ "2. vectorize-text",
+ "2. vt-pbf",
+ "2. xtend",
+ "3. @choojs/findup",
+ "3. @mapbox/geojson-area",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/vector-tile",
+ "3. abs-svg-path",
+ "3. acorn",
+ "3. array-bounds",
+ "3. big-rat",
+ "3. binary-search-bounds",
+ "3. bit-twiddle",
+ "3. boundary-cells",
+ "3. box-intersect",
+ "3. buble",
+ "3. buffer-from",
+ "3. cdt2d",
+ "3. circumradius",
+ "3. clamp",
+ "3. clean-pslg",
+ "3. color-id",
+ "3. color-parse",
+ "3. color-space",
+ "3. concat-stream",
+ "3. css-font",
+ "3. css-font-size-keywords",
+ "3. css-font-stretch-keywords",
+ "3. css-font-style-keywords",
+ "3. css-font-weight-keywords",
+ "3. css-global-keywords",
+ "3. css-system-font-keywords",
+ "3. cwise",
+ "3. cwise-compiler",
+ "3. cwise-parser",
+ "3. d",
+ "3. d3-path",
+ "3. delaunay-triangulate",
+ "3. dtype",
+ "3. dup",
+ "3. end-of-stream",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. es6-weak-map",
+ "3. escodegen",
+ "3. events",
+ "3. extend-shallow",
+ "3. extract-frustum-planes",
+ "3. foreach",
+ "3. gl-buffer",
+ "3. gl-fbo",
+ "3. gl-format-compiler-error",
+ "3. gl-mat2",
+ "3. gl-mat3",
+ "3. gl-mat4",
+ "3. gl-shader",
+ "3. gl-state",
+ "3. gl-texture2d",
+ "3. gl-vao",
+ "3. gl-vec3",
+ "3. gl-vec4",
+ "3. glsl-inject-defines",
+ "3. glsl-resolve",
+ "3. glsl-specular-beckmann",
+ "3. glsl-token-defines",
+ "3. glsl-token-depth",
+ "3. glsl-token-descope",
+ "3. glsl-token-scope",
+ "3. glsl-token-string",
+ "3. glsl-token-whitespace-trim",
+ "3. glsl-tokenizer",
+ "3. glslify",
+ "3. graceful-fs",
+ "3. ieee754",
+ "3. inherits",
+ "3. is-browser",
+ "3. is-firefox",
+ "3. is-plain-obj",
+ "3. is-svg-path",
+ "3. isarray",
+ "3. js-yaml",
+ "3. kdbush",
+ "3. kind-of",
+ "3. lerp",
+ "3. map-limit",
+ "3. marching-simplex-table",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. matrix-camera-controller",
+ "3. minimist",
+ "3. murmurhash-js",
+ "3. ndarray",
+ "3. ndarray-extract-contour",
+ "3. ndarray-linear-interpolate",
+ "3. ndarray-ops",
+ "3. ndarray-sort",
+ "3. nextafter",
+ "3. normalize-svg-path",
+ "3. number-is-integer",
+ "3. numeric",
+ "3. object-assign",
+ "3. object-keys",
+ "3. orbit-camera-controller",
+ "3. parse-svg-path",
+ "3. parse-unit",
+ "3. path-parse",
+ "3. pbf",
+ "3. performance-now",
+ "3. pick-by-alias",
+ "3. planar-graph-to-polyline",
+ "3. pxls",
+ "3. quantize",
+ "3. quat-slerp",
+ "3. quote-stream",
+ "3. rat-vec",
+ "3. readable-stream",
+ "3. reduce-simplicial-complex",
+ "3. resolve",
+ "3. resolve-protobuf-schema",
+ "3. robust-in-sphere",
+ "3. robust-linear-solve",
+ "3. robust-orientation",
+ "3. robust-segment-intersect",
+ "3. safe-buffer",
+ "3. seedrandom",
+ "3. shallow-copy",
+ "3. sharkdown",
+ "3. simplicial-complex",
+ "3. simplify-planar-graph",
+ "3. sort-asc",
+ "3. sort-desc",
+ "3. split-polygon",
+ "3. static-module",
+ "3. stream-shift",
+ "3. string-split-by",
+ "3. strip-bom-string",
+ "3. strongly-connected-components",
+ "3. surface-nets",
+ "3. through2",
+ "3. triangulate-hypercube",
+ "3. triangulate-polyline",
+ "3. turntable-camera-controller",
+ "3. two-product",
+ "3. two-sum",
+ "3. typedarray",
+ "3. typedarray-pool",
+ "3. uglify-js",
+ "3. union-find",
+ "3. uniq",
+ "3. unquote",
+ "3. vectorize-text",
+ "3. weak-map",
+ "3. weakmap-shim",
+ "3. xtend",
+ "3. zero-crossings",
+ "4. acorn",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. add-line-numbers",
+ "4. argparse",
+ "4. arr-flatten",
+ "4. big-rat",
+ "4. bit-twiddle",
+ "4. bn.js",
+ "4. buffer-equal",
+ "4. cardinal",
+ "4. cdt2d",
+ "4. cell-orientation",
+ "4. chalk",
+ "4. circumcenter",
+ "4. color-name",
+ "4. commander",
+ "4. compare-cell",
+ "4. compare-oriented-cell",
+ "4. compute-dims",
+ "4. concat-stream",
+ "4. convert-source-map",
+ "4. convex-hull",
+ "4. core-util-is",
+ "4. cwise-compiler",
+ "4. d",
+ "4. defined",
+ "4. double-bits",
+ "4. duplexer2",
+ "4. edges-to-adjacency-list",
+ "4. es5-ext",
+ "4. es6-iterator",
+ "4. es6-symbol",
+ "4. escodegen",
+ "4. esprima",
+ "4. estraverse",
+ "4. esutils",
+ "4. expect.js",
+ "4. falafel",
+ "4. filtered-vector",
+ "4. flip-pixels",
+ "4. gamma",
+ "4. gl-constants",
+ "4. gl-mat4",
+ "4. gl-quat",
+ "4. gl-vec3",
+ "4. glsl-shader-name",
+ "4. glsl-token-assignments",
+ "4. glsl-token-depth",
+ "4. glsl-token-inject-block",
+ "4. glsl-token-properties",
+ "4. glsl-token-scope",
+ "4. glsl-token-string",
+ "4. glsl-tokenizer",
+ "4. has",
+ "4. hsluv",
+ "4. inherits",
+ "4. is-browser",
+ "4. is-buffer",
+ "4. is-extendable",
+ "4. is-finite",
+ "4. is-plain-obj",
+ "4. isarray",
+ "4. magic-string",
+ "4. merge-source-map",
+ "4. minimist",
+ "4. mumath",
+ "4. next-tick",
+ "4. object-inspect",
+ "4. once",
+ "4. optionator",
+ "4. os-homedir",
+ "4. parenthesis",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. process-nextick-args",
+ "4. protocol-buffers-schema",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. robust-determinant",
+ "4. robust-dot-product",
+ "4. robust-orientation",
+ "4. robust-scale",
+ "4. robust-subtract",
+ "4. robust-sum",
+ "4. safe-buffer",
+ "4. shallow-copy",
+ "4. simplicial-complex",
+ "4. source-map",
+ "4. split",
+ "4. sprintf-js",
+ "4. static-eval",
+ "4. stream-spigot",
+ "4. string_decoder",
+ "4. svg-arc-to-cubic-bezier",
+ "4. tape",
+ "4. through",
+ "4. through2",
+ "4. to-uint8",
+ "4. two-product",
+ "4. typedarray-pool",
+ "4. uglify-to-browserify",
+ "4. union-find",
+ "4. uniq",
+ "4. util-deprecate",
+ "4. vlq",
+ "4. wgs84",
+ "4. yargs",
+ "5. acorn",
+ "5. almost-equal",
+ "5. ansi-styles",
+ "5. ansicolors",
+ "5. arr-flatten",
+ "5. atob-lite",
+ "5. binary-search-bounds",
+ "5. bit-twiddle",
+ "5. camelcase",
+ "5. cell-orientation",
+ "5. clamp",
+ "5. cliui",
+ "5. compare-angle",
+ "5. compare-cell",
+ "5. core-util-is",
+ "5. cubic-hermite",
+ "5. decamelize",
+ "5. deep-equal",
+ "5. deep-is",
+ "5. defined",
+ "5. dup",
+ "5. escape-string-regexp",
+ "5. escodegen",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. fast-levenshtein",
+ "5. for-each",
+ "5. function-bind",
+ "5. gl-mat3",
+ "5. gl-vec3",
+ "5. gl-vec4",
+ "5. glob",
+ "5. glsl-tokenizer",
+ "5. has",
+ "5. inherits",
+ "5. interval-tree-1d",
+ "5. invert-permutation",
+ "5. is-base64",
+ "5. is-float-array",
+ "5. isarray",
+ "5. levn",
+ "5. minimist",
+ "5. number-is-nan",
+ "5. object-inspect",
+ "5. optionator",
+ "5. pad-left",
+ "5. prelude-ls",
+ "5. readable-stream",
+ "5. redeyed",
+ "5. resolve",
+ "5. resumer",
+ "5. robust-compress",
+ "5. robust-linear-solve",
+ "5. robust-orientation",
+ "5. robust-scale",
+ "5. robust-sum",
+ "5. safe-buffer",
+ "5. slab-decomposition",
+ "5. source-map",
+ "5. sprintf-js",
+ "5. string.prototype.trim",
+ "5. string_decoder",
+ "5. supports-color",
+ "5. through",
+ "5. through2",
+ "5. to-array-buffer",
+ "5. two-product",
+ "5. type-check",
+ "5. typedarray-pool",
+ "5. union-find",
+ "5. uniq",
+ "5. utils-copy",
+ "5. validate.io-array",
+ "5. validate.io-matrix-like",
+ "5. validate.io-ndarray-like",
+ "5. validate.io-positive-integer",
+ "5. vlq",
+ "5. window-size",
+ "5. wordwrap",
+ "5. wrappy",
+ "5. xtend",
+ "6. amdefine",
+ "6. binary-search-bounds",
+ "6. center-align",
+ "6. color-convert",
+ "6. const-pinf-float64",
+ "6. core-util-is",
+ "6. define-properties",
+ "6. es-abstract",
+ "6. esprima",
+ "6. estraverse",
+ "6. flatten-vertex-data",
+ "6. fs.realpath",
+ "6. function-bind",
+ "6. functional-red-black-tree",
+ "6. has-flag",
+ "6. inflight",
+ "6. inherits",
+ "6. is-blob",
+ "6. is-callable",
+ "6. isarray",
+ "6. minimatch",
+ "6. object-keys",
+ "6. once",
+ "6. path-is-absolute",
+ "6. prelude-ls",
+ "6. readable-stream",
+ "6. repeat-string",
+ "6. right-align",
+ "6. robust-orientation",
+ "6. robust-product",
+ "6. robust-sum",
+ "6. signum",
+ "6. source-map",
+ "6. string-to-arraybuffer",
+ "6. string_decoder",
+ "6. through",
+ "6. two-sum",
+ "6. type-check",
+ "6. type-name",
+ "6. utils-copy-error",
+ "6. utils-indexof",
+ "6. utils-regex-from-string",
+ "6. validate.io-array",
+ "6. validate.io-buffer",
+ "6. validate.io-integer",
+ "6. validate.io-nonnegative-integer",
+ "6. wordwrap",
+ "6. xtend"
+ ],
+ "parents": [
+ "",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. @plotly/d3-sankey",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. alpha-shape",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. color-normalize",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. convex-hull",
+ "1. convex-hull",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. delaunay-triangulate",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-select-box",
+ "1. gl-select-box",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-wheel",
+ "1. mouse-wheel",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. d3-shape",
+ "2. alpha-complex",
+ "2. alpha-complex",
+ "2. simplicial-complex-boundary",
+ "2. simplicial-complex-boundary",
+ "2. color-rgba",
+ "2. color-rgba",
+ "2. color-rgba",
+ "2. affine-hull",
+ "2. incremental-convex-hull",
+ "2. incremental-convex-hull",
+ "2. monotone-convex-hull-2d",
+ "2. elementary-circuits-directed-graph",
+ "2. gl-shader",
+ "2. gl-shader",
+ "2. cdt2d",
+ "2. cdt2d",
+ "2. cdt2d",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. gl-buffer",
+ "2. gl-buffer",
+ "2. gl-buffer",
+ "2. surface-nets",
+ "2. surface-nets",
+ "2. surface-nets",
+ "2. typedarray-pool",
+ "2. typedarray-pool",
+ "2. gl-texture2d",
+ "2. gl-texture2d",
+ "2. gl-texture2d",
+ "2. barycentric",
+ "2. colormap",
+ "2. glsl-specular-cook-torrance",
+ "2. polytope-closest-point",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. text-cache",
+ "2. 3d-view",
+ "2. 3d-view",
+ "2. 3d-view",
+ "2. a-big-triangle",
+ "2. a-big-triangle",
+ "2. a-big-triangle",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-fbo",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. ndarray-gradient",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. ndarray-scratch",
+ "2. ndarray-scratch",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. parse-rect",
+ "2. to-px",
+ "2. bl",
+ "2. bl",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. duplexify",
+ "2. duplexify",
+ "2. duplexify",
+ "2. duplexify",
+ "2. falafel",
+ "2. falafel",
+ "2. falafel",
+ "2. falafel",
+ "2. from2",
+ "2. from2",
+ "2. glsl-resolve",
+ "2. glsl-resolve",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. through2",
+ "2. through2",
+ "2. resolve",
+ "2. static-eval",
+ "2. @mapbox/vector-tile",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. brfs",
+ "2. brfs",
+ "2. brfs",
+ "2. brfs",
+ "2. through2",
+ "2. through2",
+ "2. pbf",
+ "2. pbf",
+ "2. shuffle-seed",
+ "2. sort-object",
+ "2. sort-object",
+ "2. supercluster",
+ "2. vt-pbf",
+ "2. vt-pbf",
+ "2. vt-pbf",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. cwise",
+ "2. cwise",
+ "2. cwise",
+ "2. cwise",
+ "2. gl-matrix-invert",
+ "2. gl-matrix-invert",
+ "2. gl-matrix-invert",
+ "2. ndarray-warp",
+ "2. ndarray-warp",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. color-id",
+ "2. image-palette",
+ "2. image-palette",
+ "2. image-palette",
+ "2. color-alpha",
+ "2. raf",
+ "2. robust-scale",
+ "2. robust-scale",
+ "2. bitmap-sdf",
+ "2. draw-svg-path",
+ "2. draw-svg-path",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "3. circumradius",
+ "3. boundary-cells",
+ "3. reduce-simplicial-complex",
+ "3. reduce-simplicial-complex",
+ "3. reduce-simplicial-complex",
+ "3. color-parse",
+ "3. color-parse",
+ "3. color-parse",
+ "3. color-space",
+ "3. color-space",
+ "3. simplicial-complex",
+ "3. simplicial-complex",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. big-rat",
+ "3. big-rat",
+ "3. big-rat",
+ "3. box-intersect",
+ "3. box-intersect",
+ "3. nextafter",
+ "3. rat-vec",
+ "3. robust-segment-intersect",
+ "3. ndarray-extract-contour",
+ "3. triangulate-hypercube",
+ "3. triangulate-hypercube",
+ "3. triangulate-hypercube",
+ "3. zero-crossings",
+ "3. robust-linear-solve",
+ "3. marching-simplex-table",
+ "3. ndarray-sort",
+ "3. orbit-camera-controller",
+ "3. orbit-camera-controller",
+ "3. turntable-camera-controller",
+ "3. turntable-camera-controller",
+ "3. turntable-camera-controller",
+ "3. gl-state",
+ "3. split-polygon",
+ "3. split-polygon",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. simplify-planar-graph",
+ "3. simplify-planar-graph",
+ "3. triangulate-polyline",
+ "3. cwise-compiler",
+ "3. string-split-by",
+ "3. d",
+ "3. es5-ext",
+ "3. es5-ext",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-iterator",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. es6-symbol",
+ "3. number-is-integer",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. end-of-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. glsl-inject-defines",
+ "3. glsl-inject-defines",
+ "3. glsl-inject-defines",
+ "3. glsl-token-defines",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-tokenizer",
+ "3. @choojs/findup",
+ "3. map-limit",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. @mapbox/geojson-area",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. extend-shallow",
+ "3. js-yaml",
+ "3. js-yaml",
+ "3. quote-stream",
+ "3. quote-stream",
+ "3. quote-stream",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. resolve-protobuf-schema",
+ "3. mat4-decompose",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. quat-slerp",
+ "3. cwise-parser",
+ "3. cwise-parser",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. uglify-js",
+ "3. uglify-js",
+ "3. uglify-js",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. normalize-svg-path",
+ "4. circumcenter",
+ "4. circumcenter",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. compare-oriented-cell",
+ "4. compare-oriented-cell",
+ "4. mumath",
+ "4. add-line-numbers",
+ "4. glsl-shader-name",
+ "4. glsl-shader-name",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. permutation-rank",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. filtered-vector",
+ "4. filtered-vector",
+ "4. robust-dot-product",
+ "4. robust-dot-product",
+ "4. edges-to-adjacency-list",
+ "4. planar-dual",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. simplicial-complex",
+ "4. simplicial-complex",
+ "4. is-finite",
+ "4. string_decoder",
+ "4. string_decoder",
+ "4. string_decoder",
+ "4. once",
+ "4. string_decoder",
+ "4. through2",
+ "4. through2",
+ "4. once",
+ "4. string_decoder",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. cardinal",
+ "4. cardinal",
+ "4. split",
+ "4. stream-spigot",
+ "4. argparse",
+ "4. has",
+ "4. magic-string",
+ "4. duplexer2",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. merge-source-map",
+ "4. string_decoder",
+ "4. gl-quat",
+ "4. gl-quat",
+ "4. gl-quat",
+ "4. duplexer2",
+ "4. quote-stream",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. static-eval",
+ "4. through2",
+ "4. through2",
+ "4. yargs",
+ "4. yargs",
+ "4. yargs",
+ "4. yargs",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. chalk",
+ "4. chalk",
+ "4. chalk",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8"
+ ],
+ "labels": [
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. array-range",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. country-regex",
+ "1. d3",
+ "1. d3-force",
+ "1. d3-hierarchy",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. es6-promise",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-mat4",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-spikes2d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-event-offset",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. polybooljs",
+ "1. regl",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. right-now",
+ "1. robust-orientation",
+ "1. sane-topojson",
+ "1. strongly-connected-components",
+ "1. superscript-text",
+ "1. svg-path-sdf",
+ "1. tinycolor2",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-shape",
+ "2. alpha-complex",
+ "2. simplicial-complex-boundary",
+ "2. element-size",
+ "2. clamp",
+ "2. color-rgba",
+ "2. dtype",
+ "2. affine-hull",
+ "2. incremental-convex-hull",
+ "2. monotone-convex-hull-2d",
+ "2. d3-collection",
+ "2. d3-dispatch",
+ "2. d3-quadtree",
+ "2. d3-timer",
+ "2. d3-color",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-shape",
+ "2. elementary-circuits-directed-graph",
+ "2. incremental-convex-hull",
+ "2. uniq",
+ "2. is-string-blank",
+ "2. optical-properties",
+ "2. tiny-sdf",
+ "2. gl-shader",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. cdt2d",
+ "2. clean-pslg",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. glslify",
+ "2. iota-array",
+ "2. ndarray",
+ "2. surface-nets",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. glslify",
+ "2. iota-array",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-read-float",
+ "2. glslify",
+ "2. ndarray",
+ "2. barycentric",
+ "2. colormap",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-specular-cook-torrance",
+ "2. glslify",
+ "2. ndarray",
+ "2. normals",
+ "2. polytope-closest-point",
+ "2. simplicial-complex-contour",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. glsl-inverse",
+ "2. glslify",
+ "2. text-cache",
+ "2. 3d-view",
+ "2. a-big-triangle",
+ "2. gl-axes3d",
+ "2. gl-fbo",
+ "2. gl-mat4",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. gl-spikes3d",
+ "2. glslify",
+ "2. has-passive-events",
+ "2. is-mobile",
+ "2. mouse-change",
+ "2. mouse-event-offset",
+ "2. mouse-wheel",
+ "2. ndarray",
+ "2. right-now",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. glslify",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. is-string-blank",
+ "2. typedarray-pool",
+ "2. vectorize-text",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. glslify",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. bit-twiddle",
+ "2. colormap",
+ "2. dup",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. binary-search-bounds",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-specular-beckmann",
+ "2. glslify",
+ "2. ndarray",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. surface-nets",
+ "2. typedarray-pool",
+ "2. bit-twiddle",
+ "2. color-normalize",
+ "2. css-font",
+ "2. detect-kerning",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. gl-util",
+ "2. is-plain-obj",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. parse-unit",
+ "2. pick-by-alias",
+ "2. regl",
+ "2. to-px",
+ "2. typedarray-pool",
+ "2. bl",
+ "2. concat-stream",
+ "2. duplexify",
+ "2. falafel",
+ "2. from2",
+ "2. glsl-resolve",
+ "2. glsl-token-whitespace-trim",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. through2",
+ "2. minimist",
+ "2. resolve",
+ "2. stack-trace",
+ "2. static-eval",
+ "2. xtend",
+ "2. is-browser",
+ "2. is-browser",
+ "2. @mapbox/gl-matrix",
+ "2. @mapbox/jsonlint-lines-primitives",
+ "2. @mapbox/mapbox-gl-supported",
+ "2. @mapbox/point-geometry",
+ "2. @mapbox/shelf-pack",
+ "2. @mapbox/tiny-sdf",
+ "2. @mapbox/unitbezier",
+ "2. @mapbox/vector-tile",
+ "2. @mapbox/whoots-js",
+ "2. csscolorparser",
+ "2. earcut",
+ "2. geojson-rewind",
+ "2. geojson-vt",
+ "2. gray-matter",
+ "2. grid-index",
+ "2. brfs",
+ "2. minimist",
+ "2. through2",
+ "2. pbf",
+ "2. quickselect",
+ "2. rw",
+ "2. shuffle-seed",
+ "2. sort-object",
+ "2. supercluster",
+ "2. tinyqueue",
+ "2. vt-pbf",
+ "2. binary-search-bounds",
+ "2. gl-mat4",
+ "2. gl-vec3",
+ "2. mat4-interpolate",
+ "2. mouse-event",
+ "2. signum",
+ "2. right-now",
+ "2. to-px",
+ "2. iota-array",
+ "2. is-buffer",
+ "2. cwise",
+ "2. gl-matrix-invert",
+ "2. ndarray-warp",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. clamp",
+ "2. dtype",
+ "2. flatten-vertex-data",
+ "2. is-obj",
+ "2. math-log2",
+ "2. parse-rect",
+ "2. binary-search-bounds",
+ "2. array-bounds",
+ "2. bubleify",
+ "2. color-normalize",
+ "2. flatten-vertex-data",
+ "2. object-assign",
+ "2. pick-by-alias",
+ "2. to-float32",
+ "2. update-diff",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. color-normalize",
+ "2. earcut",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. glslify",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. to-float32",
+ "2. array-range",
+ "2. array-rearrange",
+ "2. clamp",
+ "2. color-id",
+ "2. color-normalize",
+ "2. color-rgba",
+ "2. flatten-vertex-data",
+ "2. glslify",
+ "2. image-palette",
+ "2. is-iexplorer",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. to-float32",
+ "2. update-diff",
+ "2. array-bounds",
+ "2. array-range",
+ "2. bubleify",
+ "2. color-alpha",
+ "2. defined",
+ "2. flatten-vertex-data",
+ "2. left-pad",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. raf",
+ "2. regl-scatter2d",
+ "2. robust-scale",
+ "2. robust-subtract",
+ "2. robust-sum",
+ "2. two-product",
+ "2. bitmap-sdf",
+ "2. draw-svg-path",
+ "2. is-svg-path",
+ "2. parse-svg-path",
+ "2. svg-path-bounds",
+ "2. commander",
+ "2. get-canvas-context",
+ "2. object-assign",
+ "3. d3-path",
+ "3. circumradius",
+ "3. delaunay-triangulate",
+ "3. boundary-cells",
+ "3. reduce-simplicial-complex",
+ "3. clamp",
+ "3. color-parse",
+ "3. color-space",
+ "3. robust-orientation",
+ "3. robust-orientation",
+ "3. simplicial-complex",
+ "3. robust-orientation",
+ "3. strongly-connected-components",
+ "3. gl-format-compiler-error",
+ "3. weakmap-shim",
+ "3. binary-search-bounds",
+ "3. robust-in-sphere",
+ "3. robust-orientation",
+ "3. big-rat",
+ "3. box-intersect",
+ "3. nextafter",
+ "3. rat-vec",
+ "3. robust-segment-intersect",
+ "3. union-find",
+ "3. uniq",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. ndarray-extract-contour",
+ "3. triangulate-hypercube",
+ "3. zero-crossings",
+ "3. bit-twiddle",
+ "3. dup",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. robust-linear-solve",
+ "3. lerp",
+ "3. glsl-specular-beckmann",
+ "3. numeric",
+ "3. marching-simplex-table",
+ "3. ndarray",
+ "3. ndarray-sort",
+ "3. typedarray-pool",
+ "3. bit-twiddle",
+ "3. cwise",
+ "3. gl-fbo",
+ "3. ndarray",
+ "3. typedarray-pool",
+ "3. vectorize-text",
+ "3. matrix-camera-controller",
+ "3. orbit-camera-controller",
+ "3. turntable-camera-controller",
+ "3. gl-buffer",
+ "3. gl-vao",
+ "3. weak-map",
+ "3. bit-twiddle",
+ "3. dup",
+ "3. extract-frustum-planes",
+ "3. gl-buffer",
+ "3. gl-mat4",
+ "3. gl-shader",
+ "3. gl-state",
+ "3. gl-vao",
+ "3. gl-vec4",
+ "3. glslify",
+ "3. robust-orientation",
+ "3. split-polygon",
+ "3. vectorize-text",
+ "3. gl-texture2d",
+ "3. gl-buffer",
+ "3. gl-shader",
+ "3. gl-vao",
+ "3. glslify",
+ "3. cdt2d",
+ "3. clean-pslg",
+ "3. ndarray",
+ "3. planar-graph-to-polyline",
+ "3. simplify-planar-graph",
+ "3. surface-nets",
+ "3. triangulate-polyline",
+ "3. cwise-compiler",
+ "3. dup",
+ "3. cwise-compiler",
+ "3. cwise-compiler",
+ "3. ndarray",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. css-font-size-keywords",
+ "3. css-font-stretch-keywords",
+ "3. css-font-style-keywords",
+ "3. css-font-weight-keywords",
+ "3. css-global-keywords",
+ "3. css-system-font-keywords",
+ "3. pick-by-alias",
+ "3. string-split-by",
+ "3. unquote",
+ "3. d",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. dtype",
+ "3. css-font",
+ "3. css-font",
+ "3. es6-weak-map",
+ "3. is-browser",
+ "3. is-firefox",
+ "3. is-plain-obj",
+ "3. number-is-integer",
+ "3. object-assign",
+ "3. pick-by-alias",
+ "3. pick-by-alias",
+ "3. parse-unit",
+ "3. readable-stream",
+ "3. safe-buffer",
+ "3. buffer-from",
+ "3. readable-stream",
+ "3. inherits",
+ "3. typedarray",
+ "3. readable-stream",
+ "3. end-of-stream",
+ "3. inherits",
+ "3. stream-shift",
+ "3. acorn",
+ "3. foreach",
+ "3. isarray",
+ "3. object-keys",
+ "3. readable-stream",
+ "3. inherits",
+ "3. resolve",
+ "3. xtend",
+ "3. glsl-inject-defines",
+ "3. glsl-token-defines",
+ "3. glsl-token-depth",
+ "3. glsl-token-descope",
+ "3. glsl-token-scope",
+ "3. glsl-token-string",
+ "3. glsl-token-whitespace-trim",
+ "3. glsl-tokenizer",
+ "3. murmurhash-js",
+ "3. shallow-copy",
+ "3. @choojs/findup",
+ "3. events",
+ "3. glsl-resolve",
+ "3. glsl-tokenizer",
+ "3. graceful-fs",
+ "3. inherits",
+ "3. map-limit",
+ "3. resolve",
+ "3. readable-stream",
+ "3. xtend",
+ "3. path-parse",
+ "3. escodegen",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/geojson-area",
+ "3. concat-stream",
+ "3. minimist",
+ "3. sharkdown",
+ "3. extend-shallow",
+ "3. kind-of",
+ "3. js-yaml",
+ "3. strip-bom-string",
+ "3. quote-stream",
+ "3. static-module",
+ "3. through2",
+ "3. resolve",
+ "3. readable-stream",
+ "3. xtend",
+ "3. ieee754",
+ "3. resolve-protobuf-schema",
+ "3. seedrandom",
+ "3. sort-asc",
+ "3. sort-desc",
+ "3. kdbush",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/vector-tile",
+ "3. pbf",
+ "3. gl-mat4",
+ "3. gl-vec3",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. quat-slerp",
+ "3. cwise-compiler",
+ "3. cwise-parser",
+ "3. static-module",
+ "3. uglify-js",
+ "3. gl-mat2",
+ "3. gl-mat3",
+ "3. gl-mat4",
+ "3. cwise",
+ "3. ndarray-linear-interpolate",
+ "3. array-bounds",
+ "3. buble",
+ "3. clamp",
+ "3. color-id",
+ "3. pxls",
+ "3. quantize",
+ "3. color-parse",
+ "3. performance-now",
+ "3. two-product",
+ "3. two-sum",
+ "3. clamp",
+ "3. abs-svg-path",
+ "3. normalize-svg-path",
+ "3. abs-svg-path",
+ "3. is-svg-path",
+ "3. parse-svg-path",
+ "3. normalize-svg-path",
+ "4. circumcenter",
+ "4. tape",
+ "4. cell-orientation",
+ "4. compare-cell",
+ "4. compare-oriented-cell",
+ "4. color-name",
+ "4. defined",
+ "4. is-plain-obj",
+ "4. hsluv",
+ "4. mumath",
+ "4. bit-twiddle",
+ "4. union-find",
+ "4. add-line-numbers",
+ "4. gl-constants",
+ "4. glsl-shader-name",
+ "4. sprintf-js",
+ "4. robust-scale",
+ "4. robust-subtract",
+ "4. robust-sum",
+ "4. two-product",
+ "4. bit-twiddle",
+ "4. bn.js",
+ "4. double-bits",
+ "4. bit-twiddle",
+ "4. typedarray-pool",
+ "4. double-bits",
+ "4. big-rat",
+ "4. robust-orientation",
+ "4. typedarray-pool",
+ "4. gamma",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. cwise-compiler",
+ "4. robust-determinant",
+ "4. convex-hull",
+ "4. typedarray-pool",
+ "4. filtered-vector",
+ "4. gl-mat4",
+ "4. filtered-vector",
+ "4. gl-mat4",
+ "4. gl-vec3",
+ "4. uniq",
+ "4. robust-dot-product",
+ "4. robust-sum",
+ "4. edges-to-adjacency-list",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. robust-orientation",
+ "4. robust-sum",
+ "4. two-product",
+ "4. uniq",
+ "4. robust-orientation",
+ "4. simplicial-complex",
+ "4. cdt2d",
+ "4. uniq",
+ "4. parenthesis",
+ "4. es5-ext",
+ "4. es6-iterator",
+ "4. es6-symbol",
+ "4. next-tick",
+ "4. d",
+ "4. es5-ext",
+ "4. es6-symbol",
+ "4. d",
+ "4. es5-ext",
+ "4. is-finite",
+ "4. isarray",
+ "4. string_decoder",
+ "4. core-util-is",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. isarray",
+ "4. string_decoder",
+ "4. core-util-is",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. once",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. glsl-token-inject-block",
+ "4. glsl-token-string",
+ "4. glsl-tokenizer",
+ "4. glsl-tokenizer",
+ "4. glsl-token-assignments",
+ "4. glsl-token-depth",
+ "4. glsl-token-properties",
+ "4. glsl-token-scope",
+ "4. through2",
+ "4. commander",
+ "4. once",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. esprima",
+ "4. estraverse",
+ "4. esutils",
+ "4. optionator",
+ "4. source-map",
+ "4. wgs84",
+ "4. cardinal",
+ "4. expect.js",
+ "4. minimist",
+ "4. split",
+ "4. stream-spigot",
+ "4. through",
+ "4. is-extendable",
+ "4. argparse",
+ "4. esprima",
+ "4. buffer-equal",
+ "4. minimist",
+ "4. through2",
+ "4. concat-stream",
+ "4. convert-source-map",
+ "4. falafel",
+ "4. has",
+ "4. magic-string",
+ "4. duplexer2",
+ "4. escodegen",
+ "4. object-inspect",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. through2",
+ "4. merge-source-map",
+ "4. shallow-copy",
+ "4. static-eval",
+ "4. core-util-is",
+ "4. inherits",
+ "4. isarray",
+ "4. string_decoder",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. protocol-buffers-schema",
+ "4. gl-mat4",
+ "4. gl-vec3",
+ "4. gl-mat4",
+ "4. gl-quat",
+ "4. esprima",
+ "4. uniq",
+ "4. concat-stream",
+ "4. duplexer2",
+ "4. falafel",
+ "4. has",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. shallow-copy",
+ "4. escodegen",
+ "4. object-inspect",
+ "4. static-eval",
+ "4. through2",
+ "4. source-map",
+ "4. uglify-to-browserify",
+ "4. yargs",
+ "4. acorn",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. vlq",
+ "4. chalk",
+ "4. magic-string",
+ "4. minimist",
+ "4. os-homedir",
+ "4. arr-flatten",
+ "4. compute-dims",
+ "4. flip-pixels",
+ "4. is-browser",
+ "4. is-buffer",
+ "4. to-uint8",
+ "4. svg-arc-to-cubic-bezier",
+ "5. dup",
+ "5. robust-linear-solve",
+ "5. deep-equal",
+ "5. defined",
+ "5. for-each",
+ "5. function-bind",
+ "5. glob",
+ "5. has",
+ "5. inherits",
+ "5. minimist",
+ "5. object-inspect",
+ "5. resolve",
+ "5. resumer",
+ "5. string.prototype.trim",
+ "5. through",
+ "5. cell-orientation",
+ "5. compare-cell",
+ "5. almost-equal",
+ "5. pad-left",
+ "5. atob-lite",
+ "5. glsl-tokenizer",
+ "5. typedarray-pool",
+ "5. invert-permutation",
+ "5. typedarray-pool",
+ "5. robust-compress",
+ "5. robust-scale",
+ "5. robust-sum",
+ "5. two-product",
+ "5. binary-search-bounds",
+ "5. cubic-hermite",
+ "5. robust-sum",
+ "5. two-product",
+ "5. uniq",
+ "5. compare-angle",
+ "5. dup",
+ "5. binary-search-bounds",
+ "5. interval-tree-1d",
+ "5. robust-orientation",
+ "5. slab-decomposition",
+ "5. bit-twiddle",
+ "5. union-find",
+ "5. number-is-nan",
+ "5. safe-buffer",
+ "5. safe-buffer",
+ "5. safe-buffer",
+ "5. wrappy",
+ "5. safe-buffer",
+ "5. readable-stream",
+ "5. xtend",
+ "5. wrappy",
+ "5. safe-buffer",
+ "5. deep-is",
+ "5. fast-levenshtein",
+ "5. levn",
+ "5. prelude-ls",
+ "5. type-check",
+ "5. wordwrap",
+ "5. ansicolors",
+ "5. redeyed",
+ "5. through",
+ "5. readable-stream",
+ "5. sprintf-js",
+ "5. function-bind",
+ "5. vlq",
+ "5. readable-stream",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. optionator",
+ "5. source-map",
+ "5. source-map",
+ "5. safe-buffer",
+ "5. gl-mat3",
+ "5. gl-vec3",
+ "5. gl-vec4",
+ "5. readable-stream",
+ "5. minimist",
+ "5. through2",
+ "5. core-util-is",
+ "5. inherits",
+ "5. isarray",
+ "5. string_decoder",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. source-map",
+ "5. escodegen",
+ "5. readable-stream",
+ "5. xtend",
+ "5. camelcase",
+ "5. cliui",
+ "5. decamelize",
+ "5. window-size",
+ "5. acorn",
+ "5. acorn",
+ "5. ansi-styles",
+ "5. escape-string-regexp",
+ "5. supports-color",
+ "5. utils-copy",
+ "5. validate.io-array",
+ "5. validate.io-matrix-like",
+ "5. validate.io-ndarray-like",
+ "5. validate.io-positive-integer",
+ "5. arr-flatten",
+ "5. clamp",
+ "5. is-base64",
+ "5. is-float-array",
+ "5. to-array-buffer"
+ ]
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 800
+ }
+}
diff --git a/test/image/mocks/treemap_packages_colorscale_novalue.json b/test/image/mocks/treemap_packages_colorscale_novalue.json
new file mode 100644
index 00000000000..99d8aa5e21b
--- /dev/null
+++ b/test/image/mocks/treemap_packages_colorscale_novalue.json
@@ -0,0 +1,2329 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "maxdepth": 3,
+ "textinfo": "label+percent parent",
+ "hoverinfo": "all",
+ "marker": {
+ "line": {
+ "color": "#777"
+ },
+ "colorscale": [[0, "#FFF"], [0.01, "#FF0"], [0.1, "#F00"], [1, "#000"]],
+ "showscale": true
+ },
+ "ids": [
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. array-range",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. country-regex",
+ "1. d3",
+ "1. d3-force",
+ "1. d3-hierarchy",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. es6-promise",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-mat4",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-spikes2d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-event-offset",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. polybooljs",
+ "1. regl",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. right-now",
+ "1. robust-orientation",
+ "1. sane-topojson",
+ "1. strongly-connected-components",
+ "1. superscript-text",
+ "1. svg-path-sdf",
+ "1. tinycolor2",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. 3d-view",
+ "2. @mapbox/gl-matrix",
+ "2. @mapbox/jsonlint-lines-primitives",
+ "2. @mapbox/mapbox-gl-supported",
+ "2. @mapbox/point-geometry",
+ "2. @mapbox/shelf-pack",
+ "2. @mapbox/tiny-sdf",
+ "2. @mapbox/unitbezier",
+ "2. @mapbox/vector-tile",
+ "2. @mapbox/whoots-js",
+ "2. a-big-triangle",
+ "2. affine-hull",
+ "2. alpha-complex",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. array-range",
+ "2. array-rearrange",
+ "2. barycentric",
+ "2. binary-search-bounds",
+ "2. bit-twiddle",
+ "2. bitmap-sdf",
+ "2. bl",
+ "2. brfs",
+ "2. bubleify",
+ "2. cdt2d",
+ "2. clamp",
+ "2. clean-pslg",
+ "2. color-alpha",
+ "2. color-id",
+ "2. color-normalize",
+ "2. color-rgba",
+ "2. colormap",
+ "2. commander",
+ "2. concat-stream",
+ "2. css-font",
+ "2. csscolorparser",
+ "2. cwise",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-color",
+ "2. d3-dispatch",
+ "2. d3-quadtree",
+ "2. d3-shape",
+ "2. d3-timer",
+ "2. defined",
+ "2. detect-kerning",
+ "2. draw-svg-path",
+ "2. dtype",
+ "2. dup",
+ "2. duplexify",
+ "2. earcut",
+ "2. element-size",
+ "2. elementary-circuits-directed-graph",
+ "2. es6-weak-map",
+ "2. falafel",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. from2",
+ "2. geojson-rewind",
+ "2. geojson-vt",
+ "2. get-canvas-context",
+ "2. gl-axes3d",
+ "2. gl-buffer",
+ "2. gl-fbo",
+ "2. gl-mat4",
+ "2. gl-matrix-invert",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. gl-spikes3d",
+ "2. gl-texture2d",
+ "2. gl-util",
+ "2. gl-vao",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glsl-read-float",
+ "2. glsl-resolve",
+ "2. glsl-specular-beckmann",
+ "2. glsl-specular-cook-torrance",
+ "2. glsl-token-whitespace-trim",
+ "2. glslify",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. gray-matter",
+ "2. grid-index",
+ "2. has-passive-events",
+ "2. image-palette",
+ "2. incremental-convex-hull",
+ "2. iota-array",
+ "2. is-browser",
+ "2. is-buffer",
+ "2. is-iexplorer",
+ "2. is-mobile",
+ "2. is-obj",
+ "2. is-plain-obj",
+ "2. is-string-blank",
+ "2. is-svg-path",
+ "2. left-pad",
+ "2. mat4-interpolate",
+ "2. math-log2",
+ "2. minimist",
+ "2. monotone-convex-hull-2d",
+ "2. mouse-change",
+ "2. mouse-event",
+ "2. mouse-event-offset",
+ "2. mouse-wheel",
+ "2. ndarray",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. ndarray-warp",
+ "2. normals",
+ "2. object-assign",
+ "2. optical-properties",
+ "2. parse-rect",
+ "2. parse-svg-path",
+ "2. parse-unit",
+ "2. pbf",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. polytope-closest-point",
+ "2. quickselect",
+ "2. raf",
+ "2. regl",
+ "2. regl-scatter2d",
+ "2. resolve",
+ "2. right-now",
+ "2. robust-scale",
+ "2. robust-subtract",
+ "2. robust-sum",
+ "2. rw",
+ "2. shuffle-seed",
+ "2. signum",
+ "2. simplicial-complex-boundary",
+ "2. simplicial-complex-contour",
+ "2. sort-object",
+ "2. stack-trace",
+ "2. static-eval",
+ "2. supercluster",
+ "2. surface-nets",
+ "2. svg-path-bounds",
+ "2. text-cache",
+ "2. through2",
+ "2. tiny-sdf",
+ "2. tinyqueue",
+ "2. to-float32",
+ "2. to-px",
+ "2. two-product",
+ "2. typedarray-pool",
+ "2. uniq",
+ "2. update-diff",
+ "2. vectorize-text",
+ "2. vt-pbf",
+ "2. xtend",
+ "3. @choojs/findup",
+ "3. @mapbox/geojson-area",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/vector-tile",
+ "3. abs-svg-path",
+ "3. acorn",
+ "3. array-bounds",
+ "3. big-rat",
+ "3. binary-search-bounds",
+ "3. bit-twiddle",
+ "3. boundary-cells",
+ "3. box-intersect",
+ "3. buble",
+ "3. buffer-from",
+ "3. cdt2d",
+ "3. circumradius",
+ "3. clamp",
+ "3. clean-pslg",
+ "3. color-id",
+ "3. color-parse",
+ "3. color-space",
+ "3. concat-stream",
+ "3. css-font",
+ "3. css-font-size-keywords",
+ "3. css-font-stretch-keywords",
+ "3. css-font-style-keywords",
+ "3. css-font-weight-keywords",
+ "3. css-global-keywords",
+ "3. css-system-font-keywords",
+ "3. cwise",
+ "3. cwise-compiler",
+ "3. cwise-parser",
+ "3. d",
+ "3. d3-path",
+ "3. delaunay-triangulate",
+ "3. dtype",
+ "3. dup",
+ "3. end-of-stream",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. es6-weak-map",
+ "3. escodegen",
+ "3. events",
+ "3. extend-shallow",
+ "3. extract-frustum-planes",
+ "3. foreach",
+ "3. gl-buffer",
+ "3. gl-fbo",
+ "3. gl-format-compiler-error",
+ "3. gl-mat2",
+ "3. gl-mat3",
+ "3. gl-mat4",
+ "3. gl-shader",
+ "3. gl-state",
+ "3. gl-texture2d",
+ "3. gl-vao",
+ "3. gl-vec3",
+ "3. gl-vec4",
+ "3. glsl-inject-defines",
+ "3. glsl-resolve",
+ "3. glsl-specular-beckmann",
+ "3. glsl-token-defines",
+ "3. glsl-token-depth",
+ "3. glsl-token-descope",
+ "3. glsl-token-scope",
+ "3. glsl-token-string",
+ "3. glsl-token-whitespace-trim",
+ "3. glsl-tokenizer",
+ "3. glslify",
+ "3. graceful-fs",
+ "3. ieee754",
+ "3. inherits",
+ "3. is-browser",
+ "3. is-firefox",
+ "3. is-plain-obj",
+ "3. is-svg-path",
+ "3. isarray",
+ "3. js-yaml",
+ "3. kdbush",
+ "3. kind-of",
+ "3. lerp",
+ "3. map-limit",
+ "3. marching-simplex-table",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. matrix-camera-controller",
+ "3. minimist",
+ "3. murmurhash-js",
+ "3. ndarray",
+ "3. ndarray-extract-contour",
+ "3. ndarray-linear-interpolate",
+ "3. ndarray-ops",
+ "3. ndarray-sort",
+ "3. nextafter",
+ "3. normalize-svg-path",
+ "3. number-is-integer",
+ "3. numeric",
+ "3. object-assign",
+ "3. object-keys",
+ "3. orbit-camera-controller",
+ "3. parse-svg-path",
+ "3. parse-unit",
+ "3. path-parse",
+ "3. pbf",
+ "3. performance-now",
+ "3. pick-by-alias",
+ "3. planar-graph-to-polyline",
+ "3. pxls",
+ "3. quantize",
+ "3. quat-slerp",
+ "3. quote-stream",
+ "3. rat-vec",
+ "3. readable-stream",
+ "3. reduce-simplicial-complex",
+ "3. resolve",
+ "3. resolve-protobuf-schema",
+ "3. robust-in-sphere",
+ "3. robust-linear-solve",
+ "3. robust-orientation",
+ "3. robust-segment-intersect",
+ "3. safe-buffer",
+ "3. seedrandom",
+ "3. shallow-copy",
+ "3. sharkdown",
+ "3. simplicial-complex",
+ "3. simplify-planar-graph",
+ "3. sort-asc",
+ "3. sort-desc",
+ "3. split-polygon",
+ "3. static-module",
+ "3. stream-shift",
+ "3. string-split-by",
+ "3. strip-bom-string",
+ "3. strongly-connected-components",
+ "3. surface-nets",
+ "3. through2",
+ "3. triangulate-hypercube",
+ "3. triangulate-polyline",
+ "3. turntable-camera-controller",
+ "3. two-product",
+ "3. two-sum",
+ "3. typedarray",
+ "3. typedarray-pool",
+ "3. uglify-js",
+ "3. union-find",
+ "3. uniq",
+ "3. unquote",
+ "3. vectorize-text",
+ "3. weak-map",
+ "3. weakmap-shim",
+ "3. xtend",
+ "3. zero-crossings",
+ "4. acorn",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. add-line-numbers",
+ "4. argparse",
+ "4. arr-flatten",
+ "4. big-rat",
+ "4. bit-twiddle",
+ "4. bn.js",
+ "4. buffer-equal",
+ "4. cardinal",
+ "4. cdt2d",
+ "4. cell-orientation",
+ "4. chalk",
+ "4. circumcenter",
+ "4. color-name",
+ "4. commander",
+ "4. compare-cell",
+ "4. compare-oriented-cell",
+ "4. compute-dims",
+ "4. concat-stream",
+ "4. convert-source-map",
+ "4. convex-hull",
+ "4. core-util-is",
+ "4. cwise-compiler",
+ "4. d",
+ "4. defined",
+ "4. double-bits",
+ "4. duplexer2",
+ "4. edges-to-adjacency-list",
+ "4. es5-ext",
+ "4. es6-iterator",
+ "4. es6-symbol",
+ "4. escodegen",
+ "4. esprima",
+ "4. estraverse",
+ "4. esutils",
+ "4. expect.js",
+ "4. falafel",
+ "4. filtered-vector",
+ "4. flip-pixels",
+ "4. gamma",
+ "4. gl-constants",
+ "4. gl-mat4",
+ "4. gl-quat",
+ "4. gl-vec3",
+ "4. glsl-shader-name",
+ "4. glsl-token-assignments",
+ "4. glsl-token-depth",
+ "4. glsl-token-inject-block",
+ "4. glsl-token-properties",
+ "4. glsl-token-scope",
+ "4. glsl-token-string",
+ "4. glsl-tokenizer",
+ "4. has",
+ "4. hsluv",
+ "4. inherits",
+ "4. is-browser",
+ "4. is-buffer",
+ "4. is-extendable",
+ "4. is-finite",
+ "4. is-plain-obj",
+ "4. isarray",
+ "4. magic-string",
+ "4. merge-source-map",
+ "4. minimist",
+ "4. mumath",
+ "4. next-tick",
+ "4. object-inspect",
+ "4. once",
+ "4. optionator",
+ "4. os-homedir",
+ "4. parenthesis",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. process-nextick-args",
+ "4. protocol-buffers-schema",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. robust-determinant",
+ "4. robust-dot-product",
+ "4. robust-orientation",
+ "4. robust-scale",
+ "4. robust-subtract",
+ "4. robust-sum",
+ "4. safe-buffer",
+ "4. shallow-copy",
+ "4. simplicial-complex",
+ "4. source-map",
+ "4. split",
+ "4. sprintf-js",
+ "4. static-eval",
+ "4. stream-spigot",
+ "4. string_decoder",
+ "4. svg-arc-to-cubic-bezier",
+ "4. tape",
+ "4. through",
+ "4. through2",
+ "4. to-uint8",
+ "4. two-product",
+ "4. typedarray-pool",
+ "4. uglify-to-browserify",
+ "4. union-find",
+ "4. uniq",
+ "4. util-deprecate",
+ "4. vlq",
+ "4. wgs84",
+ "4. yargs",
+ "5. acorn",
+ "5. almost-equal",
+ "5. ansi-styles",
+ "5. ansicolors",
+ "5. arr-flatten",
+ "5. atob-lite",
+ "5. binary-search-bounds",
+ "5. bit-twiddle",
+ "5. camelcase",
+ "5. cell-orientation",
+ "5. clamp",
+ "5. cliui",
+ "5. compare-angle",
+ "5. compare-cell",
+ "5. core-util-is",
+ "5. cubic-hermite",
+ "5. decamelize",
+ "5. deep-equal",
+ "5. deep-is",
+ "5. defined",
+ "5. dup",
+ "5. escape-string-regexp",
+ "5. escodegen",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. fast-levenshtein",
+ "5. for-each",
+ "5. function-bind",
+ "5. gl-mat3",
+ "5. gl-vec3",
+ "5. gl-vec4",
+ "5. glob",
+ "5. glsl-tokenizer",
+ "5. has",
+ "5. inherits",
+ "5. interval-tree-1d",
+ "5. invert-permutation",
+ "5. is-base64",
+ "5. is-float-array",
+ "5. isarray",
+ "5. levn",
+ "5. minimist",
+ "5. number-is-nan",
+ "5. object-inspect",
+ "5. optionator",
+ "5. pad-left",
+ "5. prelude-ls",
+ "5. readable-stream",
+ "5. redeyed",
+ "5. resolve",
+ "5. resumer",
+ "5. robust-compress",
+ "5. robust-linear-solve",
+ "5. robust-orientation",
+ "5. robust-scale",
+ "5. robust-sum",
+ "5. safe-buffer",
+ "5. slab-decomposition",
+ "5. source-map",
+ "5. sprintf-js",
+ "5. string.prototype.trim",
+ "5. string_decoder",
+ "5. supports-color",
+ "5. through",
+ "5. through2",
+ "5. to-array-buffer",
+ "5. two-product",
+ "5. type-check",
+ "5. typedarray-pool",
+ "5. union-find",
+ "5. uniq",
+ "5. utils-copy",
+ "5. validate.io-array",
+ "5. validate.io-matrix-like",
+ "5. validate.io-ndarray-like",
+ "5. validate.io-positive-integer",
+ "5. vlq",
+ "5. window-size",
+ "5. wordwrap",
+ "5. wrappy",
+ "5. xtend",
+ "6. amdefine",
+ "6. binary-search-bounds",
+ "6. center-align",
+ "6. color-convert",
+ "6. const-pinf-float64",
+ "6. core-util-is",
+ "6. define-properties",
+ "6. es-abstract",
+ "6. esprima",
+ "6. estraverse",
+ "6. flatten-vertex-data",
+ "6. fs.realpath",
+ "6. function-bind",
+ "6. functional-red-black-tree",
+ "6. has-flag",
+ "6. inflight",
+ "6. inherits",
+ "6. is-blob",
+ "6. is-callable",
+ "6. isarray",
+ "6. minimatch",
+ "6. object-keys",
+ "6. once",
+ "6. path-is-absolute",
+ "6. prelude-ls",
+ "6. readable-stream",
+ "6. repeat-string",
+ "6. right-align",
+ "6. robust-orientation",
+ "6. robust-product",
+ "6. robust-sum",
+ "6. signum",
+ "6. source-map",
+ "6. string-to-arraybuffer",
+ "6. string_decoder",
+ "6. through",
+ "6. two-sum",
+ "6. type-check",
+ "6. type-name",
+ "6. utils-copy-error",
+ "6. utils-indexof",
+ "6. utils-regex-from-string",
+ "6. validate.io-array",
+ "6. validate.io-buffer",
+ "6. validate.io-integer",
+ "6. validate.io-nonnegative-integer",
+ "6. wordwrap",
+ "6. xtend"
+ ],
+ "parents": [
+ "",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. @plotly/d3-sankey",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. alpha-shape",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. color-normalize",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. convex-hull",
+ "1. convex-hull",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-force",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. delaunay-triangulate",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-line3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-select-box",
+ "1. gl-select-box",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. gl-text",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-wheel",
+ "1. mouse-wheel",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. point-cluster",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. regl-splom",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. robust-orientation",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. svg-path-sdf",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. d3-shape",
+ "2. alpha-complex",
+ "2. alpha-complex",
+ "2. simplicial-complex-boundary",
+ "2. simplicial-complex-boundary",
+ "2. color-rgba",
+ "2. color-rgba",
+ "2. color-rgba",
+ "2. affine-hull",
+ "2. incremental-convex-hull",
+ "2. incremental-convex-hull",
+ "2. monotone-convex-hull-2d",
+ "2. elementary-circuits-directed-graph",
+ "2. gl-shader",
+ "2. gl-shader",
+ "2. cdt2d",
+ "2. cdt2d",
+ "2. cdt2d",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. clean-pslg",
+ "2. gl-buffer",
+ "2. gl-buffer",
+ "2. gl-buffer",
+ "2. surface-nets",
+ "2. surface-nets",
+ "2. surface-nets",
+ "2. typedarray-pool",
+ "2. typedarray-pool",
+ "2. gl-texture2d",
+ "2. gl-texture2d",
+ "2. gl-texture2d",
+ "2. barycentric",
+ "2. colormap",
+ "2. glsl-specular-cook-torrance",
+ "2. polytope-closest-point",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. simplicial-complex-contour",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. gl-select-static",
+ "2. text-cache",
+ "2. 3d-view",
+ "2. 3d-view",
+ "2. 3d-view",
+ "2. a-big-triangle",
+ "2. a-big-triangle",
+ "2. a-big-triangle",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-axes3d",
+ "2. gl-fbo",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. gl-spikes3d",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. vectorize-text",
+ "2. ndarray-gradient",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. ndarray-scratch",
+ "2. ndarray-scratch",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. css-font",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. gl-util",
+ "2. parse-rect",
+ "2. to-px",
+ "2. bl",
+ "2. bl",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. concat-stream",
+ "2. duplexify",
+ "2. duplexify",
+ "2. duplexify",
+ "2. duplexify",
+ "2. falafel",
+ "2. falafel",
+ "2. falafel",
+ "2. falafel",
+ "2. from2",
+ "2. from2",
+ "2. glsl-resolve",
+ "2. glsl-resolve",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. glslify-deps",
+ "2. through2",
+ "2. through2",
+ "2. resolve",
+ "2. static-eval",
+ "2. @mapbox/vector-tile",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. geojson-rewind",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. gray-matter",
+ "2. brfs",
+ "2. brfs",
+ "2. brfs",
+ "2. brfs",
+ "2. through2",
+ "2. through2",
+ "2. pbf",
+ "2. pbf",
+ "2. shuffle-seed",
+ "2. sort-object",
+ "2. sort-object",
+ "2. supercluster",
+ "2. vt-pbf",
+ "2. vt-pbf",
+ "2. vt-pbf",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. mat4-interpolate",
+ "2. cwise",
+ "2. cwise",
+ "2. cwise",
+ "2. cwise",
+ "2. gl-matrix-invert",
+ "2. gl-matrix-invert",
+ "2. gl-matrix-invert",
+ "2. ndarray-warp",
+ "2. ndarray-warp",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. color-id",
+ "2. image-palette",
+ "2. image-palette",
+ "2. image-palette",
+ "2. color-alpha",
+ "2. raf",
+ "2. robust-scale",
+ "2. robust-scale",
+ "2. bitmap-sdf",
+ "2. draw-svg-path",
+ "2. draw-svg-path",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "2. svg-path-bounds",
+ "3. circumradius",
+ "3. boundary-cells",
+ "3. reduce-simplicial-complex",
+ "3. reduce-simplicial-complex",
+ "3. reduce-simplicial-complex",
+ "3. color-parse",
+ "3. color-parse",
+ "3. color-parse",
+ "3. color-space",
+ "3. color-space",
+ "3. simplicial-complex",
+ "3. simplicial-complex",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. gl-format-compiler-error",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. robust-in-sphere",
+ "3. big-rat",
+ "3. big-rat",
+ "3. big-rat",
+ "3. box-intersect",
+ "3. box-intersect",
+ "3. nextafter",
+ "3. rat-vec",
+ "3. robust-segment-intersect",
+ "3. ndarray-extract-contour",
+ "3. triangulate-hypercube",
+ "3. triangulate-hypercube",
+ "3. triangulate-hypercube",
+ "3. zero-crossings",
+ "3. robust-linear-solve",
+ "3. marching-simplex-table",
+ "3. ndarray-sort",
+ "3. orbit-camera-controller",
+ "3. orbit-camera-controller",
+ "3. turntable-camera-controller",
+ "3. turntable-camera-controller",
+ "3. turntable-camera-controller",
+ "3. gl-state",
+ "3. split-polygon",
+ "3. split-polygon",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. planar-graph-to-polyline",
+ "3. simplify-planar-graph",
+ "3. simplify-planar-graph",
+ "3. triangulate-polyline",
+ "3. cwise-compiler",
+ "3. string-split-by",
+ "3. d",
+ "3. es5-ext",
+ "3. es5-ext",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-iterator",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. es6-symbol",
+ "3. number-is-integer",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. end-of-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. glsl-inject-defines",
+ "3. glsl-inject-defines",
+ "3. glsl-inject-defines",
+ "3. glsl-token-defines",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-token-descope",
+ "3. glsl-tokenizer",
+ "3. @choojs/findup",
+ "3. map-limit",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. escodegen",
+ "3. @mapbox/geojson-area",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. sharkdown",
+ "3. extend-shallow",
+ "3. js-yaml",
+ "3. js-yaml",
+ "3. quote-stream",
+ "3. quote-stream",
+ "3. quote-stream",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. readable-stream",
+ "3. resolve-protobuf-schema",
+ "3. mat4-decompose",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. quat-slerp",
+ "3. cwise-parser",
+ "3. cwise-parser",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. static-module",
+ "3. uglify-js",
+ "3. uglify-js",
+ "3. uglify-js",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. buble",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. pxls",
+ "3. normalize-svg-path",
+ "4. circumcenter",
+ "4. circumcenter",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. tape",
+ "4. compare-oriented-cell",
+ "4. compare-oriented-cell",
+ "4. mumath",
+ "4. add-line-numbers",
+ "4. glsl-shader-name",
+ "4. glsl-shader-name",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. permutation-rank",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. robust-determinant",
+ "4. filtered-vector",
+ "4. filtered-vector",
+ "4. robust-dot-product",
+ "4. robust-dot-product",
+ "4. edges-to-adjacency-list",
+ "4. planar-dual",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. point-in-big-polygon",
+ "4. simplicial-complex",
+ "4. simplicial-complex",
+ "4. is-finite",
+ "4. string_decoder",
+ "4. string_decoder",
+ "4. string_decoder",
+ "4. once",
+ "4. string_decoder",
+ "4. through2",
+ "4. through2",
+ "4. once",
+ "4. string_decoder",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. optionator",
+ "4. cardinal",
+ "4. cardinal",
+ "4. split",
+ "4. stream-spigot",
+ "4. argparse",
+ "4. has",
+ "4. magic-string",
+ "4. duplexer2",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. merge-source-map",
+ "4. string_decoder",
+ "4. gl-quat",
+ "4. gl-quat",
+ "4. gl-quat",
+ "4. duplexer2",
+ "4. quote-stream",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. readable-stream",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. escodegen",
+ "4. static-eval",
+ "4. through2",
+ "4. through2",
+ "4. yargs",
+ "4. yargs",
+ "4. yargs",
+ "4. yargs",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. chalk",
+ "4. chalk",
+ "4. chalk",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. compute-dims",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8",
+ "4. to-uint8"
+ ],
+ "labels": [
+ "Plotly.js v1.46.x external module counter",
+ "1. @plotly/d3-sankey",
+ "1. alpha-shape",
+ "1. array-range",
+ "1. canvas-fit",
+ "1. color-normalize",
+ "1. convex-hull",
+ "1. country-regex",
+ "1. d3",
+ "1. d3-force",
+ "1. d3-hierarchy",
+ "1. d3-interpolate",
+ "1. d3-sankey-circular",
+ "1. delaunay-triangulate",
+ "1. es6-promise",
+ "1. fast-isnumeric",
+ "1. font-atlas-sdf",
+ "1. gl-cone3d",
+ "1. gl-contour2d",
+ "1. gl-error3d",
+ "1. gl-heatmap2d",
+ "1. gl-line3d",
+ "1. gl-mat4",
+ "1. gl-mesh3d",
+ "1. gl-plot2d",
+ "1. gl-plot3d",
+ "1. gl-pointcloud2d",
+ "1. gl-scatter3d",
+ "1. gl-select-box",
+ "1. gl-spikes2d",
+ "1. gl-streamtube3d",
+ "1. gl-surface3d",
+ "1. gl-text",
+ "1. glslify",
+ "1. has-hover",
+ "1. has-passive-events",
+ "1. mapbox-gl",
+ "1. matrix-camera-controller",
+ "1. mouse-change",
+ "1. mouse-event-offset",
+ "1. mouse-wheel",
+ "1. ndarray",
+ "1. ndarray-fill",
+ "1. ndarray-homography",
+ "1. point-cluster",
+ "1. polybooljs",
+ "1. regl",
+ "1. regl-error2d",
+ "1. regl-line2d",
+ "1. regl-scatter2d",
+ "1. regl-splom",
+ "1. right-now",
+ "1. robust-orientation",
+ "1. sane-topojson",
+ "1. strongly-connected-components",
+ "1. superscript-text",
+ "1. svg-path-sdf",
+ "1. tinycolor2",
+ "1. topojson-client",
+ "1. webgl-context",
+ "1. world-calendars",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-shape",
+ "2. alpha-complex",
+ "2. simplicial-complex-boundary",
+ "2. element-size",
+ "2. clamp",
+ "2. color-rgba",
+ "2. dtype",
+ "2. affine-hull",
+ "2. incremental-convex-hull",
+ "2. monotone-convex-hull-2d",
+ "2. d3-collection",
+ "2. d3-dispatch",
+ "2. d3-quadtree",
+ "2. d3-timer",
+ "2. d3-color",
+ "2. d3-array",
+ "2. d3-collection",
+ "2. d3-shape",
+ "2. elementary-circuits-directed-graph",
+ "2. incremental-convex-hull",
+ "2. uniq",
+ "2. is-string-blank",
+ "2. optical-properties",
+ "2. tiny-sdf",
+ "2. gl-shader",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. cdt2d",
+ "2. clean-pslg",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. glslify",
+ "2. iota-array",
+ "2. ndarray",
+ "2. surface-nets",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. glslify",
+ "2. iota-array",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-shader",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-read-float",
+ "2. glslify",
+ "2. ndarray",
+ "2. barycentric",
+ "2. colormap",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-specular-cook-torrance",
+ "2. glslify",
+ "2. ndarray",
+ "2. normals",
+ "2. polytope-closest-point",
+ "2. simplicial-complex-contour",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. binary-search-bounds",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. glsl-inverse",
+ "2. glslify",
+ "2. text-cache",
+ "2. 3d-view",
+ "2. a-big-triangle",
+ "2. gl-axes3d",
+ "2. gl-fbo",
+ "2. gl-mat4",
+ "2. gl-select-static",
+ "2. gl-shader",
+ "2. gl-spikes3d",
+ "2. glslify",
+ "2. has-passive-events",
+ "2. is-mobile",
+ "2. mouse-change",
+ "2. mouse-event-offset",
+ "2. mouse-wheel",
+ "2. ndarray",
+ "2. right-now",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. glslify",
+ "2. typedarray-pool",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. is-string-blank",
+ "2. typedarray-pool",
+ "2. vectorize-text",
+ "2. gl-buffer",
+ "2. gl-shader",
+ "2. glslify",
+ "2. gl-vec3",
+ "2. glsl-inverse",
+ "2. glsl-out-of-range",
+ "2. glslify",
+ "2. bit-twiddle",
+ "2. colormap",
+ "2. dup",
+ "2. gl-buffer",
+ "2. gl-mat4",
+ "2. gl-shader",
+ "2. binary-search-bounds",
+ "2. gl-texture2d",
+ "2. gl-vao",
+ "2. glsl-out-of-range",
+ "2. glsl-specular-beckmann",
+ "2. glslify",
+ "2. ndarray",
+ "2. ndarray-gradient",
+ "2. ndarray-ops",
+ "2. ndarray-pack",
+ "2. ndarray-scratch",
+ "2. surface-nets",
+ "2. typedarray-pool",
+ "2. bit-twiddle",
+ "2. color-normalize",
+ "2. css-font",
+ "2. detect-kerning",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. font-atlas",
+ "2. font-measure",
+ "2. gl-util",
+ "2. is-plain-obj",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. parse-unit",
+ "2. pick-by-alias",
+ "2. regl",
+ "2. to-px",
+ "2. typedarray-pool",
+ "2. bl",
+ "2. concat-stream",
+ "2. duplexify",
+ "2. falafel",
+ "2. from2",
+ "2. glsl-resolve",
+ "2. glsl-token-whitespace-trim",
+ "2. glslify-bundle",
+ "2. glslify-deps",
+ "2. through2",
+ "2. minimist",
+ "2. resolve",
+ "2. stack-trace",
+ "2. static-eval",
+ "2. xtend",
+ "2. is-browser",
+ "2. is-browser",
+ "2. @mapbox/gl-matrix",
+ "2. @mapbox/jsonlint-lines-primitives",
+ "2. @mapbox/mapbox-gl-supported",
+ "2. @mapbox/point-geometry",
+ "2. @mapbox/shelf-pack",
+ "2. @mapbox/tiny-sdf",
+ "2. @mapbox/unitbezier",
+ "2. @mapbox/vector-tile",
+ "2. @mapbox/whoots-js",
+ "2. csscolorparser",
+ "2. earcut",
+ "2. geojson-rewind",
+ "2. geojson-vt",
+ "2. gray-matter",
+ "2. grid-index",
+ "2. brfs",
+ "2. minimist",
+ "2. through2",
+ "2. pbf",
+ "2. quickselect",
+ "2. rw",
+ "2. shuffle-seed",
+ "2. sort-object",
+ "2. supercluster",
+ "2. tinyqueue",
+ "2. vt-pbf",
+ "2. binary-search-bounds",
+ "2. gl-mat4",
+ "2. gl-vec3",
+ "2. mat4-interpolate",
+ "2. mouse-event",
+ "2. signum",
+ "2. right-now",
+ "2. to-px",
+ "2. iota-array",
+ "2. is-buffer",
+ "2. cwise",
+ "2. gl-matrix-invert",
+ "2. ndarray-warp",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. clamp",
+ "2. dtype",
+ "2. flatten-vertex-data",
+ "2. is-obj",
+ "2. math-log2",
+ "2. parse-rect",
+ "2. binary-search-bounds",
+ "2. array-bounds",
+ "2. bubleify",
+ "2. color-normalize",
+ "2. flatten-vertex-data",
+ "2. object-assign",
+ "2. pick-by-alias",
+ "2. to-float32",
+ "2. update-diff",
+ "2. array-bounds",
+ "2. array-normalize",
+ "2. bubleify",
+ "2. color-normalize",
+ "2. earcut",
+ "2. es6-weak-map",
+ "2. flatten-vertex-data",
+ "2. glslify",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. to-float32",
+ "2. array-range",
+ "2. array-rearrange",
+ "2. clamp",
+ "2. color-id",
+ "2. color-normalize",
+ "2. color-rgba",
+ "2. flatten-vertex-data",
+ "2. glslify",
+ "2. image-palette",
+ "2. is-iexplorer",
+ "2. object-assign",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. to-float32",
+ "2. update-diff",
+ "2. array-bounds",
+ "2. array-range",
+ "2. bubleify",
+ "2. color-alpha",
+ "2. defined",
+ "2. flatten-vertex-data",
+ "2. left-pad",
+ "2. parse-rect",
+ "2. pick-by-alias",
+ "2. point-cluster",
+ "2. raf",
+ "2. regl-scatter2d",
+ "2. robust-scale",
+ "2. robust-subtract",
+ "2. robust-sum",
+ "2. two-product",
+ "2. bitmap-sdf",
+ "2. draw-svg-path",
+ "2. is-svg-path",
+ "2. parse-svg-path",
+ "2. svg-path-bounds",
+ "2. commander",
+ "2. get-canvas-context",
+ "2. object-assign",
+ "3. d3-path",
+ "3. circumradius",
+ "3. delaunay-triangulate",
+ "3. boundary-cells",
+ "3. reduce-simplicial-complex",
+ "3. clamp",
+ "3. color-parse",
+ "3. color-space",
+ "3. robust-orientation",
+ "3. robust-orientation",
+ "3. simplicial-complex",
+ "3. robust-orientation",
+ "3. strongly-connected-components",
+ "3. gl-format-compiler-error",
+ "3. weakmap-shim",
+ "3. binary-search-bounds",
+ "3. robust-in-sphere",
+ "3. robust-orientation",
+ "3. big-rat",
+ "3. box-intersect",
+ "3. nextafter",
+ "3. rat-vec",
+ "3. robust-segment-intersect",
+ "3. union-find",
+ "3. uniq",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. ndarray-extract-contour",
+ "3. triangulate-hypercube",
+ "3. zero-crossings",
+ "3. bit-twiddle",
+ "3. dup",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. robust-linear-solve",
+ "3. lerp",
+ "3. glsl-specular-beckmann",
+ "3. numeric",
+ "3. marching-simplex-table",
+ "3. ndarray",
+ "3. ndarray-sort",
+ "3. typedarray-pool",
+ "3. bit-twiddle",
+ "3. cwise",
+ "3. gl-fbo",
+ "3. ndarray",
+ "3. typedarray-pool",
+ "3. vectorize-text",
+ "3. matrix-camera-controller",
+ "3. orbit-camera-controller",
+ "3. turntable-camera-controller",
+ "3. gl-buffer",
+ "3. gl-vao",
+ "3. weak-map",
+ "3. bit-twiddle",
+ "3. dup",
+ "3. extract-frustum-planes",
+ "3. gl-buffer",
+ "3. gl-mat4",
+ "3. gl-shader",
+ "3. gl-state",
+ "3. gl-vao",
+ "3. gl-vec4",
+ "3. glslify",
+ "3. robust-orientation",
+ "3. split-polygon",
+ "3. vectorize-text",
+ "3. gl-texture2d",
+ "3. gl-buffer",
+ "3. gl-shader",
+ "3. gl-vao",
+ "3. glslify",
+ "3. cdt2d",
+ "3. clean-pslg",
+ "3. ndarray",
+ "3. planar-graph-to-polyline",
+ "3. simplify-planar-graph",
+ "3. surface-nets",
+ "3. triangulate-polyline",
+ "3. cwise-compiler",
+ "3. dup",
+ "3. cwise-compiler",
+ "3. cwise-compiler",
+ "3. ndarray",
+ "3. ndarray",
+ "3. ndarray-ops",
+ "3. typedarray-pool",
+ "3. css-font-size-keywords",
+ "3. css-font-stretch-keywords",
+ "3. css-font-style-keywords",
+ "3. css-font-weight-keywords",
+ "3. css-global-keywords",
+ "3. css-system-font-keywords",
+ "3. pick-by-alias",
+ "3. string-split-by",
+ "3. unquote",
+ "3. d",
+ "3. es5-ext",
+ "3. es6-iterator",
+ "3. es6-symbol",
+ "3. dtype",
+ "3. css-font",
+ "3. css-font",
+ "3. es6-weak-map",
+ "3. is-browser",
+ "3. is-firefox",
+ "3. is-plain-obj",
+ "3. number-is-integer",
+ "3. object-assign",
+ "3. pick-by-alias",
+ "3. pick-by-alias",
+ "3. parse-unit",
+ "3. readable-stream",
+ "3. safe-buffer",
+ "3. buffer-from",
+ "3. readable-stream",
+ "3. inherits",
+ "3. typedarray",
+ "3. readable-stream",
+ "3. end-of-stream",
+ "3. inherits",
+ "3. stream-shift",
+ "3. acorn",
+ "3. foreach",
+ "3. isarray",
+ "3. object-keys",
+ "3. readable-stream",
+ "3. inherits",
+ "3. resolve",
+ "3. xtend",
+ "3. glsl-inject-defines",
+ "3. glsl-token-defines",
+ "3. glsl-token-depth",
+ "3. glsl-token-descope",
+ "3. glsl-token-scope",
+ "3. glsl-token-string",
+ "3. glsl-token-whitespace-trim",
+ "3. glsl-tokenizer",
+ "3. murmurhash-js",
+ "3. shallow-copy",
+ "3. @choojs/findup",
+ "3. events",
+ "3. glsl-resolve",
+ "3. glsl-tokenizer",
+ "3. graceful-fs",
+ "3. inherits",
+ "3. map-limit",
+ "3. resolve",
+ "3. readable-stream",
+ "3. xtend",
+ "3. path-parse",
+ "3. escodegen",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/geojson-area",
+ "3. concat-stream",
+ "3. minimist",
+ "3. sharkdown",
+ "3. extend-shallow",
+ "3. kind-of",
+ "3. js-yaml",
+ "3. strip-bom-string",
+ "3. quote-stream",
+ "3. static-module",
+ "3. through2",
+ "3. resolve",
+ "3. readable-stream",
+ "3. xtend",
+ "3. ieee754",
+ "3. resolve-protobuf-schema",
+ "3. seedrandom",
+ "3. sort-asc",
+ "3. sort-desc",
+ "3. kdbush",
+ "3. @mapbox/point-geometry",
+ "3. @mapbox/vector-tile",
+ "3. pbf",
+ "3. gl-mat4",
+ "3. gl-vec3",
+ "3. mat4-decompose",
+ "3. mat4-recompose",
+ "3. quat-slerp",
+ "3. cwise-compiler",
+ "3. cwise-parser",
+ "3. static-module",
+ "3. uglify-js",
+ "3. gl-mat2",
+ "3. gl-mat3",
+ "3. gl-mat4",
+ "3. cwise",
+ "3. ndarray-linear-interpolate",
+ "3. array-bounds",
+ "3. buble",
+ "3. clamp",
+ "3. color-id",
+ "3. pxls",
+ "3. quantize",
+ "3. color-parse",
+ "3. performance-now",
+ "3. two-product",
+ "3. two-sum",
+ "3. clamp",
+ "3. abs-svg-path",
+ "3. normalize-svg-path",
+ "3. abs-svg-path",
+ "3. is-svg-path",
+ "3. parse-svg-path",
+ "3. normalize-svg-path",
+ "4. circumcenter",
+ "4. tape",
+ "4. cell-orientation",
+ "4. compare-cell",
+ "4. compare-oriented-cell",
+ "4. color-name",
+ "4. defined",
+ "4. is-plain-obj",
+ "4. hsluv",
+ "4. mumath",
+ "4. bit-twiddle",
+ "4. union-find",
+ "4. add-line-numbers",
+ "4. gl-constants",
+ "4. glsl-shader-name",
+ "4. sprintf-js",
+ "4. robust-scale",
+ "4. robust-subtract",
+ "4. robust-sum",
+ "4. two-product",
+ "4. bit-twiddle",
+ "4. bn.js",
+ "4. double-bits",
+ "4. bit-twiddle",
+ "4. typedarray-pool",
+ "4. double-bits",
+ "4. big-rat",
+ "4. robust-orientation",
+ "4. typedarray-pool",
+ "4. gamma",
+ "4. permutation-parity",
+ "4. permutation-rank",
+ "4. cwise-compiler",
+ "4. robust-determinant",
+ "4. convex-hull",
+ "4. typedarray-pool",
+ "4. filtered-vector",
+ "4. gl-mat4",
+ "4. filtered-vector",
+ "4. gl-mat4",
+ "4. gl-vec3",
+ "4. uniq",
+ "4. robust-dot-product",
+ "4. robust-sum",
+ "4. edges-to-adjacency-list",
+ "4. planar-dual",
+ "4. point-in-big-polygon",
+ "4. robust-orientation",
+ "4. robust-sum",
+ "4. two-product",
+ "4. uniq",
+ "4. robust-orientation",
+ "4. simplicial-complex",
+ "4. cdt2d",
+ "4. uniq",
+ "4. parenthesis",
+ "4. es5-ext",
+ "4. es6-iterator",
+ "4. es6-symbol",
+ "4. next-tick",
+ "4. d",
+ "4. es5-ext",
+ "4. es6-symbol",
+ "4. d",
+ "4. es5-ext",
+ "4. is-finite",
+ "4. isarray",
+ "4. string_decoder",
+ "4. core-util-is",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. isarray",
+ "4. string_decoder",
+ "4. core-util-is",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. once",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. glsl-token-inject-block",
+ "4. glsl-token-string",
+ "4. glsl-tokenizer",
+ "4. glsl-tokenizer",
+ "4. glsl-token-assignments",
+ "4. glsl-token-depth",
+ "4. glsl-token-properties",
+ "4. glsl-token-scope",
+ "4. through2",
+ "4. commander",
+ "4. once",
+ "4. core-util-is",
+ "4. isarray",
+ "4. string_decoder",
+ "4. inherits",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. esprima",
+ "4. estraverse",
+ "4. esutils",
+ "4. optionator",
+ "4. source-map",
+ "4. wgs84",
+ "4. cardinal",
+ "4. expect.js",
+ "4. minimist",
+ "4. split",
+ "4. stream-spigot",
+ "4. through",
+ "4. is-extendable",
+ "4. argparse",
+ "4. esprima",
+ "4. buffer-equal",
+ "4. minimist",
+ "4. through2",
+ "4. concat-stream",
+ "4. convert-source-map",
+ "4. falafel",
+ "4. has",
+ "4. magic-string",
+ "4. duplexer2",
+ "4. escodegen",
+ "4. object-inspect",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. through2",
+ "4. merge-source-map",
+ "4. shallow-copy",
+ "4. static-eval",
+ "4. core-util-is",
+ "4. inherits",
+ "4. isarray",
+ "4. string_decoder",
+ "4. process-nextick-args",
+ "4. safe-buffer",
+ "4. util-deprecate",
+ "4. protocol-buffers-schema",
+ "4. gl-mat4",
+ "4. gl-vec3",
+ "4. gl-mat4",
+ "4. gl-quat",
+ "4. esprima",
+ "4. uniq",
+ "4. concat-stream",
+ "4. duplexer2",
+ "4. falafel",
+ "4. has",
+ "4. quote-stream",
+ "4. readable-stream",
+ "4. shallow-copy",
+ "4. escodegen",
+ "4. object-inspect",
+ "4. static-eval",
+ "4. through2",
+ "4. source-map",
+ "4. uglify-to-browserify",
+ "4. yargs",
+ "4. acorn",
+ "4. acorn-dynamic-import",
+ "4. acorn-jsx",
+ "4. vlq",
+ "4. chalk",
+ "4. magic-string",
+ "4. minimist",
+ "4. os-homedir",
+ "4. arr-flatten",
+ "4. compute-dims",
+ "4. flip-pixels",
+ "4. is-browser",
+ "4. is-buffer",
+ "4. to-uint8",
+ "4. svg-arc-to-cubic-bezier",
+ "5. dup",
+ "5. robust-linear-solve",
+ "5. deep-equal",
+ "5. defined",
+ "5. for-each",
+ "5. function-bind",
+ "5. glob",
+ "5. has",
+ "5. inherits",
+ "5. minimist",
+ "5. object-inspect",
+ "5. resolve",
+ "5. resumer",
+ "5. string.prototype.trim",
+ "5. through",
+ "5. cell-orientation",
+ "5. compare-cell",
+ "5. almost-equal",
+ "5. pad-left",
+ "5. atob-lite",
+ "5. glsl-tokenizer",
+ "5. typedarray-pool",
+ "5. invert-permutation",
+ "5. typedarray-pool",
+ "5. robust-compress",
+ "5. robust-scale",
+ "5. robust-sum",
+ "5. two-product",
+ "5. binary-search-bounds",
+ "5. cubic-hermite",
+ "5. robust-sum",
+ "5. two-product",
+ "5. uniq",
+ "5. compare-angle",
+ "5. dup",
+ "5. binary-search-bounds",
+ "5. interval-tree-1d",
+ "5. robust-orientation",
+ "5. slab-decomposition",
+ "5. bit-twiddle",
+ "5. union-find",
+ "5. number-is-nan",
+ "5. safe-buffer",
+ "5. safe-buffer",
+ "5. safe-buffer",
+ "5. wrappy",
+ "5. safe-buffer",
+ "5. readable-stream",
+ "5. xtend",
+ "5. wrappy",
+ "5. safe-buffer",
+ "5. deep-is",
+ "5. fast-levenshtein",
+ "5. levn",
+ "5. prelude-ls",
+ "5. type-check",
+ "5. wordwrap",
+ "5. ansicolors",
+ "5. redeyed",
+ "5. through",
+ "5. readable-stream",
+ "5. sprintf-js",
+ "5. function-bind",
+ "5. vlq",
+ "5. readable-stream",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. optionator",
+ "5. source-map",
+ "5. source-map",
+ "5. safe-buffer",
+ "5. gl-mat3",
+ "5. gl-vec3",
+ "5. gl-vec4",
+ "5. readable-stream",
+ "5. minimist",
+ "5. through2",
+ "5. core-util-is",
+ "5. inherits",
+ "5. isarray",
+ "5. string_decoder",
+ "5. esprima",
+ "5. estraverse",
+ "5. esutils",
+ "5. source-map",
+ "5. escodegen",
+ "5. readable-stream",
+ "5. xtend",
+ "5. camelcase",
+ "5. cliui",
+ "5. decamelize",
+ "5. window-size",
+ "5. acorn",
+ "5. acorn",
+ "5. ansi-styles",
+ "5. escape-string-regexp",
+ "5. supports-color",
+ "5. utils-copy",
+ "5. validate.io-array",
+ "5. validate.io-matrix-like",
+ "5. validate.io-ndarray-like",
+ "5. validate.io-positive-integer",
+ "5. arr-flatten",
+ "5. clamp",
+ "5. is-base64",
+ "5. is-float-array",
+ "5. to-array-buffer"
+ ]
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 800
+ }
+}
diff --git a/test/image/mocks/treemap_packings.json b/test/image/mocks/treemap_packings.json
new file mode 100644
index 00000000000..da40f76decb
--- /dev/null
+++ b/test/image/mocks/treemap_packings.json
@@ -0,0 +1,586 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "hoverinfo": "all",
+ "name": "binary treemap",
+ "tiling": {
+ "packing": "binary"
+ },
+ "pathbar": {
+ "edgeshape": "/",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": "Blackbody",
+ "reversescale": true
+ },
+ "domain": {
+ "x": [
+ 0,
+ 0.3
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "hoverinfo": "all",
+ "name": "dice-slice treemap",
+ "tiling": {
+ "packing": "dice-slice"
+ },
+ "pathbar": {
+ "edgeshape": "|",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": "Blackbody",
+ "reversescale": true
+ },
+ "domain": {
+ "x": [
+ 0.35,
+ 0.65
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "hoverinfo": "all",
+ "name": "squarify treemap",
+ "tiling": {
+ "packing": "squarify"
+ },
+ "pathbar": {
+ "edgeshape": "<",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": "Blackbody",
+ "reversescale": true
+ },
+ "domain": {
+ "x": [
+ 0.7,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "hoverinfo": "all",
+ "name": "slice treemap",
+ "tiling": {
+ "packing": "slice"
+ },
+ "pathbar": {
+ "edgeshape": ">",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": "Blackbody",
+ "reversescale": true
+ },
+ "domain": {
+ "x": [
+ 0,
+ 0.3
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "hoverinfo": "all",
+ "name": "slice-dice treemap",
+ "tiling": {
+ "packing": "slice-dice"
+ },
+ "pathbar": {
+ "edgeshape": "|",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": "Blackbody",
+ "reversescale": true
+ },
+ "domain": {
+ "x": [
+ 0.35,
+ 0.65
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "hoverinfo": "all",
+ "name": "dice treemap",
+ "tiling": {
+ "packing": "dice"
+ },
+ "pathbar": {
+ "edgeshape": "\\",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": "Blackbody",
+ "reversescale": true
+ },
+ "domain": {
+ "x": [
+ 0.7,
+ 1
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 1200,
+ "height": 800,
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "binary treemap
with / edgeshape",
+ "x": 0.15,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "dice-slice treemap
with | edgeshape",
+ "x": 0.5,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "squarify treemap
with < edgeshape",
+ "x": 0.85,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "slice treemap
with > edgeshape",
+ "x": 0.15,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "slice-dice treemap
with | edgeshape",
+ "x": 0.5,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "dice treemap
with \\ edgeshape",
+ "x": 0.85,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_pad_mirror.json b/test/image/mocks/treemap_pad_mirror.json
new file mode 100644
index 00000000000..f4872947e51
--- /dev/null
+++ b/test/image/mocks/treemap_pad_mirror.json
@@ -0,0 +1,459 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "slice-dice",
+ "pad": 0
+ },
+ "marker": {
+ "opacitybase": 0.3,
+ "opacitystep": 0.1,
+ "pad": {
+ "t": 16,
+ "l": 8,
+ "r": 4,
+ "b": 2
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.02,
+ 0.48
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "slice-dice",
+ "flip": "y",
+ "pad": 0
+ },
+ "marker": {
+ "opacitybase": 0.3,
+ "opacitystep": 0.1,
+ "pad": {
+ "t": 16,
+ "l": 8,
+ "r": 4,
+ "b": 2
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.02,
+ 0.48
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "slice-dice",
+ "flip": "x",
+ "pad": 0
+ },
+ "marker": {
+ "opacitybase": 0.3,
+ "opacitystep": 0.1,
+ "pad": {
+ "t": 16,
+ "l": 8,
+ "r": 4,
+ "b": 2
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.52,
+ 0.98
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "slice-dice",
+ "flip": "x+y",
+ "pad": 0
+ },
+ "marker": {
+ "opacitybase": 0.3,
+ "opacitystep": 0.1,
+ "pad": {
+ "t": 16,
+ "l": 8,
+ "r": 4,
+ "b": 2
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.52,
+ 0.98
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 1200,
+ "height": 800,
+ "shapes": [
+ {
+ "type": "circle",
+ "layer": "below",
+ "x0": 0.075,
+ "x1": 0.925,
+ "y0": 0.0,
+ "y1": 0.8
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.02,
+ "x1": 0.48,
+ "y0": 0,
+ "y1": 0.45
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.02,
+ "x1": 0.48,
+ "y0": 0.5,
+ "y1": 0.95
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.52,
+ "x1": 0.98,
+ "y0": 0,
+ "y1": 0.45
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.52,
+ "x1": 0.98,
+ "y0": 0.5,
+ "y1": 0.95
+ }
+ ],
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "base",
+ "font": {
+ "size": 16
+ },
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "flip y axis",
+ "font": {
+ "size": 16
+ },
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "flip x axis",
+ "font": {
+ "size": 16
+ },
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "flip both axes",
+ "font": {
+ "size": 16
+ },
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_pad_transpose.json b/test/image/mocks/treemap_pad_transpose.json
new file mode 100644
index 00000000000..55d985431b7
--- /dev/null
+++ b/test/image/mocks/treemap_pad_transpose.json
@@ -0,0 +1,430 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "dice-slice",
+ "pad": 0
+ },
+ "marker": {
+ "opacitybase": 0.5,
+ "opacitystep": 0.05,
+ "pad": {
+ "t": 16,
+ "l": 8,
+ "r": 4,
+ "b": 2
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.02,
+ 0.48
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "dice-slice",
+ "flip": "y",
+ "pad": 0
+ },
+ "marker": {
+ "opacitybase": 0.5,
+ "opacitystep": 0.05,
+ "pad": {
+ "t": 16,
+ "l": 8,
+ "r": 4,
+ "b": 2
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.02,
+ 0.48
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "dice-slice",
+ "flip": "x",
+ "pad": 0
+ },
+ "marker": {
+ "opacitybase": 0.5,
+ "opacitystep": 0.05,
+ "pad": {
+ "t": 16,
+ "l": 8,
+ "r": 4,
+ "b": 2
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.52,
+ 0.98
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "dice-slice",
+ "flip": "x+y",
+ "pad": 0
+ },
+ "marker": {
+ "opacitybase": 0.5,
+ "opacitystep": 0.05,
+ "pad": {
+ "t": 16,
+ "l": 8,
+ "r": 4,
+ "b": 2
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.52,
+ 0.98
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 1200,
+ "height": 800,
+ "paper_bgcolor": "darkblue",
+ "treemapcolorway": [
+ "#ff0",
+ "#f00",
+ "#0f0"
+ ],
+ "shapes": [
+ { "type": "circle", "layer": "below", "x0": 0.075, "x1": 0.925, "y0": 0.0, "y1": 0.8},
+ { "type": "rect", "layer": "below", "x0": 0.02, "x1": 0.48, "y0": 0.0, "y1": 0.45 },
+ { "type": "rect", "layer": "below", "x0": 0.02, "x1": 0.48, "y0": 0.5, "y1": 0.95 },
+ { "type": "rect", "layer": "below", "x0": 0.52, "x1": 0.98, "y0": 0.0, "y1": 0.45 },
+ { "type": "rect", "layer": "below", "x0": 0.52, "x1": 0.98, "y0": 0.5, "y1": 0.95 }
+ ],
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "base",
+ "font": {
+ "size": 16
+ },
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "flip y axis",
+ "font": {
+ "size": 16
+ },
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "flip x axis",
+ "font": {
+ "size": 16
+ },
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "flip both axes",
+ "font": {
+ "size": 16
+ },
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_textfit.json b/test/image/mocks/treemap_textfit.json
new file mode 100644
index 00000000000..f9ee8c98f21
--- /dev/null
+++ b/test/image/mocks/treemap_textfit.json
@@ -0,0 +1,579 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "tiling": {
+ "packing": "squarify",
+ "pad": 2
+ },
+ "textfont": {
+ "size": 256
+ },
+ "marker": {
+ "pad": {
+ "t": 32,
+ "l": 16,
+ "r": 8,
+ "b": 4
+ }
+ },
+ "texttemplate": "%{label}
%{text}",
+ "labels": [
+ "Queen - 1973",
+ "Queen II - 1974",
+ "Sheer Heart Attack - 1974",
+ "A Night at the Opera - 1975",
+ "A Day at the Races - 1976",
+ "News of the World - 1977",
+ "Jazz - 1978",
+ "The Game - 1980",
+ "Hot Space - 1982",
+ "The Works - 1984",
+ "A Kind of Magic - 1986",
+ "The Miracle - 1989",
+ "Innuendo - 1991",
+ "Made in Heaven - 1995",
+
+ "Keep
Yourself
Alive",
+ "Doing
All
Right",
+ "Great
King
Rat",
+ "My
Fairy
King",
+ "Liar",
+ "The
Night
Comes
Down",
+ "Modern
Times
Rock
'N'
Roll",
+ "Son
and
Daughter",
+ "Jesus",
+ "Seven
Seas
of
Rhye...",
+
+ "Procession",
+ "Father
to
Son",
+ "White
Queen",
+ "Some
Day
One
Day",
+ "The
Loser
in
the
End",
+ "Ogre
Battle",
+ "The
Fairy
Feller's
Master-Stroke",
+ "Nevermore",
+ "The
March
of
the
Black
Queen",
+ "Funny
How
Love
Is",
+ "Seven
Seas
of
Rhye",
+
+ "Brighton
Rock",
+ "Killer
Queen",
+ "Tenement
Funster",
+ "Flick
of
the
Wrist",
+ "Lily
of
the
Valley",
+ "Now
I'm
Here",
+ "In
the
Lap
of
the
Gods",
+ "Stone
Cold
Crazy",
+ "Dear
Friends",
+ "Misfire",
+ "Bring
Back
That
Leroy
Brown",
+ "She
Makes
Me",
+ "In
the
Lap
of
the
God
-
Revisited",
+
+ "Death
on
Two
Legs",
+ "Lazing
on
a
Sunday
Afternoon",
+ "I'm
in
Love
with
My
Car",
+ "You're
My
Best
Friend",
+ "'39",
+ "Sweet
Lady",
+ "Seaside
Rendezvous",
+ "The
Prophet's
Song",
+ "Love
of
My
Life",
+ "Good
Company",
+ "Bohemian
Rhapsody",
+ "God
Save
the
Queen",
+
+ "Tie
Your
Mother
Down",
+ "You
Take
My
Breath
Away",
+ "Long
Away",
+ "The
Millionaire
Waltz",
+ "You
and
I",
+ "Somebody
to
Love",
+ "White
Man",
+ "Good
Old-Fashioned
Lover
Boy",
+ "Drowse",
+ "Teo
Torriatte",
+
+ "We
Will
Rock
You",
+ "We
Are
the
Champions",
+ "Sheer
Heart
Attack",
+ "All
Dead,
All
Dead",
+ "Spread
Your
Wings",
+ "Fight
from
the
Inside",
+ "Get
Down,
Make
Love",
+ "Sleeping
on
the
Sidewalk",
+ "Who
Needs
You",
+ "It's
Late",
+ "My
Melancholy
Blues",
+
+ "Mustapha",
+ "Fat
Bottomed
Girls",
+ "Jealousy",
+ "Bicycle
Race",
+ "If
You
Can't
Beat
Them",
+ "Let
Me
Entertain
You",
+ "Dead
on
Time",
+ "In
Only
Seven
Days",
+ "Dreamer's
Ball",
+ "Fun
It",
+ "Leaving
Home
Ain't
Easy",
+ "Don't
Stop
Me
Now",
+ "More
of
That
Jazz",
+
+ "Play
the
Game",
+ "Dragon
Attack",
+ "Another
One
Bites
the
Dust",
+ "Need
Your
Loving
Tonight",
+ "Crazy
Little
Thing
Called
Love",
+ "Rock
It",
+ "Don't
Try
Suicide",
+ "Sail
Away
Sweet
Sister",
+ "Coming
Soon",
+ "Save
Me",
+
+ "Staying
Power",
+ "Dancer",
+ "Back
Chat",
+ "Body
Language",
+ "Action
This
Day",
+ "Put
Out
the
Fire",
+ "Life
Is
Real",
+ "Calling
All
Girls",
+ "Las
Palabras
de
Amor",
+ "Cool
Cat",
+ "Under
Pressure",
+
+ "Radio
Ga
Ga",
+ "Tear
It
Up",
+ "It's
a
Hard
Life",
+ "Man
on
the
Prowl",
+ "Back
to
Humans'",
+ "I
Want
to
Break
Free",
+ "Keep
Passing
the
Open
Windows",
+ "Hammer
to
Fall",
+ "Is
This
the
World
We
Created?",
+
+ "One
Vision",
+ "A
Kind
of
Magic",
+ "One
Year
of
Love",
+ "Pain
Is
So
Close
to
Pleasure",
+ "Friends
Will
Be
Friends",
+ "Who
Wants
to
Live
Forever",
+ "Gimme
the
Prize",
+ "Don't
Lose
Your
Head",
+ "Princes
of
the
Universe",
+
+ "Party",
+ "Khashoggi's
Ship",
+ "The
Miracle",
+ "I
Want
It
All",
+ "The
Invisible
Man",
+ "Breakthru",
+ "Rain
Must
Fall",
+ "Scandal",
+ "My
Baby
Does
Me",
+ "Was
It
All
Worth
It",
+
+ "Innuendo",
+ "I'm
Going
Slightly
Mad",
+ "Headlong",
+ "I
Can't
Live
with
You",
+ "Don't
Try
So
Hard",
+ "Ride
the
Wild
Wind",
+ "All
God's
People",
+ "These
Are
the
Days
of
Our
Lives",
+ "Delilah",
+ "The
Hitman",
+ "Bijou",
+ "The
Show
Must
Go
On",
+
+ "It's
a
Beautiful
Day",
+ "Made
in
Heaven",
+ "Let
Me
Live",
+ "Mother
Love",
+ "My
Life
Has
Been
Saved",
+ "I
Was
Born
to
Love
You",
+ "Heaven
for
Everyone",
+ "Too
Much
Love
Will
Kill
You",
+ "You
Don't
Fool
Me",
+ "A
Winter's
Tale",
+ "It's
a
Beautiful
Day
-
Revisited",
+ "Yeah",
+ "13"
+ ],
+ "parents": [
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+ "Queen records",
+
+ "Queen - 1973",
+ "Queen - 1973",
+ "Queen - 1973",
+ "Queen - 1973",
+ "Queen - 1973",
+ "Queen - 1973",
+ "Queen - 1973",
+ "Queen - 1973",
+ "Queen - 1973",
+ "Queen - 1973",
+
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+ "Queen II - 1974",
+
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+ "Sheer Heart Attack - 1974",
+
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+ "A Night at the Opera - 1975",
+
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+ "A Day at the Races - 1976",
+
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+ "News of the World - 1977",
+
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+ "Jazz - 1978",
+
+ "The Game - 1980",
+ "The Game - 1980",
+ "The Game - 1980",
+ "The Game - 1980",
+ "The Game - 1980",
+ "The Game - 1980",
+ "The Game - 1980",
+ "The Game - 1980",
+ "The Game - 1980",
+ "The Game - 1980",
+
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+ "Hot Space - 1982",
+
+ "The Works - 1984",
+ "The Works - 1984",
+ "The Works - 1984",
+ "The Works - 1984",
+ "The Works - 1984",
+ "The Works - 1984",
+ "The Works - 1984",
+ "The Works - 1984",
+ "The Works - 1984",
+
+ "A Kind of Magic - 1986",
+ "A Kind of Magic - 1986",
+ "A Kind of Magic - 1986",
+ "A Kind of Magic - 1986",
+ "A Kind of Magic - 1986",
+ "A Kind of Magic - 1986",
+ "A Kind of Magic - 1986",
+ "A Kind of Magic - 1986",
+ "A Kind of Magic - 1986",
+
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+ "The Miracle - 1989",
+
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+ "Innuendo - 1991",
+
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995",
+ "Made in Heaven - 1995"
+ ],
+ "text": [
+ "38:41",
+ "40:45",
+ "38:41",
+ "43:03",
+ "44:04",
+ "39:10",
+ "44:39",
+ "35:32",
+ "43:29",
+ "37:15",
+ "40:28",
+ "41:22",
+ "53:48",
+ "70:21",
+
+ "3:46",
+ "4:10",
+ "5:41",
+ "4:07",
+ "6:26",
+ "4:24",
+ "1:48",
+ "3:24",
+ "3:45",
+ "1:10",
+
+ "1:12",
+ "6:14",
+ "4:34",
+ "4:23",
+ "4:02",
+ "4:10",
+ "2:40",
+ "1:17",
+ "6:33",
+ "2:50",
+ "2:50",
+
+ "5:08",
+ "3:01",
+ "2:48",
+ "3:19",
+ "1:43",
+ "4:10",
+ "3:20",
+ "2:12",
+ "1:07",
+ "1:50",
+ "2:13",
+ "4:08",
+ "3:42",
+
+ "3:43",
+ "1:08",
+ "3:05",
+ "2:50",
+ "3:30",
+ "4:01",
+ "2:13",
+ "8:21",
+ "3:38",
+ "3:26",
+ "5:57",
+ "1:11",
+
+ "4:48",
+ "5:09",
+ "3:34",
+ "4:54",
+ "3:25",
+ "4:56",
+ "4:59",
+ "2:54",
+ "3:45",
+ "5:50",
+
+ "2:01",
+ "2:59",
+ "3:26",
+ "3:10",
+ "4:34",
+ "3:03",
+ "3:51",
+ "3:06",
+ "3:05",
+ "6:26",
+ "3:29",
+
+ "3:03",
+ "4:14",
+ "3:14",
+ "3:04",
+ "4:15",
+ "3:01",
+ "3:23",
+ "2:30",
+ "3:30",
+ "3:29",
+ "3:15",
+ "3:29",
+ "4:12",
+
+ "3:30",
+ "4:18",
+ "3:35",
+ "2:50",
+ "2:42",
+ "4:33",
+ "3:52",
+ "3:33",
+ "2:51",
+ "3:48",
+
+ "4:10",
+ "3:46",
+ "4:31",
+ "4:29",
+ "3:33",
+ "3:15",
+ "3:39",
+ "3:53",
+ "4:26",
+ "3:26",
+ "4:02",
+
+ "5:48",
+ "3:28",
+ "4:08",
+ "3:28",
+ "5:10",
+ "3:20",
+ "5:21",
+ "4:28",
+ "2:13",
+
+ "5:11",
+ "4:24",
+ "4:27",
+ "4:21",
+ "4:06",
+ "5:15",
+ "4:33",
+ "4:38",
+ "3:33",
+
+ "2:24",
+ "2:47",
+ "5:02",
+ "4:41",
+ "3:55",
+ "4:07",
+ "4:20",
+ "4:42",
+ "3:22",
+ "5:45",
+
+ "6:31",
+ "4:22",
+ "4:38",
+ "4:33",
+ "3:39",
+ "4:42",
+ "4:21",
+ "4:15",
+ "3:35",
+ "4:56",
+ "3:36",
+ "4:35",
+
+ "2:32",
+ "5:25",
+ "4:45",
+ "4:49",
+ "3:15",
+ "4:49",
+ "5:36",
+ "4:20",
+ "5:24",
+ "3:49",
+ "3:01",
+ "0:04",
+ "22:32"
+ ]
+ }
+ ],
+ "layout": {
+ "width": 1000,
+ "height": 1000
+ }
+}
diff --git a/test/image/mocks/treemap_textposition.json b/test/image/mocks/treemap_textposition.json
new file mode 100644
index 00000000000..0657347fa51
--- /dev/null
+++ b/test/image/mocks/treemap_textposition.json
@@ -0,0 +1,982 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "bottom left textposition",
+ "textposition": "bottom left",
+ "pathbar": {
+ "side": "bottom",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "domain": {
+ "x": [
+ 0,
+ 0.3
+ ],
+ "y": [
+ 0,
+ 0.3
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "bottom center textposition",
+ "textposition": "bottom center",
+ "pathbar": {
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "level": "Charlie",
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0.35,
+ 0.65
+ ],
+ "y": [
+ 0,
+ 0.3
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "bottom right textposition",
+ "textposition": "bottom right",
+ "pathbar": {
+ "side": "bottom",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "level": "Foxtrot",
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0.7,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.3
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "middle left textposition",
+ "textposition": "middle left",
+ "pathbar": {
+ "visible": false
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "level": "Zulu",
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0,
+ 0.3
+ ],
+ "y": [
+ 0.35,
+ 0.65
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "middle center textposition",
+ "textposition": "middle center",
+ "pathbar": {
+ "visible": false
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "level": "Alpha",
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0.35,
+ 0.65
+ ],
+ "y": [
+ 0.35,
+ 0.65
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "middle right textposition",
+ "textposition": "middle right",
+ "pathbar": {
+ "visible": false
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "level": "Bravo",
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0.7,
+ 1
+ ],
+ "y": [
+ 0.35,
+ 0.65
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "top left textposition",
+ "textposition": "top left",
+ "pathbar": {
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "level": "Juliet",
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0,
+ 0.3
+ ],
+ "y": [
+ 0.7,
+ 1
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "top center textposition",
+ "textposition": "top center",
+ "pathbar": {
+ "side": "bottom",
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "level": "Oscar",
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0.35,
+ 0.65
+ ],
+ "y": [
+ 0.7,
+ 1
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+text+percent parent+percent entry+percent root",
+ "name": "top right textposition",
+ "textposition": "top right",
+ "pathbar": {
+ "textfont": {
+ "size": 12,
+ "family": "Times New Roman"
+ }
+ },
+ "text": [
+ "A L P H A",
+ "B R A V O",
+ "C H A R L I E",
+ "D E L T A",
+ "E C H O",
+ "F O X T R O T",
+ "G O L F",
+ "H O T E L",
+ "I N D I A",
+ "J U L I E T",
+ "K I L O",
+ "L I M A",
+ "M I K E",
+ "N O V E M B E R",
+ "O S C A R",
+ "P A P A",
+ "Q U E B E C",
+ "R O M E O",
+ "S I E R R A",
+ "T A N G O",
+ "U N I F O R M",
+ "V I C T O R",
+ "W H I S K E Y",
+ "X R A Y",
+ "Y A N K E E",
+ "Z U L U"
+ ],
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "level": "Uniform",
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0.7,
+ 1
+ ],
+ "y": [
+ 0.7,
+ 1
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 1200,
+ "height": 1200,
+ "paper_bgcolor": "#210"
+ }
+}
diff --git a/test/image/mocks/treemap_transpose_nopad.json b/test/image/mocks/treemap_transpose_nopad.json
new file mode 100644
index 00000000000..dc22ed0fae3
--- /dev/null
+++ b/test/image/mocks/treemap_transpose_nopad.json
@@ -0,0 +1,418 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "hoverinfo": "all",
+ "textposition": "middle center",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "squarify",
+ "squarifyratio": 1,
+ "pad": 0
+ },
+ "marker": {
+ "pad": {
+ "t": 0,
+ "l": 0,
+ "r": 0,
+ "b": 0
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.02,
+ 0.31
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "hoverinfo": "all",
+ "textposition": "middle center",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "squarify",
+ "squarifyratio": 1.618034,
+ "pad": 0
+ },
+ "marker": {
+ "pad": {
+ "t": 0,
+ "l": 0,
+ "r": 0,
+ "b": 0
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.02,
+ 0.31
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "hoverinfo": "all",
+ "textposition": "middle center",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "squarify",
+ "squarifyratio": 1,
+ "pad": 0
+ },
+ "marker": {
+ "pad": {
+ "t": 0,
+ "l": 0,
+ "r": 0,
+ "b": 0
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.35,
+ 0.98
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "hoverinfo": "all",
+ "textposition": "middle center",
+ "textinfo": "label",
+ "textfont": {
+ "size": 10
+ },
+ "pathbar": {
+ "visible": true
+ },
+ "tiling": {
+ "packing": "squarify",
+ "squarifyratio": 1.618034,
+ "pad": 0
+ },
+ "marker": {
+ "pad": {
+ "t": 0,
+ "l": 0,
+ "r": 0,
+ "b": 0
+ }
+ },
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "",
+ "",
+ "Bravo",
+ "Bravo",
+ "Bravo",
+ "Echo",
+ "Echo",
+ "Echo",
+ "Hotel",
+ "Hotel",
+ "Hotel",
+ "Kilo",
+ "Kilo",
+ "Kilo",
+ "November",
+ "November",
+ "November",
+ "Quebec",
+ "Quebec",
+ "Quebec",
+ "Tango",
+ "Tango",
+ "Tango",
+ "Whiskey",
+ "Whiskey"
+ ],
+ "domain": {
+ "x": [
+ 0.35,
+ 0.98
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 1200,
+ "height": 800,
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "square",
+ "font": {
+ "size": 16
+ },
+ "x": 0.17,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "transposed",
+ "font": {
+ "size": 16
+ },
+ "x": 0.17,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "rectangle",
+ "font": {
+ "size": 16
+ },
+ "x": 0.66,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "transposed rectangle",
+ "font": {
+ "size": 16
+ },
+ "x": 0.66,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_values.json b/test/image/mocks/treemap_values.json
new file mode 100644
index 00000000000..81fd7a1a579
--- /dev/null
+++ b/test/image/mocks/treemap_values.json
@@ -0,0 +1,49 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "name": "with branchvalues:remainder",
+ "labels": ["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
+ "parents": ["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
+ "values": [10, 14, 12, 10, 2, 6, 6, 1, 4],
+ "textinfo": "label+value+percent parent+percent entry",
+ "domain": {"x": [0, 0.48]},
+ "outsidetextfont": {"size": 20, "color": "#377eb8"},
+ "marker": {"line": {"width": 2}},
+ "pathbar": {"visible": false}
+ },
+ {
+ "type": "treemap",
+ "name": "with branchvalues:total",
+ "branchvalues": "total",
+ "labels": ["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
+ "parents": ["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
+ "domain": {"x": [0.52, 1]},
+ "values": [65, 14, 12, 10, 2, 6, 6, 1, 4],
+ "textinfo": "label+value+percent parent+percent entry",
+ "outsidetextfont": {"size": 20, "color": "#377eb8"},
+ "marker": {"line": {"width": 2}},
+ "pathbar": {"visible": false}
+ }
+ ],
+ "layout": {
+ "annotations": [{
+ "showarrow": false,
+ "text": "branchvalues: remainder",
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 1.1,
+ "yanchor": "bottom"
+
+ }, {
+ "showarrow": false,
+ "text": "branchvalues: total",
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 1.1,
+ "yanchor": "bottom"
+ }],
+
+ "paper_bgcolor": "#d3d3d3"
+ }
+}
diff --git a/test/image/mocks/treemap_values_colorscale.json b/test/image/mocks/treemap_values_colorscale.json
new file mode 100644
index 00000000000..67ed042f626
--- /dev/null
+++ b/test/image/mocks/treemap_values_colorscale.json
@@ -0,0 +1,184 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "name": "with branchvalues:remainder",
+ "labels": [
+ "Eve",
+ "Cain",
+ "Seth",
+ "Enos",
+ "Noam",
+ "Abel",
+ "Awan",
+ "Enoch",
+ "Azura"
+ ],
+ "parents": [
+ "",
+ "Eve",
+ "Eve",
+ "Seth",
+ "Seth",
+ "Eve",
+ "Eve",
+ "Awan",
+ "Eve"
+ ],
+ "values": [
+ 10,
+ 14,
+ 12,
+ 10,
+ 2,
+ 6,
+ 6,
+ 1,
+ 4
+ ],
+ "domain": {
+ "x": [
+ 0,
+ 0.48
+ ]
+ },
+ "marker": {
+ "colorscale": "Portland",
+ "colors": [
+ 10,
+ 14,
+ 12,
+ 10,
+ 2,
+ 6,
+ 6,
+ 1,
+ 4
+ ],
+ "showscale": true,
+ "colorbar": {
+ "title": "trace A",
+ "x": 0,
+ "xanchor": "right"
+ }
+ },
+ "hovered": {
+ "marker": {
+ "line": {
+ "color": "white"
+ }
+ }
+ },
+ "textinfo": "label+value",
+ "tiling": {
+ "pad": 4
+ },
+ "pathbar": {
+ "thickness": 32,
+ "color": "black",
+ "textfont": {
+ "color": "white",
+ "size": 28,
+ "family": "Times New Roman"
+ }
+ }
+ },
+ {
+ "type": "treemap",
+ "name": "with branchvalues:total",
+ "branchvalues": "total",
+ "labels": [
+ "Eve",
+ "Cain",
+ "Seth",
+ "Enos",
+ "Noam",
+ "Abel",
+ "Awan",
+ "Enoch",
+ "Azura"
+ ],
+ "parents": [
+ "",
+ "Eve",
+ "Eve",
+ "Seth",
+ "Seth",
+ "Eve",
+ "Eve",
+ "Awan",
+ "Eve"
+ ],
+ "domain": {
+ "x": [
+ 0.52,
+ 1
+ ]
+ },
+ "values": [
+ 65,
+ 14,
+ 12,
+ 10,
+ 2,
+ 6,
+ 6,
+ 1,
+ 4
+ ],
+ "text": [
+ "sixty five",
+ "fourteen",
+ "twelve",
+ "ten",
+ "two",
+ "six",
+ "six",
+ "one",
+ "four"
+ ],
+ "marker": {
+ "cmin": 0,
+ "cmax": 25,
+ "colorscale": "Blackbody",
+ "reversescale": true,
+ "showscale": true,
+ "colorbar": {
+ "title": "trace B"
+ }
+ },
+ "textinfo": "label+text",
+ "pathbar": {
+ "side": "bottom",
+ "color": "#7F7",
+ "textfont": {
+ "size": 20,
+ "family": "Times New Roman"
+ }
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "branchvalues: remainder",
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 1.1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "branchvalues: total
used as input to marker.color",
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 1.1,
+ "yanchor": "bottom"
+ }
+ ],
+ "paper_bgcolor": "#d3d3d3"
+ }
+}
diff --git a/test/image/mocks/treemap_with-without_values.json b/test/image/mocks/treemap_with-without_values.json
new file mode 100644
index 00000000000..8fe726071c8
--- /dev/null
+++ b/test/image/mocks/treemap_with-without_values.json
@@ -0,0 +1,430 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "textinfo": "label+value+percent parent+percent entry+percent root+text+current path",
+ "hoverinfo": "all",
+ "name": "without values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "domain": {
+ "x": [
+ 0.01,
+ 0.33
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+value+percent parent+percent entry+percent root+text+current path",
+ "hoverinfo": "all",
+ "name": "with total values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "branchvalues": "total",
+ "values": [
+ 40,
+ 2,
+ 38,
+ 1.5,
+ 2.5,
+ 34,
+ 1,
+ 2,
+ 3,
+ 28,
+ 1.25,
+ 1.75,
+ 2.25,
+ 2.75,
+ 20,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3,
+ 10,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3
+ ],
+ "text": [
+ "forty",
+ "two",
+ "thirty-eight",
+ "one and a half",
+ "two and a half",
+ "thirty-four",
+ "one",
+ "two",
+ "three",
+ "twenty-eight",
+ "one and twenty-five hundredths",
+ "one and seventy-five hundredths",
+ "two and twenty-five hundredths",
+ "two and seventy-five hundredths",
+ "twenty",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three",
+ "ten",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": [
+ [
+ 0,
+ "#FFF"
+ ],
+ [
+ 0.01,
+ "#0FF"
+ ],
+ [
+ 0.1,
+ "#00F"
+ ],
+ [
+ 1,
+ "#000"
+ ]
+ ]
+ },
+ "domain": {
+ "x": [
+ 0.34,
+ 0.66
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "textinfo": "label+value+percent parent+percent entry+percent root+text+current path",
+ "hoverinfo": "all",
+ "name": "with remainder values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "branchvalues": "remainder",
+ "values": [
+ 0,
+ 2,
+ 0,
+ 1.5,
+ 2.5,
+ 0,
+ 1,
+ 2,
+ 3,
+ 0,
+ 1.25,
+ 1.75,
+ 2.25,
+ 2.75,
+ 0,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3,
+ 0,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3
+ ],
+ "text": [
+ "zero",
+ "two",
+ "zero",
+ "one and a half",
+ "two and a half",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "zero",
+ "one and twenty-five hundredths",
+ "one and seventy-five hundredths",
+ "two and twenty-five hundredths",
+ "two and seventy-five hundredths",
+ "zero",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three",
+ "zero",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": [
+ [
+ 0,
+ "#FFF"
+ ],
+ [
+ 0.01,
+ "#0FF"
+ ],
+ [
+ 0.1,
+ "#00F"
+ ],
+ [
+ 1,
+ "#000"
+ ]
+ ]
+ },
+ "domain": {
+ "x": [
+ 0.67,
+ 0.99
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "margin" : {
+ "t": 50,
+ "l": 0,
+ "r": 0,
+ "b": 25
+ },
+ "width": 1500,
+ "height": 600,
+ "shapes": [
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.01,
+ "x1": 0.33,
+ "y0": 0,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.34,
+ "x1": 0.66,
+ "y0": 0,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.67,
+ "x1": 0.99,
+ "y0": 0,
+ "y1": 1
+ }
+ ],
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "with counted leaves
",
+ "x": 0.17,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "with values and branchvalues: total
",
+ "x": 0.5,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "with values and branchvalues: remainder
",
+ "x": 0.83,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/treemap_with-without_values_template.json b/test/image/mocks/treemap_with-without_values_template.json
new file mode 100644
index 00000000000..5aec2b0a3f9
--- /dev/null
+++ b/test/image/mocks/treemap_with-without_values_template.json
@@ -0,0 +1,434 @@
+{
+ "data": [
+ {
+ "type": "treemap",
+ "texttemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "hovertemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "name": "without values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "domain": {
+ "x": [
+ 0.01,
+ 0.33
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "texttemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "hovertemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "name": "with total values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "branchvalues": "total",
+ "values": [
+ 40,
+ 2,
+ 38,
+ 1.5,
+ 2.5,
+ 34,
+ 1,
+ 2,
+ 3,
+ 28,
+ 1.25,
+ 1.75,
+ 2.25,
+ 2.75,
+ 20,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3,
+ 10,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3
+ ],
+ "text": [
+ "forty",
+ "two",
+ "thirty-eight",
+ "one and a half",
+ "two and a half",
+ "thirty-four",
+ "one",
+ "two",
+ "three",
+ "twenty-eight",
+ "one and twenty-five hundredths",
+ "one and seventy-five hundredths",
+ "two and twenty-five hundredths",
+ "two and seventy-five hundredths",
+ "twenty",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three",
+ "ten",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": [
+ [
+ 0,
+ "#FFF"
+ ],
+ [
+ 0.01,
+ "#FF0"
+ ],
+ [
+ 0.1,
+ "#F00"
+ ],
+ [
+ 1,
+ "#000"
+ ]
+ ]
+ },
+ "domain": {
+ "x": [
+ 0.34,
+ 0.66
+ ]
+ }
+ },
+ {
+ "type": "treemap",
+ "texttemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "hovertemplate": "%{currentPath}%{label}
%{text}
%{value}
%{parent} <=> %{percentParent}
%{entry} <=> %{percentEntry}
%{root} <=> %{percentRoot}",
+ "name": "with remainder values",
+ "level": "Oscar",
+ "labels": [
+ "Alpha",
+ "Bravo",
+ "Charlie",
+ "Delta",
+ "Echo",
+ "Foxtrot",
+ "Golf",
+ "Hotel",
+ "India",
+ "Juliet",
+ "Kilo",
+ "Lima",
+ "Mike",
+ "November",
+ "Oscar",
+ "Papa",
+ "Quebec",
+ "Romeo",
+ "Sierra",
+ "Tango",
+ "Uniform",
+ "Victor",
+ "Whiskey",
+ "X ray",
+ "Yankee",
+ "Zulu"
+ ],
+ "branchvalues": "remainder",
+ "values": [
+ 0,
+ 2,
+ 0,
+ 1.5,
+ 2.5,
+ 0,
+ 1,
+ 2,
+ 3,
+ 0,
+ 1.25,
+ 1.75,
+ 2.25,
+ 2.75,
+ 0,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3,
+ 0,
+ 1,
+ 1.5,
+ 2,
+ 2.5,
+ 3
+ ],
+ "text": [
+ "zero",
+ "two",
+ "zero",
+ "one and a half",
+ "two and a half",
+ "zero",
+ "one",
+ "two",
+ "three",
+ "zero",
+ "one and twenty-five hundredths",
+ "one and seventy-five hundredths",
+ "two and twenty-five hundredths",
+ "two and seventy-five hundredths",
+ "zero",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three",
+ "zero",
+ "one",
+ "one and a half",
+ "two",
+ "two and a half",
+ "three"
+ ],
+ "parents": [
+ "",
+ "Alpha",
+ "Alpha",
+ "Charlie",
+ "Charlie",
+ "Charlie",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Foxtrot",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Juliet",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Oscar",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform",
+ "Uniform"
+ ],
+ "marker": {
+ "colorscale": [
+ [
+ 0,
+ "#FFF"
+ ],
+ [
+ 0.01,
+ "#FF0"
+ ],
+ [
+ 0.1,
+ "#F00"
+ ],
+ [
+ 1,
+ "#000"
+ ]
+ ]
+ },
+ "domain": {
+ "x": [
+ 0.67,
+ 0.99
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "treemapcolorway": [
+ "#700",
+ "#077"
+ ],
+ "margin" : {
+ "t": 50,
+ "l": 0,
+ "r": 0,
+ "b": 25
+ },
+ "width": 1500,
+ "height": 600,
+ "shapes": [
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.01,
+ "x1": 0.33,
+ "y0": 0,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.34,
+ "x1": 0.66,
+ "y0": 0,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "above",
+ "x0": 0.67,
+ "x1": 0.99,
+ "y0": 0,
+ "y1": 1
+ }
+ ],
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "with counted leaves
",
+ "x": 0.17,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "with values and branchvalues: total
",
+ "x": 0.5,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ },
+ {
+ "showarrow": false,
+ "text": "with values and branchvalues: remainder
",
+ "x": 0.83,
+ "xanchor": "center",
+ "y": 0,
+ "yanchor": "top"
+ }
+ ]
+ }
+}
diff --git a/test/jasmine/assets/mock_lists.js b/test/jasmine/assets/mock_lists.js
index 267ca176811..c72bb86893f 100644
--- a/test/jasmine/assets/mock_lists.js
+++ b/test/jasmine/assets/mock_lists.js
@@ -33,6 +33,7 @@ var svgMockList = [
['range_slider_multiple', require('@mocks/range_slider_multiple.json')],
['sankey_energy', require('@mocks/sankey_energy.json')],
['sunburst_coffee', require('@mocks/sunburst_coffee.json')],
+ ['treemap_coffee', require('@mocks/treemap_coffee.json')],
['parcats_bad-displayindex', require('@mocks/parcats_bad-displayindex.json')],
['scattercarpet', require('@mocks/scattercarpet.json')],
['shapes', require('@mocks/shapes.json')],
diff --git a/test/jasmine/tests/plot_api_react_test.js b/test/jasmine/tests/plot_api_react_test.js
index 083bd0a83b2..b3e047d1283 100644
--- a/test/jasmine/tests/plot_api_react_test.js
+++ b/test/jasmine/tests/plot_api_react_test.js
@@ -1901,6 +1901,42 @@ describe('Plotly.react and uirevision attributes', function() {
.catch(failTest)
.then(done);
});
+
+ it('preserves treemap level changes', function(done) {
+ function assertLevel(msg, exp) {
+ expect(gd._fullData[0].level).toBe(exp, msg);
+ }
+
+ Plotly.react(gd, [{
+ type: 'treemap',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve'],
+ uirevision: 1
+ }])
+ .then(function() {
+ assertLevel('no set level at start', undefined);
+ })
+ .then(function() {
+ var nodeSeth = d3.select('.slice:nth-child(2)').node();
+ mouseEvent('click', 0, 0, {element: nodeSeth});
+ })
+ .then(function() {
+ assertLevel('after clicking on Seth sector', 'Seth');
+ })
+ .then(function() {
+ return Plotly.react(gd, [{
+ type: 'treemap',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura', 'Joe'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve', 'Seth'],
+ uirevision: 1
+ }]);
+ })
+ .then(function() {
+ assertLevel('after reacting with new data, but with same uirevision', 'Seth');
+ })
+ .catch(failTest)
+ .then(done);
+ });
});
describe('Test Plotly.react + interactions under uirevision:', function() {
diff --git a/test/jasmine/tests/sunburst_test.js b/test/jasmine/tests/sunburst_test.js
index cce6b6034ff..15afa342343 100644
--- a/test/jasmine/tests/sunburst_test.js
+++ b/test/jasmine/tests/sunburst_test.js
@@ -71,6 +71,18 @@ describe('Test sunburst defaults:', function() {
expect(fullData[2].visible).toBe(false, 'no labels');
});
+ it('should only coerce *count* when the *values* array is not present', function() {
+ _supply([
+ {labels: [1], parents: ['']},
+ {labels: [1], parents: [''], values: []},
+ {labels: [1], parents: [''], values: [1]}
+ ]);
+
+ expect(fullData[0].count).toBe('leaves');
+ expect(fullData[1].count).toBe('leaves', 'has empty values');
+ expect(fullData[2].count).toBe(undefined, 'has values');
+ });
+
it('should not coerce *branchvalues* when *values* is not set', function() {
_supply([
{labels: [1], parents: [''], values: [1]},
@@ -103,6 +115,16 @@ describe('Test sunburst defaults:', function() {
expect(fullData[1].marker.line.color).toBe('#fff', 'dflt');
});
+ it('should default *leaf.opacity* depending on having or not having *colorscale*', function() {
+ _supply([
+ {labels: [1], parents: ['']},
+ {labels: [1], parents: [''], marker: {colorscale: 'Blues'}}
+ ]);
+
+ expect(fullData[0].leaf.opacity).toBe(0.7, 'without colorscale');
+ expect(fullData[1].leaf.opacity).toBe(1, 'with colorscale');
+ });
+
it('should include "text" flag in *textinfo* when *text* is set', function() {
_supply([
{labels: [1], parents: [''], text: ['A']},
@@ -221,12 +243,16 @@ describe('Test sunburst calc:', function() {
var parents = ['', 'Root', 'Root', 'B'];
_calc([
- {labels: labels, parents: parents},
+ {labels: labels, parents: parents, count: 'leaves+branches'},
+ {labels: labels, parents: parents, count: 'branches'},
+ {labels: labels, parents: parents}, // N.B. counts 'leaves' in this case
{labels: labels, parents: parents, values: [0, 1, 2, 3]},
{labels: labels, parents: parents, values: [30, 20, 10, 5], branchvalues: 'total'}
]);
expect(extractPt('value')).toEqual([
+ [4, 2, 1, 1],
+ [2, 1, 0, 0],
[2, 1, 1, 1],
[6, 5, 1, 3],
[30, 20, 10, 5]
@@ -245,8 +271,8 @@ describe('Test sunburst calc:', function() {
expect(gd.calcdata[0][0].hierarchy).toBe(undefined, 'no computed hierarchy');
expect(Lib.warn).toHaveBeenCalledTimes(2);
- expect(Lib.warn.calls.allArgs()[0][0]).toBe('Total value for node Root is smaller than the sum of its children.');
- expect(Lib.warn.calls.allArgs()[1][0]).toBe('Total value for node B is smaller than the sum of its children.');
+ expect(Lib.warn.calls.allArgs()[0][0]).toBe('Total value for node Root is smaller than the sum of its children. \nparent value = 0 \nchildren sum = 3');
+ expect(Lib.warn.calls.allArgs()[1][0]).toBe('Total value for node B is smaller than the sum of its children. \nparent value = 2 \nchildren sum = 3');
});
it('should warn labels/parents lead to ambiguous hierarchy', function() {
@@ -378,7 +404,7 @@ describe('Test sunburst hover:', function() {
pos: 4,
exp: {
label: {
- nums: 'Abel\n6',
+ nums: 'Abel\nEve/\n17% of Eve\n6',
name: 'trace 0'
},
ptData: {
@@ -397,7 +423,7 @@ describe('Test sunburst hover:', function() {
pos: 4,
exp: {
label: {
- nums: 'Abel',
+ nums: 'Abel\nEve/\n17% of Eve',
name: 't...'
},
ptData: {
@@ -945,10 +971,13 @@ describe('Test sunburst tweening:', function() {
afterEach(destroyGraphDiv);
- function _run(gd, v) {
+ function _reset() {
pathTweenFnLookup = {};
textTweenFnLookup = {};
+ }
+ function _run(gd, v) {
+ _reset();
click(gd, v)();
// 1 second more than the click transition duration
@@ -980,7 +1009,7 @@ describe('Test sunburst tweening:', function() {
expect(actual).toBe(trim(exp), msg + ' | node ' + id + ' @t=' + t);
}
- it('should tween sector exit/update (case: branch, no maxdepth)', function(done) {
+ it('should tween sector exit/update (case: click on branch, no maxdepth)', function(done) {
var mock = {
data: [{
type: 'sunburst',
@@ -1018,7 +1047,7 @@ describe('Test sunburst tweening:', function() {
.then(done);
});
- it('should tween sector enter/update (case: entry, no maxdepth)', function(done) {
+ it('should tween sector enter/update (case: click on entry, no maxdepth)', function(done) {
var mock = {
data: [{
type: 'sunburst',
@@ -1057,7 +1086,7 @@ describe('Test sunburst tweening:', function() {
.then(done);
});
- it('should tween sector enter/update/exit (case: entry, maxdepth=2)', function(done) {
+ it('should tween sector enter/update/exit (case: click on entry, maxdepth=2)', function(done) {
var mock = {
data: [{
type: 'sunburst',
@@ -1094,6 +1123,70 @@ describe('Test sunburst tweening:', function() {
.catch(failTest)
.then(done);
});
+
+ it('should tween sector enter/update/exit (case: click on entry, maxdepth=2, level=B)', function(done) {
+ var mock = {
+ data: [{
+ type: 'sunburst',
+ labels: ['Root', 'A', 'B', 'b', 'bb'],
+ parents: ['', 'Root', 'Root', 'B', 'b'],
+ maxdepth: 2,
+ level: 'B'
+ }]
+ };
+
+ Plotly.plot(gd, mock)
+ .then(_run(gd, 1))
+ .then(function() {
+ _assert('exit b radially outward and to parent sector angle', 'd', 'b',
+ 'M350,133.75L350,100A135,1350,1,0485,235.00000000000003' +
+ 'L451.25,235.00000000000003A101.25,101.250,1,1350,133.75Z'
+ );
+ _assert('enter new entry radially outward', 'd', 'Root',
+ 'M350,235A0,00,1,0350,235A0,00,1,0350,235Z' +
+ 'M383.75,235A33.75,33.750,1,1316.25,235A33.75,33.750,1,1383.75,235Z'
+ );
+ _assert('enter A counterclockwise', 'd', 'A',
+ 'M417.5,235L485,235A135,1350,0,0350,100L350,167.5A67.5,67.50,0,1417.5,235Z'
+ );
+ _assert('update B to new position', 'd', 'B',
+ 'M350,201.25L350,133.75A101.25,101.250,1,0451.25,235.00000000000003' +
+ 'L383.75,235A33.75,33.750,1,1350,201.25Z'
+ );
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ /*
+ it('should tween in sectors from new traces', function(done) {
+ Plotly.plot(gd, [{type: 'sunburst'}])
+ .then(_reset)
+ .then(function() {
+ return Plotly.animate(gd, [{
+ type: 'sunburst',
+ labels: ['Root', 'A', 'B', 'b'],
+ parents: ['', 'Root', 'Root', 'B']
+ }]);
+ })
+ .then(function() {
+ _assert('enter entry from theta=0', 'd', 'Root',
+ ''
+ );
+ // _assert('enter A from theta=0', 'd', 'A',
+ // ''
+ // );
+ // _assert('enter B from theta=0', 'd', 'B',
+ // ''
+ // );
+ // _assert('enter b from theta=0', 'd', 'b',
+ // ''
+ // );
+ })
+ .catch(failTest)
+ .then(done);
+ });
+ */
});
describe('Test sunburst interactions edge cases', function() {
@@ -1239,16 +1332,189 @@ describe('Test sunburst interactions edge cases', function() {
});
});
-describe('Test sunburst texttemplate:', function() {
+describe('Test sunburst texttemplate without `values` should work at root level:', function() {
+ checkTextTemplate([{
+ type: 'sunburst',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
+ }], 'g.slicetext', [
+ ['color: %{color}', ['color: rgba(0,0,0,0)', 'color: #1f77b4', 'color: #ff7f0e', 'color: #2ca02c', 'color: #d62728', 'color: #9467bd', 'color: #ff7f0e', 'color: #ff7f0e', 'color: #d62728']],
+ ['label: %{label}', ['label: Eve', 'label: Cain', 'label: Seth', 'label: Enos', 'label: Noam', 'label: Abel', 'label: Awan', 'label: Enoch', 'label: Azura']],
+ ['text: %{text}', ['text: sixty-five', 'text: fourteen', 'text: twelve', 'text: ten', 'text: two', 'text: six', 'text: six', 'text: one', 'text: four']],
+ ['path: %{currentPath}', ['path: /', 'path: Eve/', 'path: Eve/', 'path: Eve/', 'path: Eve/', 'path: Eve', 'path: Eve/Seth', 'path: Eve/Seth/', 'path: Eve/Awan/']],
+ ['%{percentRoot} of %{root}', ['100% of Eve', '33% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve']],
+ ['%{percentEntry} of %{entry}', ['100% of Eve', '33% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve']],
+ ['%{percentParent} of %{parent}', ['%{percentParent} of %{parent}', '100% of Seth', '33% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '17% of Eve', '50% of Seth', '100% of Awan']],
+ [
+ [
+ 'label: %{label}',
+ 'text: %{text}',
+ 'value: %{value}',
+ '%{percentRoot} of %{root}',
+ '%{percentEntry} of %{entry}',
+ '%{percentParent} of %{parent}',
+ '%{percentParent} of %{parent}',
+ '%{percentParent} of %{parent}',
+ 'color: %{color}'
+ ],
+ [
+ 'label: Eve',
+ 'text: fourteen',
+ 'value: %{value}', // N.B. there is no `values` array
+ '17% of Eve',
+ '17% of Eve',
+ '17% of Eve',
+ '17% of Eve',
+ '100% of Awan',
+ 'color: #9467bd'
+ ]
+ ]
+ ]);
+});
+
+describe('Test sunburst texttemplate with *total* `values` should work at root level:', function() {
+ checkTextTemplate([{
+ type: 'sunburst',
+ branchvalues: 'total',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ values: [65, 14, 12, 10, 2, 6, 6, 1, 4],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
+ }], 'g.slicetext', [
+ ['color: %{color}', ['color: rgba(0,0,0,0)', 'color: #1f77b4', 'color: #ff7f0e', 'color: #2ca02c', 'color: #d62728', 'color: #9467bd', 'color: #ff7f0e', 'color: #ff7f0e', 'color: #d62728']],
+ ['label: %{label}', ['label: Eve', 'label: Cain', 'label: Seth', 'label: Enos', 'label: Noam', 'label: Abel', 'label: Awan', 'label: Enoch', 'label: Azura']],
+ ['value: %{value}', ['value: 65', 'value: 14', 'value: 12', 'value: 10', 'value: 2', 'value: 6', 'value: 6', 'value: 1', 'value: 4']],
+ ['text: %{text}', ['text: sixty-five', 'text: fourteen', 'text: twelve', 'text: ten', 'text: two', 'text: six', 'text: six', 'text: one', 'text: four']],
+ ['path: %{currentPath}', ['path: /', 'path: Eve/', 'path: Eve/', 'path: Eve/', 'path: Eve/', 'path: Eve', 'path: Eve/Seth', 'path: Eve/Seth/', 'path: Eve/Awan/']],
+ ['%{percentRoot} of %{root}', ['100% of Eve', '22% of Eve', '18% of Eve', '9% of Eve', '9% of Eve', '6% of Eve', '15% of Eve', '3% of Eve', '2% of Eve']],
+ ['%{percentEntry} of %{entry}', ['100% of Eve', '22% of Eve', '18% of Eve', '9% of Eve', '9% of Eve', '6% of Eve', '15% of Eve', '3% of Eve', '2% of Eve']],
+ ['%{percentParent} of %{parent}', ['%{percentParent} of %{parent}', '22% of Eve', '18% of Eve', '9% of Eve', '9% of Eve', '6% of Eve', '83% of Seth', '17% of Seth', '17% of Awan']],
+ [
+ [
+ 'label: %{label}',
+ 'text: %{text}',
+ 'value: %{value}',
+ '%{percentRoot} of %{root}',
+ '%{percentEntry} of %{entry}',
+ '%{percentParent} of %{parent}',
+ '%{percentParent} of %{parent}',
+ '%{percentParent} of %{parent}',
+ 'color: %{color}'
+ ],
+ [
+ 'label: Eve',
+ 'text: fourteen',
+ 'value: 12',
+ '9% of Eve',
+ '15% of Eve',
+ '3% of Eve',
+ '6% of Eve',
+ '17% of Awan',
+ 'color: #9467bd'
+ ]
+ ]
+ ]);
+});
+
+describe('Test sunburst texttemplate with *remainder* `values` should work at root level:', function() {
checkTextTemplate([{
type: 'sunburst',
- labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Esther'],
- values: [11, 12, 13, 14, 15],
- text: ['1', '2', '3', '4', '5'],
- parents: ['', 'Eve', 'Eve', 'Seth', 'Seth' ]
+ branchvalues: 'remainder',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ values: [65, 14, 12, 10, 2, 6, 6, 1, 4],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
}], 'g.slicetext', [
- ['txt: %{label}', ['txt: Eve', 'txt: Cain', 'txt: Seth', 'txt: Enos', 'txt: Esther']],
- [['txt: %{label}', '%{text}', 'value: %{value}'], ['txt: Eve', '2', 'value: 13', '', '']],
- ['%{color}', ['rgba(0,0,0,0)', '#1f77b4', '#ff7f0e', '#1f77b4', '#1f77b4']]
+ ['color: %{color}', ['color: rgba(0,0,0,0)', 'color: #1f77b4', 'color: #ff7f0e', 'color: #2ca02c', 'color: #d62728', 'color: #9467bd', 'color: #ff7f0e', 'color: #ff7f0e', 'color: #d62728']],
+ ['label: %{label}', ['label: Eve', 'label: Cain', 'label: Seth', 'label: Enos', 'label: Noam', 'label: Abel', 'label: Awan', 'label: Enoch', 'label: Azura']],
+ ['value: %{value}', ['value: 65', 'value: 14', 'value: 12', 'value: 10', 'value: 2', 'value: 6', 'value: 6', 'value: 1', 'value: 4']],
+ ['text: %{text}', ['text: sixty-five', 'text: fourteen', 'text: twelve', 'text: ten', 'text: two', 'text: six', 'text: six', 'text: one', 'text: four']],
+ ['path: %{currentPath}', ['path: /', 'path: Eve/', 'path: Eve/', 'path: Eve/', 'path: Eve/', 'path: Eve', 'path: Eve/Seth', 'path: Eve/Seth/', 'path: Eve/Awan/']],
+ ['%{percentRoot} of %{root}', ['100% of Eve', '20% of Eve', '12% of Eve', '6% of Eve', '5% of Eve', '3% of Eve', '8% of Eve', '2% of Eve', '1% of Eve']],
+ ['%{percentEntry} of %{entry}', ['100% of Eve', '20% of Eve', '12% of Eve', '6% of Eve', '5% of Eve', '3% of Eve', '8% of Eve', '2% of Eve', '1% of Eve']],
+ ['%{percentParent} of %{parent}', ['%{percentParent} of %{parent}', '20% of Eve', '12% of Eve', '6% of Eve', '5% of Eve', '3% of Eve', '42% of Seth', '8% of Seth', '14% of Awan']],
+ [
+ [
+ 'label: %{label}',
+ 'text: %{text}',
+ 'value: %{value}',
+ '%{percentRoot} of %{root}',
+ '%{percentEntry} of %{entry}',
+ '%{percentParent} of %{parent}',
+ '%{percentParent} of %{parent}',
+ '%{percentParent} of %{parent}',
+ 'color: %{color}'
+ ],
+ [
+ 'label: Eve',
+ 'text: fourteen',
+ 'value: 12',
+ '6% of Eve',
+ '5% of Eve',
+ '8% of Eve',
+ '2% of Eve',
+ '14% of Awan',
+ 'color: #9467bd'
+ ]
+ ]
]);
});
+
+describe('Test sunburst texttemplate without `values` should work when *level* is set:', function() {
+ checkTextTemplate([{
+ type: 'sunburst',
+ level: 'Seth',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
+ }], 'g.slicetext', [
+ ['color: %{color}', ['color: #1f77b4', 'color: #1f77b4', 'color: #1f77b4']],
+ ['label: %{label}', ['label: Seth', 'label: Enos', 'label: Noam']],
+ ['text: %{text}', ['text: twelve', 'text: ten', 'text: two']],
+ ['path: %{currentPath}', ['path: Eve/', 'path: Eve/Seth', 'path: Eve/Seth/']],
+ ['%{percentRoot} of %{root}', ['33% of Eve', '17% of Eve', '17% of Eve']],
+ ['%{percentEntry} of %{entry}', ['100% of Seth', '50% of Seth', '50% of Seth']],
+ ['%{percentParent} of %{parent}', ['33% of Eve', '50% of Seth', '50% of Seth']],
+ ], /* skipEtra */ true);
+});
+
+describe('Test sunburst texttemplate with *total* `values` should work when *level* is set:', function() {
+ checkTextTemplate([{
+ type: 'sunburst',
+ level: 'Seth',
+ branchvalues: 'total',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ values: [65, 14, 12, 10, 2, 6, 6, 1, 4],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
+ }], 'g.slicetext', [
+ ['color: %{color}', ['color: #ff7f0e', 'color: #ff7f0e', 'color: #ff7f0e']],
+ ['label: %{label}', ['label: Seth', 'label: Enos', 'label: Noam']],
+ ['text: %{text}', ['text: twelve', 'text: ten', 'text: two']],
+ ['path: %{currentPath}', ['path: Eve/', 'path: Eve/Seth', 'path: Eve/Seth/']],
+ ['%{percentRoot} of %{root}', ['18% of Eve', '15% of Eve', '3% of Eve']],
+ ['%{percentEntry} of %{entry}', ['100% of Seth', '83% of Seth', '17% of Seth']],
+ ['%{percentParent} of %{parent}', ['18% of Eve', '83% of Seth', '17% of Seth']],
+ ], /* skipEtra */ true);
+});
+
+describe('Test sunburst texttemplate with *remainder* `values` should work when *level* is set:', function() {
+ checkTextTemplate([{
+ type: 'sunburst',
+ level: 'Seth',
+ branchvalues: 'remainder',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ values: [65, 14, 12, 10, 2, 6, 6, 1, 4],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
+ }], 'g.slicetext', [
+ ['color: %{color}', ['color: #1f77b4', 'color: #1f77b4', 'color: #1f77b4']],
+ ['label: %{label}', ['label: Seth', 'label: Enos', 'label: Noam']],
+ ['text: %{text}', ['text: twelve', 'text: ten', 'text: two']],
+ ['path: %{currentPath}', ['path: Eve/', 'path: Eve/Seth', 'path: Eve/Seth/']],
+ ['%{percentRoot} of %{root}', ['20% of Eve', '8% of Eve', '2% of Eve']],
+ ['%{percentEntry} of %{entry}', ['100% of Seth', '42% of Seth', '8% of Seth']],
+ ['%{percentParent} of %{parent}', ['20% of Eve', '42% of Seth', '8% of Seth']],
+ ], /* skipEtra */ true);
+});
diff --git a/test/jasmine/tests/treemap_test.js b/test/jasmine/tests/treemap_test.js
new file mode 100644
index 00000000000..b812e58b282
--- /dev/null
+++ b/test/jasmine/tests/treemap_test.js
@@ -0,0 +1,1487 @@
+var Plotly = require('@lib');
+var Plots = require('@src/plots/plots');
+var Lib = require('@src/lib');
+var Drawing = require('@src/components/drawing');
+var constants = require('@src/traces/treemap/constants');
+
+var d3 = require('d3');
+var supplyAllDefaults = require('../assets/supply_defaults');
+var createGraphDiv = require('../assets/create_graph_div');
+var destroyGraphDiv = require('../assets/destroy_graph_div');
+var mouseEvent = require('../assets/mouse_event');
+var delay = require('../assets/delay');
+var failTest = require('../assets/fail_test');
+
+var customAssertions = require('../assets/custom_assertions');
+var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle;
+var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
+var checkTextTemplate = require('../assets/check_texttemplate');
+
+function _mouseEvent(type, gd, v) {
+ return function() {
+ if(Array.isArray(v)) {
+ // px-based position
+ mouseEvent(type, v[0], v[1]);
+ } else {
+ // position from slice number
+ var gd3 = d3.select(gd);
+ var el = gd3.select('.slice:nth-child(' + v + ')').node();
+ mouseEvent(type, 0, 0, {element: el});
+ }
+ };
+}
+
+function hover(gd, v) {
+ return _mouseEvent('mouseover', gd, v);
+}
+
+function unhover(gd, v) {
+ return _mouseEvent('mouseout', gd, v);
+}
+
+function click(gd, v) {
+ return _mouseEvent('click', gd, v);
+}
+
+describe('Test treemap defaults:', function() {
+ var gd;
+ var fullData;
+
+ function _supply(opts, layout) {
+ gd = {};
+ opts = Array.isArray(opts) ? opts : [opts];
+
+ gd.data = opts.map(function(o) {
+ return Lib.extendFlat({type: 'treemap'}, o || {});
+ });
+ gd.layout = layout || {};
+
+ supplyAllDefaults(gd);
+ fullData = gd._fullData;
+ }
+
+ it('should set *visible:false* when *labels* or *parents* is missing', function() {
+ _supply([
+ {labels: [1], parents: ['']},
+ {labels: [1]},
+ {parents: ['']}
+ ]);
+
+ expect(fullData[0].visible).toBe(true, 'base');
+ expect(fullData[1].visible).toBe(false, 'no parents');
+ expect(fullData[2].visible).toBe(false, 'no labels');
+ });
+
+ it('should only coerce *count* when the *values* array is not present', function() {
+ _supply([
+ {labels: [1], parents: ['']},
+ {labels: [1], parents: [''], values: []},
+ {labels: [1], parents: [''], values: [1]}
+ ]);
+
+ expect(fullData[0].count).toBe('leaves');
+ expect(fullData[1].count).toBe('leaves', 'has empty values');
+ expect(fullData[2].count).toBe(undefined, 'has values');
+ });
+
+ it('should not coerce *branchvalues* when *values* is not set', function() {
+ _supply([
+ {labels: [1], parents: [''], values: [1]},
+ {labels: [1], parents: ['']},
+ ]);
+
+ expect(fullData[0].branchvalues).toBe('remainder', 'base');
+ expect(fullData[1].branchvalues).toBe(undefined, 'no values');
+ });
+
+ it('should use *paper_bgcolor* as *marker.line.color* default', function() {
+ _supply([
+ {labels: [1], parents: [''], marker: {line: {color: 'red'}}},
+ {labels: [1], parents: ['']}
+ ], {
+ paper_bgcolor: 'orange'
+ });
+
+ expect(fullData[0].marker.line.color).toBe('red', 'set color');
+ expect(fullData[1].marker.line.color).toBe('orange', 'using dflt');
+ });
+
+ it('should not coerce *marker.line.color* when *marker.line.width* is 0', function() {
+ _supply([
+ {labels: [1], parents: [''], marker: {line: {width: 0}}},
+ {labels: [1], parents: ['']}
+ ]);
+
+ expect(fullData[0].marker.line.color).toBe(undefined, 'not coerced');
+ expect(fullData[1].marker.line.color).toBe('#fff', 'dflt');
+ });
+
+ it('should not coerce *marker.opacitybase*, *marker.opacitybase* and *pathbar.opacity* when having *colorscale*', function() {
+ _supply([
+ {labels: [1], parents: ['']},
+ {labels: [1], parents: [''], marker: {colorscale: 'Blues'}}
+ ]);
+
+ expect(fullData[0].marker.opacitybase).toBe(0.5);
+ expect(fullData[0].marker.opacitystep).toBe(0.5);
+ expect(fullData[0].pathbar.opacity).toBe(0.5);
+ expect(fullData[1].marker.opacitybase).toBe(undefined, 'not coerced');
+ expect(fullData[1].marker.opacitystep).toBe(undefined, 'not coerced');
+ expect(fullData[1].pathbar.opacity).toBe(undefined, 'not coerced');
+ });
+
+ it('should use *textfont.size* to adjust top, bottom , left and right *marker.pad* defaults', function() {
+ _supply([
+ {labels: [1], parents: ['']},
+ {labels: [1], parents: [''], textfont: {size: 24}},
+ {labels: [1], parents: [''], textposition: 'bottom left'},
+ {labels: [1], parents: [''], textposition: 'bottom center'},
+ {labels: [1], parents: [''], textposition: 'bottom right'},
+ {labels: [1], parents: [''], textposition: 'middle left'},
+ {labels: [1], parents: [''], textposition: 'middle center'},
+ {labels: [1], parents: [''], textposition: 'middle right'},
+ {labels: [1], parents: [''], textposition: 'top left'},
+ {labels: [1], parents: [''], textposition: 'tpo center'},
+ {labels: [1], parents: [''], textposition: 'top right'}
+ ]);
+
+ expect(fullData[0].textfont.size).toBe(12);
+ expect(fullData[0].marker.pad.t).toBe(24, 'twice of default textfont.size');
+ expect(fullData[0].marker.pad.l).toBe(6, 'half of default textfont.size');
+ expect(fullData[0].marker.pad.r).toBe(6, 'half of default textfont.size');
+ expect(fullData[0].marker.pad.b).toBe(6, 'half of default textfont.size');
+
+ expect(fullData[1].textfont.size).toBe(24);
+ expect(fullData[1].marker.pad.t).toBe(48, 'twice of increased textfont.size');
+ expect(fullData[1].marker.pad.l).toBe(12, 'half of increased textfont.size');
+ expect(fullData[1].marker.pad.r).toBe(12, 'half of increased textfont.size');
+ expect(fullData[1].marker.pad.b).toBe(12, 'half of increased textfont.size');
+
+ var i;
+ for(i = 0 + 2; i < 3 + 2; i++) {
+ expect(fullData[i].marker.pad.t).toBe(6, 'half of default textfont.size', 'with textposition:' + fullData[i].textposition);
+ expect(fullData[i].marker.pad.l).toBe(6, 'half of default textfont.size', 'with textposition:' + fullData[i].textposition);
+ expect(fullData[i].marker.pad.r).toBe(6, 'half of default textfont.size', 'with textposition:' + fullData[i].textposition);
+ expect(fullData[i].marker.pad.b).toBe(24, 'twice of default textfont.size', 'with textposition:' + fullData[i].textposition);
+ }
+ for(i = 0 + 5; i < 6 + 5; i++) {
+ expect(fullData[i].marker.pad.t).toBe(24, 'twice of default textfont.size', 'with textposition:' + fullData[i].textposition);
+ expect(fullData[i].marker.pad.l).toBe(6, 'half of default textfont.size', 'with textposition:' + fullData[i].textposition);
+ expect(fullData[i].marker.pad.r).toBe(6, 'half of default textfont.size', 'with textposition:' + fullData[i].textposition);
+ expect(fullData[i].marker.pad.b).toBe(6, 'half of default textfont.size', 'with textposition:' + fullData[i].textposition);
+ }
+ });
+
+ it('should not include "text" flag in *textinfo* when *text* is set', function() {
+ _supply([
+ {labels: [1], parents: [''], text: ['A']},
+ {labels: [1], parents: ['']}
+ ]);
+
+ expect(fullData[0].textinfo).toBe('text+label', 'with text');
+ expect(fullData[1].textinfo).toBe('label', 'no text');
+ });
+
+ it('should use *layout.colorway* as dflt for *treemapcolorway*', function() {
+ _supply([
+ {labels: [1], parents: ['']}
+ ], {
+ colorway: ['red', 'blue', 'green']
+ });
+ expect(gd._fullLayout.treemapcolorway)
+ .toEqual(['red', 'blue', 'green'], 'dflt to layout colorway');
+
+ _supply([
+ {labels: [1], parents: ['']}
+ ], {
+ colorway: ['red', 'blue', 'green'],
+ treemapcolorway: ['cyan', 'yellow', 'black']
+ });
+ expect(gd._fullLayout.treemapcolorway)
+ .toEqual(['cyan', 'yellow', 'black'], 'user-defined value');
+ });
+
+ it('should only coerce *squarifyratio* when *tiling.packing* is *squarify*', function() {
+ _supply([
+ {labels: [1], parents: ['']},
+ {labels: [1], parents: [''], tiling: {packing: 'binary'}},
+ {labels: [1], parents: [''], tiling: {packing: 'slice'}},
+ {labels: [1], parents: [''], tiling: {packing: 'dice'}},
+ {labels: [1], parents: [''], tiling: {packing: 'slice-dice'}},
+ {labels: [1], parents: [''], tiling: {packing: 'dice-slice'}}
+ ]);
+
+ expect(fullData[0].tiling.squarifyratio).toBe(1);
+ expect(fullData[1].tiling.squarifyratio).toBe(undefined, 'no squarify');
+ expect(fullData[2].tiling.squarifyratio).toBe(undefined, 'no squarify');
+ expect(fullData[3].tiling.squarifyratio).toBe(undefined, 'no squarify');
+ expect(fullData[4].tiling.squarifyratio).toBe(undefined, 'no squarify');
+ expect(fullData[5].tiling.squarifyratio).toBe(undefined, 'no squarify');
+ });
+
+ it('should not coerce *pathbar* attributes when *pathbar.visible* is false', function() {
+ _supply([
+ {labels: [1], parents: [''], pathbar: {visible: false}}
+ ]);
+
+ expect(fullData[0].pathbar.visible).toBe(false);
+ expect(fullData[0].pathbar.textfont).toBe(undefined);
+ expect(fullData[0].pathbar.thickness).toBe(undefined);
+ expect(fullData[0].pathbar.side).toBe(undefined);
+ expect(fullData[0].pathbar.edgeshape).toBe(undefined);
+ });
+
+ it('should set *pathbar.visible* to true by default', function() {
+ _supply([
+ {labels: [1], parents: ['']}
+ ]);
+
+ expect(fullData[0].pathbar.visible).toBe(true);
+ });
+
+ it('should set *pathbar.visible* to true by default', function() {
+ _supply([
+ {labels: [1], parents: ['']}
+ ]);
+
+ expect(fullData[0].pathbar.textfont.family).toBe('"Open Sans", verdana, arial, sans-serif');
+ expect(fullData[0].pathbar.textfont.color).toBe('#444');
+ expect(fullData[0].pathbar.textfont.size).toBe(12);
+ expect(fullData[0].pathbar.thickness).toBe(18);
+ expect(fullData[0].pathbar.side).toBe('top');
+ expect(fullData[0].pathbar.edgeshape).toBe('>');
+ });
+
+ it('should default *pathbar* sizes and styles to layout', function() {
+ _supply([
+ {labels: [1], parents: ['']}
+ ], {
+ font: {family: 'Times New Romans', color: '#ABC', size: 24}
+ });
+
+ expect(fullData[0].pathbar.textfont.family).toBe('Times New Romans');
+ expect(fullData[0].pathbar.textfont.color).toBe('#ABC');
+ expect(fullData[0].pathbar.textfont.size).toBe(24);
+ expect(fullData[0].pathbar.thickness).toBe(30);
+ });
+});
+
+describe('Test treemap calc:', function() {
+ var gd;
+
+ beforeEach(function() {
+ spyOn(Lib, 'warn');
+ });
+
+ function _calc(opts, layout) {
+ gd = {};
+ opts = Array.isArray(opts) ? opts : [opts];
+
+ gd.data = opts.map(function(o) {
+ return Lib.extendFlat({type: 'treemap'}, o || {});
+ });
+ gd.layout = layout || {};
+
+ supplyAllDefaults(gd);
+ Plots.doCalcdata(gd);
+ }
+
+ function extract(k) {
+ var out = gd.calcdata.map(function(cd) {
+ return cd.map(function(pt) { return pt[k]; });
+ });
+ return out.length > 1 ? out : out[0];
+ }
+
+ function extractPt(k) {
+ var out = gd.calcdata.map(function(cd) {
+ return cd[0].hierarchy.descendants().map(function(pt) {
+ return pt[k];
+ });
+ });
+ return out.length > 1 ? out : out[0];
+ }
+
+ it('should generate *id* when it can', function() {
+ _calc({
+ labels: ['Root', 'A', 'B', 'b'],
+ parents: ['', 'Root', 'Root', 'B']
+ });
+
+ expect(extract('id')).toEqual(['Root', 'A', 'B', 'b']);
+ expect(Lib.warn).toHaveBeenCalledTimes(0);
+ });
+
+ it('should generate "implied root" when it can', function() {
+ _calc({
+ labels: [ 'A', 'B', 'b'],
+ parents: ['Root', 'Root', 'B']
+ });
+
+ expect(extract('id')).toEqual(['Root', 'A', 'B', 'b']);
+ expect(extract('pid')).toEqual(['', 'Root', 'Root', 'B']);
+ expect(extract('label')).toEqual(['Root', 'A', 'B', 'b']);
+ expect(Lib.warn).toHaveBeenCalledTimes(0);
+ });
+
+ it('should warn when there are multiple implied roots', function() {
+ _calc({
+ labels: [ 'A', 'B', 'b'],
+ parents: ['Root1', 'Root22', 'B']
+ });
+
+ expect(Lib.warn).toHaveBeenCalledTimes(1);
+ expect(Lib.warn).toHaveBeenCalledWith('Multiple implied roots, cannot build treemap hierarchy.');
+ });
+
+ it('should generate "root of roots" when it can', function() {
+ spyOn(Lib, 'randstr').and.callFake(function() {
+ return 'dummy';
+ });
+
+ _calc({
+ labels: [ 'A', 'B', 'b'],
+ parents: ['', '', 'B']
+ });
+
+ expect(extract('id')).toEqual(['dummy', 'A', 'B', 'b']);
+ expect(extract('pid')).toEqual(['', 'dummy', 'dummy', 'B']);
+ expect(extract('label')).toEqual([undefined, 'A', 'B', 'b']);
+ });
+
+ it('should compute hierarchy values', function() {
+ var labels = ['Root', 'A', 'B', 'b'];
+ var parents = ['', 'Root', 'Root', 'B'];
+
+ _calc([
+ {labels: labels, parents: parents, count: 'leaves+branches'},
+ {labels: labels, parents: parents, count: 'branches'},
+ {labels: labels, parents: parents}, // N.B. counts 'leaves' in this case
+ {labels: labels, parents: parents, values: [0, 1, 2, 3]},
+ {labels: labels, parents: parents, values: [30, 20, 10, 5], branchvalues: 'total'}
+ ]);
+
+ expect(extractPt('value')).toEqual([
+ [4, 2, 1, 1],
+ [2, 1, 0, 0],
+ [2, 1, 1, 1],
+ [6, 5, 1, 3],
+ [30, 20, 10, 5]
+ ]);
+ expect(Lib.warn).toHaveBeenCalledTimes(0);
+ });
+
+ it('should warn when values under *branchvalues:total* do not add up and not show trace', function() {
+ _calc({
+ labels: ['Root', 'A', 'B', 'b'],
+ parents: ['', 'Root', 'Root', 'B'],
+ values: [0, 1, 2, 3],
+ branchvalues: 'total'
+ });
+
+ expect(gd.calcdata[0][0].hierarchy).toBe(undefined, 'no computed hierarchy');
+
+ expect(Lib.warn).toHaveBeenCalledTimes(2);
+ expect(Lib.warn.calls.allArgs()[0][0]).toBe('Total value for node Root is smaller than the sum of its children. \nparent value = 0 \nchildren sum = 3');
+ expect(Lib.warn.calls.allArgs()[1][0]).toBe('Total value for node B is smaller than the sum of its children. \nparent value = 2 \nchildren sum = 3');
+ });
+
+ it('should warn labels/parents lead to ambiguous hierarchy', function() {
+ _calc({
+ labels: ['Root', 'A', 'A', 'B'],
+ parents: ['', 'Root', 'Root', 'A']
+ });
+
+ expect(Lib.warn).toHaveBeenCalledTimes(1);
+ expect(Lib.warn).toHaveBeenCalledWith('Failed to build treemap hierarchy. Error: ambiguous: A');
+ });
+
+ it('should warn ids/parents lead to ambiguous hierarchy', function() {
+ _calc({
+ labels: ['label 1', 'label 2', 'label 3', 'label 4'],
+ ids: ['a', 'b', 'b', 'c'],
+ parents: ['', 'a', 'a', 'b']
+ });
+
+ expect(Lib.warn).toHaveBeenCalledTimes(1);
+ expect(Lib.warn).toHaveBeenCalledWith('Failed to build treemap hierarchy. Error: ambiguous: b');
+ });
+
+ it('should accept numbers (even `0`) are ids/parents items', function() {
+ _calc({
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ ids: [0, 1, 2, 3, 4, 5, 6, 7, 8],
+ parents: ['', 0, 0, 2, 2, 0, 0, 6, 0]
+ });
+
+ expect(extract('id')).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']);
+ expect(extract('pid')).toEqual(['', '0', '0', '2', '2', '0', '0', '6', '0']);
+ });
+
+ it('should accept mix typed are ids/parents items', function() {
+ _calc({
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ ids: [true, 1, '2', 3, 4, 5, 6, 7, 8],
+ parents: ['', true, true, 2, 2, 'true', 'true', '6', true]
+ });
+
+ expect(extract('id')).toEqual(['true', '1', '2', '3', '4', '5', '6', '7', '8']);
+ expect(extract('pid')).toEqual(['', 'true', 'true', '2', '2', 'true', 'true', '6', 'true']);
+ });
+});
+
+describe('Test treemap hover:', function() {
+ var gd;
+
+ var labels0 = ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'];
+ var parents0 = ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve'];
+ var values0 = [10, 14, 12, 10, 2, 6, 6, 1, 4];
+
+ afterEach(destroyGraphDiv);
+
+ function run(spec) {
+ gd = createGraphDiv();
+
+ var data = (spec.traces || [{}]).map(function(t) {
+ t.type = 'treemap';
+ if(!t.labels) t.labels = labels0.slice();
+ if(!t.parents) t.parents = parents0.slice();
+ return t;
+ });
+
+ var layout = Lib.extendFlat({
+ width: 500,
+ height: 500,
+ margin: {t: 0, b: 0, l: 0, r: 0, pad: 0}
+ }, spec.layout || {});
+
+ var exp = spec.exp || {};
+ var ptData = null;
+
+ return Plotly.plot(gd, data, layout)
+ .then(function() {
+ gd.once('plotly_hover', function(d) { ptData = d.points[0]; });
+ })
+ .then(hover(gd, spec.pos))
+ .then(function() {
+ assertHoverLabelContent(exp.label);
+
+ for(var k in exp.ptData) {
+ expect(ptData[k]).toBe(exp.ptData[k], 'pt event data key ' + k);
+ }
+
+ if(exp.style) {
+ var gd3 = d3.select(gd);
+ assertHoverLabelStyle(gd3.select('.hovertext'), exp.style);
+ }
+ });
+ }
+
+ [{
+ desc: 'base',
+ pos: 2,
+ exp: {
+ label: {
+ nums: 'Seth',
+ },
+ ptData: {
+ curveNumber: 0,
+ pointNumber: 2,
+ label: 'Seth',
+ parent: 'Eve'
+ }
+ }
+ }, {
+ desc: 'with scalar hovertext',
+ traces: [{ hovertext: 'A' }],
+ pos: 3,
+ exp: {
+ label: {
+ nums: 'Cain\nA',
+ },
+ ptData: {
+ curveNumber: 0,
+ pointNumber: 1,
+ label: 'Cain',
+ parent: 'Eve'
+ }
+ }
+ }, {
+ desc: 'with array hovertext',
+ traces: [{
+ hovertext: values0,
+ hoverinfo: 'all'
+ }],
+ pos: 4,
+ exp: {
+ label: {
+ nums: 'Abel\nEve/\n17% of Eve\n6',
+ name: 'trace 0'
+ },
+ ptData: {
+ curveNumber: 0,
+ pointNumber: 5,
+ label: 'Abel',
+ parent: 'Eve'
+ }
+ }
+ }, {
+ desc: 'with hoverlabel.namelength set ',
+ traces: [{
+ hoverlabel: {namelength: 4},
+ hoverinfo: 'all'
+ }],
+ pos: 4,
+ exp: {
+ label: {
+ nums: 'Abel\nEve/\n17% of Eve',
+ name: 't...'
+ },
+ ptData: {
+ curveNumber: 0,
+ pointNumber: 5,
+ label: 'Abel',
+ parent: 'Eve'
+ }
+ }
+ }, {
+ desc: 'with values',
+ traces: [{
+ values: values0,
+ hoverinfo: 'value'
+ }],
+ pos: 5,
+ exp: {
+ label: {
+ nums: '6'
+ },
+ ptData: {
+ curveNumber: 0,
+ pointNumber: 5,
+ label: 'Abel',
+ parent: 'Eve',
+ value: 6
+ }
+ }
+ }, {
+ desc: 'with values and hovertemplate',
+ traces: [{
+ values: values0,
+ hovertemplate: '%{label} :: %{value:.2f}N.B.'
+ }],
+ pos: 5,
+ exp: {
+ label: {
+ nums: 'Abel :: 6.00',
+ name: 'N.B.'
+ },
+ ptData: {
+ curveNumber: 0,
+ pointNumber: 5,
+ label: 'Abel',
+ parent: 'Eve',
+ value: 6
+ }
+ }
+ }, {
+ desc: 'with array hovertemplate and label styling',
+ traces: [{
+ hovertemplate: parents0.map(function(p) {
+ return p ?
+ '%{label} -| %{parent}' :
+ '%{label}THE ROOT';
+ }),
+ hoverlabel: {
+ bgcolor: 'red',
+ bordercolor: 'blue',
+ font: {
+ size: 20,
+ family: 'Roboto',
+ color: 'orange'
+ }
+ }
+ }],
+ pos: 1,
+ exp: {
+ label: {
+ nums: 'Eve',
+ name: 'THE ROOT'
+ },
+ style: {
+ bgcolor: 'rgb(255, 0, 0)',
+ bordercolor: 'rgb(0, 0, 255)',
+ fontSize: 20,
+ fontFamily: 'Roboto',
+ fontColor: 'rgb(255, 165, 0)'
+ },
+ ptData: {
+ curveNumber: 0,
+ pointNumber: 0,
+ label: 'Eve',
+ parent: ''
+ }
+ }
+ }]
+ .forEach(function(spec) {
+ it('should generate correct hover labels and event data - ' + spec.desc, function(done) {
+ run(spec).catch(failTest).then(done);
+ });
+ });
+});
+
+describe('Test treemap hover lifecycle:', function() {
+ var gd;
+ var hoverData;
+ var unhoverData;
+ var hoverCnt;
+ var unhoverCnt;
+
+ beforeEach(function() { gd = createGraphDiv(); });
+
+ afterEach(destroyGraphDiv);
+
+ function setupListeners() {
+ hoverData = null;
+ unhoverData = null;
+ hoverCnt = 0;
+ unhoverCnt = 0;
+
+ return function() {
+ gd.on('plotly_hover', function(d) {
+ hoverData = d;
+ hoverCnt++;
+ });
+ gd.on('plotly_unhover', function(d) {
+ unhoverData = d;
+ unhoverCnt++;
+ });
+ };
+ }
+
+ it('should fire the correct events', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/treemap_first.json'));
+
+ Plotly.plot(gd, mock)
+ .then(setupListeners())
+ .then(hover(gd, 1))
+ .then(function() {
+ if(hoverCnt === 1) {
+ expect(hoverData.event).toBeDefined();
+ expect(hoverData.points[0].label).toBe('Eve');
+ } else {
+ fail('did not trigger correct # of plotly_hover events');
+ }
+
+ if(unhoverCnt) {
+ fail('should not have triggered plotly_unhover');
+ }
+ })
+ .then(unhover(gd, 1))
+ .then(hover(gd, 2))
+ .then(function() {
+ if(hoverCnt === 2) {
+ expect(hoverData.event).toBeDefined();
+ expect(hoverData.points[0].label).toBe('Seth');
+ } else {
+ fail('did not trigger correct # of plotly_hover events');
+ }
+
+ if(unhoverCnt === 1) {
+ expect(unhoverData.event).toBeDefined();
+ expect(unhoverData.points[0].label).toBe('Eve');
+ } else {
+ fail('did not trigger correct # of plotly_unhover events');
+ }
+ })
+ .catch(failTest)
+ .then(done);
+ });
+});
+
+describe('Test treemap clicks:', function() {
+ var gd;
+ var trackers;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ trackers = {};
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function setupListeners(opts) {
+ opts = opts || {};
+
+ trackers.treemapclick = [];
+ trackers.click = [];
+ trackers.animating = [];
+
+ // use `.unshift` that way to latest event data object
+ // will be in entry [0], which is easier to pick out
+
+ return function() {
+ gd.on('plotly_treemapclick', function(d) {
+ trackers.treemapclick.unshift(d);
+ if(opts.turnOffAnimation) return false;
+ });
+ gd.on('plotly_click', function(d) {
+ trackers.click.unshift(d);
+ });
+ gd.on('plotly_animating', function() {
+ // N.B. does not emit event data
+ trackers.animating.unshift(true);
+ });
+ };
+ }
+
+ it('should trigger animation when clicking on branches', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/treemap_first.json'));
+
+ Plotly.plot(gd, mock)
+ .then(setupListeners())
+ .then(click(gd, 2))
+ .then(function() {
+ if(trackers.treemapclick.length === 1) {
+ expect(trackers.treemapclick[0].event).toBeDefined();
+ expect(trackers.treemapclick[0].points[0].label).toBe('Seth');
+ } else {
+ fail('incorrect plotly_treemapclick triggering');
+ }
+
+ if(trackers.click.length) {
+ fail('incorrect plotly_click triggering');
+ }
+
+ if(trackers.animating.length !== 1) {
+ fail('incorrect plotly_animating triggering');
+ }
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should trigger plotly_click event when clicking on leaf node', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/treemap_first.json'));
+
+ Plotly.plot(gd, mock)
+ .then(setupListeners())
+ .then(click(gd, 8))
+ .then(function() {
+ if(trackers.treemapclick.length === 1) {
+ expect(trackers.treemapclick[0].event).toBeDefined();
+ expect(trackers.treemapclick[0].points[0].label).toBe('Noam');
+ } else {
+ fail('incorrect plotly_treemapclick triggering');
+ }
+
+ if(trackers.click.length === 1) {
+ expect(trackers.click[0].event).toBeDefined();
+ expect(trackers.click[0].points[0].label).toBe('Noam');
+ }
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should not trigger animation when graph is transitioning', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/treemap_first.json'));
+
+ // should be same before and after 2nd click
+ function _assertCommon(msg) {
+ if(trackers.click.length) {
+ fail('incorrect plotly_click triggering - ' + msg);
+ }
+ if(trackers.animating.length !== 1) {
+ fail('incorrect plotly_animating triggering - ' + msg);
+ }
+ }
+
+ Plotly.plot(gd, mock)
+ .then(setupListeners())
+ .then(click(gd, 2))
+ .then(function() {
+ var msg = 'after 1st click';
+
+ if(trackers.treemapclick.length === 1) {
+ expect(trackers.treemapclick[0].event).toBeDefined(msg);
+ expect(trackers.treemapclick[0].points[0].label).toBe('Seth', msg);
+ } else {
+ fail('incorrect plotly_treemapclick triggering - ' + msg);
+ }
+
+ _assertCommon(msg);
+ })
+ .then(click(gd, 4))
+ .then(function() {
+ var msg = 'after 2nd click';
+
+ // should trigger plotly_treemapclick twice, but not additional
+ // plotly_click nor plotly_animating
+
+ if(trackers.treemapclick.length === 2) {
+ expect(trackers.treemapclick[0].event).toBeDefined(msg);
+ expect(trackers.treemapclick[0].points[0].label).toBe('Awan', msg);
+ } else {
+ fail('incorrect plotly_treemapclick triggering - ' + msg);
+ }
+
+ _assertCommon(msg);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should be able to override default click behavior using plotly_treemapclick handler ()', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/treemap_first.json'));
+
+ Plotly.plot(gd, mock)
+ .then(setupListeners({turnOffAnimation: true}))
+ .then(click(gd, 2))
+ .then(function() {
+ if(trackers.treemapclick.length === 1) {
+ expect(trackers.treemapclick[0].event).toBeDefined();
+ expect(trackers.treemapclick[0].points[0].label).toBe('Seth');
+ } else {
+ fail('incorrect plotly_treemapclick triggering');
+ }
+
+ if(trackers.click.length === 1) {
+ expect(trackers.click[0].event).toBeDefined();
+ expect(trackers.click[0].points[0].label).toBe('Seth');
+ } else {
+ fail('incorrect plotly_click triggering');
+ }
+
+ if(trackers.animating.length !== 0) {
+ fail('incorrect plotly_animating triggering');
+ }
+ })
+ .catch(failTest)
+ .then(done);
+ });
+});
+
+describe('Test treemap restyle:', function() {
+ var gd;
+
+ beforeEach(function() { gd = createGraphDiv(); });
+
+ afterEach(destroyGraphDiv);
+
+ function _restyle(updateObj) {
+ return function() { return Plotly.restyle(gd, updateObj); };
+ }
+
+ it('should be able to toggle visibility', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/treemap_first.json'));
+
+ function _assert(msg, exp) {
+ return function() {
+ var layer = d3.select(gd).select('.treemaplayer');
+ expect(layer.selectAll('.trace').size()).toBe(exp, msg);
+ };
+ }
+
+ Plotly.plot(gd, mock)
+ .then(_assert('base', 2))
+ .then(_restyle({'visible': false}))
+ .then(_assert('both visible:false', 0))
+ .then(_restyle({'visible': true}))
+ .then(_assert('back to visible:true', 2))
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should be able to restyle *maxdepth* and *level* w/o recomputing the hierarchy', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/treemap_coffee.json'));
+
+ function _assert(msg, exp) {
+ return function() {
+ var layer = d3.select(gd).select('.treemaplayer');
+
+ expect(layer.selectAll('.slice').size()).toBe(exp, msg);
+
+ // editType:plot
+ if(msg !== 'base') {
+ expect(Plots.doCalcdata).toHaveBeenCalledTimes(0);
+ }
+ };
+ }
+
+ Plotly.plot(gd, mock)
+ .then(_assert('base', 97))
+ .then(function() {
+ spyOn(Plots, 'doCalcdata').and.callThrough();
+ })
+ .then(_restyle({maxdepth: 3}))
+ .then(_assert('with maxdepth:3', 97))
+ .then(_restyle({level: 'Aromas'}))
+ .then(_assert('with non-root level', 67))
+ .then(_restyle({maxdepth: null, level: null}))
+ .then(_assert('back to first view', 97))
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should be able to restyle *marker.opacitybase* and *marker.opacitystep*', function(done) {
+ var mock = {
+ data: [{
+ type: 'treemap', pathbar: { visible: false },
+ labels: ['Root', 'A', 'B', 'b', 'b2', 'b3'],
+ parents: ['', 'Root', 'Root', 'B', 'b', 'b2']
+ }]
+ };
+
+ function _assert(msg, exp) {
+ return function() {
+ var layer = d3.select(gd).select('.treemaplayer');
+
+ var opacities = [];
+ layer.selectAll('path.surface').each(function() {
+ opacities.push(this.style.opacity);
+ });
+
+ expect(opacities).toEqual(exp, msg);
+
+ // editType:style
+ if(msg !== 'base') {
+ expect(Plots.doCalcdata).toHaveBeenCalledTimes(0);
+ expect(gd._fullData[0]._module.plot).toHaveBeenCalledTimes(0);
+ }
+ };
+ }
+
+ Plotly.plot(gd, mock)
+ .then(_assert('base', ['0', '1', '0.5', '0.5', '1', '1']))
+ .then(function() {
+ spyOn(Plots, 'doCalcdata').and.callThrough();
+ spyOn(gd._fullData[0]._module, 'plot').and.callThrough();
+ })
+ .then(_restyle({'marker.opacitybase': 0.2}))
+ .then(_assert('lower marker.opacitybase', ['0', '1', '0.2', '0.5', '1', '1']))
+ .then(_restyle({'marker.opacitystep': 0.1}))
+ .then(_assert('lower marker.opacitystep', ['0', '1', '0.2', '0.1', '0.2', '1']))
+ .then(_restyle({'marker.opacitybase': 0.8}))
+ .then(_assert('raise marker.opacitybase', ['0', '1', '0.8', '0.1', '0.2', '1']))
+ .then(_restyle({'marker.opacitybase': null}))
+ .then(_assert('back to dflt', ['0', '1', '0.5', '0.1', '0.2', '1']))
+ .then(_restyle({'marker.opacitystep': null}))
+ .then(_assert('back to dflt', ['0', '1', '0.5', '0.5', '1', '1']))
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should be able to restyle *pathbar.opacity*', function(done) {
+ var mock = {
+ data: [{
+ type: 'treemap',
+ labels: ['Root', 'A', 'B', 'b', 'b2', 'b3'],
+ parents: ['', 'Root', 'Root', 'B', 'b', 'b2'],
+ level: 'b'
+ }]
+ };
+
+ function _assert(msg, exp) {
+ return function() {
+ var layer = d3.select(gd).select('.treemaplayer');
+
+ var opacities = [];
+ layer.selectAll('path.surface').each(function() {
+ opacities.push(this.style.opacity);
+ });
+
+ expect(opacities).toEqual(exp, msg);
+
+ // editType:style
+ if(msg !== 'base') {
+ expect(Plots.doCalcdata).toHaveBeenCalledTimes(0);
+ expect(gd._fullData[0]._module.plot).toHaveBeenCalledTimes(0);
+ }
+ };
+ }
+
+ Plotly.plot(gd, mock)
+ .then(_assert('base', ['0.5', '0.5', '1', '0.5', '0.5']))
+ .then(function() {
+ spyOn(Plots, 'doCalcdata').and.callThrough();
+ spyOn(gd._fullData[0]._module, 'plot').and.callThrough();
+ })
+ .then(_restyle({'pathbar.opacity': 0.2}))
+ .then(_assert('lower pathbar.opacity', ['0.5', '0.5', '1', '0.2', '0.2']))
+ .then(_restyle({'pathbar.opacity': 0.8}))
+ .then(_assert('raise pathbar.opacity', ['0.5', '0.5', '1', '0.8', '0.8']))
+ .then(_restyle({'pathbar.opacity': null}))
+ .then(_assert('back to dflt', ['0.5', '0.5', '1', '0.5', '0.5']))
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should be able to restyle *textinfo*', function(done) {
+ var mock = {
+ data: [{
+ type: 'treemap',
+ labels: ['Root', 'A', 'B', 'b'],
+ parents: ['', 'Root', 'Root', 'B'],
+ text: ['node0', 'node1', 'node2', 'node3'],
+ values: [0, 1, 2, 3],
+ pathbar: { visible: false }
+ }]
+ };
+
+ function _assert(msg, exp) {
+ return function() {
+ var layer = d3.select(gd).select('.treemaplayer');
+ var tx = [];
+
+ layer.selectAll('text.slicetext').each(function() {
+ var lines = d3.select(this).selectAll('tspan');
+
+ if(lines.size()) {
+ var t = [];
+ lines.each(function() {
+ t.push(this.innerHTML);
+ });
+ tx.push(t.join('\n'));
+ } else {
+ tx.push(this.innerHTML);
+ }
+ });
+
+ expect(tx).toEqual(exp, msg);
+
+ // editType:plot
+ if(msg !== 'base') {
+ expect(Plots.doCalcdata).toHaveBeenCalledTimes(0);
+ }
+ };
+ }
+
+ Plotly.plot(gd, mock)
+ .then(_assert('base', ['Root', 'B', 'A\nnode1', 'b\nnode3']))
+ .then(function() {
+ spyOn(Plots, 'doCalcdata').and.callThrough();
+ })
+ .then(_restyle({textinfo: 'label'}))
+ .then(_assert('just label', ['Root', 'B', 'A', 'b']))
+ .then(_restyle({textinfo: 'value'}))
+ .then(_assert('show input values', ['Root', 'B', '1', '3']))
+ .then(_restyle({textinfo: 'none'}))
+ .then(_assert('no textinfo', ['Root', 'B', ' ', ' '])) // N.B. replaced empty string with space character for better transitions
+ .then(_restyle({textinfo: 'label+text+value'}))
+ .then(_assert('show everything', ['Root', 'B', 'A\n1\nnode1', 'b\n3\nnode3']))
+ .then(_restyle({textinfo: null}))
+ .then(_assert('back to dflt', ['Root', 'B', 'A\nnode1', 'b\nnode3']))
+ .catch(failTest)
+ .then(done);
+ });
+});
+
+describe('Test treemap tweening:', function() {
+ var gd;
+ var pathTweenFnLookup;
+ var textTweenFnLookup;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+
+ // hacky way to track tween functions
+ spyOn(d3.transition.prototype, 'attrTween').and.callFake(function(attrName, fn) {
+ var lookup = {d: pathTweenFnLookup, transform: textTweenFnLookup}[attrName];
+ var pt = this[0][0].__data__;
+ var id = pt.data.data.id;
+
+ // we should never tween the same node twice on a given sector click
+ lookup[id] = lookup[id] ? null : fn(pt);
+ });
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function _reset() {
+ pathTweenFnLookup = {};
+ textTweenFnLookup = {};
+ }
+
+ function _run(gd, v) {
+ _reset();
+ click(gd, v)();
+
+ // 1 second more than the click transition duration
+ return delay(constants.CLICK_TRANSITION_TIME + 1);
+ }
+
+ function trim(s) {
+ return s.replace(/\s/g, '');
+ }
+
+
+ function _assert(msg, attrName, id, exp) {
+ var lookup = {d: pathTweenFnLookup, transform: textTweenFnLookup}[attrName];
+ var fn = lookup[id];
+ // normalize time in [0, 1] where we'll assert the tweening fn output,
+ // asserting at the mid point *should be* representative enough
+ var t = 0.5;
+ var actual = trim(fn(t));
+ var msg2 = msg + ' | node ' + id + ' @t=' + t;
+
+ if(attrName === 'transform') {
+ var fake = {attr: function() { return actual; }};
+ var xy = Drawing.getTranslate(fake);
+ expect([xy.x, xy.y]).toBeWithinArray(exp, 1, msg2);
+ } else {
+ // we could maybe to bring in:
+ // https://github.com/hughsk/svg-path-parser
+ // to make these assertions more readable
+ expect(actual).toBe(trim(exp), msg2);
+ }
+ }
+
+ it('should tween sector exit/update (case: click on branch, no maxdepth)', function(done) {
+ var mock = {
+ data: [{
+ type: 'treemap', pathbar: { visible: false },
+ labels: ['Root', 'A', 'B', 'b'],
+ parents: ['', 'Root', 'Root', 'B']
+ }]
+ };
+
+ Plotly.plot(gd, mock)
+ .then(_run(gd, 3))
+ .then(function() {
+ _assert('exit entry', 'd', 'Root',
+ 'M80,100L620,100L620,370L80,370Z'
+ );
+ _assert('update A to new position', 'd', 'A',
+ 'M83,112L214.25,112L214.25,367L83,367Z'
+ );
+ _assert('update B to new position', 'd', 'B',
+ 'M215.75,112L617,112L617,367L215.75,367Z'
+ );
+ _assert('update b to new position', 'd', 'b',
+ 'M221.75,136L611,136L611,361L221.75,361Z'
+ );
+ _assert('move B text to new position', 'transform', 'B', [221.75126, 0]);
+ _assert('move b text to new position', 'transform', 'b', [224.75150, 0]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should tween sector enter/update (case: click on entry, no maxdepth)', function(done) {
+ var mock = {
+ data: [{
+ type: 'treemap', pathbar: { visible: false },
+ labels: ['Root', 'A', 'B', 'b'],
+ parents: ['', 'Root', 'Root', 'B'],
+ level: 'B'
+ }]
+ };
+
+ Plotly.plot(gd, mock)
+ .then(_run(gd, 1))
+ .then(function() {
+ _assert('enter new entry', 'd', 'Root',
+ 'M80,100L620,100L620,370L80,370Z'
+ );
+ _assert('update A to new position', 'd', 'A',
+ 'M83,112L214.25,112L214.25,367L83,367Z'
+ );
+ _assert('update B to new position', 'd', 'B',
+ 'M215.75,112L617,112L617,367L215.75,367Z'
+ );
+ _assert('update b to new position', 'd', 'b',
+ 'M221.75,136L611,136L611,361L221.75,361Z'
+ );
+ _assert('move B text to new position', 'transform', 'B', [221.75126, 0]);
+ _assert('move b text to new position', 'transform', 'b', [224.75150, 0]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should tween sector enter/update/exit (case: click on entry, maxdepth=2)', function(done) {
+ var mock = {
+ data: [{
+ type: 'treemap', pathbar: { visible: false },
+ labels: ['Root', 'A', 'B', 'b'],
+ parents: ['', 'Root', 'Root', 'B'],
+ maxdepth: 2
+ }]
+ };
+
+ Plotly.plot(gd, mock)
+ .then(_run(gd, 3))
+ .then(function() {
+ _assert('exit entry', 'd', 'Root',
+ 'M80,100L620,100L620,370L80,370Z'
+ );
+ _assert('update A to new position', 'd', 'A',
+ 'M83,112L214.25,112L214.25,367L83,367Z'
+ );
+ _assert('update B to new position', 'd', 'B',
+ 'M215.75,112L617,112L617,367L215.75,367Z'
+ );
+ _assert('enter b for parent position', 'd', 'b',
+ 'M284.375,188.5L548.375,188.5L548.375,308.5L284.375,308.5Z'
+ );
+ _assert('move B text to new position', 'transform', 'B', [220.25126, 0]);
+ _assert('enter b text to new position', 'transform', 'b', [287.375195, 5]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should tween sector enter/update/exit (case: click on entry, maxdepth=2, level=B)', function(done) {
+ var mock = {
+ data: [{
+ type: 'treemap', pathbar: { visible: false },
+ labels: ['Root', 'A', 'B', 'b', 'bb'],
+ parents: ['', 'Root', 'Root', 'B', 'b'],
+ maxdepth: 2,
+ level: 'B'
+ }]
+ };
+
+ Plotly.plot(gd, mock)
+ .then(_run(gd, 1))
+ .then(function() {
+ _assert('exit b', 'd', 'b',
+ 'M284.375,188.5L548.375,188.5L548.375,308.5L284.375,308.5Z'
+ );
+ _assert('enter new entry', 'd', 'Root',
+ 'M80,100L620,100L620,370L80,370Z'
+ );
+ _assert('enter A counterclockwise', 'd', 'A',
+ 'M83,112L214.25,112L214.25,367L83,367Z'
+ );
+ _assert('update B to new position', 'd', 'B',
+ 'M215.75,112L617,112L617,367L215.75,367Z'
+ );
+ })
+ .catch(failTest)
+ .then(done);
+ });
+});
+
+describe('Test treemap interactions edge cases', function() {
+ var gd;
+
+ beforeEach(function() { gd = createGraphDiv(); });
+
+ afterEach(destroyGraphDiv);
+
+ it('should keep tracking hover labels and hover events after *calc* edits', function(done) {
+ var mock = Lib.extendFlat({}, require('@mocks/treemap_first.json'));
+ var hoverCnt = 0;
+ var unhoverCnt = 0;
+
+ // see https://github.com/plotly/plotly.js/issues/3618
+
+ function _assert(msg, exp) {
+ expect(hoverCnt).toBe(exp.hoverCnt, msg + ' - hover cnt');
+ expect(unhoverCnt).toBe(exp.unhoverCnt, msg + ' - unhover cnt');
+
+ var label = d3.select(gd).select('g.hovertext');
+ expect(label.size()).toBe(exp.hoverLabel, msg + ' - hover label cnt');
+
+ hoverCnt = 0;
+ unhoverCnt = 0;
+ }
+
+ Plotly.plot(gd, mock)
+ .then(function() {
+ gd.on('plotly_hover', function() {
+ hoverCnt++;
+ // N.B. trigger a 'plot' edit
+ Plotly.restyle(gd, 'textinfo', 'none');
+ });
+ gd.on('plotly_unhover', function() {
+ unhoverCnt++;
+ // N.B. trigger a 'plot' edit
+ Plotly.restyle(gd, 'textinfo', null);
+ });
+ })
+ .then(hover(gd, 1))
+ .then(function() {
+ _assert('after hovering on first sector', {
+ hoverCnt: 1,
+ unhoverCnt: 0,
+ hoverLabel: 1
+ });
+ })
+ .then(unhover(gd, 1))
+ .then(function() {
+ _assert('after un-hovering from first sector', {
+ hoverCnt: 0,
+ unhoverCnt: 1,
+ hoverLabel: 0
+ });
+ })
+ .then(hover(gd, 2))
+ .then(function() {
+ _assert('after hovering onto second sector', {
+ hoverCnt: 1,
+ unhoverCnt: 0,
+ hoverLabel: 1
+ });
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should show falsy zero text', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'treemap',
+ parents: ['', 'A', 'B', 'C', 'D', 'E', 'F'],
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
+ values: [7, 6, 5, 4, 3, 2, 1],
+ text: [null, '', '0', 0, 1, true, false],
+ textinfo: 'label+text+value'
+ }],
+ layout: {
+ width: 400,
+ height: 400
+ }
+ })
+ .then(hover(gd, 4))
+ .then(function() {
+ assertHoverLabelContent({ nums: 'D\n4\n0' });
+ })
+ .then(done);
+ });
+
+ it('should transition treemap traces only', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/display-text_zero-number.json'));
+ mock.data[0].visible = false;
+ mock.data[1].type = 'treemap';
+ mock.data[1].name = 'treemap';
+
+ function _assert(msg, exp) {
+ var gd3 = d3.select(gd);
+ expect(gd3.select('.cartesianlayer').selectAll('.trace').size())
+ .toBe(exp.cartesianTraceCnt, '# of cartesian traces');
+ expect(gd3.select('.pielayer').selectAll('.trace').size())
+ .toBe(exp.pieTraceCnt, '# of pie traces');
+ expect(gd3.select('.treemaplayer').selectAll('.trace').size())
+ .toBe(exp.treemapTraceCnt, '# of treemap traces');
+ }
+
+ Plotly.plot(gd, mock)
+ .then(function() {
+ _assert('base', {
+ cartesianTraceCnt: 2,
+ pieTraceCnt: 0,
+ treemapTraceCnt: 1
+ });
+ })
+ .then(click(gd, 2))
+ .then(delay(constants.CLICK_TRANSITION_TIME + 1))
+ .then(function() {
+ _assert('after treemap click', {
+ cartesianTraceCnt: 2,
+ pieTraceCnt: 0,
+ treemapTraceCnt: 1
+ });
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should be able to transition treemap traces via `Plotly.react`', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/display-text_zero-number.json'));
+ mock.data[1].type = 'treemap';
+ mock.data[1].name = 'treemap';
+
+ mock.layout.transition = {duration: 200};
+
+ spyOn(Plots, 'transitionFromReact').and.callThrough();
+
+ Plotly.plot(gd, mock)
+ .then(function() {
+ gd.data[1].level = 'B';
+ return Plotly.react(gd, gd.data, gd.layout);
+ })
+ .then(delay(202))
+ .then(function() {
+ expect(Plots.transitionFromReact).toHaveBeenCalledTimes(1);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+});
+
+describe('Test treemap texttemplate without `values` should work:', function() {
+ checkTextTemplate([{
+ type: 'treemap', pathbar: { visible: false },
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
+ }], 'g.slicetext', [
+ ['color: %{color}', ['Eve', 'color: #1f77b4', 'Seth', 'color: #2ca02c', 'color: #d62728', 'color: #9467bd', 'Awan', 'color: #ff7f0e', 'color: #d62728']],
+ ['label: %{label}', ['Eve', 'label: Cain', 'Seth', 'label: Enos', 'label: Noam', 'label: Abel', 'Awan', 'label: Enoch', 'label: Azura']],
+ ['text: %{text}', ['Eve', 'text: fourteen', 'Seth', 'text: ten', 'text: two', 'text: six', 'Awan', 'text: one', 'text: four']],
+ ['path: %{currentPath}', ['Eve', 'path: Eve/', 'Seth', 'path: Eve/', 'path: Eve/', 'path: Eve/Seth/', 'Awan', 'path: Eve/Seth/', 'path: Eve/Awan/']],
+ ['%{percentRoot} of %{root}', ['Eve', '33% of Eve', 'Seth', '17% of Eve', '17% of Eve', '17% of Eve', 'Awan', '17% of Eve', '17% of Eve']],
+ ['%{percentEntry} of %{entry}', ['Eve', '33% of Eve', 'Seth', '17% of Eve', '17% of Eve', '17% of Eve', 'Awan', '17% of Eve', '17% of Eve']],
+ ['%{percentParent} of %{parent}', ['Eve', '100% of Seth', 'Seth', '17% of Eve', '17% of Eve', '17% of Eve', 'Awan', '50% of Seth', '100% of Awan']],
+ [
+ [
+ 'color: %{color}',
+ 'label: %{label}, text: %{text}',
+ 'text: %{text}',
+ 'value: %{value}',
+ '%{percentRoot} of %{root}',
+ '%{percentEntry} of %{entry}',
+ '%{percentParent} of %{parent}',
+ '%{percentParent} of %{parent}',
+ '%{percentParent} of %{parent}'
+ ],
+ [
+ 'Eve',
+ 'label: Cain, text: fourteen',
+ 'Seth',
+ 'value: %{value}', // N.B. there is no `values` array
+ '17% of Eve',
+ '17% of Eve',
+ 'Awan',
+ '17% of Eve',
+ '100% of Awan'
+ ]
+ ]
+ ], /* skipEtra */ true);
+});
+
+describe('Test treemap texttemplate with *total* `values` should work:', function() {
+ checkTextTemplate([{
+ type: 'treemap', pathbar: { visible: false },
+ branchvalues: 'total',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ values: [65, 14, 12, 10, 2, 6, 6, 1, 4],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
+ }], 'g.slicetext', [
+ ['color: %{color}', ['Eve', 'color: #1f77b4', 'Seth', 'color: #2ca02c', 'color: #d62728', 'color: #9467bd', 'Awan', 'color: #ff7f0e', 'color: #d62728']],
+ ['label: %{label}', ['Eve', 'label: Cain', 'Seth', 'label: Enos', 'label: Noam', 'label: Abel', 'Awan', 'label: Enoch', 'label: Azura']],
+ ['value: %{value}', ['Eve', 'value: 14', 'Seth', 'value: 10', 'value: 2', 'value: 6', 'Awan', 'value: 1', 'value: 4']],
+ ['text: %{text}', ['Eve', 'text: fourteen', 'Seth', 'text: ten', 'text: two', 'text: six', 'Awan', 'text: one', 'text: four']],
+ ['path: %{currentPath}', ['Eve', 'path: Eve/', 'Seth', 'path: Eve/', 'path: Eve/', 'path: Eve/Seth/', 'Awan', 'path: Eve/Seth/', 'path: Eve/Awan/']]
+ ], /* skipEtra */ true);
+});
+
+describe('Test treemap texttemplate with *remainder* `values` should work:', function() {
+ checkTextTemplate([{
+ type: 'treemap', pathbar: { visible: false },
+ branchvalues: 'remainder',
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve' ],
+ values: [65, 14, 12, 10, 2, 6, 6, 1, 4],
+ text: ['sixty-five', 'fourteen', 'twelve', 'ten', 'two', 'six', 'six', 'one', 'four']
+ }], 'g.slicetext', [
+ ['color: %{color}', ['Eve', 'color: #1f77b4', 'Seth', 'color: #2ca02c', 'color: #d62728', 'color: #9467bd', 'Awan', 'color: #ff7f0e', 'color: #d62728']],
+ ['label: %{label}', ['Eve', 'label: Cain', 'Seth', 'label: Enos', 'label: Noam', 'label: Abel', 'Awan', 'label: Enoch', 'label: Azura']],
+ ['value: %{value}', ['Eve', 'value: 14', 'Seth', 'value: 10', 'value: 2', 'value: 6', 'Awan', 'value: 1', 'value: 4']],
+ ['text: %{text}', ['Eve', 'text: fourteen', 'Seth', 'text: ten', 'text: two', 'text: six', 'Awan', 'text: one', 'text: four']],
+ ['path: %{currentPath}', ['Eve', 'path: Eve/', 'Seth', 'path: Eve/', 'path: Eve/', 'path: Eve/Seth/', 'Awan', 'path: Eve/Seth/', 'path: Eve/Awan/']]
+ ], /* skipEtra */ true);
+});