Skip to content

Commit a8a05e7

Browse files
Anush2303Anush
andauthored
fix(react-charts): fixed text mode for scatter polar (microsoft#35104)
Co-authored-by: Anush <[email protected]>
1 parent a37f131 commit a8a05e7

File tree

6 files changed

+34
-39
lines changed

6 files changed

+34
-39
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "fix text mode for scatter polar",
4+
"packageName": "@fluentui/react-charts",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

packages/charts/react-charts/library/src/components/DeclarativeChart/DeclarativeChart.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,7 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
409409
const mode = (plotlyInputWithValidData.data[index] as PlotData)?.mode ?? '';
410410
if (mode.includes('line')) {
411411
validTracesFilteredIndex[index].type = 'line';
412-
} else if (mode.includes('markers')) {
412+
} else if (mode.includes('markers') || mode === 'text') {
413413
validTracesFilteredIndex[index].type = 'scatter';
414414
} else {
415415
validTracesFilteredIndex[index].type = 'line';

packages/charts/react-charts/library/src/components/DeclarativeChart/PlotlySchemaAdapter.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,9 @@ const transformPlotlyJsonToScatterTraceProps = (
854854
originXOffset: (input.layout as { __polarOriginX?: number } | undefined)?.__polarOriginX,
855855
direction: input.layout?.polar?.angularaxis?.direction,
856856
rotation: input.layout?.polar?.angularaxis?.rotation,
857+
axisLabel: (series as { __axisLabel: string[] }).__axisLabel
858+
? (series as { __axisLabel: string[] }).__axisLabel
859+
: {},
857860
}
858861
: {}),
859862
},
@@ -1510,7 +1513,7 @@ export const transformPlotlyJsonToChartTableProps = (
15101513
export const projectPolarToCartesian = (input: PlotlySchema): PlotlySchema => {
15111514
const projection: PlotlySchema = { ...input };
15121515

1513-
// 1. Find the global min and max radius across all series
1516+
// Find the global min and max radius across all series
15141517
let minRadius = 0;
15151518
let maxRadius = 0;
15161519
for (let sindex = 0; sindex < input.data.length; sindex++) {
@@ -1525,18 +1528,27 @@ export const projectPolarToCartesian = (input: PlotlySchema): PlotlySchema => {
15251528
}
15261529
}
15271530

1528-
// 2. If there are negative radii, compute the shift
1531+
// If there are negative radii, compute the shift
15291532
const radiusShift = minRadius < 0 ? -minRadius : 0;
15301533

1531-
// 3. Project all points and create a perfect square domain
1534+
// Collect all unique theta values from all scatterpolar series for equal spacing
1535+
const allThetaValues: Set<string> = new Set();
1536+
for (let sindex = 0; sindex < input.data.length; sindex++) {
1537+
const series = input.data[sindex] as Partial<PlotData>;
1538+
if (series.theta && isArrayOrTypedArray(series.theta)) {
1539+
series.theta.forEach(theta => allThetaValues.add(String(theta)));
1540+
}
1541+
}
1542+
1543+
// Project all points and create a perfect square domain
15321544
const allX: number[] = [];
15331545
const allY: number[] = [];
15341546
let originX: number | null = null;
15351547
for (let sindex = 0; sindex < input.data.length; sindex++) {
15361548
const series = input.data[sindex] as Partial<PlotData>;
1537-
// If scatterpolar, set text to theta values as strings
1538-
if (series.type === 'scatterpolar' && Array.isArray(series.theta)) {
1539-
series.text = series.theta.map(v => String(v));
1549+
// If scatterpolar, set __axisLabel to all unique theta values for equal spacing
1550+
if (isArrayOrTypedArray(series.theta)) {
1551+
(series as { __axisLabel: string[] }).__axisLabel = Array.from(allThetaValues);
15401552
}
15411553
series.x = [] as Datum[];
15421554
series.y = [] as Datum[];
@@ -1566,7 +1578,7 @@ export const projectPolarToCartesian = (input: PlotlySchema): PlotlySchema => {
15661578
continue;
15671579
}
15681580

1569-
// 4. Map theta to angle in radians
1581+
// Map theta to angle in radians
15701582
let thetaRad: number;
15711583
if (categorical) {
15721584
const idx = uniqueTheta.indexOf(thetas[ptindex]);
@@ -1575,10 +1587,10 @@ export const projectPolarToCartesian = (input: PlotlySchema): PlotlySchema => {
15751587
} else {
15761588
thetaRad = startAngleInRad + dirMultiplier * (((thetas[ptindex] as number) * Math.PI) / 180);
15771589
}
1578-
// 5. Shift only the polar origin (not the cartesian)
1590+
// Shift only the polar origin (not the cartesian)
15791591
const rawRadius = rVals[ptindex] as number;
15801592
const polarRadius = rawRadius + radiusShift; // Only for projection
1581-
// 6. Calculate cartesian coordinates (with shifted polar origin)
1593+
// Calculate cartesian coordinates (with shifted polar origin)
15821594
const x = polarRadius * Math.cos(thetaRad);
15831595
const y = polarRadius * Math.sin(thetaRad);
15841596

packages/charts/react-charts/library/src/components/LineChart/LineChart.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,15 +1179,8 @@ export const LineChart: React.FunctionComponent<LineChartProps> = React.forwardR
11791179
}
11801180

11811181
if (_isScatterPolar) {
1182-
// Render category labels for all series at once to avoid overlap
1183-
const allSeriesData = _points.map(series => ({
1184-
data: series.data
1185-
.filter(pt => typeof pt.x === 'number' && typeof pt.y === 'number')
1186-
.map(pt => ({ x: pt.x as number, y: pt.y as number, text: pt.text })),
1187-
}));
11881182
pointsForLine.push(
11891183
...renderScatterPolarCategoryLabels({
1190-
allSeriesData,
11911184
xAxisScale: _xAxisScale,
11921185
yAxisScale: yScale,
11931186
className: classes.markerLabel || '',

packages/charts/react-charts/library/src/components/ScatterChart/ScatterChart.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -458,15 +458,8 @@ export const ScatterChart: React.FunctionComponent<ScatterChartProps> = React.fo
458458
}
459459

460460
if (_isScatterPolarRef.current) {
461-
// Render category labels for all series at once to avoid overlap
462-
const allSeriesData = _points.map(s => ({
463-
data: s.data
464-
.filter(pt => typeof pt.x === 'number' && typeof pt.y === 'number')
465-
.map(pt => ({ x: pt.x as number, y: pt.y as number, text: pt.text })),
466-
}));
467461
pointsForSeries.push(
468462
...renderScatterPolarCategoryLabels({
469-
allSeriesData,
470463
xAxisScale: _xAxisScale.current,
471464
yAxisScale: _yAxisScale.current,
472465
className: classes.markerLabel || '',

packages/charts/react-charts/library/src/utilities/scatterpolar-utils.tsx

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,12 @@ import { JSXElement } from '@fluentui/react-utilities';
77
* Now places labels at equal angles for all unique texts, regardless of data positions.
88
*/
99
export function renderScatterPolarCategoryLabels({
10-
allSeriesData,
1110
xAxisScale,
1211
yAxisScale,
1312
className,
1413
lineOptions,
1514
minPixelGap = 40,
1615
}: {
17-
allSeriesData: { data: { x: number; y: number; text?: string }[] }[];
1816
xAxisScale: ScaleLinear<number, number>;
1917
yAxisScale: ScaleLinear<number, number>;
2018
className: string;
@@ -24,20 +22,10 @@ export function renderScatterPolarCategoryLabels({
2422
}): JSXElement[] {
2523
const maybeLineOptions = extractMaybeLineOptions(lineOptions);
2624

27-
// 1. Aggregate all data points from all series
28-
const allLabels: { x: number; y: number; text: string }[] = [];
29-
allSeriesData.forEach(series => {
30-
series.data.forEach(pt => {
31-
if (pt.text) {
32-
allLabels.push({ x: pt.x, y: pt.y, text: pt.text });
33-
}
34-
});
35-
});
36-
37-
// 2. Deduplicate by text (angle label)
38-
const uniqueTexts = Array.from(new Set(allLabels.map(l => l.text)));
25+
// Always use axisLabel from lineOptions to display the labels
26+
const uniqueTexts: string[] = maybeLineOptions?.axisLabel ?? [];
3927

40-
// 3. Place labels at equal angles
28+
// Place labels at equal angles
4129
const renderedLabels: JSXElement[] = [];
4230
const placedPositions: { x: number; y: number }[] = [];
4331
const labelRadius = 0.7; // You can adjust this value for more/less offset
@@ -83,6 +71,7 @@ export function extractMaybeLineOptions(lineOptions: any):
8371
originXOffset?: number;
8472
direction?: 'clockwise' | 'counterclockwise';
8573
rotation?: number;
74+
axisLabel?: string[];
8675
}
8776
| undefined {
8877
return lineOptions
@@ -93,6 +82,7 @@ export function extractMaybeLineOptions(lineOptions: any):
9382
? lineOptions.direction
9483
: undefined,
9584
rotation: lineOptions.rotation,
85+
axisLabel: Array.isArray(lineOptions.axisLabel) ? lineOptions.axisLabel : undefined,
9686
}
9787
: undefined;
9888
}

0 commit comments

Comments
 (0)