提交 f3ed3fab 编写于 作者: P pissang

feat(label): use the new layout algorithm in pie

上级 678a33b9
......@@ -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) {
......
......@@ -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[]) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册