This is an automated email from the ASF dual-hosted git repository.

susiwen8 pushed a commit to branch codex/issue-21133-sunburst-label-center-clean
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 0f082e65da0dd074bf4bc56648b5c90be8ac9d11
Author: susiwen8 <[email protected]>
AuthorDate: Sat May 2 11:02:09 2026 +0800

    Keep drilled sunburst root labels visually centered
    
    A drilled sunburst view root can become a full annular sector when 
rootToNode is triggered. In that state the label represents the centered root, 
not an angular slice, so the label should sit on the chart center without 
inheriting radial rotation.
    
    Constraint: Preserve existing radial and tangential rotation for ordinary 
sunburst sectors and outside labels.
    Rejected: Keep using the annulus midpoint | it leaves full-circle drilldown 
labels offset from the visual root.
    Rejected: Only adjust x/y | the label can remain rotated by the previous 
radial orientation.
    Confidence: high
    Scope-risk: narrow
    Directive: Do not reapply radial rotation to labels placed at the center of 
a full-circle sunburst sector.
    Tested: git diff --check
    Tested: eslint src/chart/sunburst/SunburstPiece.ts 
test/ut/spec/series/sunburst.test.ts
    Tested: tsc --noEmit
    Tested: jest --config test/ut/jest.config.cjs --coverage=false --runInBand 
test/ut/spec/series
    Not-tested: Full visual screenshot baseline suite
---
 src/chart/sunburst/SunburstPiece.ts  |  12 +++--
 test/ut/spec/series/sunburst.test.ts | 102 +++++++++++++++++++++++++++++++++++
 2 files changed, 111 insertions(+), 3 deletions(-)

diff --git a/src/chart/sunburst/SunburstPiece.ts 
b/src/chart/sunburst/SunburstPiece.ts
index edff51730..cd8240373 100644
--- a/src/chart/sunburst/SunburstPiece.ts
+++ b/src/chart/sunburst/SunburstPiece.ts
@@ -220,6 +220,8 @@ class SunburstPiece extends graphic.Sector {
             const flipStartAngle = Math.PI * 0.5;
             const flipEndAngle = Math.PI * 1.5;
             const midAngleNormal = normalizeRadian(rotateType === 'tangential' 
? Math.PI / 2 - midAngle : midAngle);
+            const isFullCircle = isRadianAroundZero(Math.abs(angle) - 2 * 
Math.PI);
+            let isLabelAtCenter = false;
 
             // For text that is up-side down, rotate 180 degrees to make sure
             // it's readable
@@ -233,9 +235,10 @@ class SunburstPiece extends graphic.Sector {
             }
             else {
                 if (!textAlign || textAlign === 'center') {
-                    // Put label in the center if it's a circle
-                    if (layout.r0 === 0 && isRadianAroundZero(angle - 2 * 
Math.PI)) {
+                    // Put label in the center if it's a full circle.
+                    if (isFullCircle) {
                         r = 0;
+                        isLabelAtCenter = true;
                     }
                     else {
                         r = (layout.r + layout.r0) / 2;
@@ -259,7 +262,10 @@ class SunburstPiece extends graphic.Sector {
             state.y = r * dy + layout.cy;
 
             let rotate = 0;
-            if (rotateType === 'radial') {
+            if (isLabelAtCenter) {
+                rotate = 0;
+            }
+            else if (rotateType === 'radial') {
                 rotate = normalizeRadian(-midAngle)
                     + (needsFlip ? Math.PI : 0);
             }
diff --git a/test/ut/spec/series/sunburst.test.ts 
b/test/ut/spec/series/sunburst.test.ts
new file mode 100644
index 000000000..cb9a8f6f7
--- /dev/null
+++ b/test/ut/spec/series/sunburst.test.ts
@@ -0,0 +1,102 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*   http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied.  See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+import { EChartsType } from '@/src/echarts';
+import SunburstSeriesModel from '@/src/chart/sunburst/SunburstSeries';
+import { TreeNode } from '@/src/data/Tree';
+import { createChart, getECModel } from '../../core/utHelper';
+
+function findNodeByName(seriesModel: SunburstSeriesModel, name: string): 
TreeNode {
+    let targetNode: TreeNode;
+
+    seriesModel.getData().tree.root.eachNode(node => {
+        if (node.name === name) {
+            targetNode = node;
+        }
+    });
+
+    return targetNode;
+}
+
+describe('series/sunburst', function () {
+
+    let chart: EChartsType;
+
+    beforeEach(function () {
+        chart = createChart({
+            width: 400,
+            height: 400
+        });
+    });
+
+    afterEach(function () {
+        chart.dispose();
+    });
+
+    it('centers label when rootToNode makes an annular sector full circle', 
function () {
+        chart.setOption({
+            animation: false,
+            series: {
+                type: 'sunburst',
+                radius: [0, '80%'],
+                label: {
+                    show: true,
+                    align: 'center'
+                },
+                data: [
+                    {
+                        name: '华南',
+                        children: [
+                            {name: '广东', value: 2},
+                            {name: '广西', value: 1}
+                        ]
+                    },
+                    {
+                        name: '华北',
+                        children: [
+                            {name: '北京', value: 2},
+                            {name: '天津', value: 1}
+                        ]
+                    }
+                ]
+            }
+        });
+
+        let seriesModel = getECModel(chart).getSeriesByType('sunburst')[0] as 
SunburstSeriesModel;
+        const targetNode = findNodeByName(seriesModel, '华南');
+
+        chart.dispatchAction({
+            type: 'sunburstRootToNode',
+            targetNode: targetNode
+        });
+
+        seriesModel = getECModel(chart).getSeriesByType('sunburst')[0] as 
SunburstSeriesModel;
+        const viewRoot = seriesModel.getViewRoot();
+        const layout = viewRoot.getLayout();
+        const label = 
seriesModel.getData().getItemGraphicEl(viewRoot.dataIndex).getTextContent();
+
+        expect(viewRoot.name).toBe('华南');
+        expect(layout.r0).toBeGreaterThan(0);
+        expect(Math.abs(layout.endAngle - 
layout.startAngle)).toBeCloseTo(Math.PI * 2);
+        expect(label.x).toBeCloseTo(layout.cx);
+        expect(label.y).toBeCloseTo(layout.cy);
+        expect(label.rotation).toBeCloseTo(0);
+    });
+
+});


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to