@visactor/vrender-components 0.13.9-alpha.1 → 0.13.9-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.d.ts +1 -1
- package/cjs/index.js +1 -1
- package/cjs/index.js.map +1 -1
- package/cjs/label/arc.d.ts +69 -0
- package/cjs/label/arc.js +397 -0
- package/cjs/label/arc.js.map +1 -0
- package/cjs/label/base.d.ts +3 -1
- package/cjs/label/base.js +69 -30
- package/cjs/label/base.js.map +1 -1
- package/cjs/label/dataLabel.js +3 -2
- package/cjs/label/dataLabel.js.map +1 -1
- package/cjs/label/index.d.ts +1 -0
- package/cjs/label/index.js +2 -1
- package/cjs/label/index.js.map +1 -1
- package/cjs/label/type.d.ts +41 -2
- package/cjs/label/type.js.map +1 -1
- package/cjs/label/util.d.ts +12 -0
- package/cjs/label/util.js +113 -0
- package/cjs/label/util.js.map +1 -0
- package/cjs/link-path/type.js +1 -2
- package/cjs/marker/base.js +2 -1
- package/cjs/pager/index.js +1 -1
- package/cjs/pager/pager.js +1 -1
- package/cjs/util/common.d.ts +1 -3
- package/cjs/util/common.js +2 -14
- package/cjs/util/common.js.map +1 -1
- package/dist/index.js +853 -61
- package/dist/index.min.js +1 -1
- package/es/index.d.ts +1 -1
- package/es/index.js +1 -1
- package/es/index.js.map +1 -1
- package/es/label/arc.d.ts +69 -0
- package/es/label/arc.js +387 -0
- package/es/label/arc.js.map +1 -0
- package/es/label/base.d.ts +3 -1
- package/es/label/base.js +71 -32
- package/es/label/base.js.map +1 -1
- package/es/label/dataLabel.js +4 -1
- package/es/label/dataLabel.js.map +1 -1
- package/es/label/index.d.ts +1 -0
- package/es/label/index.js +2 -0
- package/es/label/index.js.map +1 -1
- package/es/label/type.d.ts +41 -2
- package/es/label/type.js.map +1 -1
- package/es/label/util.d.ts +12 -0
- package/es/label/util.js +99 -0
- package/es/label/util.js.map +1 -0
- package/es/link-path/type.js +1 -2
- package/es/marker/base.js +2 -1
- package/es/pager/index.js +1 -1
- package/es/pager/pager.js +1 -1
- package/es/util/common.d.ts +1 -3
- package/es/util/common.js +0 -12
- package/es/util/common.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -341,26 +341,6 @@
|
|
|
341
341
|
}
|
|
342
342
|
return obj.visible !== false;
|
|
343
343
|
};
|
|
344
|
-
function getMarksByName(root, name) {
|
|
345
|
-
if (!name) {
|
|
346
|
-
return [];
|
|
347
|
-
}
|
|
348
|
-
const group = root.find(node => node.name === name, true);
|
|
349
|
-
if (!group) {
|
|
350
|
-
return [];
|
|
351
|
-
}
|
|
352
|
-
return group.getChildren();
|
|
353
|
-
}
|
|
354
|
-
function getNoneGroupMarksByName(root, name) {
|
|
355
|
-
if (!name) {
|
|
356
|
-
return [];
|
|
357
|
-
}
|
|
358
|
-
const group = root.find(node => node.name === name, true);
|
|
359
|
-
if (!group) {
|
|
360
|
-
return [];
|
|
361
|
-
}
|
|
362
|
-
return group.findAll(node => node.type !== 'group', true);
|
|
363
|
-
}
|
|
364
344
|
|
|
365
345
|
const defaultAlternativeColors = ['#ffffff', '#000000'];
|
|
366
346
|
function labelSmartInvert(foregroundColorOrigin, backgroundColorOrogin, textType, contrastRatiosThreshold, alternativeColors) {
|
|
@@ -1612,6 +1592,10 @@
|
|
|
1612
1592
|
_lastHover;
|
|
1613
1593
|
_lastSelect;
|
|
1614
1594
|
_enableAnimation;
|
|
1595
|
+
layoutArcLabels(position, attribute, currentMarks) {
|
|
1596
|
+
const arcs = [];
|
|
1597
|
+
return arcs;
|
|
1598
|
+
}
|
|
1615
1599
|
render() {
|
|
1616
1600
|
this._prepare();
|
|
1617
1601
|
const { overlap, smartInvert, dataFilter, customLayoutFunc, customOverlapFunc } = this.attribute;
|
|
@@ -1625,12 +1609,14 @@
|
|
|
1625
1609
|
}
|
|
1626
1610
|
else {
|
|
1627
1611
|
labels = this.layout(data);
|
|
1628
|
-
if (
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1612
|
+
if (this.attribute.type !== 'arc') {
|
|
1613
|
+
if (vutils.isFunction(customOverlapFunc)) {
|
|
1614
|
+
labels = customOverlapFunc(labels, (d) => this._idToGraphic.get(d.id));
|
|
1615
|
+
}
|
|
1616
|
+
else {
|
|
1617
|
+
if (overlap !== false) {
|
|
1618
|
+
labels = this._overlapping(labels);
|
|
1619
|
+
}
|
|
1634
1620
|
}
|
|
1635
1621
|
}
|
|
1636
1622
|
}
|
|
@@ -1714,7 +1700,7 @@
|
|
|
1714
1700
|
return text;
|
|
1715
1701
|
}
|
|
1716
1702
|
_prepare() {
|
|
1717
|
-
const baseMarks =
|
|
1703
|
+
const baseMarks = this.getBaseMarks();
|
|
1718
1704
|
const currentBaseMarks = [];
|
|
1719
1705
|
baseMarks.forEach(mark => {
|
|
1720
1706
|
if (mark.releaseStatus !== 'willRelease') {
|
|
@@ -1751,20 +1737,47 @@
|
|
|
1751
1737
|
const textData = data[i];
|
|
1752
1738
|
const baseMark = this._idToGraphic.get(textData.id);
|
|
1753
1739
|
const labelAttribute = {
|
|
1740
|
+
fill: baseMark.attribute.fill,
|
|
1754
1741
|
...textStyle,
|
|
1755
1742
|
...textData
|
|
1756
1743
|
};
|
|
1757
1744
|
const text = this._createLabelText(labelAttribute);
|
|
1758
1745
|
const textBounds = this.getGraphicBounds(text);
|
|
1759
1746
|
const graphicBounds = this.getGraphicBounds(baseMark, { x: textData.x, y: textData.y });
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1747
|
+
if (this.attribute.type === 'arc') {
|
|
1748
|
+
const graphicAttributes = baseMark.attribute;
|
|
1749
|
+
const { width, height } = this.attribute;
|
|
1750
|
+
this.labeling(textBounds, graphicBounds, vutils.isFunction(position) ? position(textData) : position, offset, graphicAttributes, textData, width, height, this.attribute);
|
|
1751
|
+
labels.push(text);
|
|
1752
|
+
}
|
|
1753
|
+
else {
|
|
1754
|
+
const textLocation = this.labeling(textBounds, graphicBounds, vutils.isFunction(position) ? position(textData) : position, offset);
|
|
1755
|
+
if (!textLocation) {
|
|
1756
|
+
continue;
|
|
1757
|
+
}
|
|
1758
|
+
labelAttribute.x = textLocation.x;
|
|
1759
|
+
labelAttribute.y = textLocation.y;
|
|
1760
|
+
text.setAttributes(textLocation);
|
|
1761
|
+
labels.push(text);
|
|
1762
|
+
}
|
|
1763
|
+
}
|
|
1764
|
+
if (this.attribute.type === 'arc') {
|
|
1765
|
+
const arcs = this.layoutArcLabels(position, this.attribute, Array.from(this._idToGraphic.values()));
|
|
1766
|
+
for (let i = 0; i < data.length; i++) {
|
|
1767
|
+
const textData = data[i];
|
|
1768
|
+
const basedArc = arcs.find(arc => arc.labelText === textData.text);
|
|
1769
|
+
const labelAttribute = {
|
|
1770
|
+
x: basedArc.labelPosition.x,
|
|
1771
|
+
y: basedArc.labelPosition.y,
|
|
1772
|
+
textAlign: this.attribute.textAlign ?? basedArc.textAlign,
|
|
1773
|
+
textBaseline: this.attribute.textBaseline ?? basedArc.textBaseline,
|
|
1774
|
+
angle: this.attribute.angle ?? basedArc.angle
|
|
1775
|
+
};
|
|
1776
|
+
labels[i].setAttributes(labelAttribute);
|
|
1777
|
+
labels[i].pointA = basedArc.pointA;
|
|
1778
|
+
labels[i].pointB = basedArc.pointB;
|
|
1779
|
+
labels[i].pointC = basedArc.pointC;
|
|
1763
1780
|
}
|
|
1764
|
-
labelAttribute.x = textLocation.x;
|
|
1765
|
-
labelAttribute.y = textLocation.y;
|
|
1766
|
-
text.setAttributes(textLocation);
|
|
1767
|
-
labels.push(text);
|
|
1768
1781
|
}
|
|
1769
1782
|
return labels;
|
|
1770
1783
|
}
|
|
@@ -1782,7 +1795,7 @@
|
|
|
1782
1795
|
if (size.width === 0 || size.height === 0) {
|
|
1783
1796
|
return labels;
|
|
1784
1797
|
}
|
|
1785
|
-
const { avoidBaseMark, strategy = [], hideOnHit = true, clampForce = true
|
|
1798
|
+
const { avoidBaseMark, strategy = [], hideOnHit = true, clampForce = true } = option;
|
|
1786
1799
|
const bmpTool = this._bmpTool || bitmapTool(size.width, size.height);
|
|
1787
1800
|
const bitmap = this._bitmap || bmpTool.bitmap();
|
|
1788
1801
|
const checkBounds = strategy.some(s => s.type === 'bound');
|
|
@@ -1791,18 +1804,6 @@
|
|
|
1791
1804
|
mark.AABBBounds && bitmap.setRange(boundToRange(bmpTool, mark.AABBBounds, true));
|
|
1792
1805
|
});
|
|
1793
1806
|
}
|
|
1794
|
-
if (avoidMarks.length > 0) {
|
|
1795
|
-
avoidMarks.forEach(avoid => {
|
|
1796
|
-
if (vutils.isString(avoid)) {
|
|
1797
|
-
getNoneGroupMarksByName(this.getRootNode(), avoid).forEach(avoidMark => {
|
|
1798
|
-
avoidMark.AABBBounds && bitmap.setRange(boundToRange(bmpTool, avoidMark.AABBBounds, true));
|
|
1799
|
-
});
|
|
1800
|
-
}
|
|
1801
|
-
else if (avoid.AABBBounds) {
|
|
1802
|
-
bitmap.setRange(boundToRange(bmpTool, avoid.AABBBounds, true));
|
|
1803
|
-
}
|
|
1804
|
-
});
|
|
1805
|
-
}
|
|
1806
1807
|
for (let i = 0; i < labels.length; i++) {
|
|
1807
1808
|
if (labels[i].visible === false) {
|
|
1808
1809
|
continue;
|
|
@@ -1822,16 +1823,7 @@
|
|
|
1822
1823
|
continue;
|
|
1823
1824
|
}
|
|
1824
1825
|
}
|
|
1825
|
-
|
|
1826
|
-
for (let j = 0; j < strategy.length; j++) {
|
|
1827
|
-
hasPlace = place(bmpTool, bitmap, strategy[j], this.attribute, text, this.getGraphicBounds(baseMark, labels[i]), this.labeling);
|
|
1828
|
-
if (hasPlace !== false) {
|
|
1829
|
-
text.setAttributes({ x: hasPlace.x, y: hasPlace.y });
|
|
1830
|
-
result.push(text);
|
|
1831
|
-
break;
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
if (!hasPlace && clampForce) {
|
|
1826
|
+
if (clampForce) {
|
|
1835
1827
|
const { dx = 0, dy = 0 } = clampText(text, bmpTool.width, bmpTool.height);
|
|
1836
1828
|
if (!(dx === 0 && dy === 0) &&
|
|
1837
1829
|
canPlace(bmpTool, bitmap, {
|
|
@@ -1841,11 +1833,19 @@
|
|
|
1841
1833
|
y2: text.AABBBounds.y2 + dy
|
|
1842
1834
|
})) {
|
|
1843
1835
|
text.setAttributes({ x: text.attribute.x + dx, y: text.attribute.y + dy });
|
|
1844
|
-
bitmap.setRange(boundToRange(bmpTool, text.AABBBounds, true));
|
|
1845
1836
|
result.push(text);
|
|
1846
1837
|
continue;
|
|
1847
1838
|
}
|
|
1848
1839
|
}
|
|
1840
|
+
let hasPlace = false;
|
|
1841
|
+
for (let j = 0; j < strategy.length; j++) {
|
|
1842
|
+
hasPlace = place(bmpTool, bitmap, strategy[j], this.attribute, text, this.getGraphicBounds(baseMark, labels[i]), this.labeling);
|
|
1843
|
+
if (hasPlace !== false) {
|
|
1844
|
+
text.setAttributes({ x: hasPlace.x, y: hasPlace.y });
|
|
1845
|
+
result.push(text);
|
|
1846
|
+
break;
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
1849
|
!hasPlace && !hideOnHit && result.push(text);
|
|
1850
1850
|
}
|
|
1851
1851
|
if (vutils.isFunction(this.onAfterLabelOverlap)) {
|
|
@@ -1853,6 +1853,13 @@
|
|
|
1853
1853
|
}
|
|
1854
1854
|
return result;
|
|
1855
1855
|
}
|
|
1856
|
+
getBaseMarks() {
|
|
1857
|
+
const baseMarkGroup = this.getBaseMarkGroup();
|
|
1858
|
+
if (!baseMarkGroup) {
|
|
1859
|
+
return;
|
|
1860
|
+
}
|
|
1861
|
+
return baseMarkGroup.getChildren();
|
|
1862
|
+
}
|
|
1856
1863
|
getBaseMarkGroup() {
|
|
1857
1864
|
const { baseMarkGroupName } = this.attribute;
|
|
1858
1865
|
if (!baseMarkGroupName) {
|
|
@@ -1880,14 +1887,29 @@
|
|
|
1880
1887
|
const prevTextMap = this._graphicToText || new Map();
|
|
1881
1888
|
const texts = [];
|
|
1882
1889
|
labels.forEach((text, index) => {
|
|
1890
|
+
let labelLine;
|
|
1891
|
+
if (this.attribute.type === 'arc' && this.attribute.position === 'outside') {
|
|
1892
|
+
labelLine = vrender.createPath({
|
|
1893
|
+
visible: text.attribute?.visible ?? true,
|
|
1894
|
+
stroke: text.attribute?.line?.stroke ?? text.attribute?.fill,
|
|
1895
|
+
lineWidth: 1,
|
|
1896
|
+
path: `M${Math.round(text.pointA.x)},${Math.round(text.pointA.y)}` +
|
|
1897
|
+
` L${Math.round(text.pointB.x)},${Math.round(text.pointB.y)}` +
|
|
1898
|
+
` L${Math.round(text.pointC.x)},${Math.round(text.pointC.y)}`
|
|
1899
|
+
});
|
|
1900
|
+
}
|
|
1883
1901
|
const relatedGraphic = this._idToGraphic.get(text.attribute.id);
|
|
1884
1902
|
const state = prevTextMap?.get(relatedGraphic) ? 'update' : 'enter';
|
|
1885
1903
|
if (state === 'enter') {
|
|
1886
1904
|
texts.push(text);
|
|
1905
|
+
if (this.attribute.type === 'arc' && this.attribute.position === 'outside') ;
|
|
1887
1906
|
currentTextMap.set(relatedGraphic, text);
|
|
1888
1907
|
if (!disableAnimation && relatedGraphic) {
|
|
1889
1908
|
const { from, to } = getAnimationAttributes(text.attribute, 'fadeIn');
|
|
1890
1909
|
this.add(text);
|
|
1910
|
+
if (this.attribute.type === 'arc' && this.attribute.position === 'outside') {
|
|
1911
|
+
this.add(labelLine);
|
|
1912
|
+
}
|
|
1891
1913
|
relatedGraphic.onAnimateBind = () => {
|
|
1892
1914
|
text.setAttributes(from);
|
|
1893
1915
|
const listener = this._afterRelatedGraphicAttributeUpdate(text, texts, index, relatedGraphic, {
|
|
@@ -1995,7 +2017,15 @@
|
|
|
1995
2017
|
continue;
|
|
1996
2018
|
}
|
|
1997
2019
|
const baseMark = this._idToGraphic.get(label.attribute.id);
|
|
1998
|
-
|
|
2020
|
+
let isInside = canPlaceInside(label.AABBBounds, baseMark?.AABBBounds);
|
|
2021
|
+
if (this.attribute.type === 'arc') {
|
|
2022
|
+
if (this.attribute.position === 'inside') {
|
|
2023
|
+
isInside = true;
|
|
2024
|
+
}
|
|
2025
|
+
else {
|
|
2026
|
+
isInside = false;
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
1999
2029
|
if (label.attribute.stroke && label.attribute.lineWidth > 0) {
|
|
2000
2030
|
label.setAttributes({
|
|
2001
2031
|
fill: labelSmartInvert(label.attribute.fill, label.attribute.stroke, textType, contrastRatiosThreshold, alternativeColors)
|
|
@@ -2222,9 +2252,769 @@
|
|
|
2222
2252
|
}
|
|
2223
2253
|
}
|
|
2224
2254
|
|
|
2255
|
+
function polarToCartesian(point) {
|
|
2256
|
+
if (!point.radius) {
|
|
2257
|
+
return { x: 0, y: 0 };
|
|
2258
|
+
}
|
|
2259
|
+
return {
|
|
2260
|
+
x: Math.cos(point.angle) * point.radius,
|
|
2261
|
+
y: Math.sin(point.angle) * point.radius
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
function circlePoint(x0, y0, radius, radian) {
|
|
2265
|
+
const offset = polarToCartesian({
|
|
2266
|
+
radius,
|
|
2267
|
+
angle: radian
|
|
2268
|
+
});
|
|
2269
|
+
return {
|
|
2270
|
+
x: x0 + offset.x,
|
|
2271
|
+
y: y0 + offset.y
|
|
2272
|
+
};
|
|
2273
|
+
}
|
|
2274
|
+
function computeQuadrant(angle) {
|
|
2275
|
+
angle = normalizeAngle(angle);
|
|
2276
|
+
if (angle > 0 && angle <= Math.PI / 2) {
|
|
2277
|
+
return 2;
|
|
2278
|
+
}
|
|
2279
|
+
else if (angle > Math.PI / 2 && angle <= Math.PI) {
|
|
2280
|
+
return 3;
|
|
2281
|
+
}
|
|
2282
|
+
else if (angle > Math.PI && angle <= (3 * Math.PI) / 2) {
|
|
2283
|
+
return 4;
|
|
2284
|
+
}
|
|
2285
|
+
return 1;
|
|
2286
|
+
}
|
|
2287
|
+
function normalizeAngle(angle) {
|
|
2288
|
+
while (angle < 0) {
|
|
2289
|
+
angle += Math.PI * 2;
|
|
2290
|
+
}
|
|
2291
|
+
while (angle >= Math.PI * 2) {
|
|
2292
|
+
angle -= Math.PI * 2;
|
|
2293
|
+
}
|
|
2294
|
+
return angle;
|
|
2295
|
+
}
|
|
2296
|
+
function isQuadrantLeft(quadrant) {
|
|
2297
|
+
return quadrant === 3 || quadrant === 4;
|
|
2298
|
+
}
|
|
2299
|
+
function isQuadrantRight(quadrant) {
|
|
2300
|
+
return quadrant === 1 || quadrant === 2;
|
|
2301
|
+
}
|
|
2302
|
+
function lineCirclePoints(a, b, c, x0, y0, r) {
|
|
2303
|
+
if ((a === 0 && b === 0) || r <= 0) {
|
|
2304
|
+
return [];
|
|
2305
|
+
}
|
|
2306
|
+
if (a === 0) {
|
|
2307
|
+
const y1 = -c / b;
|
|
2308
|
+
const fy = (y1 - y0) ** 2;
|
|
2309
|
+
const fd = r ** 2 - fy;
|
|
2310
|
+
if (fd < 0) {
|
|
2311
|
+
return [];
|
|
2312
|
+
}
|
|
2313
|
+
else if (fd === 0) {
|
|
2314
|
+
return [{ x: x0, y: y1 }];
|
|
2315
|
+
}
|
|
2316
|
+
const x1 = Math.sqrt(fd) + x0;
|
|
2317
|
+
const x2 = -Math.sqrt(fd) + x0;
|
|
2318
|
+
return [
|
|
2319
|
+
{ x: x1, y: y1 },
|
|
2320
|
+
{ x: x2, y: y1 }
|
|
2321
|
+
];
|
|
2322
|
+
}
|
|
2323
|
+
else if (b === 0) {
|
|
2324
|
+
const x1 = -c / a;
|
|
2325
|
+
const fx = (x1 - x0) ** 2;
|
|
2326
|
+
const fd = r ** 2 - fx;
|
|
2327
|
+
if (fd < 0) {
|
|
2328
|
+
return [];
|
|
2329
|
+
}
|
|
2330
|
+
else if (fd === 0) {
|
|
2331
|
+
return [{ x: x1, y: y0 }];
|
|
2332
|
+
}
|
|
2333
|
+
const y1 = Math.sqrt(fd) + y0;
|
|
2334
|
+
const y2 = -Math.sqrt(fd) + y0;
|
|
2335
|
+
return [
|
|
2336
|
+
{ x: x1, y: y1 },
|
|
2337
|
+
{ x: x1, y: y2 }
|
|
2338
|
+
];
|
|
2339
|
+
}
|
|
2340
|
+
const fa = (b / a) ** 2 + 1;
|
|
2341
|
+
const fb = 2 * ((c / a + x0) * (b / a) - y0);
|
|
2342
|
+
const fc = (c / a + x0) ** 2 + y0 ** 2 - r ** 2;
|
|
2343
|
+
const fd = fb ** 2 - 4 * fa * fc;
|
|
2344
|
+
if (fd < 0) {
|
|
2345
|
+
return [];
|
|
2346
|
+
}
|
|
2347
|
+
const y1 = (-fb + Math.sqrt(fd)) / (2 * fa);
|
|
2348
|
+
const y2 = (-fb - Math.sqrt(fd)) / (2 * fa);
|
|
2349
|
+
const x1 = -(b * y1 + c) / a;
|
|
2350
|
+
const x2 = -(b * y2 + c) / a;
|
|
2351
|
+
if (fd === 0) {
|
|
2352
|
+
return [{ x: x1, y: y1 }];
|
|
2353
|
+
}
|
|
2354
|
+
return [
|
|
2355
|
+
{ x: x1, y: y1 },
|
|
2356
|
+
{ x: x2, y: y2 }
|
|
2357
|
+
];
|
|
2358
|
+
}
|
|
2359
|
+
function connectLineRadian(radius, length) {
|
|
2360
|
+
if (length > radius * 2) {
|
|
2361
|
+
return NaN;
|
|
2362
|
+
}
|
|
2363
|
+
return Math.asin(length / 2 / radius) * 2;
|
|
2364
|
+
}
|
|
2365
|
+
function checkBoundsOverlap(boundsA, boundsB) {
|
|
2366
|
+
const { x1: ax1, y1: ay1, x2: ax2, y2: ay2 } = boundsA;
|
|
2367
|
+
const { x1: bx1, y1: by1, x2: bx2, y2: by2 } = boundsB;
|
|
2368
|
+
return !((ax1 <= bx1 && ax2 <= bx1) ||
|
|
2369
|
+
(ax1 >= bx2 && ax2 >= bx2) ||
|
|
2370
|
+
(ay1 <= by1 && ay2 <= by1) ||
|
|
2371
|
+
(ay1 >= by2 && ay2 >= by2));
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
class ArcInfo {
|
|
2375
|
+
key;
|
|
2376
|
+
refDatum;
|
|
2377
|
+
center;
|
|
2378
|
+
outerCenter;
|
|
2379
|
+
labelSize;
|
|
2380
|
+
labelPosition;
|
|
2381
|
+
labelLimit;
|
|
2382
|
+
labelVisible;
|
|
2383
|
+
lastLabelY;
|
|
2384
|
+
labelYRange;
|
|
2385
|
+
labelText;
|
|
2386
|
+
pointA;
|
|
2387
|
+
pointB;
|
|
2388
|
+
pointC;
|
|
2389
|
+
quadrant;
|
|
2390
|
+
radian;
|
|
2391
|
+
middleAngle;
|
|
2392
|
+
k;
|
|
2393
|
+
textAlign;
|
|
2394
|
+
textBaseline;
|
|
2395
|
+
angle;
|
|
2396
|
+
constructor(refDatum, center, outerCenter, quadrant, radian, middleAngle) {
|
|
2397
|
+
this.refDatum = refDatum;
|
|
2398
|
+
this.center = center;
|
|
2399
|
+
this.outerCenter = outerCenter;
|
|
2400
|
+
this.quadrant = quadrant;
|
|
2401
|
+
this.radian = radian;
|
|
2402
|
+
this.middleAngle = middleAngle;
|
|
2403
|
+
this.labelVisible = true;
|
|
2404
|
+
this.labelLimit = 0;
|
|
2405
|
+
}
|
|
2406
|
+
getLabelBounds() {
|
|
2407
|
+
if (!this.labelPosition || !this.labelSize) {
|
|
2408
|
+
return { x1: 0, x2: 0, y1: 0, y2: 0 };
|
|
2409
|
+
}
|
|
2410
|
+
return {
|
|
2411
|
+
x1: this.labelPosition.x - this.labelSize.width / 2,
|
|
2412
|
+
y1: this.labelPosition.y - this.labelSize.height / 2,
|
|
2413
|
+
x2: this.labelPosition.x + this.labelSize.width / 2,
|
|
2414
|
+
y2: this.labelPosition.y + this.labelSize.height / 2
|
|
2415
|
+
};
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
class ArcLabel extends LabelBase {
|
|
2419
|
+
name = 'arc-label';
|
|
2420
|
+
static defaultAttributes = {
|
|
2421
|
+
coverEnable: false,
|
|
2422
|
+
spaceWidth: 5,
|
|
2423
|
+
layoutArcGap: 6,
|
|
2424
|
+
textStyle: {
|
|
2425
|
+
visible: true,
|
|
2426
|
+
fontSize: 14,
|
|
2427
|
+
fontWeight: 'normal',
|
|
2428
|
+
fillOpacity: 1
|
|
2429
|
+
},
|
|
2430
|
+
position: 'outside',
|
|
2431
|
+
offset: 0,
|
|
2432
|
+
line: {
|
|
2433
|
+
visible: true,
|
|
2434
|
+
line1MinLength: 20,
|
|
2435
|
+
line2MinLength: 10
|
|
2436
|
+
},
|
|
2437
|
+
layout: {
|
|
2438
|
+
align: 'arc',
|
|
2439
|
+
strategy: 'priority',
|
|
2440
|
+
tangentConstraint: true
|
|
2441
|
+
}
|
|
2442
|
+
};
|
|
2443
|
+
_ellipsisWidth = 0;
|
|
2444
|
+
_arcLeft = new Map();
|
|
2445
|
+
_arcRight = new Map();
|
|
2446
|
+
constructor(attributes) {
|
|
2447
|
+
super(vutils.merge({}, ArcLabel.defaultAttributes, attributes));
|
|
2448
|
+
}
|
|
2449
|
+
labeling(textBounds, graphicBounds, position = 'outside', offset = 0, graphicAttributes, textData, width, height, attribute) {
|
|
2450
|
+
if (!textBounds || !graphicBounds) {
|
|
2451
|
+
return;
|
|
2452
|
+
}
|
|
2453
|
+
const radiusRatio = this.computeLayoutOuterRadius(graphicAttributes.outerRadius, width, height);
|
|
2454
|
+
const radius = this.computeRadius(radiusRatio, width, height);
|
|
2455
|
+
const center = { x: graphicAttributes?.x ?? 0, y: graphicAttributes?.y ?? 0 };
|
|
2456
|
+
const item = textData;
|
|
2457
|
+
const arcMiddleAngle = (graphicAttributes.startAngle + graphicAttributes.endAngle) / 2;
|
|
2458
|
+
const intervalAngle = graphicAttributes.endAngle - graphicAttributes.startAngle;
|
|
2459
|
+
const arcQuadrant = computeQuadrant(graphicAttributes.endAngle - intervalAngle / 2);
|
|
2460
|
+
const arcMiddle = circlePoint(center.x, center.y, graphicAttributes.outerRadius, arcMiddleAngle);
|
|
2461
|
+
const outerArcMiddle = circlePoint(center.x, center.y, radius + attribute.line.line1MinLength, arcMiddleAngle);
|
|
2462
|
+
const arc = new ArcInfo(item, arcMiddle, outerArcMiddle, arcQuadrant, intervalAngle, arcMiddleAngle);
|
|
2463
|
+
arc.pointA = circlePoint(center.x, center.y, this.computeDatumRadius(center.x * 2, center.y * 2, graphicAttributes.outerRadius), arc.middleAngle);
|
|
2464
|
+
arc.labelSize = {
|
|
2465
|
+
width: textBounds.x2 - textBounds.x1,
|
|
2466
|
+
height: textBounds.y2 - textBounds.y1
|
|
2467
|
+
};
|
|
2468
|
+
if (isQuadrantRight(arc.quadrant)) {
|
|
2469
|
+
arc.textAlign = 'left';
|
|
2470
|
+
arc.textBaseline = 'middle';
|
|
2471
|
+
this._arcRight.set(arc.refDatum, arc);
|
|
2472
|
+
}
|
|
2473
|
+
else if (isQuadrantLeft(arc.quadrant)) {
|
|
2474
|
+
arc.textAlign = 'right';
|
|
2475
|
+
arc.textBaseline = 'middle';
|
|
2476
|
+
this._arcLeft.set(arc.refDatum, arc);
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
layoutArcLabels(position, attribute, currentMarks) {
|
|
2480
|
+
const leftArcs = Array.from(this._arcLeft.values());
|
|
2481
|
+
const rightArcs = Array.from(this._arcRight.values());
|
|
2482
|
+
const arcs = [];
|
|
2483
|
+
if (position === 'inside') {
|
|
2484
|
+
arcs.push(...this._layoutInsideLabels(rightArcs, attribute, currentMarks));
|
|
2485
|
+
arcs.push(...this._layoutInsideLabels(leftArcs, attribute, currentMarks));
|
|
2486
|
+
}
|
|
2487
|
+
else {
|
|
2488
|
+
arcs.push(...this._layoutOutsideLabels(rightArcs, attribute, currentMarks));
|
|
2489
|
+
arcs.push(...this._layoutOutsideLabels(leftArcs, attribute, currentMarks));
|
|
2490
|
+
}
|
|
2491
|
+
return arcs;
|
|
2492
|
+
}
|
|
2493
|
+
_layoutInsideLabels(arcs, attribute, currentMarks) {
|
|
2494
|
+
const center = { x: currentMarks[0].attribute?.x ?? 0, y: currentMarks[0].attribute?.y ?? 0 };
|
|
2495
|
+
const innerRadiusRatio = this.computeLayoutOuterRadius(currentMarks[0].attribute.innerRadius, attribute.width, attribute.height);
|
|
2496
|
+
const outerRadiusRatio = this.computeLayoutOuterRadius(currentMarks[0].attribute.outerRadius, attribute.width, attribute.height);
|
|
2497
|
+
const labelConfig = attribute;
|
|
2498
|
+
const spaceWidth = labelConfig.spaceWidth;
|
|
2499
|
+
arcs.forEach((arc) => {
|
|
2500
|
+
const { labelSize, radian } = arc;
|
|
2501
|
+
const innerRadius = this.computeRadius(innerRadiusRatio, attribute.width, attribute.height, 1);
|
|
2502
|
+
const outerRadius = this.computeRadius(outerRadiusRatio, attribute.width, attribute.height, 1);
|
|
2503
|
+
const minRadian = connectLineRadian(outerRadius, labelSize.height);
|
|
2504
|
+
let limit;
|
|
2505
|
+
if (radian < minRadian) {
|
|
2506
|
+
limit = 0;
|
|
2507
|
+
}
|
|
2508
|
+
else {
|
|
2509
|
+
let minRadius;
|
|
2510
|
+
if (radian >= Math.PI) {
|
|
2511
|
+
minRadius = innerRadius;
|
|
2512
|
+
}
|
|
2513
|
+
else {
|
|
2514
|
+
minRadius = Math.max(innerRadius, labelSize.height / 2 / Math.tan(radian / 2));
|
|
2515
|
+
}
|
|
2516
|
+
limit = outerRadius - minRadius - spaceWidth;
|
|
2517
|
+
}
|
|
2518
|
+
if (labelConfig?.rotate !== true) {
|
|
2519
|
+
limit = outerRadius - spaceWidth;
|
|
2520
|
+
}
|
|
2521
|
+
const text = this._getFormatLabelText(arc.refDatum, limit);
|
|
2522
|
+
arc.labelText = text;
|
|
2523
|
+
const labelWidth = Math.min(limit, arc.labelSize.width);
|
|
2524
|
+
const align = this._computeAlign(arc, attribute);
|
|
2525
|
+
const alignOffset = align === 'left' ? labelWidth : align === 'right' ? 0 : labelWidth / 2;
|
|
2526
|
+
const labelRadius = outerRadius - spaceWidth - alignOffset;
|
|
2527
|
+
arc.labelPosition = circlePoint(center.x, center.y, labelRadius, arc.middleAngle);
|
|
2528
|
+
arc.labelLimit = labelWidth;
|
|
2529
|
+
if (!vutils.isGreater(labelWidth, 0)) {
|
|
2530
|
+
arc.labelVisible = false;
|
|
2531
|
+
}
|
|
2532
|
+
(arc.textAlign = 'center'), (arc.textBaseline = 'middle');
|
|
2533
|
+
arc.angle = arc.middleAngle;
|
|
2534
|
+
});
|
|
2535
|
+
return arcs;
|
|
2536
|
+
}
|
|
2537
|
+
_layoutOutsideLabels(arcs, attribute, currentMarks) {
|
|
2538
|
+
const center = { x: currentMarks[0].attribute?.x ?? 0, y: currentMarks[0].attribute?.y ?? 0 };
|
|
2539
|
+
const height = center.y * 2;
|
|
2540
|
+
const line2MinLength = attribute.line.line2MinLength;
|
|
2541
|
+
const labelLayout = attribute.layout;
|
|
2542
|
+
const spaceWidth = attribute.spaceWidth;
|
|
2543
|
+
arcs.forEach(arc => {
|
|
2544
|
+
const direction = isQuadrantLeft(arc.quadrant) ? -1 : 1;
|
|
2545
|
+
arc.labelPosition = {
|
|
2546
|
+
x: arc.outerCenter.x + direction * (arc.labelSize.width / 2 + line2MinLength + spaceWidth),
|
|
2547
|
+
y: arc.outerCenter.y
|
|
2548
|
+
};
|
|
2549
|
+
});
|
|
2550
|
+
arcs.sort((a, b) => {
|
|
2551
|
+
return a.labelPosition.y - b.labelPosition.y;
|
|
2552
|
+
});
|
|
2553
|
+
if (attribute.coverEnable !== false || labelLayout.strategy === 'none') {
|
|
2554
|
+
for (const arc of arcs) {
|
|
2555
|
+
const { labelPosition, labelSize } = arc;
|
|
2556
|
+
arc.labelLimit = labelSize.width;
|
|
2557
|
+
arc.pointB = isQuadrantLeft(arc.quadrant)
|
|
2558
|
+
? {
|
|
2559
|
+
x: labelPosition.x + labelSize.width / 2 + line2MinLength + spaceWidth,
|
|
2560
|
+
y: labelPosition.y
|
|
2561
|
+
}
|
|
2562
|
+
: {
|
|
2563
|
+
x: labelPosition.x - labelSize.width / 2 - line2MinLength - spaceWidth,
|
|
2564
|
+
y: labelPosition.y
|
|
2565
|
+
};
|
|
2566
|
+
this._computeX(arc, attribute, currentMarks);
|
|
2567
|
+
}
|
|
2568
|
+
if (attribute.coverEnable === false && labelLayout.strategy === 'none') {
|
|
2569
|
+
this._coverLabels(arcs);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
else {
|
|
2573
|
+
const maxLabels = height / (attribute.textStyle?.fontSize || 16);
|
|
2574
|
+
this._adjustY(arcs, maxLabels, attribute, currentMarks);
|
|
2575
|
+
const { minY, maxY } = arcs.reduce((yInfo, arc) => {
|
|
2576
|
+
const { y1, y2 } = arc.getLabelBounds();
|
|
2577
|
+
yInfo.minY = Math.max(0, Math.min(y1, yInfo.minY));
|
|
2578
|
+
yInfo.maxY = Math.min(height, Math.max(y2, yInfo.maxY));
|
|
2579
|
+
return yInfo;
|
|
2580
|
+
}, { minY: Infinity, maxY: -Infinity });
|
|
2581
|
+
const halfY = Math.max(Math.abs(height / 2 - minY), Math.abs(maxY - height / 2));
|
|
2582
|
+
const r = this._computeLayoutRadius(halfY, attribute, currentMarks);
|
|
2583
|
+
for (const arc of arcs) {
|
|
2584
|
+
this._computePointB(arc, r, attribute, currentMarks);
|
|
2585
|
+
this._computeX(arc, attribute, currentMarks);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
const width = center.x * 2;
|
|
2589
|
+
arcs.forEach(arc => {
|
|
2590
|
+
if (arc.labelVisible &&
|
|
2591
|
+
(vutils.isLess(arc.pointB.x, line2MinLength + spaceWidth) ||
|
|
2592
|
+
vutils.isGreater(arc.pointB.x, width - line2MinLength - spaceWidth))) {
|
|
2593
|
+
arc.labelVisible = false;
|
|
2594
|
+
}
|
|
2595
|
+
arc.angle = 0;
|
|
2596
|
+
});
|
|
2597
|
+
return arcs;
|
|
2598
|
+
}
|
|
2599
|
+
_computeX(arc, attribute, currentMarks) {
|
|
2600
|
+
const center = { x: currentMarks[0].attribute?.x ?? 0, y: currentMarks[0].attribute?.y ?? 0 };
|
|
2601
|
+
const plotLayout = { width: center.x * 2, height: center.y * 2 };
|
|
2602
|
+
const radiusRatio = this.computeLayoutOuterRadius(currentMarks[0].attribute.outerRadius, attribute.width, attribute.height);
|
|
2603
|
+
const line1MinLength = attribute.line.line1MinLength;
|
|
2604
|
+
const line2MinLength = attribute.line.line2MinLength;
|
|
2605
|
+
const labelLayoutAlign = attribute.layout?.align;
|
|
2606
|
+
const spaceWidth = attribute.spaceWidth;
|
|
2607
|
+
const align = this._computeAlign(arc, attribute);
|
|
2608
|
+
const { labelPosition, quadrant, pointB } = arc;
|
|
2609
|
+
if (!vutils.isValidNumber(pointB.x * pointB.y)) {
|
|
2610
|
+
arc.pointC = { x: NaN, y: NaN };
|
|
2611
|
+
labelPosition.x = NaN;
|
|
2612
|
+
arc.labelLimit = 0;
|
|
2613
|
+
}
|
|
2614
|
+
const radius = this.computeRadius(radiusRatio, attribute.width, attribute.height);
|
|
2615
|
+
const flag = isQuadrantLeft(quadrant) ? -1 : 1;
|
|
2616
|
+
let cx = 0;
|
|
2617
|
+
const restWidth = flag > 0 ? plotLayout.width - pointB.x : pointB.x;
|
|
2618
|
+
let limit = restWidth - line2MinLength - spaceWidth;
|
|
2619
|
+
if (labelLayoutAlign === 'labelLine') {
|
|
2620
|
+
cx = (radius + line1MinLength + line2MinLength) * flag + center.x;
|
|
2621
|
+
limit = (flag > 0 ? plotLayout.width - cx : cx) - spaceWidth;
|
|
2622
|
+
}
|
|
2623
|
+
const text = this._getFormatLabelText(arc.refDatum, limit);
|
|
2624
|
+
arc.labelText = text;
|
|
2625
|
+
let labelWidth = Math.min(limit, arc.labelSize.width);
|
|
2626
|
+
switch (labelLayoutAlign) {
|
|
2627
|
+
case 'labelLine':
|
|
2628
|
+
break;
|
|
2629
|
+
case 'edge':
|
|
2630
|
+
cx = flag > 0 ? plotLayout.width - labelWidth - spaceWidth : labelWidth + spaceWidth;
|
|
2631
|
+
break;
|
|
2632
|
+
case 'arc':
|
|
2633
|
+
default:
|
|
2634
|
+
cx = pointB.x + flag * line2MinLength;
|
|
2635
|
+
break;
|
|
2636
|
+
}
|
|
2637
|
+
labelWidth = Math.max(this._ellipsisWidth, labelWidth);
|
|
2638
|
+
arc.pointC = { x: cx, y: labelPosition.y };
|
|
2639
|
+
if (labelLayoutAlign === 'edge') {
|
|
2640
|
+
const alignOffset = this._computeAlignOffset(align, labelWidth, -flag);
|
|
2641
|
+
labelPosition.x = flag > 0 ? plotLayout.width + alignOffset : alignOffset;
|
|
2642
|
+
}
|
|
2643
|
+
else {
|
|
2644
|
+
const alignOffset = this._computeAlignOffset(align, labelWidth, flag);
|
|
2645
|
+
labelPosition.x = cx + alignOffset + flag * spaceWidth;
|
|
2646
|
+
}
|
|
2647
|
+
arc.labelLimit = labelWidth;
|
|
2648
|
+
}
|
|
2649
|
+
_computeAlignOffset(align, labelWidth, alignFlag) {
|
|
2650
|
+
switch (align) {
|
|
2651
|
+
case 'left':
|
|
2652
|
+
return alignFlag < 0 ? -labelWidth : 0;
|
|
2653
|
+
case 'right':
|
|
2654
|
+
return alignFlag < 0 ? 0 : labelWidth;
|
|
2655
|
+
case 'center':
|
|
2656
|
+
default:
|
|
2657
|
+
return (labelWidth / 2) * alignFlag;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
_computeAlign(arc, attribute) {
|
|
2661
|
+
const labelConfig = attribute;
|
|
2662
|
+
const textAlign = labelConfig.textStyle?.textAlign ?? labelConfig.textStyle?.align;
|
|
2663
|
+
const layoutAlign = labelConfig.layout?.textAlign ?? labelConfig.layout?.align;
|
|
2664
|
+
if (labelConfig.position !== 'inside') {
|
|
2665
|
+
if (vutils.isNil(textAlign) || textAlign === 'auto') {
|
|
2666
|
+
if (layoutAlign === 'edge') {
|
|
2667
|
+
return isQuadrantLeft(arc.quadrant) ? 'left' : 'right';
|
|
2668
|
+
}
|
|
2669
|
+
return isQuadrantLeft(arc.quadrant) ? 'right' : 'left';
|
|
2670
|
+
}
|
|
2671
|
+
return textAlign;
|
|
2672
|
+
}
|
|
2673
|
+
return vutils.isNil(textAlign) || textAlign === 'auto' ? 'center' : textAlign;
|
|
2674
|
+
}
|
|
2675
|
+
_getFormatLabelText(value, limit) {
|
|
2676
|
+
return value.text;
|
|
2677
|
+
}
|
|
2678
|
+
_adjustY(arcs, maxLabels, attribute, currentMarks) {
|
|
2679
|
+
const center = { x: currentMarks[0].attribute?.x ?? 0, y: currentMarks[0].attribute?.y ?? 0 };
|
|
2680
|
+
const plotRect = { width: center.x * 2, height: center.y * 2 };
|
|
2681
|
+
const labelLayout = attribute.layout;
|
|
2682
|
+
if (labelLayout.strategy === 'vertical') {
|
|
2683
|
+
let lastY = 0;
|
|
2684
|
+
let delta;
|
|
2685
|
+
const len = arcs.length;
|
|
2686
|
+
if (len <= 0) {
|
|
2687
|
+
return;
|
|
2688
|
+
}
|
|
2689
|
+
for (let i = 0; i < len; i++) {
|
|
2690
|
+
const { y1 } = arcs[i].getLabelBounds();
|
|
2691
|
+
delta = y1 - lastY;
|
|
2692
|
+
if (vutils.isLess(delta, 0)) {
|
|
2693
|
+
const index = this._shiftY(arcs, i, len - 1, -delta);
|
|
2694
|
+
this._shiftY(arcs, index, 0, delta / 2);
|
|
2695
|
+
}
|
|
2696
|
+
const { y2 } = arcs[i].getLabelBounds();
|
|
2697
|
+
lastY = y2;
|
|
2698
|
+
}
|
|
2699
|
+
const { y1: firstY1 } = arcs[0].getLabelBounds();
|
|
2700
|
+
delta = firstY1 - 0;
|
|
2701
|
+
if (vutils.isLess(delta, 0)) {
|
|
2702
|
+
this._shiftY(arcs, 0, len - 1, -delta);
|
|
2703
|
+
}
|
|
2704
|
+
for (let i = arcs.length - 1; i >= 0; i--) {
|
|
2705
|
+
if (arcs[i].getLabelBounds().y2 > plotRect.height) {
|
|
2706
|
+
arcs[i].labelVisible = false;
|
|
2707
|
+
}
|
|
2708
|
+
else {
|
|
2709
|
+
break;
|
|
2710
|
+
}
|
|
2711
|
+
}
|
|
2712
|
+
}
|
|
2713
|
+
else if (labelLayout.strategy !== 'none') {
|
|
2714
|
+
const priorityArcs = arcs.map((arc, i) => {
|
|
2715
|
+
return {
|
|
2716
|
+
arc,
|
|
2717
|
+
originIndex: i,
|
|
2718
|
+
priorityIndex: 0
|
|
2719
|
+
};
|
|
2720
|
+
});
|
|
2721
|
+
priorityArcs.sort((a, b) => {
|
|
2722
|
+
return b.arc.radian - a.arc.radian;
|
|
2723
|
+
});
|
|
2724
|
+
priorityArcs.forEach((priorityArc, i) => {
|
|
2725
|
+
priorityArc.priorityIndex = i;
|
|
2726
|
+
priorityArc.arc.labelVisible = false;
|
|
2727
|
+
});
|
|
2728
|
+
let topLabelIndex = Infinity;
|
|
2729
|
+
let bottomLabelIndex = -Infinity;
|
|
2730
|
+
for (let i = 0; i < maxLabels && i < arcs.length; i++) {
|
|
2731
|
+
this._storeY(arcs);
|
|
2732
|
+
const arc = priorityArcs[i].arc;
|
|
2733
|
+
this._computeYRange(arc, attribute, currentMarks);
|
|
2734
|
+
arc.labelVisible = true;
|
|
2735
|
+
const curY = arc.labelPosition.y;
|
|
2736
|
+
const { lastIndex, nextIndex } = this._findNeighborIndex(arcs, priorityArcs[i]);
|
|
2737
|
+
const lastArc = arcs[lastIndex];
|
|
2738
|
+
const nextArc = arcs[nextIndex];
|
|
2739
|
+
if (lastIndex === -1 && nextIndex !== -1) {
|
|
2740
|
+
const nextY = nextArc.labelPosition.y;
|
|
2741
|
+
if (curY > nextY) {
|
|
2742
|
+
arc.labelPosition.y = nextY - nextArc.labelSize.height / 2 - arc.labelSize.height / 2;
|
|
2743
|
+
}
|
|
2744
|
+
else {
|
|
2745
|
+
this._twoWayShift(arcs, arc, nextArc, nextIndex);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
else if (lastIndex !== -1 && nextIndex === -1) {
|
|
2749
|
+
const lastY = lastArc.labelPosition.y;
|
|
2750
|
+
if (curY < lastY) {
|
|
2751
|
+
arc.labelPosition.y = lastY + lastArc.labelSize.height / 2 + arc.labelSize.height / 2;
|
|
2752
|
+
}
|
|
2753
|
+
else {
|
|
2754
|
+
this._twoWayShift(arcs, lastArc, arc, priorityArcs[i].originIndex);
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
else if (lastIndex !== -1 && nextIndex !== -1) {
|
|
2758
|
+
const lastY = lastArc.labelPosition.y;
|
|
2759
|
+
const nextY = nextArc.labelPosition.y;
|
|
2760
|
+
if (curY > nextY) {
|
|
2761
|
+
arc.labelPosition.y = nextY - nextArc.labelSize.height / 2 - arc.labelSize.height / 2;
|
|
2762
|
+
this._twoWayShift(arcs, lastArc, arc, priorityArcs[i].originIndex);
|
|
2763
|
+
}
|
|
2764
|
+
else if (curY < lastY) {
|
|
2765
|
+
arc.labelPosition.y = lastY + lastArc.labelSize.height / 2 + arc.labelSize.height / 2;
|
|
2766
|
+
this._twoWayShift(arcs, arc, nextArc, nextIndex);
|
|
2767
|
+
}
|
|
2768
|
+
else {
|
|
2769
|
+
this._twoWayShift(arcs, lastArc, arc, priorityArcs[i].originIndex);
|
|
2770
|
+
this._twoWayShift(arcs, arc, nextArc, nextIndex);
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
const nextTopIndex = Math.min(topLabelIndex, priorityArcs[i].originIndex);
|
|
2774
|
+
const nextBottomIndex = Math.max(bottomLabelIndex, priorityArcs[i].originIndex);
|
|
2775
|
+
let delta;
|
|
2776
|
+
delta = arcs[nextBottomIndex].getLabelBounds().y2 - plotRect.height;
|
|
2777
|
+
if (vutils.isGreater(delta, 0)) {
|
|
2778
|
+
this._shiftY(arcs, nextBottomIndex, 0, -delta);
|
|
2779
|
+
}
|
|
2780
|
+
delta = arcs[nextTopIndex].getLabelBounds().y1 - 0;
|
|
2781
|
+
if (vutils.isLess(delta, 0)) {
|
|
2782
|
+
this._shiftY(arcs, nextTopIndex, arcs.length - 1, -delta);
|
|
2783
|
+
}
|
|
2784
|
+
delta = arcs[nextBottomIndex].getLabelBounds().y2 - plotRect.height;
|
|
2785
|
+
if (vutils.isGreater(delta, 0)) {
|
|
2786
|
+
arc.labelVisible = false;
|
|
2787
|
+
this._restoreY(arcs);
|
|
2788
|
+
break;
|
|
2789
|
+
}
|
|
2790
|
+
else if (labelLayout.tangentConstraint && !this._checkYRange(arcs)) {
|
|
2791
|
+
arc.labelVisible = false;
|
|
2792
|
+
this._restoreY(arcs);
|
|
2793
|
+
}
|
|
2794
|
+
else {
|
|
2795
|
+
topLabelIndex = nextTopIndex;
|
|
2796
|
+
bottomLabelIndex = nextBottomIndex;
|
|
2797
|
+
}
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
_shiftY(arcs, start, end, delta) {
|
|
2802
|
+
const direction = start < end ? 1 : -1;
|
|
2803
|
+
let index = start;
|
|
2804
|
+
while (index !== -1) {
|
|
2805
|
+
arcs[index].labelPosition.y += delta;
|
|
2806
|
+
const nextIndex = this._findNextVisibleIndex(arcs, index, end, direction);
|
|
2807
|
+
if (nextIndex >= 0 && nextIndex < arcs.length) {
|
|
2808
|
+
const { y1: curY1, y2: curY2 } = arcs[index].getLabelBounds();
|
|
2809
|
+
const { y1: nextY1, y2: nextY2 } = arcs[nextIndex].getLabelBounds();
|
|
2810
|
+
if ((direction > 0 && curY2 < nextY1) || (direction < 0 && curY1 > nextY2)) {
|
|
2811
|
+
return index;
|
|
2812
|
+
}
|
|
2813
|
+
}
|
|
2814
|
+
index = nextIndex;
|
|
2815
|
+
}
|
|
2816
|
+
return end;
|
|
2817
|
+
}
|
|
2818
|
+
_findNextVisibleIndex(arcs, start, end, direction) {
|
|
2819
|
+
const diff = (end - start) * direction;
|
|
2820
|
+
for (let i = 1; i <= diff; i++) {
|
|
2821
|
+
const index = start + i * direction;
|
|
2822
|
+
if (arcs[index].labelVisible) {
|
|
2823
|
+
return index;
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
return -1;
|
|
2827
|
+
}
|
|
2828
|
+
_computePointB(arc, r, attribute, currentMarks) {
|
|
2829
|
+
const labelConfig = attribute;
|
|
2830
|
+
const radiusRatio = this.computeLayoutOuterRadius(currentMarks[0].attribute.outerRadius, attribute.width, attribute.height);
|
|
2831
|
+
const line1MinLength = labelConfig.line.line1MinLength;
|
|
2832
|
+
const labelLayout = labelConfig.layout;
|
|
2833
|
+
if (labelLayout.strategy === 'none') {
|
|
2834
|
+
arc.pointB = {
|
|
2835
|
+
x: arc.outerCenter.x,
|
|
2836
|
+
y: arc.outerCenter.y
|
|
2837
|
+
};
|
|
2838
|
+
}
|
|
2839
|
+
else {
|
|
2840
|
+
const center = { x: currentMarks[0].attribute?.x ?? 0, y: currentMarks[0].attribute?.y ?? 0 };
|
|
2841
|
+
const radius = this.computeRadius(radiusRatio, attribute.width, attribute.height);
|
|
2842
|
+
const { labelPosition, quadrant } = arc;
|
|
2843
|
+
const outerR = Math.max(radius + line1MinLength, currentMarks[0].attribute.outerRadius);
|
|
2844
|
+
const rd = r - outerR;
|
|
2845
|
+
const x = Math.sqrt(r ** 2 - Math.abs(center.y - labelPosition.y) ** 2) - rd;
|
|
2846
|
+
if (vutils.isValidNumber(x)) {
|
|
2847
|
+
arc.pointB = {
|
|
2848
|
+
x: center.x + x * (isQuadrantLeft(quadrant) ? -1 : 1),
|
|
2849
|
+
y: labelPosition.y
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
else {
|
|
2853
|
+
arc.pointB = { x: NaN, y: NaN };
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
_storeY(arcs) {
|
|
2858
|
+
for (const arc of arcs) {
|
|
2859
|
+
if (arc.labelVisible) {
|
|
2860
|
+
arc.lastLabelY = arc.labelPosition.y;
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
_computeYRange(arc, attribute, currentMarks) {
|
|
2865
|
+
const center = { x: currentMarks[0].attribute?.x ?? 0, y: currentMarks[0].attribute?.y ?? 0 };
|
|
2866
|
+
const plotRect = { width: center.x * 2, height: center.y * 2 };
|
|
2867
|
+
const radiusRatio = this.computeLayoutOuterRadius(currentMarks[0].attribute.outerRadius, attribute.width, attribute.height);
|
|
2868
|
+
const line1MinLength = attribute.line.line1MinLength;
|
|
2869
|
+
const { width, height } = plotRect;
|
|
2870
|
+
const radius = this.computeRadius(radiusRatio, attribute.width, attribute.height);
|
|
2871
|
+
const r = this._computeLayoutRadius(height / 2, attribute, currentMarks);
|
|
2872
|
+
const cx = Math.abs(arc.center.x - width / 2);
|
|
2873
|
+
const cy = arc.center.y - height / 2;
|
|
2874
|
+
let a;
|
|
2875
|
+
let b;
|
|
2876
|
+
let c;
|
|
2877
|
+
if (vutils.isNumberClose(width / 2, cx)) {
|
|
2878
|
+
a = 0;
|
|
2879
|
+
b = 1;
|
|
2880
|
+
c = -cy;
|
|
2881
|
+
}
|
|
2882
|
+
else if (vutils.isNumberClose(height / 2, cy)) {
|
|
2883
|
+
a = 1;
|
|
2884
|
+
b = 0;
|
|
2885
|
+
c = -cx;
|
|
2886
|
+
}
|
|
2887
|
+
else {
|
|
2888
|
+
const k = -1 / (cy / cx);
|
|
2889
|
+
a = k;
|
|
2890
|
+
b = -1;
|
|
2891
|
+
c = cy - k * cx;
|
|
2892
|
+
}
|
|
2893
|
+
const points = lineCirclePoints(a, b, c, line1MinLength + radius - r, 0, r);
|
|
2894
|
+
if (points.length < 2) {
|
|
2895
|
+
return;
|
|
2896
|
+
}
|
|
2897
|
+
let min;
|
|
2898
|
+
let max;
|
|
2899
|
+
if (points[0].x > points[1].x) {
|
|
2900
|
+
points.reverse();
|
|
2901
|
+
}
|
|
2902
|
+
if (points[0].x < 0) {
|
|
2903
|
+
if (vutils.isNumberClose(points[0].y, points[1].y)) {
|
|
2904
|
+
if (Math.abs(arc.middleAngle) < Math.PI / 2) {
|
|
2905
|
+
min = 0;
|
|
2906
|
+
max = points[1].y + height / 2;
|
|
2907
|
+
}
|
|
2908
|
+
else {
|
|
2909
|
+
min = points[1].y + height / 2;
|
|
2910
|
+
max = height;
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
else if (points[0].y < points[1].y) {
|
|
2914
|
+
min = 0;
|
|
2915
|
+
max = points[1].y + height / 2;
|
|
2916
|
+
}
|
|
2917
|
+
else {
|
|
2918
|
+
min = points[1].y + height / 2;
|
|
2919
|
+
max = plotRect.height;
|
|
2920
|
+
}
|
|
2921
|
+
}
|
|
2922
|
+
else {
|
|
2923
|
+
min = Math.min(points[0].y, points[1].y) + height / 2;
|
|
2924
|
+
max = Math.max(points[0].y, points[1].y) + height / 2;
|
|
2925
|
+
}
|
|
2926
|
+
arc.labelYRange = [min, max];
|
|
2927
|
+
}
|
|
2928
|
+
_computeLayoutRadius(halfYLength, attribute, currentMarks) {
|
|
2929
|
+
const labelConfig = attribute;
|
|
2930
|
+
const layoutArcGap = labelConfig.layoutArcGap;
|
|
2931
|
+
const line1MinLength = labelConfig.line.line1MinLength;
|
|
2932
|
+
const radiusRatio = this.computeLayoutOuterRadius(currentMarks[0].attribute.outerRadius, attribute.width, attribute.height);
|
|
2933
|
+
const radius = this.computeRadius(radiusRatio, attribute.width, attribute.height);
|
|
2934
|
+
const outerR = radius + line1MinLength;
|
|
2935
|
+
const a = outerR - layoutArcGap;
|
|
2936
|
+
return Math.max((a ** 2 + halfYLength ** 2) / (2 * a), outerR);
|
|
2937
|
+
}
|
|
2938
|
+
_findNeighborIndex(arcs, priorityArc) {
|
|
2939
|
+
const index = priorityArc.originIndex;
|
|
2940
|
+
let lastIndex = -1;
|
|
2941
|
+
let nextIndex = -1;
|
|
2942
|
+
for (let i = index - 1; i >= 0; i--) {
|
|
2943
|
+
if (arcs[i].labelVisible) {
|
|
2944
|
+
lastIndex = i;
|
|
2945
|
+
break;
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
for (let i = index + 1; i < arcs.length; i++) {
|
|
2949
|
+
if (arcs[i].labelVisible) {
|
|
2950
|
+
nextIndex = i;
|
|
2951
|
+
break;
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
return {
|
|
2955
|
+
lastIndex,
|
|
2956
|
+
nextIndex
|
|
2957
|
+
};
|
|
2958
|
+
}
|
|
2959
|
+
_twoWayShift(arcs, lastArc, nextArc, nextIndex) {
|
|
2960
|
+
const delta = nextArc.getLabelBounds().y1 - lastArc.getLabelBounds().y2;
|
|
2961
|
+
if (vutils.isLess(delta, 0)) {
|
|
2962
|
+
const i = this._shiftY(arcs, nextIndex, arcs.length - 1, -delta);
|
|
2963
|
+
this._shiftY(arcs, i, 0, delta / 2);
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
_restoreY(arcs) {
|
|
2967
|
+
for (const arc of arcs) {
|
|
2968
|
+
if (arc.labelVisible) {
|
|
2969
|
+
arc.labelPosition.y = arc.lastLabelY;
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
_checkYRange(arcs) {
|
|
2974
|
+
for (const arc of arcs) {
|
|
2975
|
+
const { labelYRange, labelPosition } = arc;
|
|
2976
|
+
if (arc.labelVisible &&
|
|
2977
|
+
labelYRange &&
|
|
2978
|
+
(vutils.isLess(labelPosition.y, labelYRange[0]) || vutils.isGreater(labelPosition.y, labelYRange[1]))) {
|
|
2979
|
+
return false;
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
return true;
|
|
2983
|
+
}
|
|
2984
|
+
_coverLabels(arcs) {
|
|
2985
|
+
if (arcs.length <= 1) {
|
|
2986
|
+
return;
|
|
2987
|
+
}
|
|
2988
|
+
let lastBounds = arcs[0].getLabelBounds();
|
|
2989
|
+
for (let i = 1; i < arcs.length; i++) {
|
|
2990
|
+
const bounds = arcs[i].getLabelBounds();
|
|
2991
|
+
if (!checkBoundsOverlap(lastBounds, bounds)) {
|
|
2992
|
+
lastBounds = bounds;
|
|
2993
|
+
}
|
|
2994
|
+
else {
|
|
2995
|
+
arcs[i].labelVisible = false;
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
computeRadius(r, width, height, k) {
|
|
3000
|
+
return this.computeLayoutRadius(width ? width : 0, height ? height : 0) * r * (vutils.isNil(k) ? 1 : k);
|
|
3001
|
+
}
|
|
3002
|
+
computeLayoutRadius(width, height) {
|
|
3003
|
+
return Math.min(width / 2, height / 2);
|
|
3004
|
+
}
|
|
3005
|
+
computeLayoutOuterRadius(r, width, height) {
|
|
3006
|
+
return r / (Math.min(width, height) / 2);
|
|
3007
|
+
}
|
|
3008
|
+
computeDatumRadius(width, height, outerRadius) {
|
|
3009
|
+
const outerRadiusRatio = this.computeLayoutOuterRadius(outerRadius, width, height);
|
|
3010
|
+
return this.computeLayoutRadius(width ? width : 0, height ? height : 0) * outerRadiusRatio;
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
|
|
2225
3014
|
const labelComponentMap = {
|
|
2226
3015
|
rect: RectLabel,
|
|
2227
|
-
symbol: SymbolLabel
|
|
3016
|
+
symbol: SymbolLabel,
|
|
3017
|
+
arc: ArcLabel
|
|
2228
3018
|
};
|
|
2229
3019
|
class DataLabel extends AbstractComponent {
|
|
2230
3020
|
name = 'data-label';
|
|
@@ -9621,9 +10411,11 @@
|
|
|
9621
10411
|
}
|
|
9622
10412
|
}
|
|
9623
10413
|
|
|
9624
|
-
const version = "0.13.9-alpha.
|
|
10414
|
+
const version = "0.13.9-alpha.5";
|
|
9625
10415
|
|
|
9626
10416
|
exports.AbstractComponent = AbstractComponent;
|
|
10417
|
+
exports.ArcInfo = ArcInfo;
|
|
10418
|
+
exports.ArcLabel = ArcLabel;
|
|
9627
10419
|
exports.BasePlayer = BasePlayer;
|
|
9628
10420
|
exports.Brush = Brush;
|
|
9629
10421
|
exports.CircleAxis = CircleAxis;
|