in packages/charts/src/chart_types/xy_chart/state/utils/utils.ts [278:504]
function renderGeometries(
dataSeries: DataSeries[],
xDomain: XDomain,
yScales: Map<GroupId, ScaleContinuous>,
smVScale: ScaleBand,
smHScale: ScaleBand,
barIndexOrderPerPanel: Record<string, string[]>,
seriesSpecs: BasicSeriesSpec[],
seriesColorsMap: Map<SeriesKey, Color>,
defaultColor: string,
axesSpecs: AxisSpec[],
chartTheme: Theme,
enableHistogramMode: boolean,
chartRotation: Rotation,
fallBackTickFormatter: TickFormatter,
measureText: TextMeasure,
): Omit<ComputedGeometries, 'scales'> {
const bars: Array<PerPanel<BarGeometry[]>> = [];
const areas: Array<PerPanel<AreaGeometry>> = [];
const lines: Array<PerPanel<LineGeometry>> = [];
const bubbles: Array<PerPanel<BubbleGeometry>> = [];
const geometriesIndex = new IndexedGeometryMap();
const isMixedChart = isUniqueArray(seriesSpecs, ({ seriesType }) => seriesType) && seriesSpecs.length > 1;
const geometriesCounts: GeometriesCounts = {
points: 0,
bars: 0,
areas: 0,
areasPoints: 0,
lines: 0,
linePoints: 0,
bubbles: 0,
bubblePoints: 0,
};
const barsPadding = enableHistogramMode ? chartTheme.scales.histogramPadding : chartTheme.scales.barsPadding;
// This var remains Infinity if we don't have points, or we just have a single point per series.
// In this case the point should be visible if the visibility style is set to `auto`
let globalMinPointsDistance = Infinity;
dataSeries.forEach((ds) => {
const spec = getSpecsById<BasicSeriesSpec>(seriesSpecs, ds.specId);
if (spec === undefined) {
return;
}
// compute the y scale
const yScale = yScales.get(getSpecDomainGroupId(ds.spec));
if (!yScale) {
return;
}
// compute the panel unique key
const barPanelKey = [ds.smVerticalAccessorValue, ds.smHorizontalAccessorValue].join('|');
const barIndexOrder = barIndexOrderPerPanel[barPanelKey] ?? [];
// compute x scale
const xScale = computeXScale({
xDomain,
totalBarsInCluster: barIndexOrder?.length ?? 0,
range: [0, isHorizontalRotation(chartRotation) ? smHScale.bandwidth : smVScale.bandwidth],
barsPadding,
enableHistogramMode,
});
const { stackMode } = ds;
const leftPos = (!isNil(ds.smHorizontalAccessorValue) && smHScale.scale(ds.smHorizontalAccessorValue)) || 0;
const topPos = (!isNil(ds.smVerticalAccessorValue) && smVScale.scale(ds.smVerticalAccessorValue)) || 0;
const panel: Dimensions = {
width: smHScale.bandwidth,
height: smVScale.bandwidth,
top: topPos,
left: leftPos,
};
const dataSeriesKey = getSeriesKey(
{
specId: ds.specId,
yAccessor: ds.yAccessor,
splitAccessors: ds.splitAccessors,
},
ds.groupId,
);
const color = seriesColorsMap.get(dataSeriesKey) || defaultColor;
if (isBarSeriesSpec(spec)) {
const shift = barIndexOrder.indexOf(getBarIndexKey(ds));
if (shift === -1) return; // skip bar dataSeries if index is not available
const barSeriesStyle = mergePartial(chartTheme.barSeriesStyle, spec.barSeriesStyle);
const { yAxis } = getAxesSpecForSpecId(axesSpecs, spec.groupId, chartRotation);
const valueFormatter = yAxis?.tickFormat ?? fallBackTickFormatter;
const displayValueSettings = spec.displayValueSettings
? { valueFormatter, ...spec.displayValueSettings }
: undefined;
const renderedBars = renderBars(
measureText,
shift,
ds,
xScale,
yScale,
panel,
chartRotation,
spec.minBarHeight ?? 0,
color,
isBandedSpec(spec),
barSeriesStyle,
displayValueSettings,
spec.styleAccessor,
stackMode,
);
geometriesIndex.merge(renderedBars.indexedGeometryMap);
bars.push({ panel, value: renderedBars.barGeometries });
geometriesCounts.bars += renderedBars.barGeometries.length;
} else if (isBubbleSeriesSpec(spec)) {
const bubbleShift = barIndexOrder && barIndexOrder.length > 0 ? barIndexOrder.length : 1;
const bubbleSeriesStyle = spec.bubbleSeriesStyle
? mergePartial(chartTheme.bubbleSeriesStyle, spec.bubbleSeriesStyle)
: chartTheme.bubbleSeriesStyle;
const xScaleOffset = computeXScaleOffset(xScale, enableHistogramMode);
const renderedBubbles = renderBubble(
(xScale.bandwidth * bubbleShift) / 2,
ds,
xScale,
yScale,
color,
panel,
isBandedSpec(spec),
xScaleOffset,
bubbleSeriesStyle,
{
enabled: spec.markSizeAccessor !== undefined,
ratio: chartTheme.markSizeRatio,
},
isMixedChart,
spec.pointStyleAccessor,
);
geometriesIndex.merge(renderedBubbles.indexedGeometryMap);
bubbles.push({
panel,
value: renderedBubbles.bubbleGeometry,
});
geometriesCounts.bubblePoints += renderedBubbles.bubbleGeometry.points.length;
geometriesCounts.bubbles += 1;
} else if (isLineSeriesSpec(spec)) {
const lineShift = barIndexOrder && barIndexOrder.length > 0 ? barIndexOrder.length : 1;
const lineSeriesStyle = getLineSeriesStyles(chartTheme.lineSeriesStyle, spec.lineSeriesStyle);
const xScaleOffset = computeXScaleOffset(xScale, enableHistogramMode, spec.histogramModeAlignment);
const renderedLines = renderLine(
// move the point on half of the bandwidth if we have mixed bars/lines
(xScale.bandwidth * lineShift) / 2,
ds,
xScale,
yScale,
panel,
color,
spec.curve || CurveType.LINEAR,
isBandedSpec(spec),
xScaleOffset,
lineSeriesStyle,
{
enabled: spec.markSizeAccessor !== undefined && lineSeriesStyle.point.visible !== 'never',
ratio: chartTheme.markSizeRatio,
},
hasFitFnConfigured(spec.fit),
spec.pointStyleAccessor,
);
geometriesIndex.merge(renderedLines.indexedGeometryMap);
lines.push({
panel,
value: renderedLines.lineGeometry,
});
geometriesCounts.linePoints += renderedLines.lineGeometry.points.length;
geometriesCounts.lines += 1;
globalMinPointsDistance = Math.min(globalMinPointsDistance, renderedLines.lineGeometry.minPointDistance);
} else if (isAreaSeriesSpec(spec)) {
const areaShift = barIndexOrder && barIndexOrder.length > 0 ? barIndexOrder.length : 1;
const areaSeriesStyle = getAreaSeriesStyles(chartTheme.areaSeriesStyle, spec.areaSeriesStyle);
const xScaleOffset = computeXScaleOffset(xScale, enableHistogramMode, spec.histogramModeAlignment);
const renderedArea = renderArea(
// move the point on half of the bandwidth if we have mixed bars/lines
(xScale.bandwidth * areaShift) / 2,
ds,
xScale,
yScale,
panel,
color,
spec.curve || CurveType.LINEAR,
isBandedSpec(spec),
xScaleOffset,
areaSeriesStyle,
{
enabled: spec.markSizeAccessor !== undefined && areaSeriesStyle.point.visible !== 'never',
ratio: chartTheme.markSizeRatio,
},
spec.stackAccessors ? spec.stackAccessors.length > 0 : false,
hasFitFnConfigured(spec.fit),
spec.pointStyleAccessor,
);
geometriesIndex.merge(renderedArea.indexedGeometryMap);
areas.push({
panel,
value: renderedArea.areaGeometry,
});
geometriesCounts.areasPoints += renderedArea.areaGeometry.points.length;
geometriesCounts.areas += 1;
globalMinPointsDistance = Math.min(globalMinPointsDistance, renderedArea.areaGeometry.minPointDistance);
}
});
return {
geometries: {
bars,
areas: areas.map((a) => {
a.value.minPointDistance = globalMinPointsDistance;
return a;
}),
lines: lines.map((l) => {
l.value.minPointDistance = globalMinPointsDistance;
return l;
}),
bubbles,
},
geometriesIndex,
geometriesCounts,
};
}