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