Skip to content

Commit 91752ff

Browse files
Anexus5919alexfauquetteCopilot
authored
[charts] Save full-circle flag on polar point-scale rotation axis (#22162)
Co-authored-by: alex <alex.fauquette@gmail.com> Co-authored-by: Copilot <copilot@github.com>
1 parent c16ac09 commit 91752ff

4 files changed

Lines changed: 34 additions & 44 deletions

File tree

packages/x-charts/src/ChartsRadialGrid/ChartsRadialGrid.tsx

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { GridRoot } from './styledComponents';
1111
import { ChartsRotationGrid } from './ChartsRotationGrid';
1212
import { ChartsRadiusGrid } from './ChartsRadiusGrid';
1313
import { useRotationAxes, useRadiusAxes } from '../hooks/useAxis';
14-
import { EPSILON } from '../utils/epsilon';
1514

1615
const useUtilityClasses = ({ classes }: ChartsRadialGridProps) => {
1716
const slots = {
@@ -67,20 +66,8 @@ function ChartsRadialGrid(inProps: ChartsRadialGridProps) {
6766
const outerRadius = radiusAxisConfig?.scale.range()[1] ?? 0;
6867

6968
const startAngle = rotationAxisConfig?.scale.range()[0] ?? 0;
70-
let endAngle = rotationAxisConfig?.scale.range()[1] ?? 0;
71-
72-
if (rotationAxisConfig.scaleType === 'point') {
73-
// The rotation gap we add between the first and last point.
74-
const dataRotationGap = (2 * Math.PI) / rotationAxisConfig.data!.length;
75-
76-
// The missing angle between the last and first point.
77-
const angleGap = 2 * Math.PI - Math.abs(endAngle - startAngle);
78-
if (Math.abs(angleGap - dataRotationGap) < EPSILON) {
79-
// If they are close enough we close the circle.
80-
// Otherwise it means user deliberately modified the angles and so keep it as it is.
81-
endAngle = startAngle + 2 * Math.PI;
82-
}
83-
}
69+
const endAngle = rotationAxisConfig?.scale.range()[1] ?? 0;
70+
const isFullCircle = rotationAxisConfig?.isFullCircle ?? false;
8471

8572
return (
8673
<GridRoot {...other} className={clsx(classes.root, className)}>
@@ -98,6 +85,7 @@ function ChartsRadialGrid(inProps: ChartsRadialGridProps) {
9885
axis={radiusAxisConfig}
9986
startAngle={startAngle}
10087
endAngle={endAngle}
88+
isFullCircle={isFullCircle}
10189
classes={classes}
10290
/>
10391
)}

packages/x-charts/src/ChartsRadialGrid/ChartsRadiusGrid.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ import {
88
type UseChartPolarAxisSignature,
99
} from '../internals/plugins/featurePlugins/useChartPolarAxis';
1010
import { type PolarAxisDefaultized } from '../models/axis';
11-
import { EPSILON } from '../utils/epsilon';
1211

1312
interface ChartsRadiusGridProps {
1413
axis: PolarAxisDefaultized<any, any, any>;
1514
startAngle: number;
1615
endAngle: number;
16+
isFullCircle: boolean;
1717
classes: Partial<ChartsRadialGridClasses>;
1818
}
1919

@@ -22,7 +22,7 @@ interface ChartsRadiusGridProps {
2222
*/
2323
export function ChartsRadiusGrid(props: ChartsRadiusGridProps) {
2424
const { store } = useChartsContext<[UseChartPolarAxisSignature]>();
25-
const { axis, startAngle, endAngle, classes } = props;
25+
const { axis, startAngle, endAngle, isFullCircle, classes } = props;
2626
const { cx, cy } = store.use(selectorChartPolarCenter);
2727

2828
const { scale, tickNumber, tickInterval, tickSpacing } = axis;
@@ -35,8 +35,6 @@ export function ChartsRadiusGrid(props: ChartsRadiusGridProps) {
3535
direction: 'radius',
3636
});
3737

38-
const isFullCircle = Math.abs(endAngle - startAngle) >= 2 * Math.PI - EPSILON;
39-
4038
if (isFullCircle) {
4139
return (
4240
<React.Fragment>

packages/x-charts/src/internals/plugins/featurePlugins/useChartPolarAxis/computeAxisValue.ts

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import { type AxisConfig, type ScaleName } from '../../../../models';
1+
import type { ContinuousScaleName, AxisConfig, ScaleName } from '../../../../models';
22
import {
33
type ChartsAxisProps,
44
isBandScaleConfig,
55
isPointScaleConfig,
6+
isContinuousScaleConfig,
67
type ChartsRotationAxisProps,
78
type ChartsRadiusAxisProps,
89
type PolarAxisDefaultized,
910
type AxisId,
1011
type PolarAxisConfig,
11-
isContinuousScaleConfig,
12+
type ComputedAxis,
1213
} from '../../../../models/axis';
1314
import {
1415
type ChartSeriesType,
@@ -26,6 +27,7 @@ import { deg2rad } from '../../../angleConversion';
2627
import { getAxisTriggerTooltip } from './getAxisTriggerTooltip';
2728
import { scaleBand, scalePoint } from '../../../scales';
2829
import { type ComputedAxisConfig } from '../useChartCartesianAxis';
30+
import { EPSILON } from '../../../../utils/epsilon';
2931

3032
export type DefaultizedAxisConfig<
3133
AxisProps extends ChartsRotationAxisProps | ChartsRadiusAxisProps,
@@ -40,30 +42,28 @@ function getRange(
4042
drawingArea: ChartDrawingArea,
4143
axisDirection: 'rotation' | 'radius',
4244
axis: PolarAxisConfig<ScaleName, any>,
43-
) {
45+
): { range: number[]; isFullCircle: boolean } {
4446
if (axisDirection === 'rotation') {
45-
if (axis.scaleType === 'point') {
46-
const angles = [
47-
deg2rad((axis as RotationConfig).startAngle, 0),
48-
deg2rad((axis as RotationConfig).endAngle, 2 * Math.PI),
49-
];
50-
const diff = angles[1] - angles[0];
51-
if (diff > Math.PI * 2 - 0.1) {
52-
// If we cover a full circle, we remove a slice to avoid having data point at the same place.
53-
angles[1] -= diff / axis.data!.length;
54-
}
55-
return angles;
56-
}
57-
return [
47+
const angles = [
5848
deg2rad((axis as RotationConfig).startAngle, 0),
5949
deg2rad((axis as RotationConfig).endAngle, 2 * Math.PI),
6050
];
51+
const diff = angles[1] - angles[0];
52+
const isFullCircle = diff >= Math.PI * 2 - EPSILON;
53+
if (axis.scaleType === 'point' && isFullCircle) {
54+
// For point scale, remove a slice to avoid overlapping first and last points.
55+
angles[1] -= diff / axis.data!.length;
56+
}
57+
return { range: angles, isFullCircle };
6158
}
6259
const availableRadius = Math.min(drawingArea.height, drawingArea.width) / 2;
63-
return [
64-
(axis as RadiusConfig).minRadius ?? 0,
65-
(axis as RadiusConfig).maxRadius ?? availableRadius,
66-
];
60+
return {
61+
range: [
62+
(axis as RadiusConfig).minRadius ?? 0,
63+
(axis as RadiusConfig).maxRadius ?? availableRadius,
64+
],
65+
isFullCircle: false,
66+
};
6767
}
6868

6969
const DEFAULT_CATEGORY_GAP_RATIO = 0.2;
@@ -116,10 +116,10 @@ export function computeAxisValue<SeriesType extends ChartSeriesType>({
116116
allAxis[0].id,
117117
);
118118

119-
const completeAxis: DefaultizedAxisConfig<ChartsAxisProps> = {};
119+
const completeAxis: ComputedAxisConfig<ChartsAxisProps> = {};
120120
allAxis.forEach((eachAxis, axisIndex) => {
121121
const axis = eachAxis as Readonly<AxisConfig<ScaleName, any, Readonly<ChartsAxisProps>>>;
122-
const range = getRange(drawingArea, axisDirection, axis);
122+
const { range, isFullCircle } = getRange(drawingArea, axisDirection, axis);
123123

124124
const [minData, maxData] = getAxisExtremum(
125125
axis,
@@ -153,7 +153,8 @@ export function computeAxisValue<SeriesType extends ChartSeriesType>({
153153
(axis.colorMap.type === 'ordinal'
154154
? getOrdinalColorScale({ values: axis.data, ...axis.colorMap })
155155
: getColorScale(axis.colorMap)),
156-
};
156+
isFullCircle,
157+
} as ComputedAxis<'band', any, ChartsAxisProps>;
157158

158159
if (isDateData(axis.data)) {
159160
const dateFormatter = createDateFormatter(axis.data, range, axis.tickNumber);
@@ -173,7 +174,8 @@ export function computeAxisValue<SeriesType extends ChartSeriesType>({
173174
(axis.colorMap.type === 'ordinal'
174175
? getOrdinalColorScale({ values: axis.data, ...axis.colorMap })
175176
: getColorScale(axis.colorMap)),
176-
};
177+
isFullCircle,
178+
} as ComputedAxis<'point', any, ChartsAxisProps>;
177179

178180
if (isDateData(axis.data)) {
179181
const dateFormatter = createDateFormatter(axis.data, range, axis.tickNumber);
@@ -223,7 +225,7 @@ export function computeAxisValue<SeriesType extends ChartSeriesType>({
223225
scale: finalScale.domain(domain) as any,
224226
tickNumber,
225227
colorScale: axis.colorMap && getColorScale(axis.colorMap),
226-
};
228+
} as ComputedAxis<ContinuousScaleName, any, ChartsAxisProps>;
227229
});
228230
return {
229231
axis: completeAxis,

packages/x-charts/src/models/axis.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,8 @@ export type ComputedAxis<
626626
* Indicate if the axis should be consider by a tooltip with `trigger='axis'`.
627627
*/
628628
triggerTooltip?: boolean;
629+
/** @ignore - internal. True when a rotation axis covers a full circle. */
630+
isFullCircle?: boolean;
629631
} & (AxisProps extends ChartsXAxisProps
630632
? AxisSideConfig<AxisProps> & { height: number }
631633
: AxisProps extends ChartsYAxisProps

0 commit comments

Comments
 (0)