/
githubmirror
/
echarts
Обзор
Документация
Войти
/
githubmirror
/
echarts
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
6.0.0
src/scale/Log.ts
225 строк
7 KB
100pah
fix(scale): Fix that extreme small numbers can not be displayed in Cartesian due to the inappropriate rounding precision. Close #16266
22 июл 2025, 12:03
22 июл 2025, 12:03
18b5db2
Код
Авторство
О чём код?
/* * 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 * as zrUtil from 'zrender/src/core/util'; import Scale, { ScaleGetTicksOpt } from './Scale'; import * as numberUtil from '../util/number'; // Use some method of IntervalScale import IntervalScale from './Interval'; import { DimensionLoose, DimensionName, ParsedAxisBreakList, AxisBreakOption, ScaleTick } from '../util/types'; import { getIntervalPrecision, logTransform } from './helper'; import SeriesData from '../data/SeriesData'; import { getScaleBreakHelper } from './break'; const fixRound = numberUtil.round; const mathFloor = Math.floor; const mathCeil = Math.ceil; const mathPow = Math.pow; const mathLog = Math.log; class LogScale extends IntervalScale { static type = 'log'; readonly type = 'log'; base = 10; private _originalScale = new IntervalScale(); private _fixMin: boolean; private _fixMax: boolean; /** * @param Whether expand the ticks to niced extent. */ getTicks(opt?: ScaleGetTicksOpt): ScaleTick[] { opt = opt || {}; const extent = this._extent.slice() as [number, number]; const originalExtent = this._originalScale.getExtent(); const ticks = super.getTicks(opt); const base = this.base; const originalBreaks = this._originalScale._innerGetBreaks(); const scaleBreakHelper = getScaleBreakHelper(); return zrUtil.map(ticks, function (tick) { const val = tick.value; let roundingCriterion = null; let powVal = mathPow(base, val); // Fix #4158 if (val === extent[0] && this._fixMin) { roundingCriterion = originalExtent[0]; } else if (val === extent[1] && this._fixMax) { roundingCriterion = originalExtent[1]; } let vBreak; if (scaleBreakHelper) { const transformed = scaleBreakHelper.getTicksLogTransformBreak( tick, base, originalBreaks, fixRoundingError ); vBreak = transformed.vBreak; if (roundingCriterion == null) { roundingCriterion = transformed.brkRoundingCriterion; } } if (roundingCriterion != null) { powVal = fixRoundingError(powVal, roundingCriterion); } return { value: powVal, break: vBreak, }; }, this); } protected _getNonTransBreaks(): ParsedAxisBreakList { return this._originalScale._innerGetBreaks(); } setExtent(start: number, end: number): void { this._originalScale.setExtent(start, end); const loggedExtent = logTransform(this.base, [start, end]); super.setExtent(loggedExtent[0], loggedExtent[1]); } /** * @return {number} end */ getExtent() { const base = this.base; const extent = super.getExtent(); extent[0] = mathPow(base, extent[0]); extent[1] = mathPow(base, extent[1]); // Fix #4158 const originalExtent = this._originalScale.getExtent(); this._fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0])); this._fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1])); return extent; } unionExtentFromData(data: SeriesData, dim: DimensionName | DimensionLoose): void { this._originalScale.unionExtentFromData(data, dim); const loggedOther = logTransform(this.base, data.getApproximateExtent(dim), true); this._innerUnionExtent(loggedOther); } /** * Update interval and extent of intervals for nice ticks * @param approxTickNum default 10 Given approx tick number */ calcNiceTicks(approxTickNum: number): void { approxTickNum = approxTickNum || 10; const extent = this._extent.slice() as [number, number]; const span = this._getExtentSpanWithBreaks(); if (!isFinite(span) || span <= 0) { return; } let interval = numberUtil.quantity(span); const err = approxTickNum / span * interval; // Filter ticks to get closer to the desired count. if (err <= 0.5) { interval *= 10; } // Interval should be integer while (!isNaN(interval) && Math.abs(interval) < 1 && Math.abs(interval) > 0) { interval *= 10; } const niceExtent = [ fixRound(mathCeil(extent[0] / interval) * interval), fixRound(mathFloor(extent[1] / interval) * interval) ] as [number, number]; this._interval = interval; this._intervalPrecision = getIntervalPrecision(interval); this._niceExtent = niceExtent; } calcNiceExtent(opt: { splitNumber: number, fixMin?: boolean, fixMax?: boolean, minInterval?: number, maxInterval?: number }): void { super.calcNiceExtent(opt); this._fixMin = opt.fixMin; this._fixMax = opt.fixMax; } contain(val: number): boolean { val = mathLog(val) / mathLog(this.base); return super.contain(val); } normalize(val: number): number { val = mathLog(val) / mathLog(this.base); return super.normalize(val); } scale(val: number): number { val = super.scale(val); return mathPow(this.base, val); } setBreaksFromOption( breakOptionList: AxisBreakOption[], ): void { const scaleBreakHelper = getScaleBreakHelper(); if (!scaleBreakHelper) { return; } const { parsedOriginal, parsedLogged } = scaleBreakHelper.logarithmicParseBreaksFromOption( breakOptionList, this.base, zrUtil.bind(this.parse, this) ); this._originalScale._innerSetBreak(parsedOriginal); this._innerSetBreak(parsedLogged); } } function fixRoundingError(val: number, originalVal: number): number { return fixRound(val, numberUtil.getPrecision(originalVal)); } Scale.registerClass(LogScale); export default LogScale;