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/07/03 15:30:38 UTC
[incubator-echarts] branch label-enhancement updated: feat(label):
add maxSurfaceAngle limit 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 c003487 feat(label): add maxSurfaceAngle limit in pie
c003487 is described below
commit c003487f6c0ef96be90d6f24955a2d2d5641ee94
Author: pissang <bm...@gmail.com>
AuthorDate: Fri Jul 3 23:29:03 2020 +0800
feat(label): add maxSurfaceAngle limit in pie
---
src/chart/pie/PieSeries.ts | 19 ++++++++----
src/chart/pie/labelLayout.ts | 36 ++++++++++++----------
src/label/labelGuideHelper.ts | 69 +++++++++++++++++++++++++++++++++++++++++++
test/pie-label-extreme.html | 6 ++--
test/pie-label-mobile.html | 45 ++++++++++++++++++++++++++--
5 files changed, 150 insertions(+), 25 deletions(-)
diff --git a/src/chart/pie/PieSeries.ts b/src/chart/pie/PieSeries.ts
index e3e877c..de34310 100644
--- a/src/chart/pie/PieSeries.ts
+++ b/src/chart/pie/PieSeries.ts
@@ -51,18 +51,26 @@ interface PieLabelOption extends Omit<LabelOption, 'rotate' | 'position'> {
position?: LabelOption['position'] | 'outer' | 'inner' | 'center'
}
+interface PieLabelLineOption extends LabelLineOption {
+ /**
+ * Max angle between labelLine and surface normal.
+ * 0 - 180
+ */
+ maxSurfaceAngle?: number
+}
+
export interface PieDataItemOption extends
OptionDataItemObject<OptionDataValueNumeric>,
SelectableTarget {
itemStyle?: ItemStyleOption
label?: PieLabelOption
- labelLine?: LabelLineOption
+ labelLine?: PieLabelLineOption
emphasis?: {
itemStyle?: ItemStyleOption
label?: PieLabelOption
- labelLine?: LabelLineOption
+ labelLine?: PieLabelLineOption
}
}
export interface PieSeriesOption extends
@@ -81,7 +89,7 @@ export interface PieSeriesOption extends
// TODO: TYPE Color Callback
itemStyle?: ItemStyleOption
label?: PieLabelOption
- labelLine?: LabelLineOption
+ labelLine?: PieLabelLineOption
clockwise?: boolean
startAngle?: number
@@ -99,7 +107,7 @@ export interface PieSeriesOption extends
emphasis?: {
itemStyle?: ItemStyleOption
label?: PieLabelOption
- labelLine?: LabelLineOption
+ labelLine?: PieLabelLineOption
}
animationType?: 'expansion' | 'scale'
@@ -272,7 +280,8 @@ class PieSeriesModel extends SeriesModel<PieSeriesOption> {
// 引导线两段中的第二段长度
length2: 15,
smooth: false,
- minTurnAngle: 100,
+ minTurnAngle: 90,
+ maxSurfaceAngle: 90,
lineStyle: {
// color: 各异,
width: 1,
diff --git a/src/chart/pie/labelLayout.ts b/src/chart/pie/labelLayout.ts
index ed77887..d47f18f 100644
--- a/src/chart/pie/labelLayout.ts
+++ b/src/chart/pie/labelLayout.ts
@@ -26,7 +26,7 @@ import { Sector, Polyline, Point } from '../../util/graphic';
import ZRText from 'zrender/src/graphic/Text';
import BoundingRect, {RectLike} from 'zrender/src/core/BoundingRect';
import { each } from 'zrender/src/core/util';
-import { limitTurnAngle } from '../../label/labelGuideHelper';
+import { limitTurnAngle, limitSurfaceAngle } from '../../label/labelGuideHelper';
import { shiftLayoutOnY } from '../../label/labelLayoutHelper';
const RADIAN = Math.PI / 180;
@@ -38,6 +38,8 @@ interface LabelLayout {
len: number
len2: number
minTurnAngle: number
+ maxSurfaceAngle: number
+ surfaceNormal: Point
linePoints: VectorArray[]
textAlign: HorizontalAlign
labelDistance: number,
@@ -281,8 +283,8 @@ export default function (
}
const midAngle = (sectorShape.startAngle + sectorShape.endAngle) / 2;
- const dx = Math.cos(midAngle);
- const dy = Math.sin(midAngle);
+ const nx = Math.cos(midAngle);
+ const ny = Math.sin(midAngle);
let textX;
let textY;
@@ -300,27 +302,27 @@ export default function (
textAlign = 'center';
}
else {
- const x1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * dx : sectorShape.r * dx) + cx;
- const y1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * dy : sectorShape.r * dy) + cy;
+ const x1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * nx : sectorShape.r * nx) + cx;
+ const y1 = (isLabelInside ? (sectorShape.r + sectorShape.r0) / 2 * ny : sectorShape.r * ny) + cy;
- textX = x1 + dx * 3;
- textY = y1 + dy * 3;
+ textX = x1 + nx * 3;
+ textY = y1 + ny * 3;
if (!isLabelInside) {
// For roseType
- const x2 = x1 + dx * (labelLineLen + r - sectorShape.r);
- const y2 = y1 + dy * (labelLineLen + r - sectorShape.r);
- const x3 = x2 + ((dx < 0 ? -1 : 1) * labelLineLen2);
+ const x2 = x1 + nx * (labelLineLen + r - sectorShape.r);
+ const y2 = y1 + ny * (labelLineLen + r - sectorShape.r);
+ const x3 = x2 + ((nx < 0 ? -1 : 1) * labelLineLen2);
const y3 = y2;
if (labelAlignTo === 'edge') {
// Adjust textX because text align of edge is opposite
- textX = dx < 0
+ textX = nx < 0
? viewLeft + edgeDistance
: viewLeft + viewWidth - edgeDistance;
}
else {
- textX = x3 + (dx < 0 ? -labelDistance : labelDistance);
+ textX = x3 + (nx < 0 ? -labelDistance : labelDistance);
}
textY = y3;
linePoints = [[x1, y1], [x2, y2], [x3, y3]];
@@ -329,8 +331,8 @@ export default function (
textAlign = isLabelInside
? 'center'
: (labelAlignTo === 'edge'
- ? (dx > 0 ? 'right' : 'left')
- : (dx > 0 ? 'left' : 'right'));
+ ? (nx > 0 ? 'right' : 'left')
+ : (nx > 0 ? 'left' : 'right'));
}
let labelRotate;
@@ -340,7 +342,7 @@ export default function (
}
else {
labelRotate = rotate
- ? (dx < 0 ? -midAngle + Math.PI : -midAngle)
+ ? (nx < 0 ? -midAngle + Math.PI : -midAngle)
: 0;
}
@@ -368,6 +370,8 @@ export default function (
len: labelLineLen,
len2: labelLineLen2,
minTurnAngle: labelLineModel.get('minTurnAngle'),
+ maxSurfaceAngle: labelLineModel.get('maxSurfaceAngle'),
+ surfaceNormal: new Point(nx, ny),
linePoints: linePoints,
textAlign: textAlign,
labelDistance: labelDistance,
@@ -420,6 +424,8 @@ export default function (
}
else {
limitTurnAngle(linePoints, layout.minTurnAngle);
+ limitSurfaceAngle(linePoints, layout.surfaceNormal, layout.maxSurfaceAngle);
+
labelLine.setShape({ points: linePoints });
// Set the anchor to the midpoint of sector
diff --git a/src/label/labelGuideHelper.ts b/src/label/labelGuideHelper.ts
index f089d30..a501d1c 100644
--- a/src/label/labelGuideHelper.ts
+++ b/src/label/labelGuideHelper.ts
@@ -444,6 +444,10 @@ export function limitTurnAngle(linePoints: number[][], minTurnAngle: number) {
const t = pt2.x !== pt1.x
? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x)
: (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);
+ if (isNaN(t)) {
+ return;
+ }
+
if (t < 0) {
Point.copy(tmpProjPoint, pt1);
}
@@ -455,6 +459,71 @@ export function limitTurnAngle(linePoints: number[][], minTurnAngle: number) {
}
}
+/**
+ * Limit the angle of line and the surface
+ * @param maxSurfaceAngle Radian of minimum turn angle. 0 - 180. 0 is same direction to normal. 180 is opposite
+ */
+export function limitSurfaceAngle(linePoints: vector.VectorArray[], surfaceNormal: Point, maxSurfaceAngle: number) {
+ if (!(maxSurfaceAngle <= 180 && maxSurfaceAngle > 0)) {
+ return;
+ }
+ maxSurfaceAngle = maxSurfaceAngle / 180 * Math.PI;
+
+ pt0.fromArray(linePoints[0]);
+ pt1.fromArray(linePoints[1]);
+ pt2.fromArray(linePoints[2]);
+
+ Point.sub(dir, pt1, pt0);
+ Point.sub(dir2, pt2, pt1);
+
+ const len1 = dir.len();
+ const len2 = dir2.len();
+
+ if (len1 < 1e-3 || len2 < 1e-3) {
+ return;
+ }
+
+ dir.scale(1 / len1);
+ dir2.scale(1 / len2);
+
+ const angleCos = dir.dot(surfaceNormal);
+ const maxSurfaceAngleCos = Math.cos(maxSurfaceAngle);
+
+ if (angleCos < maxSurfaceAngleCos) {
+ // Calculate project point of pt0 on pt1-pt2
+ const d = projectPointToLine(pt1.x, pt1.y, pt2.x, pt2.y, pt0.x, pt0.y, tmpArr, false);
+ tmpProjPoint.fromArray(tmpArr);
+
+ const HALF_PI = Math.PI / 2;
+ const angle2 = Math.acos(dir2.dot(surfaceNormal));
+ const newAngle = HALF_PI + angle2 - maxSurfaceAngle;
+ if (newAngle >= HALF_PI) {
+ // parallel
+ Point.copy(tmpProjPoint, pt2);
+ }
+ else {
+ // Calculate new projected length with limited minTurnAngle and get the new connect point
+ tmpProjPoint.scaleAndAdd(dir2, d / Math.tan(Math.PI / 2 - newAngle));
+ // Limit the new calculated connect point between pt1 and pt2.
+ const t = pt2.x !== pt1.x
+ ? (tmpProjPoint.x - pt1.x) / (pt2.x - pt1.x)
+ : (tmpProjPoint.y - pt1.y) / (pt2.y - pt1.y);
+ if (isNaN(t)) {
+ return;
+ }
+
+ if (t < 0) {
+ Point.copy(tmpProjPoint, pt1);
+ }
+ else if (t > 1) {
+ Point.copy(tmpProjPoint, pt2);
+ }
+ }
+
+ tmpProjPoint.toArray(linePoints[1]);
+ }
+}
+
type LabelLineModel = Model<LabelLineOption>;
diff --git a/test/pie-label-extreme.html b/test/pie-label-extreme.html
index fb9a278..7ce7d45 100644
--- a/test/pie-label-extreme.html
+++ b/test/pie-label-extreme.html
@@ -688,7 +688,7 @@ under the License.
minTurnAngle: 110
},
label: {
- margin: 25,
+ edgeDistance: 25,
bleedMargin: 10,
alignTo: 'none',
overflow: 'truncate'
@@ -699,7 +699,7 @@ under the License.
chart.setOption({
series: {
label: Object.assign({}, config.label, {
- margin: config.label.margin + '%'
+ edgeDistance: config.label.edgeDistance + '%'
}),
labelLine: config.labelLine
}
@@ -712,7 +712,7 @@ under the License.
labelLineFolder.open();
labelFolder.add(config.label, 'alignTo', ['none', 'edge', 'labelLine']).onChange(update);
labelFolder.add(config.label, 'overflow', ['truncate', 'break', 'breakAll']).onChange(update);
- labelFolder.add(config.label, 'margin', 0, 50).onChange(update);
+ labelFolder.add(config.label, 'edgeDistance', 0, 50).onChange(update);
labelFolder.add(config.label, 'bleedMargin', 0, 500).onChange(update);
labelLineFolder.add(config.labelLine, 'length', 0, 500).onChange(update);
labelLineFolder.add(config.labelLine, 'length2', 0, 500).onChange(update);
diff --git a/test/pie-label-mobile.html b/test/pie-label-mobile.html
index 352dbbe..aef18c6 100644
--- a/test/pie-label-mobile.html
+++ b/test/pie-label-mobile.html
@@ -28,6 +28,7 @@ under the License.
<script src="lib/jquery.min.js"></script>
<script src="lib/facePrint.js"></script>
<script src="lib/testHelper.js"></script>
+ <script src="lib/dat.gui.min.js"></script>
<link rel="stylesheet" href="lib/reset.css" />
</head>
<body>
@@ -46,6 +47,9 @@ under the License.
background-color: #fff;
text-align: left;
}
+ .dg {
+ text-align: left;
+ }
</style>
@@ -73,7 +77,7 @@ under the License.
{ name: '其它', value: 3.8 }
],
- ////////////////////////////////////////
+ // ////////////////////////////////////////
[
{ name: '银河帝国5:迈向基地', value: 3.8 },
{ name: '俞军产品方法论', value: 2.3 },
@@ -94,6 +98,8 @@ under the License.
]
];
+ const charts = [];
+
datas.forEach(function (data) {
const dom = document.createElement('div');
dom.classList.add('chart');
@@ -133,7 +139,8 @@ under the License.
},
labelLine: {
length: 15,
- length2: 0
+ length2: 0,
+ maxSurfaceAngle: 80
},
labelLayout: function (params) {
const isLeft = params.labelRect.x < chart.getWidth() / 2;
@@ -150,7 +157,41 @@ under the License.
data: data
}]
});
+
+ charts.push(chart);
});
+
+ const gui = new dat.GUI();
+ const config = {
+ labelLine: {
+ smooth: 0,
+ maxSurfaceAngle: 80
+ },
+ label: {
+ edgeDistance: 10
+ }
+ };
+
+ function update() {
+ charts.forEach(function (chart) {
+ chart.setOption({
+ series: {
+ label: Object.assign({}, config.label, {
+ edgeDistance: config.label.edgeDistance
+ }),
+ labelLine: config.labelLine
+ }
+ });
+ });
+ }
+
+ const labelFolder = gui.addFolder('label');
+ const labelLineFolder = gui.addFolder('labelLine');
+ labelFolder.open();
+ labelLineFolder.open();
+ labelFolder.add(config.label, 'edgeDistance', 0, 50).onChange(update);
+ labelLineFolder.add(config.labelLine, 'maxSurfaceAngle', 0, 90).onChange(update);
+ labelLineFolder.add(config.labelLine, 'smooth', 0, 1).onChange(update);
});
</script>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org