Skip to content

Commit

Permalink
Merge pull request #159 from plotly/pie-trace-cleaning
Browse files Browse the repository at this point in the history
Pie trace cleaning
  • Loading branch information
mdtusz committed Jan 5, 2016
2 parents 7236922 + c47255f commit ecb476d
Show file tree
Hide file tree
Showing 10 changed files with 1,032 additions and 932 deletions.
1 change: 1 addition & 0 deletions src/traces/pie/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var colorAttrs = require('../../components/color/attributes');
var fontAttrs = require('../../plots/font_attributes');
Expand Down
145 changes: 145 additions & 0 deletions src/traces/pie/calc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
/**
* Copyright 2012-2016, 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');

var Color = require('../../components/color');
var helpers = require('./helpers');

module.exports = function calc(gd, trace) {
var vals = trace.values,
labels = trace.labels,
cd = [],
fullLayout = gd._fullLayout,
colorMap = fullLayout._piecolormap,
allThisTraceLabels = {},
needDefaults = false,
vTotal = 0,
hiddenLabels = fullLayout.hiddenlabels || [],
i,
v,
label,
color,
hidden,
pt;

if(trace.dlabel) {
labels = new Array(vals.length);
for(i = 0; i < vals.length; i++) {
labels[i] = String(trace.label0 + i * trace.dlabel);
}
}

for(i = 0; i < vals.length; i++) {
v = vals[i];
if(!isNumeric(v)) continue;
v = +v;
if(v < 0) continue;

label = labels[i];
if(label === undefined || label === '') label = i;
label = String(label);
// only take the first occurrence of any given label.
// TODO: perhaps (optionally?) sum values for a repeated label?
if(allThisTraceLabels[label] === undefined) allThisTraceLabels[label] = true;
else continue;

color = tinycolor(trace.marker.colors[i]);
if(color.isValid()) {
color = Color.addOpacity(color, color.getAlpha());
if(!colorMap[label]) {
colorMap[label] = color;
}
}
// have we seen this label and assigned a color to it in a previous trace?
else if(colorMap[label]) color = colorMap[label];
// color needs a default - mark it false, come back after sorting
else {
color = false;
needDefaults = true;
}

hidden = hiddenLabels.indexOf(label) !== -1;

if(!hidden) vTotal += v;

cd.push({
v: v,
label: label,
color: color,
i: i,
hidden: hidden
});
}

if(trace.sort) cd.sort(function(a, b) { return b.v - a.v; });

/**
* now go back and fill in colors we're still missing
* this is done after sorting, so we pick defaults
* in the order slices will be displayed
*/

if(needDefaults) {
for(i = 0; i < cd.length; i++) {
pt = cd[i];
if(pt.color === false) {
colorMap[pt.label] = pt.color = nextDefaultColor(fullLayout._piedefaultcolorcount);
fullLayout._piedefaultcolorcount++;
}
}
}

// include the sum of all values in the first point
if(cd[0]) cd[0].vTotal = vTotal;

// now insert text
if(trace.textinfo && trace.textinfo !== 'none') {
var hasLabel = trace.textinfo.indexOf('label') !== -1,
hasText = trace.textinfo.indexOf('text') !== -1,
hasValue = trace.textinfo.indexOf('value') !== -1,
hasPercent = trace.textinfo.indexOf('percent') !== -1,
thisText;

for(i = 0; i < cd.length; i++) {
pt = cd[i];
thisText = hasLabel ? [pt.label] : [];
if(hasText && trace.text[pt.i]) thisText.push(trace.text[pt.i]);
if(hasValue) thisText.push(helpers.formatPieValue(pt.v));
if(hasPercent) thisText.push(helpers.formatPiePercent(pt.v / vTotal));
pt.text = thisText.join('<br>');
}
}

return cd;
};

/**
* pick a default color from the main default set, augmented by
* itself lighter then darker before repeating
*/
var pieDefaultColors;

function nextDefaultColor(index) {
if(!pieDefaultColors) {
// generate this default set on demand (but then it gets saved in the module)
var mainDefaults = Color.defaults;
pieDefaultColors = mainDefaults.slice();
for(var i = 0; i < mainDefaults.length; i++) {
pieDefaultColors.push(tinycolor(mainDefaults[i]).lighten(20).toHexString());
}
for(i = 0; i < Color.defaults.length; i++) {
pieDefaultColors.push(tinycolor(mainDefaults[i]).darken(20).toHexString());
}
}

return pieDefaultColors[index % pieDefaultColors.length];
}
82 changes: 82 additions & 0 deletions src/traces/pie/defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* Copyright 2012-2016, 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 Lib = require('../../lib');
var attributes = require('./attributes');

module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
function coerce(attr, dflt) {
return Lib.coerce(traceIn, traceOut, attributes, attr, dflt);
}

var coerceFont = Lib.coerceFont;

var vals = coerce('values');
if(!Array.isArray(vals) || !vals.length) {
traceOut.visible = false;
return;
}

var labels = coerce('labels');
if(!Array.isArray(labels)) {
coerce('label0');
coerce('dlabel');
}

var lineWidth = coerce('marker.line.width');
if(lineWidth) coerce('marker.line.color');

var colors = coerce('marker.colors');
if(!Array.isArray(colors)) traceOut.marker.colors = []; // later this will get padded with default colors

coerce('scalegroup');
// TODO: tilt, depth, and hole all need to be coerced to the same values within a scaleegroup
// (ideally actually, depth would get set the same *after* scaling, ie the same absolute depth)
// and if colors aren't specified we should match these up - potentially even if separate pies
// are NOT in the same sharegroup


var textData = coerce('text');
var textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent');

coerce('hoverinfo', (layout._dataLength === 1) ? 'label+text+value+percent' : undefined);

if(textInfo && textInfo !== 'none') {
var textPosition = coerce('textposition'),
hasBoth = Array.isArray(textPosition) || textPosition === 'auto',
hasInside = hasBoth || textPosition === 'inside',
hasOutside = hasBoth || textPosition === 'outside';

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

coerce('domain.x');
coerce('domain.y');

// 3D attributes commented out until I finish them in a later PR
// var tilt = coerce('tilt');
// if(tilt) {
// coerce('tiltaxis');
// coerce('depth');
// coerce('shading');
// }

coerce('hole');

coerce('sort');
coerce('direction');
coerce('rotation');

coerce('pull');
};
21 changes: 21 additions & 0 deletions src/traces/pie/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright 2012-2016, 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';

exports.formatPiePercent = function formatPiePercent(v) {
var vRounded = (v * 100).toPrecision(3);
if(vRounded.indexOf('.') !== -1) return vRounded.replace(/[.]?0+$/,'') + '%';
return vRounded + '%';
};

exports.formatPieValue = function formatPieValue(v) {
var vRounded = v.toPrecision(10);
if(vRounded.indexOf('.') !== -1) return vRounded.replace(/[.]?0+$/,'');
return vRounded;
};
Loading

0 comments on commit ecb476d

Please sign in to comment.