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 238b36769b51ed82ad7bfcbcec65dbb8c834f25b
Merge: f75a60310 2b0bd9a7c
Author: 100pah <[email protected]>
AuthorDate: Thu Apr 10 23:29:21 2025 +0800

    merge v6

 package.json                                       |    2 +-
 src/chart/bar/BarView.ts                           |   13 +
 src/chart/chord/ChordEdge.ts                       |  192 +++
 src/chart/chord/ChordPiece.ts                      |  169 ++
 src/chart/chord/ChordSeries.ts                     |  340 ++++
 src/chart/chord/ChordView.ts                       |  153 ++
 src/chart/chord/chordLayout.ts                     |  269 +++
 src/{export/api/time.ts => chart/chord/install.ts} |   15 +-
 src/chart/funnel/funnelLayout.ts                   |   11 +-
 src/chart/pie/PieView.ts                           |   13 +-
 src/chart/pie/pieLayout.ts                         |   63 +-
 src/chart/sankey/sankeyLayout.ts                   |   14 +-
 src/chart/tree/layoutHelper.ts                     |   15 -
 src/chart/tree/treeLayout.ts                       |    2 +-
 src/component/axis/AngleAxisView.ts                |    2 +-
 src/component/axis/AxisBuilder.ts                  |  232 ++-
 src/component/axis/CartesianAxisView.ts            |   36 +-
 src/component/axis/ParallelAxisView.ts             |    2 +-
 src/component/axis/RadiusAxisView.ts               |    5 +-
 src/component/axis/SingleAxisView.ts               |   38 +-
 src/component/axis/axisAction.ts                   |  147 ++
 src/component/axis/axisBreakHelper.ts              |   90 +
 src/component/axis/axisBreakHelperImpl.ts          |  559 ++++++
 src/component/axis/axisSplitHelper.ts              |    4 +-
 .../api/time.ts => component/axis/installBreak.ts} |   12 +-
 src/component/axisPointer/axisTrigger.ts           |    3 +-
 src/component/marker/MarkPointView.ts              |   21 +-
 src/component/marker/MarkerModel.ts                |    1 +
 src/component/marker/markerHelper.ts               |   14 +-
 src/component/radar/RadarView.ts                   |    6 +-
 src/component/tooltip/TooltipView.ts               |    3 +
 src/coord/Axis.ts                                  |   20 +-
 src/coord/CoordinateSystem.ts                      |    6 +-
 src/coord/axisAlignTicks.ts                        |   15 +-
 src/coord/axisCommonTypes.ts                       |  102 +-
 src/coord/axisDefault.ts                           |   32 +-
 src/coord/axisHelper.ts                            |  112 +-
 src/coord/axisModelCreator.ts                      |   15 +-
 src/coord/axisTickLabelBuilder.ts                  |   34 +-
 src/coord/cartesian/Cartesian2D.ts                 |    7 +-
 src/coord/cartesian/Grid.ts                        |    5 +-
 src/coord/cartesian/cartesianAxisHelper.ts         |    6 +-
 src/coord/polar/Polar.ts                           |    8 +-
 src/core/ExtendedElement.ts                        |    9 +
 src/core/echarts.ts                                |  287 +++-
 src/data/DataStore.ts                              |   57 -
 src/data/SeriesData.ts                             |   14 -
 src/echarts.all.ts                                 |    9 +-
 src/echarts.common.ts                              |   10 +-
 src/export/api/time.ts                             |    2 +-
 src/export/charts.ts                               |    2 +
 src/export/core.ts                                 |   11 +-
 src/export/features.ts                             |    3 +-
 src/export/option.ts                               |    3 +
 src/label/labelLayoutHelper.ts                     |  114 +-
 src/legacy/dataSelectAction.ts                     |    6 +-
 src/model/Global.ts                                |    1 +
 src/model/Series.ts                                |   61 +-
 src/scale/Interval.ts                              |  148 +-
 src/scale/Log.ts                                   |  160 +-
 src/scale/Ordinal.ts                               |   21 +-
 src/scale/Scale.ts                                 |  122 +-
 src/scale/Time.ts                                  |  312 ++--
 src/scale/break.ts                                 |  147 ++
 src/scale/breakImpl.ts                             |  717 ++++++++
 src/scale/helper.ts                                |   45 +-
 src/util/graphic.ts                                |    5 +-
 src/util/layout.ts                                 |   59 +-
 src/util/states.ts                                 |    1 +
 src/util/time.ts                                   |  340 +++-
 src/util/types.ts                                  |  192 ++-
 test/axis-break-2.html                             | 1812 ++++++++++++++++++++
 test/axis-break-3.html                             |  552 ++++++
 test/axis-break-4.html                             |  831 +++++++++
 test/axis-break.html                               | 1109 ++++++++++++
 test/chord.html                                    |  719 ++++++++
 test/connect.html                                  |    3 +
 test/dataSelect.html                               |   11 +-
 test/lib/caseFrame.js                              |    5 +-
 test/lib/config.js                                 |    2 +
 test/markPoint-stock.html                          |  207 +++
 test/runTest/actions/__meta__.json                 |    5 +
 test/runTest/actions/axis-break-2.json             |    1 +
 test/runTest/actions/axis-break-3.json             |    1 +
 test/runTest/actions/axis-break-4.json             |    1 +
 test/runTest/actions/axis-break.json               |    1 +
 test/runTest/actions/chord.json                    |    1 +
 test/runTest/client/client.js                      |    4 +-
 test/tmp-base.html                                 |    5 +
 test/tooltip-axisPointer-snap.html                 |  370 ++++
 test/ut/spec/data/SeriesData.test.ts               |   21 -
 test/ut/spec/scale/interval.test.ts                |    3 +-
 test/ut/spec/util/time.test.ts                     |   49 +-
 93 files changed, 10501 insertions(+), 878 deletions(-)

diff --cc src/component/axis/AxisBuilder.ts
index 846bc62f6,813f7d5c0..317f1acc0
--- a/src/component/axis/AxisBuilder.ts
+++ b/src/component/axis/AxisBuilder.ts
@@@ -30,13 -30,26 +30,25 @@@ import * as matrixUtil from 'zrender/sr
  import {applyTransform as v2ApplyTransform} from 'zrender/src/core/vector';
  import {isNameLocationCenter, shouldShowAllLabels} from 
'../../coord/axisHelper';
  import { AxisBaseModel } from '../../coord/AxisBaseModel';
- import { ZRTextVerticalAlign, ZRTextAlign, ECElement, ColorString } from 
'../../util/types';
+ import {
+     ZRTextVerticalAlign, ZRTextAlign, ECElement, ColorString,
+     VisualAxisBreak,
+     ParsedAxisBreak,
+     LabelMarginType,
+     LabelExtendedText,
+ } from '../../util/types';
 -import { AxisBaseOption } from '../../coord/axisCommonTypes';
 +import { AxisBaseOption, AxisBaseOptionCommon } from 
'../../coord/axisCommonTypes';
  import type Element from 'zrender/src/Element';
- import { PathStyleProps } from 'zrender/src/graphic/Path';
+ import { PathProps, PathStyleProps } from 'zrender/src/graphic/Path';
  import OrdinalScale from '../../scale/Ordinal';
- import { prepareLayoutList, hideOverlap } from 
'../../label/labelLayoutHelper';
- 
+ import {
+     prepareLayoutList, hideOverlap, detectAxisLabelPairIntersection,
+ } from '../../label/labelLayoutHelper';
+ import ExtensionAPI from '../../core/ExtensionAPI';
 -import CartesianAxisModel from '../../coord/cartesian/AxisModel';
+ import { makeInner } from '../../util/model';
+ import { getAxisBreakHelper } from './axisBreakHelper';
+ import { AXIS_BREAK_EXPAND_ACTION_TYPE, BaseAxisBreakPayload } from 
'./axisAction';
+ import { getScaleBreakHelper } from '../../scale/break';
  
  const PI = Math.PI;
  
@@@ -137,12 -162,14 +163,17 @@@ class AxisBuilder 
      readonly group = new graphic.Group();
  
      private _transformGroup: graphic.Group;
+     private _api: ExtensionAPI;
  
 +    /**
 +     * [CAUTION]: axisModel.axis.extent/scale must be ready to use.
 +     */
-     constructor(axisModel: AxisBaseModel, opt?: AxisBuilderCfg) {
- 
+     constructor(
+         axisModel: AxisBaseModel,
+         api: ExtensionAPI,
+         opt?: AxisBuilderCfg
+     ) {
+         this._api = api;
          this.opt = opt;
  
          this.axisModel = axisModel;
@@@ -176,13 -203,12 +207,13 @@@
          this._transformGroup = transformGroup;
      }
  
 -    hasBuilder(name: keyof typeof builders) {
 -        return !!builders[name];
 -    }
 -
 -    add(name: keyof typeof builders) {
 -        builders[name](this.opt, this.axisModel, this.group, 
this._transformGroup, this._api);
 +    build(axisPartNameMap: AxisBuilderAxisPartMap) {
 +        // axisName layout depends on axisTickLabel layout result to resolve 
overlap.
 +        each(['axisLine', 'axisTickLabel', 'axisName'] as const, partName => {
 +            if (axisPartNameMap[partName]) {
-                 builders[partName](this.opt, this.axisModel, this.group, 
this._transformGroup);
++                builders[partName](this.opt, this.axisModel, this.group, 
this._transformGroup, this._api);
 +            }
 +        });
      }
  
      getGroup() {
@@@ -248,12 -275,9 +280,12 @@@ interface AxisElementsBuilder 
      ):void
  }
  
 -const builders: Record<'axisLine' | 'axisTickLabel' | 'axisName', 
AxisElementsBuilder> = {
 +export type AxisBuilderAxisPartName = 'axisLine' | 'axisTickLabel' | 
'axisName';
 +export type AxisBuilderAxisPartMap = {[axisPartName in 
AxisBuilderAxisPartName]?: boolean};
 +
 +const builders: Record<AxisBuilderAxisPartName, AxisElementsBuilder> = {
  
-     axisLine(opt, axisModel, group, transformGroup) {
+     axisLine(opt, axisModel, group, transformGroup, api) {
  
          let shown = axisModel.get(['axisLine', 'show']);
          if (shown === 'auto' && opt.handleAutoShown) {
diff --cc src/component/axis/CartesianAxisView.ts
index 5a8088713,2e44150bf..81084e766
--- a/src/component/axis/CartesianAxisView.ts
+++ b/src/component/axis/CartesianAxisView.ts
@@@ -27,14 -28,14 +27,15 @@@ import ExtensionAPI from '../../core/Ex
  import CartesianAxisModel from '../../coord/cartesian/AxisModel';
  import GridModel from '../../coord/cartesian/GridModel';
  import { Payload } from '../../util/types';
 -import { isIntervalOrLogScale } from '../../scale/helper';
+ import { getAxisBreakHelper } from './axisBreakHelper';
  
 -const axisBuilderAttrs = [
 -    'axisLine', 'axisTickLabel', 'axisName'
 -] as const;
 +const axisBuilderAttrs = {
 +    axisLine: true,
 +    axisTickLabel: true,
 +    axisName: true,
 +} as const;
  const selfBuilderAttrs = [
-     'splitArea', 'splitLine', 'minorSplitLine'
+     'splitArea', 'splitLine', 'minorSplitLine', 'breakArea'
  ] as const;
  
  class CartesianAxisView extends AxisView {
@@@ -64,13 -65,25 +65,14 @@@
  
          const gridModel = axisModel.getCoordSysModel();
  
 -        const layout = cartesianAxisHelper.layout(gridModel, axisModel);
 -
 -        const axisBuilder = new AxisBuilder(axisModel, api, zrUtil.extend({
 -            handleAutoShown(elementType) {
 -                const cartesians = gridModel.coordinateSystem.getCartesians();
 -                for (let i = 0; i < cartesians.length; i++) {
 -                    if 
(isIntervalOrLogScale(cartesians[i].getOtherAxis(axisModel.axis).scale)) {
 -                        // Still show axis tick or axisLine if other axis is 
value / log
 -                        return true;
 -                    }
 -                }
 -                // Not show axisTick or axisLine if other axis is category / 
time
 -                return false;
 -            }
 -        } as AxisBuilderCfg, layout));
 -
 -        zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder);
 -
 -        this._axisGroup.add(axisBuilder.getGroup());
 +        const grid = gridModel.coordinateSystem;
 +        
this._axisGroup.add(cartesianAxisHelper.buildCartesianAxisViewCommonPart(
 +            axisBuilderAttrs,
 +            grid.getRect(),
 +            grid.getCartesians(),
-             axisModel
++            axisModel,
++            api
 +        ));
  
          zrUtil.each(selfBuilderAttrs, function (name) {
              if (axisModel.get([name, 'show'])) {
diff --cc src/component/axis/ParallelAxisView.ts
index d4fae3c27,b2c8b38e8..de6e40554
--- a/src/component/axis/ParallelAxisView.ts
+++ b/src/component/axis/ParallelAxisView.ts
@@@ -92,9 -92,9 +92,9 @@@ class ParallelAxisView extends Componen
              axisLayout
          );
  
-         const axisBuilder = new AxisBuilder(axisModel, builderOpt);
+         const axisBuilder = new AxisBuilder(axisModel, api, builderOpt);
  
 -        zrUtil.each(elementList, axisBuilder.add, axisBuilder);
 +        axisBuilder.build(axisBuilderAxisPartMap);
  
          this._axisGroup.add(axisBuilder.getGroup());
  
diff --cc src/component/axis/RadiusAxisView.ts
index 404804a00,4ca88f664..611a06e5c
--- a/src/component/axis/RadiusAxisView.ts
+++ b/src/component/axis/RadiusAxisView.ts
@@@ -25,10 -25,11 +25,11 @@@ import { RadiusAxisModel } from '../../
  import Polar from '../../coord/polar/Polar';
  import RadiusAxis from '../../coord/polar/RadiusAxis';
  import GlobalModel from '../../model/Global';
+ import ExtensionAPI from '../../core/ExtensionAPI';
  
 -const axisBuilderAttrs = [
 -    'axisLine', 'axisTickLabel', 'axisName'
 -] as const;
 +const axisBuilderAttrs = {
 +    axisLine: true, axisTickLabel: true, axisName: true
 +} as const;
  const selfBuilderAttrs = [
      'splitLine', 'splitArea', 'minorSplitLine'
  ] as const;
@@@ -63,8 -64,8 +64,8 @@@ class RadiusAxisView extends AxisView 
          const radiusExtent = radiusAxis.getExtent();
  
          const layout = layoutAxis(polar, radiusAxisModel, axisAngle);
-         const axisBuilder = new AxisBuilder(radiusAxisModel, layout);
+         const axisBuilder = new AxisBuilder(radiusAxisModel, api, layout);
 -        zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder);
 +        axisBuilder.build(axisBuilderAttrs);
          newAxisGroup.add(axisBuilder.getGroup());
  
          graphic.groupTransition(oldAxisGroup, newAxisGroup, radiusAxisModel);
diff --cc src/component/axis/SingleAxisView.ts
index 8a1ce700b,ec1701b6d..ad6d90908
--- a/src/component/axis/SingleAxisView.ts
+++ b/src/component/axis/SingleAxisView.ts
@@@ -27,12 -27,13 +27,13 @@@ import SingleAxisModel from '../../coor
  import GlobalModel from '../../model/Global';
  import ExtensionAPI from '../../core/ExtensionAPI';
  import { Payload } from '../../util/types';
+ import { getAxisBreakHelper } from './axisBreakHelper';
  
 -const axisBuilderAttrs = [
 -    'axisLine', 'axisTickLabel', 'axisName'
 -] as const;
 +const axisBuilderAttrs = {
 +    axisLine: true, axisTickLabel: true, axisName: true
 + } as const;
  
- const selfBuilderAttrs = ['splitArea', 'splitLine'] as const;
+ const selfBuilderAttrs = ['splitArea', 'splitLine', 'breakArea'] as const;
  
  class SingleAxisView extends AxisView {
  
@@@ -54,9 -55,9 +55,9 @@@
  
          const layout = singleAxisHelper.layout(axisModel);
  
-         const axisBuilder = new AxisBuilder(axisModel, layout);
+         const axisBuilder = new AxisBuilder(axisModel, api, layout);
  
 -        zrUtil.each(axisBuilderAttrs, axisBuilder.add, axisBuilder);
 +        axisBuilder.build(axisBuilderAttrs);
  
          group.add(this._axisGroup);
          group.add(axisBuilder.getGroup());
diff --cc src/coord/axisHelper.ts
index 8d5b26df4,195dc06f0..c6321472f
--- a/src/coord/axisHelper.ts
+++ b/src/coord/axisHelper.ts
@@@ -37,15 -38,20 +37,20 @@@ import 
      CategoryAxisBaseOption,
      LogAxisBaseOption,
      TimeAxisLabelFormatterOption,
-     ValueAxisBaseOption
+     AxisBaseOptionCommon,
+     AxisLabelCategoryFormatter,
+     AxisLabelValueFormatter,
+     AxisLabelFormatterExtraParams,
  } from './axisCommonTypes';
 -import CartesianAxisModel, { CartesianAxisPosition, 
inverseCartesianAxisPositionMap } from './cartesian/AxisModel';
 +import CartesianAxisModel from './cartesian/AxisModel';
  import SeriesData from '../data/SeriesData';
  import { getStackedDimension } from '../data/helper/dataStackHelper';
- import { Dictionary, DimensionName, ScaleTick, TimeScaleTick } from 
'../util/types';
+ import { Dictionary, DimensionName, ScaleTick } from '../util/types';
  import { ensureScaleRawExtentInfo } from './scaleRawExtentInfo';
 -import Axis2D from './cartesian/Axis2D';
 +import BoundingRect from 'zrender/src/core/BoundingRect';
+ import { parseTimeAxisLabelFormatter } from '../util/time';
+ import { getScaleBreakHelper } from '../scale/break';
+ import { error } from '../util/log';
  
  
  type BarWidthAndOffset = ReturnType<typeof makeColumnLayout>;
@@@ -402,6 -431,159 +413,33 @@@ export function unionAxisExtentFromData
      }
  }
  
 -export function isNameLocationCenter(nameLocation: string) {
 +export function isNameLocationCenter(nameLocation: 
AxisBaseOptionCommon['nameLocation']) {
      return nameLocation === 'middle' || nameLocation === 'center';
  }
+ 
 -function isNameLocationStart(nameLocation: string) {
 -    return nameLocation === 'start';
 -}
 -
 -function isNameLocationEnd(nameLocation: string) {
 -    return nameLocation === 'end';
 -}
 -
 -
 -export type CartesianAxisPositionMargins = {[K in CartesianAxisPosition]: 
number};
 -
 -export type ReservedSpace = {
 -    labels: CartesianAxisPositionMargins,
 -    name: CartesianAxisPositionMargins,
 -    nameGap: CartesianAxisPositionMargins,
 -    namePositionCurrAxis: CartesianAxisPosition
 -};
 -
 -/*
 - * Compute the reserved space (determined by axis labels and axis names) in 
each direction
 - */
 -export function computeReservedSpace(
 -    axis: Axis2D, labelUnionRect: BoundingRect, nameBoundingRect: BoundingRect
 -): ReservedSpace {
 -    const reservedSpace: ReservedSpace = {
 -        labels: {left: 0, top: 0, right: 0, bottom: 0},
 -        nameGap: {left: 0, top: 0, right: 0, bottom: 0},
 -        name: {left: 0, top: 0, right: 0, bottom: 0},
 -        namePositionCurrAxis: null
 -    };
 -
 -    const boundingRectDim = axis.isHorizontal() ? 'height' : 'width';
 -
 -    if (labelUnionRect) {
 -        const margin = axis.model.get(['axisLabel', 'margin']);
 -        reservedSpace.labels[axis.position] = labelUnionRect[boundingRectDim] 
+ margin;
 -    }
 -
 -    if (nameBoundingRect) {
 -        let nameLocation = axis.model.get('nameLocation');
 -        const onZeroOfAxis = axis.getAxesOnZeroOf()?.[0];
 -        let namePositionOrthogonalAxis: CartesianAxisPosition = axis.position;
 -        if (onZeroOfAxis && ['start', 'end'].includes(nameLocation)) {
 -            const defaultZero = onZeroOfAxis.isHorizontal() ? 'left' : 
'bottom';
 -            namePositionOrthogonalAxis = onZeroOfAxis.inverse
 -                ? inverseCartesianAxisPositionMap[defaultZero]
 -                : defaultZero;
 -        }
 -
 -        const nameGap = axis.model.get('nameGap');
 -        const nameRotate = axis.model.get('nameRotate');
 -
 -        if (axis.inverse) {
 -            if (nameLocation === 'start') {
 -                nameLocation = 'end';
 -            }
 -            else if (nameLocation === 'end') {
 -                nameLocation = 'start';
 -            }
 -        }
 -
 -        const nameBoundingRectSize = nameBoundingRect[boundingRectDim];
 -
 -        if (isNameLocationCenter(nameLocation)) {
 -            reservedSpace.namePositionCurrAxis = axis.position;
 -            reservedSpace.nameGap[axis.position] = nameGap;
 -            reservedSpace.name[axis.position] = nameBoundingRectSize;
 -        }
 -        else {
 -            const inverseBoundingRectDim = boundingRectDim === 'height' ? 
'width' : 'height';
 -            const nameBoundingRectSizeInverseDim = 
nameBoundingRect?.[inverseBoundingRectDim] || 0;
 -
 -            const rotationInRadians = nameRotate * (Math.PI / 180);
 -            const sin = Math.sin(rotationInRadians);
 -            const cos = Math.cos(rotationInRadians);
 -
 -            const nameRotationIsFirstOrThirdQuadrant = sin > 0 && cos > 0 || 
sin < 0 && cos < 0;
 -            const nameRotationIsSecondOrFourthQuadrant = sin > 0 && cos < 0 
|| sin < 0 && cos > 0;
 -            const nameRotationIsMultipleOf180degrees = sin === 0 || cos === 1 
|| cos === -1;
 -            const nameRotationIsMultipleOf90degrees =
 -                nameRotationIsMultipleOf180degrees || sin === 1 || sin === -1 
|| cos === 0;
 -
 -            const nameLocationIsStart = isNameLocationStart(nameLocation);
 -            const nameLocationIsEnd = isNameLocationEnd(nameLocation);
 -
 -            const reservedSpacePosition = axis.isHorizontal()
 -                ? (nameLocationIsStart ? 'left' : 'right')
 -                : (nameLocationIsStart ? 'bottom' : 'top');
 -
 -            reservedSpace.namePositionCurrAxis = reservedSpacePosition;
 -            reservedSpace.nameGap[reservedSpacePosition] = nameGap;
 -            reservedSpace.name[reservedSpacePosition] = 
nameBoundingRectSizeInverseDim;
 -
 -            const reservedLabelSpace = 
reservedSpace.labels[namePositionOrthogonalAxis];
 -            const reservedNameSpace = nameBoundingRectSize - 
reservedLabelSpace;
 -
 -            const orthogonalAxisPositionIsTop = namePositionOrthogonalAxis 
=== 'top';
 -            const orthogonalAxisPositionIsBottom = namePositionOrthogonalAxis 
=== 'bottom';
 -            const orthogonalAxisPositionIsLeft = namePositionOrthogonalAxis 
=== 'left';
 -            const orthogonalAxisPositionIsRight = namePositionOrthogonalAxis 
=== 'right';
 -
 -            if (axis.isHorizontal() && nameRotationIsMultipleOf90degrees
 -                || !axis.isHorizontal() && 
nameRotationIsMultipleOf180degrees) {
 -                    reservedSpace.name[namePositionOrthogonalAxis] = 
nameBoundingRectSize / 2 - reservedLabelSpace;
 -            }
 -            else if (
 -                axis.isHorizontal() && (
 -                    nameLocationIsStart && orthogonalAxisPositionIsTop && 
nameRotationIsSecondOrFourthQuadrant
 -                        || nameLocationIsStart && 
orthogonalAxisPositionIsBottom && nameRotationIsFirstOrThirdQuadrant
 -                        || nameLocationIsEnd && orthogonalAxisPositionIsTop 
&& nameRotationIsFirstOrThirdQuadrant
 -                        || nameLocationIsEnd && 
orthogonalAxisPositionIsBottom && nameRotationIsSecondOrFourthQuadrant
 -                )
 -                || !axis.isHorizontal() && (
 -                    nameLocationIsStart && orthogonalAxisPositionIsLeft && 
nameRotationIsFirstOrThirdQuadrant
 -                        || nameLocationIsStart && 
orthogonalAxisPositionIsRight && nameRotationIsSecondOrFourthQuadrant
 -                        || nameLocationIsEnd && orthogonalAxisPositionIsLeft 
&& nameRotationIsSecondOrFourthQuadrant
 -                        || nameLocationIsEnd && orthogonalAxisPositionIsRight 
&& nameRotationIsFirstOrThirdQuadrant
 -                )
 -            ) {
 -                reservedSpace.name[namePositionOrthogonalAxis] = 
reservedNameSpace;
 -            }
 -        }
 -    }
 -    return reservedSpace;
 -}
 -
+ export function retrieveAxisBreaksOption(model: AxisBaseModel): 
AxisBaseOptionCommon['breaks'] {
+     const option = model.get('breaks', true);
+     if (option != null) {
+         if (!getScaleBreakHelper()) {
+             if (__DEV__) {
+                 error(
+                     'Must `import {AxisBreak} from "echarts/features"; 
use(AxisBreak);` first if using breaks option.'
+                 );
+             }
+             return undefined;
+         }
+         if (!isSupportAxisBreak(model.axis)) {
+             if (__DEV__) {
+                 error(`Axis '${model.axis.dim}'-'${model.axis.type}' does not 
support break.`);
+             }
+             return undefined;
+         }
+         return option;
+     }
+ }
+ 
+ function isSupportAxisBreak(axis: Axis): boolean {
+     // The polar radius axis can also support break feasibly. Do not do it 
until the requirements are met.
+     return (axis.dim === 'x' || axis.dim === 'y' || axis.dim === 'z' || 
axis.dim === 'single')
+         && axis.type !== 'category';
+ }
diff --cc src/coord/cartesian/Grid.ts
index 18daa1822,2c10825aa..44c040e09
--- a/src/coord/cartesian/Grid.ts
+++ b/src/coord/cartesian/Grid.ts
@@@ -195,21 -184,49 +195,21 @@@ class Grid implements CoordinateSystemM
              });
  
          this._rect = gridRect;
 +        const axesMap = this._axesMap;
  
 -        const axesList = this._axesList;
 -
 -        adjustAxes();
 -
 -        // Minus label, name, and nameGap size
 -        if (isContainLabel) {
 -            const reservedSpacePerAxis: ReservedSpace[] = [];
 -            each(axesList, function (axis) {
 -                const nameBoundingRect = computeNameBoundingRect(axis);
 -
 -                let labelUnionRect: BoundingRect;
 -                if (!axis.model.get(['axisLabel', 'inside'])) {
 -                    labelUnionRect = estimateLabelUnionRect(axis);
 -                }
 -
 -                reservedSpacePerAxis.push(computeReservedSpace(axis, 
labelUnionRect, nameBoundingRect));
 -            });
 -
 -            const maxLabelSpace: CartesianAxisPositionMargins = { left: 0, 
top: 0, right: 0, bottom: 0};
 -            const maxNameAndNameGapSpace: CartesianAxisPositionMargins = { 
left: 0, top: 0, right: 0, bottom: 0};
 -            const cartesianAxisPositions: CartesianAxisPosition[] = ['left', 
'top', 'right', 'bottom'];
 -
 -            each(cartesianAxisPositions, (position) => {
 -                maxLabelSpace[position] = 
Math.max(...map(reservedSpacePerAxis, ({ labels }) => labels[position]));
 -                maxNameAndNameGapSpace[position] =
 -                    Math.max(...map(reservedSpacePerAxis, ({ name, nameGap }) 
=> name[position] + nameGap[position]));
 -            });
 -
 -            axesList.forEach((axis, axisIndex) => {
 -                axis.model.axisToNameGapStartGap =
 -                    
maxLabelSpace[reservedSpacePerAxis[axisIndex].namePositionCurrAxis];
 -            });
 -
 -            const maxReservedSpaceLeft = maxLabelSpace.left + 
maxNameAndNameGapSpace.left;
 -            const maxReservedSpaceTop = maxLabelSpace.top + 
maxNameAndNameGapSpace.top;
 -
 -            gridRect.x += maxReservedSpaceLeft;
 -            gridRect.y += maxReservedSpaceTop;
 -            gridRect.width -= maxReservedSpaceLeft + maxLabelSpace.right + 
maxNameAndNameGapSpace.right;
 -            gridRect.height -= maxReservedSpaceTop + maxLabelSpace.bottom + 
maxNameAndNameGapSpace.bottom;
 +        const optionContainLabel = gridModel.get('containLabel'); // No 
`true` for backward compat.
 +        const optionLayoutContain = gridModel.get('layoutContain', true) || 
{};
  
 -            adjustAxes();
 +        if (!beforeDataProcessing
 +            && (optionLayoutContain.axisLabel || optionLayoutContain.axisName)
 +        ) {
-             layOutGridByContained(optionLayoutContain, gridRect, 
this._coordsList, this._axesMap);
++            layOutGridByContained(optionLayoutContain, gridRect, 
this._coordsList, this._axesMap, api);
 +        }
 +        else if (!beforeDataProcessing && optionContainLabel) {
 +            legacyLayOutGridByContained(axesMap, this._axesList, gridRect);
 +        }
 +        else {
 +            updateAxisLayoutAllByGridRect(axesMap, gridRect);
          }
  
          each(this._coordsList, function (coord) {
@@@ -657,97 -684,4 +657,98 @@@ function updateAxisTransform(axis: Axis
          };
  }
  
 +function updateAxisLayoutAllByGridRect(axesMap: AxesMap, gridRect: 
LayoutRect) {
 +    each(axesMap.x, axis => updateAxisLayoutByGridRect(axis, gridRect.x, 
gridRect.width));
 +    each(axesMap.y, axis => updateAxisLayoutByGridRect(axis, gridRect.y, 
gridRect.height));
 +}
 +
 +function updateAxisLayoutByGridRect(axis: Axis2D, gridXY: number, gridWH: 
number): void {
 +    const extent = [0, gridWH];
 +    const idx = axis.inverse ? 1 : 0;
 +    axis.setExtent(extent[idx], extent[1 - idx]);
 +    updateAxisTransform(axis, gridXY);
 +}
 +
 +/**
 + * The input gridRect and axes will be modified.
 + */
 +function legacyLayOutGridByContained(axesMap: AxesMap, axesList: Axis2D[], 
gridRect: LayoutRect): void {
 +    updateAxisLayoutAllByGridRect(axesMap, gridRect);
 +    each(axesList, function (axis) {
 +        if (!axis.model.get(['axisLabel', 'inside'])) {
 +            const labelUnionRect = legacyEstimateLabelUnionRect(axis);
 +            if (labelUnionRect) {
 +                const dim: 'height' | 'width' = axis.isHorizontal() ? 
'height' : 'width';
 +                const margin = axis.model.get(['axisLabel', 'margin']);
 +                gridRect[dim] -= labelUnionRect[dim] + margin;
 +                if (axis.position === 'top') {
 +                    gridRect.y += labelUnionRect.height + margin;
 +                }
 +                else if (axis.position === 'left') {
 +                    gridRect.x += labelUnionRect.width + margin;
 +                }
 +            }
 +        }
 +    });
 +    updateAxisLayoutAllByGridRect(axesMap, gridRect);
 +}
 +
 +/**
 + * The input gridRect and axes will be modified.
 + */
 +function layOutGridByContained(
 +    optionLayoutContain: GridOption['layoutContain'],
 +    gridRect: LayoutRect,
 +    cartesians: Cartesian2D[],
 +    axesMap: AxesMap,
++    api: ExtensionAPI
 +): void {
 +    const axisBuilderAxisPartMap = {
 +        axisLine: true,
 +        axisTickLabel: !!optionLayoutContain.axisLabel,
 +        axisName: !!optionLayoutContain.axisName,
 +    };
 +
 +    updateAxisLayoutAllByGridRect(axesMap, gridRect);
 +
 +    const layoutRect = BoundingRect.create(gridRect);
 +    // The bounding rect of the created `axisGroup` might be sensitve to 
variations in
 +    // `axis.extent` due to strategies like hideOverlap/moveOverlap. To make 
it more
 +    // consistent to the final actual layout, `gridRect` is modified 
immediately one dimension
 +    // is calculated, and the latter calculation is based on the updated 
`gridRect`. And yAxis
 +    // is calculated first, as empirically, the yAxis lables is less 
sensitive to variations
 +    // in "axis.extent".
 +    each([1, 0] as const, xyIdx => {
 +        // - Considered axis may be blank or no labels and the returned rect 
size is 0.
 +        // - The final rect must not be greater than the original input 
`gridRect`. That is, event if
 +        //  labels/ticks/lines/names are all hide or inside, other parts not 
addressed here, such as
 +        //  splitLine and tooltip trigger area, still need to be displayed 
within the gridRect.
 +        const unionRect = BoundingRect.create(gridRect);
 +        each(axesMap[XY[xyIdx]], axis => {
 +            const axisGroup = buildCartesianAxisViewCommonPart(
-                 axisBuilderAxisPartMap, gridRect, cartesians, axis.model
++                axisBuilderAxisPartMap, gridRect, cartesians, axis.model, api
 +            );
 +            unionRect.union(axisGroup.getBoundingRect());
 +        });
 +        trimGridRect(unionRect, 1 - xyIdx);
 +        trimGridRect(unionRect, xyIdx);
 +
 +        function trimGridRect(unionRect: BoundingRect, xyIdx: number): void {
 +            const wh = WH[xyIdx];
 +            const xy = XY[xyIdx];
 +            let minNew = gridRect[xy]
 +                + Math.max(0, layoutRect[xy] - unionRect[xy]);
 +            let maxNew = (gridRect[xy] + gridRect[wh])
 +                - Math.max(0, (unionRect[xy] + unionRect[wh]) - 
(layoutRect[xy] + layoutRect[wh]));
 +            if (minNew > maxNew) {
 +                minNew = maxNew = (minNew + maxNew) / 2;
 +            }
 +            gridRect[xy] = minNew;
 +            gridRect[wh] = maxNew - minNew;
 +
 +            each(axesMap[xy], axis => updateAxisLayoutByGridRect(axis, 
gridRect[xy], gridRect[wh]));
 +        }
 +    });
 +}
 +
  export default Grid;
diff --cc src/coord/cartesian/cartesianAxisHelper.ts
index 18ad36f2b,6f977bf2a..b17a38f48
--- a/src/coord/cartesian/cartesianAxisHelper.ts
+++ b/src/coord/cartesian/cartesianAxisHelper.ts
@@@ -22,11 -22,7 +22,12 @@@ import * as zrUtil from 'zrender/src/co
  import CartesianAxisModel from './AxisModel';
  import SeriesModel from '../../model/Series';
  import { SINGLE_REFERRING } from '../../util/model';
 +import { LayoutRect } from '../../util/layout';
 +import Group from 'zrender/src/graphic/Group';
 +import AxisBuilder, { AxisBuilderAxisPartMap, AxisBuilderCfg } from 
'../../component/axis/AxisBuilder';
 +import { isIntervalOrLogScale } from '../../scale/helper';
 +import type Cartesian2D from './Cartesian2D';
++import ExtensionAPI from '../../core/ExtensionAPI';
  
  interface CartesianAxisLayout {
      position: [number, number];
@@@ -134,28 -132,3 +135,29 @@@ export function findAxisModels(seriesMo
      return axisModelMap;
  }
  
 +export function buildCartesianAxisViewCommonPart(
 +    axisBuilderAxisPartMap: AxisBuilderAxisPartMap,
 +    gridRect: LayoutRect,
 +    cartesians: Cartesian2D[],
-     axisModel: CartesianAxisModel
++    axisModel: CartesianAxisModel,
++    api: ExtensionAPI
 +): Group {
 +    const layoutResult = layout(gridRect, axisModel);
 +
-     const axisBuilder = new AxisBuilder(axisModel, zrUtil.extend({
++    const axisBuilder = new AxisBuilder(axisModel, api, zrUtil.extend({
 +        handleAutoShown(elementType) {
 +            for (let i = 0; i < cartesians.length; i++) {
 +                if 
(isIntervalOrLogScale(cartesians[i].getOtherAxis(axisModel.axis).scale)) {
 +                    // Still show axis tick or axisLine if other axis is 
value / log
 +                    return true;
 +                }
 +            }
 +            // Not show axisTick or axisLine if other axis is category / time
 +            return false;
 +        }
 +    } as AxisBuilderCfg, layoutResult));
 +
 +    axisBuilder.build(axisBuilderAxisPartMap);
 +
 +    return axisBuilder.group;
 +}
diff --cc src/scale/Scale.ts
index 3d5671ed6,089791b0f..1f1fbdecf
--- a/src/scale/Scale.ts
+++ b/src/scale/Scale.ts
@@@ -37,9 -58,18 +58,19 @@@ abstract class Scale<SETTING extends Sc
  
      private _setting: SETTING;
  
+     // [CAVEAT]: Should update only by `_innerSetExtent`!
 +    // Make sure that extent[0] always <= extent[1].
      protected _extent: [number, number];
  
+     // FIXME: Effectively, both logorithmic scale and break scale are numeric 
axis transformation
+     //  mechanisms. However, for historical reason, logorithmic scale is 
implemented as a subclass,
+     //  while break scale is implemented inside the base class `Scale`. If 
more transformations
+     //  need to be introduced in futher, we should probably refactor them for 
better orthogonal
+     //  composition. (e.g. use decorator-like patterns rather than the 
current class inheritance?)
+     protected _brkCtx: ScaleBreakContext | NullUndefined;
+ 
+     protected _calculator: ScaleCalculator = new ScaleCalculator();
+ 
      private _isBlank: boolean;
  
      // Inject


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


Reply via email to