diff --git a/lib/icicle.js b/lib/icicle.js
new file mode 100644
index 00000000000..2fb9ed0df7c
--- /dev/null
+++ b/lib/icicle.js
@@ -0,0 +1,3 @@
+'use strict';
+
+module.exports = require('../src/traces/icicle');
diff --git a/lib/index-strict.js b/lib/index-strict.js
index aa541fd970c..e57d825d435 100644
--- a/lib/index-strict.js
+++ b/lib/index-strict.js
@@ -19,6 +19,7 @@ Plotly.register([
require('./pie'),
require('./sunburst'),
require('./treemap'),
+ require('./icicle'),
require('./funnelarea'),
require('./scattergeo'),
require('./choropleth'),
diff --git a/lib/index.js b/lib/index.js
index a447211d9ab..77d07357c88 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -19,6 +19,7 @@ Plotly.register([
require('./pie'),
require('./sunburst'),
require('./treemap'),
+ require('./icicle'),
require('./funnelarea'),
require('./scatter3d'),
require('./surface'),
diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js
index 03397d85096..a31e5189d06 100644
--- a/src/plot_api/plot_api.js
+++ b/src/plot_api/plot_api.js
@@ -2374,7 +2374,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 & Treemap traces
+ {pattern: /^level$/}, // for Sunburst, Treemap and Icicle traces
// below this you must be in editable: true mode
// TODO: I still put name and title with `trace.uirevision`
@@ -3738,6 +3738,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._iciclelayer = fullLayout._paper.append('g').classed('iciclelayer', true);
+
// single treemap layer for the whole plot
fullLayout._treemaplayer = fullLayout._paper.append('g').classed('treemaplayer', true);
diff --git a/src/plots/plots.js b/src/plots/plots.js
index e350ccc75ef..fffa7e90735 100644
--- a/src/plots/plots.js
+++ b/src/plots/plots.js
@@ -2927,10 +2927,11 @@ plots.doCalcdata = function(gd, traces) {
gd._hmpixcount = 0;
gd._hmlumcount = 0;
- // for sharing colors across pies / sunbursts / treemap / funnelarea (and for legend)
+ // for sharing colors across pies / sunbursts / treemap / icicle / funnelarea (and for legend)
fullLayout._piecolormap = {};
fullLayout._sunburstcolormap = {};
fullLayout._treemapcolormap = {};
+ fullLayout._iciclecolormap = {};
fullLayout._funnelareacolormap = {};
// If traces were specified and this trace was not included,
diff --git a/src/traces/bar/uniform_text.js b/src/traces/bar/uniform_text.js
index b40ab37c9e1..b7b2390d8b5 100644
--- a/src/traces/bar/uniform_text.js
+++ b/src/traces/bar/uniform_text.js
@@ -17,6 +17,7 @@ function resizeText(gd, gTrace, traceType) {
selector = 'g.slice';
break;
case 'treemap' :
+ case 'icicle' :
selector = 'g.slice, g.pathbar';
break;
default :
diff --git a/src/traces/icicle/attributes.js b/src/traces/icicle/attributes.js
new file mode 100644
index 00000000000..abb5f117d85
--- /dev/null
+++ b/src/traces/icicle/attributes.js
@@ -0,0 +1,98 @@
+'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 treemapAttrs = require('../treemap/attributes');
+var constants = require('../treemap/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: {
+ orientation: {
+ valType: 'enumerated',
+ values: ['v', 'h'],
+ dflt: 'h',
+ editType: 'plot',
+ description: [
+ 'When set in conjunction with `tiling.flip`, determines on',
+ 'which side the root nodes are drawn in the chart. If',
+ '`tiling.orientation` is *v* and `tiling.flip` is **, the root',
+ 'nodes appear at the top. If `tiling.orientation` is *v* and',
+ '`tiling.flip` is *y*, the root nodes appear at the bottom. If',
+ '`tiling.orientation` is *h* and `tiling.flip` is **, the',
+ 'root nodes appear at the left. If `tiling.orientation` is *h*',
+ 'and `tiling.flip` is *x*, the root nodes appear at the right.',
+ ].join(' ')
+ },
+
+ flip: treemapAttrs.tiling.flip,
+
+ pad: {
+ valType: 'number',
+ min: 0,
+ dflt: 0,
+ editType: 'plot',
+ description: [
+ 'Sets the inner padding (in px).'
+ ].join(' ')
+ },
+
+
+ editType: 'calc',
+ },
+
+ marker: extendFlat({
+ colors: sunburstAttrs.marker.colors,
+
+ line: sunburstAttrs.marker.line,
+
+ editType: 'calc'
+ },
+ colorScaleAttrs('marker', {
+ colorAttr: 'colors',
+ anim: false // TODO: set to anim: true?
+ })
+ ),
+
+ leaf: sunburstAttrs.leaf,
+
+ pathbar: treemapAttrs.pathbar,
+
+ 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: treemapAttrs.outsidetextfont,
+
+ textposition: treemapAttrs.textposition,
+ sort: pieAttrs.sort,
+ root: sunburstAttrs.root,
+
+ domain: domainAttrs({name: 'icicle', trace: true, editType: 'calc'}),
+};
diff --git a/src/traces/icicle/base_plot.js b/src/traces/icicle/base_plot.js
new file mode 100644
index 00000000000..d33b024f695
--- /dev/null
+++ b/src/traces/icicle/base_plot.js
@@ -0,0 +1,13 @@
+'use strict';
+
+var plots = require('../../plots/plots');
+
+exports.name = 'icicle';
+
+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/icicle/calc.js b/src/traces/icicle/calc.js
new file mode 100644
index 00000000000..2bf57bd16da
--- /dev/null
+++ b/src/traces/icicle/calc.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var calc = require('../sunburst/calc');
+
+exports.calc = function(gd, trace) {
+ return calc.calc(gd, trace);
+};
+
+exports.crossTraceCalc = function(gd) {
+ return calc._runCrossTraceCalc('icicle', gd);
+};
diff --git a/src/traces/icicle/defaults.js b/src/traces/icicle/defaults.js
new file mode 100644
index 00000000000..a549f1eac6e
--- /dev/null
+++ b/src/traces/icicle/defaults.js
@@ -0,0 +1,101 @@
+'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');
+
+ coerce('tiling.orientation');
+ 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 hasPathbar = coerce('pathbar.visible');
+
+ var textposition = 'auto';
+ handleText(traceIn, traceOut, layout, coerce, textposition, {
+ hasPathbar: hasPathbar,
+ moduleHasSelected: false,
+ moduleHasUnselected: false,
+ moduleHasConstrain: false,
+ moduleHasCliponaxis: false,
+ moduleHasTextangle: false,
+ moduleHasInsideanchor: false
+ });
+ coerce('textposition');
+
+ var lineWidth = coerce('marker.line.width');
+ if(lineWidth) coerce('marker.line.color', layout.paper_bgcolor);
+
+ coerce('marker.colors');
+ var withColorscale = traceOut._hasColorscale = (
+ hasColorscale(traceIn, 'marker', 'colors') ||
+ (traceIn.marker || {}).coloraxis // N.B. special logic to consider "values" colorscales
+ );
+ if(withColorscale) {
+ colorscaleDefaults(traceIn, traceOut, layout, coerce, {prefix: 'marker.', cLetter: 'c'});
+ }
+
+ coerce('leaf.opacity', withColorscale ? 1 : 0.7);
+
+ traceOut._hovered = {
+ marker: {
+ line: {
+ width: 2,
+ color: Color.contrast(layout.paper_bgcolor)
+ }
+ }
+ };
+
+ if(hasPathbar) {
+ // This works even for multi-line labels as icicle pathbar trim out line breaks
+ coerce('pathbar.thickness', traceOut.pathbar.textfont.size + 2 * TEXTPAD);
+
+ coerce('pathbar.side');
+ coerce('pathbar.edgeshape');
+ }
+
+ coerce('sort');
+
+ coerce('root.color');
+
+ handleDomainDefaults(traceOut, layout, coerce);
+
+ // do not support transforms for now
+ traceOut._length = null;
+};
diff --git a/src/traces/icicle/draw_descendants.js b/src/traces/icicle/draw_descendants.js
new file mode 100644
index 00000000000..b742438ea24
--- /dev/null
+++ b/src/traces/icicle/draw_descendants.js
@@ -0,0 +1,200 @@
+'use strict';
+
+var d3 = require('@plotly/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('../treemap/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;
+
+ // N.B. slice data isn't the calcdata,
+ // grab corresponding calcdata item in sliceData[i].data.data
+ var allData = partition(entry, [width, height], {
+ flipX: trace.tiling.flip.indexOf('x') > -1,
+ flipY: trace.tiling.flip.indexOf('y') > -1,
+ orientation: trace.tiling.orientation,
+ pad: {
+ inner: trace.tiling.pad
+ },
+ maxDepth: trace._maxDepth
+ });
+
+ var sliceData = allData.descendants();
+
+ var minVisibleDepth = Infinity;
+ var maxVisibleDepth = -Infinity;
+ sliceData.forEach(function(pt) {
+ var depth = pt.depth;
+ if(depth >= trace._maxDepth) {
+ // hide slices that won't show up on graph
+ pt.x0 = pt.x1 = (pt.x0 + pt.x1) / 2;
+ pt.y0 = pt.y1 = (pt.y0 + pt.y1) / 2;
+ } else {
+ minVisibleDepth = Math.min(minVisibleDepth, depth);
+ maxVisibleDepth = Math.max(maxVisibleDepth, depth);
+ }
+ });
+
+ slices = slices.data(sliceData, helpers.getPtId);
+
+ trace._maxVisibleLayers = isFinite(maxVisibleDepth) ? maxVisibleDepth - minVisibleDepth + 1 : 0;
+
+ 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) {
+ pt._hoverX = viewX(pt.x1 - trace.tiling.pad),
+ pt._hoverY = hasBottom ?
+ viewY(pt.y1 - trace.tiling.pad / 2) :
+ viewY(pt.y0 + trace.tiling.pad / 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],
+ {
+ orientation: trace.tiling.orientation,
+ flipX: trace.tiling.flip.indexOf('x') > -1,
+ flipY: trace.tiling.flip.indexOf('y') > -1,
+ }
+ );
+ 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
+ });
+
+ if(pt.x0 === pt.x1 || pt.y0 === pt.y1) {
+ pt._text = '';
+ } else {
+ pt._text = formatSliceLabel(pt, entry, trace, cd, fullLayout) || '';
+ }
+
+ 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 font = Lib.ensureUniformFontSize(gd, helpers.determineTextFont(trace, pt, fullLayout.font));
+
+ sliceText.text(pt._text || ' ') // use one space character instead of a blank string to avoid jumps during transition
+ .classed('slicetext', true)
+ .attr('text-anchor', hasRight ? 'end' : hasLeft ? 'start' : 'middle')
+ .call(Drawing.font, font)
+ .call(svgTextUtils.convertToTspans, gd);
+
+ pt.textBB = Drawing.bBox(sliceText.node());
+ pt.transform = toMoveInsideSlice(pt, {
+ fontSize: font.size
+ });
+ pt.transform.fontSize = font.size;
+
+ 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/icicle/index.js b/src/traces/icicle/index.js
new file mode 100644
index 00000000000..45c513cc51d
--- /dev/null
+++ b/src/traces/icicle/index.js
@@ -0,0 +1,30 @@
+'use strict';
+
+module.exports = {
+ moduleType: 'trace',
+ name: 'icicle',
+ 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 icicle sectors are determined by the entries in',
+ '*labels* or *ids* and in *parents*.'
+ ].join(' ')
+ }
+};
diff --git a/src/traces/icicle/layout_attributes.js b/src/traces/icicle/layout_attributes.js
new file mode 100644
index 00000000000..dc6989c067a
--- /dev/null
+++ b/src/traces/icicle/layout_attributes.js
@@ -0,0 +1,29 @@
+'use strict';
+
+module.exports = {
+ iciclecolorway: {
+ valType: 'colorlist',
+ editType: 'calc',
+ description: [
+ 'Sets the default icicle 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 `extendiciclecolors`.'
+ ].join(' ')
+ },
+ extendiciclecolors: {
+ valType: 'boolean',
+ dflt: true,
+ editType: 'calc',
+ description: [
+ 'If `true`, the icicle slice colors (whether given by `iciclecolorway` 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/icicle/layout_defaults.js b/src/traces/icicle/layout_defaults.js
new file mode 100644
index 00000000000..1e123933605
--- /dev/null
+++ b/src/traces/icicle/layout_defaults.js
@@ -0,0 +1,12 @@
+'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('iciclecolorway', layoutOut.colorway);
+ coerce('extendiciclecolors');
+};
diff --git a/src/traces/icicle/partition.js b/src/traces/icicle/partition.js
new file mode 100644
index 00000000000..4e67b400bfd
--- /dev/null
+++ b/src/traces/icicle/partition.js
@@ -0,0 +1,34 @@
+'use strict';
+
+var d3Hierarchy = require('d3-hierarchy');
+var flipTree = require('../treemap/flip_tree');
+
+module.exports = function partition(entry, size, opts) {
+ var flipX = opts.flipX;
+ var flipY = opts.flipY;
+ var swapXY = opts.orientation === 'h';
+ var maxDepth = opts.maxDepth;
+
+ var newWidth = size[0];
+ var newHeight = size[1];
+ if(maxDepth) {
+ newWidth = (entry.height + 1) * size[0] / Math.min(entry.height + 1, maxDepth);
+ newHeight = (entry.height + 1) * size[1] / Math.min(entry.height + 1, maxDepth);
+ }
+
+ var result = d3Hierarchy
+ .partition()
+ .padding(opts.pad.inner)
+ .size(
+ swapXY ? [size[1], newWidth] : [size[0], newHeight]
+ )(entry);
+
+ if(swapXY || flipX || flipY) {
+ flipTree(result, size, {
+ swapXY: swapXY,
+ flipX: flipX,
+ flipY: flipY
+ });
+ }
+ return result;
+};
diff --git a/src/traces/icicle/plot.js b/src/traces/icicle/plot.js
new file mode 100644
index 00000000000..d87b205f5ad
--- /dev/null
+++ b/src/traces/icicle/plot.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var draw = require('../treemap/draw');
+var drawDescendants = require('./draw_descendants');
+
+module.exports = function _plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback) {
+ return draw(gd, cdmodule, transitionOpts, makeOnCompleteCallback, {
+ type: 'icicle',
+ drawDescendants: drawDescendants
+ });
+};
diff --git a/src/traces/icicle/style.js b/src/traces/icicle/style.js
new file mode 100644
index 00000000000..d4c429df953
--- /dev/null
+++ b/src/traces/icicle/style.js
@@ -0,0 +1,41 @@
+'use strict';
+
+var d3 = require('@plotly/d3');
+var Color = require('../../components/color');
+var Lib = require('../../lib');
+var resizeText = require('../bar/uniform_text').resizeText;
+
+function style(gd) {
+ var s = gd._fullLayout._iciclelayer.selectAll('.trace');
+ resizeText(gd, s, 'icicle');
+
+ s.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);
+ });
+ });
+}
+
+function styleOne(s, pt, trace) {
+ var cdi = pt.data.data;
+ var isLeaf = !pt.children;
+ var ptNumber = cdi.i;
+ var lineColor = Lib.castOption(trace, ptNumber, 'marker.line.color') || Color.defaultLine;
+ var lineWidth = Lib.castOption(trace, ptNumber, 'marker.line.width') || 0;
+
+ s.style('stroke-width', lineWidth)
+ .call(Color.fill, cdi.color)
+ .call(Color.stroke, lineColor)
+ .style('opacity', isLeaf ? trace.leaf.opacity : null);
+}
+
+module.exports = {
+ style: style,
+ styleOne: styleOne
+};
diff --git a/src/traces/sunburst/attributes.js b/src/traces/sunburst/attributes.js
index 01f7be1b0a2..a2835e70c25 100644
--- a/src/traces/sunburst/attributes.js
+++ b/src/traces/sunburst/attributes.js
@@ -206,7 +206,7 @@ module.exports = {
editType: 'calc',
dflt: 'rgba(0,0,0,0)',
description: [
- 'sets the color of the root node for a sunburst or a treemap trace.',
+ 'sets the color of the root node for a sunburst/treemap/icicle trace.',
'this has no effect when a colorscale is used to set the markers.'
].join(' ')
},
diff --git a/src/traces/sunburst/calc.js b/src/traces/sunburst/calc.js
index 097656a2ec5..c05e4a39f1e 100644
--- a/src/traces/sunburst/calc.js
+++ b/src/traces/sunburst/calc.js
@@ -13,6 +13,7 @@ var ALMOST_EQUAL = require('../../constants/numerical').ALMOST_EQUAL;
var sunburstExtendedColorWays = {};
var treemapExtendedColorWays = {};
+var icicleExtendedColorWays = {};
exports.calc = function(gd, trace) {
var fullLayout = gd._fullLayout;
@@ -245,7 +246,9 @@ exports._runCrossTraceCalc = function(desiredType, gd) {
if(fullLayout['extend' + desiredType + 'colors']) {
colorWay = generateExtendedColors(colorWay,
- desiredType === 'treemap' ? treemapExtendedColorWays : sunburstExtendedColorWays
+ desiredType === 'icicle' ? icicleExtendedColorWays :
+ desiredType === 'treemap' ? treemapExtendedColorWays :
+ sunburstExtendedColorWays
);
}
var dfltColorCount = 0;
diff --git a/src/traces/sunburst/fx.js b/src/traces/sunburst/fx.js
index a12a3f55ea8..66f2cec6d27 100644
--- a/src/traces/sunburst/fx.js
+++ b/src/traces/sunburst/fx.js
@@ -18,7 +18,9 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
var hierarchy = cd0.hierarchy;
var isSunburst = trace.type === 'sunburst';
- var isTreemap = trace.type === 'treemap';
+ var isTreemapOrIcicle =
+ trace.type === 'treemap' ||
+ trace.type === 'icicle';
// hover state vars
// have we drawn a hover label, so it should be cleared later
@@ -57,7 +59,7 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
hoverCenterX = cd0.cx + pt.pxmid[0] * (1 - pt.rInscribed);
hoverCenterY = cd0.cy + pt.pxmid[1] * (1 - pt.rInscribed);
}
- if(isTreemap) {
+ if(isTreemapOrIcicle) {
hoverCenterX = pt._hoverX;
hoverCenterY = pt._hoverY;
}
@@ -145,7 +147,7 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
hoverItems.x1 = hoverCenterX + pt.rInscribed * pt.rpx1;
hoverItems.idealAlign = pt.pxmid[0] < 0 ? 'left' : 'right';
}
- if(isTreemap) {
+ if(isTreemapOrIcicle) {
hoverItems.x = hoverCenterX;
hoverItems.idealAlign = hoverCenterX < 0 ? 'left' : 'right';
}
@@ -159,7 +161,7 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
trace._hasHoverLabel = true;
}
- if(isTreemap) {
+ if(isTreemapOrIcicle) {
var slice = sliceTop.select('path.surface');
opts.styleOne(slice, pt, traceNow, {
hovered: true
@@ -192,7 +194,7 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) {
trace._hasHoverLabel = false;
}
- if(isTreemap) {
+ if(isTreemapOrIcicle) {
var slice = sliceTop.select('path.surface');
opts.styleOne(slice, pt, traceNow, {
hovered: false
diff --git a/src/traces/sunburst/helpers.js b/src/traces/sunburst/helpers.js
index 8249d6fa9a6..9c90feac35c 100644
--- a/src/traces/sunburst/helpers.js
+++ b/src/traces/sunburst/helpers.js
@@ -134,7 +134,7 @@ exports.determineTextFont = function(trace, pt, layoutFont, opts) {
exports.hasTransition = function(transitionOpts) {
// We could optimize hasTransition per trace,
- // as sunburst & treemap have no cross-trace logic!
+ // as sunburst, treemap & icicle have no cross-trace logic!
return !!(transitionOpts && transitionOpts.duration > 0);
};
diff --git a/src/traces/treemap/draw.js b/src/traces/treemap/draw.js
new file mode 100644
index 00000000000..9fa5a7126c2
--- /dev/null
+++ b/src/traces/treemap/draw.js
@@ -0,0 +1,69 @@
+'use strict';
+
+var d3 = require('@plotly/d3');
+
+var helpers = require('../sunburst/helpers');
+var uniformText = require('../bar/uniform_text');
+var clearMinTextSize = uniformText.clearMinTextSize;
+var resizeText = require('../bar/style').resizeText;
+
+var plotOne = require('./plot_one');
+
+module.exports = function _plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback, opts) {
+ var type = opts.type;
+ var drawDescendants = opts.drawDescendants;
+
+ var fullLayout = gd._fullLayout;
+ var layer = fullLayout['_' + type + 'layer'];
+ var join, onComplete;
+
+ // If transition config is provided, then it is only a partial replot and traces not
+ // updated are removed.
+ var isFullReplot = !transitionOpts;
+
+ clearMinTextSize(type, fullLayout);
+
+ join = layer.selectAll('g.trace.' + type)
+ .data(cdmodule, function(cd) { return cd[0].trace.uid; });
+
+ join.enter().append('g')
+ .classed('trace', true)
+ .classed(type, true);
+
+ join.order();
+
+ if(!fullLayout.uniformtext.mode && helpers.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, drawDescendants);
+ });
+ });
+ } else {
+ join.each(function(cd) {
+ plotOne(gd, cd, this, transitionOpts, drawDescendants);
+ });
+
+ if(fullLayout.uniformtext.mode) {
+ resizeText(gd, layer.selectAll('.trace'), type);
+ }
+ }
+
+ if(isFullReplot) {
+ join.exit().remove();
+ }
+};
diff --git a/src/traces/treemap/flip_tree.js b/src/traces/treemap/flip_tree.js
new file mode 100644
index 00000000000..4f8350d7daa
--- /dev/null
+++ b/src/traces/treemap/flip_tree.js
@@ -0,0 +1,36 @@
+'use strict';
+
+module.exports = 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/partition.js b/src/traces/treemap/partition.js
index 36f12d3697a..0b9af74c6a7 100644
--- a/src/traces/treemap/partition.js
+++ b/src/traces/treemap/partition.js
@@ -1,6 +1,7 @@
'use strict';
var d3Hierarchy = require('d3-hierarchy');
+var flipTree = require('./flip_tree');
module.exports = function partition(entry, size, opts) {
var flipX = opts.flipX;
@@ -59,38 +60,3 @@ function getTilingMethod(key, squarifyratio) {
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
index b61aa9921b3..20076b5403b 100644
--- a/src/traces/treemap/plot.js
+++ b/src/traces/treemap/plot.js
@@ -1,625 +1,11 @@
'use strict';
-var d3 = require('@plotly/d3');
-
-var helpers = require('../sunburst/helpers');
-
-var Lib = require('../../lib');
-var TEXTPAD = require('../bar/constants').TEXTPAD;
-var barPlot = require('../bar/plot');
-var toMoveInsideBar = barPlot.toMoveInsideBar;
-var uniformText = require('../bar/uniform_text');
-var recordMinTextSize = uniformText.recordMinTextSize;
-var clearMinTextSize = uniformText.clearMinTextSize;
-var resizeText = require('../bar/style').resizeText;
-var constants = require('./constants');
+var draw = require('./draw');
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;
-
- clearMinTextSize('treemap', fullLayout);
-
- 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(!fullLayout.uniformtext.mode && helpers.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(fullLayout.uniformtext.mode) {
- resizeText(gd, fullLayout._treemaplayer.selectAll('.trace'), 'treemap');
- }
- }
-
- 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 entry = helpers.findEntryWithLevel(hierarchy, trace.level);
-
- var gTrace = d3.select(element);
- var selAncestors = gTrace.selectAll('g.pathbar');
- var selDescendants = gTrace.selectAll('g.slice');
-
- if(!entry) {
- selAncestors.remove();
- selDescendants.remove();
- return;
- }
-
- var isRoot = helpers.isHierarchyRoot(entry);
- var hasTransition = !fullLayout.uniformtext.mode && helpers.hasTransition(transitionOpts);
-
- 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 {};
- };
-
- // N.B. handle multiple-root special case
- if(cd0.hasMultipleRoots && isRoot) {
- maxDepth++;
- }
-
- trace._maxDepth = maxDepth;
- trace._backgroundColor = fullLayout.paper_bgcolor;
- trace._entryDepth = entry.data.depth;
- trace._atRootLevel = isRoot;
-
- 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(pt, opts) {
- var x0 = pt.x0;
- var x1 = pt.x1;
- var y0 = pt.y0;
- var y1 = pt.y1;
- var textBB = pt.textBB;
-
- 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 leftToRight =
- hasLeft ? -1 :
- hasRight ? 1 : 0;
-
- var pad = trace.marker.pad;
- if(opts.isHeader) {
- x0 += pad.l - TEXTPAD;
- x1 -= pad.r - TEXTPAD;
- if(x0 >= x1) {
- var mid = (x0 + x1) / 2;
- x0 = mid;
- x1 = mid;
- }
-
- // 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,
- leftToRight: leftToRight
- });
- transform.fontSize = opts.fontSize;
-
- transform.targetX = viewMapX(transform.targetX);
- transform.targetY = viewMapY(transform.targetY);
-
- if(isNaN(transform.targetX) || isNaN(transform.targetY)) {
- return {};
- }
-
- if(x0 !== x1 && y0 !== y1) {
- recordMinTextSize(trace.type, transform, fullLayout);
- }
-
- return {
- scale: transform.scale,
- rotate: transform.rotate,
- textX: transform.textX,
- textY: transform.textY,
- anchorX: transform.anchorX,
- anchorY: transform.anchorY,
- 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({
- x0: origin.x0,
- x1: origin.x1,
- y0: origin.y0,
- y1: origin.y1,
- textBB: pt.textBB,
- _text: pt._text
- }, {
- 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));
- }
- }
-
- var transform = pt.transform;
- if(pt.x0 !== pt.x1 && pt.y0 !== pt.y1) {
- recordMinTextSize(trace.type, transform, fullLayout);
- }
-
- return d3.interpolate(prev, {
- transform: {
- scale: transform.scale,
- rotate: transform.rotate,
- textX: transform.textX,
- textY: transform.textY,
- anchorX: transform.anchorX,
- anchorY: transform.anchorY,
- targetX: transform.targetX,
- targetY: 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) {
- var transform = d.transform;
-
- if(d.x0 !== d.x1 && d.y0 !== d.y1) {
- recordMinTextSize(trace.type, transform, fullLayout);
- }
-
- return Lib.getTextTransform({
- textX: transform.textX,
- textY: transform.textY,
- anchorX: transform.anchorX,
- anchorY: transform.anchorY,
- targetX: transform.targetX,
- targetY: transform.targetY,
- scale: transform.scale,
- rotate: transform.rotate
- });
- };
-
- 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,
- anchorX: pt.transform.anchorX,
- anchorY: pt.transform.anchorY,
- 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,
- anchorX: pt.transform.anchorX,
- anchorY: pt.transform.anchorY,
- 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
+module.exports = function _plot(gd, cdmodule, transitionOpts, makeOnCompleteCallback) {
+ return draw(gd, cdmodule, transitionOpts, makeOnCompleteCallback, {
+ type: 'treemap',
+ drawDescendants: drawDescendants
});
-
- 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
- });
- } else {
- selAncestors.remove();
- }
-}
+};
diff --git a/src/traces/treemap/plot_one.js b/src/traces/treemap/plot_one.js
new file mode 100644
index 00000000000..55de51c8302
--- /dev/null
+++ b/src/traces/treemap/plot_one.js
@@ -0,0 +1,589 @@
+'use strict';
+
+var d3 = require('@plotly/d3');
+
+var helpers = require('../sunburst/helpers');
+
+var Lib = require('../../lib');
+var TEXTPAD = require('../bar/constants').TEXTPAD;
+var barPlot = require('../bar/plot');
+var toMoveInsideBar = barPlot.toMoveInsideBar;
+var uniformText = require('../bar/uniform_text');
+var recordMinTextSize = uniformText.recordMinTextSize;
+var constants = require('./constants');
+var drawAncestors = require('./draw_ancestors');
+
+function getKey(pt) {
+ return helpers.isHierarchyRoot(pt) ?
+ '' : // don't use the dummyId
+ helpers.getPtId(pt);
+}
+
+module.exports = function plotOne(gd, cd, element, transitionOpts, drawDescendants) {
+ var fullLayout = gd._fullLayout;
+ var cd0 = cd[0];
+ var trace = cd0.trace;
+ var type = trace.type;
+ var isIcicle = type === 'icicle';
+
+ var hierarchy = cd0.hierarchy;
+ var entry = helpers.findEntryWithLevel(hierarchy, trace.level);
+
+ var gTrace = d3.select(element);
+ var selAncestors = gTrace.selectAll('g.pathbar');
+ var selDescendants = gTrace.selectAll('g.slice');
+
+ if(!entry) {
+ selAncestors.remove();
+ selDescendants.remove();
+ return;
+ }
+
+ var isRoot = helpers.isHierarchyRoot(entry);
+ var hasTransition = !fullLayout.uniformtext.mode && helpers.hasTransition(transitionOpts);
+
+ 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; };
+
+ if(pt.x0 === ref.x0 && pt.x1 === ref.x1 && pt.y0 === ref.y0 && pt.y1 === ref.y1) {
+ return {
+ x0: pt.x0,
+ x1: pt.x1,
+ y0: pt.y0,
+ y1: pt.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 {};
+ };
+
+ // N.B. handle multiple-root special case
+ if(cd0.hasMultipleRoots && isRoot) {
+ maxDepth++;
+ }
+
+ trace._maxDepth = maxDepth;
+ trace._backgroundColor = fullLayout.paper_bgcolor;
+ trace._entryDepth = entry.data.depth;
+ trace._atRootLevel = isRoot;
+
+ 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(pt, opts) {
+ var x0 = pt.x0;
+ var x1 = pt.x1;
+ var y0 = pt.y0;
+ var y1 = pt.y1;
+ var textBB = pt.textBB;
+
+ 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 leftToRight =
+ hasLeft ? -1 :
+ hasRight ? 1 : 0;
+
+ // Note that `pad` is just an integer for `icicle`` traces where
+ // `pad` is a hashmap for treemap: pad.t, pad.b, pad.l, and pad.r
+ var pad = trace[isIcicle ? 'tiling' : 'marker'].pad;
+ if(opts.isHeader) {
+ x0 += (isIcicle ? pad : pad.l) - TEXTPAD;
+ x1 -= (isIcicle ? pad : pad.r) - TEXTPAD;
+ if(x0 >= x1) {
+ var mid = (x0 + x1) / 2;
+ x0 = mid;
+ x1 = mid;
+ }
+
+ // limit the drawing area for headers
+ var limY;
+ if(hasBottom) {
+ limY = y1 - (isIcicle ? pad : pad.b);
+ if(y0 < limY && limY < y1) y0 = limY;
+ } else {
+ limY = y0 + (isIcicle ? pad : 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,
+ leftToRight: leftToRight
+ });
+ transform.fontSize = opts.fontSize;
+
+ transform.targetX = viewMapX(transform.targetX);
+ transform.targetY = viewMapY(transform.targetY);
+
+ if(isNaN(transform.targetX) || isNaN(transform.targetY)) {
+ return {};
+ }
+
+ if(x0 !== x1 && y0 !== y1) {
+ recordMinTextSize(trace.type, transform, fullLayout);
+ }
+
+ return {
+ scale: transform.scale,
+ rotate: transform.rotate,
+ textX: transform.textX,
+ textY: transform.textY,
+ anchorX: transform.anchorX,
+ anchorY: transform.anchorY,
+ 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, opts) {
+ 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 = Lib.extendFlat({}, pt);
+ if(isIcicle) {
+ if(opts.orientation === 'h') {
+ if(opts.flipX) prev.x0 = pt.x1;
+ else prev.x1 = 0;
+ } else {
+ if(opts.flipY) prev.y0 = pt.y1;
+ else prev.y1 = 0;
+ }
+ }
+ }
+ } 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({
+ x0: origin.x0,
+ x1: origin.x1,
+ y0: origin.y0,
+ y1: origin.y1,
+ textBB: pt.textBB,
+ _text: pt._text
+ }, {
+ 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));
+ }
+ }
+
+ var transform = pt.transform;
+ if(pt.x0 !== pt.x1 && pt.y0 !== pt.y1) {
+ recordMinTextSize(trace.type, transform, fullLayout);
+ }
+
+ return d3.interpolate(prev, {
+ transform: {
+ scale: transform.scale,
+ rotate: transform.rotate,
+ textX: transform.textX,
+ textY: transform.textY,
+ anchorX: transform.anchorX,
+ anchorY: transform.anchorY,
+ targetX: transform.targetX,
+ targetY: 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) {
+ var transform = d.transform;
+
+ if(d.x0 !== d.x1 && d.y0 !== d.y1) {
+ recordMinTextSize(trace.type, transform, fullLayout);
+ }
+
+ return Lib.getTextTransform({
+ textX: transform.textX,
+ textY: transform.textY,
+ anchorX: transform.anchorX,
+ anchorY: transform.anchorY,
+ targetX: transform.targetX,
+ targetY: transform.targetY,
+ scale: transform.scale,
+ rotate: transform.rotate
+ });
+ };
+
+ 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,
+ anchorX: pt.transform.anchorX,
+ anchorY: pt.transform.anchorY,
+ 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,
+ anchorX: pt.transform.anchorX,
+ anchorY: pt.transform.anchorY,
+ 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
+ });
+ } else {
+ selAncestors.remove();
+ }
+};
diff --git a/tasks/test_syntax.js b/tasks/test_syntax.js
index f6983905f30..e028ef944d6 100644
--- a/tasks/test_syntax.js
+++ b/tasks/test_syntax.js
@@ -132,15 +132,16 @@ 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 and treemap where we use 'children'
+ // add special case for sunburst, icicle and treemap where we use 'children'
// off the d3-hierarchy output
var dirParts = path.dirname(file).split(path.sep);
var filename = dirParts[dirParts.length - 1];
- var isSunburstOrTreemap =
+ var isSunburstOrIcicleOrTreemap =
filename === 'sunburst' ||
+ filename === 'icicle' ||
filename === 'treemap';
var isLinkedToObject = ['pt', 'd', 'parent', 'node'].indexOf(parts[parts.length - 2]) !== -1;
- if(!(isSunburstOrTreemap && isLinkedToObject)) {
+ if(!(isSunburstOrIcicleOrTreemap && isLinkedToObject)) {
logs.push(file + ' : contains .' + lastPart + ' (IE failure in SVG)');
}
}
diff --git a/tasks/util/constants.js b/tasks/util/constants.js
index 45326680e25..7f149e2fbca 100644
--- a/tasks/util/constants.js
+++ b/tasks/util/constants.js
@@ -121,6 +121,7 @@ var partialBundleTraces = {
'histogram',
'histogram2d',
'histogram2dcontour',
+ 'icicle',
'image',
'indicator',
'ohlc',
diff --git a/test/image/baselines/icicle_coffee-maxdepth3-all-directions.png b/test/image/baselines/icicle_coffee-maxdepth3-all-directions.png
new file mode 100644
index 00000000000..dbe66b5aae6
Binary files /dev/null and b/test/image/baselines/icicle_coffee-maxdepth3-all-directions.png differ
diff --git a/test/image/baselines/icicle_coffee-maxdepth3.png b/test/image/baselines/icicle_coffee-maxdepth3.png
new file mode 100644
index 00000000000..3034d92e5fe
Binary files /dev/null and b/test/image/baselines/icicle_coffee-maxdepth3.png differ
diff --git a/test/image/baselines/icicle_coffee.png b/test/image/baselines/icicle_coffee.png
new file mode 100644
index 00000000000..f8eb965d8ec
Binary files /dev/null and b/test/image/baselines/icicle_coffee.png differ
diff --git a/test/image/baselines/icicle_count_branches.png b/test/image/baselines/icicle_count_branches.png
new file mode 100644
index 00000000000..a2e55960540
Binary files /dev/null and b/test/image/baselines/icicle_count_branches.png differ
diff --git a/test/image/baselines/icicle_first.png b/test/image/baselines/icicle_first.png
new file mode 100644
index 00000000000..8266e9d834e
Binary files /dev/null and b/test/image/baselines/icicle_first.png differ
diff --git a/test/image/baselines/icicle_flare.png b/test/image/baselines/icicle_flare.png
new file mode 100644
index 00000000000..44985bebd14
Binary files /dev/null and b/test/image/baselines/icicle_flare.png differ
diff --git a/test/image/baselines/icicle_leaf-opacity-level.png b/test/image/baselines/icicle_leaf-opacity-level.png
new file mode 100644
index 00000000000..d1bae3b10da
Binary files /dev/null and b/test/image/baselines/icicle_leaf-opacity-level.png differ
diff --git a/test/image/baselines/icicle_packages_colorscale_novalue.png b/test/image/baselines/icicle_packages_colorscale_novalue.png
new file mode 100644
index 00000000000..91af8476999
Binary files /dev/null and b/test/image/baselines/icicle_packages_colorscale_novalue.png differ
diff --git a/test/image/baselines/icicle_root-sort.png b/test/image/baselines/icicle_root-sort.png
new file mode 100644
index 00000000000..b8e3ba74e4c
Binary files /dev/null and b/test/image/baselines/icicle_root-sort.png differ
diff --git a/test/image/baselines/icicle_textposition.png b/test/image/baselines/icicle_textposition.png
new file mode 100644
index 00000000000..14b613c2110
Binary files /dev/null and b/test/image/baselines/icicle_textposition.png differ
diff --git a/test/image/baselines/icicle_values_colorscale.png b/test/image/baselines/icicle_values_colorscale.png
new file mode 100644
index 00000000000..cd176a8c029
Binary files /dev/null and b/test/image/baselines/icicle_values_colorscale.png differ
diff --git a/test/image/baselines/icicle_with-without_values.png b/test/image/baselines/icicle_with-without_values.png
new file mode 100644
index 00000000000..d2fc7073c5c
Binary files /dev/null and b/test/image/baselines/icicle_with-without_values.png differ
diff --git a/test/image/baselines/icicle_with-without_values_template.png b/test/image/baselines/icicle_with-without_values_template.png
new file mode 100644
index 00000000000..30440a17e69
Binary files /dev/null and b/test/image/baselines/icicle_with-without_values_template.png differ
diff --git a/test/image/baselines/uniformtext_icicle.png b/test/image/baselines/uniformtext_icicle.png
new file mode 100644
index 00000000000..bd985e605a9
Binary files /dev/null and b/test/image/baselines/uniformtext_icicle.png differ
diff --git a/test/image/mocks/icicle_coffee-maxdepth3-all-directions.json b/test/image/mocks/icicle_coffee-maxdepth3-all-directions.json
new file mode 100644
index 00000000000..3c1c10b7452
--- /dev/null
+++ b/test/image/mocks/icicle_coffee-maxdepth3-all-directions.json
@@ -0,0 +1,1320 @@
+{
+ "data": [
+{
+ "type": "icicle",
+ "pathbar": {
+ "visible": false
+ },
+ "tiling": {
+ "orientation": "h",
+ "flip": "x"
+ },
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0.05,
+ 0.45
+ ]
+ },
+ "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"
+ ]
+ },
+ {
+ "type": "icicle",
+ "pathbar": {
+ "visible": false
+ },
+ "tiling": {
+ "orientation": "h"
+ },
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0.05,
+ 0.45
+ ]
+ },
+ "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"
+ ]
+ },
+ {
+ "type": "icicle",
+ "pathbar": {
+ "visible": false
+ },
+ "tiling": {
+ "orientation": "v",
+ "flip": "y"
+ },
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0.55,
+ 0.95
+ ]
+ },
+ "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"
+ ]
+ },
+ {
+ "type": "icicle",
+ "pathbar": {
+ "visible": false
+ },
+ "tiling": {
+ "orientation": "v"
+ },
+ "maxdepth": 3,
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0.55,
+ 0.95
+ ]
+ },
+ "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": 1200,
+ "height": 1200,
+ "shapes": [
+ { "type": "rect", "layer": "below", "x0": 0, "x1": 1, "y0": 0, "y1": 1 }
+ ],
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "orientation: 'h', flip: 'x'",
+ "font": {
+ "size": 16
+ },
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 0.5,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "orientation: 'h', flip: ''",
+ "font": {
+ "size": 16
+ },
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 0.5,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "orientation: 'v', flip: 'y'",
+ "font": {
+ "size": 16
+ },
+ "x": 0.25,
+ "xanchor": "center",
+ "y": 0.95,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "orientation: 'v', flip: ''",
+ "font": {
+ "size": 16
+ },
+ "x": 0.75,
+ "xanchor": "center",
+ "y": 0.95,
+ "yanchor": "bottom"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/icicle_coffee-maxdepth3.json b/test/image/mocks/icicle_coffee-maxdepth3.json
new file mode 100644
index 00000000000..bd3a41e3986
--- /dev/null
+++ b/test/image/mocks/icicle_coffee-maxdepth3.json
@@ -0,0 +1,314 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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/icicle_coffee.json b/test/image/mocks/icicle_coffee.json
new file mode 100644
index 00000000000..cbbbd8e00e0
--- /dev/null
+++ b/test/image/mocks/icicle_coffee.json
@@ -0,0 +1,313 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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/icicle_count_branches.json b/test/image/mocks/icicle_count_branches.json
new file mode 100644
index 00000000000..10f9a122e0d
--- /dev/null
+++ b/test/image/mocks/icicle_count_branches.json
@@ -0,0 +1,78 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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/icicle_first.json b/test/image/mocks/icicle_first.json
new file mode 100644
index 00000000000..415f87ab6ec
--- /dev/null
+++ b/test/image/mocks/icicle_first.json
@@ -0,0 +1,92 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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": "icicle",
+ "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/icicle_flare.json b/test/image/mocks/icicle_flare.json
new file mode 100644
index 00000000000..54a778210fa
--- /dev/null
+++ b/test/image/mocks/icicle_flare.json
@@ -0,0 +1,774 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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/icicle_leaf-opacity-level.json b/test/image/mocks/icicle_leaf-opacity-level.json
new file mode 100644
index 00000000000..b8f99331d78
--- /dev/null
+++ b/test/image/mocks/icicle_leaf-opacity-level.json
@@ -0,0 +1,84 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "labels": ["Eve", "Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
+ "parents": ["", "Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
+ "domain": {"x": [0, 0.20], "y": [0, 0.9]},
+ "root": {"color": "limegreen"},
+ "level": "",
+ "leaf": {
+ "opacity": 1
+ }
+ },
+ {
+ "type": "icicle",
+ "labels": ["Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
+ "parents": ["Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
+ "domain": {"x": [0.25, 0.45], "y": [0, 0.9]},
+ "root": {"color": "limegreen"},
+ "level": "Seth",
+ "leaf": {
+ "opacity": 0.7
+ }
+ },
+ {
+ "type": "icicle",
+ "labels": ["Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
+ "parents": ["Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
+ "domain": {"x": [0.5, 0.70], "y": [0, 0.9]},
+ "root": {"color": "limegreen"},
+ "level": "Enos",
+ "leaf": {
+ "opacity": 0.3
+ }
+ },
+ {
+ "type": "icicle",
+ "labels": ["Cain", "Seth", "Enos", "Noam", "Abel", "Awan", "Enoch", "Azura"],
+ "parents": ["Eve", "Eve", "Seth", "Seth", "Eve", "Eve", "Awan", "Eve" ],
+ "domain": {"x": [0.75, 0.95], "y": [0, 0.9]},
+ "root": {"color": "limegreen"},
+ "level": "Enos",
+ "leaf": {
+ "opacity": 0.1
+ }
+ }
+ ],
+ "layout": {
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "opacity=0.1, level='Enos'",
+ "x": 0.75,
+ "xanchor": "left",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "opacity=0.3, level='Enos'",
+ "x": 0.5,
+ "xanchor": "left",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "opacity=0.7, level='Seth'",
+ "x": 0.25,
+ "xanchor": "left",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "opacity=1, level=''",
+ "x": 0.0,
+ "xanchor": "left",
+ "y": 1,
+ "yanchor": "bottom"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/icicle_packages_colorscale_novalue.json b/test/image/mocks/icicle_packages_colorscale_novalue.json
new file mode 100644
index 00000000000..38e85058555
--- /dev/null
+++ b/test/image/mocks/icicle_packages_colorscale_novalue.json
@@ -0,0 +1,2328 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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/icicle_root-sort.json b/test/image/mocks/icicle_root-sort.json
new file mode 100644
index 00000000000..d695b132f51
--- /dev/null
+++ b/test/image/mocks/icicle_root-sort.json
@@ -0,0 +1,105 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "sort": true,
+ "root": {
+ "color": "DodgerBlue"
+ },
+ "domain": {
+ "x": [0, 0.45]
+ },
+ "textinfo": "label+percent parent",
+ "labels": [
+ "Fruit",
+ "Apple",
+ "Pear",
+ "Grape",
+ "Orange",
+ "Kiwi",
+ "Banana",
+ "Melon",
+ "Watermelon",
+ "Pineapple",
+ "Strawberry"
+ ],
+ "parents": [
+ "",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit"
+ ],
+ "values": [100, 5, 15, 10, 8, 20, 12, 9, 5, 8, 8],
+ "branchvalues": "total"
+ },
+ {
+ "type": "icicle",
+ "sort": false,
+ "root": {
+ "color": "DodgerBlue"
+ },
+ "domain": {
+ "x": [0.55, 1],
+ "y": [0, 1]
+ },
+ "textinfo": "label+percent parent",
+ "labels": [
+ "Fruit",
+ "Apple",
+ "Pear",
+ "Grape",
+ "Orange",
+ "Kiwi",
+ "Banana",
+ "Melon",
+ "Watermelon",
+ "Pineapple",
+ "Strawberry"
+ ],
+ "parents": [
+ "",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit",
+ "Fruit"
+ ],
+ "values": [100, 5, 15, 10, 8, 20, 12, 9, 5, 8, 8],
+ "branchvalues": "total"
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 700,
+ "annotations": [
+ {
+ "showarrow": false,
+ "text": "sort: true",
+ "x": 0.225,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ },
+ {
+ "showarrow": false,
+ "text": "sort: false",
+ "x": 0.775,
+ "xanchor": "center",
+ "y": 1,
+ "yanchor": "bottom"
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/icicle_textposition.json b/test/image/mocks/icicle_textposition.json
new file mode 100644
index 00000000000..ce3ab3b8bca
--- /dev/null
+++ b/test/image/mocks/icicle_textposition.json
@@ -0,0 +1,991 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "root": { "color": "yellow" },
+ "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": "icicle",
+ "root": { "color": "yellow" },
+ "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": "icicle",
+ "root": { "color": "yellow" },
+ "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": "icicle",
+ "root": { "color": "yellow" },
+ "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": "icicle",
+ "root": { "color": "yellow" },
+ "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": "icicle",
+ "root": { "color": "yellow" },
+ "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": "icicle",
+ "root": { "color": "yellow" },
+ "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": "icicle",
+ "root": { "color": "yellow" },
+ "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": "icicle",
+ "root": { "color": "yellow" },
+ "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": "black"
+ }
+}
diff --git a/test/image/mocks/icicle_values_colorscale.json b/test/image/mocks/icicle_values_colorscale.json
new file mode 100644
index 00000000000..0954d9bfc9d
--- /dev/null
+++ b/test/image/mocks/icicle_values_colorscale.json
@@ -0,0 +1,172 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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": {
+ "text": "trace A"
+ },
+ "x": 0,
+ "xanchor": "right"
+ }
+ },
+ "textinfo": "label+value",
+ "tiling": {
+ "pad": 4
+ },
+ "pathbar": {
+ "thickness": 32,
+ "textfont": {
+ "color": "white",
+ "size": 28,
+ "family": "Times New Roman"
+ }
+ }
+ },
+ {
+ "type": "icicle",
+ "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": {
+ "text": "trace B"
+ }
+ }
+ },
+ "textinfo": "label+text"
+ }
+ ],
+ "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/icicle_with-without_values.json b/test/image/mocks/icicle_with-without_values.json
new file mode 100644
index 00000000000..158e022e424
--- /dev/null
+++ b/test/image/mocks/icicle_with-without_values.json
@@ -0,0 +1,430 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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": "icicle",
+ "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": "icicle",
+ "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/icicle_with-without_values_template.json b/test/image/mocks/icicle_with-without_values_template.json
new file mode 100644
index 00000000000..c7867756bbd
--- /dev/null
+++ b/test/image/mocks/icicle_with-without_values_template.json
@@ -0,0 +1,434 @@
+{
+ "data": [
+ {
+ "type": "icicle",
+ "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": "icicle",
+ "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": "icicle",
+ "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": {
+ "iciclecolorway": [
+ "#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/image/mocks/uniformtext_icicle.json b/test/image/mocks/uniformtext_icicle.json
new file mode 100644
index 00000000000..12037ceb949
--- /dev/null
+++ b/test/image/mocks/uniformtext_icicle.json
@@ -0,0 +1,365 @@
+{
+ "data": [
+ {
+ "tiling": {
+ "pad": 3,
+ "orientation": "v"
+ },
+ "marker": {
+ "coloraxis": "coloraxis"
+ },
+ "type": "icicle",
+ "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"
+ ],
+ "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"
+ ],
+ "textinfo": "label",
+ "domain": {
+ "x": [
+ 0.52,
+ 0.98
+ ],
+ "y": [
+ 0.52,
+ 0.98
+ ]
+ }
+ },
+ {
+ "tiling": {
+ "pad": 3,
+ "orientation": "v"
+ },
+ "marker": {
+ "coloraxis": "coloraxis"
+ },
+ "type": "icicle",
+ "level": "Uniform",
+ "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"
+ ],
+ "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"
+ ],
+ "textinfo": "label",
+ "textfont": {
+ "family": "Raleway, sans-serif",
+ "color": "#FFF",
+ "size": 15
+ },
+ "domain": {
+ "x": [
+ 0.52,
+ 0.98
+ ],
+ "y": [
+ 0.02,
+ 0.48
+ ]
+ }
+ },
+ {
+ "tiling": {
+ "pad": 3
+ },
+ "marker": {
+ "coloraxis": "coloraxis"
+ },
+ "type": "icicle",
+ "level": "Juliet",
+ "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"
+ ],
+ "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"
+ ],
+ "textinfo": "label",
+ "insidetextfont": {
+ "family": "Courier New, monospace",
+ "color": "#000",
+ "size": 10
+ },
+ "pathbar": {
+ "textfont": {
+ "family": "Times New Roman, Times, serif",
+ "color": "#FF0",
+ "size": 20
+ }
+ },
+ "domain": {
+ "x": [
+ 0.02,
+ 0.48
+ ],
+ "y": [
+ 0.02,
+ 0.48
+ ]
+ }
+ },
+ {
+ "tiling": {
+ "pad": 3
+ },
+ "marker": {
+ "coloraxis": "coloraxis"
+ },
+ "type": "icicle",
+ "level": "Juliet",
+ "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"
+ ],
+ "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"
+ ],
+ "textinfo": "label",
+ "insidetextfont": {
+ "family": "Times New Roman, Times, serif",
+ "color": "#FF0",
+ "size": 20
+ },
+ "pathbar": {
+ "textfont": {
+ "family": "Courier New, monospace",
+ "color": "#000",
+ "size": 10
+ }
+ },
+ "domain": {
+ "x": [
+ 0.02,
+ 0.48
+ ],
+ "y": [
+ 0.52,
+ 0.98
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "coloraxis": {
+ "colorscale": "Portland",
+ "reversescale": true,
+ "showscale": false
+ },
+ "width": 550,
+ "height": 800,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "uniformtext": {
+ "minsize": 12,
+ "mode": "hide"
+ }
+ }
+}
diff --git a/test/jasmine/tests/icicle_test.js b/test/jasmine/tests/icicle_test.js
new file mode 100644
index 00000000000..143165feb80
--- /dev/null
+++ b/test/jasmine/tests/icicle_test.js
@@ -0,0 +1,922 @@
+var Plotly = require('@lib/index');
+var Plots = require('@src/plots/plots');
+var Lib = require('@src/lib');
+
+var d3Select = require('../../strict-d3').select;
+var d3SelectAll = require('../../strict-d3').selectAll;
+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 customAssertions = require('../assets/custom_assertions');
+var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle;
+var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
+var checkTextTemplate = require('../assets/check_texttemplate');
+
+var SLICES_SELECTOR = '.iciclelayer path.surface';
+
+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 = d3Select(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);
+}
+
+describe('Test icicle 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: 'icicle'}, 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 default *leaf.opacity* depending on a *colorscale* being present or not', 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']},
+ {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 *iciclecolorway*', function() {
+ _supply([
+ {labels: [1], parents: ['']}
+ ], {
+ colorway: ['red', 'blue', 'green']
+ });
+ expect(gd._fullLayout.iciclecolorway)
+ .toEqual(['red', 'blue', 'green'], 'dflt to layout colorway');
+
+ _supply([
+ {labels: [1], parents: ['']}
+ ], {
+ colorway: ['red', 'blue', 'green'],
+ iciclecolorway: ['cyan', 'yellow', 'black']
+ });
+ expect(gd._fullLayout.iciclecolorway)
+ .toEqual(['cyan', 'yellow', 'black'], 'user-defined value');
+ });
+
+ it('should not default *marker.colorscale* when *marker.colors* is not present', function() {
+ _supply([
+ {labels: [1], parents: ['']}
+ ]);
+
+ expect(fullData[0].marker.colorscale).toBe(undefined);
+ });
+
+ it('should default *marker.colorscale* to *Reds* when *marker.colors* is present', function() {
+ _supply([
+ {labels: [1], parents: [''], marker: {
+ colors: [0]
+ }}
+ ]);
+
+ expect(fullData[0].marker.colorscale).toBeCloseToArray([
+ [ 0, 'rgb(5,10,172)' ],
+ [ 0.35, 'rgb(106,137,247)' ],
+ [ 0.5, 'rgb(190,190,190)' ],
+ [ 0.6, 'rgb(220,170,132)' ],
+ [ 0.7, 'rgb(230,145,90)' ],
+ [ 1, 'rgb(178,10,28)' ]
+ ]);
+ });
+});
+
+describe('Test icicle 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: 'icicle'}, 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 Lib.nestedProperty(pt, k).get();
+ });
+ });
+ 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 icicle hierarchy of trace 0. These roots include: Root1, Root22');
+ });
+
+ 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(['', '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 of trace 0 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 of trace 0 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 icicle hierarchy of trace 0. 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 icicle hierarchy of trace 0. 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']);
+ });
+
+ it('should compute correct sector *value* for generated implied root', function() {
+ _calc([{
+ labels: [ 'A', 'B', 'b'],
+ parents: ['Root', 'Root', 'B'],
+ values: [1, 2, 1],
+ branchvalues: 'remainder'
+ }, {
+ labels: [ 'A', 'B', 'b'],
+ parents: ['Root', 'Root', 'B'],
+ values: [1, 2, 1],
+ branchvalues: 'total'
+ }]);
+
+ expect(extractPt('data.data.id')).toEqual([
+ ['Root', 'B', 'A', 'b'],
+ ['Root', 'B', 'A', 'b']
+ ]);
+ expect(extractPt('value')).toEqual([
+ [4, 3, 1, 1],
+ [3, 2, 1, 1]
+ ]);
+ });
+
+ it('should compute correct sector *value* for generated "root of roots"', function() {
+ spyOn(Lib, 'randstr').and.callFake(function() { return 'dummy'; });
+
+ _calc([{
+ labels: [ 'A', 'B', 'b'],
+ parents: ['', '', 'B'],
+ values: [1, 2, 1],
+ branchvalues: 'remainder'
+ }, {
+ labels: [ 'A', 'B', 'b'],
+ parents: ['', '', 'B'],
+ values: [1, 2, 1],
+ branchvalues: 'total'
+ }]);
+
+ expect(extractPt('data.data.id')).toEqual([
+ ['dummy', 'B', 'A', 'b'],
+ ['dummy', 'B', 'A', 'b']
+ ]);
+ expect(extractPt('value')).toEqual([
+ [4, 3, 1, 1],
+ [3, 2, 1, 1]
+ ]);
+ });
+
+ it('should use *marker.colors*', function() {
+ _calc({
+ marker: { colors: ['pink', '#777', '#f00', '#ff0', '#0f0', '#0ff', '#00f', '#f0f', '#fff'] },
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve']
+ });
+
+ var cd = gd.calcdata[0];
+ expect(cd.length).toEqual(9);
+ expect(cd[0].color).toEqual('rgba(255, 192, 203, 1)');
+ expect(cd[1].color).toEqual('rgba(119, 119, 119, 1)');
+ expect(cd[2].color).toEqual('rgba(255, 0, 0, 1)');
+ expect(cd[3].color).toEqual('rgba(255, 255, 0, 1)');
+ expect(cd[4].color).toEqual('rgba(0, 255, 0, 1)');
+ expect(cd[5].color).toEqual('rgba(0, 255, 255, 1)');
+ expect(cd[6].color).toEqual('rgba(0, 0, 255, 1)');
+ expect(cd[7].color).toEqual('rgba(255, 0, 255, 1)');
+ expect(cd[8].color).toEqual('rgba(255, 255, 255, 1)');
+ });
+
+ it('should use *marker.colors* numbers with default colorscale', function() {
+ _calc({
+ marker: { colors: [-4, -3, -2, -1, 0, 1, 2, 3, 4] },
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve']
+ });
+
+ var cd = gd.calcdata[0];
+ expect(cd.length).toEqual(9);
+ expect(cd[0].color).toEqual('rgb(5, 10, 172)');
+ expect(cd[1].color).toEqual('rgb(41, 55, 199)');
+ expect(cd[2].color).toEqual('rgb(77, 101, 226)');
+ expect(cd[3].color).toEqual('rgb(120, 146, 238)');
+ expect(cd[4].color).toEqual('rgb(190, 190, 190)');
+ expect(cd[5].color).toEqual('rgb(223, 164, 122)');
+ expect(cd[6].color).toEqual('rgb(221, 123, 80)');
+ expect(cd[7].color).toEqual('rgb(200, 66, 54)');
+ expect(cd[8].color).toEqual('rgb(178, 10, 28)');
+ });
+
+ it('should use *marker.colors* numbers with desired colorscale', function() {
+ _calc({
+ marker: { colors: [1, 2, 3, 4, 5, 6, 7, 8, 9], colorscale: 'Portland' },
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve']
+ });
+
+ var cd = gd.calcdata[0];
+ expect(cd.length).toEqual(9);
+ expect(cd[0].color).toEqual('rgb(12, 51, 131)');
+ expect(cd[1].color).toEqual('rgb(11, 94, 159)');
+ expect(cd[2].color).toEqual('rgb(10, 136, 186)');
+ expect(cd[3].color).toEqual('rgb(126, 174, 121)');
+ expect(cd[4].color).toEqual('rgb(242, 211, 56)');
+ expect(cd[5].color).toEqual('rgb(242, 177, 56)');
+ expect(cd[6].color).toEqual('rgb(242, 143, 56)');
+ expect(cd[7].color).toEqual('rgb(230, 87, 43)');
+ expect(cd[8].color).toEqual('rgb(217, 30, 30)');
+ });
+
+ it('should use *marker.colors* numbers not values with colorscale', function() {
+ _calc({
+ values: [0.0001, 0.001, 0.01, 0.1, 1, 10, 100, 1000, 10000],
+ marker: { colors: [1, 2, 3, 4, 5, 6, 7, 8, 9], colorscale: 'Portland' },
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve']
+ });
+
+ var cd = gd.calcdata[0];
+ expect(cd.length).toEqual(9);
+ expect(cd[0].color).toEqual('rgb(12, 51, 131)');
+ expect(cd[1].color).toEqual('rgb(11, 94, 159)');
+ expect(cd[2].color).toEqual('rgb(10, 136, 186)');
+ expect(cd[3].color).toEqual('rgb(126, 174, 121)');
+ expect(cd[4].color).toEqual('rgb(242, 211, 56)');
+ expect(cd[5].color).toEqual('rgb(242, 177, 56)');
+ expect(cd[6].color).toEqual('rgb(242, 143, 56)');
+ expect(cd[7].color).toEqual('rgb(230, 87, 43)');
+ expect(cd[8].color).toEqual('rgb(217, 30, 30)');
+ });
+
+ it('should use values with colorscale when *marker.colors* in empty', function() {
+ _calc({
+ values: [1, 2, 3, 4, 5, 6, 7, 8, 9],
+ marker: { colors: [], colorscale: 'Portland' },
+ labels: ['Eve', 'Cain', 'Seth', 'Enos', 'Noam', 'Abel', 'Awan', 'Enoch', 'Azura'],
+ parents: ['', 'Eve', 'Eve', 'Seth', 'Seth', 'Eve', 'Eve', 'Awan', 'Eve']
+ });
+
+ var cd = gd.calcdata[0];
+ expect(cd.length).toEqual(9);
+ expect(cd[0].color).toEqual('rgb(12, 51, 131)');
+ expect(cd[1].color).toEqual('rgb(11, 94, 159)');
+ expect(cd[2].color).toEqual('rgb(10, 136, 186)');
+ expect(cd[3].color).toEqual('rgb(126, 174, 121)');
+ expect(cd[4].color).toEqual('rgb(242, 211, 56)');
+ expect(cd[5].color).toEqual('rgb(242, 177, 56)');
+ expect(cd[6].color).toEqual('rgb(242, 143, 56)');
+ expect(cd[7].color).toEqual('rgb(230, 87, 43)');
+ expect(cd[8].color).toEqual('rgb(217, 30, 30)');
+ });
+});
+
+describe('Test icicle 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 = 'icicle';
+ 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.newPlot(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 = d3Select(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).then(done, done.fail);
+ });
+ });
+});
+
+describe('Test icicle 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/icicle_first.json'));
+
+ function _assert(msg, exp) {
+ return function() {
+ var layer = d3Select(gd).select('.iciclelayer');
+ expect(layer.selectAll('.trace').size()).toBe(exp, msg);
+ };
+ }
+
+ Plotly.newPlot(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))
+ .then(done, done.fail);
+ });
+
+ it('should be able to restyle *maxdepth* and *level* w/o recomputing the hierarchy', function(done) {
+ var mock = Lib.extendDeep({}, require('@mocks/icicle_coffee.json'));
+
+ function _assert(msg, exp) {
+ return function() {
+ var layer = d3Select(gd).select('.iciclelayer');
+
+ expect(layer.selectAll('.slice').size()).toBe(exp, msg);
+
+ // editType:plot
+ if(msg !== 'base') {
+ expect(Plots.doCalcdata).toHaveBeenCalledTimes(0);
+ }
+ };
+ }
+
+ Plotly.newPlot(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))
+ .then(done, done.fail);
+ });
+
+ it('should be able to restyle *leaf.opacity*', function(done) {
+ var mock = {
+ data: [{
+ type: 'icicle',
+ labels: ['Root', 'A', 'B', 'b'],
+ parents: ['', 'Root', 'Root', 'B']
+ }]
+ };
+
+ function _assert(msg, exp) {
+ return function() {
+ var layer = d3Select(gd).select('.iciclelayer');
+
+ 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.newPlot(gd, mock)
+ .then(_assert('base', ['', '0.7', '', '0.7']))
+ .then(function() {
+ spyOn(Plots, 'doCalcdata').and.callThrough();
+ spyOn(gd._fullData[0]._module, 'plot').and.callThrough();
+ })
+ .then(_restyle({'leaf.opacity': 0.3}))
+ .then(_assert('lower leaf.opacity', ['', '0.3', '', '0.3']))
+ .then(_restyle({'leaf.opacity': 1}))
+ .then(_assert('raise leaf.opacity', ['', '1', '', '1']))
+ .then(_restyle({'leaf.opacity': null}))
+ .then(_assert('back to dflt', ['', '0.7', '', '0.7']))
+ .then(done, done.fail);
+ });
+});
+
+describe('Test icicle texttemplate without `values` should work at root level:', function() {
+ checkTextTemplate([{
+ type: 'icicle',
+ 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 icicle texttemplate with *total* `values` should work at root level:', function() {
+ checkTextTemplate([{
+ type: 'icicle',
+ 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('icicle pathbar react', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ it('should show and hide pathbar', function(done) {
+ var fig = {
+ data: [{
+ type: 'icicle',
+ parents: ['', 'A', 'B', 'C'],
+ labels: ['A', 'B', 'C', 'D'],
+ level: 'C'
+ }],
+ layout: {}
+ };
+
+ function _assert(msg, exp) {
+ return function() {
+ var selection = d3SelectAll(SLICES_SELECTOR);
+ var size = selection.size();
+
+ expect(size).toBe(exp, msg);
+ };
+ }
+
+ Plotly.newPlot(gd, fig)
+ .then(_assert('default pathbar.visible: true', 4))
+ .then(function() {
+ fig.data[0].pathbar = {visible: false};
+ return Plotly.react(gd, fig);
+ })
+ .then(_assert('disable pathbar', 2))
+ .then(function() {
+ fig.data[0].pathbar = {visible: true};
+ return Plotly.react(gd, fig);
+ })
+ .then(_assert('enable pathbar', 4))
+ .then(done, done.fail);
+ });
+});