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]
