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 8fbe6bd5bb7a434c97da7c0edd590b2aebe8f53d Merge: 983e4ccbc 87b2f7763 Author: 100pah <[email protected]> AuthorDate: Tue Jun 24 11:26:26 2025 +0800 Merge branch 'next' into fix/contain-label-name # Conflicts: # src/component/axis/CartesianAxisView.ts # src/component/axis/axisBreakHelper.ts # src/coord/cartesian/Grid.ts # src/coord/cartesian/GridModel.ts # src/echarts.all.ts # src/export/features.ts # src/label/labelStyle.ts # src/util/graphic.ts # src/util/types.ts # test/build/mktest-tpl.html # test/lib/testHelper.js # test/tmp-base.html CONTRIBUTING.md | 2 +- build/source-release/prepareReleaseMaterials.js | 4 +- package-lock.json | 43 +- package.json | 8 +- src/action/roamHelper.ts | 92 - src/animation/customGraphicTransition.ts | 4 +- src/chart/bar/BarSeries.ts | 18 +- src/chart/bar/BarView.ts | 10 +- src/chart/bar/BaseBarSeries.ts | 9 +- src/chart/bar/PictorialBarSeries.ts | 8 +- src/chart/boxplot/BoxplotSeries.ts | 5 +- src/chart/custom/CustomSeries.ts | 27 +- src/chart/custom/CustomView.ts | 15 +- src/chart/funnel/FunnelSeries.ts | 8 +- src/chart/funnel/FunnelView.ts | 11 +- src/chart/funnel/funnelLayout.ts | 6 +- src/chart/gauge/GaugeSeries.ts | 21 +- src/chart/graph/GraphSeries.ts | 17 +- src/chart/graph/GraphView.ts | 273 +- src/chart/graph/Thumbnail.ts | 272 - src/chart/graph/createView.ts | 31 +- src/chart/graph/install.ts | 27 +- src/chart/heatmap/HeatmapLayer.ts | 3 +- src/chart/heatmap/HeatmapSeries.ts | 17 +- src/chart/heatmap/HeatmapView.ts | 44 +- src/chart/helper/LargeLineDraw.ts | 3 +- src/chart/helper/Line.ts | 7 +- src/chart/helper/LinePath.ts | 3 +- src/chart/helper/Symbol.ts | 11 +- src/chart/helper/createGraphFromNodeEdge.ts | 2 +- src/chart/helper/createSeriesData.ts | 10 + src/chart/line/LineSeries.ts | 5 +- src/chart/line/poly.ts | 3 +- src/chart/map/MapSeries.ts | 31 +- src/chart/pie/PieSeries.ts | 22 +- src/chart/pie/PieView.ts | 14 +- src/chart/pie/labelLayout.ts | 6 +- src/chart/pie/pieLayout.ts | 20 +- src/chart/sankey/SankeySeries.ts | 12 +- src/chart/sankey/SankeyView.ts | 29 +- src/chart/sankey/install.ts | 10 +- src/chart/sankey/sankeyLayout.ts | 5 +- src/chart/scatter/ScatterSeries.ts | 5 +- src/chart/scatter/install.ts | 8 +- src/chart/scatter/jitterLayout.ts | 50 + src/chart/sunburst/sunburstVisual.ts | 6 + src/chart/tree/TreeSeries.ts | 9 +- src/chart/tree/TreeView.ts | 20 +- src/chart/tree/treeAction.ts | 11 +- src/chart/tree/treeLayout.ts | 5 +- src/chart/treemap/Breadcrumb.ts | 35 +- src/chart/treemap/TreemapSeries.ts | 33 +- src/chart/treemap/TreemapView.ts | 49 +- src/chart/treemap/treemapLayout.ts | 21 +- src/component/axis/AngleAxisView.ts | 13 +- src/component/axis/CartesianAxisView.ts | 6 - src/component/axis/axisBreakHelper.ts | 2 +- src/component/axisPointer/AxisPointerModel.ts | 15 +- src/component/axisPointer/modelHelper.ts | 1 + src/component/brush/BrushModel.ts | 19 +- src/component/calendar/CalendarView.ts | 8 +- src/component/dataZoom/SliderZoomModel.ts | 46 +- src/component/dataZoom/SliderZoomView.ts | 96 +- src/component/dataZoom/roams.ts | 29 +- src/component/geo/install.ts | 24 +- src/component/helper/BrushController.ts | 5 +- src/component/helper/MapDraw.ts | 72 +- src/component/helper/RoamController.ts | 373 +- src/component/helper/cursorHelper.ts | 57 +- src/component/helper/interactionMutex.ts | 36 +- src/component/helper/listComponent.ts | 54 +- src/component/helper/roamHelper.ts | 159 +- src/component/helper/thumbnailBridge.ts | 91 + src/component/legend/LegendModel.ts | 31 +- src/component/legend/LegendView.ts | 48 +- src/component/legend/ScrollableLegendModel.ts | 7 +- src/component/marker/MarkAreaModel.ts | 1 + src/component/marker/MarkAreaView.ts | 15 +- src/component/marker/MarkLineModel.ts | 3 +- src/component/marker/MarkLineView.ts | 6 +- src/component/marker/MarkPointModel.ts | 1 + src/component/marker/MarkPointView.ts | 6 +- src/component/marker/markerHelper.ts | 8 +- src/component/{timeline.ts => matrix.ts} | 8 +- src/component/matrix/MatrixView.ts | 400 ++ .../features.ts => component/matrix/install.ts} | 14 +- src/{theme/light.ts => component/thumbnail.ts} | 16 +- src/component/thumbnail/ThumbnailBridgeImpl.ts | 93 + src/component/thumbnail/ThumbnailModel.ts | 161 + src/component/thumbnail/ThumbnailView.ts | 322 + .../{timeline.ts => thumbnail/install.ts} | 14 +- src/component/timeline.ts | 2 +- src/component/timeline/SliderTimelineModel.ts | 58 +- src/component/timeline/SliderTimelineView.ts | 5 +- src/component/timeline/TimelineModel.ts | 5 +- src/component/title/install.ts | 30 +- src/component/toolbox/ToolboxModel.ts | 11 +- src/component/toolbox/ToolboxView.ts | 27 +- src/component/toolbox/feature/DataView.ts | 15 +- src/component/toolbox/feature/DataZoom.ts | 3 +- src/component/toolbox/feature/SaveAsImage.ts | 5 +- src/component/tooltip/TooltipHTMLContent.ts | 60 +- src/component/tooltip/TooltipModel.ts | 22 +- src/component/tooltip/TooltipView.ts | 22 +- src/component/tooltip/tooltipMarkup.ts | 7 +- src/component/visualMap/ContinuousModel.ts | 9 +- src/component/visualMap/ContinuousView.ts | 6 +- src/component/visualMap/PiecewiseView.ts | 20 +- src/component/visualMap/VisualMapModel.ts | 58 +- src/component/visualMap/VisualMapView.ts | 3 +- src/coord/CoordinateSystem.ts | 70 +- src/coord/View.ts | 109 +- src/coord/axisAlignTicks.ts | 5 +- src/coord/axisCommonTypes.ts | 4 + src/coord/axisDefault.ts | 23 +- src/coord/calendar/Calendar.ts | 107 +- src/coord/calendar/CalendarModel.ts | 28 +- src/coord/calendar/prepareCustom.ts | 13 +- src/coord/cartesian/Cartesian2D.ts | 4 +- src/coord/cartesian/Grid.ts | 78 +- src/coord/cartesian/GridModel.ts | 17 +- src/coord/cartesian/cartesianAxisHelper.ts | 13 +- src/coord/cartesian/defaultAxisExtentFromData.ts | 13 +- src/coord/geo/Geo.ts | 25 +- src/coord/geo/GeoModel.ts | 34 +- src/coord/geo/geoCreator.ts | 52 +- src/coord/matrix/Matrix.ts | 631 ++ src/coord/matrix/MatrixBodyCorner.ts | 291 + src/coord/matrix/MatrixDim.ts | 453 ++ src/coord/matrix/MatrixModel.ts | 333 + src/coord/matrix/matrixCoordHelper.ts | 368 ++ src/coord/{calendar => matrix}/prepareCustom.ts | 31 +- src/coord/parallel/Parallel.ts | 9 +- src/coord/polar/Polar.ts | 31 +- src/coord/polar/polarCreator.ts | 11 +- src/coord/radar/Radar.ts | 11 +- src/coord/radar/RadarModel.ts | 8 +- src/coord/single/AxisModel.ts | 6 +- src/coord/single/Single.ts | 44 +- src/core/CoordinateSystem.ts | 317 +- src/core/ExtensionAPI.ts | 1 + src/core/echarts.ts | 210 +- src/data/Graph.ts | 49 +- src/data/OrdinalMeta.ts | 35 +- src/data/SeriesData.ts | 11 +- src/data/helper/dataProvider.ts | 59 +- src/echarts.all.ts | 15 +- src/export/components.ts | 6 + src/export/features.ts | 3 +- src/export/option.ts | 6 + src/i18n/langnb-NO.ts | 143 + src/label/labelStyle.ts | 61 +- src/layout/barGrid.ts | 5 +- src/loading/default.ts | 7 +- src/model/Component.ts | 21 +- src/model/Global.ts | 5 +- src/model/Series.ts | 3 +- src/model/globalDefault.ts | 21 +- src/model/referHelper.ts | 36 +- src/processor/dataStack.ts | 29 +- src/scale/Ordinal.ts | 2 +- src/theme/dark.ts | 226 +- src/util/graphic.ts | 137 +- src/util/jitter.ts | 185 + src/util/layout.ts | 345 +- src/util/model.ts | 87 +- src/util/number.ts | 46 +- src/util/styleCompat.ts | 7 +- src/util/symbol.ts | 3 +- src/util/types.ts | 152 +- src/visual/tokens.ts | 214 + src/visual/visualDefault.ts | 3 +- test/areaLineUpdate.html | 4 +- test/axis-dataset-null.html | 86 + test/axis-extrema.html | 10 +- test/axisLabel.html | 52 +- test/bar-markArea.html | 1 + test/bar-stack-reverse.html | 281 + test/build/mktest-tpl.html | 6 +- test/calendar-heatmap2.html | 81 + test/calendar-other-coord-sys.html | 882 +++ test/check-build.html | 5 +- test/circle-packing-with-d3.compat.html | 7 +- test/circle-packing-with-d3.html | 9 +- test/custom-feature.html | 181 +- test/custom-shape-morphing2.html | 6 +- test/custom-shape-morphing3.html | 1 - test/custom-transition-ecStat.html | 1 - test/dataZoom-action.html | 20 +- test/dataZoom-timeAxis.html | 224 + test/geo-map-roam.html | 556 ++ test/geo-svg-demo.html | 1 + test/graph-layout-roam.html | 515 ++ test/graph-thumbnail.html | 170 +- test/ie-svg-export.html | 9 +- test/ios13-z-bug.html | 6 +- test/legend-action.html | 66 + test/legend.html | 2 +- test/lib/config.js | 63 +- test/lib/draggable.js | 8 +- test/lib/testHelper.js | 58 +- test/lines-ny.html | 7 +- test/markPoint-stock.html | 1 + test/marker-z2.html | 400 ++ test/matrix.html | 602 ++ test/matrix2.html | 1803 ++++++ test/matrix3.html | 1319 ++++ test/matrix_application.html | 1381 +++++ test/matrix_application2.html | 801 +++ test/pie-coordinate-system.html | 16 +- test/pie-label.html | 57 +- test/polar-rounded.html | 92 + test/rich-inherit-plain-label.html | 283 + test/richText.html | 2 +- test/runTest/actions/__meta__.json | 10 + test/runTest/actions/axis-dataset-null.json | 1 + test/runTest/actions/axisLabel.json | 1 + test/runTest/actions/graph-thumbnail.json | 1 + test/runTest/actions/matrix2.json | 1 + test/runTest/actions/matrix3.json | 1 + test/runTest/actions/matrix_application.json | 1 + test/runTest/actions/matrix_application2.json | 1 + test/runTest/actions/rich-inherit-plain-label.json | 1 + .../actions/series-omitUnusedDimensions.json | 1 + .../runTest/actions/tooltip-displayTransition.json | 1 + test/runTest/cli.js | 13 +- test/runTest/client/client.css | 117 +- test/runTest/client/client.js | 445 +- test/runTest/client/index.html | 98 +- test/runTest/genReport.js | 265 +- test/runTest/marks/axis-break-2.json | 10 + test/runTest/marks/axis-break-3.json | 10 + test/runTest/marks/axis-break-4.json | 10 + test/runTest/marks/axis-break.json | 10 + test/runTest/marks/brush2.json | 10 + test/runTest/marks/dataSelect.json | 10 + test/runTest/marks/decal.json | 10 + test/runTest/marks/emphasis-disabled.json | 10 + test/runTest/marks/force-edge-ignoreLayout.json | 10 + test/runTest/marks/gauge.json | 10 + test/runTest/marks/graph-edge-highlight.json | 10 + test/runTest/marks/graph-grid.json | 10 + test/runTest/marks/graph-thumbnail.json | 10 + test/runTest/marks/graph.json | 10 + test/runTest/marks/hoverFocus.json | 10 + test/runTest/marks/loading.json | 10 + test/runTest/marks/sankey-labelValue.json | 10 + test/runTest/marks/sunburst-book.json | 10 + test/runTest/marks/sunburst-emphasis-focus.json | 10 + test/runTest/marks/sunburst-label-align.json | 10 + test/runTest/marks/sunburst-label-rotate.json | 10 + test/runTest/marks/sunburst-label.json | 10 + test/runTest/marks/sunburst-simple.json | 10 + test/runTest/marks/theme-list.json | 10 + test/runTest/marks/theme.json | 10 + test/runTest/marks/universalTransition3.json | 10 + test/runTest/package-lock.json | 128 +- test/runTest/server.js | 124 +- test/runTest/store.js | 196 +- test/runTest/util.js | 8 + test/sankey-emphasis.html | 6338 +++++++++++++++++++- test/sankey-roam.html | 220 +- test/scatter-jitter.html | 633 ++ test/series-omitUnusedDimensions.html | 221 + test/theme-list.html | 665 ++ test/tmp-base.html | 2 +- test/tooltip-displayTransition.html | 142 + test/touch-test.html | 6 +- test/tree-roam.html | 321 +- test/treemap-simple2.html | 103 + test/ut/spec/api/containPixel.test.ts | 6 +- test/ut/spec/api/converter.test.ts | 6 +- test/ut/spec/component/visualMap/setOption.test.ts | 3 +- test/ut/spec/series/custom.test.ts | 7 + test/violin.html | 208 + test/visualMap-pieces.html | 11 +- theme/rainbow.js | 63 + theme/v5.js | 581 ++ 278 files changed, 28420 insertions(+), 2185 deletions(-) diff --cc src/component/axis/CartesianAxisView.ts index d07dda388,b03269d25..e81ae7c14 --- a/src/component/axis/CartesianAxisView.ts +++ b/src/component/axis/CartesianAxisView.ts @@@ -19,22 -19,21 +19,16 @@@ import * as zrUtil from 'zrender/src/core/util'; import * as graphic from '../../util/graphic'; -import AxisBuilder, {AxisBuilderCfg} from './AxisBuilder'; import AxisView from './AxisView'; --import * as cartesianAxisHelper from '../../coord/cartesian/cartesianAxisHelper'; import {rectCoordAxisBuildSplitArea, rectCoordAxisHandleRemove} from './axisSplitHelper'; import GlobalModel from '../../model/Global'; import ExtensionAPI from '../../core/ExtensionAPI'; 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'; +import { shouldAxisShow } from '../../coord/axisHelper'; - const axisBuilderAttrs = { - axisLine: true, - axisTickLabel: true, - axisName: true, - } as const; -const axisBuilderAttrs = [ - 'axisLine', 'axisTickLabel', 'axisName' -] as const; const selfBuilderAttrs = [ 'splitArea', 'splitLine', 'minorSplitLine', 'breakArea' ] as const; diff --cc src/component/axis/axisBreakHelper.ts index 0907a744e,cbf0aba4a..ed40c098f --- a/src/component/axis/axisBreakHelper.ts +++ b/src/component/axis/axisBreakHelper.ts @@@ -27,10 -27,9 +27,10 @@@ import type { PathProps } from 'zrender import type SingleAxisView from './SingleAxisView'; import type { AxisBuilderCfg } from './AxisBuilder'; import type { BaseAxisBreakPayload } from './axisAction'; - import type { ComponentModel } from '../../echarts.all'; import type { AxisBaseOption } from '../../coord/axisCommonTypes'; import type { AxisBreakOptionIdentifierInAxis, NullUndefined } from '../../util/types'; +import { LabelLayoutInfoComputed } from '../../label/labelLayoutHelper'; + import type ComponentModel from '../../model/Component'; /** * @file The fasade of axis break view and mode. diff --cc src/coord/cartesian/Grid.ts index 321b7a26e,6583bb3ca..49d708a63 --- a/src/coord/cartesian/Grid.ts +++ b/src/coord/cartesian/Grid.ts @@@ -23,8 -23,8 +23,8 @@@ * TODO Default cartesian */ -import {isObject, each, indexOf, retrieve3, keys, map} from 'zrender/src/core/util'; +import {isObject, each, indexOf, retrieve3, keys, assert} from 'zrender/src/core/util'; - import {getLayoutRect, LayoutRect} from '../../util/layout'; + import {createBoxLayoutReference, getLayoutRect, LayoutRect} from '../../util/layout'; import { createScaleByModel, ifAxisCrossZero, @@@ -44,29 -47,18 +44,30 @@@ import GlobalModel from '../../model/Gl import ExtensionAPI from '../../core/ExtensionAPI'; import { Dictionary } from 'zrender/src/core/types'; import {CoordinateSystemMaster} from '../CoordinateSystem'; -import { ScaleDataValue } from '../../util/types'; +import { NullUndefined, ScaleDataValue } from '../../util/types'; import SeriesData from '../../data/SeriesData'; import OrdinalScale from '../../scale/Ordinal'; -import { findAxisModels, isCartesian2DInjectedAsDataCoordSys } from './cartesianAxisHelper'; +import { - isCartesian2DSeries, + findAxisModels, + createCartesianAxisViewCommonPartBuilder, - updateCartesianAxisViewCommonPartBuilder ++ updateCartesianAxisViewCommonPartBuilder, ++ isCartesian2DInjectedAsDataCoordSys +} from './cartesianAxisHelper'; import { CategoryAxisBaseOption, NumericAxisBaseOptionCommon } from '../axisCommonTypes'; import { AxisBaseModel } from '../AxisBaseModel'; import { isIntervalOrLogScale } from '../../scale/helper'; import { alignScaleTicks } from '../axisAlignTicks'; import IntervalScale from '../../scale/Interval'; import LogScale from '../../scale/Log'; +import { BoundingRect, expandOrShrinkRect, WH, XY } from '../../util/graphic'; +import { + AxisBuilderSharedContext, + resolveAxisNameOverlapDefault, + moveIfOverlapByLinearLabels, +} from '../../component/axis/AxisBuilder'; +import { error, log } from '../../util/log'; +import { AxisTickLabelComputingKind } from '../axisTickLabelBuilder'; + import { injectCoordSysByOption } from '../../core/CoordinateSystem'; -import { BoundingRect } from 'zrender'; type Cartesian2DDimensionName = 'x' | 'y'; @@@ -181,81 -171,60 +182,75 @@@ class Grid implements CoordinateSystemM } /** - * Resize the grid + * Resize the grid. + * + * [NOTE] + * If both "grid.containLabel/grid.contain" and pixel-required-data-processing (such as, "dataSampling") + * exist, circular dependency occurs in logic. + * The final compromised sequence is: + * 1. Calculate "axis.extent" (pixel extent) and AffineTransform based on only "grid layout options". + * Not accurate if "grid.containLabel/grid.contain" is required, but it is a compromise to avoid + * circular dependency. + * 2. Perform "series data processing" (where "dataSampling" requires "axis.extent"). + * 3. Calculate "scale.extent" (data extent) based on "processed series data". + * 4. Modify "axis.extent" for "grid.containLabel/grid.contain": + * 4.1. Calculate "axis labels" based on "scale.extent". + * 4.2. Modify "axis.extent" by the bounding rects of "axis labels and names". */ - resize(gridModel: GridModel, api: ExtensionAPI, ignoreContainLabel?: boolean): void { - - const isContainLabel = !ignoreContainLabel && gridModel.get('containLabel'); + resize(gridModel: GridModel, api: ExtensionAPI, beforeDataProcessing?: boolean): void { - const boxLayoutParams = gridModel.getBoxLayoutParams(); - - const gridRect = getLayoutRect( - boxLayoutParams, - { - width: api.getWidth(), - height: api.getHeight() - }); - - this._rect = gridRect; + const layoutRef = createBoxLayoutReference(gridModel, api); + const gridRect = this._rect = getLayoutRect(gridModel.getBoxLayoutParams(), layoutRef.refContainer); + // PENDING: whether to support that if the input `coord` is out of the base coord sys, + // do not render anything. At present, the behavior is undefined. - 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 axesMap = this._axesMap; + const coordsList = this._coordsList; - 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']; + const optionContainLabel = gridModel.get('containLabel'); // No `.get(, true)` for backward compat. - each(cartesianAxisPositions, (position) => { - maxLabelSpace[position] = Math.max(...map(reservedSpacePerAxis, ({ labels }) => labels[position])); - maxNameAndNameGapSpace[position] = - Math.max(...map(reservedSpacePerAxis, ({ name, nameGap }) => name[position] + nameGap[position])); - }); + updateAllAxisExtentTransByGridRect(axesMap, gridRect); - axesList.forEach((axis, axisIndex) => { - axis.model.axisToNameGapStartGap = - maxLabelSpace[reservedSpacePerAxis[axisIndex].namePositionCurrAxis]; - }); + if (!beforeDataProcessing) { + createAxisBiulders(gridRect, coordsList, axesMap, api); - 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; + let noPxChange: boolean; + if (optionContainLabel) { + if (legacyLayOutGridByContainLabel) { + // console.time('legacyLayOutGridByContainLabel'); + legacyLayOutGridByContainLabel(this._axesList, gridRect); + updateAllAxisExtentTransByGridRect(axesMap, gridRect); + // console.timeEnd('legacyLayOutGridByContainLabel'); + } + else { + if (__DEV__) { + log('Specified `grid.containLabel` but no `use(LegacyGridContainLabel)`;' + + 'use `grid.outerBounds` instead.', + true + ); + } + noPxChange = layOutGridByOuterBounds(gridRect.clone(), 'axisLabel', gridRect, axesMap); + } + } + else { + const {outerBoundsRect, parsedOuterBoundsContain} = prepareOuterBounds(gridModel, gridRect, api); + if (outerBoundsRect) { + // console.time('layOutGridByOuterBounds'); + noPxChange = layOutGridByOuterBounds(outerBoundsRect, parsedOuterBoundsContain, gridRect, axesMap); + // console.timeEnd('layOutGridByOuterBounds'); + } + } - adjustAxes(); - } + // console.time('buildAxesView_determine'); + createOrUpdateAxesView( + gridRect, + axesMap, + AxisTickLabelComputingKind.determine, + null, + noPxChange + ); + // console.timeEnd('buildAxesView_determine'); + } // End of beforeDataProcessing each(this._coordsList, function (coord) { // Calculate affine matrix to accelerate the data to point transform. diff --cc src/coord/cartesian/GridModel.ts index 541ffe29d,e7cad526e..8bea3f9b5 --- a/src/coord/cartesian/GridModel.ts +++ b/src/coord/cartesian/GridModel.ts @@@ -19,15 -19,16 +19,18 @@@ import ComponentModel from '../../model/Component'; -import { - ComponentOption, BoxLayoutOptionMixin, ZRColor, ShadowOptionMixin, -} from '../../util/types'; +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'; + import tokens from '../../visual/tokens'; +export const OUTER_BOUNDS_DEFAULT = {left: 5, right: 5, top: 5, bottom: 5}; + - export interface GridOption extends ComponentOption, BoxLayoutOptionMixin, ShadowOptionMixin { + export interface GridOption + extends ComponentOption, BoxLayoutOptionMixin, ShadowOptionMixin { + mainType?: 'grid'; show?: boolean; @@@ -107,21 -57,17 +110,21 @@@ class GridModel extends ComponentModel< show: false, // zlevel: 0, z: 0, - left: '10%', - top: 60, + left: '15%', + top: 65, right: '10%', - bottom: 70, - + bottom: 80, + // If grid size contain label containLabel: false, + outerBoundsMode: 'auto', + outerBounds: OUTER_BOUNDS_DEFAULT, + outerBoundsContain: 'all', + // width: {totalWidth} - left - right, // height: {totalHeight} - top - bottom, - backgroundColor: 'rgba(0,0,0,0)', + backgroundColor: tokens.color.transparent, borderWidth: 1, - borderColor: '#ccc' + borderColor: tokens.color.neutral30 }; } diff --cc src/echarts.all.ts index 3781ac685,13aca093e..58509b44f --- a/src/echarts.all.ts +++ b/src/echarts.all.ts @@@ -92,7 -94,7 +94,8 @@@ import UniversalTransition, LabelLayout, AxisBreak, + LegacyGridContainLabel, + ScatterJitter } from './export/features'; @@@ -358,4 -369,5 +370,5 @@@ use(UniversalTransition) use(LabelLayout); use(AxisBreak); - +use(LegacyGridContainLabel); + use(ScatterJitter); diff --cc src/export/features.ts index 736d48d91,d5087cd7c..7e2f09852 --- a/src/export/features.ts +++ b/src/export/features.ts @@@ -22,4 -22,5 +22,5 @@@ export {installUniversalTransition as UniversalTransition} from '../animation/universalTransition'; export {installLabelLayout as LabelLayout} from '../label/installLabelLayout'; export {installAxisBreak as AxisBreak} from '../component/axis/installBreak'; - export {installLegacyGridContainLabel as LegacyGridContainLabel} from '../coord/cartesian/legacyContainLabel'; ++export {installLegacyGridContainLabel as LegacyGridContainLabel} from '../coord/cartesian/legacyContainLabel'; + export {installScatterJitter as ScatterJitter} from '../chart/scatter/install'; - diff --cc src/label/labelStyle.ts index 7a93e9144,4bcbc14b0..644c93d34 --- a/src/label/labelStyle.ts +++ b/src/label/labelStyle.ts @@@ -31,15 -31,10 +31,16 @@@ import ZRStyleProps, AnimationOptionMixin, InterpolatableValue, + GlobalTextStyleOption, + LabelCommonOption, + TextCommonOptionNuanceBase, + TextCommonOptionNuanceDefault, + LabelMarginType, - LabelExtendedTextStyle ++ LabelExtendedTextStyle, + NullUndefined } from '../util/types'; import GlobalModel from '../model/Global'; - import { isFunction, retrieve2, extend, keys, trim, clone } from 'zrender/src/core/util'; -import { isFunction, retrieve2, extend, keys, trim, retrieve3 } from 'zrender/src/core/util'; ++import { isFunction, retrieve2, extend, keys, trim, clone, retrieve3 } from 'zrender/src/core/util'; import { SPECIAL_STATES, DISPLAY_STATES } from '../util/states'; import { deprecateReplaceLog } from '../util/log'; import { makeInner, interpolateRawValues } from '../util/model'; @@@ -71,7 -66,8 +72,9 @@@ type TextCommonParams = textStyle?: ZRStyleProps + defaultTextMargin?: number | number[] + autoOverflowArea?: ElementTextConfig['autoOverflowArea'], + layoutRect?: ElementTextConfig['layoutRect'], }; const EMPTY_OBJ = {}; @@@ -412,17 -409,25 +425,25 @@@ function setTextStyleCommon let richResult: TextStyleProps['rich']; if (richItemNames) { richResult = {}; + const richInheritPlainLabelOptionName = 'richInheritPlainLabel' as const; - const richInheritPlainLabel = retrieve2( - textStyleModel.get(richInheritPlainLabelOptionName), ++ const richInheritPlainLabel: boolean = retrieve2( ++ textStyleModel.get(richInheritPlainLabelOptionName as any), + ecModel ? ecModel.get(richInheritPlainLabelOptionName) : undefined + ); for (const name in richItemNames) { if (richItemNames.hasOwnProperty(name)) { // Cascade is supported in rich. -- const richTextStyle = textStyleModel.getModel(['rich', name]); ++ const richTextStyle = textStyleModel.getModel(['rich', name]) as LabelCommonModel<TNuance>; // In rich, never `disableBox`. - // FIXME: consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`, + // consider `label: {formatter: '{a|xx}', color: 'blue', rich: {a: {}}}`, // the default color `'blue'` will not be adopted if no color declared in `rich`. // That might confuses users. So probably we should put `textStyleModel` as the // root ancestor of the `richTextStyle`. But that would be a break change. - setTokenTextStyle( - richResult[name] = {}, richTextStyle, globalTextStyle, opt, isNotNormal, isAttached, false, true + // Since v6, the rich style inherits plain label by default + // but this behavior can be disabled by setting `richInheritPlainLabel` to `false`. - setTokenTextStyle( ++ setTokenTextStyle<TNuance>( + richResult[name] = {}, richTextStyle, globalTextStyle, textStyleModel, richInheritPlainLabel, + opt, isNotNormal, isAttached, false, true ); } } @@@ -434,21 -439,17 +455,27 @@@ if (overflow) { textStyle.overflow = overflow; } + const minMargin = textStyleModel.get('minMargin'); + if (minMargin != null) { + textStyle.margin = minMargin; + (textStyle as LabelExtendedTextStyle).__marginType = LabelMarginType.minMargin; + } + const textMargin = textStyleModel.get('textMargin'); + if (textMargin != null) { + textStyle.margin = clone(textMargin); + (textStyle as LabelExtendedTextStyle).__marginType = LabelMarginType.textMargin; + } + else if (opt.defaultTextMargin != null) { + textStyle.margin = clone(opt.defaultTextMargin); + (textStyle as LabelExtendedTextStyle).__marginType = LabelMarginType.textMargin; + } - setTokenTextStyle<TNuance>(textStyle, textStyleModel, globalTextStyle, opt, isNotNormal, isAttached, true, false); + const lineOverflow = textStyleModel.get('lineOverflow'); + if (lineOverflow) { + textStyle.lineOverflow = lineOverflow; + } - const margin = textStyleModel.get('minMargin'); - if (margin != null) { - textStyle.margin = margin; - } - setTokenTextStyle( ++ setTokenTextStyle<TNuance>( + textStyle, textStyleModel, globalTextStyle, null, null, opt, isNotNormal, isAttached, true, false + ); } // Consider case: // { @@@ -497,11 -496,12 +524,13 @@@ const TEXT_PROPS_BOX = 'shadowColor', 'shadowBlur', 'shadowOffsetX', 'shadowOffsetY' ] as const; -function setTokenTextStyle( +function setTokenTextStyle<TNuance extends TextCommonOptionNuanceBase>( textStyle: TextStyleProps['rich'][string], - textStyleModel: Model<LabelOption>, - globalTextStyle: LabelOption, - plainTextModel: Model<LabelOption> | NullUndefined, + // FIXME: check/refactor for ellipsis handling of rich text. + textStyleModel: Model<TextCommonOption<TNuance> & Pick<LabelCommonOption, 'ellipsis'>>, + globalTextStyle: GlobalTextStyleOption, ++ plainTextModel: LabelCommonModel<TNuance>, + richInheritPlainLabel: boolean, opt?: Pick<TextCommonParams, 'inheritColor' | 'defaultOpacity' | 'disableBox'>, isNotNormal?: boolean, isAttached?: boolean, diff --cc src/util/graphic.ts index e84848c0f,a2838adac..a965b1200 --- a/src/util/graphic.ts +++ b/src/util/graphic.ts @@@ -722,17 -724,129 +724,140 @@@ export function traverseElements(els: E } } +/** + * After a boundingRect applying a `transform`, whether to be still parallel screen X and Y. + */ +export function isBoundingRectAxisAligned(transform: matrix.MatrixArray | NullUndefined): boolean { + return !transform + || (Math.abs(transform[1]) < AXIS_ALIGN_EPSILON && Math.abs(transform[2]) < AXIS_ALIGN_EPSILON) + || (Math.abs(transform[0]) < AXIS_ALIGN_EPSILON && Math.abs(transform[3]) < AXIS_ALIGN_EPSILON); +} +const AXIS_ALIGN_EPSILON = 1e-5; + + + export function retrieveZInfo( + model: Model<Partial<Pick<ComponentOption, 'z' | 'zlevel'>>>, + ): { + z: ComponentOption['z'] + zlevel: ComponentOption['zlevel'] + } { + return { + z: model.get('z') || 0, + zlevel: model.get('zlevel') || 0, + }; + } + + /** + * Assume all of the elements has the same `z` and `zlevel`. + */ + export function calcZ2Range(el: Element): { + min: number + max: number + } { + let max = -Infinity; + let min = Infinity; + traverseElement(el, el => { + visitEl(el); + visitEl(el.getTextContent()); + visitEl(el.getTextGuideLine()); + }); + function visitEl(el: Element): void { + if (!el || el.isGroup) { + return; + } + const currentStates = el.currentStates; + if (currentStates.length) { + for (let idx = 0; idx < currentStates.length; idx++) { + calcZ2(el.states[currentStates[idx]] as Displayable); + } + } + calcZ2(el as Displayable); + } + function calcZ2(entity: Pick<Displayable, 'z2'>): void { + if (entity) { + const z2 = entity.z2; + // Consider z2 may be NullUndefined + if (z2 > max) { + max = z2; + } + if (z2 < min) { + min = z2; + } + } + } + if (min > max) { + min = max = 0; + } + return {min, max}; + } + + export function traverseUpdateZ( + el: Element, + z: number, + zlevel: number, + ): void { + doUpdateZ(el, z, zlevel); + } + + function doUpdateZ( + el: Element, + z: number, + zlevel: number + ): number { + let maxZ2 = -Infinity; + + // `ignoreModelZ` is used to intentionally lift elements to cover other elements, + // where maxZ2 (for label.z2) should also not be counted for its parents. + if ((el as ExtendedElement).ignoreModelZ) { + return maxZ2; + } + + // Group may also have textContent + const label = el.getTextContent(); + const labelLine = el.getTextGuideLine(); + const isGroup = el.isGroup; + + if (isGroup) { + // set z & zlevel of children elements of Group + const children = (el as Group).childrenRef(); + for (let i = 0; i < children.length; i++) { + maxZ2 = Math.max( + doUpdateZ( + children[i], + z, + zlevel, + ), + maxZ2 + ); + } + } + else { + // not Group + (el as Displayable).z = z; + (el as Displayable).zlevel = zlevel; + + maxZ2 = Math.max((el as Displayable).z2 || 0, maxZ2); + } + + // always set z and zlevel if label/labelLine exists + if (label) { + label.z = z; + label.zlevel = zlevel; + // lift z2 of text content + // TODO if el.emphasis.z2 is spcefied, what about textContent. + isFinite(maxZ2) && (label.z2 = maxZ2 + 2); + } + if (labelLine) { + const textGuideLineConfig = el.textGuideLineConfig; + labelLine.z = z; + labelLine.zlevel = zlevel; + isFinite(maxZ2) + && (labelLine.z2 = maxZ2 + (textGuideLineConfig && textGuideLineConfig.showAbove ? 1 : -1)); + } + return maxZ2; + } + + // Register built-in shapes. These shapes might be overwritten // by users, although we do not recommend that. registerShape('circle', Circle); diff --cc src/util/types.ts index f84f73169,83d5c5543..22926f7d7 --- a/src/util/types.ts +++ b/src/util/types.ts @@@ -1161,12 -1237,8 +1242,12 @@@ export interface TextCommonOption borderDashOffset?: number borderRadius?: number | number[] padding?: number | number[] + /** + * Currently margin related options are not declared here. They are not supported in rich text. + * @see {LabelCommonOption} + */ - width?: number | string// Percent + width?: number | string // Percent height?: number textBorderColor?: string textBorderWidth?: number @@@ -1199,7 -1260,9 +1280,9 @@@ export interface LabelFormatterCallback * LabelOption is an option set to control the style of labels. * Include color, background, shadow, truncate, rotation, distance, etc.. */ - export interface LabelOption extends LabelCommonOption { + export interface LabelOption< + TNuance extends {positionExtra: unknown} = {positionExtra: never} -> extends TextCommonOption { ++> extends LabelCommonOption { /** * If show label */ @@@ -1230,32 -1277,22 +1313,33 @@@ export interface LabelCommonOption /** * Min margin between labels. Used when label has layout. * PENDING: @see {LabelMarginType} + * It's `minMargin` instead of `margin` is for not breaking the previous code using `margin`. + * See the summary in `textMargin`. */ - // It's minMargin instead of margin is for not breaking the previous code using margin. minMargin?: number + /** + * The space around the label to escape from overlapping. + * Applied on the label local rect (rather than rotated enlarged rect) + * Follow the format defined by `format.ts#normalizeCssArray`. + * + * Introduce the name `textMargin` rather than reuse the existing names to avoid breaking change: + * - `margin` historically have been used to indicate the distance from `label.x/.y` to something: + * - `axisLabel.margin` & `axisPointer.label.margin`: to the axis line. + * - `calendar.dayLabel/monthLabel/yearLabel.margin`: + * - `series-pie.label.margin`: to pie body (deprecated, replaced by `edgeDistance`) + * - `series-themeRiver.label.margin`: to the shape edge + * - `minMargin` conveys the same meaning as this `textMargin` but has a different nuance, + * it works like CSS margin collapse (gap = label1.minMargin/2 + label2.minMargin/2), + * and `minMargin` applied on the global bounding rect (parallel to screen x and y) rather + * than the original local bounding rect (can be rotated, smaller and more presice). + * PENDING: @see {LabelMarginType} + */ + textMargin?: number | number[] overflow?: TextStyleProps['overflow'] - ellipsis?: TextStyleProps['ellipsis'] + lineOverflow?: TextStyleProps['lineOverflow'] - - silent?: boolean - precision?: number | 'auto' - valueAnimation?: boolean - - // TODO: TYPE not all label support formatter - // formatter?: string | ((params: CallbackDataParams) => string) - - rich?: Dictionary<TextCommonOption> + ellipsis?: TextStyleProps['ellipsis'] + rich?: RichTextOption } /** diff --cc test/runTest/actions/__meta__.json index e2ee77534,e7349c87d..0f02ffeb9 --- a/test/runTest/actions/__meta__.json +++ b/test/runTest/actions/__meta__.json @@@ -180,8 -188,9 +188,9 @@@ "sankey-jump": 1, "sankey-level": 1, "scatter-random-stream-fix-axis": 1, - "scatter-single-axis": 2, + "scatter-single-axis": 3, "scatterMatrix": 3, + "series-omitUnusedDimensions": 1, "setOption": 2, "splitLine": 1, "stackBar-dataZoom": 7, --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
