| Index: tracing/tracing/ui/base/bar_chart.html
|
| diff --git a/tracing/tracing/ui/base/bar_chart.html b/tracing/tracing/ui/base/bar_chart.html
|
| index fc81cd028b39e3c7ab4b244e9f9e115dc516e085..c6f06374128cfed0e440468ec12e551511ea26d9 100644
|
| --- a/tracing/tracing/ui/base/bar_chart.html
|
| +++ b/tracing/tracing/ui/base/bar_chart.html
|
| @@ -24,7 +24,28 @@ tr.exportTo('tr.ui.b', function() {
|
| decorate: function() {
|
| ChartBase2DBrushX.prototype.decorate.call(this);
|
| Polymer.dom(this).classList.add('bar-chart');
|
| - this.xCushion_ = 0;
|
| +
|
| + // BarChart allows bars to have arbitrary, non-uniform widths. Bars need
|
| + // not all be the same width. The width of each bar is automatically
|
| + // computed from the bar's x-coordinate and that of the next bar, which
|
| + // can not define the width of the last bar. This is the width (in the
|
| + // xScale's domain (as opposed to the xScale's range (which is measured in
|
| + // pixels))) of the last bar. When there are at least 2 bars, this is
|
| + // computed as the average width of the bars. When there is a single bar,
|
| + // this must default to a non-zero number so that the width of the only
|
| + // bar will not be zero.
|
| + this.xCushion_ = 1;
|
| +
|
| + this.isStacked_ = false;
|
| + },
|
| +
|
| + set isStacked(stacked) {
|
| + this.isStacked_ = true;
|
| + this.updateContents_();
|
| + },
|
| +
|
| + get isStacked() {
|
| + return this.isStacked_;
|
| },
|
|
|
| isDatumFieldSeries_: function(fieldName) {
|
| @@ -50,11 +71,11 @@ tr.exportTo('tr.ui.b', function() {
|
| xDifferences += currentX - previousX;
|
| }
|
|
|
| - this.seriesKeys_.forEach(function(key) {
|
| + for (var [key, series] of this.seriesByKey_) {
|
| // Allow for sparse data
|
| if (datum[key] !== undefined)
|
| yRange.addValue(datum[key]);
|
| - });
|
| + }
|
| }, this);
|
|
|
| // X.
|
| @@ -64,7 +85,8 @@ tr.exportTo('tr.ui.b', function() {
|
| var width = this.chartAreaSize.width;
|
| this.xScale_.range([0, width]);
|
| var domain = d3.extent(this.data_, this.getXForDatum_.bind(this));
|
| - this.xCushion_ = xDifferences / (this.data_.length - 1);
|
| + if (this.data_.length > 1)
|
| + this.xCushion_ = xDifferences / (this.data_.length - 1);
|
| this.xScale_.domain([domain[0], domain[1] + this.xCushion_]);
|
|
|
| // Y.
|
| @@ -72,9 +94,75 @@ tr.exportTo('tr.ui.b', function() {
|
| this.yScale_.domain(this.getYScaleDomain_(yRange.min, yRange.max));
|
| },
|
|
|
| + getYScaleDomain_: function(minValue, maxValue) {
|
| + if (!this.isStacked) {
|
| + return ChartBase2DBrushX.prototype.getYScaleDomain_.call(
|
| + this, minValue, maxValue);
|
| + }
|
| +
|
| + var range = new tr.b.Range();
|
| + range.addValue(0);
|
| + this.data_.forEach(function(datum, index) {
|
| + var sum = 0;
|
| + for (var [key, series] of this.seriesByKey_) {
|
| + if (datum[key] === undefined)
|
| + continue;
|
| + sum += datum[key];
|
| + }
|
| + range.addValue(sum);
|
| + }, this);
|
| + return [range.min, range.max];
|
| + },
|
| +
|
| + getStackedRectsForDatum_: function(datum, index) {
|
| + var stacks = [];
|
| + var bottom = this.yScale_.range()[0];
|
| + var sum = 0;
|
| + for (var [key, series] of this.seriesByKey_) {
|
| + if (datum[key] === undefined || !this.isSeriesEnabled(key))
|
| + continue;
|
| + sum += datum[key];
|
| + var heightPx = bottom - this.yScale_(sum);
|
| + bottom -= heightPx;
|
| + stacks.push({
|
| + key: key,
|
| + value: datum[key],
|
| + color: getColorOfKey(key),
|
| + heightPx: heightPx,
|
| + topPx: bottom
|
| + });
|
| + }
|
| + return stacks;
|
| + },
|
| +
|
| + getRectsForDatum_: function(datum, index) {
|
| + if (this.isStacked)
|
| + return this.getStackedRectsForDatum_(datum, index);
|
| +
|
| + var stacks = [];
|
| + for (var [key, series] of this.seriesByKey_) {
|
| + if (datum[key] === undefined || !this.isSeriesEnabled(key))
|
| + continue;
|
| + var topPx = this.yScale_(Math.max(datum[key], this.getYScaleMin_()));
|
| + stacks.push({
|
| + key: key,
|
| + value: datum[key],
|
| + topPx: topPx,
|
| + heightPx: this.yScale_.range()[0] - topPx,
|
| + color: getColorOfKey(key)
|
| + });
|
| + }
|
| + stacks.sort(function(a, b) {
|
| + return b.topPx - a.topPx;
|
| + });
|
| + return stacks;
|
| + },
|
| +
|
| updateDataContents_: function(dataSel) {
|
| dataSel.selectAll('*').remove();
|
| - var rectsSel = dataSel.selectAll('path').data(this.seriesKeys_);
|
| + var chartAreaSel = d3.select(this.chartAreaElement);
|
| + var rectsSel = dataSel.selectAll('path').data(
|
| + [...this.seriesByKey_.keys()]);
|
| this.data_.forEach(function(datum, index) {
|
| var currentX = this.getXForDatum_(datum, index);
|
| var width = undefined;
|
| @@ -84,28 +172,45 @@ tr.exportTo('tr.ui.b', function() {
|
| } else {
|
| width = this.xCushion_;
|
| }
|
| -
|
| - var stacks = [];
|
| - this.seriesKeys_.forEach(function(key) {
|
| - if (datum[key] !== undefined)
|
| - stacks.push({y: datum[key], color: getColorOfKey(key)});
|
| - });
|
| - stacks.sort(function(a, b) {
|
| - return b.y - a.y;
|
| - });
|
| -
|
| - stacks.forEach(function(stack) {
|
| - var left = this.xScale_(currentX);
|
| - var right = this.xScale_(currentX + width);
|
| - var widthPx = right - left;
|
| - var top = this.yScale_(Math.max(stack.y, this.getYScaleMin_()));
|
| + this.getRectsForDatum_(datum, index).forEach(function(rect) {
|
| + var leftPx = this.xScale_(currentX);
|
| + var rightPx = this.xScale_(currentX + width);
|
| + var widthPx = rightPx - leftPx;
|
| rectsSel.enter()
|
| .append('rect')
|
| - .attr('fill', stack.color)
|
| - .attr('x', left)
|
| - .attr('y', top)
|
| + .attr('fill', rect.color)
|
| + .attr('x', leftPx)
|
| + .attr('y', rect.topPx)
|
| .attr('width', widthPx)
|
| - .attr('height', this.yScale_.range()[0] - top);
|
| + .attr('height', rect.heightPx)
|
| + .on('mouseenter', function() {
|
| + chartAreaSel.selectAll('.hover').remove();
|
| + chartAreaSel
|
| + .append('rect')
|
| + .attr('class', 'hover')
|
| + .attr('fill', 'white')
|
| + .attr('x', leftPx + widthPx)
|
| + .attr('y', rect.topPx)
|
| + .attr('width', this.margin.right)
|
| + .attr('height', 30);
|
| + chartAreaSel
|
| + .append('text')
|
| + .attr('class', 'hover')
|
| + .attr('fill', rect.color)
|
| + .attr('x', leftPx + widthPx + 2)
|
| + .attr('y', rect.topPx + 10)
|
| + .text(rect.key);
|
| + chartAreaSel
|
| + .append('text')
|
| + .attr('class', 'hover')
|
| + .attr('fill', rect.color)
|
| + .attr('x', leftPx + widthPx + 2)
|
| + .attr('y', rect.topPx + 25)
|
| + .text(rect.value);
|
| + }.bind(this))
|
| + .on('mouseleave', function() {
|
| + chartAreaSel.selectAll('.hover').remove();
|
| + }.bind(this));
|
| }, this);
|
| }, this);
|
| rectsSel.exit().remove();
|
|
|