You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2020/06/20 14:50:07 UTC

[incubator-echarts] branch label-enhancement updated: feat(label): use the new layout algorithm in pie

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

shenyi pushed a commit to branch label-enhancement
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git


The following commit(s) were added to refs/heads/label-enhancement by this push:
     new f3ed3fa  feat(label): use the new layout algorithm in pie
f3ed3fa is described below

commit f3ed3fab45c69e566e2aea00c173900325b80fde
Author: pissang <bm...@gmail.com>
AuthorDate: Sat Jun 20 22:49:29 2020 +0800

    feat(label): use the new layout algorithm in pie
---
 src/chart/pie/labelLayout.ts   | 145 +++++++++++++----------------------------
 src/label/labelLayoutHelper.ts |  22 +++++--
 2 files changed, 59 insertions(+), 108 deletions(-)

diff --git a/src/chart/pie/labelLayout.ts b/src/chart/pie/labelLayout.ts
index d2f7361..470119a 100644
--- a/src/chart/pie/labelLayout.ts
+++ b/src/chart/pie/labelLayout.ts
@@ -21,18 +21,17 @@
 import {parsePercent} from '../../util/number';
 import PieSeriesModel, { PieSeriesOption, PieDataItemOption } from './PieSeries';
 import { VectorArray } from 'zrender/src/core/vector';
-import { HorizontalAlign, ZRRectLike, ZRTextAlign } from '../../util/types';
+import { HorizontalAlign, ZRTextAlign } from '../../util/types';
 import { Sector, Polyline } from '../../util/graphic';
 import ZRText from 'zrender/src/graphic/Text';
-import { RectLike } from 'zrender/src/core/BoundingRect';
+import BoundingRect, {RectLike} from 'zrender/src/core/BoundingRect';
 import { each } from 'zrender/src/core/util';
 import { limitTurnAngle } from '../../label/labelGuideHelper';
+import { shiftLayoutOnY } from '../../label/labelLayoutHelper';
 
 const RADIAN = Math.PI / 180;
 
 interface LabelLayout {
-    x: number
-    y: number
     label: ZRText,
     labelLine: Polyline,
     position: PieSeriesOption['label']['position'],
@@ -41,12 +40,11 @@ interface LabelLayout {
     minTurnAngle: number
     linePoints: VectorArray[]
     textAlign: HorizontalAlign
-    rotation: number,
     labelDistance: number,
     labelAlignTo: PieSeriesOption['label']['alignTo'],
     labelMargin: number,
     bleedMargin: PieSeriesOption['label']['bleedMargin'],
-    textRect: ZRRectLike
+    rect: BoundingRect
 }
 
 function adjustSingleSide(
@@ -65,46 +63,6 @@ function adjustSingleSide(
         return;
     }
 
-    list.sort(function (a, b) {
-        return a.y - b.y;
-    });
-
-    let adjusted = false;
-
-    function shiftDown(start: number, end: number, delta: number, dir: number) {
-        for (let j = start; j < end; j++) {
-            list[j].y += delta;
-            adjusted = true;
-
-            if (j > start && j + 1 < end
-                && list[j + 1].y > list[j].y + list[j].textRect.height
-            ) {
-                // Shift up so it can be more equaly distributed.
-                shiftUp(j, delta / 2);
-                return;
-            }
-        }
-
-        shiftUp(end - 1, delta / 2);
-    }
-
-    function shiftUp(end: number, delta: number) {
-        for (let j = end; j >= 0; j--) {
-            list[j].y -= delta;
-            adjusted = true;
-
-            const textHeight = list[j].textRect.height;
-            if (list[j].y - textHeight / 2 < viewTop) {
-                list[j].y = viewTop + textHeight / 2;
-            }
-
-            if (j > 0
-                && list[j].y > list[j - 1].y + list[j - 1].textRect.height
-            ) {
-                break;
-            }
-        }
-    }
 
     interface SemiInfo {
         list: LabelLayout[]
@@ -117,13 +75,13 @@ function adjustSingleSide(
         const rB2 = rB * rB;
         for (let i = 0; i < semi.list.length; i++) {
             const item = semi.list[i];
-            const dy = Math.abs(item.y - cy);
+            const dy = Math.abs(item.label.y - cy);
             // horizontal r is always same with original r because x is not changed.
             const rA = r + item.len;
             const rA2 = rA * rA;
             // Use ellipse implicit function to calculate x
             const dx = Math.sqrt((1 - Math.abs(dy * dy / rB2)) * rA2);
-            item.x = cx + (dx + item.len2) * dir;
+            item.label.x = cx + (dx + item.len2) * dir;
         }
     }
 
@@ -138,10 +96,10 @@ function adjustSingleSide(
                 continue;
             }
             const item = items[i];
-            const semi = item.y > cy ? bottomSemi : topSemi;
-            const dy = Math.abs(item.y - cy);
+            const semi = item.label.y > cy ? bottomSemi : topSemi;
+            const dy = Math.abs(item.label.y - cy);
             if (dy > semi.maxY) {
-                const dx = item.x - cx - item.len2 * dir;
+                const dx = item.label.x - cx - item.len2 * dir;
                 // horizontal r is always same with original r because x is not changed.
                 const rA = r + item.len;
                 // Canculate rB based on the topest / bottemest label.
@@ -158,30 +116,16 @@ function adjustSingleSide(
         recalculateXOnSemiToAlignOnEllipseCurve(bottomSemi);
     }
 
-    let lastY = 0;
-    let delta;
     const len = list.length;
     for (let i = 0; i < len; i++) {
         if (list[i].position === 'outer' && list[i].labelAlignTo === 'labelLine') {
-            const dx = list[i].x - farthestX;
+            const dx = list[i].label.x - farthestX;
             list[i].linePoints[1][0] += dx;
-            list[i].x = farthestX;
-        }
-
-        delta = list[i].y - lastY;
-        if (delta < 0) {
-            shiftDown(i, len, -delta, dir);
+            list[i].label.x = farthestX;
         }
-        lastY = list[i].y + list[i].textRect.height;
-    }
-    // PENDING:
-    // If data is sorted. Left top is usually the small data with a lower priority.
-    // So shift up and make sure the data on the bottom is always displayed well.
-    if (viewHeight - lastY < 0) {
-        shiftUp(len - 1, lastY - viewHeight);
     }
 
-    if (adjusted) {
+    if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) {
         recalculateX(list);
     }
 }
@@ -201,15 +145,16 @@ function avoidOverlap(
     let leftmostX = Number.MAX_VALUE;
     let rightmostX = -Number.MAX_VALUE;
     for (let i = 0; i < labelLayoutList.length; i++) {
+        const label = labelLayoutList[i].label;
         if (isPositionCenter(labelLayoutList[i])) {
             continue;
         }
-        if (labelLayoutList[i].x < cx) {
-            leftmostX = Math.min(leftmostX, labelLayoutList[i].x);
+        if (label.x < cx) {
+            leftmostX = Math.min(leftmostX, label.x);
             leftList.push(labelLayoutList[i]);
         }
         else {
-            rightmostX = Math.max(rightmostX, labelLayoutList[i].x);
+            rightmostX = Math.max(rightmostX, label.x);
             rightList.push(labelLayoutList[i]);
         }
     }
@@ -219,6 +164,7 @@ function avoidOverlap(
 
     for (let i = 0; i < labelLayoutList.length; i++) {
         const layout = labelLayoutList[i];
+        const label = layout.label;
         if (isPositionCenter(layout)) {
             continue;
         }
@@ -227,10 +173,10 @@ function avoidOverlap(
         if (linePoints) {
             const isAlignToEdge = layout.labelAlignTo === 'edge';
 
-            let realTextWidth = layout.textRect.width;
+            let realTextWidth = layout.rect.width;
             let targetTextWidth;
             if (isAlignToEdge) {
-                if (layout.x < cx) {
+                if (label.x < cx) {
                     targetTextWidth = linePoints[2][0] - layout.labelDistance
                             - viewLeft - layout.labelMargin;
                 }
@@ -240,14 +186,14 @@ function avoidOverlap(
                 }
             }
             else {
-                if (layout.x < cx) {
-                    targetTextWidth = layout.x - viewLeft - layout.bleedMargin;
+                if (label.x < cx) {
+                    targetTextWidth = label.x - viewLeft - layout.bleedMargin;
                 }
                 else {
-                    targetTextWidth = viewLeft + viewWidth - layout.x - layout.bleedMargin;
+                    targetTextWidth = viewLeft + viewWidth - label.x - layout.bleedMargin;
                 }
             }
-            if (targetTextWidth < layout.textRect.width) {
+            if (targetTextWidth < layout.rect.width) {
                 // TODOTODO
                 // layout.text = textContain.truncateText(layout.text, targetTextWidth, layout.font);
                 layout.label.style.width = targetTextWidth;
@@ -260,7 +206,7 @@ function avoidOverlap(
 
             const dist = linePoints[1][0] - linePoints[2][0];
             if (isAlignToEdge) {
-                if (layout.x < cx) {
+                if (label.x < cx) {
                     linePoints[2][0] = viewLeft + layout.labelMargin + realTextWidth + layout.labelDistance;
                 }
                 else {
@@ -269,15 +215,15 @@ function avoidOverlap(
                 }
             }
             else {
-                if (layout.x < cx) {
-                    linePoints[2][0] = layout.x + layout.labelDistance;
+                if (label.x < cx) {
+                    linePoints[2][0] = label.x + layout.labelDistance;
                 }
                 else {
-                    linePoints[2][0] = layout.x - layout.labelDistance;
+                    linePoints[2][0] = label.x - layout.labelDistance;
                 }
                 linePoints[1][0] = linePoints[2][0] + dist;
             }
-            linePoints[1][1] = linePoints[2][1] = layout.y;
+            linePoints[1][1] = linePoints[2][1] = label.y;
         }
     }
 }
@@ -347,12 +293,6 @@ export default function (
         cx = sectorShape.cx;
         cy = sectorShape.cy;
 
-        const textRect = label.getBoundingRect().clone();
-        // Text has a default 1px stroke. Exclude this.
-        textRect.x -= 1;
-        textRect.y -= 1;
-        textRect.width += 2.1;
-        textRect.height += 2.1;
 
         const isLabelInside = labelPosition === 'inside' || labelPosition === 'inner';
         if (labelPosition === 'center') {
@@ -407,31 +347,37 @@ export default function (
 
         hasLabelRotate = !!labelRotate;
 
+        label.x = textX;
+        label.y = textY;
+        label.rotation = labelRotate;
+
         // Not sectorShape the inside label
         if (!isLabelInside) {
+            const textRect = label.getBoundingRect().clone();
+            textRect.applyTransform(label.getComputedTransform());
+            // Text has a default 1px stroke. Exclude this.
+            textRect.x -= 1;
+            textRect.y -= 1;
+            textRect.width += 2.1;
+            textRect.height += 2.1;
+
             labelLayoutList.push({
                 label,
                 labelLine,
-                x: textX,
-                y: textY,
                 position: labelPosition,
                 len: labelLineLen,
                 len2: labelLineLen2,
                 minTurnAngle: labelLineModel.get('minTurnAngle'),
                 linePoints: linePoints,
                 textAlign: textAlign,
-                rotation: labelRotate,
                 labelDistance: labelDistance,
                 labelAlignTo: labelAlignTo,
                 labelMargin: labelMargin,
                 bleedMargin: bleedMargin,
-                textRect: textRect
+                rect: textRect
             });
         }
         else {
-            label.x = textX;
-            label.y = textY;
-            label.rotation = labelRotate;
             label.setStyle({
                 align: textAlign,
                 verticalAlign: 'middle'
@@ -450,11 +396,8 @@ export default function (
         const layout = labelLayoutList[i];
         const label = layout.label;
         const labelLine = layout.labelLine;
-        const notShowLabel = isNaN(layout.x) || isNaN(layout.y);
+        const notShowLabel = isNaN(label.x) || isNaN(label.y);
         if (label) {
-            label.x = layout.x;
-            label.y = layout.y;
-            label.rotation = layout.rotation;
             label.setStyle({
                 align: layout.textAlign,
                 verticalAlign: 'middle'
@@ -465,8 +408,8 @@ export default function (
             }
             const selectState = label.states.select;
             if (selectState) {
-                selectState.x += layout.x;
-                selectState.y += layout.y;
+                selectState.x += label.x;
+                selectState.y += label.y;
             }
         }
         if (labelLine) {
diff --git a/src/label/labelLayoutHelper.ts b/src/label/labelLayoutHelper.ts
index 3c8a6e3..644091e 100644
--- a/src/label/labelLayoutHelper.ts
+++ b/src/label/labelLayoutHelper.ts
@@ -91,7 +91,7 @@ export function prepareLayoutList(input: LabelLayoutListPrepareInput[]): LabelLa
 }
 
 function shiftLayout(
-    list: LabelLayoutInfo[],
+    list: Pick<LabelLayoutInfo, 'rect' | 'label'>[],
     xyDim: 'x' | 'y',
     sizeDim: 'width' | 'height',
     minBound: number,
@@ -109,6 +109,8 @@ function shiftLayout(
 
     let lastPos = 0;
     let delta;
+    let adjusted = false;
+
     const shifts = [];
     let totalShifts = 0;
     for (let i = 0; i < len; i++) {
@@ -119,6 +121,7 @@ function shiftLayout(
             // shiftForward(i, len, -delta);
             rect[xyDim] -= delta;
             item.label[xyDim] -= delta;
+            adjusted = true;
         }
         const shift = Math.max(-delta, 0);
         shifts.push(shift);
@@ -170,6 +173,9 @@ function shiftLayout(
     }
 
     function shiftList(delta: number, start: number, end: number) {
+        if (delta !== 0) {
+            adjusted = true;
+        }
         for (let i = start; i < end; i++) {
             const item = list[i];
             const rect = item.rect;
@@ -236,28 +242,30 @@ function shiftLayout(
             }
         }
     }
+
+    return adjusted;
 }
 
 /**
  * Adjust labels on x direction to avoid overlap.
  */
 export function shiftLayoutOnX(
-    list: LabelLayoutInfo[],
+    list: Pick<LabelLayoutInfo, 'rect' | 'label'>[],
     leftBound: number,
     rightBound: number
-) {
-    shiftLayout(list, 'x', 'width', leftBound, rightBound);
+): boolean {
+    return shiftLayout(list, 'x', 'width', leftBound, rightBound);
 }
 
 /**
  * Adjust labels on y direction to avoid overlap.
  */
 export function shiftLayoutOnY(
-    list: LabelLayoutInfo[],
+    list: Pick<LabelLayoutInfo, 'rect' | 'label'>[],
     topBound: number,
     bottomBound: number
-) {
-    shiftLayout(list, 'y', 'height', topBound, bottomBound);
+): boolean {
+    return shiftLayout(list, 'y', 'height', topBound, bottomBound);
 }
 
 export function hideOverlap(labelList: LabelLayoutInfo[]) {


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