diff --git a/src/lib/index.js b/src/lib/index.js
index 17ff52c33db..6039e420d2b 100644
--- a/src/lib/index.js
+++ b/src/lib/index.js
@@ -1182,31 +1182,48 @@ lib.isHidden = function(gd) {
return !display || display === 'none';
};
-lib.getTextTransform = function(opts) {
- var textX = opts.textX;
- var textY = opts.textY;
- var targetX = opts.targetX;
- var targetY = opts.targetY;
- var scale = opts.scale;
- var rotate = opts.rotate;
-
- var transformScale;
- var transformRotate;
- var transformTranslate;
-
- if(scale < 1) transformScale = 'scale(' + scale + ') ';
- else {
- scale = 1;
- transformScale = '';
- }
-
- transformRotate = (rotate) ?
- 'rotate(' + rotate + ' ' + textX + ' ' + textY + ') ' : '';
-
- // Note that scaling also affects the center of the text box
- var translateX = (targetX - scale * textX);
- var translateY = (targetY - scale * textY);
- transformTranslate = 'translate(' + translateX + ' ' + translateY + ')';
+/** Return transform text for bar bar-like rectangles and pie-like slices
+ * @param {object} transform
+ * - targetX: desired position on the x-axis
+ * - targetY: desired position on the y-axis
+ * - textX: width of text
+ * - textY: height of text
+ * - scale: (optional) scale applied after translate
+ * - rotate: (optional) rotation applied after scale
+ * - noCenter: when defined no extra arguments needed in rotation
+ */
+lib.getTextTransform = function(transform) {
+ var noCenter = transform.noCenter;
+ var textX = transform.textX;
+ var textY = transform.textY;
+ var targetX = transform.targetX;
+ var targetY = transform.targetY;
+ var rotate = transform.rotate;
+ var scale = transform.scale;
+ if(!scale) scale = 0;
+ else if(scale > 1) scale = 1;
+
+ return (
+ 'translate(' +
+ (targetX - scale * textX) + ',' +
+ (targetY - scale * textY) +
+ ')' +
+ (scale < 1 ?
+ 'scale(' + scale + ')' : ''
+ ) +
+ (rotate ?
+ 'rotate(' + rotate +
+ (noCenter ? '' : ' ' + textX + ' ' + textY) +
+ ')' : ''
+ )
+ );
+};
- return transformTranslate + transformScale + transformRotate;
+lib.ensureUniformFontSize = function(gd, baseFont) {
+ var out = lib.extendFlat({}, baseFont);
+ out.size = Math.max(
+ baseFont.size,
+ gd._fullLayout.uniformtext.minsize || 0
+ );
+ return out;
};
diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js
index e3fecbc40c8..d96cec68298 100644
--- a/src/plots/layout_attributes.js
+++ b/src/plots/layout_attributes.js
@@ -140,6 +140,36 @@ module.exports = {
}),
editType: 'layoutstyle'
},
+ uniformtext: {
+ mode: {
+ valType: 'enumerated',
+ values: [false, 'hide', 'show'],
+ dflt: false,
+ role: 'info',
+ editType: 'plot',
+ description: [
+ 'Determines how the font size for various text',
+ 'elements are uniformed between each trace type.',
+ 'If the computed text sizes were smaller than',
+ 'the minimum size defined by `uniformtext.minsize`',
+ 'using *hide* option hides the text; and',
+ 'using *show* option shows the text without further downscaling.',
+ 'Please note that if the size defined by `minsize` is greater than',
+ 'the font size defined by trace, then the `minsize` is used.'
+ ].join(' ')
+ },
+ minsize: {
+ valType: 'number',
+ min: 0,
+ dflt: 0,
+ role: 'info',
+ editType: 'plot',
+ description: [
+ 'Sets the minimum text size between traces of the same type.'
+ ].join(' ')
+ },
+ editType: 'plot'
+ },
autosize: {
valType: 'boolean',
role: 'info',
diff --git a/src/plots/plots.js b/src/plots/plots.js
index 136d9779cff..3d94c561d1f 100644
--- a/src/plots/plots.js
+++ b/src/plots/plots.js
@@ -1472,6 +1472,11 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) {
coerce('title.pad.b');
coerce('title.pad.l');
+ var uniformtextMode = coerce('uniformtext.mode');
+ if(uniformtextMode) {
+ coerce('uniformtext.minsize');
+ }
+
// Make sure that autosize is defaulted to *true*
// on layouts with no set width and height for backward compatibly,
// in particular https://plot.ly/javascript/responsive-fluid-layout/
diff --git a/src/traces/bar/plot.js b/src/traces/bar/plot.js
index d67a5abb677..e7b5e108cb2 100644
--- a/src/traces/bar/plot.js
+++ b/src/traces/bar/plot.js
@@ -237,14 +237,14 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback)
Registry.getComponentMethod('errorbars', 'plot')(gd, bartraces, plotinfo, opts);
}
-function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, makeOnCompleteCallback) {
+function appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCompleteCallback) {
var xa = plotinfo.xaxis;
var ya = plotinfo.yaxis;
var fullLayout = gd._fullLayout;
var textPosition;
- function appendTextNode(bar, text, textFont) {
+ function appendTextNode(bar, text, font) {
var textSelection = Lib.ensureSingle(bar, 'text')
.text(text)
.attr({
@@ -254,17 +254,17 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, ma
// tex and regular text together
'data-notex': 1
})
- .call(Drawing.font, textFont)
+ .call(Drawing.font, font)
.call(svgTextUtils.convertToTspans, gd);
return textSelection;
}
// get trace attributes
- var trace = calcTrace[0].trace;
+ var trace = cd[0].trace;
var isHorizontal = (trace.orientation === 'h');
- var text = getText(fullLayout, calcTrace, i, xa, ya);
+ var text = getText(fullLayout, cd, i, xa, ya);
textPosition = getTextPosition(trace, i);
// compute text position
@@ -272,7 +272,7 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, ma
opts.mode === 'stack' ||
opts.mode === 'relative';
- var calcBar = calcTrace[i];
+ var calcBar = cd[i];
var isOutmostBar = !inStackOrRelativeMode || calcBar._outmost;
if(!text ||
@@ -285,7 +285,7 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, ma
}
var layoutFont = fullLayout.font;
- var barColor = style.getBarColor(calcTrace[i], trace);
+ var barColor = style.getBarColor(cd[i], trace);
var insideTextFont = style.getInsideTextFont(trace, i, layoutFont, barColor);
var outsideTextFont = style.getOutsideTextFont(trace, i, layoutFont);
@@ -318,6 +318,7 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, ma
var textBB;
var textWidth;
var textHeight;
+ var font;
if(textPosition === 'outside') {
if(!isOutmostBar && !calcBar.hasB) textPosition = 'inside';
@@ -327,7 +328,10 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, ma
if(isOutmostBar) {
// draw text using insideTextFont and check if it fits inside bar
textPosition = 'inside';
- textSelection = appendTextNode(bar, text, insideTextFont);
+
+ font = Lib.ensureUniformFontSize(gd, insideTextFont);
+
+ textSelection = appendTextNode(bar, text, font);
textBB = Drawing.bBox(textSelection.node()),
textWidth = textBB.width,
@@ -357,9 +361,9 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, ma
}
if(!textSelection) {
- textSelection = appendTextNode(bar, text,
- (textPosition === 'outside') ?
- outsideTextFont : insideTextFont);
+ font = Lib.ensureUniformFontSize(gd, (textPosition === 'outside') ? outsideTextFont : insideTextFont);
+
+ textSelection = appendTextNode(bar, text, font);
var currentTransform = textSelection.attr('transform');
textSelection.attr('transform', '');
@@ -374,6 +378,8 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, ma
}
}
+ var angle = trace.textangle;
+
// compute text transform
var transform, constrained;
if(textPosition === 'outside') {
@@ -381,25 +387,52 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts, ma
trace.constraintext === 'both' ||
trace.constraintext === 'outside';
- transform = Lib.getTextTransform(toMoveOutsideBar(x0, x1, y0, y1, textBB, {
+ transform = toMoveOutsideBar(x0, x1, y0, y1, textBB, {
isHorizontal: isHorizontal,
constrained: constrained,
- angle: trace.textangle
- }));
+ angle: angle
+ });
} else {
constrained =
trace.constraintext === 'both' ||
trace.constraintext === 'inside';
- transform = Lib.getTextTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, {
+ transform = toMoveInsideBar(x0, x1, y0, y1, textBB, {
isHorizontal: isHorizontal,
constrained: constrained,
- angle: trace.textangle,
+ angle: angle,
anchor: trace.insidetextanchor
- }));
+ });
}
- transition(textSelection, opts, makeOnCompleteCallback).attr('transform', transform);
+ transform.fontSize = font.size;
+ recordMinTextSize(trace.type, transform, fullLayout);
+ calcBar.transform = transform;
+
+ transition(textSelection, opts, makeOnCompleteCallback)
+ .attr('transform', Lib.getTextTransform(transform));
+}
+
+function recordMinTextSize(
+ traceType, // in
+ transform, // inout
+ fullLayout // inout
+) {
+ if(fullLayout.uniformtext.mode) {
+ var minKey = '_' + traceType + 'Text_minsize';
+ var minSize = fullLayout.uniformtext.minsize;
+ var size = transform.scale * transform.fontSize;
+
+ transform.hide = size < minSize;
+
+ fullLayout[minKey] = fullLayout[minKey] || Infinity;
+ if(!transform.hide) {
+ fullLayout[minKey] = Math.min(
+ fullLayout[minKey],
+ Math.max(size, minSize)
+ );
+ }
+ }
}
function getRotateFromAngle(angle) {
@@ -549,15 +582,15 @@ function toMoveOutsideBar(x0, x1, y0, y1, textBB, opts) {
};
}
-function getText(fullLayout, calcTrace, index, xa, ya) {
- var trace = calcTrace[0].trace;
+function getText(fullLayout, cd, index, xa, ya) {
+ var trace = cd[0].trace;
var texttemplate = trace.texttemplate;
var value;
if(texttemplate) {
- value = calcTexttemplate(fullLayout, calcTrace, index, xa, ya);
+ value = calcTexttemplate(fullLayout, cd, index, xa, ya);
} else if(trace.textinfo) {
- value = calcTextinfo(calcTrace, index, xa, ya);
+ value = calcTextinfo(cd, index, xa, ya);
} else {
value = helpers.getValue(trace.text, index);
}
@@ -570,8 +603,8 @@ function getTextPosition(trace, index) {
return helpers.coerceEnumerated(attributeTextPosition, value);
}
-function calcTexttemplate(fullLayout, calcTrace, index, xa, ya) {
- var trace = calcTrace[0].trace;
+function calcTexttemplate(fullLayout, cd, index, xa, ya) {
+ var trace = cd[0].trace;
var texttemplate = Lib.castOption(trace, index, 'texttemplate');
if(!texttemplate) return '';
var isWaterfall = (trace.type === 'waterfall');
@@ -599,7 +632,7 @@ function calcTexttemplate(fullLayout, calcTrace, index, xa, ya) {
return tickText(vAxis, +v, true).text;
}
- var cdi = calcTrace[index];
+ var cdi = cd[index];
var obj = {};
obj.label = cdi.p;
@@ -640,8 +673,8 @@ function calcTexttemplate(fullLayout, calcTrace, index, xa, ya) {
return Lib.texttemplateString(texttemplate, obj, fullLayout._d3locale, pt, obj, trace._meta || {});
}
-function calcTextinfo(calcTrace, index, xa, ya) {
- var trace = calcTrace[0].trace;
+function calcTextinfo(cd, index, xa, ya) {
+ var trace = cd[0].trace;
var isHorizontal = (trace.orientation === 'h');
var isWaterfall = (trace.type === 'waterfall');
var isFunnel = (trace.type === 'funnel');
@@ -657,7 +690,7 @@ function calcTextinfo(calcTrace, index, xa, ya) {
}
var textinfo = trace.textinfo;
- var cdi = calcTrace[index];
+ var cdi = cd[index];
var parts = textinfo.split('+');
var text = [];
@@ -666,7 +699,7 @@ function calcTextinfo(calcTrace, index, xa, ya) {
var hasFlag = function(flag) { return parts.indexOf(flag) !== -1; };
if(hasFlag('label')) {
- text.push(formatLabel(calcTrace[index].p));
+ text.push(formatLabel(cd[index].p));
}
if(hasFlag('text')) {
@@ -717,5 +750,5 @@ function calcTextinfo(calcTrace, index, xa, ya) {
module.exports = {
plot: plot,
toMoveInsideBar: toMoveInsideBar,
- toMoveOutsideBar: toMoveOutsideBar
+ recordMinTextSize: recordMinTextSize
};
diff --git a/src/traces/bar/style.js b/src/traces/bar/style.js
index b781d301854..2b204b45e8e 100644
--- a/src/traces/bar/style.js
+++ b/src/traces/bar/style.js
@@ -20,8 +20,40 @@ var attributeInsideTextFont = attributes.insidetextfont;
var attributeOutsideTextFont = attributes.outsidetextfont;
var helpers = require('./helpers');
+function resizeText(gd, gTrace, traceType) {
+ var fullLayout = gd._fullLayout;
+ var minSize = fullLayout['_' + traceType + 'Text_minsize'];
+ if(minSize) {
+ var shouldHide = fullLayout.uniformtext.mode === 'hide';
+
+ var t;
+ switch(traceType) {
+ case 'funnelarea' :
+ case 'pie' :
+ case 'sunburst' :
+ case 'treemap' :
+ t = gTrace.selectAll('g.slice');
+ break;
+ default :
+ t = gTrace.selectAll('g.points > g.point');
+ }
+
+ t.each(function(d) {
+ var transform = d.transform;
+ if(transform) {
+ transform.scale = (shouldHide && transform.hide) ? 0 : minSize / transform.fontSize;
+
+ var el = d3.select(this).select('text');
+ el.attr('transform', Lib.getTextTransform(transform));
+ }
+ });
+ }
+}
+
function style(gd) {
var s = d3.select(gd).selectAll('g.barlayer').selectAll('g.trace');
+ resizeText(gd, s, 'bar');
+
var barcount = s.size();
var fullLayout = gd._fullLayout;
@@ -57,7 +89,8 @@ function stylePoints(sel, trace, gd) {
function styleTextPoints(sel, trace, gd) {
sel.selectAll('text').each(function(d) {
var tx = d3.select(this);
- var font = determineFont(tx, d, trace, gd);
+ var font = Lib.ensureUniformFontSize(gd, determineFont(tx, d, trace, gd));
+
Drawing.font(tx, font);
});
}
@@ -84,7 +117,7 @@ function styleTextInSelectionMode(txs, trace, gd) {
var font;
if(d.selected) {
- font = Lib.extendFlat({}, determineFont(tx, d, trace, gd));
+ font = Lib.ensureUniformFontSize(gd, determineFont(tx, d, trace, gd));
var selectedFontColor = trace.selected.textfont && trace.selected.textfont.color;
if(selectedFontColor) {
@@ -171,5 +204,6 @@ module.exports = {
styleOnSelect: styleOnSelect,
getInsideTextFont: getInsideTextFont,
getOutsideTextFont: getOutsideTextFont,
- getBarColor: getBarColor
+ getBarColor: getBarColor,
+ resizeText: resizeText
};
diff --git a/src/traces/funnel/style.js b/src/traces/funnel/style.js
index e59c81fe864..edb48c5568b 100644
--- a/src/traces/funnel/style.js
+++ b/src/traces/funnel/style.js
@@ -13,11 +13,13 @@ var d3 = require('d3');
var Drawing = require('../../components/drawing');
var Color = require('../../components/color');
var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
-
-var styleTextPoints = require('../bar/style').styleTextPoints;
+var barStyle = require('../bar/style');
+var resizeText = barStyle.resizeText;
+var styleTextPoints = barStyle.styleTextPoints;
function style(gd, cd, sel) {
var s = sel ? sel : d3.select(gd).selectAll('g.funnellayer').selectAll('g.trace');
+ resizeText(gd, s, 'funnel');
s.style('opacity', function(d) { return d[0].trace.opacity; });
diff --git a/src/traces/funnelarea/plot.js b/src/traces/funnelarea/plot.js
index 29ce263c1d1..6c0cb07567c 100644
--- a/src/traces/funnelarea/plot.js
+++ b/src/traces/funnelarea/plot.js
@@ -16,6 +16,7 @@ var svgTextUtils = require('../../lib/svg_text_utils');
var barPlot = require('../bar/plot');
var toMoveInsideBar = barPlot.toMoveInsideBar;
+var recordMinTextSize = barPlot.recordMinTextSize;
var pieHelpers = require('../pie/helpers');
var piePlot = require('../pie/plot');
@@ -26,6 +27,7 @@ var determineInsideTextFont = piePlot.determineInsideTextFont;
var layoutAreas = piePlot.layoutAreas;
var prerenderTitles = piePlot.prerenderTitles;
var positionTitleOutside = piePlot.positionTitleOutside;
+var formatSliceLabel = piePlot.formatSliceLabel;
module.exports = function plot(gd, cdModule) {
var fullLayout = gd._fullLayout;
@@ -47,7 +49,7 @@ module.exports = function plot(gd, cdModule) {
.classed('slice', true);
slices.exit().remove();
- slices.each(function(pt) {
+ slices.each(function(pt, i) {
if(pt.hidden) {
d3.select(this).selectAll('path,g').remove();
return;
@@ -78,7 +80,7 @@ module.exports = function plot(gd, cdModule) {
slicePath.attr('d', shape);
// add text
- piePlot.formatSliceLabel(gd, pt, cd0);
+ formatSliceLabel(gd, pt, cd0);
var textPosition = pieHelpers.castOption(trace.textposition, pt.pts);
var sliceTextGroup = sliceTop.selectAll('g.slicetext')
.data(pt.text && (textPosition !== 'none') ? [0] : []);
@@ -94,13 +96,15 @@ module.exports = function plot(gd, cdModule) {
s.attr('data-notex', 1);
});
+ var font = Lib.ensureUniformFontSize(gd, determineInsideTextFont(trace, pt, fullLayout.font));
+
sliceText.text(pt.text)
.attr({
'class': 'slicetext',
transform: '',
'text-anchor': 'middle'
})
- .call(Drawing.font, determineInsideTextFont(trace, pt, gd._fullLayout.font))
+ .call(Drawing.font, font)
.call(svgTextUtils.convertToTspans, gd);
// position the text relative to the slice
@@ -108,22 +112,24 @@ module.exports = function plot(gd, cdModule) {
var transform;
var x0, x1;
- var y0 = Math.min(pt.BL[1], pt.BR[1]);
- var y1 = Math.max(pt.TL[1], pt.TR[1]);
+ var y0 = Math.min(pt.BL[1], pt.BR[1]) + cy;
+ var y1 = Math.max(pt.TL[1], pt.TR[1]) + cy;
- x0 = Math.max(pt.TL[0], pt.BL[0]);
- x1 = Math.min(pt.TR[0], pt.BR[0]);
+ x0 = Math.max(pt.TL[0], pt.BL[0]) + cx;
+ x1 = Math.min(pt.TR[0], pt.BR[0]) + cx;
- transform = Lib.getTextTransform(toMoveInsideBar(x0, x1, y0, y1, textBB, {
+ transform = toMoveInsideBar(x0, x1, y0, y1, textBB, {
isHorizontal: true,
constrained: true,
angle: 0,
anchor: 'middle'
- }));
+ });
+
+ transform.fontSize = font.size;
+ recordMinTextSize(trace.type, transform, fullLayout);
+ cd[i].transform = transform;
- sliceText.attr('transform',
- 'translate(' + cx + ',' + cy + ')' + transform
- );
+ sliceText.attr('transform', Lib.getTextTransform(transform));
});
});
diff --git a/src/traces/funnelarea/style.js b/src/traces/funnelarea/style.js
index af5455a7613..a17265e1092 100644
--- a/src/traces/funnelarea/style.js
+++ b/src/traces/funnelarea/style.js
@@ -11,9 +11,13 @@
var d3 = require('d3');
var styleOne = require('../pie/style_one');
+var resizeText = require('../bar/style').resizeText;
module.exports = function style(gd) {
- gd._fullLayout._funnelarealayer.selectAll('.trace').each(function(cd) {
+ var s = gd._fullLayout._funnelarealayer.selectAll('.trace');
+ resizeText(gd, s, 'funnelarea');
+
+ s.each(function(cd) {
var cd0 = cd[0];
var trace = cd0.trace;
var traceSelection = d3.select(this);
diff --git a/src/traces/pie/attributes.js b/src/traces/pie/attributes.js
index 32bd7980258..be8e0f9bd25 100644
--- a/src/traces/pie/attributes.js
+++ b/src/traces/pie/attributes.js
@@ -181,6 +181,21 @@ module.exports = {
textfont: extendFlat({}, textFontAttrs, {
description: 'Sets the font used for `textinfo`.'
}),
+ insidetextorientation: {
+ valType: 'enumerated',
+ role: 'info',
+ values: ['horizontal', 'radial', 'tangential', 'auto'],
+ dflt: 'auto',
+ editType: 'plot',
+ description: [
+ 'Determines the orientation of text inside slices.',
+ 'With *auto* the texts may automatically be',
+ 'rotated to fit with the maximum size inside the slice.',
+ 'Using *horizontal* option forces text to be horizontal.',
+ 'Using *radial* option forces text to be radial.',
+ 'Using *tangential* option forces text to be tangential.'
+ ].join(' ')
+ },
insidetextfont: extendFlat({}, textFontAttrs, {
description: 'Sets the font used for `textinfo` lying inside the sector.'
}),
diff --git a/src/traces/pie/defaults.js b/src/traces/pie/defaults.js
index 0d7cfc47343..1b41a052688 100644
--- a/src/traces/pie/defaults.js
+++ b/src/traces/pie/defaults.js
@@ -70,6 +70,10 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
if(hasOutside) {
coerce('automargin');
}
+
+ if(textposition === 'inside' || textposition === 'auto' || Array.isArray(textposition)) {
+ coerce('insidetextorientation');
+ }
}
handleDomainDefaults(traceOut, layout, coerce);
diff --git a/src/traces/pie/plot.js b/src/traces/pie/plot.js
index 519c8d3a821..fcc4bc1f2ef 100644
--- a/src/traces/pie/plot.js
+++ b/src/traces/pie/plot.js
@@ -16,6 +16,7 @@ var Color = require('../../components/color');
var Drawing = require('../../components/drawing');
var Lib = require('../../lib');
var svgTextUtils = require('../../lib/svg_text_utils');
+var recordMinTextSize = require('../bar/plot').recordMinTextSize;
var helpers = require('./helpers');
var eventData = require('./event_data');
@@ -52,7 +53,7 @@ function plot(gd, cdModule) {
];
var hasOutsideText = false;
- slices.each(function(pt) {
+ slices.each(function(pt, i) {
if(pt.hidden) {
d3.select(this).selectAll('path,g').remove();
return;
@@ -144,15 +145,18 @@ function plot(gd, cdModule) {
s.attr('data-notex', 1);
});
+ var font = Lib.ensureUniformFontSize(gd, textPosition === 'outside' ?
+ determineOutsideTextFont(trace, pt, fullLayout.font) :
+ determineInsideTextFont(trace, pt, fullLayout.font)
+ );
+
sliceText.text(pt.text)
.attr({
'class': 'slicetext',
transform: '',
'text-anchor': 'middle'
})
- .call(Drawing.font, textPosition === 'outside' ?
- determineOutsideTextFont(trace, pt, gd._fullLayout.font) :
- determineInsideTextFont(trace, pt, gd._fullLayout.font))
+ .call(Drawing.font, font)
.call(svgTextUtils.convertToTspans, gd);
// position the text relative to the slice
@@ -164,36 +168,37 @@ function plot(gd, cdModule) {
} else {
transform = transformInsideText(textBB, pt, cd0);
if(textPosition === 'auto' && transform.scale < 1) {
- sliceText.call(Drawing.font, trace.outsidetextfont);
- if(trace.outsidetextfont.family !== trace.insidetextfont.family ||
- trace.outsidetextfont.size !== trace.insidetextfont.size) {
- textBB = Drawing.bBox(sliceText.node());
- }
+ var newFont = Lib.ensureUniformFontSize(gd, trace.outsidetextfont);
+
+ sliceText.call(Drawing.font, newFont);
+ textBB = Drawing.bBox(sliceText.node());
+
transform = transformOutsideText(textBB, pt);
}
}
- var translateX = cx + pt.pxmid[0] * transform.rCenter + (transform.x || 0);
- var translateY = cy + pt.pxmid[1] * transform.rCenter + (transform.y || 0);
+ var textPosAngle = transform.textPosAngle;
+ var textXY = textPosAngle === undefined ? pt.pxmid : getCoords(cd0.r, textPosAngle);
+ transform.targetX = cx + textXY[0] * transform.rCenter + (transform.x || 0);
+ transform.targetY = cy + textXY[1] * transform.rCenter + (transform.y || 0);
+ computeTransform(transform, textBB);
// save some stuff to use later ensure no labels overlap
if(transform.outside) {
- pt.yLabelMin = translateY - textBB.height / 2;
- pt.yLabelMid = translateY;
- pt.yLabelMax = translateY + textBB.height / 2;
+ var targetY = transform.targetY;
+ pt.yLabelMin = targetY - textBB.height / 2;
+ pt.yLabelMid = targetY;
+ pt.yLabelMax = targetY + textBB.height / 2;
pt.labelExtraX = 0;
pt.labelExtraY = 0;
hasOutsideText = true;
}
- sliceText.attr('transform',
- 'translate(' + translateX + ',' + translateY + ')' +
- (transform.scale < 1 ? ('scale(' + transform.scale + ')') : '') +
- (transform.rotate ? ('rotate(' + transform.rotate + ')') : '') +
- 'translate(' +
- (-(textBB.left + textBB.right) / 2) + ',' +
- (-(textBB.top + textBB.bottom) / 2) +
- ')');
+ transform.fontSize = font.size;
+ recordMinTextSize(trace.type, transform, fullLayout);
+ cd[i].transform = transform;
+
+ sliceText.attr('transform', Lib.getTextTransform(transform));
});
});
@@ -298,8 +303,10 @@ function plotTextLines(slices, trace) {
// first move the text to its new location
var sliceText = sliceTop.select('g.slicetext text');
- sliceText.attr('transform', 'translate(' + pt.labelExtraX + ',' + pt.labelExtraY + ')' +
- sliceText.attr('transform'));
+ pt.transform.targetX += pt.labelExtraX;
+ pt.transform.targetY += pt.labelExtraY;
+
+ sliceText.attr('transform', Lib.getTextTransform(pt.transform));
// then add a line to the new location
var lineStartX = pt.cxFinal + pt.pxmid[0];
@@ -549,59 +556,150 @@ function prerenderTitles(cdModule, gd) {
function transformInsideText(textBB, pt, cd0) {
var textDiameter = Math.sqrt(textBB.width * textBB.width + textBB.height * textBB.height);
- var textAspect = textBB.width / textBB.height;
var halfAngle = pt.halfangle;
+ var midAngle = pt.midangle;
var ring = pt.ring;
var rInscribed = pt.rInscribed;
var r = cd0.r || pt.rpx1;
+ var orientation = cd0.trace.insidetextorientation;
+ var isHorizontal = orientation === 'horizontal';
+ var isTangential = orientation === 'tangential';
+ var isRadial = orientation === 'radial';
+ var isAuto = orientation === 'auto';
+ var isCircle = (ring === 1) && (Math.abs(pt.startangle - pt.stopangle) === Math.PI * 2);
+ var allTransforms = [];
+ var newT;
+
+ if(!isAuto) {
+ // max size if text is placed (horizontally) at the top or bottom of the arc
+
+ var considerCrossing = function(angle, key) {
+ if(isCrossing(pt, angle)) {
+ var dStart = Math.abs(angle - pt.startangle);
+ var dStop = Math.abs(angle - pt.stopangle);
+
+ var closestEdge = dStart < dStop ? dStart : dStop;
+
+ if(key === 'tan') {
+ newT = calcTanTransform(textBB, r, ring, closestEdge, 0);
+ } else { // case of 'rad'
+ newT = calcRadTransform(textBB, r, ring, closestEdge, Math.PI / 2);
+ }
+ newT.textPosAngle = angle;
- // max size text can be inserted inside without rotating it
- // this inscribes the text rectangle in a circle, which is then inscribed
- // in the slice, so it will be an underestimate, which some day we may want
- // to improve so this case can get more use
- var transform = {
- scale: rInscribed * r * 2 / textDiameter,
+ allTransforms.push(newT);
+ }
+ };
- // and the center position and rotation in this case
- rCenter: 1 - rInscribed,
- rotate: 0
- };
+ // to cover all cases with trace.rotation added
+ var i;
+ if(isHorizontal || isTangential) {
+ // top
+ for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * i, 'tan');
+ // bottom
+ for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 1), 'tan');
+ }
+ if(isHorizontal || isRadial) {
+ // left
+ for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 1.5), 'rad');
+ // right
+ for(i = 4; i >= -4; i -= 2) considerCrossing(Math.PI * (i + 0.5), 'rad');
+ }
+ }
- if(transform.scale >= 1) return transform;
+ if(isCircle || isAuto || isHorizontal) {
+ // max size text can be inserted inside without rotating it
+ // this inscribes the text rectangle in a circle, which is then inscribed
+ // in the slice, so it will be an underestimate, which some day we may want
+ // to improve so this case can get more use
+ newT = {
+ scale: rInscribed * r * 2 / textDiameter,
+
+ // and the center position and rotation in this case
+ rCenter: 1 - rInscribed,
+ rotate: 0
+ };
- // max size if text is rotated radially
- var Qr = textAspect + 1 / (2 * Math.tan(halfAngle));
- var maxHalfHeightRotRadial = r * Math.min(
- 1 / (Math.sqrt(Qr * Qr + 0.5) + Qr),
- ring / (Math.sqrt(textAspect * textAspect + ring / 2) + textAspect)
+ newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
+ if(newT.scale >= 1) return newT;
+
+ allTransforms.push(newT);
+ }
+
+ if(isAuto || isRadial) {
+ newT = calcRadTransform(textBB, r, ring, halfAngle, midAngle);
+ newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
+ allTransforms.push(newT);
+ }
+
+ if(isAuto || isTangential) {
+ newT = calcTanTransform(textBB, r, ring, halfAngle, midAngle);
+ newT.textPosAngle = (pt.startangle + pt.stopangle) / 2;
+ allTransforms.push(newT);
+ }
+
+ var id = 0;
+ var maxScale = 0;
+ for(var k = 0; k < allTransforms.length; k++) {
+ var s = allTransforms[k].scale;
+ if(maxScale < s) {
+ maxScale = s;
+ id = k;
+ }
+
+ if(!isAuto && maxScale >= 1) {
+ // respect test order for non-auto options
+ break;
+ }
+ }
+ return allTransforms[id];
+}
+
+function isCrossing(pt, angle) {
+ var start = pt.startangle;
+ var stop = pt.stopangle;
+ return (
+ (start > angle && angle > stop) ||
+ (start < angle && angle < stop)
);
- var radialTransform = {
- scale: maxHalfHeightRotRadial * 2 / textBB.height,
- rCenter: Math.cos(maxHalfHeightRotRadial / r) -
- maxHalfHeightRotRadial * textAspect / r,
- rotate: (180 / Math.PI * pt.midangle + 720) % 180 - 90
+}
+
+function calcRadTransform(textBB, r, ring, halfAngle, midAngle) {
+ // max size if text is rotated radially
+ var a = textBB.width / textBB.height;
+ var s = calcMaxHalfSize(a, halfAngle, r, ring);
+ return {
+ scale: s * 2 / textBB.height,
+ rCenter: calcRCenter(a, s / r),
+ rotate: calcRotate(midAngle)
};
+}
+function calcTanTransform(textBB, r, ring, halfAngle, midAngle) {
// max size if text is rotated tangentially
- var aspectInv = 1 / textAspect;
- var Qt = aspectInv + 1 / (2 * Math.tan(halfAngle));
- var maxHalfWidthTangential = r * Math.min(
- 1 / (Math.sqrt(Qt * Qt + 0.5) + Qt),
- ring / (Math.sqrt(aspectInv * aspectInv + ring / 2) + aspectInv)
- );
- var tangentialTransform = {
- scale: maxHalfWidthTangential * 2 / textBB.width,
- rCenter: Math.cos(maxHalfWidthTangential / r) -
- maxHalfWidthTangential / textAspect / r,
- rotate: (180 / Math.PI * pt.midangle + 810) % 180 - 90
+ var a = textBB.height / textBB.width;
+ var s = calcMaxHalfSize(a, halfAngle, r, ring);
+ return {
+ scale: s * 2 / textBB.width,
+ rCenter: calcRCenter(a, s / r),
+ rotate: calcRotate(midAngle + Math.PI / 2)
};
- // if we need a rotated transform, pick the biggest one
- // even if both are bigger than 1
- var rotatedTransform = tangentialTransform.scale > radialTransform.scale ?
- tangentialTransform : radialTransform;
+}
+
+function calcRCenter(a, b) {
+ return Math.cos(b) - a * b;
+}
+
+function calcRotate(t) {
+ return (180 / Math.PI * t + 720) % 180 - 90;
+}
- if(transform.scale < 1 && rotatedTransform.scale > transform.scale) return rotatedTransform;
- return transform;
+function calcMaxHalfSize(a, halfAngle, r, ring) {
+ var q = a + 1 / (2 * Math.tan(halfAngle));
+ return r * Math.min(
+ 1 / (Math.sqrt(q * q + 0.5) + q),
+ ring / (Math.sqrt(a * a + ring / 2) + a)
+ );
}
function getInscribedRadiusFraction(pt, cd0) {
@@ -921,6 +1019,7 @@ function groupScale(cdModule, scaleGroups) {
function setCoords(cd) {
var cd0 = cd[0];
+ var r = cd0.r;
var trace = cd0.trace;
var currentAngle = trace.rotation * Math.PI / 180;
var angleFactor = 2 * Math.PI / cd0.vTotal;
@@ -941,11 +1040,7 @@ function setCoords(cd) {
lastPt = 'px0';
}
- function getCoords(angle) {
- return [cd0.r * Math.sin(angle), -cd0.r * Math.cos(angle)];
- }
-
- currentCoords = getCoords(currentAngle);
+ currentCoords = getCoords(r, currentAngle);
for(i = 0; i < cd.length; i++) {
cdi = cd[i];
@@ -953,12 +1048,13 @@ function setCoords(cd) {
cdi[firstPt] = currentCoords;
+ cdi.startangle = currentAngle;
currentAngle += angleFactor * cdi.v / 2;
- cdi.pxmid = getCoords(currentAngle);
+ cdi.pxmid = getCoords(r, currentAngle);
cdi.midangle = currentAngle;
-
currentAngle += angleFactor * cdi.v / 2;
- currentCoords = getCoords(currentAngle);
+ currentCoords = getCoords(r, currentAngle);
+ cdi.stopangle = currentAngle;
cdi[lastPt] = currentCoords;
@@ -970,6 +1066,10 @@ function setCoords(cd) {
}
}
+function getCoords(r, angle) {
+ return [r * Math.sin(angle), -r * Math.cos(angle)];
+}
+
function formatSliceLabel(gd, pt, cd0) {
var fullLayout = gd._fullLayout;
var trace = cd0.trace;
@@ -1024,6 +1124,25 @@ function formatSliceLabel(gd, pt, cd0) {
}
}
}
+
+function computeTransform(
+ transform, // inout
+ textBB // in
+) {
+ var rotate = transform.rotate;
+ var scale = transform.scale;
+ if(scale > 1) scale = 1;
+
+ var a = rotate * Math.PI / 180;
+ var cosA = Math.cos(a);
+ var sinA = Math.sin(a);
+ var midX = (textBB.left + textBB.right) / 2;
+ var midY = (textBB.top + textBB.bottom) / 2;
+ transform.textX = midX * cosA - midY * sinA;
+ transform.textY = midX * sinA + midY * cosA;
+ transform.noCenter = true;
+}
+
module.exports = {
plot: plot,
formatSliceLabel: formatSliceLabel,
@@ -1033,4 +1152,5 @@ module.exports = {
prerenderTitles: prerenderTitles,
layoutAreas: layoutAreas,
attachFxHandlers: attachFxHandlers,
+ computeTransform: computeTransform
};
diff --git a/src/traces/pie/style.js b/src/traces/pie/style.js
index 86b1e46cc0b..c2261b49f96 100644
--- a/src/traces/pie/style.js
+++ b/src/traces/pie/style.js
@@ -11,9 +11,13 @@
var d3 = require('d3');
var styleOne = require('./style_one');
+var resizeText = require('../bar/style').resizeText;
module.exports = function style(gd) {
- gd._fullLayout._pielayer.selectAll('.trace').each(function(cd) {
+ var s = gd._fullLayout._pielayer.selectAll('.trace');
+ resizeText(gd, s, 'pie');
+
+ s.each(function(cd) {
var cd0 = cd[0];
var trace = cd0.trace;
var traceSelection = d3.select(this);
diff --git a/src/traces/sunburst/attributes.js b/src/traces/sunburst/attributes.js
index adb667df11b..6209ff5d64c 100644
--- a/src/traces/sunburst/attributes.js
+++ b/src/traces/sunburst/attributes.js
@@ -192,6 +192,7 @@ module.exports = {
}),
textfont: pieAttrs.textfont,
+ insidetextorientation: pieAttrs.insidetextorientation,
insidetextfont: pieAttrs.insidetextfont,
outsidetextfont: pieAttrs.outsidetextfont,
diff --git a/src/traces/sunburst/defaults.js b/src/traces/sunburst/defaults.js
index 43fb7766675..7ab81f3b1c9 100644
--- a/src/traces/sunburst/defaults.js
+++ b/src/traces/sunburst/defaults.js
@@ -68,6 +68,8 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
moduleHasInsideanchor: false
});
+ coerce('insidetextorientation');
+
handleDomainDefaults(traceOut, layout, coerce);
// do not support transforms for now
diff --git a/src/traces/sunburst/plot.js b/src/traces/sunburst/plot.js
index db61cb8d8ed..67e7b283f40 100644
--- a/src/traces/sunburst/plot.js
+++ b/src/traces/sunburst/plot.js
@@ -14,8 +14,10 @@ var d3Hierarchy = require('d3-hierarchy');
var Drawing = require('../../components/drawing');
var Lib = require('../../lib');
var svgTextUtils = require('../../lib/svg_text_utils');
-
-var transformInsideText = require('../pie/plot').transformInsideText;
+var recordMinTextSize = require('../bar/plot').recordMinTextSize;
+var piePlot = require('../pie/plot');
+var computeTransform = piePlot.computeTransform;
+var transformInsideText = piePlot.transformInsideText;
var styleOne = require('./style').styleOne;
var attachFxHandlers = require('./fx');
@@ -148,8 +150,9 @@ function plotOne(gd, cd, element, transitionOpts) {
// slice path generation fn
var pathSlice = function(d) { return Lib.pathAnnulus(d.rpx0, d.rpx1, d.x0, d.x1, cx, cy); };
// slice text translate x/y
- var transTextX = function(d) { return cx + d.pxmid[0] * d.transform.rCenter + (d.transform.x || 0); };
- var transTextY = function(d) { return cy + d.pxmid[1] * d.transform.rCenter + (d.transform.y || 0); };
+
+ var getTargetX = function(d) { return cx + getTextXY(d)[0] * (d.transform.rCenter || 0) + (d.transform.x || 0); };
+ var getTargetY = function(d) { return cy + getTextXY(d)[1] * (d.transform.rCenter || 0) + (d.transform.y || 0); };
slices = slices.data(sliceData, helpers.getPtId);
@@ -214,6 +217,8 @@ function plotOne(gd, cd, element, transitionOpts) {
pt.xmid = (pt.x0 + pt.x1) / 2;
pt.pxmid = rx2px(pt.rpx1, pt.xmid);
pt.midangle = -(pt.xmid - Math.PI / 2);
+ pt.startangle = -(pt.x0 - Math.PI / 2);
+ pt.stopangle = -(pt.x1 - Math.PI / 2);
pt.halfangle = 0.5 * Math.min(Lib.angleDelta(pt.x0, pt.x1) || Math.PI, Math.PI);
pt.ring = 1 - (pt.rpx0 / pt.rpx1);
pt.rInscribed = getInscribedRadiusFraction(pt, trace);
@@ -248,26 +253,28 @@ function plotOne(gd, cd, element, transitionOpts) {
s.attr('data-notex', 1);
});
+ var font = Lib.ensureUniformFontSize(gd, helpers.determineTextFont(trace, pt, fullLayout.font));
+
sliceText.text(exports.formatSliceLabel(pt, entry, trace, cd, fullLayout))
.classed('slicetext', true)
.attr('text-anchor', 'middle')
- .call(Drawing.font, helpers.determineTextFont(trace, pt, fullLayout.font))
+ .call(Drawing.font, font)
.call(svgTextUtils.convertToTspans, gd);
// position the text relative to the slice
var textBB = Drawing.bBox(sliceText.node());
pt.transform = transformInsideText(textBB, pt, cd0);
- pt.translateX = transTextX(pt);
- pt.translateY = transTextY(pt);
+ pt.transform.targetX = getTargetX(pt);
+ pt.transform.targetY = getTargetY(pt);
var strTransform = function(d, textBB) {
- return 'translate(' + d.translateX + ',' + d.translateY + ')' +
- (d.transform.scale < 1 ? ('scale(' + d.transform.scale + ')') : '') +
- (d.transform.rotate ? ('rotate(' + d.transform.rotate + ')') : '') +
- 'translate(' +
- (-(textBB.left + textBB.right) / 2) + ',' +
- (-(textBB.top + textBB.bottom) / 2) +
- ')';
+ var transform = d.transform;
+ computeTransform(transform, textBB);
+
+ transform.fontSize = font.size;
+ recordMinTextSize(trace.type, transform, fullLayout);
+
+ return Lib.getTextTransform(transform);
};
if(hasTransition) {
@@ -370,6 +377,7 @@ function plotOne(gd, cd, element, transitionOpts) {
prev = {
rpx1: pt.rpx1,
transform: {
+ textPosAngle: transform.textPosAngle,
scale: 0,
rotate: transform.rotate,
rCenter: transform.rCenter,
@@ -402,6 +410,7 @@ function plotOne(gd, cd, element, transitionOpts) {
}
}
+ var textPosAngleFn = d3.interpolate(prev.transform.textPosAngle, pt.transform.textPosAngle);
var rpx1Fn = d3.interpolate(prev.rpx1, pt.rpx1);
var x0Fn = d3.interpolate(prev.x0, pt.x0);
var x1Fn = d3.interpolate(prev.x1, pt.x1);
@@ -421,28 +430,30 @@ function plotOne(gd, cd, element, transitionOpts) {
var x0 = x0Fn(t);
var x1 = x1Fn(t);
var rCenter = rCenterFn(t);
+ var pxmid = rx2px(rpx1, (x0 + x1) / 2);
+ var textPosAngle = textPosAngleFn(t);
var d = {
- pxmid: rx2px(rpx1, (x0 + x1) / 2),
+ pxmid: pxmid,
+ rpx1: rpx1,
transform: {
+ textPosAngle: textPosAngle,
rCenter: rCenter,
x: transform.x,
y: transform.y
}
};
- var out = {
- rpx1: rpx1Fn(t),
- translateX: transTextX(d),
- translateY: transTextY(d),
+ recordMinTextSize(trace.type, transform, fullLayout);
+ return {
transform: {
+ targetX: getTargetX(d),
+ targetY: getTargetY(d),
scale: scaleFn(t),
rotate: rotateFn(t),
rCenter: rCenter
}
};
-
- return out;
};
}
@@ -600,3 +611,11 @@ function getInscribedRadiusFraction(pt) {
));
}
}
+
+function getTextXY(d) {
+ return getCoords(d.rpx1, d.transform.textPosAngle);
+}
+
+function getCoords(r, angle) {
+ return [r * Math.sin(angle), -r * Math.cos(angle)];
+}
diff --git a/src/traces/sunburst/style.js b/src/traces/sunburst/style.js
index 687ae7e3be0..8c8ebba53b4 100644
--- a/src/traces/sunburst/style.js
+++ b/src/traces/sunburst/style.js
@@ -11,9 +11,13 @@
var d3 = require('d3');
var Color = require('../../components/color');
var Lib = require('../../lib');
+var resizeText = require('../bar/style').resizeText;
function style(gd) {
- gd._fullLayout._sunburstlayer.selectAll('.trace').each(function(cd) {
+ var s = gd._fullLayout._sunburstlayer.selectAll('.trace');
+ resizeText(gd, s, 'sunburst');
+
+ s.each(function(cd) {
var gTrace = d3.select(this);
var cd0 = cd[0];
var trace = cd0.trace;
diff --git a/src/traces/treemap/draw_ancestors.js b/src/traces/treemap/draw_ancestors.js
index 776a4ccf01a..d209f657953 100644
--- a/src/traces/treemap/draw_ancestors.js
+++ b/src/traces/treemap/draw_ancestors.js
@@ -141,10 +141,12 @@ module.exports = function drawAncestors(gd, cd, entry, slices, opts) {
s.attr('data-notex', 1);
});
+ var font = Lib.ensureUniformFontSize(gd, helpers.determineTextFont(trace, pt, fullLayout.font, trace.pathdir));
+
sliceText.text(pt._text || ' ') // use one space character instead of a blank string to avoid jumps during transition
.classed('slicetext', true)
.attr('text-anchor', 'start')
- .call(Drawing.font, helpers.determineTextFont(trace, pt, fullLayout.font, trace.pathdir))
+ .call(Drawing.font, font)
.call(svgTextUtils.convertToTspans, gd);
pt.textBB = Drawing.bBox(sliceText.node());
@@ -152,12 +154,17 @@ module.exports = function drawAncestors(gd, cd, entry, slices, opts) {
onPathbar: true
});
+ pt.transform.fontSize = font.size;
+
if(helpers.isOutsideText(trace, pt)) {
// consider in/out diff font sizes
- pt.transform.targetY -= (
- helpers.getOutsideTextFontKey('size', trace, pt, fullLayout.font) -
- helpers.getInsideTextFontKey('size', trace, pt, fullLayout.font)
- );
+ var outsideFont = helpers.getOutsideTextFontKey('size', trace, pt, fullLayout.font);
+ var insideFont = helpers.getInsideTextFontKey('size', trace, pt, fullLayout.font);
+
+ var diffFontSize = outsideFont - insideFont;
+
+ pt.transform.targetY -= diffFontSize;
+ pt.transform.fontSize -= diffFontSize;
}
if(hasTransition) {
diff --git a/src/traces/treemap/draw_descendants.js b/src/traces/treemap/draw_descendants.js
index 8990ed0ce00..c8a345ec525 100644
--- a/src/traces/treemap/draw_descendants.js
+++ b/src/traces/treemap/draw_descendants.js
@@ -182,16 +182,19 @@ module.exports = function drawDescendants(gd, cd, entry, slices, opts) {
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 || isHeader) ? 'start' : 'middle')
- .call(Drawing.font, helpers.determineTextFont(trace, pt, fullLayout.font))
+ .call(Drawing.font, font)
.call(svgTextUtils.convertToTspans, gd);
pt.textBB = Drawing.bBox(sliceText.node());
pt.transform = toMoveInsideSlice(pt, {
isHeader: isHeader
});
+ pt.transform.fontSize = font.size;
if(hasTransition) {
sliceText.transition().attrTween('transform', function(pt2) {
diff --git a/src/traces/treemap/plot.js b/src/traces/treemap/plot.js
index f6fadac0891..2782d82d04d 100644
--- a/src/traces/treemap/plot.js
+++ b/src/traces/treemap/plot.js
@@ -15,7 +15,9 @@ var helpers = require('../sunburst/helpers');
var Lib = require('../../lib');
var TEXTPAD = require('../bar/constants').TEXTPAD;
-var toMoveInsideBar = require('../bar/plot').toMoveInsideBar;
+var barPlot = require('../bar/plot');
+var toMoveInsideBar = barPlot.toMoveInsideBar;
+var recordMinTextSize = barPlot.recordMinTextSize;
var constants = require('./constants');
var drawDescendants = require('./draw_descendants');
@@ -365,6 +367,7 @@ function plotOne(gd, cd, element, transitionOpts) {
return {};
}
+ recordMinTextSize(trace.type, transform, fullLayout);
return {
scale: transform.scale,
rotate: transform.rotate,
@@ -481,14 +484,16 @@ function plotOne(gd, cd, element, transitionOpts) {
}
}
+ var transform = pt.transform;
+ recordMinTextSize(trace.type, transform, fullLayout);
return d3.interpolate(prev, {
transform: {
- scale: pt.transform.scale,
- rotate: pt.transform.rotate,
- textX: pt.transform.textX,
- textY: pt.transform.textY,
- targetX: pt.transform.targetX,
- targetY: pt.transform.targetY
+ scale: transform.scale,
+ rotate: transform.rotate,
+ textX: transform.textX,
+ textY: transform.textY,
+ targetX: transform.targetX,
+ targetY: transform.targetY
}
});
};
@@ -518,13 +523,16 @@ function plotOne(gd, cd, element, transitionOpts) {
};
var strTransform = function(d) {
+ var transform = d.transform;
+ recordMinTextSize(trace.type, transform, fullLayout);
+
return Lib.getTextTransform({
- textX: d.transform.textX,
- textY: d.transform.textY,
- targetX: d.transform.targetX,
- targetY: d.transform.targetY,
- scale: d.transform.scale,
- rotate: d.transform.rotate
+ textX: transform.textX,
+ textY: transform.textY,
+ targetX: transform.targetX,
+ targetY: transform.targetY,
+ scale: transform.scale,
+ rotate: transform.rotate
});
};
diff --git a/src/traces/treemap/style.js b/src/traces/treemap/style.js
index 9ca251a4c4f..daf41cd1c24 100644
--- a/src/traces/treemap/style.js
+++ b/src/traces/treemap/style.js
@@ -12,9 +12,13 @@ var d3 = require('d3');
var Color = require('../../components/color');
var Lib = require('../../lib');
var helpers = require('../sunburst/helpers');
+var resizeText = require('../bar/style').resizeText;
function style(gd) {
- gd._fullLayout._treemaplayer.selectAll('.trace').each(function(cd) {
+ var s = gd._fullLayout._treemaplayer.selectAll('.trace');
+ resizeText(gd, s, 'treemap');
+
+ s.each(function(cd) {
var gTrace = d3.select(this);
var cd0 = cd[0];
var trace = cd0.trace;
diff --git a/src/traces/waterfall/style.js b/src/traces/waterfall/style.js
index 8026fb28176..aaf72693f29 100644
--- a/src/traces/waterfall/style.js
+++ b/src/traces/waterfall/style.js
@@ -13,11 +13,13 @@ var d3 = require('d3');
var Drawing = require('../../components/drawing');
var Color = require('../../components/color');
var DESELECTDIM = require('../../constants/interactions').DESELECTDIM;
-
-var styleTextPoints = require('../bar/style').styleTextPoints;
+var barStyle = require('../bar/style');
+var resizeText = barStyle.resizeText;
+var styleTextPoints = barStyle.styleTextPoints;
function style(gd, cd, sel) {
var s = sel ? sel : d3.select(gd).selectAll('g.waterfalllayer').selectAll('g.trace');
+ resizeText(gd, s, 'waterfall');
s.style('opacity', function(d) { return d[0].trace.opacity; });
diff --git a/test/image/baselines/pie_inside-text-orientation.png b/test/image/baselines/pie_inside-text-orientation.png
new file mode 100644
index 00000000000..64af61e6e86
Binary files /dev/null and b/test/image/baselines/pie_inside-text-orientation.png differ
diff --git a/test/image/baselines/sunburst_inside-text-orientation.png b/test/image/baselines/sunburst_inside-text-orientation.png
new file mode 100644
index 00000000000..a77ba3102ea
Binary files /dev/null and b/test/image/baselines/sunburst_inside-text-orientation.png differ
diff --git a/test/image/baselines/sunburst_inside-text-orientation_clock.png b/test/image/baselines/sunburst_inside-text-orientation_clock.png
new file mode 100644
index 00000000000..28ab975ef98
Binary files /dev/null and b/test/image/baselines/sunburst_inside-text-orientation_clock.png differ
diff --git a/test/image/baselines/sunburst_with-without_values.png b/test/image/baselines/sunburst_with-without_values.png
index 80263d41d03..a00afc950f4 100644
Binary files a/test/image/baselines/sunburst_with-without_values.png and b/test/image/baselines/sunburst_with-without_values.png differ
diff --git a/test/image/baselines/uniformtext_bar-like_10_auto.png b/test/image/baselines/uniformtext_bar-like_10_auto.png
new file mode 100644
index 00000000000..b3af71c55e4
Binary files /dev/null and b/test/image/baselines/uniformtext_bar-like_10_auto.png differ
diff --git a/test/image/baselines/uniformtext_bar-like_8_horizontal.png b/test/image/baselines/uniformtext_bar-like_8_horizontal.png
new file mode 100644
index 00000000000..9066539beed
Binary files /dev/null and b/test/image/baselines/uniformtext_bar-like_8_horizontal.png differ
diff --git a/test/image/baselines/uniformtext_bar-like_8_textangle.png b/test/image/baselines/uniformtext_bar-like_8_textangle.png
new file mode 100644
index 00000000000..70382642b5a
Binary files /dev/null and b/test/image/baselines/uniformtext_bar-like_8_textangle.png differ
diff --git a/test/image/baselines/uniformtext_bar_edgecase1.png b/test/image/baselines/uniformtext_bar_edgecase1.png
new file mode 100644
index 00000000000..68554e660fa
Binary files /dev/null and b/test/image/baselines/uniformtext_bar_edgecase1.png differ
diff --git a/test/image/baselines/uniformtext_bar_edgecase2.png b/test/image/baselines/uniformtext_bar_edgecase2.png
new file mode 100644
index 00000000000..9fed91ea3c3
Binary files /dev/null and b/test/image/baselines/uniformtext_bar_edgecase2.png differ
diff --git a/test/image/baselines/uniformtext_bar_edgecase3.png b/test/image/baselines/uniformtext_bar_edgecase3.png
new file mode 100644
index 00000000000..d71ced1c2ca
Binary files /dev/null and b/test/image/baselines/uniformtext_bar_edgecase3.png differ
diff --git a/test/image/baselines/uniformtext_funnelarea.png b/test/image/baselines/uniformtext_funnelarea.png
new file mode 100644
index 00000000000..c5a3fc27dc4
Binary files /dev/null and b/test/image/baselines/uniformtext_funnelarea.png differ
diff --git a/test/image/baselines/uniformtext_pie_16_auto.png b/test/image/baselines/uniformtext_pie_16_auto.png
new file mode 100644
index 00000000000..22277b46af9
Binary files /dev/null and b/test/image/baselines/uniformtext_pie_16_auto.png differ
diff --git a/test/image/baselines/uniformtext_pie_8_horizontal.png b/test/image/baselines/uniformtext_pie_8_horizontal.png
new file mode 100644
index 00000000000..8847a079d5f
Binary files /dev/null and b/test/image/baselines/uniformtext_pie_8_horizontal.png differ
diff --git a/test/image/baselines/uniformtext_pie_8_horizontal_center.png b/test/image/baselines/uniformtext_pie_8_horizontal_center.png
new file mode 100644
index 00000000000..41e1e7af0ca
Binary files /dev/null and b/test/image/baselines/uniformtext_pie_8_horizontal_center.png differ
diff --git a/test/image/baselines/uniformtext_pie_8_radial.png b/test/image/baselines/uniformtext_pie_8_radial.png
new file mode 100644
index 00000000000..67ab4369d93
Binary files /dev/null and b/test/image/baselines/uniformtext_pie_8_radial.png differ
diff --git a/test/image/baselines/uniformtext_pie_8_tangential.png b/test/image/baselines/uniformtext_pie_8_tangential.png
new file mode 100644
index 00000000000..d8b2075be3b
Binary files /dev/null and b/test/image/baselines/uniformtext_pie_8_tangential.png differ
diff --git a/test/image/baselines/uniformtext_pie_inside-text-orientation.png b/test/image/baselines/uniformtext_pie_inside-text-orientation.png
new file mode 100644
index 00000000000..a4b138c519e
Binary files /dev/null and b/test/image/baselines/uniformtext_pie_inside-text-orientation.png differ
diff --git a/test/image/baselines/uniformtext_pie_outside.png b/test/image/baselines/uniformtext_pie_outside.png
new file mode 100644
index 00000000000..b142a904a1d
Binary files /dev/null and b/test/image/baselines/uniformtext_pie_outside.png differ
diff --git a/test/image/baselines/uniformtext_pie_pull.png b/test/image/baselines/uniformtext_pie_pull.png
new file mode 100644
index 00000000000..18073572235
Binary files /dev/null and b/test/image/baselines/uniformtext_pie_pull.png differ
diff --git a/test/image/baselines/uniformtext_sunburst_inside-text-orientation.png b/test/image/baselines/uniformtext_sunburst_inside-text-orientation.png
new file mode 100644
index 00000000000..fa4725ab038
Binary files /dev/null and b/test/image/baselines/uniformtext_sunburst_inside-text-orientation.png differ
diff --git a/test/image/baselines/uniformtext_sunburst_treemap.png b/test/image/baselines/uniformtext_sunburst_treemap.png
new file mode 100644
index 00000000000..eb00334ba3e
Binary files /dev/null and b/test/image/baselines/uniformtext_sunburst_treemap.png differ
diff --git a/test/image/mocks/pie_inside-text-orientation.json b/test/image/mocks/pie_inside-text-orientation.json
new file mode 100644
index 00000000000..e34af7b7fbc
--- /dev/null
+++ b/test/image/mocks/pie_inside-text-orientation.json
@@ -0,0 +1,371 @@
+{
+ "data": [
+ {
+ "name": "horizontal",
+ "title": {
+ "text": "horizontal",
+ "font": {
+ "size": 20
+ }
+ },
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "type": "pie",
+ "hole": 0.5,
+ "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"
+ ],
+ "marker": {
+ "colors": [
+ "aquamarine",
+ "brown",
+ "chocolate",
+ "darkblue",
+ "darkgreen",
+ "forestgreen",
+ "gold",
+ "honeydew",
+ "indigo",
+ "navajowhite",
+ "khaki",
+ "lightblue",
+ "magenta",
+ "navy",
+ "orange",
+ "pink",
+ "aqua",
+ "red",
+ "silver",
+ "tomato",
+ "turquoise",
+ "violet",
+ "wheat",
+ "yellow",
+ "azure"
+ ]
+ },
+ "values": [
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0.52,
+ 1
+ ],
+ "y": [
+ 0.52,
+ 1
+ ]
+ }
+ },
+ {
+ "name": "radial",
+ "title": {
+ "text": "radial",
+ "font": {
+ "size": 20
+ }
+ },
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "type": "pie",
+ "hole": 0.5,
+ "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"
+ ],
+ "values": [
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0.52,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.48
+ ]
+ }
+ },
+ {
+ "name": "tangential",
+ "title": {
+ "text": "tangential",
+ "font": {
+ "size": 20
+ }
+ },
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "type": "pie",
+ "hole": 0.5,
+ "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"
+ ],
+ "values": [
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.48
+ ],
+ "y": [
+ 0,
+ 0.48
+ ]
+ }
+ },
+ {
+ "name": "auto",
+ "title": {
+ "text": "auto",
+ "font": {
+ "size": 20
+ }
+ },
+ "insidetextorientation": "auto",
+ "textposition": "inside",
+ "type": "pie",
+ "hole": 0.5,
+ "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"
+ ],
+ "values": [
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.48
+ ],
+ "y": [
+ 0.52,
+ 1
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 925,
+ "height": 750,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "legend": {
+ "title": {
+ "text": "inside text orientation"
+ }
+ },
+ "font": {
+ "size": 14
+ }
+ }
+}
diff --git a/test/image/mocks/sunburst_inside-text-orientation.json b/test/image/mocks/sunburst_inside-text-orientation.json
new file mode 100644
index 00000000000..ad1680a2ca9
--- /dev/null
+++ b/test/image/mocks/sunburst_inside-text-orientation.json
@@ -0,0 +1,430 @@
+{
+ "data": [
+ {
+ "name": "horizontal",
+ "insidetextorientation": "horizontal",
+ "type": "sunburst",
+ "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"
+ ],
+ "marker": {
+ "colors": [
+ "aquamarine",
+ "brown",
+ "chocolate",
+ "darkblue",
+ "darkgreen",
+ "forestgreen",
+ "gold",
+ "honeydew",
+ "indigo",
+ "navajowhite",
+ "khaki",
+ "lightblue",
+ "magenta",
+ "navy",
+ "orange",
+ "pink",
+ "aqua",
+ "red",
+ "silver",
+ "tomato",
+ "turquoise",
+ "violet",
+ "wheat",
+ "yellow",
+ "azure"
+ ]
+ },
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0.5,
+ 1
+ ]
+ }
+ },
+ {
+ "name": "radial",
+ "insidetextorientation": "radial",
+ "type": "sunburst",
+ "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+value",
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.5
+ ]
+ }
+ },
+ {
+ "name": "tangential",
+ "insidetextorientation": "tangential",
+ "type": "sunburst",
+ "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+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0,
+ 0.5
+ ]
+ }
+ },
+ {
+ "name": "auto",
+ "insidetextorientation": "auto",
+ "type": "sunburst",
+ "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+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0.5,
+ 1
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 800,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "font": {
+ "size": 14
+ },
+ "shapes": [
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0,
+ "x1": 0.5,
+ "y0": 0,
+ "y1": 0.5
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.5,
+ "x1": 1,
+ "y0": 0,
+ "y1": 0.5
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.5,
+ "x1": 1,
+ "y0": 0.5,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0,
+ "x1": 0.5,
+ "y0": 0.5,
+ "y1": 1
+ }
+ ],
+ "annotations": [
+ {
+ "text": "auto",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 0.5,
+ "y": 0.5,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "tangential",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 0.5,
+ "y": 0,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "radial",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 1,
+ "y": 0,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "horizontal",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 1,
+ "y": 0.5,
+ "font": {
+ "size": 20
+ }
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/sunburst_inside-text-orientation_clock.json b/test/image/mocks/sunburst_inside-text-orientation_clock.json
new file mode 100644
index 00000000000..a6d7312bbee
--- /dev/null
+++ b/test/image/mocks/sunburst_inside-text-orientation_clock.json
@@ -0,0 +1,397 @@
+{
+ "data": [
+ {
+ "name": "horizontal",
+ "insidetextorientation": "horizontal",
+ "type": "sunburst",
+ "parents": [
+ "",
+ "A",
+ "A",
+ "C",
+ "C",
+ "C",
+ "F",
+ "F",
+ "F",
+ "F",
+ "J",
+ "J",
+ "J",
+ "J",
+ "J",
+ "O",
+ "O",
+ "O",
+ "O",
+ "O",
+ "O",
+ "U",
+ "U",
+ "U",
+ "U",
+ "U",
+ "U"
+ ],
+ "labels": [
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z"
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0.5,
+ 1
+ ]
+ }
+ },
+ {
+ "name": "radial",
+ "insidetextorientation": "radial",
+ "type": "sunburst",
+ "parents": [
+ "",
+ "A",
+ "A",
+ "C",
+ "C",
+ "C",
+ "F",
+ "F",
+ "F",
+ "F",
+ "J",
+ "J",
+ "J",
+ "J",
+ "J",
+ "O",
+ "O",
+ "O",
+ "O",
+ "O",
+ "O",
+ "U",
+ "U",
+ "U",
+ "U",
+ "U",
+ "U"
+ ],
+ "labels": [
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z"
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.5
+ ]
+ }
+ },
+ {
+ "name": "tangential",
+ "insidetextorientation": "tangential",
+ "type": "sunburst",
+ "parents": [
+ "",
+ "A",
+ "A",
+ "C",
+ "C",
+ "C",
+ "F",
+ "F",
+ "F",
+ "F",
+ "J",
+ "J",
+ "J",
+ "J",
+ "J",
+ "O",
+ "O",
+ "O",
+ "O",
+ "O",
+ "O",
+ "U",
+ "U",
+ "U",
+ "U",
+ "U",
+ "U"
+ ],
+ "labels": [
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z"
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0,
+ 0.5
+ ]
+ }
+ },
+ {
+ "name": "auto",
+ "insidetextorientation": "auto",
+ "type": "sunburst",
+ "parents": [
+ "",
+ "A",
+ "A",
+ "C",
+ "C",
+ "C",
+ "F",
+ "F",
+ "F",
+ "F",
+ "J",
+ "J",
+ "J",
+ "J",
+ "J",
+ "O",
+ "O",
+ "O",
+ "O",
+ "O",
+ "O",
+ "U",
+ "U",
+ "U",
+ "U",
+ "U",
+ "U"
+ ],
+ "labels": [
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z"
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0.5,
+ 1
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 800,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "font": {
+ "size": 14
+ },
+ "shapes": [
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0,
+ "x1": 0.5,
+ "y0": 0,
+ "y1": 0.5
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.5,
+ "x1": 1,
+ "y0": 0,
+ "y1": 0.5
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.5,
+ "x1": 1,
+ "y0": 0.5,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0,
+ "x1": 0.5,
+ "y0": 0.5,
+ "y1": 1
+ }
+ ],
+ "annotations": [
+ {
+ "text": "auto",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 0.5,
+ "y": 0.5,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "tangential",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 0.5,
+ "y": 0,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "radial",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 1,
+ "y": 0,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "horizontal",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 1,
+ "y": 0.5,
+ "font": {
+ "size": 20
+ }
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/uniformtext_bar-like_10_auto.json b/test/image/mocks/uniformtext_bar-like_10_auto.json
new file mode 100644
index 00000000000..8a959962d28
--- /dev/null
+++ b/test/image/mocks/uniformtext_bar-like_10_auto.json
@@ -0,0 +1,207 @@
+{
+ "data": [
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textinfo": "text",
+ "textposition": "auto",
+ "xaxis": "x1",
+ "yaxis": "y1"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "visible"
+ ],
+ "type": "bar",
+ "textinfo": "text",
+ "textposition": "auto",
+ "xaxis": "x1",
+ "yaxis": "y1"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textinfo": "text",
+ "textposition": "inside",
+ "xaxis": "x2",
+ "yaxis": "y2"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textinfo": "text",
+ "textposition": "inside",
+ "xaxis": "x2",
+ "yaxis": "y2"
+ },
+ {
+ "y": [
+ 5,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "funnel",
+ "textinfo": "text",
+ "textposition": "inside",
+ "xaxis": "x3",
+ "yaxis": "y3"
+ },
+ {
+ "y": [
+ 3,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "funnel",
+ "textinfo": "text",
+ "textposition": "inside",
+ "xaxis": "x3",
+ "yaxis": "y3"
+ },
+ {
+ "y": [
+ 4,
+ -0.5,
+ 0.25,
+ null
+ ],
+ "measure": [
+ "",
+ "",
+ "",
+ "total"
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible",
+ "very
L
O
N
G
text"
+ ],
+ "type": "waterfall",
+ "textinfo": "text",
+ "textposition": "inside",
+ "insidetextanchor": "middle",
+ "xaxis": "x4",
+ "yaxis": "y4"
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "margin": {
+ "t": 20,
+ "b": 20,
+ "l": 20,
+ "r": 20
+ },
+ "legend": {
+ "orientation": "h",
+ "title": {
+ "text": "bar, funnel & waterfall uniform text
with auto orientation
minsize=8"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 10
+ },
+ "barmode": "stack",
+ "xaxis": {
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "xaxis2": {
+ "anchor": "y2",
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "xaxis3": {
+ "anchor": "y3",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "xaxis4": {
+ "anchor": "y4",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis": {
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "yaxis2": {
+ "anchor": "x2",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis3": {
+ "anchor": "x3",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis4": {
+ "anchor": "x4",
+ "domain": [
+ 0,
+ 0.48
+ ]
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_bar-like_8_horizontal.json b/test/image/mocks/uniformtext_bar-like_8_horizontal.json
new file mode 100644
index 00000000000..b791df0d8a8
--- /dev/null
+++ b/test/image/mocks/uniformtext_bar-like_8_horizontal.json
@@ -0,0 +1,214 @@
+{
+ "data": [
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textangle": 0,
+ "textinfo": "text",
+ "textposition": "auto",
+ "xaxis": "x1",
+ "yaxis": "y1"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "visible"
+ ],
+ "type": "bar",
+ "textangle": 0,
+ "textinfo": "text",
+ "textposition": "auto",
+ "xaxis": "x1",
+ "yaxis": "y1"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textangle": 0,
+ "textinfo": "text",
+ "textposition": "inside",
+ "xaxis": "x2",
+ "yaxis": "y2"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textangle": 0,
+ "textinfo": "text",
+ "textposition": "inside",
+ "xaxis": "x2",
+ "yaxis": "y2"
+ },
+ {
+ "y": [
+ 5,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "funnel",
+ "textangle": 0,
+ "textinfo": "text",
+ "textposition": "inside",
+ "xaxis": "x3",
+ "yaxis": "y3"
+ },
+ {
+ "y": [
+ 3,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "funnel",
+ "textangle": 0,
+ "textinfo": "text",
+ "textposition": "inside",
+ "xaxis": "x3",
+ "yaxis": "y3"
+ },
+ {
+ "y": [
+ 4,
+ -0.5,
+ 0.25,
+ null
+ ],
+ "measure": [
+ "",
+ "",
+ "",
+ "total"
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible",
+ "very
L
O
N
G
text"
+ ],
+ "type": "waterfall",
+ "textangle": 0,
+ "textinfo": "text",
+ "textposition": "inside",
+ "insidetextanchor": "middle",
+ "xaxis": "x4",
+ "yaxis": "y4"
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "margin": {
+ "t": 20,
+ "b": 20,
+ "l": 20,
+ "r": 20
+ },
+ "legend": {
+ "orientation": "h",
+ "title": {
+ "text": "bar, funnel & waterfall uniform text
with horizontal orientation
minsize=8"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 8
+ },
+ "barmode": "stack",
+ "xaxis": {
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "xaxis2": {
+ "anchor": "y2",
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "xaxis3": {
+ "anchor": "y3",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "xaxis4": {
+ "anchor": "y4",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis": {
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "yaxis2": {
+ "anchor": "x2",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis3": {
+ "anchor": "x3",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis4": {
+ "anchor": "x4",
+ "domain": [
+ 0,
+ 0.48
+ ]
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_bar-like_8_textangle.json b/test/image/mocks/uniformtext_bar-like_8_textangle.json
new file mode 100644
index 00000000000..a66d1de8ff0
--- /dev/null
+++ b/test/image/mocks/uniformtext_bar-like_8_textangle.json
@@ -0,0 +1,214 @@
+{
+ "data": [
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textinfo": "text",
+ "textposition": "auto",
+ "textangle": -90,
+ "xaxis": "x1",
+ "yaxis": "y1"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "visible"
+ ],
+ "type": "bar",
+ "textinfo": "text",
+ "textposition": "auto",
+ "textangle": -90,
+ "xaxis": "x1",
+ "yaxis": "y1"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textinfo": "text",
+ "textposition": "inside",
+ "textangle": -90,
+ "xaxis": "x2",
+ "yaxis": "y2"
+ },
+ {
+ "y": [
+ 4,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "bar",
+ "textinfo": "text",
+ "textposition": "inside",
+ "textangle": -90,
+ "xaxis": "x2",
+ "yaxis": "y2"
+ },
+ {
+ "y": [
+ 5,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "funnel",
+ "textinfo": "text",
+ "textposition": "inside",
+ "textangle": -90,
+ "xaxis": "x3",
+ "yaxis": "y3"
+ },
+ {
+ "y": [
+ 3,
+ 1,
+ 0.5
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "funnel",
+ "textinfo": "text",
+ "textposition": "inside",
+ "textangle": -90,
+ "xaxis": "x3",
+ "yaxis": "y3"
+ },
+ {
+ "y": [
+ 4,
+ -0.5,
+ 0.25,
+ null
+ ],
+ "measure": [
+ "",
+ "",
+ "",
+ "total"
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible",
+ "very
L
O
N
G
text"
+ ],
+ "type": "waterfall",
+ "textinfo": "text",
+ "textposition": "inside",
+ "insidetextanchor": "middle",
+ "textangle": -90,
+ "xaxis": "x4",
+ "yaxis": "y4"
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "margin": {
+ "t": 20,
+ "b": 20,
+ "l": 20,
+ "r": 20
+ },
+ "legend": {
+ "orientation": "h",
+ "title": {
+ "text": "bar, funnel & waterfall uniform text
with vertical orientation
textangle:-90 | minsize=8"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 8
+ },
+ "barmode": "stack",
+ "xaxis": {
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "xaxis2": {
+ "anchor": "y2",
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "xaxis3": {
+ "anchor": "y3",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "xaxis4": {
+ "anchor": "y4",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis": {
+ "domain": [
+ 0,
+ 0.48
+ ]
+ },
+ "yaxis2": {
+ "anchor": "x2",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis3": {
+ "anchor": "x3",
+ "domain": [
+ 0.52,
+ 1
+ ]
+ },
+ "yaxis4": {
+ "anchor": "x4",
+ "domain": [
+ 0,
+ 0.48
+ ]
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_bar_edgecase1.json b/test/image/mocks/uniformtext_bar_edgecase1.json
new file mode 100644
index 00000000000..93326891cf9
--- /dev/null
+++ b/test/image/mocks/uniformtext_bar_edgecase1.json
@@ -0,0 +1,42 @@
+{
+ "data": [
+ {
+ "type": "bar",
+ "orientation": "h",
+ "x": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ],
+ "y": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ],
+ "text": [
+ "9",
+ "iiiii",
+ "i",
+ "d",
+ "50"
+ ],
+ "textposition": "auto",
+ "textangle": 0
+ }
+ ],
+ "layout": {
+ "height": 300,
+ "width": 500,
+ "font": {
+ "size": 24
+ },
+ "uniformtext": {
+ "minsize": 9,
+ "mode": "hide"
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_bar_edgecase2.json b/test/image/mocks/uniformtext_bar_edgecase2.json
new file mode 100644
index 00000000000..ce7ab047d71
--- /dev/null
+++ b/test/image/mocks/uniformtext_bar_edgecase2.json
@@ -0,0 +1,42 @@
+{
+ "data": [
+ {
+ "type": "bar",
+ "orientation": "v",
+ "x": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ],
+ "y": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5
+ ],
+ "text": [
+ "9",
+ "iiiiiii",
+ "i",
+ "d",
+ "50"
+ ],
+ "textposition": "inside",
+ "textangle": 0
+ }
+ ],
+ "layout": {
+ "height": 500,
+ "width": 300,
+ "font": {
+ "size": 24
+ },
+ "uniformtext": {
+ "minsize": 10,
+ "mode": "hide"
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_bar_edgecase3.json b/test/image/mocks/uniformtext_bar_edgecase3.json
new file mode 100644
index 00000000000..aaef5d09958
--- /dev/null
+++ b/test/image/mocks/uniformtext_bar_edgecase3.json
@@ -0,0 +1,34 @@
+{
+ "data": [
+ {
+ "type": "bar",
+ "y": [
+ 999,
+ 4999,
+ 8999,
+ 12999,
+ 16999,
+ 20999,
+ 24999,
+ 28999,
+ 32999,
+ 36999,
+ 40999,
+ 44999
+ ],
+ "texttemplate": "%{y}",
+ "textposition": "auto",
+ "textangle": 0,
+ "textfont": {
+ "size": 16
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 600,
+ "uniformtext": {
+ "mode": "hide"
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_funnelarea.json b/test/image/mocks/uniformtext_funnelarea.json
new file mode 100644
index 00000000000..a9dca96a617
--- /dev/null
+++ b/test/image/mocks/uniformtext_funnelarea.json
@@ -0,0 +1,159 @@
+{
+ "data": [
+ {
+ "baseratio": 0,
+ "aspectratio": 2,
+ "type": "funnelarea",
+ "values": [6, 5, 4, 3, 2, 1],
+ "textinfo": "value",
+ "title": {
+ "position": "top right",
+ "text": "1 - 6 no group"
+ },
+ "domain": {
+ "x": [0.7, 1],
+ "y": [0.7, 1]
+ }
+ },
+ {
+ "baseratio": 0.5,
+ "aspectratio": 2,
+ "type": "funnelarea",
+ "values": [16, 15, 14, 13, 12, 11],
+ "textinfo": "value",
+ "title": {
+ "position": "top right",
+ "text": "11 - 16 no group"
+ },
+ "domain": {
+ "x": [0.7, 1],
+ "y": [0.35, 0.65]
+ }
+ },
+ {
+ "baseratio": 1,
+ "aspectratio": 2,
+ "type": "funnelarea",
+ "values": [60, 50, 40, 30, 20, 10],
+ "textinfo": "value",
+ "title": {
+ "position": "top right",
+ "text": "10 - 60 no group"
+ },
+ "domain": {
+ "x": [0.7, 1],
+ "y": [0, 0.3]
+ }
+ },
+ {
+ "baseratio": 0,
+ "aspectratio": 1,
+ "type": "funnelarea",
+ "values": [6, 5, 4, 3, 2, 1],
+ "textinfo": "value",
+ "title": {
+ "position": "top center",
+ "text": "1 - 6 no group"
+ },
+ "domain": {
+ "x": [0.35, 0.65],
+ "y": [0.7, 1]
+ }
+ },
+ {
+ "baseratio": 0.5,
+ "aspectratio": 1,
+ "type": "funnelarea",
+ "title": {
+ "position": "top center",
+ "text": "11 - 16 no group"
+ },
+ "values": [16, 15, 14, 13, 12, 11],
+ "textinfo": "value",
+ "domain": {
+ "x": [0.35, 0.65],
+ "y": [0.35, 0.65]
+ }
+ },
+ {
+ "baseratio": 1,
+ "aspectratio": 1,
+ "type": "funnelarea",
+ "values": [60, 50, 40, 30, 20, 10],
+ "textinfo": "value",
+ "title": {
+ "position": "top center",
+ "text": "10 - 60 no group"
+ },
+ "domain": {
+ "x": [0.35, 0.65],
+ "y": [0, 0.3]
+ }
+ },
+ {
+ "baseratio": 0,
+ "aspectratio": 0.5,
+ "type": "funnelarea",
+ "values": [6, 5, 4, 3, 2, 1],
+ "textinfo": "value",
+ "title": {
+ "position": "top left",
+ "text": "1 - 6 no group"
+ },
+ "domain": {
+ "x": [0, 0.3],
+ "y": [0.7, 1]
+ }
+ },
+ {
+ "baseratio": 0.5,
+ "aspectratio": 0.5,
+ "type": "funnelarea",
+ "values": [16, 15, 14, 13, 12, 11],
+ "textinfo": "value",
+ "title": {
+ "position": "top left",
+ "text": "11 - 16 no group"
+ },
+ "domain": {
+ "x": [0, 0.3],
+ "y": [0.35, 0.65]
+ }
+ },
+ {
+ "baseratio": 1,
+ "aspectratio": 0.5,
+ "type": "funnelarea",
+ "values": [60, 50, 40, 30, 20, 10],
+ "textinfo": "value",
+ "title": {
+ "position": "top left",
+ "text": "10 - 60 no group"
+ },
+ "domain": {
+ "x": [0, 0.3],
+ "y": [0, 0.3]
+ }
+ }
+ ],
+ "layout": {
+ "hiddenlabels": ["1", "4"],
+ "width": 800,
+ "height": 800,
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": "16"
+ },
+ "shapes": [
+ { "type": "rect", "layer": "below", "x0": 0, "x1": 0.3, "y0": 0, "y1": 0.3 },
+ { "type": "rect", "layer": "below", "x0": 0, "x1": 0.3, "y0": 0.35, "y1": 0.65 },
+ { "type": "rect", "layer": "below", "x0": 0, "x1": 0.3, "y0": 0.7, "y1": 1 },
+ { "type": "rect", "layer": "below", "x0": 0.35, "x1": 0.65, "y0": 0, "y1": 0.3 },
+ { "type": "rect", "layer": "below", "x0": 0.35, "x1": 0.65, "y0": 0.35, "y1": 0.65 },
+ { "type": "rect", "layer": "below", "x0": 0.35, "x1": 0.65, "y0": 0.7, "y1": 1 },
+ { "type": "rect", "layer": "below", "x0": 0.7, "x1": 1, "y0": 0, "y1": 0.3 },
+ { "type": "rect", "layer": "below", "x0": 0.7, "x1": 1, "y0": 0.35, "y1": 0.65 },
+ { "type": "rect", "layer": "below", "x0": 0.7, "x1": 1, "y0": 0.7, "y1": 1 }
+ ]
+ }
+}
diff --git a/test/image/mocks/uniformtext_pie_16_auto.json b/test/image/mocks/uniformtext_pie_16_auto.json
new file mode 100644
index 00000000000..28dceeed95e
--- /dev/null
+++ b/test/image/mocks/uniformtext_pie_16_auto.json
@@ -0,0 +1,192 @@
+{
+ "data": [
+ {
+ "rotation": -360,
+ "values": [
+ 10,
+ 1,
+ 0.1
+ ],
+ "type": "pie",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0,
+ 0.24
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": -360,
+ "values": [
+ 10,
+ 1,
+ 0.1
+ ],
+ "type": "pie",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.25,
+ 0.49
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 10,
+ 1,
+ 0.1
+ ],
+ "type": "pie",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0,
+ 0.24
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 10,
+ 1,
+ 0.1
+ ],
+ "type": "pie",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.25,
+ 0.49
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 10,
+ 1,
+ 0.1
+ ],
+ "type": "pie",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.5,
+ 0.74
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 10,
+ 1,
+ 0.1
+ ],
+ "type": "pie",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.75,
+ 0.99
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 10,
+ 1,
+ 0.1
+ ],
+ "type": "pie",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.5,
+ 0.74
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 10,
+ 1,
+ 0.1
+ ],
+ "type": "pie",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.75,
+ 0.99
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "legend": {
+ "orientation": "h",
+ "title": {
+ "text": "pie uniform text
with auto orientation
minsize=16"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 16
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_pie_8_horizontal.json b/test/image/mocks/uniformtext_pie_8_horizontal.json
new file mode 100644
index 00000000000..20d952a2f30
--- /dev/null
+++ b/test/image/mocks/uniformtext_pie_8_horizontal.json
@@ -0,0 +1,240 @@
+{
+ "data": [
+ {
+ "rotation": -360,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0,
+ 0.24
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": -360,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.25,
+ 0.49
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0,
+ 0.24
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.25,
+ 0.49
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.5,
+ 0.74
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.75,
+ 0.99
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "very L O N G text",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.5,
+ 0.74
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "very
L
O
N
G
text",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.75,
+ 0.99
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "legend": {
+ "orientation": "h",
+ "title": {
+ "text": "pie uniform text
with horizontal orientation
minsize=8"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 8
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_pie_8_horizontal_center.json b/test/image/mocks/uniformtext_pie_8_horizontal_center.json
new file mode 100644
index 00000000000..b503a427e0d
--- /dev/null
+++ b/test/image/mocks/uniformtext_pie_8_horizontal_center.json
@@ -0,0 +1,200 @@
+{
+ "data": [
+ {
+ "rotation": -360,
+ "values": [
+ 4,
+ 1,
+ 0.25
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "auto",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.05,
+ 0.25
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "rotation": -360,
+ "values": [
+ 4,
+ 1,
+ 0.25
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "auto",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.3,
+ 0.5
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 4,
+ 1,
+ 0.25
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "auto",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.05,
+ 0.25
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 4,
+ 1,
+ 0.25
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "auto",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.3,
+ 0.5
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 4,
+ 1,
+ 0.25
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "auto",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.55,
+ 0.75
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 4,
+ 1,
+ 0.25
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "auto",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.8,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.45
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 4,
+ 1,
+ 0.25
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "auto",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.55,
+ 0.75
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 4,
+ 1,
+ 0.25
+ ],
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "textposition": "auto",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.8,
+ 1
+ ],
+ "y": [
+ 0.5,
+ 0.95
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "legend": {
+ "orientation": "h",
+ "title": {
+ "text": "pie uniform text
with horizontal orientation
minsize=8"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 8
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_pie_8_radial.json b/test/image/mocks/uniformtext_pie_8_radial.json
new file mode 100644
index 00000000000..037c522214e
--- /dev/null
+++ b/test/image/mocks/uniformtext_pie_8_radial.json
@@ -0,0 +1,248 @@
+{
+ "data": [
+ {
+ "rotation": -360,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L O N G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0,
+ 0.24
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": -360,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L
O
N
G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.25,
+ 0.49
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L O N G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0,
+ 0.24
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L
O
N
G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.25,
+ 0.49
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L O N G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.5,
+ 0.74
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L
O
N
G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.75,
+ 0.99
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L O N G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.5,
+ 0.74
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L
O
N
G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.75,
+ 0.99
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "legend": {
+ "orientation": "h",
+ "title": {
+ "text": "pie uniform text
with radial orientation
minsize=8"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 8
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_pie_8_tangential.json b/test/image/mocks/uniformtext_pie_8_tangential.json
new file mode 100644
index 00000000000..3490e845771
--- /dev/null
+++ b/test/image/mocks/uniformtext_pie_8_tangential.json
@@ -0,0 +1,248 @@
+{
+ "data": [
+ {
+ "rotation": -360,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L O N G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0,
+ 0.24
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": -360,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L
O
N
G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.25,
+ 0.49
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L O N G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0,
+ 0.24
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": 180,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L
O
N
G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.25,
+ 0.49
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L O N G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.5,
+ 0.74
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": -270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L
O
N
G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.75,
+ 0.99
+ ],
+ "y": [
+ 0,
+ 0.49
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L O N G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.5,
+ 0.74
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ },
+ {
+ "rotation": 270,
+ "values": [
+ 7,
+ 0.9,
+ 0.1
+ ],
+ "text": [
+ "L
O
N
G",
+ "text",
+ "invisible"
+ ],
+ "type": "pie",
+ "textinfo": "text",
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "hole": 0.5,
+ "domain": {
+ "x": [
+ 0.75,
+ 0.99
+ ],
+ "y": [
+ 0.5,
+ 0.99
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 500,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "legend": {
+ "orientation": "h",
+ "title": {
+ "text": "pie uniform text
with tangential orientation
minsize=8"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 8
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_pie_inside-text-orientation.json b/test/image/mocks/uniformtext_pie_inside-text-orientation.json
new file mode 100644
index 00000000000..aa0d8c20b5f
--- /dev/null
+++ b/test/image/mocks/uniformtext_pie_inside-text-orientation.json
@@ -0,0 +1,375 @@
+{
+ "data": [
+ {
+ "name": "horizontal",
+ "title": {
+ "text": "horizontal",
+ "font": {
+ "size": 20
+ }
+ },
+ "insidetextorientation": "horizontal",
+ "textposition": "inside",
+ "type": "pie",
+ "hole": 0.5,
+ "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"
+ ],
+ "marker": {
+ "colors": [
+ "aquamarine",
+ "brown",
+ "chocolate",
+ "darkblue",
+ "darkgreen",
+ "forestgreen",
+ "gold",
+ "honeydew",
+ "indigo",
+ "navajowhite",
+ "khaki",
+ "lightblue",
+ "magenta",
+ "navy",
+ "orange",
+ "pink",
+ "aqua",
+ "red",
+ "silver",
+ "tomato",
+ "turquoise",
+ "violet",
+ "wheat",
+ "yellow",
+ "azure"
+ ]
+ },
+ "values": [
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0.52,
+ 1
+ ],
+ "y": [
+ 0.52,
+ 1
+ ]
+ }
+ },
+ {
+ "name": "radial",
+ "title": {
+ "text": "radial",
+ "font": {
+ "size": 20
+ }
+ },
+ "insidetextorientation": "radial",
+ "textposition": "inside",
+ "type": "pie",
+ "hole": 0.5,
+ "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"
+ ],
+ "values": [
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0.52,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.48
+ ]
+ }
+ },
+ {
+ "name": "tangential",
+ "title": {
+ "text": "tangential",
+ "font": {
+ "size": 20
+ }
+ },
+ "insidetextorientation": "tangential",
+ "textposition": "inside",
+ "type": "pie",
+ "hole": 0.5,
+ "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"
+ ],
+ "values": [
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.48
+ ],
+ "y": [
+ 0,
+ 0.48
+ ]
+ }
+ },
+ {
+ "name": "auto",
+ "title": {
+ "text": "auto",
+ "font": {
+ "size": 20
+ }
+ },
+ "insidetextorientation": "auto",
+ "textposition": "inside",
+ "type": "pie",
+ "hole": 0.5,
+ "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"
+ ],
+ "values": [
+ 26,
+ 25,
+ 24,
+ 23,
+ 22,
+ 21,
+ 20,
+ 19,
+ 18,
+ 17,
+ 16,
+ 15,
+ 14,
+ 13,
+ 12,
+ 11,
+ 10,
+ 9,
+ 8,
+ 7,
+ 6,
+ 5,
+ 4,
+ 3,
+ 2,
+ 1
+ ],
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.48
+ ],
+ "y": [
+ 0.52,
+ 1
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 925,
+ "height": 750,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "legend": {
+ "title": {
+ "text": "inside text orientation"
+ }
+ },
+ "font": {
+ "size": 14
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 9
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_pie_outside.json b/test/image/mocks/uniformtext_pie_outside.json
new file mode 100644
index 00000000000..8060f23908e
--- /dev/null
+++ b/test/image/mocks/uniformtext_pie_outside.json
@@ -0,0 +1,81 @@
+{
+ "data": [
+ {
+ "values": [
+ 3,
+ 1,
+ 5,
+ 2,
+ 4
+ ],
+ "type": "pie",
+ "domain": {
+ "x": [0, 0.4],
+ "y": [0.6, 1]
+ },
+ "sort": true,
+ "direction": "counterclockwise",
+ "name": "sorted ccw (default)"
+ },
+ {
+ "values": [
+ 3,
+ 1,
+ 5,
+ 2,
+ 4
+ ],
+ "type": "pie",
+ "domain": {
+ "x": [0.6, 1],
+ "y": [0.6, 1]
+ },
+ "sort": true,
+ "direction": "clockwise",
+ "name": "sorted cw"
+ },
+ {
+ "values": [
+ 3,
+ 1,
+ 5,
+ 2,
+ 4
+ ],
+ "type": "pie",
+ "domain": {
+ "x": [0, 0.4],
+ "y": [0, 0.4]
+ },
+ "sort": false,
+ "direction": "counterclockwise",
+ "name": "unsorted ccw"
+ },
+ {
+ "values": [
+ 3,
+ 1,
+ 5,
+ 2,
+ 4
+ ],
+ "type": "pie",
+ "domain": {
+ "x": [0.6, 1],
+ "y": [0, 0.4]
+ },
+ "sort": false,
+ "direction": "clockwise",
+ "name": "unsorted cw"
+ }
+ ],
+ "layout": {
+ "height": 500,
+ "width": 600,
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 16
+ },
+ "showlegend": false
+ }
+}
diff --git a/test/image/mocks/uniformtext_pie_pull.json b/test/image/mocks/uniformtext_pie_pull.json
new file mode 100644
index 00000000000..9ed3c6e508a
--- /dev/null
+++ b/test/image/mocks/uniformtext_pie_pull.json
@@ -0,0 +1,135 @@
+{
+ "data": [
+ {
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "hole": 0.25,
+ "domain": {
+ "y": [
+ 0.5,
+ 1
+ ]
+ },
+ "sort": false,
+ "labels": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12
+ ],
+ "values": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12
+ ],
+ "pull": [
+ 0,
+ 0.5,
+ 0,
+ 0.5,
+ 0,
+ 0.5,
+ 0,
+ 0.5,
+ 0,
+ 0.5,
+ 0,
+ 0.5
+ ],
+ "textposition": "inside",
+ "textinfo": "value"
+ },
+ {
+ "type": "pie",
+ "insidetextorientation": "horizontal",
+ "hole": 0.25,
+ "domain": {
+ "y": [
+ 0,
+ 0.5
+ ]
+ },
+ "sort": false,
+ "labels": [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12
+ ],
+ "text": [
+ "slice",
+ "slice",
+ "slice",
+ "slice",
+ "slice",
+ "slice",
+ "slice",
+ "slice",
+ "slice",
+ "slice",
+ "slice",
+ "slice"
+ ],
+ "pull": [
+ 0,
+ 0.5,
+ 0,
+ 0.5,
+ 0,
+ 0.5,
+ 0,
+ 0.5,
+ 0,
+ 0.5,
+ 0,
+ 0.5
+ ],
+ "textposition": "inside",
+ "textinfo": "text"
+ }
+ ],
+ "layout": {
+ "width": 600,
+ "height": 900,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "legend": {
+ "title": {
+ "text": "pie uniform text
with horizontal orientation
minsize=20"
+ }
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 20
+ }
+ }
+}
diff --git a/test/image/mocks/uniformtext_sunburst_inside-text-orientation.json b/test/image/mocks/uniformtext_sunburst_inside-text-orientation.json
new file mode 100644
index 00000000000..33bb7a4da9e
--- /dev/null
+++ b/test/image/mocks/uniformtext_sunburst_inside-text-orientation.json
@@ -0,0 +1,433 @@
+{
+ "data": [
+ {
+ "name": "horizontal",
+ "insidetextorientation": "horizontal",
+ "type": "sunburst",
+ "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"
+ ],
+ "marker": {
+ "colors": [
+ "aquamarine",
+ "brown",
+ "chocolate",
+ "darkblue",
+ "darkgreen",
+ "forestgreen",
+ "gold",
+ "honeydew",
+ "indigo",
+ "navajowhite",
+ "khaki",
+ "lightblue",
+ "magenta",
+ "navy",
+ "orange",
+ "pink",
+ "aqua",
+ "red",
+ "silver",
+ "tomato",
+ "turquoise",
+ "violet",
+ "wheat",
+ "yellow",
+ "azure"
+ ]
+ },
+ "textinfo": "label+value",
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0.5,
+ 1
+ ]
+ }
+ },
+ {
+ "name": "radial",
+ "insidetextorientation": "radial",
+ "type": "sunburst",
+ "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+value",
+ "domain": {
+ "x": [
+ 0.5,
+ 1
+ ],
+ "y": [
+ 0,
+ 0.5
+ ]
+ }
+ },
+ {
+ "name": "tangential",
+ "insidetextorientation": "tangential",
+ "type": "sunburst",
+ "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+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0,
+ 0.5
+ ]
+ }
+ },
+ {
+ "name": "auto",
+ "insidetextorientation": "auto",
+ "type": "sunburst",
+ "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+value",
+ "domain": {
+ "x": [
+ 0,
+ 0.5
+ ],
+ "y": [
+ 0.5,
+ 1
+ ]
+ }
+ }
+ ],
+ "layout": {
+ "width": 800,
+ "height": 800,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "font": {
+ "size": 14
+ },
+ "uniformtext": {
+ "mode": "hide"
+ },
+ "shapes": [
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0,
+ "x1": 0.5,
+ "y0": 0,
+ "y1": 0.5
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.5,
+ "x1": 1,
+ "y0": 0,
+ "y1": 0.5
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0.5,
+ "x1": 1,
+ "y0": 0.5,
+ "y1": 1
+ },
+ {
+ "type": "rect",
+ "layer": "below",
+ "x0": 0,
+ "x1": 0.5,
+ "y0": 0.5,
+ "y1": 1
+ }
+ ],
+ "annotations": [
+ {
+ "text": "auto",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 0.5,
+ "y": 0.5,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "tangential",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 0.5,
+ "y": 0,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "radial",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 1,
+ "y": 0,
+ "font": {
+ "size": 20
+ }
+ },
+ {
+ "text": "horizontal",
+ "showarrow": false,
+ "xref": "paper",
+ "yref": "paper",
+ "xanchor": "right",
+ "yanchor": "bottom",
+ "x": 1,
+ "y": 0.5,
+ "font": {
+ "size": 20
+ }
+ }
+ ]
+ }
+}
diff --git a/test/image/mocks/uniformtext_sunburst_treemap.json b/test/image/mocks/uniformtext_sunburst_treemap.json
new file mode 100644
index 00000000000..d39af521b92
--- /dev/null
+++ b/test/image/mocks/uniformtext_sunburst_treemap.json
@@ -0,0 +1,166 @@
+{
+ "data": [
+ {
+ "type": "sunburst",
+ "insidetextorientation": "horizontal",
+ "domain": {
+ "y": [0.5, 1]
+ },
+ "count": "leaves+branches",
+ "textinfo": "label",
+ "marker": {
+ "line": {
+ "color": "#777"
+ },
+ "colorscale": "Blackbody",
+ "reversescale": true,
+ "showscale": false
+ },
+ "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"
+ ]
+ },
+ {
+ "type": "treemap",
+ "domain": {
+ "y": [0, 0.5]
+ },
+ "count": "leaves+branches",
+ "textinfo": "label",
+ "marker": {
+ "line": {
+ "color": "#777"
+ },
+ "colorscale": "Blackbody",
+ "reversescale": true,
+ "showscale": false
+ },
+ "level": "Juliet",
+ "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": 500,
+ "height": 1000,
+ "margin": {
+ "t": 10,
+ "b": 10,
+ "l": 10,
+ "r": 10
+ },
+ "uniformtext": {
+ "mode": "hide",
+ "minsize": 8
+ }
+ }
+}
diff --git a/test/jasmine/tests/bar_test.js b/test/jasmine/tests/bar_test.js
index 3fab8efd6cc..fbf81926472 100644
--- a/test/jasmine/tests/bar_test.js
+++ b/test/jasmine/tests/bar_test.js
@@ -2787,3 +2787,123 @@ describe('bar tweening', function() {
.then(done);
});
});
+
+describe('bar uniformtext', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextSizes(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(BAR_TEXT_SELECTOR);
+ var size = selection.size();
+ ['fontsizes', 'scales'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ selection.each(function(d, i) {
+ var fontSize = this.style.fontSize;
+ expect(fontSize).toBe(opts.fontsizes[i] + 'px', 'fontSize for element ' + i, msg);
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('scale(');
+ var scale = 1;
+ if(pos0 !== -1) {
+ pos0 += 'scale('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ scale = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.scales[i]).toBeCloseTo(scale, 1, 'scale for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react with new uniform text options', function(done) {
+ var fig = {
+ data: [{
+ type: 'bar',
+ orientation: 'h',
+ x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+
+ // no text element would be created for null and empty string
+ text: [
+ 'long lablel',
+ '$',
+ '=',
+ '|',
+ '.',
+ ' ',
+ '',
+ null,
+ '
',
+ 0
+ ],
+
+ textinfo: 'text',
+ textposition: 'inside',
+ textangle: 0
+ }],
+ layout: {
+ width: 500,
+ height: 500
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextSizes('without uniformtext', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0.48, 1, 1, 1, 1, 1, 1],
+ }))
+ .then(function() {
+ fig.layout.uniformtext = {mode: 'hide'}; // default with minsize=0
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "hide"', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0.48, 0.48, 0.48, 0.48, 0.48, 0.48, 0.48],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 9; // set a minsize less than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 9', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0, 0.48, 0.48, 0.48, 0.48, 0.48, 0.48],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 32; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 32', {
+ fontsizes: [32, 32, 32, 32, 32, 32, 32],
+ scales: [0, 0, 0, 0, 0, 0, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 14; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 14', {
+ fontsizes: [14, 14, 14, 14, 14, 14, 14],
+ scales: [0, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.mode = 'show';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "show"', {
+ fontsizes: [14, 14, 14, 14, 14, 14, 14],
+ scales: [0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36],
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});
diff --git a/test/jasmine/tests/funnel_test.js b/test/jasmine/tests/funnel_test.js
index 9a26e14fcb6..bc5603ab6bb 100644
--- a/test/jasmine/tests/funnel_test.js
+++ b/test/jasmine/tests/funnel_test.js
@@ -1610,3 +1610,122 @@ function assertTraceField(calcData, prop, expectation) {
expect(values).toBeCloseToArray(expectation, undefined, '(field ' + prop + ')');
}
+
+describe('funnel uniformtext', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextSizes(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(FUNNEL_TEXT_SELECTOR);
+ var size = selection.size();
+ ['fontsizes', 'scales'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ selection.each(function(d, i) {
+ var fontSize = this.style.fontSize;
+ expect(fontSize).toBe(opts.fontsizes[i] + 'px', 'fontSize for element ' + i, msg);
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('scale(');
+ var scale = 1;
+ if(pos0 !== -1) {
+ pos0 += 'scale('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ scale = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.scales[i]).toBeCloseTo(scale, 1, 'scale for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react with new uniform text options', function(done) {
+ var fig = {
+ data: [{
+ type: 'funnel',
+ orientation: 'h',
+ x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+
+ text: [
+ 'long lablel',
+ '$',
+ '=',
+ '|',
+ '.',
+ ' ',
+ '',
+ null,
+ '
',
+ 0
+ ],
+
+ textinfo: 'text',
+ textposition: 'inside',
+ textangle: 0
+ }],
+ layout: {
+ width: 500,
+ height: 500
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextSizes('without uniformtext', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0.44, 1, 1, 1, 1, 1, 1],
+ }))
+ .then(function() {
+ fig.layout.uniformtext = {mode: 'hide'}; // default with minsize=0
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "hide"', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 9; // set a minsize less than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 9', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 32; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 32', {
+ fontsizes: [32, 32, 32, 32, 32, 32, 32],
+ scales: [0, 0, 0, 0, 0, 0, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 14; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 14', {
+ fontsizes: [14, 14, 14, 14, 14, 14, 14],
+ scales: [0, 0.33, 0.33, 0.33, 0.33, 0.33, 0.33],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.mode = 'show';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "show"', {
+ fontsizes: [14, 14, 14, 14, 14, 14, 14],
+ scales: [0.33, 0.33, 0.33, 0.33, 0.33, 0.33, 0.33],
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});
diff --git a/test/jasmine/tests/funnelarea_test.js b/test/jasmine/tests/funnelarea_test.js
index 67e2fa6a015..ac6ef54bd10 100644
--- a/test/jasmine/tests/funnelarea_test.js
+++ b/test/jasmine/tests/funnelarea_test.js
@@ -1711,3 +1711,124 @@ describe('Test funnelarea calculated areas with scalegroup on various domain rat
});
});
});
+
+describe('funnelarea uniformtext', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextSizes(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(SLICES_TEXT_SELECTOR);
+ var size = selection.size();
+ ['fontsizes', 'scales'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ selection.each(function(d, i) {
+ var fontSize = this.style.fontSize;
+ expect(fontSize).toBe(opts.fontsizes[i] + 'px', 'fontSize for element ' + i, msg);
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('scale(');
+ var scale = 1;
+ if(pos0 !== -1) {
+ pos0 += 'scale('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ scale = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.scales[i]).toBeCloseTo(scale, 1, 'scale for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react with new uniform text options', function(done) {
+ var fig = {
+ data: [{
+ type: 'funnelarea',
+ baseratio: 1,
+ labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ values: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ sort: false,
+
+ text: [
+ 0,
+ '
',
+ null,
+ '',
+ ' ',
+ '.',
+ '|',
+ '=',
+ 'longest word in German',
+ 'Rindfleischetikettierungsueberwachungsaufgabenuebertragungsgesetz'
+ ],
+
+ textinfo: 'text',
+ textangle: 0,
+ showlegend: false
+ }],
+ layout: {
+ width: 450,
+ height: 450
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextSizes('without uniformtext', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [1, 1, 1, 1, 1, 1, 1, 0.69],
+ }))
+ .then(function() {
+ fig.layout.uniformtext = {mode: 'hide'}; // default with minsize=0
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "hide"', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [0.69, 0.69, 0.69, 0.69, 0.69, 0.69, 0.69, 0.69],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 9; // set a minsize less than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 9', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [0.69, 0.69, 0.69, 0.69, 0.69, 0.69, 0.69, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 32; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 32', {
+ fontsizes: [32, 32, 32, 32, 32, 32, 32, 32],
+ scales: [0, 0.26, 0, 0, 0, 0, 0, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 13; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 13', {
+ fontsizes: [13, 13, 13, 13, 13, 13, 13, 13],
+ scales: [0.64, 0.64, 0.64, 0.64, 0.64, 0.64, 0.64, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.mode = 'show';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "show"', {
+ fontsizes: [13, 13, 13, 13, 13, 13, 13, 13],
+ scales: [0.64, 0.64, 0.64, 0.64, 0.64, 0.64, 0.64, 0.64],
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});
diff --git a/test/jasmine/tests/pie_test.js b/test/jasmine/tests/pie_test.js
index b8303f19897..668f1e42832 100644
--- a/test/jasmine/tests/pie_test.js
+++ b/test/jasmine/tests/pie_test.js
@@ -86,6 +86,29 @@ describe('Pie defaults', function() {
var out4 = _supply({type: 'pie', labels: ['A', 'B'], values: [1, 2], automargin: true, textposition: 'outside'});
expect(out4.automargin).toBe(true, 'textposition outside');
});
+
+ it('should not coerce *insidetextorientation* if `textposition` is *outside* or *none*', function() {
+ var out = _supply({type: 'pie', labels: ['A', 'B'], values: [1, 2], textposition: 'none'});
+ expect(out.insidetextorientation).toBe(undefined, 'textposition none');
+
+ var out2 = _supply({type: 'pie', labels: ['A', 'B'], values: [1, 2], textposition: 'outside'});
+ expect(out2.insidetextorientation).toBe(undefined, 'textposition outside');
+ });
+
+ it('should coerce *insidetextorientation* if `textposition` is *inside* or *auto*', function() {
+ var out = _supply({type: 'pie', labels: ['A', 'B'], values: [1, 2], textposition: 'auto'});
+ expect(out.insidetextorientation).toBe('auto', 'textposition auto');
+
+ var out2 = _supply({type: 'pie', labels: ['A', 'B'], values: [1, 2], textposition: 'inside'});
+ expect(out2.insidetextorientation).toBe('auto', 'textposition inside');
+ });
+
+ it('should coerce *insidetextorientation* if `textposition` is an array', function() {
+ var out = _supply({type: 'pie', labels: ['A', 'B', 'C', 'D', 'E', 'F'], values: [1, 2, 3, 4, 5, 6],
+ textposition: [null, '', 'none', 'auto', 'inside', 'outside']
+ });
+ expect(out.insidetextorientation).toBe('auto', 'textposition auto');
+ });
});
describe('Pie traces', function() {
@@ -1774,3 +1797,219 @@ describe('Test pie interactions edge cases:', function() {
.then(done);
});
});
+
+describe('pie inside text orientation', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextRotations(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(SLICES_TEXT_SELECTOR);
+ var size = selection.size();
+ ['rotations'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('rotate(');
+ var rotate = 0;
+ if(pos0 !== -1) {
+ pos0 += 'rotate('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ rotate = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.rotations[i]).toBeCloseTo(rotate, -1, 'rotation for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react to new insidetextorientation option', function(done) {
+ var fig = {
+ data: [{
+ type: 'pie',
+ labels: [64, 32, 16, 8],
+ values: [64, 32, 16, 8],
+ sort: false,
+
+ text: [
+ 'very long label',
+ 'label',
+ 'long label',
+ '+'
+ ],
+
+ textinfo: 'text',
+ textposition: 'inside',
+ showlegend: false
+ }],
+ layout: {
+ width: 300,
+ height: 300
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextRotations('using default "auto"', {
+ rotations: [-84, 0, -30, 0]
+ }))
+ .then(function() {
+ fig.data[0].insidetextorientation = 'horizontal';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextRotations('using "horizontal"', {
+ rotations: [0, 0, 0, 0]
+ }))
+ .then(function() {
+ fig.data[0].insidetextorientation = 'radial';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextRotations('using "radial"', {
+ rotations: [0, 42, -30, -66]
+ }))
+ .then(function() {
+ fig.data[0].insidetextorientation = 'tangential';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextRotations('using "tangential"', {
+ rotations: [-84, -48, 60, 24]
+ }))
+ .then(function() {
+ fig.data[0].insidetextorientation = 'auto';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextRotations('back to "auto"', {
+ rotations: [-84, 0, -30, 0]
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});
+
+describe('pie uniformtext', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextSizes(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(SLICES_TEXT_SELECTOR);
+ var size = selection.size();
+ ['fontsizes', 'scales'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ selection.each(function(d, i) {
+ var fontSize = this.style.fontSize;
+ expect(fontSize).toBe(opts.fontsizes[i] + 'px', 'fontSize for element ' + i, msg);
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('scale(');
+ var scale = 1;
+ if(pos0 !== -1) {
+ pos0 += 'scale('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ scale = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.scales[i]).toBeCloseTo(scale, 1, 'scale for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react with new uniform text options', function(done) {
+ var fig = {
+ data: [{
+ type: 'pie',
+ labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ values: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ sort: false,
+
+ text: [
+ 0,
+ '
',
+ null,
+ '',
+ ' ',
+ '.',
+ '+',
+ '=',
+ '$',
+ 'very long lablel'
+ ],
+
+ textinfo: 'text',
+ textposition: 'inside',
+ showlegend: false
+ }],
+ layout: {
+ width: 300,
+ height: 300
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextSizes('without uniformtext', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [1, 1, 1, 1, 1, 1, 1, 0.58],
+ }))
+ .then(function() {
+ fig.layout.uniformtext = {mode: 'hide'}; // default with minsize=0
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "hide"', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 9; // set a minsize less than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 9', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 32; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 32', {
+ fontsizes: [32, 32, 32, 32, 32, 32, 32, 32],
+ scales: [0, 0.22, 0.22, 0.22, 0, 0, 0, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 16; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 16', {
+ fontsizes: [16, 16, 16, 16, 16, 16, 16, 16],
+ scales: [0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.mode = 'show';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "show"', {
+ fontsizes: [16, 16, 16, 16, 16, 16, 16, 16],
+ scales: [0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44],
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});
diff --git a/test/jasmine/tests/plots_test.js b/test/jasmine/tests/plots_test.js
index 8c3c3388fed..6669a31a2c0 100644
--- a/test/jasmine/tests/plots_test.js
+++ b/test/jasmine/tests/plots_test.js
@@ -13,6 +13,12 @@ describe('Test Plots', function() {
'use strict';
describe('Plots.supplyDefaults', function() {
+ it('should not coerce `minsize` when `uniformtext.mode` is *false*', function() {
+ var gd = {};
+ supplyAllDefaults(gd);
+ expect(gd._fullLayout.uniformtext.minsize).toBe(undefined);
+ });
+
it('should not accept ranges where the end is not greater than the start', function() {
var gd = {
data: [{
diff --git a/test/jasmine/tests/sunburst_test.js b/test/jasmine/tests/sunburst_test.js
index 64771bb0e38..b46033f7dbb 100644
--- a/test/jasmine/tests/sunburst_test.js
+++ b/test/jasmine/tests/sunburst_test.js
@@ -1,6 +1,7 @@
var Plotly = require('@lib');
var Plots = require('@src/plots/plots');
var Lib = require('@src/lib');
+var Drawing = require('@src/components/drawing');
var constants = require('@src/traces/sunburst/constants');
var d3 = require('d3');
@@ -16,6 +17,8 @@ var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle;
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
var checkTextTemplate = require('../assets/check_texttemplate');
+var SLICES_TEXT_SELECTOR = '.sunburstlayer text.slicetext';
+
function _mouseEvent(type, gd, v) {
return function() {
if(Array.isArray(v)) {
@@ -1066,7 +1069,7 @@ describe('Test sunburst restyle:', function() {
.then(done);
});
- it('should be able to restyle *textinfo*', function(done) {
+ it('should be able to restyle *textinfo* with various *insidetextorientation*', function(done) {
var mock = {
data: [{
type: 'sunburst',
@@ -1120,6 +1123,27 @@ describe('Test sunburst restyle:', function() {
.then(_assert('show everything', ['Root\n0\nnode0', 'B\n2\nnode2', 'A\n1\nnode1', 'b\n3\nnode3']))
.then(_restyle({textinfo: null}))
.then(_assert('back to dflt', ['Root\nnode0', 'B\nnode2', 'A\nnode1', 'b\nnode3']))
+ // now change insidetextorientation to 'horizontal'
+ .then(_restyle({insidetextorientation: 'horizontal'}))
+ .then(_assert('back to dflt', ['Root\nnode0', 'B\nnode2', 'A\nnode1', 'b\nnode3']))
+ .then(_restyle({textinfo: 'none'}))
+ .then(_assert('no textinfo', ['', '', '', '']))
+ .then(_restyle({textinfo: null}))
+ .then(_assert('back to dflt', ['Root\nnode0', 'B\nnode2', 'A\nnode1', 'b\nnode3']))
+ // now change insidetextorientation to 'tangential'
+ .then(_restyle({insidetextorientation: 'tangential'}))
+ .then(_assert('back to dflt', ['Root\nnode0', 'B\nnode2', 'A\nnode1', 'b\nnode3']))
+ .then(_restyle({textinfo: 'none'}))
+ .then(_assert('no textinfo', ['', '', '', '']))
+ .then(_restyle({textinfo: null}))
+ .then(_assert('back to dflt', ['Root\nnode0', 'B\nnode2', 'A\nnode1', 'b\nnode3']))
+ // now change insidetextorientation to 'radial'
+ .then(_restyle({insidetextorientation: 'radial'}))
+ .then(_assert('back to dflt', ['Root\nnode0', 'B\nnode2', 'A\nnode1', 'b\nnode3']))
+ .then(_restyle({textinfo: 'none'}))
+ .then(_assert('no textinfo', ['', '', '', '']))
+ .then(_restyle({textinfo: null}))
+ .then(_assert('back to dflt', ['Root\nnode0', 'B\nnode2', 'A\nnode1', 'b\nnode3']))
.catch(failTest)
.then(done);
});
@@ -1163,25 +1187,25 @@ describe('Test sunburst tweening:', function() {
return s.replace(/\s/g, '');
}
- function _assert(msg, attrName, id, exp) {
+ function _assert(msg, attrName, id, exp, tolerance) {
var lookup = {d: pathTweenFnLookup, transform: textTweenFnLookup}[attrName];
var fn = lookup[id];
// normalize time in [0, 1] where we'll assert the tweening fn output,
// asserting at the mid point *should be* representative enough
var t = 0.5;
var actual = trim(fn(t));
+ var msg2 = msg + ' | node ' + id + ' @t=' + t;
- // do not assert textBB translate piece,
- // which isn't tweened and has OS-dependent results
if(attrName === 'transform') {
- actual = actual.split('translate').slice(0, 2).join('translate');
- }
-
+ var fake = {attr: function() { return actual; }};
+ var xy = Drawing.getTranslate(fake);
+ expect([xy.x, xy.y]).toBeWithinArray(exp, tolerance || 2, msg2);
+ } else {
// we could maybe to bring in:
// https://github.com/hughsk/svg-path-parser
// to make these assertions more readable
-
- expect(actual).toBe(trim(exp), msg + ' | node ' + id + ' @t=' + t);
+ expect(actual).toBe(trim(exp), msg2);
+ }
}
it('should tween sector exit/update (case: click on branch, no maxdepth)', function(done) {
@@ -1211,12 +1235,8 @@ describe('Test sunburst tweening:', function() {
'M350,156.25 L350,100 A135,1350,1,0485,235.00000000000003 L428.75,235.00000000000003' +
'A78.75,78.750,1,1350,156.25Z'
);
- _assert('move B text to new position', 'transform', 'B',
- 'translate(313.45694251914836,271.54305748085164)'
- );
- _assert('move b text to new position', 'transform', 'b',
- 'translate(274.4279627606877,310.57203723931224)'
- );
+ _assert('move B text to new position', 'transform', 'B', [313.45, 275.54]);
+ _assert('move b text to new position', 'transform', 'b', [274.42, 314.57]);
})
.catch(failTest)
.then(done);
@@ -1250,12 +1270,8 @@ describe('Test sunburst tweening:', function() {
'M350,156.25 L350,100 A135,1350,1,0485,235.00000000000003 L428.75,235.00000000000003' +
'A78.75,78.750,1,1350,156.25Z'
);
- _assert('move B text to new position', 'transform', 'B',
- 'translate(316.8522926358638,268.1477073641362)'
- );
- _assert('move b text to new position', 'transform', 'b',
- 'translate(274.4279627606877,310.57203723931224)'
- );
+ _assert('move B text to new position', 'transform', 'B', [316.85, 272.14]);
+ _assert('move b text to new position', 'transform', 'b', [274.42, 314.57]);
})
.catch(failTest)
.then(done);
@@ -1288,12 +1304,8 @@ describe('Test sunburst tweening:', function() {
_assert('enter b for parent position', 'd', 'b',
'M350,133.75 L350,100 A135,1350,0,0350,370 L350,336.25 A101.25,101.250,0,1350,133.75Z'
);
- _assert('move B text to new position', 'transform', 'B',
- 'translate(303.0160689531907,281.9839310468093)'
- );
- _assert('enter b text to new position', 'transform', 'b',
- 'translate(248.75,235)'
- );
+ _assert('move B text to new position', 'transform', 'B', [303.01, 285.98]);
+ _assert('enter b text to new position', 'transform', 'b', [248.75, 239]);
})
.catch(failTest)
.then(done);
@@ -1362,6 +1374,178 @@ describe('Test sunburst tweening:', function() {
.then(done);
});
*/
+
+ it('should update text position during transition using *auto* insidetextorientation', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'sunburst',
+ textinfo: 'label',
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
+ parents: ['', 'A', 'A', 'C', 'C', 'C', 'F', 'F', 'F', 'F', 'J', 'J', 'J', 'J', 'J', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'U', 'U'],
+ insidetextorientation: 'auto'
+ }]
+ })
+ .then(_run(gd, 4))
+ .then(function() {
+ _assert('move J text to new position', 'transform', 'J', [309.3085305481173, 202.66937078300114]);
+ _assert('move O text to new position', 'transform', 'O', [337.158534264498, 162.57550532369754], 5);
+ _assert('move U text to new position', 'transform', 'U', [416.1153793700712, 163.4078137147134]);
+ _assert('move V text to new position', 'transform', 'V', [471.63745793297295, 218.00377184475153]);
+ _assert('move W text to new position', 'transform', 'W', [455.10235209157037, 177.717459723826], 5);
+ _assert('move X text to new position', 'transform', 'X', [431.0320488371527, 145.88885474402548]);
+ _assert('move Y text to new position', 'transform', 'Y', [395.12660928295867, 124.11350635624726]);
+ _assert('move Z text to new position', 'transform', 'Z', [354.1550374068844, 115.63596810986363]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should update text position during transition using *horizontal* insidetextorientation', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'sunburst',
+ textinfo: 'label',
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
+ parents: ['', 'A', 'A', 'C', 'C', 'C', 'F', 'F', 'F', 'F', 'J', 'J', 'J', 'J', 'J', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'U', 'U'],
+ insidetextorientation: 'horizontal'
+ }]
+ })
+ .then(_run(gd, 4))
+ .then(function() {
+ _assert('move J text to new position', 'transform', 'J', [350, 185.9244172266002]);
+ _assert('move O text to new position', 'transform', 'O', [350.1640625, 162.2952497427013]);
+ _assert('move U text to new position', 'transform', 'U', [416.1153793700712, 163.4078137147134]);
+ _assert('move V text to new position', 'transform', 'V', [471.63745793297295, 218.00377184475153]);
+ _assert('move W text to new position', 'transform', 'W', [457.21539566810236, 178.44157384259557]);
+ _assert('move X text to new position', 'transform', 'X', [431.0320488371527, 145.88885474402548]);
+ _assert('move Y text to new position', 'transform', 'Y', [395.12660928295867, 124.11350635624726]);
+ _assert('move Z text to new position', 'transform', 'Z', [354.1550374068844, 115.63596810986363]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should update text position during transition using *tangential* insidetextorientation', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'sunburst',
+ textinfo: 'label',
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
+ parents: ['', 'A', 'A', 'C', 'C', 'C', 'F', 'F', 'F', 'F', 'J', 'J', 'J', 'J', 'J', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'U', 'U'],
+ insidetextorientation: 'tangential'
+ }]
+ })
+ .then(_run(gd, 4))
+ .then(function() {
+ _assert('move J text to new position', 'transform', 'J', [350, 185.9244172266002]);
+ _assert('move O text to new position', 'transform', 'O', [350.1640625, 162.3617907020963]);
+ _assert('move U text to new position', 'transform', 'U', [387.0665312800944, 146.39132446549587]);
+ _assert('move V text to new position', 'transform', 'V', [467.5637172232141, 214.71357776223093]);
+ _assert('move W text to new position', 'transform', 'W', [453.6883022471187, 176.23118240799604]);
+ _assert('move X text to new position', 'transform', 'X', [428.32070483274055, 145.007590714263]);
+ _assert('move Y text to new position', 'transform', 'Y', [393.6173101979463, 123.958130483835]);
+ _assert('move Z text to new position', 'transform', 'Z', [359.52567880729003, 116.05583257124167]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should update text position during transition using *radial* insidetextorientation', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'sunburst',
+ textinfo: 'label',
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
+ parents: ['', 'A', 'A', 'C', 'C', 'C', 'F', 'F', 'F', 'F', 'J', 'J', 'J', 'J', 'J', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'U', 'U'],
+ insidetextorientation: 'radial'
+ }]
+ })
+ .then(_run(gd, 4))
+ .then(function() {
+ _assert('move J text to new position', 'transform', 'J', [298.18238454231454, 239]);
+ _assert('move O text to new position', 'transform', 'O', [299.00421744782363, 183.7721980352468]);
+ _assert('move U text to new position', 'transform', 'U', [418.6530444037927, 162.19895218157865]);
+ _assert('move V text to new position', 'transform', 'V', [471.8671910181962, 218.0219264868202]);
+ _assert('move W text to new position', 'transform', 'W', [459.0093083790858, 178.21113754411613]);
+ _assert('move X text to new position', 'transform', 'X', [433.74669513154777, 144.8536840385141]);
+ _assert('move Y text to new position', 'transform', 'Y', [398.67767996405655, 121.9940236084775]);
+ _assert('move Z text to new position', 'transform', 'Z', [354.00770212095256, 116.19286557341015]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should update text position during transition using *radial* insidetextorientation with level', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'sunburst',
+ textinfo: 'label',
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
+ parents: ['', 'A', 'A', 'C', 'C', 'C', 'F', 'F', 'F', 'F', 'J', 'J', 'J', 'J', 'J', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'U', 'U'],
+ insidetextorientation: 'radial',
+ level: 'O',
+ }]
+ })
+ .then(_run(gd, 2))
+ .then(function() {
+ _assert('move U text to new position', 'transform', 'U', [317.71031126211744, 202.23522389350774]);
+ _assert('move V text to new position', 'transform', 'V', [444.88381073744586, 191.14358863479603]);
+ _assert('move W text to new position', 'transform', 'W', [365.5485731154604, 134.6827081871288]);
+ _assert('move X text to new position', 'transform', 'X', [277.7815763779703, 162.7705278345142]);
+ _assert('move Y text to new position', 'transform', 'Y', [247.47466543373307, 255.288278237516]);
+ _assert('move Z text to new position', 'transform', 'Z', [300.75324430542196, 332.0135787956955]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should update text position during transition using *tangential* insidetextorientation with level', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'sunburst',
+ textinfo: 'label',
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
+ parents: ['', 'A', 'A', 'C', 'C', 'C', 'F', 'F', 'F', 'F', 'J', 'J', 'J', 'J', 'J', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'U', 'U'],
+ insidetextorientation: 'tangential',
+ level: 'O',
+ }]
+ })
+ .then(_run(gd, 2))
+ .then(function() {
+ _assert('move U text to new position', 'transform', 'U', [313.79288001914836, 202.45694251914836]);
+ _assert('move V text to new position', 'transform', 'V', [441.011030377721, 188.63633201157208]);
+ _assert('move W text to new position', 'transform', 'W', [382.1346244328249, 135.0126788235936], 5);
+ _assert('move X text to new position', 'transform', 'X', [277.7815763779703, 162.7705278345142]);
+ _assert('move Y text to new position', 'transform', 'Y', [249.73412124927503, 271.78420776316403]);
+ _assert('move Z text to new position', 'transform', 'Z', [305.39156336654094, 331.3597434293286]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
+
+ it('should update text position during transition using *horizontal* insidetextorientation with level', function(done) {
+ Plotly.plot(gd, {
+ data: [{
+ type: 'sunburst',
+ textinfo: 'label',
+ labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'],
+ parents: ['', 'A', 'A', 'C', 'C', 'C', 'F', 'F', 'F', 'F', 'J', 'J', 'J', 'J', 'J', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'U', 'U'],
+ insidetextorientation: 'horizontal',
+ level: 'O',
+ }]
+ })
+ .then(_run(gd, 2))
+ .then(function() {
+ _assert('move U text to new position', 'transform', 'U', [313.79288001914836, 202.45694251914836]);
+ _assert('move V text to new position', 'transform', 'V', [445.2341347726318, 190.47976534033592]);
+ _assert('move W text to new position', 'transform', 'W', [366.3829959511747, 133.44080859889465]);
+ _assert('move X text to new position', 'transform', 'X', [274.43577526068776, 163.42796276068773]);
+ _assert('move Y text to new position', 'transform', 'Y', [244.44862109889465, 255.71893345117468]);
+ _assert('move Z text to new position', 'transform', 'Z', [301.6438278403359, 334.2263222726318]);
+ })
+ .catch(failTest)
+ .then(done);
+ });
});
describe('Test sunburst interactions edge cases', function() {
@@ -1693,3 +1877,218 @@ describe('Test sunburst texttemplate with *remainder* `values` should work when
['%{percentParent} of %{parent}', ['20% of Eve', '42% of Seth', '8% of Seth']],
], /* skipEtra */ true);
});
+
+describe('sunburst inside text orientation', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextRotations(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(SLICES_TEXT_SELECTOR);
+ var size = selection.size();
+ ['rotations'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('rotate(');
+ var rotate = 0;
+ if(pos0 !== -1) {
+ pos0 += 'rotate('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ rotate = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.rotations[i]).toBeCloseTo(rotate, -1, 'rotation for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react to new insidetextorientation option', function(done) {
+ var fig = {
+ data: [{
+ type: 'sunburst',
+ parents: ['', '', '', ''],
+ labels: [64, 32, 16, 8],
+ values: [64, 32, 16, 8],
+ sort: false,
+
+ text: [
+ 'very long label',
+ 'label',
+ 'long label',
+ '+'
+ ],
+
+ textinfo: 'text',
+ textposition: 'inside',
+ showlegend: false
+ }],
+ layout: {
+ width: 300,
+ height: 300
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextRotations('using default "auto"', {
+ rotations: [-0.6, 0, 48, 0]
+ }))
+ .then(function() {
+ fig.data[0].insidetextorientation = 'horizontal';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextRotations('using "horizontal"', {
+ rotations: [0, 0, 0, 0]
+ }))
+ .then(function() {
+ fig.data[0].insidetextorientation = 'radial';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextRotations('using "radial"', {
+ rotations: [84, -60, 48, 12]
+ }))
+ .then(function() {
+ fig.data[0].insidetextorientation = 'tangential';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextRotations('using "tangential"', {
+ rotations: [0, 0, -42, -78]
+ }))
+ .then(function() {
+ fig.data[0].insidetextorientation = 'auto';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextRotations('back to "auto"', {
+ rotations: [-0.6, 0, 48, 0]
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});
+
+describe('sunburst uniformtext', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextSizes(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(SLICES_TEXT_SELECTOR);
+ var size = selection.size();
+ ['fontsizes', 'scales'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ selection.each(function(d, i) {
+ var fontSize = this.style.fontSize;
+ expect(fontSize).toBe(opts.fontsizes[i] + 'px', 'fontSize for element ' + i, msg);
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('scale(');
+ var scale = 1;
+ if(pos0 !== -1) {
+ pos0 += 'scale('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ scale = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.scales[i]).toBeCloseTo(scale, 1, 'scale for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react with new uniform text options', function(done) {
+ var fig = {
+ data: [{
+ type: 'sunburst',
+ parents: ['', '', '', '', '', '', '', '', '', ''],
+ labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ values: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+
+ text: [
+ 0,
+ '
',
+ null,
+ '',
+ ' ',
+ '.',
+ '+',
+ '=',
+ '$',
+ 'very long lablel'
+ ],
+
+ textinfo: 'text'
+ }],
+ layout: {
+ width: 300,
+ height: 300
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextSizes('without uniformtext', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [1, 1, 1, 1, 1, 1, 1, 1, 1, 0.58],
+ }))
+ .then(function() {
+ fig.layout.uniformtext = {mode: 'hide'}; // default with minsize=0
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "hide"', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 9; // set a minsize less than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 9', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0.58, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 32; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 32', {
+ fontsizes: [32, 32, 32, 32, 32, 32, 32, 32, 32, 32],
+ scales: [0, 0.22, 0.22, 0.22, 0.22, 0.22, 0, 0, 0, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 16; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 16', {
+ fontsizes: [16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
+ scales: [0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.mode = 'show';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "show"', {
+ fontsizes: [16, 16, 16, 16, 16, 16, 16, 16, 16, 16],
+ scales: [0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44, 0.44],
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});
diff --git a/test/jasmine/tests/treemap_test.js b/test/jasmine/tests/treemap_test.js
index 7a613dfb309..96d89755d31 100644
--- a/test/jasmine/tests/treemap_test.js
+++ b/test/jasmine/tests/treemap_test.js
@@ -17,6 +17,8 @@ var assertHoverLabelStyle = customAssertions.assertHoverLabelStyle;
var assertHoverLabelContent = customAssertions.assertHoverLabelContent;
var checkTextTemplate = require('../assets/check_texttemplate');
+var SLICES_TEXT_SELECTOR = '.treemaplayer text.slicetext';
+
function _mouseEvent(type, gd, v) {
return function() {
if(Array.isArray(v)) {
@@ -1318,7 +1320,7 @@ describe('Test treemap tweening:', function() {
}
- function _assert(msg, attrName, id, exp) {
+ function _assert(msg, attrName, id, exp, tolerance) {
var lookup = {d: pathTweenFnLookup, transform: textTweenFnLookup}[attrName];
var fn = lookup[id];
// normalize time in [0, 1] where we'll assert the tweening fn output,
@@ -1330,7 +1332,7 @@ describe('Test treemap tweening:', function() {
if(attrName === 'transform') {
var fake = {attr: function() { return actual; }};
var xy = Drawing.getTranslate(fake);
- expect([xy.x, xy.y]).toBeWithinArray(exp, 2, msg2);
+ expect([xy.x, xy.y]).toBeWithinArray(exp, tolerance || 2, msg2);
} else {
// we could maybe to bring in:
// https://github.com/hughsk/svg-path-parser
@@ -1363,8 +1365,8 @@ describe('Test treemap tweening:', function() {
_assert('update b to new position', 'd', 'b',
'M221.75,136L611,136L611,361L221.75,361Z'
);
- _assert('move B text to new position', 'transform', 'B', [221.75126, 0]);
- _assert('move b text to new position', 'transform', 'b', [224.75150, 0]);
+ _assert('move B text to new position', 'transform', 'B', [222.75, 126], 3);
+ _assert('move b text to new position', 'transform', 'b', [225.75, 150], 3);
})
.catch(failTest)
.then(done);
@@ -1395,8 +1397,8 @@ describe('Test treemap tweening:', function() {
_assert('update b to new position', 'd', 'b',
'M221.75,136L611,136L611,361L221.75,361Z'
);
- _assert('move B text to new position', 'transform', 'B', [221.75126, 0]);
- _assert('move b text to new position', 'transform', 'b', [224.75150, 0]);
+ _assert('move B text to new position', 'transform', 'B', [222.75, 126], 3);
+ _assert('move b text to new position', 'transform', 'b', [225.75, 150], 3);
})
.catch(failTest)
.then(done);
@@ -1427,11 +1429,8 @@ describe('Test treemap tweening:', function() {
_assert('enter b for parent position', 'd', 'b',
'M284.375,188.5L548.375,188.5L548.375,308.5L284.375,308.5Z'
);
- _assert('move B text to new position', 'transform', 'B', [220.25126, 0]);
-
- // TODO this node gets cleared out of viewport to some
- // machine-dependent position - skip for now
- // _assert('enter b text to new position', 'transform', 'b', [286.16071428571433, 35714285714286]);
+ _assert('move B text to new position', 'transform', 'B', [221.25, 126], 3);
+ _assert('enter b text to new position', 'transform', 'b', [286.63, 196.35]);
})
.catch(failTest)
.then(done);
@@ -1691,3 +1690,113 @@ describe('Test treemap texttemplate with *remainder* `values` should work:', fun
['path: %{currentPath}', ['Eve', 'path: Eve/', 'Seth', 'path: Eve/', 'path: Eve/', 'path: Eve/Seth/', 'Awan', 'path: Eve/Seth/', 'path: Eve/Awan/']]
], /* skipEtra */ true);
});
+
+describe('treemap uniformtext', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextSizes(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(SLICES_TEXT_SELECTOR);
+ var size = selection.size();
+ ['fontsizes', 'scales'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ selection.each(function(d, i) {
+ var fontSize = this.style.fontSize;
+ expect(fontSize).toBe(opts.fontsizes[i] + 'px', 'fontSize for element ' + i, msg);
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('scale(');
+ var scale = 1;
+ if(pos0 !== -1) {
+ pos0 += 'scale('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ scale = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.scales[i]).toBeCloseTo(scale, 1, 'scale for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react with new uniform text options', function(done) {
+ var fig = {
+ data: [{
+ type: 'treemap', tiling: { packing: 'slice' },
+ parents: ['', '', '', '', '', '', '', '', '', ''],
+ labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+ values: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+
+ text: [
+ 0,
+ '
',
+ null,
+ '',
+ ' ',
+ '.',
+ '|',
+ '=',
+ 'longest word in German',
+ 'Rindfleischetikettierungsueberwachungsaufgabenuebertragungsgesetz'
+ ],
+
+ textinfo: 'text'
+ }],
+ layout: {
+ width: 500,
+ height: 500
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextSizes('without uniformtext', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0.84],
+ }))
+ .then(function() {
+ fig.layout.uniformtext = {mode: 'hide'}; // default with minsize=0
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "hide"', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 9; // set a minsize less than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 9', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12],
+ scales: [0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84, 0.84],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 13; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 13', {
+ fontsizes: [13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13],
+ scales: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.mode = 'show';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "show"', {
+ fontsizes: [13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13],
+ scales: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});
diff --git a/test/jasmine/tests/waterfall_test.js b/test/jasmine/tests/waterfall_test.js
index f9a35962863..eaeafc9c436 100644
--- a/test/jasmine/tests/waterfall_test.js
+++ b/test/jasmine/tests/waterfall_test.js
@@ -1733,3 +1733,122 @@ function assertTraceField(calcData, prop, expectation) {
expect(values).toBeCloseToArray(expectation, undefined, '(field ' + prop + ')');
}
+
+describe('waterfall uniformtext', function() {
+ 'use strict';
+
+ var gd;
+
+ beforeEach(function() {
+ gd = createGraphDiv();
+ });
+
+ afterEach(destroyGraphDiv);
+
+ function assertTextSizes(msg, opts) {
+ return function() {
+ var selection = d3.selectAll(WATERFALL_TEXT_SELECTOR);
+ var size = selection.size();
+ ['fontsizes', 'scales'].forEach(function(e) {
+ expect(size).toBe(opts[e].length, 'length for ' + e + ' does not match with the number of elements');
+ });
+
+ selection.each(function(d, i) {
+ var fontSize = this.style.fontSize;
+ expect(fontSize).toBe(opts.fontsizes[i] + 'px', 'fontSize for element ' + i, msg);
+ });
+
+ for(var i = 0; i < selection[0].length; i++) {
+ var transform = selection[0][i].getAttribute('transform');
+ var pos0 = transform.indexOf('scale(');
+ var scale = 1;
+ if(pos0 !== -1) {
+ pos0 += 'scale('.length;
+ var pos1 = transform.indexOf(')', pos0);
+ scale = +(transform.substring(pos0, pos1));
+ }
+
+ expect(opts.scales[i]).toBeCloseTo(scale, 1, 'scale for element ' + i, msg);
+ }
+ };
+ }
+
+ it('should be able to react with new uniform text options', function(done) {
+ var fig = {
+ data: [{
+ type: 'waterfall',
+ orientation: 'h',
+ x: [-1, 2, -3, 4, -5, 6, -7, 8, -9, 10],
+
+ text: [
+ 'long lablel',
+ '$',
+ '=',
+ '|',
+ '.',
+ ' ',
+ '',
+ null,
+ '
',
+ 0
+ ],
+
+ textinfo: 'text',
+ textposition: 'inside',
+ textangle: 0
+ }],
+ layout: {
+ width: 500,
+ height: 500
+ }
+ };
+
+ Plotly.plot(gd, fig)
+ .then(assertTextSizes('without uniformtext', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0.48, 1, 1, 1, 1, 1, 1],
+ }))
+ .then(function() {
+ fig.layout.uniformtext = {mode: 'hide'}; // default with minsize=0
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "hide"', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0.48, 0.48, 0.48, 0.48, 0.48, 0.48, 0.48],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 9; // set a minsize less than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 9', {
+ fontsizes: [12, 12, 12, 12, 12, 12, 12],
+ scales: [0, 0.48, 0.48, 0.48, 0.48, 0.48, 0.48],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 32; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 32', {
+ fontsizes: [32, 32, 32, 32, 32, 32, 32],
+ scales: [0, 0, 0, 0, 0, 0, 0],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.minsize = 14; // set a minsize greater than trace font size
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using minsize: 14', {
+ fontsizes: [14, 14, 14, 14, 14, 14, 14],
+ scales: [0, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36],
+ }))
+ .then(function() {
+ fig.layout.uniformtext.mode = 'show';
+ return Plotly.react(gd, fig);
+ })
+ .then(assertTextSizes('using mode: "show"', {
+ fontsizes: [14, 14, 14, 14, 14, 14, 14],
+ scales: [0.36, 0.36, 0.36, 0.36, 0.36, 0.36, 0.36],
+ }))
+ .catch(failTest)
+ .then(done);
+ });
+});