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

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

commit 983e4ccbc2f6d6aac1ae70ea175a1012bd313e73
Author: 100pah <[email protected]>
AuthorDate: Tue Jun 24 11:00:38 2025 +0800

    feat(grid layout): (1) introduce outerBoundsMode and make outerBounds pure 
object. (2) fix name overlap cache bug. (3) make outerBounds merge as box 
layout merge.
---
 src/component/axis/AxisBuilder.ts | 15 ++++++-------
 src/coord/cartesian/Grid.ts       | 42 +++++++++++++++++--------------------
 src/coord/cartesian/GridModel.ts  | 44 +++++++++++++++++++++++++++++++--------
 src/label/labelLayoutHelper.ts    | 30 +++++++++++++-------------
 src/util/layout.ts                | 10 +++------
 test/axis-layout-0.html           | 18 +++++++---------
 6 files changed, 87 insertions(+), 72 deletions(-)

diff --git a/src/component/axis/AxisBuilder.ts 
b/src/component/axis/AxisBuilder.ts
index 40e0e4da9..23fc61a90 100644
--- a/src/component/axis/AxisBuilder.ts
+++ b/src/component/axis/AxisBuilder.ts
@@ -376,7 +376,7 @@ export const resolveAxisNameOverlapDefault: 
AxisBuilderSharedContext['resolveAxi
 
 function moveIfOverlap(
     basedLayoutInfo: LabelIntersectionCheckInfo,
-    movableLayoutInfo: LabelIntersectionCheckInfo & 
Pick<LabelLayoutInfoComputed, 'label'>,
+    movableLayoutInfo: LabelLayoutInfoComputed,
     moveDirVec: Point
 ): void {
     const mtv = new Point();
@@ -386,13 +386,14 @@ function moveIfOverlap(
         touchThreshold: 0.05,
     })) {
         Point.add(movableLayoutInfo.label, movableLayoutInfo.label, mtv);
+        
ensureLabelLayoutInfoComputed(rollbackToLabelLayoutInfoRaw(movableLayoutInfo));
     }
 }
 
 export function moveIfOverlapByLinearLabels(
     baseLayoutInfoList: (LabelIntersectionCheckInfo)[],
     baseDirVec: Point,
-    movableLayoutInfo: (LabelIntersectionCheckInfo & 
Pick<LabelLayoutInfoComputed, 'label'>),
+    movableLayoutInfo: LabelLayoutInfoComputed,
     moveDirVec: Point,
 ): void {
     // Detect and move from far to close.
@@ -1040,11 +1041,11 @@ function fixMinMaxLabelShow(
         if (showMinMaxLabel === false) {
             ignoreEl(outmostLabelLayout.label);
         }
-        // If `optionHideOverlap === false`, do not hide anything.
-        // e.g., in category axis, hide some label is not reasonable.
-        // And currently the bounding rect of text might not accurate enough,
-        // might slightly bigger, which causes false positive.
-        else if (optionHideOverlap !== false) {
+        // PENDING: Originally we thougth `optionHideOverlap === false` means 
do not hide anything,
+        //  since currently the bounding rect of text might not accurate 
enough and might slightly bigger,
+        //  which causes false positive. But `optionHideOverlap: 
null/undfined` is falsy and likely
+        //  be treated as false.
+        else {
             // In most fonts the glyph does not reach the boundary of the 
bouding rect.
             // This is needed to avoid too aggressive to hide two elements 
that meet at the edge
             // due to compact layout by the same bounding rect or OBB.
diff --git a/src/coord/cartesian/Grid.ts b/src/coord/cartesian/Grid.ts
index f11e6bc8d..321b7a26e 100644
--- a/src/coord/cartesian/Grid.ts
+++ b/src/coord/cartesian/Grid.ts
@@ -38,7 +38,7 @@ import Axis2D from './Axis2D';
 import {ParsedModelFinder, ParsedModelFinderKnown, SINGLE_REFERRING} from 
'../../util/model';
 
 // Depends on GridModel, AxisModel, which performs preprocess.
-import GridModel from './GridModel';
+import GridModel, { OUTER_BOUNDS_DEFAULT } from './GridModel';
 import CartesianAxisModel from './AxisModel';
 import GlobalModel from '../../model/Global';
 import ExtensionAPI from '../../core/ExtensionAPI';
@@ -222,10 +222,10 @@ class Grid implements CoordinateSystemMaster {
             let noPxChange: boolean;
             if (optionContainLabel) {
                 if (legacyLayOutGridByContainLabel) {
-                    console.time('legacyLayOutGridByContainLabel');
+                    // console.time('legacyLayOutGridByContainLabel');
                     legacyLayOutGridByContainLabel(this._axesList, gridRect);
                     updateAllAxisExtentTransByGridRect(axesMap, gridRect);
-                    console.timeEnd('legacyLayOutGridByContainLabel');
+                    // console.timeEnd('legacyLayOutGridByContainLabel');
                 }
                 else {
                     if (__DEV__) {
@@ -240,13 +240,13 @@ class Grid implements CoordinateSystemMaster {
             else {
                 const {outerBoundsRect, parsedOuterBoundsContain} = 
prepareOuterBounds(gridModel, gridRect, api);
                 if (outerBoundsRect) {
-                    console.time('layOutGridByOuterBounds');
+                    // console.time('layOutGridByOuterBounds');
                     noPxChange = layOutGridByOuterBounds(outerBoundsRect, 
parsedOuterBoundsContain, gridRect, axesMap);
-                    console.timeEnd('layOutGridByOuterBounds');
+                    // console.timeEnd('layOutGridByOuterBounds');
                 }
             }
 
-            console.time('buildAxesView_determine');
+            // console.time('buildAxesView_determine');
             createOrUpdateAxesView(
                 gridRect,
                 axesMap,
@@ -254,7 +254,7 @@ class Grid implements CoordinateSystemMaster {
                 null,
                 noPxChange
             );
-            console.timeEnd('buildAxesView_determine');
+            // console.timeEnd('buildAxesView_determine');
         } // End of beforeDataProcessing
 
         each(this._coordsList, function (coord) {
@@ -848,25 +848,21 @@ function prepareOuterBounds(
     outerBoundsRect: BoundingRect | NullUndefined
     parsedOuterBoundsContain: ParsedOuterBoundsContain
 } {
-    let optionOuterBounds = gridModel.get('outerBounds', true);
     let outerBoundsRect: BoundingRect | NullUndefined;
-    if (optionOuterBounds !== false) {
-        if (optionOuterBounds == null || optionOuterBounds === true || 
optionOuterBounds === 'auto') {
-            optionOuterBounds = {left: 5, right: 5, top: 5, bottom: 5};
-        }
-        if (optionOuterBounds === 'same') {
-            outerBoundsRect = gridRect.clone();
-        }
-        else if (isObject(optionOuterBounds)) {
-            const refContainer = {width: api.getWidth(), height: 
api.getHeight()};
-            outerBoundsRect = getLayoutRect(optionOuterBounds, refContainer);
-        }
-        else {
-            if (__DEV__) {
-                error(`Invalid 
grid[${gridModel.componentIndex}].outerBounds.`);
-            }
+    const optionOuterBoundsMode = gridModel.get('outerBoundsMode', true);
+    if (optionOuterBoundsMode === 'same') {
+        outerBoundsRect = gridRect.clone();
+    }
+    else if (optionOuterBoundsMode == null || optionOuterBoundsMode === 
'auto') {
+        const refContainer = {width: api.getWidth(), height: api.getHeight()};
+        outerBoundsRect = getLayoutRect(gridModel.get('outerBounds', true) || 
OUTER_BOUNDS_DEFAULT, refContainer);
+    }
+    else if (optionOuterBoundsMode !== 'none') {
+        if (__DEV__) {
+            error(`Invalid 
grid[${gridModel.componentIndex}].outerBoundsMode.`);
         }
     }
+
     const optionOuterBoundsContain = gridModel.get('outerBoundsContain', true);
     let parsedOuterBoundsContain: ParsedOuterBoundsContain;
     if (optionOuterBoundsContain == null || optionOuterBoundsContain === 
'auto') {
diff --git a/src/coord/cartesian/GridModel.ts b/src/coord/cartesian/GridModel.ts
index f76274507..541ffe29d 100644
--- a/src/coord/cartesian/GridModel.ts
+++ b/src/coord/cartesian/GridModel.ts
@@ -22,6 +22,10 @@ import ComponentModel from '../../model/Component';
 import { ComponentOption, BoxLayoutOptionMixin, ZRColor, ShadowOptionMixin, 
NullUndefined } from '../../util/types';
 import Grid from './Grid';
 import { CoordinateSystemHostModel } from '../CoordinateSystem';
+import type GlobalModel from '../../model/Global';
+import { getLayoutParams, mergeLayoutParam } from '../../util/layout';
+
+export const OUTER_BOUNDS_DEFAULT = {left: 5, right: 5, top: 5, bottom: 5};
 
 export interface GridOption extends ComponentOption, BoxLayoutOptionMixin, 
ShadowOptionMixin {
     mainType?: 'grid';
@@ -41,18 +45,21 @@ export interface GridOption extends ComponentOption, 
BoxLayoutOptionMixin, Shado
      * But if axisLabel and/or axisName overflow the outerBounds, shrink the 
layout to avoid that overflow.
      *
      * Options:
-     *  - `false`: outerBounds is infinity.
+     *  - 'none': outerBounds is infinity.
      *  - 'same': outerBounds is the same as the layout rect defined by 
`grid.left/right/top/bottom/width/height`.
-     *  - {left, right, top, bottom, width, height}: Define a outerBounds 
rect, based on:
-     *      - the canvas by default.
-     *      - or the `dataToLayout` result if a `boxCoordinateSystem` is 
specified.
-     *  - 'auto'/null/undefined/true: Default. be 'same' if 
`containLabel:true`,
-     *      otherwises `{left: 5, right: 5, top: 5, bottom: 5}`.
+     *  - 'auto'/null/undefined: Default. Use `outerBounds`, or 'same' if 
`containLabel:true`.
      *
      * Note:
-     *  `grid.containLabel` is equivalent to `{outerBounds: 'same', 
outerBoundsContain: 'axisLabel'}`.
+     *  `grid.containLabel` is equivalent to `{outerBoundsMode: 'same', 
outerBoundsContain: 'axisLabel'}`.
+     */
+    outerBoundsMode?: 'auto' | NullUndefined | 'same' | 'none';
+    /**
+     * {left, right, top, bottom, width, height}: Define a outerBounds rect, 
based on:
+     *  - the canvas by default.
+     *  - or the `dataToLayout` result if a `boxCoordinateSystem` is specified.
+     * By default {left: 5, right: 5, top: 5, bottom: 5}.
      */
-    outerBounds?: boolean | 'same' | 'auto' | BoxLayoutOptionMixin | 
NullUndefined;
+    outerBounds?: BoxLayoutOptionMixin;
     /**
      * - 'all': Default. Contains the cartesian rect and axis labels and axis 
name.
      * - 'axisLabel': Contains the cartesian rect and axis labels. This effect 
differs slightly from the
@@ -78,6 +85,24 @@ class GridModel extends ComponentModel<GridOption> 
implements CoordinateSystemHo
 
     coordinateSystem: Grid;
 
+    mergeDefaultAndTheme(option: GridOption, ecModel: GlobalModel): void {
+        const outerBoundsCp = getLayoutParams(option.outerBounds);
+
+        super.mergeDefaultAndTheme.apply(this, arguments as any);
+
+        if (outerBoundsCp && option.outerBounds) {
+            mergeLayoutParam(option.outerBounds, outerBoundsCp);
+        }
+    }
+
+    mergeOption(newOption: GridOption, ecModel: GlobalModel) {
+        super.mergeOption.apply(this, arguments as any);
+
+        if (this.option.outerBounds && newOption.outerBounds) {
+            mergeLayoutParam(this.option.outerBounds, newOption.outerBounds);
+        }
+    }
+
     static defaultOption: GridOption = {
         show: false,
         // zlevel: 0,
@@ -88,7 +113,8 @@ class GridModel extends ComponentModel<GridOption> 
implements CoordinateSystemHo
         bottom: 70,
 
         containLabel: false,
-        outerBounds: 'auto',
+        outerBoundsMode: 'auto',
+        outerBounds: OUTER_BOUNDS_DEFAULT,
         outerBoundsContain: 'all',
 
         // width: {totalWidth} - left - right,
diff --git a/src/label/labelLayoutHelper.ts b/src/label/labelLayoutHelper.ts
index 1893730bf..6c9001c7a 100644
--- a/src/label/labelLayoutHelper.ts
+++ b/src/label/labelLayoutHelper.ts
@@ -90,6 +90,21 @@ export interface LabelIntersectionCheckInfo {
     transform: number[] | NullUndefined
 }
 
+export function createLabelLayoutList(
+    rawList: LabelLayoutInfoRaw[]
+): LabelLayoutInfoComputed[] {
+    const resultList: LabelLayoutInfoComputed[] = [];
+    each(rawList, raw => {
+        // PENDING: necessary?
+        raw = extend({}, raw);
+        const layoutInfo = prepareLabelLayoutInfo(raw);
+        if (layoutInfo) {
+            resultList.push(layoutInfo);
+        }
+    });
+    return resultList;
+}
+
 /**
  * If `defaultAttr.ignore: true`, return `NullUndefined`.
  *  (the caller is reponsible for ensuring the label is always `ignore: true`.)
@@ -155,21 +170,6 @@ export function rollbackToLabelLayoutInfoRaw(
     return raw;
 }
 
-export function createLabelLayoutList(
-    rawList: LabelLayoutInfoRaw[]
-): LabelLayoutInfoComputed[] {
-    const resultList: LabelLayoutInfoComputed[] = [];
-    each(rawList, raw => {
-        // PENDING: necessary?
-        raw = extend({}, raw);
-        const layoutInfo = prepareLabelLayoutInfo(raw);
-        if (layoutInfo) {
-            resultList.push(layoutInfo);
-        }
-    });
-    return resultList;
-}
-
 /**
  * This method supports that the label layout info is not computed until 
needed,
  * for performance consideration.
diff --git a/src/util/layout.ts b/src/util/layout.ts
index dcf2f7762..698038b07 100644
--- a/src/util/layout.ts
+++ b/src/util/layout.ts
@@ -549,7 +549,7 @@ export function mergeLayoutParam<T extends 
BoxLayoutOptionMixin>(
         each(names, function (name: BoxLayoutKeys) {
             // Consider case: newOption.width is null, which is
             // set by user for removing width setting.
-            hasProp(newOption, name) && (newParams[name] = merged[name] = 
newOption[name]);
+            zrUtil.hasOwn(newOption, name) && (newParams[name] = merged[name] 
= newOption[name]);
             hasValue(newParams, name) && newValueCount++;
             hasValue(merged, name) && mergedValueCount++;
         });
@@ -582,7 +582,7 @@ export function mergeLayoutParam<T extends 
BoxLayoutOptionMixin>(
             // Chose another param from targetOption by priority.
             for (let i = 0; i < names.length; i++) {
                 const name = names[i];
-                if (!hasProp(newParams, name) && hasProp(targetOption, name)) {
+                if (!zrUtil.hasOwn(newParams, name) && 
zrUtil.hasOwn(targetOption, name)) {
                     newParams[name] = targetOption[name];
                     break;
                 }
@@ -591,10 +591,6 @@ export function mergeLayoutParam<T extends 
BoxLayoutOptionMixin>(
         }
     }
 
-    function hasProp(obj: object, name: string): boolean {
-        return obj.hasOwnProperty(name);
-    }
-
     function hasValue(obj: Dictionary<any>, name: string): boolean {
         return obj[name] != null && obj[name] !== 'auto';
     }
@@ -620,7 +616,7 @@ export function getLayoutParams(source: 
BoxLayoutOptionMixin): BoxLayoutOptionMi
  */
 export function copyLayoutParams(target: BoxLayoutOptionMixin, source: 
BoxLayoutOptionMixin): BoxLayoutOptionMixin {
     source && target && each(LOCATION_PARAMS, function (name: BoxLayoutKeys) {
-        source.hasOwnProperty(name) && (target[name] = source[name]);
+        zrUtil.hasOwn(source, name) && (target[name] = source[name]);
     });
     return target;
 }
diff --git a/test/axis-layout-0.html b/test/axis-layout-0.html
index b3051e783..ea320e699 100755
--- a/test/axis-layout-0.html
+++ b/test/axis-layout-0.html
@@ -58,7 +58,7 @@ under the License.
                 let _useLazyUpdate;
                 let _currNameTextMargin;
                 let _currAxisLabelTextMargin;
-                let _currOuterBounds;
+                let _currOuterBoundsMode;
                 let _currXAxisType;
                 let _currYAxisType;
                 let _currContainLabel;
@@ -68,7 +68,7 @@ under the License.
                     _useLazyUpdate = false;
                     _currNameTextMargin = [3, 5, 3, 5];
                     _currAxisLabelTextMargin = [0, 3, 0, 3];
-                    _currOuterBounds = undefined;
+                    _currOuterBoundsMode = undefined;
                     _currXAxisType = 'value';
                     _currYAxisType = 'log';
                     _currContainLabel = false;
@@ -146,7 +146,7 @@ under the License.
                 }
 
                 function updateIndicatorShapeOptions() {
-                    let parsedOuterBounds = _currOuterBounds;
+                    let parsedOuterBounds = _currOuterBoundsMode;
                     if (parsedOuterBounds == null || parsedOuterBounds === 
true || parsedOuterBounds === 'auto') {
                         if (_currContainLabel) {
                             parsedOuterBounds = 'same';
@@ -564,13 +564,13 @@ under the License.
                         },
                         {
                             type: 'select',
-                            text: 'grid.outerBounds:',
-                            values: [undefined, {}, 'same', false, true, 
'auto'],
+                            text: 'grid.outerBoundsMode:',
+                            values: [undefined, 'same', 'none', 'auto'],
                             onchange() {
-                                _currOuterBounds = this.value;
+                                _currOuterBoundsMode = this.value;
                                 updateIndicatorShapeOptions();
                                 chartSetOption(chart, {
-                                    grid: {outerBounds: _currOuterBounds},
+                                    grid: {outerBoundsMode: 
_currOuterBoundsMode},
                                     graphic: {
                                         elements: [
                                             makeGridBoxIndicatorOption(),
@@ -587,10 +587,6 @@ under the License.
                             max: 500,
                             value: _initValues.grid[prop],
                             onchange() {
-                                if (testHelper.getType(_currOuterBounds) !== 
'object') {
-                                    console.error('should set grid.outerBounds 
to object first');
-                                    return;
-                                }
                                 const newVal = this.value;
                                 _gridOuterBoundsIndicatorShapeOptions[prop] = 
newVal;
                                 updateIndicatorShapeOptions();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to