Skip to content

Commit

Permalink
Merge pull request #3130 from plotly/2951-contrasting-pie-and-bar-ins…
Browse files Browse the repository at this point in the history
…ide-text

2951 contrasting pie and bar inside text
  • Loading branch information
rmoestl authored Oct 25, 2018
2 parents 36d9fb4 + 69b35b5 commit d5ebb50
Show file tree
Hide file tree
Showing 27 changed files with 799 additions and 147 deletions.
15 changes: 14 additions & 1 deletion src/traces/bar/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,21 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout

if(hasInside || hasOutside) {
var textFont = coerceFont(coerce, 'textfont', layout.font);
if(hasInside) coerceFont(coerce, 'insidetextfont', textFont);

// Note that coercing `insidetextfont` is always needed –
// even if `textposition` is `outside` for each trace – since
// an outside label can become an inside one, for example because
// of a bar being stacked on top of it.
var insideTextFontDefault = Lib.extendFlat({}, textFont);
var isTraceTextfontColorSet = traceIn.textfont && traceIn.textfont.color;
var isColorInheritedFromLayoutFont = !isTraceTextfontColorSet;
if(isColorInheritedFromLayoutFont) {
delete insideTextFontDefault.color;
}
coerceFont(coerce, 'insidetextfont', insideTextFontDefault);

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

coerce('constraintext');
coerce('selected.textfont.color');
coerce('unselected.textfont.color');
Expand Down
67 changes: 67 additions & 0 deletions src/traces/bar/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright 2012-2018, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var isNumeric = require('fast-isnumeric');
var tinycolor = require('tinycolor2');

exports.coerceString = function(attributeDefinition, value, defaultValue) {
if(typeof value === 'string') {
if(value || !attributeDefinition.noBlank) return value;
}
else if(typeof value === 'number') {
if(!attributeDefinition.strict) return String(value);
}

return (defaultValue !== undefined) ?
defaultValue :
attributeDefinition.dflt;
};

exports.coerceNumber = function(attributeDefinition, value, defaultValue) {
if(isNumeric(value)) {
value = +value;

var min = attributeDefinition.min,
max = attributeDefinition.max,
isOutOfBounds = (min !== undefined && value < min) ||
(max !== undefined && value > max);

if(!isOutOfBounds) return value;
}

return (defaultValue !== undefined) ?
defaultValue :
attributeDefinition.dflt;
};

exports.coerceColor = function(attributeDefinition, value, defaultValue) {
if(tinycolor(value).isValid()) return value;

return (defaultValue !== undefined) ?
defaultValue :
attributeDefinition.dflt;
};

exports.coerceEnumerated = function(attributeDefinition, value, defaultValue) {
if(attributeDefinition.coerceNumber) value = +value;

if(attributeDefinition.values.indexOf(value) !== -1) return value;

return (defaultValue !== undefined) ?
defaultValue :
attributeDefinition.dflt;
};

exports.getValue = function(arrayOrScalar, index) {
var value;
if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar;
else if(index < arrayOrScalar.length) value = arrayOrScalar[index];
return value;
};
110 changes: 11 additions & 99 deletions src/traces/bar/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

var d3 = require('d3');
var isNumeric = require('fast-isnumeric');
var tinycolor = require('tinycolor2');

var Lib = require('../../lib');
var svgTextUtils = require('../../lib/svg_text_utils');
Expand All @@ -22,10 +21,9 @@ var Registry = require('../../registry');

var attributes = require('./attributes'),
attributeText = attributes.text,
attributeTextPosition = attributes.textposition,
attributeTextFont = attributes.textfont,
attributeInsideTextFont = attributes.insidetextfont,
attributeOutsideTextFont = attributes.outsidetextfont;
attributeTextPosition = attributes.textposition;
var helpers = require('./helpers');
var style = require('./style');

// padding in pixels around text
var TEXTPAD = 3;
Expand Down Expand Up @@ -177,9 +175,10 @@ function appendBarText(gd, bar, calcTrace, i, x0, x1, y0, y1) {
return;
}

var textFont = getTextFont(trace, i, gd._fullLayout.font),
insideTextFont = getInsideTextFont(trace, i, textFont),
outsideTextFont = getOutsideTextFont(trace, i, textFont);
var layoutFont = gd._fullLayout.font;
var barColor = style.getBarColor(calcTrace[i], trace);
var insideTextFont = style.getInsideTextFont(trace, i, layoutFont, barColor);
var outsideTextFont = style.getOutsideTextFont(trace, i, layoutFont);

// compute text position
var barmode = gd._fullLayout.barmode,
Expand Down Expand Up @@ -429,98 +428,11 @@ function getTransform(textX, textY, targetX, targetY, scale, rotate) {
}

function getText(trace, index) {
var value = getValue(trace.text, index);
return coerceString(attributeText, value);
var value = helpers.getValue(trace.text, index);
return helpers.coerceString(attributeText, value);
}

function getTextPosition(trace, index) {
var value = getValue(trace.textposition, index);
return coerceEnumerated(attributeTextPosition, value);
}

function getTextFont(trace, index, defaultValue) {
return getFontValue(
attributeTextFont, trace.textfont, index, defaultValue);
}

function getInsideTextFont(trace, index, defaultValue) {
return getFontValue(
attributeInsideTextFont, trace.insidetextfont, index, defaultValue);
}

function getOutsideTextFont(trace, index, defaultValue) {
return getFontValue(
attributeOutsideTextFont, trace.outsidetextfont, index, defaultValue);
}

function getFontValue(attributeDefinition, attributeValue, index, defaultValue) {
attributeValue = attributeValue || {};

var familyValue = getValue(attributeValue.family, index),
sizeValue = getValue(attributeValue.size, index),
colorValue = getValue(attributeValue.color, index);

return {
family: coerceString(
attributeDefinition.family, familyValue, defaultValue.family),
size: coerceNumber(
attributeDefinition.size, sizeValue, defaultValue.size),
color: coerceColor(
attributeDefinition.color, colorValue, defaultValue.color)
};
}

function getValue(arrayOrScalar, index) {
var value;
if(!Array.isArray(arrayOrScalar)) value = arrayOrScalar;
else if(index < arrayOrScalar.length) value = arrayOrScalar[index];
return value;
}

function coerceString(attributeDefinition, value, defaultValue) {
if(typeof value === 'string') {
if(value || !attributeDefinition.noBlank) return value;
}
else if(typeof value === 'number') {
if(!attributeDefinition.strict) return String(value);
}

return (defaultValue !== undefined) ?
defaultValue :
attributeDefinition.dflt;
}

function coerceEnumerated(attributeDefinition, value, defaultValue) {
if(attributeDefinition.coerceNumber) value = +value;

if(attributeDefinition.values.indexOf(value) !== -1) return value;

return (defaultValue !== undefined) ?
defaultValue :
attributeDefinition.dflt;
}

function coerceNumber(attributeDefinition, value, defaultValue) {
if(isNumeric(value)) {
value = +value;

var min = attributeDefinition.min,
max = attributeDefinition.max,
isOutOfBounds = (min !== undefined && value < min) ||
(max !== undefined && value > max);

if(!isOutOfBounds) return value;
}

return (defaultValue !== undefined) ?
defaultValue :
attributeDefinition.dflt;
}

function coerceColor(attributeDefinition, value, defaultValue) {
if(tinycolor(value).isValid()) return value;

return (defaultValue !== undefined) ?
defaultValue :
attributeDefinition.dflt;
var value = helpers.getValue(trace.textposition, index);
return helpers.coerceEnumerated(attributeTextPosition, value);
}
122 changes: 104 additions & 18 deletions src/traces/bar/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,17 @@
'use strict';

var d3 = require('d3');
var Color = require('../../components/color');
var Drawing = require('../../components/drawing');
var Lib = require('../../lib');
var Registry = require('../../registry');

var attributes = require('./attributes'),
attributeTextFont = attributes.textfont,
attributeInsideTextFont = attributes.insidetextfont,
attributeOutsideTextFont = attributes.outsidetextfont;
var helpers = require('./helpers');

function style(gd, cd) {
var s = cd ? cd[0].node3 : d3.select(gd).selectAll('g.trace.bars');
var barcount = s.size();
Expand Down Expand Up @@ -50,21 +58,8 @@ function stylePoints(sel, trace, gd) {

txs.each(function(d) {
var tx = d3.select(this);
var textFont;

if(tx.classed('bartext-inside')) {
textFont = trace.insidetextfont;
} else if(tx.classed('bartext-outside')) {
textFont = trace.outsidetextfont;
}
if(!textFont) textFont = trace.textfont;

function cast(k) {
var cont = textFont[k];
return Array.isArray(cont) ? cont[d.i] : cont;
}

Drawing.font(tx, cast('family'), cast('size'), cast('color'));
var font = determineFont(tx, d, trace, gd);
Drawing.font(tx, font);
});
}

Expand All @@ -73,14 +68,105 @@ function styleOnSelect(gd, cd) {
var trace = cd[0].trace;

if(trace.selectedpoints) {
Drawing.selectedPointStyle(s.selectAll('path'), trace);
Drawing.selectedTextStyle(s.selectAll('text'), trace);
stylePointsInSelectionMode(s, trace, gd);
} else {
stylePoints(s, trace, gd);
}
}

function stylePointsInSelectionMode(s, trace, gd) {
Drawing.selectedPointStyle(s.selectAll('path'), trace);
styleTextInSelectionMode(s.selectAll('text'), trace, gd);
}

function styleTextInSelectionMode(txs, trace, gd) {
txs.each(function(d) {
var tx = d3.select(this);
var font;

if(d.selected) {
font = Lib.extendFlat({}, determineFont(tx, d, trace, gd));

var selectedFontColor = trace.selected.textfont && trace.selected.textfont.color;
if(selectedFontColor) {
font.color = selectedFontColor;
}

Drawing.font(tx, font);
} else {
Drawing.selectedTextStyle(tx, trace);
}
});
}

function determineFont(tx, d, trace, gd) {
var layoutFont = gd._fullLayout.font;
var textFont = trace.textfont;

if(tx.classed('bartext-inside')) {
var barColor = getBarColor(d, trace);
textFont = getInsideTextFont(trace, d.i, layoutFont, barColor);
} else if(tx.classed('bartext-outside')) {
textFont = getOutsideTextFont(trace, d.i, layoutFont);
}

return textFont;
}

function getTextFont(trace, index, defaultValue) {
return getFontValue(
attributeTextFont, trace.textfont, index, defaultValue);
}

function getInsideTextFont(trace, index, layoutFont, barColor) {
var defaultFont = getTextFont(trace, index, layoutFont);

var wouldFallBackToLayoutFont =
(trace._input.textfont === undefined || trace._input.textfont.color === undefined) ||
(Array.isArray(trace.textfont.color) && trace.textfont.color[index] === undefined);
if(wouldFallBackToLayoutFont) {
defaultFont = {
color: Color.contrast(barColor),
family: defaultFont.family,
size: defaultFont.size
};
}

return getFontValue(
attributeInsideTextFont, trace.insidetextfont, index, defaultFont);
}

function getOutsideTextFont(trace, index, layoutFont) {
var defaultFont = getTextFont(trace, index, layoutFont);
return getFontValue(
attributeOutsideTextFont, trace.outsidetextfont, index, defaultFont);
}

function getFontValue(attributeDefinition, attributeValue, index, defaultValue) {
attributeValue = attributeValue || {};

var familyValue = helpers.getValue(attributeValue.family, index),
sizeValue = helpers.getValue(attributeValue.size, index),
colorValue = helpers.getValue(attributeValue.color, index);

return {
family: helpers.coerceString(
attributeDefinition.family, familyValue, defaultValue.family),
size: helpers.coerceNumber(
attributeDefinition.size, sizeValue, defaultValue.size),
color: helpers.coerceColor(
attributeDefinition.color, colorValue, defaultValue.color)
};
}

function getBarColor(cd, trace) {
return cd.mc || trace.marker.color;
}

module.exports = {
style: style,
styleOnSelect: styleOnSelect
styleOnSelect: styleOnSelect,
getInsideTextFont: getInsideTextFont,
getOutsideTextFont: getOutsideTextFont,
getBarColor: getBarColor
};
Loading

0 comments on commit d5ebb50

Please sign in to comment.