Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix uniformtext and enable coloraxis for sunburst and treemap as well as pathbar.textfont #4444

Merged
merged 16 commits into from
Jan 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1186,8 +1186,10 @@ lib.isHidden = function(gd) {
* @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
* - textX: text middle position on the x-axis
* - textY: text middle position on the y-axis
* - anchorX: (optional) text anchor position on the x-axis (computed from textX), zero for middle anchor
* - anchorY: (optional) text anchor position on the y-axis (computed from textY), zero for middle anchor
* - scale: (optional) scale applied after translate
* - rotate: (optional) rotation applied after scale
* - noCenter: when defined no extra arguments needed in rotation
Expand All @@ -1198,15 +1200,17 @@ lib.getTextTransform = function(transform) {
var textY = transform.textY;
var targetX = transform.targetX;
var targetY = transform.targetY;
var anchorX = transform.anchorX || 0;
var anchorY = transform.anchorY || 0;
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) +
(targetX - scale * (textX + anchorX)) + ',' +
(targetY - scale * (textY + anchorY)) +
')' +
(scale < 1 ?
'scale(' + scale + ')' : ''
Expand Down
10 changes: 9 additions & 1 deletion src/traces/bar/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ function handleText(traceIn, traceOut, layout, coerce, textposition, opts) {
var moduleHasCliponaxis = !(opts.moduleHasCliponaxis === false);
var moduleHasTextangle = !(opts.moduleHasTextangle === false);
var moduleHasInsideanchor = !(opts.moduleHasInsideanchor === false);
var hasPathbar = !!opts.hasPathbar;

var hasBoth = Array.isArray(textposition) || textposition === 'auto';
var hasInside = hasBoth || textposition === 'inside';
Expand All @@ -147,8 +148,15 @@ function handleText(traceIn, traceOut, layout, coerce, textposition, opts) {
}
coerceFont(coerce, 'insidetextfont', insideTextFontDefault);

if(hasOutside) coerceFont(coerce, 'outsidetextfont', dfltFont);
if(hasPathbar) {
var pathbarTextFontDefault = Lib.extendFlat({}, dfltFont);
if(isColorInheritedFromLayoutFont) {
delete pathbarTextFontDefault.color;
}
coerceFont(coerce, 'pathbar.textfont', pathbarTextFontDefault);
}

if(hasOutside) coerceFont(coerce, 'outsidetextfont', dfltFont);

if(moduleHasSelected) coerce('selected.textfont.color');
if(moduleHasUnselected) coerce('unselected.textfont.color');
Expand Down
155 changes: 76 additions & 79 deletions src/traces/bar/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ var Drawing = require('../../components/drawing');
var Registry = require('../../registry');
var tickText = require('../../plots/cartesian/axes').tickText;

var uniformText = require('./uniform_text');
var recordMinTextSize = uniformText.recordMinTextSize;
var clearMinTextSize = uniformText.clearMinTextSize;

var style = require('./style');
var helpers = require('./helpers');
var constants = require('./constants');
Expand Down Expand Up @@ -58,8 +62,8 @@ function getXY(di, xa, ya, isHorizontal) {
return isHorizontal ? [s, p] : [p, s];
}

function transition(selection, opts, makeOnCompleteCallback) {
if(hasTransition(opts)) {
function transition(selection, fullLayout, opts, makeOnCompleteCallback) {
if(!fullLayout.uniformtext.mode && hasTransition(opts)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? I thought you said this transitions looked fine

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They did not apply a uniform text size during and after smooth transition.

var onComplete;
if(makeOnCompleteCallback) {
onComplete = makeOnCompleteCallback();
Expand Down Expand Up @@ -91,6 +95,9 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback)
gap: fullLayout.bargap,
groupgap: fullLayout.bargroupgap
};

// don't clear bar when this is called from waterfall or funnel
clearMinTextSize('bar', fullLayout);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait. Here you're clearing the min-text-size stash during the plot step. So what's happens when a style-only edit (e.g. Plotly.restyle(gd, 'marker.color', 'red')) gets called?

To me, this logic should probably be somewhere in the calc step.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restyle works. Considering interactions (e.g. zoom and selection) this should be cleared after calc and at the stat of plot.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Considering interactions (e.g. zoom and selection) this should be cleared after calc and at the stat of plot.

Can you explain a bit more here? To me, sounds like we should not clear minTextSize during zoom interactions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or let me rephrase my question: if you do move clearMinTextSize to the calc step, do any of the tests fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. In that case the react tests would fail.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you share the branch you used to test that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summing up a private convo:

  • I was under the impression that the uniformtext attributes were editType: 'calc', so that's why I thought it would be best to clear the min-text-size during the calc step, but they're not. The uniformtext attributes are editType: 'plot'.
  • @archmoj says that making the uniformtext attributes editType: 'calc' would have an impact on the way graph with set uniformtext would behave on zoom
  • Since we'll need to refactor the trace text pipeline at some point (more info in Consistent text mode for bar-like & pie-like traces and feature to control text orientation inside pie/sunburst slices #4420 (comment)), let's keep the clear-min-text logic in the plot step for now.
  • The most important of this PR are the newly added tests.

}

var bartraces = Lib.makeTraceGroups(traceLayer, cdModule, 'trace bars').each(function(cd) {
Expand Down Expand Up @@ -209,13 +216,13 @@ function plot(gd, plotinfo, cdModule, traceLayer, opts, makeOnCompleteCallback)
y1 = fixpx(y1, y0);
}

var sel = transition(Lib.ensureSingle(bar, 'path'), opts, makeOnCompleteCallback);
var sel = transition(Lib.ensureSingle(bar, 'path'), fullLayout, opts, makeOnCompleteCallback);
sel
.style('vector-effect', 'non-scaling-stroke')
.attr('d', 'M' + x0 + ',' + y0 + 'V' + y1 + 'H' + x1 + 'V' + y0 + 'Z')
.call(Drawing.setClipUrl, plotinfo.layerClipId, gd);

if(hasTransition(opts)) {
if(!fullLayout.uniformtext.mode && hasTransition(opts)) {
var styleFns = Drawing.makePointStyleFns(trace);
Drawing.singlePointStyle(di, sel, trace, styleFns, gd);
}
Expand Down Expand Up @@ -409,47 +416,39 @@ function appendBarText(gd, plotinfo, bar, cd, i, x0, x1, y0, y1, opts, makeOnCom
recordMinTextSize(trace.type, transform, fullLayout);
calcBar.transform = transform;

transition(textSelection, opts, makeOnCompleteCallback)
transition(textSelection, fullLayout, 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) {
return (angle === 'auto') ? 0 : angle;
}

function getRotatedTextSize(textBB, rotate) {
var a = Math.PI / 180 * rotate;
var absSin = Math.abs(Math.sin(a));
var absCos = Math.abs(Math.cos(a));

return {
x: textBB.width * absCos + textBB.height * absSin,
y: textBB.width * absSin + textBB.height * absCos
};
}

function toMoveInsideBar(x0, x1, y0, y1, textBB, opts) {
var isHorizontal = !!opts.isHorizontal;
var constrained = !!opts.constrained;
var angle = opts.angle || 0;
var anchor = opts.anchor || 0;
var anchor = opts.anchor || 'end';
var isEnd = anchor === 'end';
var isStart = anchor === 'start';

var textWidth = textBB.width;
var textHeight = textBB.height;
var lx = Math.abs(x1 - x0);
var ly = Math.abs(y1 - y0);

// compute remaining space
var textpad = (
lx > (2 * TEXTPAD) &&
ly > (2 * TEXTPAD)
Expand All @@ -458,67 +457,64 @@ function toMoveInsideBar(x0, x1, y0, y1, textBB, opts) {
lx -= 2 * textpad;
ly -= 2 * textpad;

var autoRotate = (angle === 'auto');
var isAutoRotated = false;
if(autoRotate &&
var rotate = getRotateFromAngle(angle);
if((angle === 'auto') &&
!(textWidth <= lx && textHeight <= ly) &&
(textWidth > lx || textHeight > ly) && (
!(textWidth > ly || textHeight > lx) ||
((textWidth < textHeight) !== (lx < ly))
)) {
isAutoRotated = true;
rotate += 90;
}

if(isAutoRotated) {
// don't rotate yet only swap bar width with height
var tmp = ly;
ly = lx;
lx = tmp;
}
var t = getRotatedTextSize(textBB, rotate);

var rotate = getRotateFromAngle(angle);
var absSin = Math.abs(Math.sin(Math.PI / 180 * rotate));
var absCos = Math.abs(Math.cos(Math.PI / 180 * rotate));

// compute and apply text padding
var dx = Math.max(lx * absCos, ly * absSin);
var dy = Math.max(lx * absSin, ly * absCos);

var scale = (constrained) ?
Math.min(dx / textWidth, dy / textHeight) :
Math.max(absCos, absSin);

scale = Math.min(1, scale);
var scale = 1;
if(constrained) {
scale = Math.min(
1,
lx / t.x,
ly / t.y
);
}

// compute text and target positions
var textX = (textBB.left + textBB.right) / 2;
var textY = (textBB.top + textBB.bottom) / 2;
var targetX = (x0 + x1) / 2;
var targetY = (y0 + y1) / 2;

if(anchor !== 'middle') { // case of 'start' or 'end'
var targetWidth = scale * (isHorizontal !== isAutoRotated ? textHeight : textWidth);
var targetHeight = scale * (isHorizontal !== isAutoRotated ? textWidth : textHeight);
textpad += 0.5 * (targetWidth * absSin + targetHeight * absCos);
var anchorX = 0;
var anchorY = 0;
if(isStart || isEnd) {
var extrapad = (isHorizontal ? t.x : t.y) / 2;
var dir = isHorizontal ? dirSign(x0, x1) : dirSign(y0, y1);

if(isHorizontal) {
textpad *= dirSign(x0, x1);
targetX = (anchor === 'start') ? x0 + textpad : x1 - textpad;
if(isStart) {
targetX = x0 + dir * textpad;
anchorX = -dir * extrapad;
} else {
targetX = x1 - dir * textpad;
anchorX = dir * extrapad;
}
} else {
textpad *= dirSign(y0, y1);
targetY = (anchor === 'start') ? y0 + textpad : y1 - textpad;
if(isStart) {
targetY = y0 + dir * textpad;
anchorY = -dir * extrapad;
} else {
targetY = y1 - dir * textpad;
anchorY = dir * extrapad;
}
}
}

var textX = (textBB.left + textBB.right) / 2;
var textY = (textBB.top + textBB.bottom) / 2;

// lastly apply auto rotation
if(isAutoRotated) rotate += 90;

return {
textX: textX,
textY: textY,
targetX: targetX,
targetY: targetY,
anchorX: anchorX,
anchorY: anchorY,
scale: scale,
rotate: rotate
};
Expand Down Expand Up @@ -552,31 +548,33 @@ function toMoveOutsideBar(x0, x1, y0, y1, textBB, opts) {
}

var rotate = getRotateFromAngle(angle);
var absSin = Math.abs(Math.sin(Math.PI / 180 * rotate));
var absCos = Math.abs(Math.cos(Math.PI / 180 * rotate));
var t = getRotatedTextSize(textBB, rotate);

// compute text and target positions
var targetWidth = scale * (isHorizontal ? textHeight : textWidth);
var targetHeight = scale * (isHorizontal ? textWidth : textHeight);
textpad += 0.5 * (targetWidth * absSin + targetHeight * absCos);

var extrapad = (isHorizontal ? t.x : t.y) / 2;
var textX = (textBB.left + textBB.right) / 2;
var textY = (textBB.top + textBB.bottom) / 2;
var targetX = (x0 + x1) / 2;
var targetY = (y0 + y1) / 2;
var anchorX = 0;
var anchorY = 0;

var dir = isHorizontal ? dirSign(x1, x0) : dirSign(y0, y1);
if(isHorizontal) {
targetX = x1 - textpad * dirSign(x1, x0);
targetX = x1 - dir * textpad;
anchorX = dir * extrapad;
} else {
targetY = y1 + textpad * dirSign(y0, y1);
targetY = y1 + dir * textpad;
anchorY = -dir * extrapad;
}

var textX = (textBB.left + textBB.right) / 2;
var textY = (textBB.top + textBB.bottom) / 2;

return {
textX: textX,
textY: textY,
targetX: targetX,
targetY: targetY,
anchorX: anchorX,
anchorY: anchorY,
scale: scale,
rotate: rotate
};
Expand Down Expand Up @@ -749,6 +747,5 @@ function calcTextinfo(cd, index, xa, ya) {

module.exports = {
plot: plot,
toMoveInsideBar: toMoveInsideBar,
recordMinTextSize: recordMinTextSize
toMoveInsideBar: toMoveInsideBar
};
31 changes: 1 addition & 30 deletions src/traces/bar/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,13 @@ var Drawing = require('../../components/drawing');
var Lib = require('../../lib');
var Registry = require('../../registry');

var resizeText = require('./uniform_text').resizeText;
var attributes = require('./attributes');
var attributeTextFont = attributes.textfont;
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');
Expand Down
Loading