in packages/charts/src/chart_types/xy_chart/rendering/bars.ts [34:170]
export function renderBars(
measureText: TextMeasure,
orderIndex: number,
dataSeries: DataSeries,
xScale: ScaleContinuous | ScaleBand,
yScale: ScaleContinuous,
panel: Dimensions,
chartRotation: number,
minBarHeight: number,
color: Color,
isBandedSpec: boolean,
sharedSeriesStyle: BarSeriesStyle,
displayValueSettings?: DisplayValueSpec,
styleAccessor?: BarStyleAccessor,
stackMode?: StackMode,
): BarTuple {
const { fontSize, fontFamily } = sharedSeriesStyle.displayValue;
const initialBarTuple: BarTuple = { barGeometries: [], indexedGeometryMap: new IndexedGeometryMap() } as BarTuple;
const y1Fn = getY1ScaledValueFn(yScale);
const y0Fn = getY0ScaledValueFn(yScale);
return dataSeries.data.reduce((barTuple: BarTuple, datum) => {
const xScaled = xScale.scale(datum.x);
if (!xScale.isValueInDomain(datum.x) || Number.isNaN(xScaled)) {
return barTuple; // don't create a bar if not within the xScale domain
}
const { barGeometries, indexedGeometryMap } = barTuple;
const { y1, initialY1, filled } = datum;
const y1Scaled = y1Fn(datum);
const y0Scaled = y0Fn(datum);
// orientation independent height
const yDiff = Math.abs(y1Scaled - y0Scaled);
// amount required to reach the minBarHeight requested
const addedMinBarHeight = yDiff === 0 || yDiff >= minBarHeight ? 0 : minBarHeight - yDiff;
// the y coordinate in screen-space.
const yScreenSpaceCoord =
Math.min(y1Scaled, y0Scaled) +
// adding half of the required minBarHeight if banded bar chart
// and reduce the y coordinate if the Y value is positive to render correctly the increased bar height
(isBandedSpec ? -addedMinBarHeight / 2 : -addedMinBarHeight * ((y1 ?? 0) >= 0 ? 1 : 0));
// the actual height of the bar
const height = yDiff + addedMinBarHeight;
const seriesIdentifier: XYChartSeriesIdentifier = getSeriesIdentifierFromDataSeries(dataSeries);
const seriesStyle = getBarStyleOverrides(datum, seriesIdentifier, sharedSeriesStyle, styleAccessor);
const maxPixelWidth = clamp(seriesStyle.rect.widthRatio ?? 1, 0, 1) * xScale.bandwidth;
const minPixelWidth = clamp(seriesStyle.rect.widthPixel ?? 0, 0, maxPixelWidth);
const width = clamp(seriesStyle.rect.widthPixel ?? xScale.bandwidth, minPixelWidth, maxPixelWidth);
const x = xScaled + xScale.bandwidth * orderIndex + xScale.bandwidth / 2 - width / 2;
const y1Value = getDatumYValue(datum, false, isBandedSpec, stackMode);
const formattedDisplayValue = displayValueSettings?.valueFormatter?.(y1Value);
// only show displayValue for even bars if showOverlappingValue
const displayValueText =
displayValueSettings?.isAlternatingValueLabel && barGeometries.length % 2 ? undefined : formattedDisplayValue;
const { displayValueWidth, fixedFontScale } = computeBoxWidth(displayValueText ?? '', {
padding: PADDING,
fontSize,
fontFamily,
measureText,
});
const isHorizontalRotation = chartRotation % 180 === 0;
// Pick the right side of the label's box to use as factor reference
const referenceWidth = Math.max(isHorizontalRotation ? displayValueWidth : fixedFontScale, 1);
const textScalingFactor = getFinalFontScalingFactor(
(width * FONT_SIZE_FACTOR) / referenceWidth,
fixedFontScale,
fontSize,
);
const overflowConstraints: Set<LabelOverflowConstraint> = new Set(
displayValueSettings?.overflowConstraints ?? [
LabelOverflowConstraint.ChartEdges,
LabelOverflowConstraint.BarGeometry,
],
);
// Based on rotation scale the width of the text box
const bboxWidthFactor = isHorizontalRotation ? textScalingFactor : 1;
const displayValue: BarGeometry['displayValue'] | undefined =
displayValueText && displayValueSettings?.showValueLabel
? {
fontScale: textScalingFactor,
fontSize: fixedFontScale,
text: displayValueText,
width: bboxWidthFactor * displayValueWidth,
height: textScalingFactor * fixedFontScale,
overflowConstraints,
}
: undefined;
const barGeometry: BarGeometry = {
displayValue,
x,
y: yScreenSpaceCoord,
transform: { x: 0, y: 0 },
width,
height,
color,
value: { x: datum.x, y: y1Value, mark: null, accessor: BandedAccessorType.Y1, datum: datum.datum },
seriesIdentifier,
seriesStyle,
panel,
};
if (isBandedSpec) {
// index also the Y0 value with the same geometry
indexedGeometryMap.set({
...barGeometry,
value: {
x: datum.x,
y: getDatumYValue(datum, true, isBandedSpec, stackMode),
mark: null,
accessor: BandedAccessorType.Y0,
datum: datum.datum,
},
});
}
indexedGeometryMap.set(barGeometry);
if (y1 !== null && initialY1 !== null && filled?.y1 === undefined) {
barGeometries.push(barGeometry);
}
return barTuple;
}, initialBarTuple);
}