From d07be486b2357713ab1b22ea2d3f8c109b05dee7 Mon Sep 17 00:00:00 2001 From: Bairui Su Date: Tue, 16 Jul 2024 17:24:07 +0800 Subject: [PATCH] feat(pie): optimize spider (#6357) * feat(pie): optimize spider * feat(label): optimize dodgeY * refactor(spdier): label line * docs(spider): add examples --- .../polio-point-stack-enter/interval0-0.svg | 2 +- .../polio-point-stack-enter/interval0-1.svg | 2 +- .../polio-point-stack-enter/interval0-2.svg | 2 +- .../polio-point-stack-enter/interval0-3.svg | 2 +- .../step0.svg | 2 +- .../step1.svg | 2 +- .../indices-line-chart-index-series/step0.svg | 10 +- .../snapshots/static/aaplLineMissingLabel.svg | 12 +- .../acmeCropIncomeIntervalConnector.svg | 18 +- .../alphabetIntervalFunnelConnectorLabel.svg | 16 +- .../static/alphabetIntervalLabelRotate.svg | 10 +- .../static/alphabetIntervalTransposed.svg | 52 +- .../static/blockChainLineAnnotation.svg | 4 +- .../snapshots/static/carsPointScatterPlot.svg | 64 +- .../snapshots/static/energySankeyCustom.svg | 96 +- .../snapshots/static/energySankeyDefaults.svg | 96 +- .../snapshots/static/flareTreeCustom.svg | 504 ++-- .../snapshots/static/flareTreemapCustom.svg | 440 +-- .../static/flareTreemapDrillDown.svg | 6 +- .../static/githubStarIntervalRadialLabel.svg | 6 +- .../snapshots/static/incomeLinkAnnotation.svg | 10 +- .../incomeStatementByRegionIntervalCustom.svg | 18 +- .../snapshots/static/intervalPointBullet.svg | 2 +- .../static/intervalPointBulletDatas.svg | 4 +- .../snapshots/static/intervalPointBullets.svg | 8 +- .../static/marketIntervalMarimekko.svg | 32 +- ...marketIntervalMarimekkoAutoPaddingFlex.svg | 32 +- .../snapshots/static/mockPieSpider.svg | 1495 +++++++++++ .../snapshots/static/mockPieSpiderHide.svg | 2390 +++++++++++++++++ .../snapshots/static/mockPieSpiderMany.svg | 2066 ++++++++++++++ .../snapshots/static/mockPieSpiderRight.svg | 1495 +++++++++++ .../snapshots/static/paragraphTextVis.svg | 6 +- .../static/population2015IntervalDonut.svg | 30 +- .../population2015IntervalDonutLabel.svg | 36 +- .../static/population2015IntervalPie.svg | 30 +- .../static/population2015IntervalPieLabel.svg | 36 +- .../population2015IntervalSpiderLabel.svg | 60 +- .../static/populationFlowChordDefault.svg | 16 +- .../static/populationIntervalRoseLabel.svg | 30 +- .../populationIntervalRoseSurroundLabel.svg | 30 +- .../snapshots/static/receiptsLineSlope.svg | 60 +- .../static/vaccinesCellScaleRelation.svg | 4 +- .../worldHistoryIntervalMultiTickCount.svg | 102 +- __tests__/plots/static/index.ts | 4 + .../plots/static/mock-pie-spider-hide.ts | 66 + .../plots/static/mock-pie-spider-many.ts | 58 + .../plots/static/mock-pie-spider-right.ts | 32 + __tests__/plots/static/mock-pie-spider.ts | 31 + site/examples/general/pie/demo/meta.json | 8 + .../general/pie/demo/spider-label-overlap.ts | 37 + src/shape/label/label.ts | 7 +- src/shape/label/position/spider.ts | 39 +- src/shape/label/position/utils.ts | 77 + src/shape/text/advance.ts | 114 +- 54 files changed, 8814 insertions(+), 997 deletions(-) create mode 100644 __tests__/integration/snapshots/static/mockPieSpider.svg create mode 100644 __tests__/integration/snapshots/static/mockPieSpiderHide.svg create mode 100644 __tests__/integration/snapshots/static/mockPieSpiderMany.svg create mode 100644 __tests__/integration/snapshots/static/mockPieSpiderRight.svg create mode 100644 __tests__/plots/static/mock-pie-spider-hide.ts create mode 100644 __tests__/plots/static/mock-pie-spider-many.ts create mode 100644 __tests__/plots/static/mock-pie-spider-right.ts create mode 100644 __tests__/plots/static/mock-pie-spider.ts create mode 100644 site/examples/general/pie/demo/spider-label-overlap.ts create mode 100644 src/shape/label/position/utils.ts diff --git a/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-0.svg b/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-0.svg index defd8580aa..f6ffc4b63c 100644 --- a/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-0.svg +++ b/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-0.svg @@ -8158,7 +8158,7 @@ - + diff --git a/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-1.svg b/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-1.svg index 260a8ca433..40b921bc7d 100644 --- a/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-1.svg +++ b/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-1.svg @@ -7650,7 +7650,7 @@ - + diff --git a/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-2.svg b/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-2.svg index 089a4b6fab..839a6f7d61 100644 --- a/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-2.svg +++ b/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-2.svg @@ -7140,7 +7140,7 @@ - + diff --git a/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-3.svg b/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-3.svg index 4c64b55fbe..bdc144c3b2 100644 --- a/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-3.svg +++ b/__tests__/integration/snapshots/animation/polio-point-stack-enter/interval0-3.svg @@ -7132,7 +7132,7 @@ - + diff --git a/__tests__/integration/snapshots/interaction/countries-annotation-slider-filter/step0.svg b/__tests__/integration/snapshots/interaction/countries-annotation-slider-filter/step0.svg index d4f61e1d42..1f4a4c2242 100644 --- a/__tests__/integration/snapshots/interaction/countries-annotation-slider-filter/step0.svg +++ b/__tests__/integration/snapshots/interaction/countries-annotation-slider-filter/step0.svg @@ -2053,7 +2053,7 @@ - + diff --git a/__tests__/integration/snapshots/interaction/countries-annotation-slider-filter/step1.svg b/__tests__/integration/snapshots/interaction/countries-annotation-slider-filter/step1.svg index 0428b7c664..952ae5a97f 100644 --- a/__tests__/integration/snapshots/interaction/countries-annotation-slider-filter/step1.svg +++ b/__tests__/integration/snapshots/interaction/countries-annotation-slider-filter/step1.svg @@ -2070,7 +2070,7 @@ - + diff --git a/__tests__/integration/snapshots/interaction/indices-line-chart-index-series/step0.svg b/__tests__/integration/snapshots/interaction/indices-line-chart-index-series/step0.svg index 4a57afc561..edf8c483be 100644 --- a/__tests__/integration/snapshots/interaction/indices-line-chart-index-series/step0.svg +++ b/__tests__/integration/snapshots/interaction/indices-line-chart-index-series/step0.svg @@ -159,7 +159,7 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/aaplLineMissingLabel.svg b/__tests__/integration/snapshots/static/aaplLineMissingLabel.svg index b498ea2440..c44b738dd3 100644 --- a/__tests__/integration/snapshots/static/aaplLineMissingLabel.svg +++ b/__tests__/integration/snapshots/static/aaplLineMissingLabel.svg @@ -890,7 +890,7 @@ - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/alphabetIntervalFunnelConnectorLabel.svg b/__tests__/integration/snapshots/static/alphabetIntervalFunnelConnectorLabel.svg index de2e21b70b..8dfe818638 100644 --- a/__tests__/integration/snapshots/static/alphabetIntervalFunnelConnectorLabel.svg +++ b/__tests__/integration/snapshots/static/alphabetIntervalFunnelConnectorLabel.svg @@ -483,7 +483,7 @@ - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/alphabetIntervalLabelRotate.svg b/__tests__/integration/snapshots/static/alphabetIntervalLabelRotate.svg index 3babdacf35..bd86dfb043 100644 --- a/__tests__/integration/snapshots/static/alphabetIntervalLabelRotate.svg +++ b/__tests__/integration/snapshots/static/alphabetIntervalLabelRotate.svg @@ -974,7 +974,7 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/alphabetIntervalTransposed.svg b/__tests__/integration/snapshots/static/alphabetIntervalTransposed.svg index 5f92bbc411..695d2eff37 100644 --- a/__tests__/integration/snapshots/static/alphabetIntervalTransposed.svg +++ b/__tests__/integration/snapshots/static/alphabetIntervalTransposed.svg @@ -2320,7 +2320,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/blockChainLineAnnotation.svg b/__tests__/integration/snapshots/static/blockChainLineAnnotation.svg index 3415a4a882..e81dac7026 100644 --- a/__tests__/integration/snapshots/static/blockChainLineAnnotation.svg +++ b/__tests__/integration/snapshots/static/blockChainLineAnnotation.svg @@ -957,7 +957,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/energySankeyCustom.svg b/__tests__/integration/snapshots/static/energySankeyCustom.svg index bb822e28a1..e440c57425 100644 --- a/__tests__/integration/snapshots/static/energySankeyCustom.svg +++ b/__tests__/integration/snapshots/static/energySankeyCustom.svg @@ -1424,7 +1424,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/energySankeyDefaults.svg b/__tests__/integration/snapshots/static/energySankeyDefaults.svg index bb83580c8f..b537cb963a 100644 --- a/__tests__/integration/snapshots/static/energySankeyDefaults.svg +++ b/__tests__/integration/snapshots/static/energySankeyDefaults.svg @@ -1335,7 +1335,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/flareTreeCustom.svg b/__tests__/integration/snapshots/static/flareTreeCustom.svg index d0a242a0df..15424cbcf8 100644 --- a/__tests__/integration/snapshots/static/flareTreeCustom.svg +++ b/__tests__/integration/snapshots/static/flareTreeCustom.svg @@ -5391,7 +5391,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/flareTreemapCustom.svg b/__tests__/integration/snapshots/static/flareTreemapCustom.svg index 1cba88495c..ea35a1288c 100644 --- a/__tests__/integration/snapshots/static/flareTreemapCustom.svg +++ b/__tests__/integration/snapshots/static/flareTreemapCustom.svg @@ -4264,7 +4264,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -11737,7 +11737,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/flareTreemapDrillDown.svg b/__tests__/integration/snapshots/static/flareTreemapDrillDown.svg index d89d82ad19..d22e2d5e71 100644 --- a/__tests__/integration/snapshots/static/flareTreemapDrillDown.svg +++ b/__tests__/integration/snapshots/static/flareTreemapDrillDown.svg @@ -458,7 +458,7 @@ - + - + - + diff --git a/__tests__/integration/snapshots/static/githubStarIntervalRadialLabel.svg b/__tests__/integration/snapshots/static/githubStarIntervalRadialLabel.svg index 7495a3f792..9d836c81bb 100644 --- a/__tests__/integration/snapshots/static/githubStarIntervalRadialLabel.svg +++ b/__tests__/integration/snapshots/static/githubStarIntervalRadialLabel.svg @@ -696,7 +696,7 @@ - + - + - + diff --git a/__tests__/integration/snapshots/static/incomeLinkAnnotation.svg b/__tests__/integration/snapshots/static/incomeLinkAnnotation.svg index 0ad155ed7b..e63d8b71a5 100644 --- a/__tests__/integration/snapshots/static/incomeLinkAnnotation.svg +++ b/__tests__/integration/snapshots/static/incomeLinkAnnotation.svg @@ -1407,7 +1407,7 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/incomeStatementByRegionIntervalCustom.svg b/__tests__/integration/snapshots/static/incomeStatementByRegionIntervalCustom.svg index fd3f4d48fd..66c61fb211 100644 --- a/__tests__/integration/snapshots/static/incomeStatementByRegionIntervalCustom.svg +++ b/__tests__/integration/snapshots/static/incomeStatementByRegionIntervalCustom.svg @@ -1701,7 +1701,7 @@ - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/intervalPointBullet.svg b/__tests__/integration/snapshots/static/intervalPointBullet.svg index 131b5c1401..a89cd78b4e 100644 --- a/__tests__/integration/snapshots/static/intervalPointBullet.svg +++ b/__tests__/integration/snapshots/static/intervalPointBullet.svg @@ -662,7 +662,7 @@ - + diff --git a/__tests__/integration/snapshots/static/intervalPointBulletDatas.svg b/__tests__/integration/snapshots/static/intervalPointBulletDatas.svg index ecced33ba1..8e87528c12 100644 --- a/__tests__/integration/snapshots/static/intervalPointBulletDatas.svg +++ b/__tests__/integration/snapshots/static/intervalPointBulletDatas.svg @@ -707,7 +707,7 @@ - + - + diff --git a/__tests__/integration/snapshots/static/intervalPointBullets.svg b/__tests__/integration/snapshots/static/intervalPointBullets.svg index 4675f1beb3..7ad261d436 100644 --- a/__tests__/integration/snapshots/static/intervalPointBullets.svg +++ b/__tests__/integration/snapshots/static/intervalPointBullets.svg @@ -936,7 +936,7 @@ - + - + - + - + diff --git a/__tests__/integration/snapshots/static/marketIntervalMarimekko.svg b/__tests__/integration/snapshots/static/marketIntervalMarimekko.svg index 09bf0a3eeb..86b2a304aa 100644 --- a/__tests__/integration/snapshots/static/marketIntervalMarimekko.svg +++ b/__tests__/integration/snapshots/static/marketIntervalMarimekko.svg @@ -1616,7 +1616,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/marketIntervalMarimekkoAutoPaddingFlex.svg b/__tests__/integration/snapshots/static/marketIntervalMarimekkoAutoPaddingFlex.svg index 2a341b27bf..9f078d4650 100644 --- a/__tests__/integration/snapshots/static/marketIntervalMarimekkoAutoPaddingFlex.svg +++ b/__tests__/integration/snapshots/static/marketIntervalMarimekkoAutoPaddingFlex.svg @@ -1616,7 +1616,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/mockPieSpider.svg b/__tests__/integration/snapshots/static/mockPieSpider.svg new file mode 100644 index 0000000000..abdb8d8f9f --- /dev/null +++ b/__tests__/integration/snapshots/static/mockPieSpider.svg @@ -0,0 +1,1495 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 微博 + + + + + + + + + + + + + + + + + + + + 其他 + + + + + + + + + + + + + + + + + + + + 论坛 + + + + + + + + + + + + + + + + + + + + 网站 + + + + + + + + + + + + + + + + + + + + 微信 + + + + + + + + + + + + + + + + + + + + 客户端 + + + + + + + + + + + + + + + + + + + + 新闻 + + + + + + + + + + + + + + + + + + + + 视频 + + + + + + + + + + + + + + + + + + + + 博客 + + + + + + + + + + + + + + + + + + + + 报刊 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 微博 (93.33) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 博客 (0.37) + + + + + + + + + + + + + 报刊 (0.17) + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/mockPieSpiderHide.svg b/__tests__/integration/snapshots/static/mockPieSpiderHide.svg new file mode 100644 index 0000000000..cefd47fb95 --- /dev/null +++ b/__tests__/integration/snapshots/static/mockPieSpiderHide.svg @@ -0,0 +1,2390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 微博 (93.33) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 博客 (0.37) + + + + + + + + + + + + + 博客 (0.37) + + + + + + + + + + + + + 博客 (0.37) + + + + + + + + + + + + + 报刊 (0.17) + + + + + + + + + + + + + 报刊 (0.17) + + + + + + + + + + + + + 报刊 (0.17) + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/mockPieSpiderMany.svg b/__tests__/integration/snapshots/static/mockPieSpiderMany.svg new file mode 100644 index 0000000000..8f645a425f --- /dev/null +++ b/__tests__/integration/snapshots/static/mockPieSpiderMany.svg @@ -0,0 +1,2066 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 微博 (93.33) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 博客 (0.37) + + + + + + + + + + + + + 报刊 (0.17) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 博客 (0.37) + + + + + + + + + + + + + 报刊 (0.17) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 博客 (0.37) + + + + + + + + + + + + + 报刊 (0.17) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/mockPieSpiderRight.svg b/__tests__/integration/snapshots/static/mockPieSpiderRight.svg new file mode 100644 index 0000000000..2ac7d2e30f --- /dev/null +++ b/__tests__/integration/snapshots/static/mockPieSpiderRight.svg @@ -0,0 +1,1495 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 报刊 + + + + + + + + + + + + + + + + + + + + 博客 + + + + + + + + + + + + + + + + + + + + 视频 + + + + + + + + + + + + + + + + + + + + 新闻 + + + + + + + + + + + + + + + + + + + + 客户端 + + + + + + + + + + + + + + + + + + + + 微信 + + + + + + + + + + + + + + + + + + + + 网站 + + + + + + + + + + + + + + + + + + + + 论坛 + + + + + + + + + + + + + + + + + + + + 其他 + + + + + + + + + + + + + + + + + + + + 微博 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 报刊 (0.17) + + + + + + + + + + + + + 博客 (0.37) + + + + + + + + + + + + + 视频 (0.39) + + + + + + + + + + + + + 新闻 (0.81) + + + + + + + + + + + + + 客户端 (1.05) + + + + + + + + + + + + + 微信 (1.12) + + + + + + + + + + + + + 网站 (1.44) + + + + + + + + + + + + + 论坛 (4.77) + + + + + + + + + + + + + 其他 (6.67) + + + + + + + + + + + + + 微博 (93.33) + + + + + + + + + + + + \ No newline at end of file diff --git a/__tests__/integration/snapshots/static/paragraphTextVis.svg b/__tests__/integration/snapshots/static/paragraphTextVis.svg index 12f3dcab93..3552d18c69 100644 --- a/__tests__/integration/snapshots/static/paragraphTextVis.svg +++ b/__tests__/integration/snapshots/static/paragraphTextVis.svg @@ -104,7 +104,7 @@ - + - + - + diff --git a/__tests__/integration/snapshots/static/population2015IntervalDonut.svg b/__tests__/integration/snapshots/static/population2015IntervalDonut.svg index 8d516f748b..91620939eb 100644 --- a/__tests__/integration/snapshots/static/population2015IntervalDonut.svg +++ b/__tests__/integration/snapshots/static/population2015IntervalDonut.svg @@ -987,7 +987,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -1038,7 +1038,7 @@ @@ -1084,7 +1084,7 @@ @@ -1130,7 +1130,7 @@ @@ -1176,7 +1176,7 @@ @@ -1222,7 +1222,7 @@ @@ -1268,7 +1268,7 @@ @@ -1314,7 +1314,7 @@ @@ -1360,7 +1360,7 @@ @@ -1406,7 +1406,7 @@ @@ -1452,7 +1452,7 @@ @@ -1498,7 +1498,7 @@ @@ -1544,7 +1544,7 @@ @@ -1590,7 +1590,7 @@ @@ -1636,7 +1636,7 @@ @@ -1682,7 +1682,7 @@ @@ -1728,7 +1728,7 @@ @@ -1774,7 +1774,7 @@ diff --git a/__tests__/integration/snapshots/static/population2015IntervalPie.svg b/__tests__/integration/snapshots/static/population2015IntervalPie.svg index 00cd160de7..b6ca704671 100644 --- a/__tests__/integration/snapshots/static/population2015IntervalPie.svg +++ b/__tests__/integration/snapshots/static/population2015IntervalPie.svg @@ -987,7 +987,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -890,7 +890,7 @@ @@ -935,7 +935,7 @@ @@ -980,7 +980,7 @@ @@ -1025,7 +1025,7 @@ @@ -1070,7 +1070,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/static/vaccinesCellScaleRelation.svg b/__tests__/integration/snapshots/static/vaccinesCellScaleRelation.svg index 3c4087d73a..fa268d3700 100644 --- a/__tests__/integration/snapshots/static/vaccinesCellScaleRelation.svg +++ b/__tests__/integration/snapshots/static/vaccinesCellScaleRelation.svg @@ -68972,7 +68972,7 @@ - + - + diff --git a/__tests__/integration/snapshots/static/worldHistoryIntervalMultiTickCount.svg b/__tests__/integration/snapshots/static/worldHistoryIntervalMultiTickCount.svg index 124c50a43e..ed9cb35f4d 100644 --- a/__tests__/integration/snapshots/static/worldHistoryIntervalMultiTickCount.svg +++ b/__tests__/integration/snapshots/static/worldHistoryIntervalMultiTickCount.svg @@ -2218,7 +2218,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/__tests__/plots/static/index.ts b/__tests__/plots/static/index.ts index 5951cf3e3e..8c83accb7f 100644 --- a/__tests__/plots/static/index.ts +++ b/__tests__/plots/static/index.ts @@ -337,3 +337,7 @@ export { stackBarAnnotationStyleText } from './stack-bar-annotation-style-text'; export { githubStarIntervalDarkThemeTransparent } from './github-star-interval-dark-theme-transparent'; export { diamondIntervalGroupX } from './diamond-interval-group-x'; export { mockIntervalSeriesOrder } from './mock-interval-series-order'; +export { mockPieSpider } from './mock-pie-spider'; +export { mockPieSpiderRight } from './mock-pie-spider-right'; +export { mockPieSpiderMany } from './mock-pie-spider-many'; +export { mockPieSpiderHide } from './mock-pie-spider-hide'; diff --git a/__tests__/plots/static/mock-pie-spider-hide.ts b/__tests__/plots/static/mock-pie-spider-hide.ts new file mode 100644 index 0000000000..4412873a49 --- /dev/null +++ b/__tests__/plots/static/mock-pie-spider-hide.ts @@ -0,0 +1,66 @@ +import { G2Spec } from '../../../src'; + +export function mockPieSpiderHide(): G2Spec { + return { + type: 'interval', + data: [ + { type: '微博', value: 93.33 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + ] + .sort((a, b) => a.value - b.value) + .reverse(), + encode: { + y: 'value', + color: 'type', + }, + labels: [ + { + position: 'spider', + text: (obj) => `${obj.type} (${obj.value})`, + connectorOpacity: 0.5, + }, + ], + transform: [{ type: 'stackY' }], + coordinate: { type: 'theta' }, + legend: false, + }; +} diff --git a/__tests__/plots/static/mock-pie-spider-many.ts b/__tests__/plots/static/mock-pie-spider-many.ts new file mode 100644 index 0000000000..c21362e77b --- /dev/null +++ b/__tests__/plots/static/mock-pie-spider-many.ts @@ -0,0 +1,58 @@ +import { G2Spec } from '../../../src'; + +export function mockPieSpiderMany(): G2Spec { + return { + type: 'interval', + data: [ + { type: '微博', value: 93.33 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + ], + encode: { + y: 'value', + color: 'type', + }, + labels: [ + { + position: 'spider', + text: (obj) => `${obj.type} (${obj.value})`, + connectorOpacity: 0.5, + }, + ], + transform: [{ type: 'stackY' }], + coordinate: { type: 'theta' }, + legend: false, + }; +} diff --git a/__tests__/plots/static/mock-pie-spider-right.ts b/__tests__/plots/static/mock-pie-spider-right.ts new file mode 100644 index 0000000000..e81ed47b61 --- /dev/null +++ b/__tests__/plots/static/mock-pie-spider-right.ts @@ -0,0 +1,32 @@ +import { G2Spec } from '../../../src'; + +export function mockPieSpiderRight(): G2Spec { + return { + type: 'interval', + data: [ + { type: '微博', value: 93.33 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + ].reverse(), + encode: { + y: 'value', + color: 'type', + }, + labels: [ + { + position: 'spider', + text: (obj) => `${obj.type} (${obj.value})`, + labelHeight: 30, + }, + ], + transform: [{ type: 'stackY' }], + coordinate: { type: 'theta' }, + }; +} diff --git a/__tests__/plots/static/mock-pie-spider.ts b/__tests__/plots/static/mock-pie-spider.ts new file mode 100644 index 0000000000..14e518d81e --- /dev/null +++ b/__tests__/plots/static/mock-pie-spider.ts @@ -0,0 +1,31 @@ +import { G2Spec } from '../../../src'; + +export function mockPieSpider(): G2Spec { + return { + type: 'interval', + data: [ + { type: '微博', value: 93.33 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + ], + encode: { + y: 'value', + color: 'type', + }, + labels: [ + { + position: 'spider', + text: (obj) => `${obj.type} (${obj.value})`, + }, + ], + transform: [{ type: 'stackY' }], + coordinate: { type: 'theta' }, + }; +} diff --git a/site/examples/general/pie/demo/meta.json b/site/examples/general/pie/demo/meta.json index 422e7b5e50..eb03d66ec8 100644 --- a/site/examples/general/pie/demo/meta.json +++ b/site/examples/general/pie/demo/meta.json @@ -60,6 +60,14 @@ }, "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*gC20SLxWVicAAAAAAAAAAAAADmJ7AQ/original" }, + { + "filename": "spider-label-overlap.ts", + "title": { + "zh": "调整蜘蛛布局", + "en": "Adjust Spider Layout" + }, + "screenshot": "https://mdn.alipayobjects.com/huamei_qa8qxu/afts/img/A*JuJwSJRS4yQAAAAAAAAAAAAADmJ7AQ/original" + }, { "filename": "point-jitter-radial.ts", "title": { diff --git a/site/examples/general/pie/demo/spider-label-overlap.ts b/site/examples/general/pie/demo/spider-label-overlap.ts new file mode 100644 index 0000000000..54337a7ad1 --- /dev/null +++ b/site/examples/general/pie/demo/spider-label-overlap.ts @@ -0,0 +1,37 @@ +/** + * A recreation of this demo: https://nivo.rocks/pie/ + */ +import { Chart } from '@antv/g2'; + +const chart = new Chart({ + container: 'container', +}); + +chart; + +chart + .interval() + .data([ + { type: '微博', value: 93.33 }, + { type: '其他', value: 6.67 }, + { type: '论坛', value: 4.77 }, + { type: '网站', value: 1.44 }, + { type: '微信', value: 1.12 }, + { type: '客户端', value: 1.05 }, + { type: '新闻', value: 0.81 }, + { type: '视频', value: 0.39 }, + { type: '博客', value: 0.37 }, + { type: '报刊', value: 0.17 }, + ]) + .encode('y', 'value') + .encode('color', 'type') + .transform({ type: 'stackY' }) + .coordinate({ type: 'theta' }) + .animate('enter', { type: 'waveIn' }) + .label({ + position: 'spider', + text: (d) => `${d.type} (${d.value})`, + }) + .legend(false); + +chart.render(); diff --git a/src/shape/label/label.ts b/src/shape/label/label.ts index 861105d5aa..c5d4b38ac2 100644 --- a/src/shape/label/label.ts +++ b/src/shape/label/label.ts @@ -29,6 +29,7 @@ function getDefaultStyle( coordinate: Coordinate, theme: G2Theme, options: LabelOptions, + labels: Vector2[][], ): Record { // For non-series mark, calc position for label based on // position and the bounds of shape. @@ -48,7 +49,7 @@ function getDefaultStyle( } return { ...t, - ...processor(p, points, v, coordinate, options), + ...processor(p, points, v, coordinate, options, labels), }; } @@ -59,7 +60,7 @@ function getDefaultStyle( export const Label: SC = (options, context) => { const { coordinate, theme } = context; const { render } = options; - return (points, value) => { + return (points, value, style, labels) => { const { text, x, @@ -73,7 +74,7 @@ export const Label: SC = (options, context) => { rotate = 0, transform = '', ...defaultStyle - } = getDefaultStyle(points, value, coordinate, theme, options); + } = getDefaultStyle(points, value, coordinate, theme, options, labels); return select(new Advance()) .call(applyStyle, defaultStyle) diff --git a/src/shape/label/position/spider.ts b/src/shape/label/position/spider.ts index b864368e63..f993abbf32 100644 --- a/src/shape/label/position/spider.ts +++ b/src/shape/label/position/spider.ts @@ -3,19 +3,16 @@ import { Vector2 } from '../../../runtime'; import { isCircular } from '../../../utils/coordinate'; import { LabelPosition } from './default'; import { inferOutsideCircularStyle, radiusOf, angleOf } from './outside'; +import { hideAndDodgeY } from './utils'; -/** - * Spider label transform only suitable for the labels in polar coordinate, labels should distinguish coordinate type. - */ -export function spider( - position: LabelPosition, +const styleByPoints = new WeakMap(); + +function compute( points: Vector2[], value: Record, coordinate: Coordinate, ) { - if (!isCircular(coordinate)) return {}; const { connectorLength, connectorLength2, connectorDistance } = value; - const { ...style }: any = inferOutsideCircularStyle( 'outside', points, @@ -27,13 +24,35 @@ export function spider( const angle = angleOf(points, value, coordinate); const radius1 = radius + connectorLength + connectorLength2; const sign = Math.sin(angle) > 0 ? 1 : -1; - const newX = center[0] + (radius1 + +connectorDistance) * sign; - const { x: originX } = style; const dx = newX - originX; style.x += dx; style.connectorPoints[0][0] -= dx; - return style; } + +/** + * Spider label transform only suitable for the labels in polar coordinate, + * labels should distinguish coordinate type. + */ +export function spider( + position: LabelPosition, + points: Vector2[], + value: Record, + coordinate: Coordinate, + options: Record, + labels: Vector2[][], +) { + if (!isCircular(coordinate)) return {}; + if (styleByPoints.has(points)) return styleByPoints.get(points); + const computed = labels.map((points) => compute(points, value, coordinate)); + const { width, height } = coordinate.getOptions(); + const left = computed.filter((d) => d.x < width / 2); + const right = computed.filter((d) => d.x >= width / 2); + const extendedOptions = { ...options, height }; + hideAndDodgeY(left, extendedOptions); + hideAndDodgeY(right, extendedOptions); + computed.forEach((style, i) => styleByPoints.set(labels[i], style)); + return styleByPoints.get(points); +} diff --git a/src/shape/label/position/utils.ts b/src/shape/label/position/utils.ts new file mode 100644 index 0000000000..e19ca5a13d --- /dev/null +++ b/src/shape/label/position/utils.ts @@ -0,0 +1,77 @@ +import { sort } from 'd3-array'; + +// Optimize antiCollision from: https://github.com/antvis/G2/blob/master/src/geometry/label/layout/pie/util.ts +export function dodgeY( + labels: Record[], + options: Record = {}, +) { + const { labelHeight = 14, height } = options; + + // Sort labels by y and init boxes (one box for each label) + const sortedLabels = sort(labels, (d) => d.y); + const n = sortedLabels.length; + const boxes = new Array(n); + for (let i = 0; i < n; i++) { + const label = sortedLabels[i]; + const { y } = label; + boxes[i] = { y, y1: y + labelHeight, labels: [y] }; + } + + // Merge boxes until no overlapping boxes or only one box left. + // All the boxes should start higher than 0, but maybe higher than height. + let overlap = true; + while (overlap) { + overlap = false; + // Scan backward because boxes maybe deleted. + for (let i = boxes.length - 1; i > 0; i--) { + const box = boxes[i]; + const preBox = boxes[i - 1]; + if (preBox.y1 > box.y) { + overlap = true; + preBox.labels.push(...box.labels); + boxes.splice(i, 1); + + // Compute new y1 to contain the current box. + preBox.y1 += box.y1 - box.y; + + // Make sure the new box is in the range of [0, height]. + const newHeight = preBox.y1 - preBox.y; + preBox.y1 = Math.max(Math.min(preBox.y1, height), newHeight); + preBox.y = preBox.y1 - newHeight; + } + } + } + + let i = 0; + for (const box of boxes) { + const { y, labels } = box; + let prevY = y - labelHeight; + for (const curY of labels) { + const label = sortedLabels[i++]; + const expectedY = prevY + labelHeight; + const dy = expectedY - curY; + label.connectorPoints[0][1] -= dy; + label.y = prevY + labelHeight; + prevY += labelHeight; + } + } +} + +export function hideAndDodgeY( + unsorted: Record[], + options: Record, +) { + const labels = sort(unsorted, (d) => d.y); + const { height, labelHeight = 14 } = options; + const maxCount = Math.ceil(height / labelHeight); + if (labels.length <= maxCount) return dodgeY(labels, options); + const filtered = []; + for (let i = 0; i < labels.length; i++) { + // Hide labels out of range. + if (i < labels.length - maxCount) { + labels[i].opacity = 0; + labels[i].connector = false; + } else filtered.push(labels[i]); + } + dodgeY(filtered, options); +} diff --git a/src/shape/text/advance.ts b/src/shape/text/advance.ts index 90f7477210..ea0e1e72ad 100644 --- a/src/shape/text/advance.ts +++ b/src/shape/text/advance.ts @@ -50,7 +50,7 @@ type TextShapeStyleProps = Omit & text?: string; }; -function getConnectorPoint(shape: GText | Rect) { +function getConnectorPoint(shape: GText | Rect): [number, number] { const { min: [x0, y0], max: [x1, y1], @@ -61,7 +61,6 @@ function getConnectorPoint(shape: GText | Rect) { if (x1 < 0) x = x1; if (y0 > 0) y = y0; if (y1 < 0) y = y1; - return [x, y]; } @@ -91,41 +90,76 @@ const cos = (p0: Vector2, p1: Vector2, p2: Vector2) => { return (a ** 2 + b ** 2 - c ** 2) / (2 * a * b); }; +// A path from element to label. +// Adapted drawLabelLine from https://github.com/antvis/G2/blob/master/src/geometry/label/layout/pie/spider.ts function inferConnectorPath( shape: DisplayObject, - points: Vector2[], - controlPoints: Vector2[], - coordCenter, + end: Vector2, + control: Vector2[], + coordCenter: Vector2, + left = true, + top = true, ) { - const [[x0, y0], [x1, y1]] = points; - const [x, y] = getConnectorPoint(shape); - // Straight connector line. - if (x0 === x1 && y0 === y1) { - return line()([ - [0, 0], - [x, y], - ]); - } + const path = (points) => line()(points); - const P: any = [[x0 - x1, y0 - y1]].concat( - controlPoints.length ? controlPoints : [[0, 0]], - ); + if (!end[0] && !end[1]) return path([getConnectorPoint(shape), end]); + if (!control.length) return path([[0, 0], end]); + + const [inflection, start] = control; + const p1 = [...start]; + const p2 = [...inflection]; + + // Label has been adjusted, so add offset to the label. + if (start[0] !== inflection[0]) { + const offset = left ? -4 : 4; + p1[1] = start[1]; - const p0 = [coordCenter[0] - x1, coordCenter[1] - y1] as Vector2; - const [p1, p2] = P; - // If angle is smaller than 90, which will cause connector overlap with element. - if (cos(p0, p1, p2) > 0) { - const x2 = (() => { - const { min, max } = shape.getLocalBounds(); - // A(x1,y2) perpendicular to B(x2,y2) => x1*x2 + y1*y2 = 0 - const vx = p1[0] + ((p1[1] - p0[1]) * (p1[1] - 0)) / (p1[0] - p0[0]); - if (max[0] < p0[0]) return Math.min(max[0], vx); - return Math.max(min[0], vx); - })(); - P.splice(1, 1, [x2, 0]); + // For the label in the first quadrant. + if (top && !left) { + p1[0] = Math.max(inflection[0], start[0] - offset); + if (start[1] < inflection[1]) { + p2[1] = p1[1]; + } else { + p2[1] = inflection[1]; + p2[0] = Math.max(p2[0], p1[0] - offset); + } + } + + // For the label in the second quadrant. + if (!top && !left) { + p1[0] = Math.max(inflection[0], start[0] - offset); + if (start[1] > inflection[1]) { + p2[1] = p1[1]; + } else { + p2[1] = inflection[1]; + p2[0] = Math.max(p2[0], p1[0] - offset); + } + } + + // For the label in the third quadrant. + if (!top && left) { + p1[0] = Math.min(inflection[0], start[0] - offset); + if (start[1] > inflection[1]) { + p2[1] = p1[1]; + } else { + p2[1] = inflection[1]; + p2[0] = Math.min(p2[0], p1[0] - offset); + } + } + + // For the label in the fourth quadrant. + if (top && left) { + p1[0] = Math.min(inflection[0], start[0] - offset); + if (start[1] < inflection[1]) { + p2[1] = p1[1]; + } else { + p2[1] = inflection[1]; + p2[0] = Math.min(p2[0], p1[0] - offset); + } + } } - return line()(P); + return path([start, p1, p2, inflection, end]); } export const Advance = createElement((g) => { @@ -161,14 +195,12 @@ export const Advance = createElement((g) => { } const { padding, ...backgroundStyle } = subObject(rest, 'background'); - const { points = [], ...connectorStyle } = subObject(rest, 'connector'); - const endPoints: Vector2[] = [ - [+x0, +y0], - [+x, +y], - ]; + const { points: controlPoints = [], ...connectorStyle } = subObject( + rest, + 'connector', + ); let textShape; - // Use html to customize advance text. if (innerHTML) { textShape = select(g) .maybeAppend('html', 'html', className) @@ -201,12 +233,18 @@ export const Advance = createElement((g) => { .call(applyStyle, background ? backgroundStyle : {}) .node(); + const left = +x0 < coordCenter[0]; + const top = +y0 < coordCenter[1]; + const end: [number, number] = [+x0 - +x, +y0 - +y]; const connectorPath = inferConnectorPath( rect, - endPoints, - points, + end, + controlPoints, coordCenter, + left, + top, ); + const markerStart = startMarker && new Marker({