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); + }); +});