| /* | 
 | * 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 from './Scale'; | 
 | import * as numberUtil from '../util/number'; | 
 | import * as scaleHelper from './helper'; | 
 |  | 
 | // Use some method of IntervalScale | 
 | import IntervalScale from './Interval'; | 
 | import SeriesData from '../data/SeriesData'; | 
 | import { DimensionName, ScaleTick } from '../util/types'; | 
 |  | 
 | const scaleProto = Scale.prototype; | 
 | // FIXME:TS refactor: not good to call it directly with `this`? | 
 | const intervalScaleProto = IntervalScale.prototype; | 
 |  | 
 | const roundingErrorFix = numberUtil.round; | 
 |  | 
 | const mathFloor = Math.floor; | 
 | const mathCeil = Math.ceil; | 
 | const mathPow = Math.pow; | 
 |  | 
 | const mathLog = Math.log; | 
 |  | 
 | class LogScale extends Scale { | 
 |     static type = 'log'; | 
 |     readonly type = 'log'; | 
 |  | 
 |     base = 10; | 
 |  | 
 |     private _originalScale: IntervalScale = new IntervalScale(); | 
 |  | 
 |     private _fixMin: boolean; | 
 |     private _fixMax: boolean; | 
 |  | 
 |     // FIXME:TS actually used by `IntervalScale` | 
 |     private _interval: number = 0; | 
 |     // FIXME:TS actually used by `IntervalScale` | 
 |     private _niceExtent: [number, number]; | 
 |  | 
 |  | 
 |     /** | 
 |      * @param Whether expand the ticks to niced extent. | 
 |      */ | 
 |     getTicks(expandToNicedExtent?: boolean): ScaleTick[] { | 
 |         const originalScale = this._originalScale; | 
 |         const extent = this._extent; | 
 |         const originalExtent = originalScale.getExtent(); | 
 |  | 
 |         const ticks = intervalScaleProto.getTicks.call(this, expandToNicedExtent); | 
 |  | 
 |         return zrUtil.map(ticks, function (tick) { | 
 |             const val = tick.value; | 
 |             let powVal = numberUtil.round(mathPow(this.base, val)); | 
 |  | 
 |             // Fix #4158 | 
 |             powVal = (val === extent[0] && this._fixMin) | 
 |                 ? fixRoundingError(powVal, originalExtent[0]) | 
 |                 : powVal; | 
 |             powVal = (val === extent[1] && this._fixMax) | 
 |                 ? fixRoundingError(powVal, originalExtent[1]) | 
 |                 : powVal; | 
 |  | 
 |             return { | 
 |                 value: powVal | 
 |             }; | 
 |         }, this); | 
 |     } | 
 |  | 
 |     setExtent(start: number, end: number): void { | 
 |         const base = mathLog(this.base); | 
 |         // log(-Infinity) is NaN, so safe guard here | 
 |         start = mathLog(Math.max(0, start)) / base; | 
 |         end = mathLog(Math.max(0, end)) / base; | 
 |         intervalScaleProto.setExtent.call(this, start, end); | 
 |     } | 
 |  | 
 |     /** | 
 |      * @return {number} end | 
 |      */ | 
 |     getExtent() { | 
 |         const base = this.base; | 
 |         const extent = scaleProto.getExtent.call(this); | 
 |         extent[0] = mathPow(base, extent[0]); | 
 |         extent[1] = mathPow(base, extent[1]); | 
 |  | 
 |         // Fix #4158 | 
 |         const originalScale = this._originalScale; | 
 |         const originalExtent = originalScale.getExtent(); | 
 |         this._fixMin && (extent[0] = fixRoundingError(extent[0], originalExtent[0])); | 
 |         this._fixMax && (extent[1] = fixRoundingError(extent[1], originalExtent[1])); | 
 |  | 
 |         return extent; | 
 |     } | 
 |  | 
 |     unionExtent(extent: [number, number]): void { | 
 |         this._originalScale.unionExtent(extent); | 
 |  | 
 |         const base = this.base; | 
 |         extent[0] = mathLog(extent[0]) / mathLog(base); | 
 |         extent[1] = mathLog(extent[1]) / mathLog(base); | 
 |         scaleProto.unionExtent.call(this, extent); | 
 |     } | 
 |  | 
 |     unionExtentFromData(data: SeriesData, dim: DimensionName): void { | 
 |         // TODO | 
 |         // filter value that <= 0 | 
 |         this.unionExtent(data.getApproximateExtent(dim)); | 
 |     } | 
 |  | 
 |     /** | 
 |      * 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; | 
 |         const span = extent[1] - extent[0]; | 
 |         if (span === Infinity || 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 = [ | 
 |             numberUtil.round(mathCeil(extent[0] / interval) * interval), | 
 |             numberUtil.round(mathFloor(extent[1] / interval) * interval) | 
 |         ] as [number, number]; | 
 |  | 
 |         this._interval = interval; | 
 |         this._niceExtent = niceExtent; | 
 |     } | 
 |  | 
 |     calcNiceExtent(opt: { | 
 |         splitNumber: number, // By default 5. | 
 |         fixMin?: boolean, | 
 |         fixMax?: boolean, | 
 |         minInterval?: number, | 
 |         maxInterval?: number | 
 |     }): void { | 
 |         intervalScaleProto.calcNiceExtent.call(this, opt); | 
 |  | 
 |         this._fixMin = opt.fixMin; | 
 |         this._fixMax = opt.fixMax; | 
 |     } | 
 |  | 
 |     parse(val: any): number { | 
 |         return val; | 
 |     } | 
 |  | 
 |     contain(val: number): boolean { | 
 |         val = mathLog(val) / mathLog(this.base); | 
 |         return scaleHelper.contain(val, this._extent); | 
 |     } | 
 |  | 
 |     normalize(val: number): number { | 
 |         val = mathLog(val) / mathLog(this.base); | 
 |         return scaleHelper.normalize(val, this._extent); | 
 |     } | 
 |  | 
 |     scale(val: number): number { | 
 |         val = scaleHelper.scale(val, this._extent); | 
 |         return mathPow(this.base, val); | 
 |     } | 
 |  | 
 |     getMinorTicks: IntervalScale['getMinorTicks']; | 
 |     getLabel: IntervalScale['getLabel']; | 
 | } | 
 |  | 
 | const proto = LogScale.prototype; | 
 | proto.getMinorTicks = intervalScaleProto.getMinorTicks; | 
 | proto.getLabel = intervalScaleProto.getLabel; | 
 |  | 
 | function fixRoundingError(val: number, originalVal: number): number { | 
 |     return roundingErrorFix(val, numberUtil.getPrecision(originalVal)); | 
 | } | 
 |  | 
 |  | 
 | Scale.registerClass(LogScale); | 
 |  | 
 | export default LogScale; |