/
githubmirror
/
echarts
Обзор
Документация
Войти
/
githubmirror
/
echarts
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/scale/breakImpl.ts
785 строк
29 KB
100pah
lint: fix linter issues.
07 май 2026, 14:44
07 май 2026, 14:44
bc96cc7
Код
Авторство
О чём код?
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import { assert, clone, each, filter, find, isString, map, trim } from 'zrender/src/core/util'; import { NullUndefined, ParsedAxisBreak, ParsedAxisBreakList, AxisBreakOption, AxisBreakOptionIdentifierInAxis, ScaleTick, VisualAxisBreak, } from '../util/types'; import { error } from '../util/log'; import type Scale from './Scale'; import { AxisBreakParsingResult, registerScaleBreakHelperImpl, ParamPruneByBreak, BreakScaleMapper, ParseBreakOptionOpt } from './break'; import { mathMax, mathMin, mathRound } from '../util/number'; import { AxisLabelFormatterExtraParams } from '../coord/axisCommonTypes'; import { DecoratedScaleMapperMethods, decorateScaleMapper, enableScaleMapperFreeze, initLinearScaleMapper, SCALE_EXTENT_KIND_EFFECTIVE, SCALE_MAPPER_DEPTH_OUT_OF_BREAK, ScaleMapper, ScaleMapperTransformOutOpt } from './scaleMapper'; import { isValidBoundsForExtent } from '../util/model'; import { ValueTransformLookupOpt } from './helper'; /** * @caution * Must not export anything except `installScaleBreakHelper` */ interface BreakScaleMapperImpl extends BreakScaleMapper { // Linear space, the elapsed result. _linear: ScaleMapper; } class BreakScaleMapperImpl { // [CAVEAT]: Should set only by the constructor! readonly breaks: ParsedAxisBreakList; // Only used to save the original extent to avoid rounding error. private _outOfBrk: ScaleMapper; constructor( breakParsed: AxisBreakParsingResult | NullUndefined, initialExtent: number[] | NullUndefined ) { decorateScaleMapper(this, BreakScaleMapperImpl.decoratedMethods); this._outOfBrk = initLinearScaleMapper(null, initialExtent); const mapper = this._linear = initLinearScaleMapper(null, initialExtent); enableScaleMapperFreeze(this, mapper); this.breaks = breakParsed && breakParsed.breaks || []; } hasBreaks(): boolean { return !!this.breaks.length; } /** * When iteratively generating ticks by nice interval, currently the `interval`, which is * calculated by break-elapsed extent span, is probably very small comparing to the original * extent, leading to a large number of iteration and tick generation, even over `safeLimit`. * Thus stepping over breaks is necessary in that loop. * * "Nice" should be ensured on ticks when step over the breaks. Thus this method returns * a integer multiple of the "nice tick interval". * * This method does little work; it is just for unifying and restricting the behavior. */ calcNiceTickMultiple( tickVal: number, estimateNiceMultiple: (tickVal: number, brkEnd: number) => number ): number { for (let idx = 0; idx < this.breaks.length; idx++) { const brk = this.breaks[idx]; if (brk.vmin < tickVal && tickVal < brk.vmax) { const multiple = estimateNiceMultiple(tickVal, brk.vmax); if (__DEV__) { // If not, it may cause dead loop or not nice tick. assert(multiple >= 0 && mathRound(multiple) === multiple); } return multiple; } } return 0; } static decoratedMethods: DecoratedScaleMapperMethods<BreakScaleMapperImpl> = { needTransform() { return !this.breaks.length; }, getExtent() { return this._outOfBrk.getExtent(); }, getExtentUnsafe(kind, depth) { return (depth == null || depth === SCALE_MAPPER_DEPTH_OUT_OF_BREAK ) ? this._outOfBrk.getExtentUnsafe(kind, null) : this._linear.getExtentUnsafe(kind, null); }, setExtent(start, end) { this.setExtent2(SCALE_EXTENT_KIND_EFFECTIVE, start, end); }, setExtent2(kind, start, end) { if (isValidBoundsForExtent(start, end)) { if (kind === SCALE_EXTENT_KIND_EFFECTIVE) { updateAxisBreakGapReal(this, [start, end]); } this._outOfBrk.setExtent2(kind, start, end); this._linear.setExtent2( kind, this.transformIn(start, null), this.transformIn(end, null) ); } }, normalize(val) { return this._linear.normalize(this.transformIn(val, null)); }, scale(val) { return this.transformOut(this._linear.scale(val), null); }, contain(val) { return this._outOfBrk.contain(val); }, /** * a.k.a., "elapse" * Suppose: * AXIS_BREAK_LAST_BREAK_END_BASE: 0 * AXIS_BREAK_ELAPSED_BASE: 0 * breaks: [ * {start: -400, end: -300, gap: 27}, * {start: -100, end: 100, gap: 10}, * {start: 200, end: 400, gap: 300}, * ] * The mapping will be: * | | * 400 + -> + 237 * | | | | (gap: 300) * 200 + -> + -63 * | | * 100 + -> + -163 * | | | | (gap: 10) * -100 + -> + -173 * | | * -300 + -> + -373 * | | | | (gap: 27) * -400 + -> + -400 * | | * origianl elapsed * * Note: * `transformIn` and `transformOut` has nothing to do with "scale extent" - out of extent is supported. */ transformIn(val, opt) { if (opt && opt.depth === SCALE_MAPPER_DEPTH_OUT_OF_BREAK) { return val; } // If the value is in the break, return the normalized value in the break let elapsedVal = AXIS_BREAK_ELAPSED_BASE; let lastBreakEnd = AXIS_BREAK_LAST_BREAK_END_BASE; let stillOver = true; for (let i = 0; i < this.breaks.length; i++) { const brk = this.breaks[i]; if (val <= brk.vmax) { if (val > brk.vmin) { elapsedVal += brk.vmin - lastBreakEnd + (val - brk.vmin) / (brk.vmax - brk.vmin) * brk.gapReal; } else { elapsedVal += val - lastBreakEnd; } lastBreakEnd = brk.vmax; stillOver = false; break; } elapsedVal += brk.vmin - lastBreakEnd + brk.gapReal; lastBreakEnd = brk.vmax; } if (stillOver) { elapsedVal += val - lastBreakEnd; } return elapsedVal; }, /** * @see transformIn * a.k.a., "unelapse" */ transformOut(elapsedVal, opt) { if (opt && opt.depth === SCALE_MAPPER_DEPTH_OUT_OF_BREAK) { return elapsedVal; } let lastElapsedEnd = AXIS_BREAK_ELAPSED_BASE; let lastBreakEnd = AXIS_BREAK_LAST_BREAK_END_BASE; let stillOver = true; let unelapsedVal = 0; for (let i = 0; i < this.breaks.length; i++) { const brk = this.breaks[i]; const elapsedStart = lastElapsedEnd + brk.vmin - lastBreakEnd; const elapsedEnd = elapsedStart + brk.gapReal; if (elapsedVal <= elapsedEnd) { if (elapsedVal > elapsedStart) { unelapsedVal = brk.vmin + (elapsedVal - elapsedStart) / (elapsedEnd - elapsedStart) * (brk.vmax - brk.vmin); } else { unelapsedVal = lastBreakEnd + elapsedVal - lastElapsedEnd; } lastBreakEnd = brk.vmax; stillOver = false; break; } lastElapsedEnd = elapsedEnd; lastBreakEnd = brk.vmax; } if (stillOver) { unelapsedVal = lastBreakEnd + elapsedVal - lastElapsedEnd; } return unelapsedVal; }, }; }; function createBreakScaleMapper( breakParsed: AxisBreakParsingResult | NullUndefined, initialExtent: number[] | NullUndefined ): BreakScaleMapper { return new BreakScaleMapperImpl(breakParsed, initialExtent); } // Both can start with any finite value, and are not necessarily equal. But they need to // be the same in `axisBreakElapse` and `axisBreakUnelapse` respectively. const AXIS_BREAK_ELAPSED_BASE = 0; const AXIS_BREAK_LAST_BREAK_END_BASE = 0; /** * `gapReal` in brkMapper.breaks will be calculated. */ function updateAxisBreakGapReal( brkMapper: BreakScaleMapper, scaleExtent: number[] ): void { // Considered the effect: // - Use dataZoom to move some of the breaks outside the extent. // - Some scenarios that `series.clip: false`. // // How to calculate `prctBrksGapRealSum`: // Based on the formula: // xxx.span = brk.vmax - brk.vmin // xxx.tpPrct.val / xxx.tpAbs.val means ParsedAxisBreak['gapParsed']['val'] // .S/.E means a break that is semi in scaleExtent[0] or scaleExtent[1] // valP = ( // + (fullyInExtBrksSum.tpAbs.gapReal - fullyInExtBrksSum.tpAbs.span) // + (semiInExtBrk.S.tpAbs.gapReal - semiInExtBrk.S.tpAbs.span) * semiInExtBrk.S.tpAbs.inExtFrac // + (semiInExtBrk.E.tpAbs.gapReal - semiInExtBrk.E.tpAbs.span) * semiInExtBrk.E.tpAbs.inExtFrac // ) // valQ = ( // - fullyInExtBrksSum.tpPrct.span // - semiInExtBrk.S.tpPrct.span * semiInExtBrk.S.tpPrct.inExtFrac // - semiInExtBrk.E.tpPrct.span * semiInExtBrk.E.tpPrct.inExtFrac // ) // gapPrctSum = sum(xxx.tpPrct.val) // gapPrctSum = prctBrksGapRealSum / ( // + (scaleExtent[1] - scaleExtent[0]) + valP + valQ // + fullyInExtBrksSum.tpPrct.gapReal // + semiInExtBrk.S.tpPrct.gapReal * semiInExtBrk.S.tpPrct.inExtFrac // + semiInExtBrk.E.tpPrct.gapReal * semiInExtBrk.E.tpPrct.inExtFrac // ) // Assume: // xxx.tpPrct.gapReal = xxx.tpPrct.val / gapPrctSum * prctBrksGapRealSum // (NOTE: This is not accurate when semi-in-extent break exist because its // proportion is not linear, but this assumption approximately works.) // Derived as follows: // prctBrksGapRealSum = gapPrctSum * ( (scaleExtent[1] - scaleExtent[0]) + valP + valQ ) // / (1 // - fullyInExtBrksSum.tpPrct.val // - semiInExtBrk.S.tpPrct.val * semiInExtBrk.S.tpPrct.inExtFrac // - semiInExtBrk.E.tpPrct.val * semiInExtBrk.E.tpPrct.inExtFrac // ) let gapPrctSum = 0; const fullyInExtBrksSum = { tpAbs: {span: 0, val: 0}, tpPrct: {span: 0, val: 0}, }; const init = () => ({has: false, span: NaN, inExtFrac: NaN, val: NaN}); const semiInExtBrk = { S: {tpAbs: init(), tpPrct: init()}, E: {tpAbs: init(), tpPrct: init()}, }; each(brkMapper.breaks, brk => { const gapParsed = brk.gapParsed; if (gapParsed.type === 'tpPrct') { gapPrctSum += gapParsed.val; } const clampedBrk = clampBreakByExtent(brk, scaleExtent); if (clampedBrk) { const vminClamped = clampedBrk.vmin !== brk.vmin; const vmaxClamped = clampedBrk.vmax !== brk.vmax; const clampedSpan = clampedBrk.vmax - clampedBrk.vmin; if (vminClamped && vmaxClamped) { // Do nothing, which simply makes the result `gapReal` cover the entire scaleExtent. // This transform is not consistent with the other cases but practically works. } else if (vminClamped || vmaxClamped) { const sOrE = vminClamped ? 'S' : 'E'; semiInExtBrk[sOrE][gapParsed.type].has = true; semiInExtBrk[sOrE][gapParsed.type].span = clampedSpan; semiInExtBrk[sOrE][gapParsed.type].inExtFrac = clampedSpan / (brk.vmax - brk.vmin); semiInExtBrk[sOrE][gapParsed.type].val = gapParsed.val; } else { fullyInExtBrksSum[gapParsed.type].span += clampedSpan; fullyInExtBrksSum[gapParsed.type].val += gapParsed.val; } } }); const prctBrksGapRealSum = gapPrctSum * (0 + (scaleExtent[1] - scaleExtent[0]) + (fullyInExtBrksSum.tpAbs.val - fullyInExtBrksSum.tpAbs.span) + (semiInExtBrk.S.tpAbs.has ? (semiInExtBrk.S.tpAbs.val - semiInExtBrk.S.tpAbs.span) * semiInExtBrk.S.tpAbs.inExtFrac : 0 ) + (semiInExtBrk.E.tpAbs.has ? (semiInExtBrk.E.tpAbs.val - semiInExtBrk.E.tpAbs.span) * semiInExtBrk.E.tpAbs.inExtFrac : 0 ) - fullyInExtBrksSum.tpPrct.span - (semiInExtBrk.S.tpPrct.has ? semiInExtBrk.S.tpPrct.span * semiInExtBrk.S.tpPrct.inExtFrac : 0) - (semiInExtBrk.E.tpPrct.has ? semiInExtBrk.E.tpPrct.span * semiInExtBrk.E.tpPrct.inExtFrac : 0) ) / (1 - fullyInExtBrksSum.tpPrct.val - (semiInExtBrk.S.tpPrct.has ? semiInExtBrk.S.tpPrct.val * semiInExtBrk.S.tpPrct.inExtFrac : 0) - (semiInExtBrk.E.tpPrct.has ? semiInExtBrk.E.tpPrct.val * semiInExtBrk.E.tpPrct.inExtFrac : 0) ); each(brkMapper.breaks, brk => { const gapParsed = brk.gapParsed; if (gapParsed.type === 'tpPrct') { brk.gapReal = gapPrctSum !== 0 // prctBrksGapRealSum is supposed to be non-negative but add a safe guard ? mathMax(prctBrksGapRealSum, 0) * gapParsed.val / gapPrctSum : 0; } if (gapParsed.type === 'tpAbs') { brk.gapReal = gapParsed.val; } if (brk.gapReal == null) { brk.gapReal = 0; } }); } function pruneTicksByBreak<TItem extends ScaleTick | number>( pruneByBreak: ParamPruneByBreak, ticks: TItem[], breaks: ParsedAxisBreakList, getValue: (item: TItem) => number, interval: number, scaleExtent: number[], ): void { if (pruneByBreak === 'no') { return; } each(breaks, brk => { // break.vmin/vmax that out of extent must not impact the visible of // normal ticks and labels. const clampedBrk = clampBreakByExtent(brk, scaleExtent); if (!clampedBrk) { return; } // Remove some normal ticks to avoid zigzag shapes overlapping with split lines // and to avoid break labels overlapping with normal tick labels (thouth it can // also be avoided by `axisLabel.hideOverlap`). // It's OK to O(n^2) since the number of `ticks` are small. for (let j = ticks.length - 1; j >= 0; j--) { const tick = ticks[j]; const val = getValue(tick); // 1. Ensure there is no ticks inside `break.vmin` and `break.vmax`. // 2. Use an empirically gap value here. Theoritically `zigzagAmplitude` is // supposed to be involved to provide better precision but it will brings // more complexity. The empirically gap value is conservative because break // labels and normal tick lables are prone to overlapping. const gap = interval * 3 / 4; if (val > clampedBrk.vmin - gap && val < clampedBrk.vmax + gap && ( pruneByBreak !== 'preserve_extent_bound' || ( val !== scaleExtent[0] && val !== scaleExtent[1] ) ) ) { ticks.splice(j, 1); } } }); } function addBreaksToTicks( // The input ticks should be in accending order. ticks: ScaleTick[], breaks: ParsedAxisBreakList, scaleExtent: number[], // Keep the break ends at the same level to avoid an awkward appearance. getTimeProps?: (clampedBrk: ParsedAxisBreak) => ScaleTick['time'], ): void { each(breaks, brk => { const clampedBrk = clampBreakByExtent(brk, scaleExtent); if (!clampedBrk) { return; } // - When neight `break.vmin` nor `break.vmax` is in scale extent, // break label should not be displayed and we do not add them to the result. // - When only one of `break.vmin` and `break.vmax` is inside the extent and the // other is outsite, we comply with the extent and display only part of the breaks area, // because the extent might be determined by user settings (such as `axis.min/max`) ticks.push({ value: clampedBrk.vmin, break: { type: 'vmin', parsedBreak: clampedBrk, }, time: getTimeProps ? getTimeProps(clampedBrk) : undefined, }); // When gap is 0, start tick overlap with end tick, but we still count both of them. Break // area shape can address that overlapping. `axisLabel` need draw both start and end separately, // otherwise it brings complexity to the logic of label overlapping resolving (e.g., when label // rotated), and introduces inconsistency to users in `axisLabel.formatter` between gap is 0 or not. ticks.push({ value: clampedBrk.vmax, break: { type: 'vmax', parsedBreak: clampedBrk, }, time: getTimeProps ? getTimeProps(clampedBrk) : undefined, }); }); if (breaks.length) { ticks.sort((a, b) => a.value - b.value); } } /** * If break and extent does not intersect, return null/undefined. * If the intersection is only a point at scaleExtent[0] or scaleExtent[1], return null/undefined. */ function clampBreakByExtent( brk: ParsedAxisBreak, scaleExtent: number[] ): NullUndefined | ParsedAxisBreak { const vmin = mathMax(brk.vmin, scaleExtent[0]); const vmax = mathMin(brk.vmax, scaleExtent[1]); return ( vmin < vmax || (vmin === vmax && vmin > scaleExtent[0] && vmin < scaleExtent[1]) ) ? { vmin, vmax, breakOption: brk.breakOption, gapParsed: brk.gapParsed, gapReal: brk.gapReal, } : null; } function parseAxisBreakOption( // raw user input breaks, retrieved from axis model. breakOptionList: AxisBreakOption[] | NullUndefined, scale: {parse: Scale['parse']}, opt?: ParseBreakOptionOpt ): AxisBreakParsingResult { const parsedBreaks: ParsedAxisBreakList = []; if (!breakOptionList) { return {breaks: parsedBreaks}; } function validatePercent(normalizedPercent: number, msg: string): boolean { if (normalizedPercent >= 0 && normalizedPercent < 1 - 1e-5) { // Avoid division error. return true; } if (__DEV__) { error(`${msg} must be >= 0 and < 1, rather than ${normalizedPercent} .`); } return false; } each(breakOptionList, brkOption => { if (!brkOption || brkOption.start == null || brkOption.end == null) { if (__DEV__) { error('The input axis breaks start/end should not be empty.'); } return; } if (brkOption.isExpanded) { return; } const parsedBrk: ParsedAxisBreak = { breakOption: clone(brkOption), vmin: scale.parse(brkOption.start), vmax: scale.parse(brkOption.end), gapParsed: {type: 'tpAbs', val: 0}, gapReal: null }; if (brkOption.gap != null) { let isPrct = false; if (isString(brkOption.gap)) { const trimmedGap = trim(brkOption.gap); if (trimmedGap.match(/%$/)) { let normalizedPercent = parseFloat(trimmedGap) / 100; if (!validatePercent(normalizedPercent, 'Percent gap')) { normalizedPercent = 0; } parsedBrk.gapParsed.type = 'tpPrct'; parsedBrk.gapParsed.val = normalizedPercent; isPrct = true; } } if (!isPrct) { let absolute = scale.parse(brkOption.gap); if (!isFinite(absolute) || absolute < 0) { if (__DEV__) { error(`Axis breaks gap must positive finite rather than (${brkOption.gap}).`); } absolute = 0; } parsedBrk.gapParsed.type = 'tpAbs'; parsedBrk.gapParsed.val = absolute; } } if (parsedBrk.vmin === parsedBrk.vmax) { parsedBrk.gapParsed.type = 'tpAbs'; parsedBrk.gapParsed.val = 0; } if (opt && opt.noNegative) { each(['vmin', 'vmax'] as const, se => { if (parsedBrk[se] < 0) { if (__DEV__) { error(`Axis break.${se} must not be negative.`); } parsedBrk[se] = 0; } }); } // Ascending numerical order is the prerequisite of the calculation in Scale#normalize. // User are allowed to input desending vmin/vmax for simplifying the usage. if (parsedBrk.vmin > parsedBrk.vmax) { const tmp = parsedBrk.vmax; parsedBrk.vmax = parsedBrk.vmin; parsedBrk.vmin = tmp; } parsedBreaks.push(parsedBrk); }); // Ascending numerical order is the prerequisite of the calculation in Scale#normalize. parsedBreaks.sort((item1, item2) => item1.vmin - item2.vmin); // Make sure that the intervals in breaks are not overlap. let lastEnd = -Infinity; each(parsedBreaks, (brk, idx) => { if (lastEnd > brk.vmin) { if (__DEV__) { error('Axis breaks must not overlap.'); } parsedBreaks[idx] = null; } lastEnd = brk.vmax; }); return { breaks: filter(parsedBreaks, brk => !!brk), }; } function identifyAxisBreak( brk: AxisBreakOption, identifier: AxisBreakOptionIdentifierInAxis ): boolean { return serializeAxisBreakIdentifier(identifier) === serializeAxisBreakIdentifier(brk); } function serializeAxisBreakIdentifier(identifier: AxisBreakOptionIdentifierInAxis): string { // We use user input start/end to identify break. Considered cases like `start: new Date(xxx)`, // Theoretically `Scale#parse` should be used here, but not used currently to reduce dependencies, // since simply converting to string happens to be correct. return identifier.start + '_\0_' + identifier.end; } /** * - A break pair represents `[vmin, vmax]`, * - Only both vmin and vmax item exist, they are counted as a pair. */ function retrieveAxisBreakPairs<TItem, TReturnIdx extends boolean>( itemList: TItem[], getVisualAxisBreak: (item: TItem) => VisualAxisBreak | NullUndefined, returnIdx: TReturnIdx ): ( TReturnIdx extends false ? TItem[][] : number[][] ) { const idxPairList: number[][] = []; each(itemList, (el, idx) => { const vBreak = getVisualAxisBreak(el); if (vBreak && vBreak.type === 'vmin') { idxPairList.push([idx]); } }); each(itemList, (el, idx) => { const vBreak = getVisualAxisBreak(el); if (vBreak && vBreak.type === 'vmax') { const idxPair = find( idxPairList, // parsedBreak may be changed, can only use breakOption to match them. pr => identifyAxisBreak( getVisualAxisBreak(itemList[pr[0]]).parsedBreak.breakOption, vBreak.parsedBreak.breakOption ) ); idxPair && idxPair.push(idx); } }); const result = [] as (TReturnIdx extends false ? TItem[][] : number[][]); each(idxPairList, idxPair => { if (idxPair.length === 2) { result.push(returnIdx ? (idxPair as any) : ([itemList[idxPair[0]] as any, itemList[idxPair[1]] as any]) ); } }); return result; } function getTicksBreakOutwardTransform( scale: ScaleMapper, tick: ScaleTick, outermostBreaks: ParsedAxisBreakList, lookup: ValueTransformLookupOpt['lookup'] ): { tickVal: number; vBreak: VisualAxisBreak | NullUndefined; // Return: If no break, return null/undefined. } | NullUndefined { if (!tick.break) { return; } const brk = tick.break.parsedBreak; const originalBrkItem = find(outermostBreaks, brk => identifyAxisBreak( brk.breakOption, tick.break.parsedBreak.breakOption )); // NOTE: `tick.break` may have been clamped by scale extent. const opt: ScaleMapperTransformOutOpt = {lookup, depth: SCALE_MAPPER_DEPTH_OUT_OF_BREAK}; const vmin = scale.transformOut(brk.vmin, opt); const vmax = scale.transformOut(brk.vmax, opt); const parsedBreak = { vmin, vmax, breakOption: brk.breakOption, // It is not changed by extent clamping. gapParsed: clone(originalBrkItem.gapParsed), gapReal: brk.gapReal, }; return { tickVal: parsedBreak[tick.break.type], vBreak: {type: tick.break.type, parsedBreak}, }; } function parseAxisBreakOptionInwardTransform( breakOptionList: AxisBreakOption[] | NullUndefined, scale: Scale, parseOpt: ParseBreakOptionOpt, lookupStartIdx: number, out: { lookup: ValueTransformLookupOpt['lookup']; original?: AxisBreakParsingResult; transformed?: AxisBreakParsingResult; } ): void { out.original = parseAxisBreakOption(breakOptionList, scale, parseOpt); const transformed = out.transformed = parseAxisBreakOption(breakOptionList, scale, parseOpt); const lookup = out.lookup; transformed.breaks = map(transformed.breaks, (brk, idx) => { const transOpt = {depth: SCALE_MAPPER_DEPTH_OUT_OF_BREAK} as const; const vmin = scale.transformIn(brk.vmin, transOpt); const vmax = scale.transformIn(brk.vmax, transOpt); const gapParsed = { type: brk.gapParsed.type, val: brk.gapParsed.type === 'tpAbs' ? scale.transformIn(brk.vmin + brk.gapParsed.val, transOpt) - vmin : brk.gapParsed.val, }; lookup.from[lookupStartIdx + idx] = vmin; lookup.to[lookupStartIdx + idx] = brk.vmin; lookup.from[lookupStartIdx + idx + 1] = vmax; lookup.to[lookupStartIdx + idx + 1] = brk.vmax; return { vmin, vmax, gapParsed, gapReal: brk.gapReal, breakOption: brk.breakOption }; }); } const BREAK_MIN_MAX_TO_PARAM = {vmin: 'start', vmax: 'end'} as const; function makeAxisLabelFormatterParamBreak( extraParam: AxisLabelFormatterExtraParams | NullUndefined, vBreak: VisualAxisBreak | NullUndefined ): AxisLabelFormatterExtraParams | NullUndefined { if (vBreak) { extraParam = extraParam || ({} as AxisLabelFormatterExtraParams); extraParam.break = { type: BREAK_MIN_MAX_TO_PARAM[vBreak.type], start: vBreak.parsedBreak.vmin, end: vBreak.parsedBreak.vmax, }; } return extraParam; } export function installScaleBreakHelper(): void { registerScaleBreakHelperImpl({ createBreakScaleMapper, pruneTicksByBreak, addBreaksToTicks, parseAxisBreakOption, identifyAxisBreak, serializeAxisBreakIdentifier, retrieveAxisBreakPairs, getTicksBreakOutwardTransform, parseAxisBreakOptionInwardTransform, makeAxisLabelFormatterParamBreak, }); }