You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by ov...@apache.org on 2021/06/18 08:17:01 UTC

[echarts] branch feat-sector-label created (now d15dcf6)

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

ovilia pushed a change to branch feat-sector-label
in repository https://gitbox.apache.org/repos/asf/echarts.git.


      at d15dcf6  feat(label): sector label position #14616

This branch includes the following new commits:

     new d15dcf6  feat(label): sector label position #14616

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org


[echarts] 01/01: feat(label): sector label position #14616

Posted by ov...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ovilia pushed a commit to branch feat-sector-label
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit d15dcf67abc496afa29630d0e2b245b6c752b0f9
Author: Ovilia <zw...@gmail.com>
AuthorDate: Fri Jun 18 16:15:48 2021 +0800

    feat(label): sector label position #14616
---
 src/chart/bar/BarView.ts           |   9 +-
 src/chart/gauge/GaugeView.ts       |   2 +-
 src/label/calculateTextPosition.ts | 200 +++++++++++++++++++++++++++++++++++++
 src/util/shape/sausage.ts          |   6 +-
 test/bar-polar-label.html          | 126 +++++++++++++++++++++++
 5 files changed, 335 insertions(+), 8 deletions(-)

diff --git a/src/chart/bar/BarView.ts b/src/chart/bar/BarView.ts
index 470c6f6..39bbf10 100644
--- a/src/chart/bar/BarView.ts
+++ b/src/chart/bar/BarView.ts
@@ -32,7 +32,7 @@ import { enableHoverEmphasis, setStatesStylesFromModel } from '../../util/states
 import { setLabelStyle, getLabelStatesModels, setLabelValueAnimation } from '../../label/labelStyle';
 import {throttle} from '../../util/throttle';
 import {createClipPath} from '../helper/createClipPathFromCoordSys';
-import Sausage from '../../util/shape/sausage';
+import {SausageShape, Sausage} from '../../util/shape/sausage';
 import ChartView from '../../view/Chart';
 import List, {DefaultDataVisual} from '../../data/List';
 import GlobalModel from '../../model/Global';
@@ -60,6 +60,7 @@ import CartesianAxisModel from '../../coord/cartesian/AxisModel';
 import {LayoutRect} from '../../util/layout';
 import {EventCallback} from 'zrender/src/core/Eventful';
 import { warn } from '../../util/log';
+import {createSectorCalculateTextPosition} from '../../label/calculateTextPosition';
 
 const _eventPos = [0, 0];
 
@@ -761,6 +762,8 @@ const elementCreator: {
 
         sector.name = 'item';
 
+        sector.calculateTextPosition = createSectorCalculateTextPosition();
+
         // Animation
         if (animationModel) {
             const sectorShape = sector.shape;
@@ -911,7 +914,7 @@ function updateStyle(
     const cursorStyle = itemModel.getShallow('cursor');
     cursorStyle && (el as Path).attr('cursor', cursorStyle);
 
-    if (!isPolar) {
+    // if (!isPolar) {
         const labelPositionOutside = isHorizontal
             ? ((layout as RectLayout).height > 0 ? 'bottom' as const : 'top' as const)
             : ((layout as RectLayout).width > 0 ? 'left' as const : 'right' as const);
@@ -937,7 +940,7 @@ function updateStyle(
             seriesModel.getRawValue(dataIndex) as ParsedValue,
             (value: number) => getDefaultInterpolatedLabel(data, value)
         );
-    }
+    // }
 
     const emphasisModel = itemModel.getModel(['emphasis']);
     enableHoverEmphasis(el, emphasisModel.get('focus'), emphasisModel.get('blurScope'));
diff --git a/src/chart/gauge/GaugeView.ts b/src/chart/gauge/GaugeView.ts
index b005b53..398f231 100644
--- a/src/chart/gauge/GaugeView.ts
+++ b/src/chart/gauge/GaugeView.ts
@@ -28,7 +28,7 @@ import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import { ColorString, ECElement } from '../../util/types';
 import List from '../../data/List';
-import Sausage from '../../util/shape/sausage';
+import {Sausage} from '../../util/shape/sausage';
 import {createSymbol} from '../../util/symbol';
 import ZRImage from 'zrender/src/graphic/Image';
 import {extend} from 'zrender/src/core/util';
diff --git a/src/label/calculateTextPosition.ts b/src/label/calculateTextPosition.ts
new file mode 100644
index 0000000..a7d02b1
--- /dev/null
+++ b/src/label/calculateTextPosition.ts
@@ -0,0 +1,200 @@
+import {RectLike, Sector} from 'zrender';
+import {calculateTextPosition, TextPositionCalculationResult} from 'zrender/src/contain/text';
+import {BuiltinTextPosition, TextAlign, TextVerticalAlign} from 'zrender/src/core/types';
+import {ElementCalculateTextPosition, ElementTextConfig} from 'zrender/src/Element';
+
+export type SectorTextPosition = BuiltinTextPosition
+    | 'start' | 'end'
+    | 'startTop' | 'insideStartTop' | 'insideStart' | 'insideStartBottom' | 'startBottom'
+    | 'middleTop' | 'insideMiddleTop' | 'middle' | 'insideMiddleBottom' | 'middleBottom'
+    | 'endTop' | 'insideEndTop' | 'insideEnd' | 'insideEndBottom' | 'endBottom';
+
+export type SectorLike = {
+    cx: number
+    cy: number
+    r0: number
+    r: number
+    startAngle: number
+    endAngle: number
+    clockwise: boolean
+};
+
+export function createSectorCalculateTextPosition(): ElementCalculateTextPosition {
+    return function (
+        this: Sector,
+        out: TextPositionCalculationResult,
+        opts: {
+            position?: SectorTextPosition | (number | string)[]
+            distance?: number   // Default 5
+            global?: boolean
+        },
+        boundingRect: RectLike
+    ) {
+        const textPosition = opts.position;
+
+        if (!textPosition || textPosition instanceof Array) {
+            return calculateTextPosition(
+                out,
+                opts as ElementTextConfig,
+                boundingRect
+            );
+        }
+
+        const distance = opts.distance != null ? opts.distance : 5;
+        const sector = this.shape;
+        const cx = sector.cx;
+        const cy = sector.cy;
+        const r = sector.r;
+        const r0 = sector.r0;
+        const middleR = (r + r0) / 2;
+        const startAngle = sector.clockwise ? sector.startAngle : sector.endAngle;
+        const endAngle = sector.clockwise ? sector.endAngle : sector.startAngle;
+        const middleAngle = (startAngle + endAngle) / 2;
+
+        // base position: top-left
+        let x = cx + r * Math.cos(startAngle);
+        let y = cy + r * Math.sin(startAngle);
+
+        let textAlign: TextAlign = 'left';
+        let textVerticalAlign: TextVerticalAlign = 'top';
+
+        switch (textPosition) {
+            case 'start':
+                x = cx + middleR * Math.cos(startAngle);
+                y = cy + middleR * Math.sin(startAngle);
+                textAlign = fixAlign('right', startAngle);
+                textVerticalAlign = fixVerticalAlign('middle', startAngle);
+                break;
+            case 'end':
+                x = cx + middleR * Math.cos(endAngle);
+                y = cy + middleR * Math.sin(endAngle);
+                textAlign = fixAlign('left', endAngle);
+                textVerticalAlign = fixVerticalAlign('middle', endAngle);
+                break;
+
+            case 'startTop':
+                x = cx + (r + distance) * Math.cos(startAngle);
+                y = cy + (r + distance) * Math.sin(startAngle);
+                textAlign = fixAlign('left', startAngle);
+                textVerticalAlign = fixVerticalAlign('bottom', startAngle);
+                break;
+            case 'insideStartTop':
+                x = cx + (r - distance) * Math.cos(startAngle);
+                y = cy + (r - distance) * Math.sin(startAngle);
+                textAlign = fixAlign('left', startAngle);
+                textVerticalAlign = fixVerticalAlign('top', startAngle);
+                break;
+            case 'insideStart':
+                x = cx + middleR * Math.cos(startAngle);
+                y = cy + middleR * Math.sin(startAngle);
+                textAlign = fixAlign('left', startAngle);
+                textVerticalAlign = fixVerticalAlign('middle', startAngle);
+                break;
+            case 'insideStartBottom':
+                x = cx + (r0 + distance) * Math.cos(startAngle);
+                y = cy + (r0 + distance) * Math.sin(startAngle);
+                textAlign = fixAlign('left', startAngle);
+                textVerticalAlign = fixVerticalAlign('bottom', startAngle);
+                break;
+            case 'startBottom':
+                x = cx + (r0 - distance) * Math.cos(startAngle);
+                y = cy + (r0 - distance) * Math.sin(startAngle);
+                textAlign = fixAlign('left', startAngle);
+                textVerticalAlign = fixVerticalAlign('top', startAngle);
+                break;
+
+            case 'middleTop':
+                x = cx + (r + distance) * Math.cos(middleAngle);
+                y = cy + (r + distance) * Math.sin(middleAngle);
+                textAlign = fixAlign('center', middleAngle);
+                textVerticalAlign = fixVerticalAlign('bottom', middleAngle);
+                break;
+            case 'insideMiddleTop':
+                x = cx + (r - distance) * Math.cos(middleAngle);
+                y = cy + (r - distance) * Math.sin(middleAngle);
+                textAlign = fixAlign('center', middleAngle);
+                textVerticalAlign = fixVerticalAlign('top', middleAngle);
+                break;
+            case 'middle':
+                x = cx + middleR * Math.cos(middleAngle);
+                y = cy + middleR * Math.sin(middleAngle);
+                textAlign = fixAlign('center', middleAngle);
+                textVerticalAlign = fixVerticalAlign('middle', middleAngle);
+                break;
+            case 'insideMiddleBottom':
+                x = cx + (r0 + distance) * Math.cos(middleAngle);
+                y = cy + (r0 + distance) * Math.sin(middleAngle);
+                textAlign = fixAlign('center', middleAngle);
+                textVerticalAlign = fixVerticalAlign('bottom', middleAngle);
+                break;
+            case 'middleBottom':
+                x = cx + (r0 - distance) * Math.cos(middleAngle);
+                y = cy + (r0 - distance) * Math.sin(middleAngle);
+                textAlign = fixAlign('center', middleAngle);
+                textVerticalAlign = fixVerticalAlign('top', middleAngle);
+                break;
+
+            case 'endTop':
+                x = cx + (r + distance) * Math.cos(endAngle);
+                y = cy + (r + distance) * Math.sin(endAngle);
+                textAlign = fixAlign('right', endAngle);
+                textVerticalAlign = fixVerticalAlign('bottom', endAngle);
+            case 'insideEndTop':
+                x = cx + (r - distance) * Math.cos(endAngle);
+                y = cy + (r - distance) * Math.sin(endAngle);
+                textAlign = fixAlign('right', endAngle);
+                textVerticalAlign = fixVerticalAlign('top', endAngle);
+                break;
+            case 'insideEnd':
+                x = cx + middleR * Math.cos(endAngle);
+                y = cy + middleR * Math.sin(endAngle);
+                textAlign = fixAlign('right', endAngle);
+                textVerticalAlign = fixVerticalAlign('middle', endAngle);
+                break;
+            case 'insideEndBottom':
+                x = cx + (r0 + distance) * Math.cos(endAngle);
+                y = cy + (r0 + distance) * Math.sin(endAngle);
+                textAlign = fixAlign('right', endAngle);
+                textVerticalAlign = fixVerticalAlign('bottom', endAngle);
+                break;
+            case 'endBottom':
+                x = cx + (r0 - distance) * Math.cos(endAngle);
+                y = cy + (r0 - distance) * Math.sin(endAngle);
+                textAlign = fixAlign('right', endAngle);
+                textVerticalAlign = fixVerticalAlign('top', endAngle);
+                break;
+            default:
+                return calculateTextPosition(
+                    out,
+                    opts as ElementTextConfig,
+                    boundingRect
+                );
+        }
+
+        out = out || {} as TextPositionCalculationResult;
+        out.x = x;
+        out.y = y;
+        out.align = textAlign;
+        out.verticalAlign = textVerticalAlign;
+
+        return out;
+    };
+}
+
+function fixAlign(align: TextAlign, angle: number) {
+    if (angle > Math.PI / 2) {
+        return align === 'left'
+            ? 'right'
+            : (align === 'right' ? 'left' : align);
+    }
+    return align;
+}
+
+function fixVerticalAlign(align: TextVerticalAlign, angle: number) {
+    if (angle > 0 && angle <= Math.PI) {
+        return align === 'top'
+            ? 'bottom'
+            : (align === 'bottom' ? 'top' : align);
+    }
+    return align;
+}
diff --git a/src/util/shape/sausage.ts b/src/util/shape/sausage.ts
index b0a843e..a30d2cb 100644
--- a/src/util/shape/sausage.ts
+++ b/src/util/shape/sausage.ts
@@ -24,7 +24,7 @@ import { PathProps } from 'zrender/src/graphic/Path';
  * Sausage: similar to sector, but have half circle on both sides
  */
 
-class SausageShape {
+export class SausageShape {
     cx = 0;
     cy = 0;
     r0 = 0;
@@ -38,7 +38,7 @@ interface SausagePathProps extends PathProps {
     shape?: SausageShape
 }
 
-class SausagePath extends Path<SausagePathProps> {
+export class Sausage extends Path<SausagePathProps> {
 
     type = 'sausage';
 
@@ -97,5 +97,3 @@ class SausagePath extends Path<SausagePathProps> {
         ctx.closePath();
     }
 }
-
-export default SausagePath;
\ No newline at end of file
diff --git a/test/bar-polar-label.html b/test/bar-polar-label.html
new file mode 100644
index 0000000..bcb2569
--- /dev/null
+++ b/test/bar-polar-label.html
@@ -0,0 +1,126 @@
+
+<!--
+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.
+-->
+
+<html>
+    <head>
+        <meta charset="utf-8">
+        <script src="lib/simpleRequire.js"></script>
+        <script src="lib/config.js"></script>
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+    </head>
+    <body>
+        <style>
+            #main0, #main1 {
+                width: 100%;
+                height: 1800px;
+                margin: 0;
+            }
+        </style>
+        <h3>Polar bar series label positions</h3>
+        <div id="main0"></div>
+        <div id="main1"></div>
+        <script>
+
+            require(['echarts'], function (echarts) {
+                function setChart(chartIndex) {
+                    var positions = [
+                        'start', 'end',
+                        'startTop', 'insideStartTop', 'insideStart', 'insideStartBottom', 'startBottom',
+                        'middleTop', 'insideMiddleTop', 'middle', 'inside', 'insideMiddleBottom', 'middleBottom',
+                        'endTop', 'insideEndTop', 'insideEnd', 'insideEndBottom', 'endBottom'
+                    ];
+
+                    var chart = echarts.init(document.getElementById('main' + chartIndex));
+                    var rows = 5;
+                    var cols = 4;
+
+                    var angleAxis = [];
+                    var radiusAxis = [];
+                    var polar = [];
+                    var series = [];
+                    var title = [];
+                    var cnt = positions.length;
+                    for (var i = 0; i < rows; ++i) {
+                        for (var j = 0; j < cols; ++j) {
+                            var id = i * cols + j;
+                            if (id >= cnt) {
+                                break;
+                            }
+                            angleAxis.push({
+                                max: 4,
+                                polarIndex: id,
+                                startAngle: 75
+                            });
+                            radiusAxis.push({
+                                polarIndex: id,
+                                type: 'category',
+                                data: ['a', 'b', 'c', 'd'],
+                                startAngle: 75
+                            });
+                            polar.push({
+                                center: [
+                                    100 / cols * (j + 0.5) + '%',
+                                    100 / rows * (i + 0.5) + '%'
+                                ],
+                                radius: [
+                                    chartIndex ? 10: 20,
+                                    100 / rows * 0.8 + '%'
+                                ]
+                            });
+                            series.push({
+                                type: 'bar',
+                                data: [0.5, 1.2, 2.4, 3.6],
+                                coordinateSystem: 'polar',
+                                polarIndex: id,
+                                label: {
+                                    show: true,
+                                    position: positions[id],
+                                    formatter: '{b}: {c}',
+                                    borderColor: '#0ff',
+                                    borderWidth: 2
+                                }
+                            });
+                            title.push({
+                                text: positions[id],
+                                left: 100 / cols * (j + 0.5) + '%',
+                                top: 100 / rows * (i + 0.92) + '%',
+                                textAlign: 'center'
+                            });
+                        }
+                    }
+
+                    chart.setOption({
+                        title: title,
+                        angleAxis: chartIndex ? angleAxis : radiusAxis,
+                        radiusAxis: chartIndex ? radiusAxis : angleAxis,
+                        polar: polar,
+                        tooltip: {},
+                        series: series,
+                        backgroundColor: '#fff',
+                        animation: 0
+                    });
+                }
+
+                setChart(0);
+                setChart(1);
+            });
+        </script>
+    </body>
+</html>

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org