You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2025/07/15 09:12:25 UTC

(echarts) 01/07: fix(labelLayout): clarify label layout modification logic.

This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch fix/label-layout-margin
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 9b8e3e3003b3c7a899f610ebcb6282e206a6b51c
Author: 100pah <su...@gmail.com>
AuthorDate: Sun Jul 6 18:06:47 2025 +0800

    fix(labelLayout): clarify label layout modification logic.
---
 src/chart/pie/labelLayout.ts      |   4 +-
 src/component/axis/AxisBuilder.ts |   4 +-
 src/label/LabelManager.ts         |   6 +--
 src/label/labelLayoutHelper.ts    | 106 +++++++++++++++++++++++---------------
 src/util/graphic.ts               |   4 ++
 5 files changed, 75 insertions(+), 49 deletions(-)

diff --git a/src/chart/pie/labelLayout.ts b/src/chart/pie/labelLayout.ts
index 35edb358c..0c9507926 100644
--- a/src/chart/pie/labelLayout.ts
+++ b/src/chart/pie/labelLayout.ts
@@ -27,7 +27,7 @@ import ZRText from 'zrender/src/graphic/Text';
 import BoundingRect, {RectLike} from 'zrender/src/core/BoundingRect';
 import { each, isNumber } from 'zrender/src/core/util';
 import { limitTurnAngle, limitSurfaceAngle } from '../../label/labelGuideHelper';
-import { shiftLayoutOnY } from '../../label/labelLayoutHelper';
+import { shiftLayoutOnXY } from '../../label/labelLayoutHelper';
 
 const RADIAN = Math.PI / 180;
 
@@ -139,7 +139,7 @@ function adjustSingleSide(
         }
     }
 
-    if (shiftLayoutOnY(list, viewTop, viewTop + viewHeight)) {
+    if (shiftLayoutOnXY(list, 1, viewTop, viewTop + viewHeight)) {
         recalculateX(list);
     }
 }
diff --git a/src/component/axis/AxisBuilder.ts b/src/component/axis/AxisBuilder.ts
index f5fa72125..7e60b89a6 100644
--- a/src/component/axis/AxisBuilder.ts
+++ b/src/component/axis/AxisBuilder.ts
@@ -62,6 +62,7 @@ import {
     rollbackToLabelLayoutInfoRaw,
     LABEL_LAYOUT_INFO_KIND_COMPUTED,
     createSingleLayoutInfoComputed,
+    applyTranslationOnLabelLayoutInfo,
 } from '../../label/labelLayoutHelper';
 import ExtensionAPI from '../../core/ExtensionAPI';
 import { makeInner } from '../../util/model';
@@ -406,8 +407,7 @@ function moveIfOverlap(
         bidirectional: false,
         touchThreshold: 0.05,
     })) {
-        Point.add(movableLayoutInfo.label, movableLayoutInfo.label, mtv);
-        ensureLabelLayoutInfoComputed(rollbackToLabelLayoutInfoRaw(movableLayoutInfo));
+        applyTranslationOnLabelLayoutInfo(movableLayoutInfo, mtv);
     }
 }
 
diff --git a/src/label/LabelManager.ts b/src/label/LabelManager.ts
index 2f262c4eb..b69d77e16 100644
--- a/src/label/LabelManager.ts
+++ b/src/label/LabelManager.ts
@@ -52,7 +52,7 @@ import { retrieve2, each, keys, isFunction, filter, indexOf } from 'zrender/src/
 import { PathStyleProps } from 'zrender/src/graphic/Path';
 import Model from '../model/Model';
 import {
-    hideOverlap, shiftLayoutOnX, shiftLayoutOnY,
+    hideOverlap, shiftLayoutOnXY,
     createLabelLayoutList,
     LabelLayoutInfoRaw,
     LABEL_LAYOUT_INFO_KIND_RAW,
@@ -443,8 +443,8 @@ class LabelManager {
             return item.layoutOption.moveOverlap === 'shiftY';
         });
 
-        shiftLayoutOnX(labelsNeedsAdjustOnX, 0, width);
-        shiftLayoutOnY(labelsNeedsAdjustOnY, 0, height);
+        shiftLayoutOnXY(labelsNeedsAdjustOnX, 0, 0, width);
+        shiftLayoutOnXY(labelsNeedsAdjustOnY, 1, 0, height);
 
         const labelsNeedsHideOverlap = filter(labelList, function (item) {
             return item.layoutOption.hideOverlap;
diff --git a/src/label/labelLayoutHelper.ts b/src/label/labelLayoutHelper.ts
index 66d07d20d..b665a4815 100644
--- a/src/label/labelLayoutHelper.ts
+++ b/src/label/labelLayoutHelper.ts
@@ -20,7 +20,7 @@
 import ZRText from 'zrender/src/graphic/Text';
 import { LabelExtendedTextStyle, LabelLayoutOption, LabelMarginType, NullUndefined } from '../util/types';
 import {
-    BoundingRect, OrientedBoundingRect, Polyline, expandOrShrinkRect,
+    BoundingRect, OrientedBoundingRect, Polyline, WH, XY, expandOrShrinkRect,
     isBoundingRectAxisAligned
 } from '../util/graphic';
 import type Element from 'zrender/src/Element';
@@ -157,7 +157,38 @@ function prepareLabelLayoutInfo(
 }
 
 /**
- * The reverse operation of `ensureLabelLayoutInfoComputedv`.
+ * This is a shortcut of
+ *   ```js
+ *   layoutInfo.label.x = newX;
+ *   layoutInfo.label.y = newY;
+ *   ensureLabelLayoutInfoComputed(rollbackToLabelLayoutInfoRaw(layoutInfo));
+ *   ```
+ * and provide better performance in this common case.
+ */
+export function applyTranslationOnLabelLayoutInfo(
+    layoutInfo: LabelLayoutInfoAll,
+    offset: PointLike, // [offsetX, offsetY]
+): void {
+    layoutInfo.label.x += offset.x;
+    layoutInfo.label.y += offset.y;
+    if (layoutInfo.kind === LABEL_LAYOUT_INFO_KIND_COMPUTED) {
+        const transform = layoutInfo.transform;
+        if (transform) {
+            transform[4] += offset.x;
+            transform[5] += offset.y;
+        }
+        const globalRect = layoutInfo.rect;
+        globalRect.x += offset.x;
+        globalRect.y += offset.y;
+        const obb = layoutInfo.obb;
+        if (obb) {
+            obb.fromBoundingRect(layoutInfo.localRect, transform);
+        }
+    }
+}
+
+/**
+ * The reverse operation of `ensureLabelLayoutInfoComputed`.
  */
 export function rollbackToLabelLayoutInfoRaw(
     labelLayoutInfo: LabelLayoutInfoAll
@@ -177,7 +208,8 @@ export function rollbackToLabelLayoutInfoRaw(
  * [CAUTION]
  *  - If the raw label is changed, must call
  *    `ensureLabelLayoutInfoComputed(rollbackToLabelLayoutInfoRaw(layoutInfo))`
- *    to recreate the layout info.
+ *    to recreate the layout info,
+ *    or call `applyTranslationOnLabelLayoutInfo(layoutInfo)` is only translation is performed.
  *  - Null checking is needed for the result. @see prepareLabelLayoutInfo
  *
  * Usage:
@@ -245,18 +277,37 @@ export function applyTextMarginToLocalRect(
     return localRect;
 }
 
-function shiftLayout(
+/**
+ * Adjust labels on x/y direction to avoid overlap.
+ *
+ * PENDING: the current implementation is based on the global bounding rect rather than the local rect,
+ *  which may be not preferable in some edge cases when the label has rotation, but works for most cases,
+ *  since rotation is unnecessary when there is sufficient space, while squeezing is applied regardless
+ *  of overlapping when there is no enough space.
+ *
+ * NOTICE:
+ *  The input `list` and its content will be modified (sort, label.x/y, rect).
+ *  The caller should sync the modifications to the other parts by `rollbackToLabelLayoutInfoRaw` if needed.
+ *
+ * @return adjusted
+ */
+export function shiftLayoutOnXY(
     list: Pick<LabelLayoutInfoComputed, 'rect' | 'label'>[],
-    xyDim: 'x' | 'y',
-    sizeDim: 'width' | 'height',
-    minBound: number,
-    maxBound: number,
-    balanceShift: boolean
-) {
+    xyDimIdx: 0 | 1, // 0 for x, 1 for y
+    minBound: number, // for x, leftBound; for y, topBound
+    maxBound: number, // for x, rightBound; for y, bottomBound
+    // If average the shifts on all labels and add them to 0
+    // TODO: Not sure if should enable it.
+    // Pros: The angle of lines will distribute more equally
+    // Cons: In some layout. It may not what user wanted. like in pie. the label of last sector is usually changed unexpectedly.
+    balanceShift?: boolean
+): boolean {
     const len = list.length;
+    const xyDim = XY[xyDimIdx];
+    const sizeDim = WH[xyDimIdx];
 
     if (len < 2) {
-        return;
+        return false;
     }
 
     list.sort(function (a, b) {
@@ -267,7 +318,7 @@ function shiftLayout(
     let delta;
     let adjusted = false;
 
-    const shifts = [];
+    // const shifts = [];
     let totalShifts = 0;
     for (let i = 0; i < len; i++) {
         const item = list[i];
@@ -280,7 +331,7 @@ function shiftLayout(
             adjusted = true;
         }
         const shift = Math.max(-delta, 0);
-        shifts.push(shift);
+        // shifts.push(shift);
         totalShifts += shift;
 
         lastPos = rect[xyDim] + rect[sizeDim];
@@ -412,35 +463,6 @@ function shiftLayout(
     return adjusted;
 }
 
-/**
- * Adjust labels on x direction to avoid overlap.
- */
-export function shiftLayoutOnX(
-    list: Pick<LabelLayoutInfoComputed, 'rect' | 'label'>[],
-    leftBound: number,
-    rightBound: number,
-    // If average the shifts on all labels and add them to 0
-    // TODO: Not sure if should enable it.
-    // Pros: The angle of lines will distribute more equally
-    // Cons: In some layout. It may not what user wanted. like in pie. the label of last sector is usually changed unexpectedly.
-    balanceShift?: boolean
-): boolean {
-    return shiftLayout(list, 'x', 'width', leftBound, rightBound, balanceShift);
-}
-
-/**
- * Adjust labels on y direction to avoid overlap.
- */
-export function shiftLayoutOnY(
-    list: Pick<LabelLayoutInfoComputed, 'rect' | 'label'>[],
-    topBound: number,
-    bottomBound: number,
-    // If average the shifts on all labels and add them to 0
-    balanceShift?: boolean
-): boolean {
-    return shiftLayout(list, 'y', 'height', topBound, bottomBound, balanceShift);
-}
-
 /**
  * [CAUTION]: the `label.ignore` in the input is not necessarily falsy.
  *  this method checks intersection regardless of current `ignore`,
diff --git a/src/util/graphic.ts b/src/util/graphic.ts
index 259d3a5d8..e863e713f 100644
--- a/src/util/graphic.ts
+++ b/src/util/graphic.ts
@@ -68,6 +68,7 @@ import {
     isArray,
     isNumber,
     clone,
+    assert,
 } from 'zrender/src/core/util';
 import { getECData } from './innerStore';
 import ComponentModel from '../model/Component';
@@ -607,6 +608,9 @@ export function expandOrShrinkRect<TRect extends RectLike>(
         _tmpExpandRectDelta[0] = _tmpExpandRectDelta[1] = _tmpExpandRectDelta[2] = _tmpExpandRectDelta[3] = delta;
     }
     else {
+        if (__DEV__) {
+            assert(delta.length === 4);
+        }
         _tmpExpandRectDelta[0] = delta[0];
         _tmpExpandRectDelta[1] = delta[1];
         _tmpExpandRectDelta[2] = delta[2];


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org