UNPKG

@amcharts/amcharts5

Version:
1,435 lines 62.7 kB
import { DataItem } from "../../../core/render/Component";
import { Axis } from "./Axis";
import { MultiDisposer } from "../../../core/util/Disposer";
import * as $type from "../../../core/util/Type";
import * as $array from "../../../core/util/Array";
import * as $math from "../../../core/util/Math";
import * as $utils from "../../../core/util/Utils";
/**
 * Creates a value axis.
 *
 * @see {@link https://www.amcharts.com/docs/v5/charts/xy-chart/axes/value-axis/} for more info
 * @important
 */
export class ValueAxis extends Axis {
    constructor() {
        super(...arguments);
        this._dirtyExtremes = false;
        this._dirtySelectionExtremes = false;
        this._dseHandled = false;
        this._deltaMinMax = 1;
        this._baseValue = 0;
        this._minLogAdjusted = 1;
    }
    /**
     * @ignore
     */
    markDirtyExtremes() {
        this._dirtyExtremes = true;
        this.markDirty();
    }
    /**
     * @ignore
     */
    markDirtySelectionExtremes() {
        this._dirtySelectionExtremes = true;
        this.markDirty();
    }
    _isSynced() {
        return this.get("syncWithAxis") != null;
    }
    _afterNew() {
        this._settings.themeTags = $utils.mergeTags(this._settings.themeTags, ["axis"]);
        this.setPrivateRaw("name", "value");
        this.addTag("value");
        super._afterNew();
    }
    _prepareChildren() {
        var _a;
        super._prepareChildren();
        if (this.isDirty("syncWithAxis")) {
            let previousValue = this._prevSettings.syncWithAxis;
            if (previousValue) {
                if (this._syncDp) {
                    this._syncDp.dispose();
                }
            }
            let syncWithAxis = this.get("syncWithAxis");
            if (syncWithAxis) {
                // Re-sync whenever any of the source axis's sync inputs change.
                // With autoZoom the sync target is the visible selection; without
                // it the axis holds its full range and only the full extremes
                // need recomputing. Mark dirty (not just a flag) so this axis is
                // queued even when the source publishes after its update pass.
                const resync = () => {
                    if (this.get("autoZoom", true)) {
                        this.markDirtySelectionExtremes();
                    }
                    else {
                        this.markDirtyExtremes();
                    }
                };
                this._syncDp = new MultiDisposer([
                    syncWithAxis.onPrivate("selectionMinFinal", resync),
                    syncWithAxis.onPrivate("selectionMaxFinal", resync),
                    syncWithAxis.onPrivate("selectionStepFinal", resync),
                    syncWithAxis.onPrivate("minFinal", resync),
                    syncWithAxis.onPrivate("maxFinal", resync),
                    // Step can change on its own (e.g. a resize changes
                    // gridCount while min/max stay the same)
                    syncWithAxis.onPrivate("step", resync)
                ]);
            }
        }
        // `syncZeros` changes how the synced axis' min/max align (via `_syncAxes`),
        // so re-run both the full and selection extremes when it changes. Set flags
        // directly to avoid re-entering `_prepareChildren` while it is still dirty.
        if (this.isDirty("syncZeros")) {
            this._dirtyExtremes = true;
            this._dirtySelectionExtremes = true;
        }
        let someDirty = false;
        if (this.isDirty("min") || this.isDirty("max") || this.isDirty("maxPrecision") || this.isDirty("numberFormat")) {
            someDirty = true;
            (_a = this.ghostLabel) === null || _a === void 0 ? void 0 : _a.set("text", "");
        }
        // If a sync recompute only changes the step (e.g. a resize changed
        // gridCount while min/max stayed the same), nothing else marks the
        // range dirty, so track the step explicitly for the render gate below.
        const prevSelectionStepFinal = this.getPrivate("selectionStepFinal");
        //if (this._dirtyExtremes || this.isPrivateDirty("width") || this.isPrivateDirty("height") || this.isDirty("min") || this.isDirty("max") || this.isDirty("extraMin") || this.isDirty("extraMax") || this.isDirty("logarithmic") || this.isDirty("treatZeroAs") || this.isDirty("baseValue") || this.isDirty("strictMinMax") || this.isDirty("maxPrecision")) {
        if (this._sizeDirty || this._dirtyExtremes || this._valuesDirty || someDirty || this.isPrivateDirty("width") || this.isPrivateDirty("height") || this.isDirty("extraMin") || this.isDirty("extraMax") || this.isDirty("logarithmic") || this.isDirty("treatZeroAs") || this.isDirty("baseValue") || this.isDirty("strictMinMax") || this.isDirty("strictMinMaxSelection")) {
            this._getMinMax();
            this._dirtyExtremes = false;
        }
        this._handleSizeDirty();
        if (this._dirtySelectionExtremes && !this._isPanning && this.get("autoZoom", true)) {
            const chart = this.chart;
            let getMM = false;
            // #1563
            if (chart) {
                const letter = this.get("renderer").getPrivate("letter");
                if (letter == "Y") {
                    chart.xAxes.each((axis) => {
                        if (axis.className != "ValueAxis") {
                            getMM = true;
                        }
                    });
                }
                else if (letter == "X") {
                    chart.yAxes.each((axis) => {
                        if (axis.className != "ValueAxis") {
                            getMM = true;
                        }
                    });
                }
            }
            if (getMM) {
                this._getSelectionMinMax();
            }
            this._dirtySelectionExtremes = false;
        }
        this._groupData();
        if (this._sizeDirty || this._valuesDirty || this.isDirty("start") || this.isDirty("end") || this.isPrivateDirty("min") || this.isPrivateDirty("selectionMax") || this.isPrivateDirty("selectionMin") || this.isPrivateDirty("max") || this.isPrivateDirty("step") || this.isPrivateDirty("width") || this.isPrivateDirty("height") || this.isDirty("logarithmic") || prevSelectionStepFinal !== this.getPrivate("selectionStepFinal")) {
            this._handleRangeChange();
            this._prepareAxisItems();
            this._updateAxisRanges();
        }
        this._baseValue = this.baseValue();
    }
    _handleSizeDirty() {
        if (this._sizeDirty && !this._dseHandled) {
            this._dirtySelectionExtremes = true;
            this._dseHandled = true;
            if (this.getPrivate("selectionMinFinal") != this.getPrivate("selectionMin") || this.getPrivate("selectionMaxFinal") != this.getPrivate("selectionMax")) {
                this._dirtySelectionExtremes = false;
            }
        }
    }
    _clearDirty() {
        super._clearDirty();
        this._dseHandled = false;
    }
    _groupData() {
    }
    /**
     * Logarithmic grid values are computed via `Math.pow(10, ...)`, which
     * carries floating-point noise (e.g. `4999.999999999999` instead of
     * `5000`). Round to ~10 significant digits so the grid values (and their
     * labels) come out clean; a format without a decimal directive (e.g.
     * `"#,###"`) would otherwise pass the noisy fraction straight through.
     */
    _cleanLogValue(value) {
        if (!(value > 0)) {
            return value;
        }
        const decCount = 10 - Math.floor(Math.log(value) * Math.LOG10E);
        // $math.round computes Math.pow(10, decCount), which overflows to
        // Infinity for extremely small values and would turn the value into
        // NaN. Such values are off scale anyway, so leave them as is.
        if (decCount > 280) {
            return value;
        }
        return $math.round(value, decCount);
    }
    _formatText(value) {
        const numberFormat = this.get("numberFormat");
        const formatter = this.getNumberFormatter();
        let text = "";
        if (numberFormat) {
            text = formatter.format(value, numberFormat);
        }
        else {
            // On a logarithmic axis every grid label is a different magnitude,
            // and the default number format keeps at most 5 decimals, so a
            // smaller value (e.g. 1e-8) would be rounded away to "0". Give such
            // values a format with just enough decimals to show them in full.
            // Values the default format can show keep the regular path, so
            // their labels stay exactly as before.
            let logDecimals = 0;
            if (this.get("logarithmic") && $type.isNumber(value) && value != 0) {
                logDecimals = Math.min($utils.decimalPlaces(value), 20);
            }
            if (logDecimals > 5) {
                text = formatter.format(value, "#,###." + new Array(logDecimals + 1).join("#"));
            }
            else {
                text = formatter.format(value, undefined, this.getPrivate("stepDecimalPlaces"));
            }
        }
        return text;
    }
    _prepareAxisItems() {
        const min = this.getPrivate("min");
        const max = this.getPrivate("max");
        if ($type.isNumber(min) && $type.isNumber(max)) {
            const logarithmic = this.get("logarithmic");
            const step = this.getPrivate("step");
            const selectionMin = this.getPrivate("selectionMin");
            const selectionMax = this.getPrivate("selectionMax") + step;
            const gridCount = this.get("renderer").gridCount();
            let value = selectionMin - step;
            let differencePower = 1;
            let minLog = min;
            // decades one major grid line jumps, and the power the grid starts at
            let logPowerStep = 1;
            let logStartPower = 0;
            if (logarithmic) {
                value = this._minLogAdjusted;
                if (value < selectionMin) {
                    while (value < selectionMin) {
                        value += step;
                    }
                }
                minLog = value;
                if (minLog <= 0) {
                    let minRealLog = this._minRealLog;
                    if (!$type.isNumber(minRealLog)) {
                        minRealLog = 1;
                    }
                    minLog = Math.pow(10, Math.log(minRealLog) * Math.LOG10E);
                    if (step < 1) {
                        if ($type.isNumber(this._minRealLog)) {
                            minLog = this._minRealLog;
                        }
                        else {
                            minLog = Math.pow(10, -50);
                        }
                    }
                }
                differencePower = Math.log(selectionMax - step) * Math.LOG10E - Math.log(minLog) * Math.LOG10E;
                if (differencePower > 2) {
                    // Capped at the range itself, or a stride wider than the range
                    // would step right over it and leave no grid at all.
                    logPowerStep = Math.max(1, Math.min(Math.ceil(differencePower / Math.max(1, gridCount)), Math.floor(differencePower)));
                    // Anchor the grid on round powers of ten - 1, 10, 100 - rather
                    // than on the axis minimum, which gave labels like 3.01, 30.1, 301.
                    logStartPower = Math.floor(Math.log(minLog) * Math.LOG10E / logPowerStep) * logPowerStep;
                    value = Math.pow(10, logStartPower);
                }
            }
            /// minor grid
            const renderer = this.get("renderer");
            const minorLabelsEnabled = renderer.get("minorLabelsEnabled");
            const minorGridEnabled = renderer.get("minorGridEnabled", minorLabelsEnabled);
            let stepPower = Math.pow(10, Math.floor(Math.log(Math.abs(step)) * Math.LOG10E));
            const stepAdjusted = Math.round(step / stepPower);
            let minorGridCount = 2;
            if ($math.round(stepAdjusted / 5, 10) % 1 == 0) {
                minorGridCount = 5;
            }
            if ($math.round(stepAdjusted / 10, 10) % 1 == 0) {
                minorGridCount = 10;
            }
            let minorStep = step / minorGridCount;
            // end of minor grid
            let i = 0;
            let m = 0;
            let previous = -Infinity;
            while (value < selectionMax) {
                let dataItem;
                if (this.dataItems.length < i + 1) {
                    dataItem = new DataItem(this, undefined, {});
                    this._dataItems.push(dataItem);
                    this.processDataItem(dataItem);
                }
                else {
                    dataItem = this.dataItems[i];
                }
                this._createAssets(dataItem, []);
                this._toggleDataItem(dataItem, true);
                const itemValue = logarithmic ? this._cleanLogValue(value) : value;
                dataItem.setRaw("value", itemValue);
                const label = dataItem.get("label");
                if (label) {
                    label.set("text", this._formatText(itemValue));
                }
                this._prepareDataItem(dataItem);
                let nextValue = value;
                if (!logarithmic) {
                    nextValue += step;
                }
                else {
                    if (differencePower > 2) {
                        nextValue = Math.pow(10, logStartPower + (i + 1) * logPowerStep);
                    }
                    else {
                        nextValue += step;
                    }
                }
                // minor grid
                if (minorGridEnabled) {
                    let minorValue = value + minorStep;
                    if (logarithmic) {
                        if (differencePower > 2) {
                            let minorMinMaxStep = this._adjustMinMax(value, nextValue, 10);
                            minorStep = minorMinMaxStep.step;
                        }
                        minorValue = value + minorStep;
                    }
                    while (minorValue < nextValue - step * 0.00000000001) {
                        let minorDataItem;
                        if (this.minorDataItems.length < m + 1) {
                            minorDataItem = new DataItem(this, undefined, {});
                            this.minorDataItems.push(minorDataItem);
                            this.processDataItem(minorDataItem);
                        }
                        else {
                            minorDataItem = this.minorDataItems[m];
                        }
                        this._createAssets(minorDataItem, ["minor"], true);
                        this._toggleDataItem(minorDataItem, true);
                        const minorItemValue = logarithmic ? this._cleanLogValue(minorValue) : minorValue;
                        minorDataItem.setRaw("value", minorItemValue);
                        const minorLabel = minorDataItem.get("label");
                        if (minorLabel && minorLabelsEnabled) {
                            minorLabel.set("text", this._formatText(minorItemValue));
                        }
                        this._prepareDataItem(minorDataItem);
                        // Hide the minor label after `_prepareDataItem`: it calls
                        // `toggleVisibility`, which would otherwise re-show the label.
                        if (minorLabel && !minorLabelsEnabled) {
                            minorLabel.setPrivate("visible", false);
                        }
                        minorValue += minorStep;
                        m++;
                    }
                }
                value = nextValue;
                if (previous == value) {
                    break;
                }
                let stepPower = Math.pow(10, Math.floor(Math.log(Math.abs(step)) * Math.LOG10E));
                if (stepPower < 1 && !logarithmic) {
                    // exponent is less then 1 too. Count decimals of exponent
                    let decCount = Math.round(Math.abs(Math.log(Math.abs(stepPower)) * Math.LOG10E)) + 2;
                    // round value to avoid floating point issues
                    value = $math.round(value, decCount);
                }
                i++;
                // #103520
                if (logarithmic && differencePower <= 2) {
                    if (value - step < step) {
                        value = step;
                    }
                }
                previous = value;
            }
            for (let j = i; j < this.dataItems.length; j++) {
                this._toggleDataItem(this.dataItems[j], false);
            }
            for (let j = m; j < this.minorDataItems.length; j++) {
                this._toggleDataItem(this.minorDataItems[j], false);
            }
            $array.each(this.series, (series) => {
                if (series.inited) {
                    series._markDirtyAxes();
                }
            });
            this._updateGhost();
        }
    }
    _prepareDataItem(dataItem, count) {
        let renderer = this.get("renderer");
        let value = dataItem.get("value");
        let endValue = dataItem.get("endValue");
        let position = this.valueToPosition(value);
        let endPosition = position;
        let fillEndPosition = this.valueToPosition(value + this.getPrivate("step"));
        if ($type.isNumber(endValue)) {
            endPosition = this.valueToPosition(endValue);
            fillEndPosition = endPosition;
        }
        if (dataItem.get("isRange")) {
            if (endValue == null) {
                fillEndPosition = position;
            }
        }
        let labelEndPosition = endPosition;
        let labelEndValue = dataItem.get("labelEndValue");
        if (labelEndValue != null) {
            labelEndPosition = this.valueToPosition(labelEndValue);
        }
        renderer.updateLabel(dataItem.get("label"), position, labelEndPosition, count);
        const grid = dataItem.get("grid");
        renderer.updateGrid(grid, position, endPosition);
        if (grid) {
            if (value == this.get("baseValue", 0)) {
                grid.addTag("base");
                grid._applyThemes();
            }
            else if (grid.hasTag("base")) {
                grid.removeTag("base");
                grid._applyThemes();
            }
        }
        renderer.updateTick(dataItem.get("tick"), position, labelEndPosition, count);
        renderer.updateFill(dataItem.get("axisFill"), position, fillEndPosition);
        this._processBullet(dataItem);
        renderer.updateBullet(dataItem.get("bullet"), position, endPosition);
        if (!dataItem.get("isRange")) {
            const fillRule = this.get("fillRule");
            if (fillRule) {
                fillRule(dataItem);
            }
        }
    }
    _handleRangeChange() {
        let selectionMin = this.positionToValue(this.get("start", 0));
        let selectionMax = this.positionToValue(this.get("end", 1));
        let step;
        if (this._isSynced()) {
            // Honor the bounds/step that _syncAxes already computed; _adjustMinMax
            // would otherwise pick its own gridCount-driven step and silently
            // break the sync.
            selectionMin = this.getPrivate("selectionMinFinal", selectionMin);
            selectionMax = this.getPrivate("selectionMaxFinal", selectionMax);
            const stepFinal = this.getPrivate("selectionStepFinal");
            if ($type.isNumber(stepFinal) && stepFinal > 0 && stepFinal < Infinity) {
                step = stepFinal;
            }
            else {
                // Sync hasn't produced a usable step yet (e.g. first render
                // pass); a fixed or degenerate step here could make
                // _prepareAxisItems generate a near-infinite number of items.
                step = this._adjustMinMax(selectionMin, selectionMax, this.get("renderer").gridCount(), true).step;
            }
            const stepDecimalPlaces = $utils.decimalPlaces(step);
            this.setPrivateRaw("stepDecimalPlaces", stepDecimalPlaces);
        }
        else {
            const gridCount = this.get("renderer").gridCount();
            let minMaxStep = this._adjustMinMax(selectionMin, selectionMax, gridCount, true);
            let stepDecimalPlaces = $utils.decimalPlaces(minMaxStep.step);
            this.setPrivateRaw("stepDecimalPlaces", stepDecimalPlaces);
            selectionMin = $math.round(selectionMin, stepDecimalPlaces);
            selectionMax = $math.round(selectionMax, stepDecimalPlaces);
            minMaxStep = this._adjustMinMax(selectionMin, selectionMax, gridCount, true);
            step = minMaxStep.step;
            selectionMin = minMaxStep.min;
            selectionMax = minMaxStep.max;
        }
        if (this.getPrivate("selectionMin") !== selectionMin || this.getPrivate("selectionMax") !== selectionMax || this.getPrivate("step") !== step) {
            // do not change to setPrivate, will cause SO
            this.setPrivateRaw("selectionMin", selectionMin);
            this.setPrivateRaw("selectionMax", selectionMax);
            this.setPrivateRaw("step", step);
        }
    }
    /**
     * Converts a relative position to a corresponding numeric value from axis
     * scale.
     *
     * @param   position  Relative position
     * @return            Value
     */
    positionToValue(position) {
        const min = this.getPrivate("min");
        const max = this.getPrivate("max");
        if (!this.get("logarithmic")) {
            return position * (max - min) + min;
        }
        else {
            return Math.pow(Math.E, (position * ((Math.log(max) * Math.LOG10E - Math.log(min) * Math.LOG10E)) + Math.log(min) * Math.LOG10E) / Math.LOG10E);
        }
    }
    /**
     * Convers value to a relative position on axis.
     *
     * @param   value  Value
     * @return         Relative position
     */
    valueToPosition(value) {
        const min = this.getPrivate("min");
        const max = this.getPrivate("max");
        if (!this.get("logarithmic")) {
            return (value - min) / (max - min);
        }
        else {
            if (value <= 0) {
                let treatZeroAs = this.get("treatZeroAs");
                if ($type.isNumber(treatZeroAs)) {
                    value = treatZeroAs;
                }
            }
            return (Math.log(value) * Math.LOG10E - Math.log(min) * Math.LOG10E) / ((Math.log(max) * Math.LOG10E - Math.log(min) * Math.LOG10E));
        }
    }
    /**
     * @ignore
     */
    valueToFinalPosition(value) {
        const min = this.getPrivate("minFinal");
        const max = this.getPrivate("maxFinal");
        if (!this.get("logarithmic")) {
            return (value - min) / (max - min);
        }
        else {
            if (value <= 0) {
                let treatZeroAs = this.get("treatZeroAs");
                if ($type.isNumber(treatZeroAs)) {
                    value = treatZeroAs;
                }
            }
            return (Math.log(value) * Math.LOG10E - Math.log(min) * Math.LOG10E) / ((Math.log(max) * Math.LOG10E - Math.log(min) * Math.LOG10E));
        }
    }
    /**
     * Returns X coordinate in pixels corresponding to specific value.
     *
     * @param   value     Numeric value
     * @param   location  Location
     * @param   baseValue Base value
     * @return            X coordinate
     */
    getX(value, location, baseValue) {
        value = baseValue + (value - baseValue) * location;
        const position = this.valueToPosition(value);
        return this._settings.renderer.positionToCoordinate(position);
    }
    /**
     * Returns X coordinate in pixels corresponding to specific value.
     *
     * @param   value     Numeric value
     * @param   location  Location
     * @param   baseValue Base value
     * @return            X coordinate
     */
    getY(value, location, baseValue) {
        value = baseValue + (value - baseValue) * location;
        const position = this.valueToPosition(value);
        return this._settings.renderer.positionToCoordinate(position);
    }
    /**
     * @ignore
     */
    getDataItemCoordinateX(dataItem, field, _cellLocation, axisLocation) {
        return this._settings.renderer.positionToCoordinate(this.getDataItemPositionX(dataItem, field, _cellLocation, axisLocation));
    }
    /**
     * @ignore
     */
    getDataItemPositionX(dataItem, field, _cellLocation, axisLocation) {
        let value = dataItem.get(field);
        const stackToItem = dataItem.get("stackToItemX");
        if (stackToItem) {
            const series = dataItem.component;
            value = value * axisLocation + series.getStackedXValueWorking(dataItem, field);
        }
        else {
            value = this._baseValue + (value - this._baseValue) * axisLocation;
        }
        return this.valueToPosition(value);
    }
    /**
     * @ignore
     */
    getDataItemCoordinateY(dataItem, field, _cellLocation, axisLocation) {
        return this._settings.renderer.positionToCoordinate(this.getDataItemPositionY(dataItem, field, _cellLocation, axisLocation));
    }
    /**
     * @ignore
     */
    getDataItemPositionY(dataItem, field, _cellLocation, axisLocation) {
        let value = dataItem.get(field);
        const stackToItem = dataItem.get("stackToItemY");
        if (stackToItem) {
            const series = dataItem.component;
            value = value * axisLocation + series.getStackedYValueWorking(dataItem, field);
        }
        else {
            value = this._baseValue + (value - this._baseValue) * axisLocation;
        }
        return this.valueToPosition(value);
    }
    /**
     * Returns relative position of axis' `baseValue`.
     *
     * @return  Base value position
     */
    basePosition() {
        return this.valueToPosition(this.baseValue());
    }
    /**
     * Base value of the [[ValueAxis]], which determines positive and negative
     * values.
     *
     * @return Base value
     */
    baseValue() {
        const min = Math.min(this.getPrivate("minFinal", -Infinity), this.getPrivate("selectionMin", -Infinity));
        const max = Math.max(this.getPrivate("maxFinal", Infinity), this.getPrivate("selectionMax", Infinity));
        let baseValue = this.get("baseValue", 0);
        if (baseValue < min) {
            baseValue = min;
        }
        if (baseValue > max) {
            baseValue = max;
        }
        return baseValue;
    }
    /**
     * @ignore
     */
    cellEndValue(value) {
        return value;
    }
    fixSmallStep(step) {
        // happens because of floating point error
        if (1 + step === 1) {
            step *= 2;
            return this.fixSmallStep(step);
        }
        return step;
    }
    _fixMin(min) {
        return min;
    }
    _fixMax(max) {
        return max;
    }
    _calculateTotals() {
        if (this.get("calculateTotals")) {
            let series = this.series[0];
            if (series) {
                let startIndex = series.startIndex();
                if (series.dataItems.length > 0) {
                    if (startIndex > 0) {
                        startIndex--;
                    }
                    let endIndex = series.endIndex();
                    if (endIndex < series.dataItems.length) {
                        endIndex++;
                    }
                    let field;
                    let vc;
                    if (series.get("yAxis") == this) {
                        field = "valueY";
                        vc = "vcy";
                    }
                    else if (series.get("xAxis") == this) {
                        field = "valueX";
                        vc = "vcx";
                    }
                    let fieldWorking = field + "Working";
                    if (field) {
                        let seriesCount = this.series.length;
                        let includedSeries = [];
                        let vcValues = [];
                        for (let s = 0; s < seriesCount; s++) {
                            let ser = this.series[s];
                            if (!ser.get("excludeFromTotal")) {
                                includedSeries.push(ser);
                                vcValues.push(ser.get(vc));
                            }
                        }
                        let len = includedSeries.length;
                        let fieldTotal = (field + "Total");
                        let fieldSum = (field + "Sum");
                        let fieldTotalPercent = (field + "TotalPercent");
                        for (let i = startIndex; i < endIndex; i++) {
                            let sum = 0;
                            let total = 0;
                            for (let s = 0; s < len; s++) {
                                let dataItem = includedSeries[s].dataItems[i];
                                if (dataItem) {
                                    let value = dataItem.get(fieldWorking) * vcValues[s];
                                    if (!$type.isNaN(value)) {
                                        sum += value;
                                        total += Math.abs(value);
                                    }
                                }
                            }
                            for (let s = 0; s < len; s++) {
                                let dataItem = includedSeries[s].dataItems[i];
                                if (dataItem) {
                                    let value = dataItem.get(fieldWorking) * vcValues[s];
                                    if (!$type.isNaN(value)) {
                                        dataItem.set(fieldTotal, total);
                                        dataItem.set(fieldSum, sum);
                                        dataItem.set(fieldTotalPercent, total == 0 ? 0 : value / total * 100);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    _getSelectionMinMax() {
        const min = this.getPrivate("minFinal");
        const max = this.getPrivate("maxFinal");
        const minDefined = this.get("min");
        const maxDefined = this.get("max");
        let extraMin = this.get("extraMin", 0);
        let extraMax = this.get("extraMax", 0);
        if (this.get("logarithmic")) {
            if (this.get("extraMin") == null) {
                extraMin = 0.1;
            }
            if (this.get("extraMax") == null) {
                extraMax = 0.2;
            }
        }
        const gridCount = this.get("renderer").gridCount();
        const selectionStrictMinMax = this.get("strictMinMaxSelection");
        let strictMinMax = this.get("strictMinMax");
        if ($type.isNumber(min) && $type.isNumber(max)) {
            let selectionMin = max;
            let selectionMax = min;
            $array.each(this.series, (series) => {
                if (!series.get("ignoreMinMax")) {
                    let seriesMin;
                    let seriesMax;
                    const outOfSelection = series.getPrivate("outOfSelection");
                    if (series.get("xAxis") === this) {
                        if (!outOfSelection) {
                            let minX = series.getPrivate("minX");
                            let maxX = series.getPrivate("maxX");
                            // solves #90085
                            if (series.startIndex() != 0 || series.endIndex() != series.dataItems.length) {
                                minX = undefined;
                                maxX = undefined;
                            }
                            seriesMin = series.getPrivate("selectionMinX", minX);
                            seriesMax = series.getPrivate("selectionMaxX", maxX);
                        }
                    }
                    else if (series.get("yAxis") === this) {
                        if (!outOfSelection) {
                            let minY = series.getPrivate("minY");
                            let maxY = series.getPrivate("maxY");
                            // solves #90085
                            if (series.startIndex() != 0 || series.endIndex() != series.dataItems.length) {
                                minY = undefined;
                                maxY = undefined;
                            }
                            seriesMin = series.getPrivate("selectionMinY", minY);
                            seriesMax = series.getPrivate("selectionMaxY", maxY);
                        }
                    }
                    if (!series.isHidden() && !series.isShowing()) {
                        if ($type.isNumber(seriesMin)) {
                            selectionMin = Math.min(selectionMin, seriesMin);
                        }
                        if ($type.isNumber(seriesMax)) {
                            selectionMax = Math.max(selectionMax, seriesMax);
                        }
                    }
                }
            });
            this.axisRanges.each((range) => {
                if (range.get("affectsMinMax")) {
                    let value = range.get("value");
                    if (value != null) {
                        selectionMin = Math.min(selectionMin, value);
                        selectionMax = Math.max(selectionMax, value);
                    }
                    value = range.get("endValue");
                    if (value != null) {
                        selectionMin = Math.min(selectionMin, value);
                        selectionMax = Math.max(selectionMax, value);
                    }
                }
            });
            if (selectionMin > selectionMax) {
                [selectionMin, selectionMax] = [selectionMax, selectionMin];
            }
            if ($type.isNumber(minDefined)) {
                if (strictMinMax) {
                    selectionMin = minDefined;
                }
                else {
                    selectionMin = min;
                }
            }
            else if (strictMinMax) {
                if ($type.isNumber(this._minReal)) {
                    selectionMin = this._minReal;
                }
            }
            if ($type.isNumber(maxDefined)) {
                if (strictMinMax) {
                    selectionMax = maxDefined;
                }
                else {
                    selectionMax = max;
                }
            }
            else if (strictMinMax) {
                if ($type.isNumber(this._maxReal)) {
                    selectionMax = this._maxReal;
                }
            }
            if (selectionMin === selectionMax) {
                let smin = selectionMin;
                selectionMin -= this._deltaMinMax;
                selectionMax += this._deltaMinMax;
                if (selectionMin < min) {
                    let d = smin - min;
                    if (d == 0) {
                        d = this._deltaMinMax;
                    }
                    selectionMin = smin - d;
                    selectionMax = smin + d;
                    strictMinMax = true;
                }
                let minMaxStep2 = this._adjustMinMax(selectionMin, selectionMax, gridCount, strictMinMax);
                selectionMin = minMaxStep2.min;
                selectionMax = minMaxStep2.max;
            }
            let selectionMinReal = selectionMin;
            let selectionMaxReal = selectionMax;
            let delta = selectionMax - selectionMin;
            selectionMin -= delta * extraMin;
            selectionMax += delta * extraMax;
            let minMaxStep = this._adjustMinMax(selectionMin, selectionMax, gridCount);
            selectionMin = minMaxStep.min;
            selectionMax = minMaxStep.max;
            selectionMin = $math.fitToRange(selectionMin, min, max);
            selectionMax = $math.fitToRange(selectionMax, min, max);
            // do it for the second time !important
            minMaxStep = this._adjustMinMax(selectionMin, selectionMax, gridCount, true);
            if (!strictMinMax) {
                selectionMin = minMaxStep.min;
                selectionMax = minMaxStep.max;
            }
            const syncWithAxis = this.get("syncWithAxis");
            if (syncWithAxis) {
                minMaxStep = this._syncAxes(selectionMin, selectionMax, minMaxStep.step, syncWithAxis.getPrivate("selectionMinFinal", syncWithAxis.getPrivate("minFinal", 0)), syncWithAxis.getPrivate("selectionMaxFinal", syncWithAxis.getPrivate("maxFinal", 1)), syncWithAxis.getPrivate("selectionStepFinal", syncWithAxis.getPrivate("step", 1)));
                selectionMin = minMaxStep.min;
                selectionMax = minMaxStep.max;
            }
            if (strictMinMax && !syncWithAxis) {
                if ($type.isNumber(minDefined)) {
                    selectionMin = Math.max(selectionMin, minDefined);
                }
                if ($type.isNumber(maxDefined)) {
                    selectionMax = Math.min(selectionMax, maxDefined);
                }
            }
            if (selectionStrictMinMax && !syncWithAxis) {
                selectionMin = selectionMinReal - (selectionMaxReal - selectionMinReal) * extraMin;
                selectionMax = selectionMaxReal + (selectionMaxReal - selectionMinReal) * extraMax;
            }
            if (strictMinMax && !syncWithAxis) {
                if ($type.isNumber(minDefined)) {
                    selectionMin = minDefined;
                }
                else {
                    selectionMin = selectionMinReal;
                }
                if ($type.isNumber(maxDefined)) {
                    selectionMax = maxDefined;
                }
                else {
                    selectionMax = selectionMaxReal;
                }
                if (selectionMax - selectionMin <= 0.00000001) {
                    selectionMin -= this._deltaMinMax;
                    selectionMax += this._deltaMinMax;
                }
                let delta = selectionMax - selectionMin;
                selectionMin -= delta * extraMin;
                selectionMax += delta * extraMax;
            }
            if (this.get("logarithmic")) {
                if (selectionMin <= 0) {
                    selectionMin = selectionMinReal * (1 - Math.min(extraMin, 0.99));
                }
                if (selectionMin < min) {
                    selectionMin = min;
                }
                if (selectionMax > max) {
                    selectionMax = max;
                }
            }
            let len = Math.min(20, Math.ceil(Math.log(this.getPrivate("maxZoomFactor", 100) + 1) / Math.LN10) + 2);
            let start = $math.round(this.valueToFinalPosition(selectionMin), len);
            let end = $math.round(this.valueToFinalPosition(selectionMax), len);
            this.setPrivateRaw("selectionMinFinal", selectionMin);
            this.setPrivateRaw("selectionMaxFinal", selectionMax);
            this.setPrivateRaw("selectionStepFinal", minMaxStep.step);
            this.zoom(start, end);
        }
    }
    _getMinMax() {
        let minDefined = this.get("min");
        let maxDefined = this.get("max");
        let min = Infinity;
        let max = -Infinity;
        let extraMin = this.get("extraMin", 0);
        let extraMax = this.get("extraMax", 0);
        if (this.get("logarithmic")) {
            if (!this.get("strictMinMax")) {
                if (this.get("extraMin") == null) {
                    extraMin = 0.1;
                }
                if (this.get("extraMax") == null) {
                    extraMax = 0.2;
                }
            }
        }
        let minDiff = Infinity;
        $array.each(this.series, (series) => {
            if (!series.get("ignoreMinMax")) {
                let seriesMin;
                let seriesMax;
                if (series.get("xAxis") === this) {
                    seriesMin = series.getPrivate("minX");
                    seriesMax = series.getPrivate("maxX");
                }
                else if (series.get("yAxis") === this) {
                    seriesMin = series.getPrivate("minY");
                    seriesMax = series.getPrivate("maxY");
                }
                if ($type.isNumber(seriesMin) && $type.isNumber(seriesMax)) {
                    min = Math.min(min, seriesMin);
                    max = Math.max(max, seriesMax);
                    let diff = seriesMax - seriesMin;
                    if (diff <= 0) {
                        diff = Math.abs(seriesMax / 100);
                    }
                    if (diff < minDiff) {
                        minDiff = diff;
                    }
                }
            }
        });
        this.axisRanges.each((range) => {
            if (range.get("affectsMinMax")) {
                let value = range.get("value");
                if (value != null) {
                    min = Math.min(min, value);
                    max = Math.max(max, value);
                }
                value = range.get("endValue");
                if (value != null) {
                    min = Math.min(min, value);
                    max = Math.max(max, value);
                }
            }
        });
        if (this.get("logarithmic")) {
            let treatZeroAs = this.get("treatZeroAs");
            if ($type.isNumber(treatZeroAs)) {
                if (min <= 0) {
                    min = treatZeroAs;
                }
            }
            if (min <= 0) {
                new Error("Logarithmic value axis can not have values <= 0.");
            }
        }
        if (min === 0 && max === 0) {
            max = 0.9;
            min = -0.9;
        }
        if ($type.isNumber(minDefined)) {
            min = minDefined;
        }
        if ($type.isNumber(maxDefined)) {
            max = maxDefined;
        }
        // meaning no min/max found on series/ranges and no min/max was defined
        if (min === Infinity || max === -Infinity) {
            this.setPrivate("minFinal", undefined);
            this.setPrivate("maxFinal", undefined);
            return;
        }
        if (min > max) {
            [min, max] = [max, min];
        }
        const initialMin = min;
        const initialMax = max;
        // adapter
        let minAdapted = this.adapters.fold("min", min);
        let maxAdapted = this.adapters.fold("max", max);
        this._minRealLog = min;
        if ($type.isNumber(minAdapted)) {
            min = minAdapted;
        }
        if ($type.isNumber(maxAdapted)) {
            max = maxAdapted;
        }
        // DateAxis does some magic here
        min = this._fixMin(min);
        max = this._fixMax(max);
        // this happens if starLocation and endLocation are 0.5 and DateAxis has only one date
        if (max - min <= 1 / Math.pow(10, 15)) {
            if (max - min !== 0) {
                this._deltaMinMax = (max - min) / 2;
            }
            else {
                this._getDelta(max);
            }
            min -= this._deltaMinMax;
            max += this._deltaMinMax;
        }
        // add extras
        min -= (max - min) * extraMin;
        max += (max - min) * extraMax;
        if (this.get("logarithmic")) {
            // don't let min go below 0 if real min is >= 0
            if (min < 0 && initialMin >= 0) {
                min = 0;
            }
            // don't let max go above 0 if real max is <= 0
            if (max > 0 && initialMax <= 0) {
                max = 0;
            }
        }
        this._minReal = min;
        this._maxReal = max;
        let strictMinMax = this.get("strictMinMax");
        let strictMinMaxSelection = this.get("strictMinMaxSelection", false);
        if (strictMinMaxSelection) {
            strictMinMax = strictMinMaxSelection;
        }
        let strict = strictMinMax;
        if ($type.isNumber(maxDefined)) {
            strict = true;
        }
        let gridCount = this.get("renderer").gridCount();
        let minMaxStep = this._adjustMinMax(min, max, gridCount, strict);
        min = minMaxStep.min;
        max = minMaxStep.max;
        // do it for the second time with strict true (importat!)
        minMaxStep = this._adjustMinMax(min, max, gridCount, true);
        min = minMaxStep.min;
        max = minMaxStep.max;
        // return min max if strict
        if (strictMinMax) {
            if ($type.isNumber(minDefined)) {
                min = minDefined;
            }
            else {
                min = this._minReal;
            }
            if ($type.isNumber(maxDefined)) {
                max = maxDefined;
            }
            else {
                max = this._maxReal;
            }
            if (max - min <= 0.00000001) {
                min -= this._deltaMinMax;
                max += this._deltaMinMax;
            }
            let delta = max - min;
            min -= delta * extraMin;
            max += delta * extraMax;
        }
        minAdapted = this.adapters.fold("min", min);
        maxAdapted = this.adapters.fold("max", max);
        if ($type.isNumber(minAdapted)) {
            min = minAdapted;
        }
        if ($type.isNumber(maxAdapted)) {
            max = maxAdapted;
        }
        if (minDiff == Infinity) {
            minDiff = (max - min);
        }
        // this is to avoid floating point number error
        let decCount = Math.round(Math.abs(Math.log(Math.abs(max - min)) * Math.LOG10E)) + 5;
        min = $math.round(min, decCount);
        max = $math.round(max, decCount);
        const syncWithAxis = this.get("syncWithAxis");
        if (syncWithAxis) {
            minMaxStep = this._syncAxes(min, max, minMaxStep.step, syncWithAxis.getPrivate("minFinal", syncWithAxis.getPrivate("min", 0)), syncWithAxis.getPrivate("maxFinal", syncWithAxis.getPrivate("max", 1)), syncWithAxis.getPrivate("step", 1));
            min = minMaxStep.min;
            max = minMaxStep.max;
            // Without autoZoom the axis holds its full range, so the synced
            // full range is also its selection. Publish it so that
            // _handleRangeChange renders the synced step and so that axes
            // synced to this one get notified. With autoZoom the selection is
            // computed in _getSelectionMinMax instead.
            if (!this.get("autoZoom", true)) {
                this.setPrivateRaw("selectionMinFinal", min);
                this.setPrivateRaw("selectionMaxFinal", max);
                this.setPrivateRaw("selectionStepFinal", minMaxStep.step);
            }
        }
        this.setPrivateRaw("maxZoomFactor", Math.max(1, Math.ceil((max - min) / minDiff * this.get("maxZoomFactor", 100))));
        this._fixZoomFactor();
        if (this.get("logarithmic")) {
            this._minLogAdjusted = min;
            min = this._minReal;
            max = this._maxReal;
            if (min <= 0) {
                min = initialMin * (1 - Math.min(extraMin, 0.99));
            }
        }
        if ($type.isNumber(min) && $type.isNumber(max)) {
            if (this.getPrivate("minFinal") !== min || this.getPrivate("maxFinal") !== max) {
                this.setPrivate("minFinal", min);
                this.setPrivate("maxFinal", max);
                this._saveMinMax(min, max);
                const duration = this.get("interpolationDuration", 0);
                const easing = this.get("interpolationEasing");
                this.animatePrivate({ key: "min", to: min, duration, easing });
                this.animatePrivate({ key: "max", to: max, duration, easing });
            }
        }
    }
    _fixZoomFactor() {
    }
    _getDelta(max) {
        // the number by which we need to raise 10 to get difference
        let exponent = Math.log(Math.abs(max)) * Math.LOG10E;
        // here we find a number which is power of 10 and has the same count of numbers as difference has
        let power = Math.pow(10, Math.floor(exponent));
        // reduce this number by 10 times
        power = power / 10;
        this._deltaMinMax = power;
    }
    _saveMinMax(_min, _max) {
    }
    _adjustMinMax(min, max, gridCount, strictMode) {
        // will fail if 0
        if (gridCount <= 1) {
            gridCount = 1;
        }
        gridCount = Math.round(gridCount);
        let initialMin = min;
        let initialMax = max;
        let difference = max - min;
        // in case min and max is the same, use max
        if (difference === 0) {
            difference = Math.abs(max);
        }
        // the number by which we need to raise 10 to get difference
        let exponent = Math.log(Math.abs(difference)) * Math.LOG10E;
        // here we find a number which is power of 10 and has the same count of numbers as difference has
        let power = Math.pow(10, Math.floor(exponent));
        // reduce this number by 10 times
        power = power / 10;
        let extra = power;
        if (strictMode) {
            extra = 0;
        }
        // round down min
        if (strictMode) {
            min = Math.floor(min / power) * power;
            // round up max
            max = Math.ceil(max / power) * power;
        }
        else {
            min = Math.ceil(min / power) * power - extra;
            // round up max
            max = Math.floor(max / power) * power + extra;
        }
        // don't let min go below 0 if real min is >= 0
        if (min < 0 && initialMin >= 0) {
            min = 0;
        }
        // don't let max go above 0 if real max is <= 0
        if (max > 0 && initialMax <= 0) {
            max = 0;
        }
        exponent = Math.log(Math.abs(difference)) * Math.LOG10E;
        power = Math.pow(10, Math.floor(exponent));
        power = power / 100; // used to be 10 in v4, but this caused issue that there could be limited number of grids with even very small minGridDistance
        // approximate difference between two grid lines
        let step = Math.ceil((difference / gridCount) / power) * power;
        let stepPower = Math.pow(10, Math.floor(Math.log(Math.abs(step)) * Math.LOG10E));
        // the step should divide by  2, 5, and 10.
        let stepDivisor = Math.ceil(step / stepPower); // number 0 - 10
        if (stepDivisor > 5) {
            stepDivisor = 10;
        }
        else if (stepDivisor <= 5 && stepDivisor > 2) {
            stepDivisor = 5;
        }
        // now get real step
        step = Math.ceil(step / (stepPower * stepDivisor)) * stepPower * stepDivisor;
        let maxPrecision = this.get("maxPrecision");
        if ($type.isNumber(maxPrecision)) {
            let ceiledStep = $math.ceil(step, maxPrecision);
            if (maxPrecision < Number.MAX_VALUE && step !== ceiledStep) {
                step = ceiledStep;
                if (step == 0) {
                    step = 1;
                }
            }
        }
        let decCount = 0;
        // in case numbers are smaller than 1
        if (stepPower < 1) {
            // exponent is less then 1 too. Count decimals of exponent
            decCount = Math.round(Math.abs(Math.log(Math.abs(stepPower)) * Math.LOG10E)) + 1;
            // round step
            step = $math.round(step, decCount);
        }
        // final min and max
        let minCount = Math.floor(min / step);
        min = $math.round(step * minCount, decCount);
        let maxCount;
        if (!strictMode) {
            maxCount = Math.ceil(max / step);
        }
        else {
            maxCount = Math.floor(max / step);
        }
        if (maxCount === minCount) {
            maxCount++;
        }
        max = $math.round(step * maxCount, decCount);
        if (max < initialMax) {
            max = max + step;
        }
        if (min > initialMin) {
            min = min - step;
        }
        step = this.fixSmallStep(step);
        return { min: min, max: max, step: step };
    }
    /**
     * Returns text to be used in an axis tooltip for specific relative position.
     *
     * @param   position  Position
     * @return            Tooltip text
     */
    getTooltipText(position, _adjustPosition) {
        const numberFormat = this.get("tooltipNumberFormat", this.get("numberFormat"));
        const formatter = this.getNumberFormatter();
        const extraDecimals = this.get("extraTooltipPrecision", 0);
        const decimals = this.getPrivate("stepDecimalPlaces", 0) + extraDecimals;
        const value = $math.round(this.positionToValue(position), decimals);
        if (numberFormat) {
            return formatter.format(value, numberFormat);
        }
        else {
            return formatter.format(value, undefined, decimals);
        }
    }
    /**
     * Returns a data item from series that is closest to the `position`.
     *
     * @param   series    Series
     * @param   position  Relative position
     * @return            Data item
     */
    getSeriesItem(series, position) {
        let fieldName = (this.getPrivate("name") + this.get("renderer").getPrivate("letter"));
        let value = this.positionToValue(position);
        let index = undefined;
        let oldDiff;
        $array.each(series.dataItems, (dataItem, i) => {
            const diff = Math.abs(dataItem.get(fieldName) - value);
            if (index === undefined || diff < oldDiff) {
                index = i;
                oldDiff = diff;
            }
        });
        if (index != null) {
            return series.dataItems[index];
        }
    }
    /**
     * Zooms the axis to specific `start` and `end` values.
     *
     * Optional `duration` specifies duration of zoom animation in milliseconds.
     *
     * @param  start     Start value
     * @param  end       End value
     * @param  duration  Duration in milliseconds
     */
    zoomToValues(start, end, duration) {
        const min = this.getPrivate("minFinal", this.getPrivate("min", 0));
        const max = this.getPrivate("maxFinal", this.getPrivate("max", 1));
        if (this.getPrivate("min") != null && this.getPrivate("max") != null) {
            this.zoom((start - min) / (max - min), (end - min) / (max - min), duration);
        }
    }
    /**
     * Syncs with a target axis.
     *
     * @param  min  Min
     * @param  max  Max
     * @param  step Step
     */
    _syncAxes(min, max, step, syncMin, syncMax, syncStep) {
        let axis = this.get("syncWithAxis");
        if (axis) {
            let targetCount = Math.round((syncMax - syncMin) / syncStep);
            if ($type.isNumber(targetCount) && targetCount > 0 && targetCount < Infinity) {
                if (this.get("syncZeros") && syncMin <= 0 && syncMax >= 0) {
                    // Count intervals below/above zero on source axis
                    let belowCount = Math.round((0 - syncMin) / syncStep);
                    let aboveCount = targetCount - belowCount;
                    // The source has no room on a side this axis has data on - it
                    // stops at zero where this one does not. Aligning anyway sets that
                    // side of the range to zero, putting the data outside the axis,
                    // where it is silently not drawn. Only the source can make the
                    // room (give it `extraMin`/`extraMax`), so keep this axis's own
                    // bounds instead: the zero lines will not line up, but nothing
                    // disappears.
                    const needsBelow = min < 0 && belowCount == 0;
                    const needsAbove = max > 0 && aboveCount == 0;
                    if (!needsBelow && !needsAbove) {
                        // Find the smallest nice step that covers this axis's
                        // data on both sides with the same below/above split
                        let rawStep = 0;
                        if (belowCount > 0 && min < 0) {
                            rawStep = Math.abs(min) / belowCount;
                        }
                        if (aboveCount > 0 && max > 0) {
                            rawStep = Math.max(rawStep, max / aboveCount);
                        }
                        // Fallback: if one side has no data but has intervals
                        if (rawStep == 0) {
                            rawStep = (max - min) / targetCount;
                        }
                        // A degenerate range would produce a zero/NaN step; keep
                        // the axis's own min/max/step in that case
                        if (rawStep > 0 && rawStep < Infinity) {
                            step = this._niceStep(rawStep);
                            min = -step * belowCount;
                            max = step * aboveCount;
                        }
                    }
                }
                else {
                    // Regular grid count sync (no zero alignment)
                    let rawStep = (max - min) / targetCount;
                    // A degenerate range would produce a zero/NaN step; keep
                    // the axis's own min/max/step in that case
                    if (rawStep > 0 && rawStep < Infinity) {
                        step = this._niceStep(rawStep);
                        // Snap min/max to step boundaries ensuring data coverage
                        let newMin = Math.floor(min / step) * step;
                        let newMax = newMin + step * targetCount;
                        // Ensure max covers the data
                        if (newMax < max) {
                            newMin = Math.ceil(max / step) * step - step * targetCount;
                            newMax = newMin + step * targetCount;
                        }
                        // Preserve zero boundaries
                        if (min >= 0 && newMin < 0) {
                            newMin = 0;
                            newMax = step * targetCount;
                        }
                        if (max <= 0 && newMax > 0) {
                            newMax = 0;
                            newMin = -(step * targetCount);
                        }
                        min = newMin;
                        max = newMax;
                    }
                }
            }
        }
        return { min: min, max: max, step: step };
    }
    /**
     * Rounds a raw step value up to the nearest "nice" number
     * (1, 2, 5, 10, 20, 50, etc.), respecting maxPrecision.
     */
    _niceStep(rawStep) {
        // A zero/NaN/negative step would stall axis item generation downstream,
        // so never let one through
        if (!$type.isNumber(rawStep) || rawStep <= 0 || rawStep == Infinity) {
            return 1;
        }
        let stepPower = Math.pow(10, Math.floor(Math.log(Math.abs(rawStep)) * Math.LOG10E));
        let normalized = rawStep / stepPower;
        if (normalized <= 1)
            normalized = 1;
        else if (normalized <= 2)
            normalized = 2;
        else if (normalized <= 5)
            normalized = 5;
        else
            normalized = 10;
        let step = normalized * stepPower;
        // stepPower can underflow to 0 for extremely small raw steps
        if (!(step > 0 && step < Infinity)) {
            step = rawStep;
        }
        let maxPrecision = this.get("maxPrecision");
        if ($type.isNumber(maxPrecision)) {
            let ceiledStep = $math.ceil(step, maxPrecision);
            if (maxPrecision < Number.MAX_VALUE && step !== ceiledStep) {
                step = ceiledStep;
                if (step == 0) {
                    step = 1;
                }
            }
        }
        return step;
    }
    /**
     * Returns relative position between two grid lines of the axis.
     *
     * @return Position
     */
    getCellWidthPosition() {
        let max = this.getPrivate("selectionMax", this.getPrivate("max"));
        let min = this.getPrivate("selectionMin", this.getPrivate("min"));
        if ($type.isNumber(max) && $type.isNumber(min)) {
            return this.getPrivate("step", 1) / (max - min);
        }
        return 0.05;
    }
    /**
     * @ignore
     */
    nextPosition(count) {
        if (count == null) {
            count = 1;
        }
        if (this.get("renderer").getPrivate("letter") == "Y") {
            count *= -1;
        }
        let value = this.positionToValue(this.getPrivate("tooltipPosition", 0));
        value += this.getPrivate("step", 1) * count;
        value = $math.fitToRange(value, this.getPrivate("selectionMin", 0), this.getPrivate("selectionMax", 1));
        return this.toGlobalPosition(this.valueToPosition(value));
    }
}
ValueAxis.className = "ValueAxis";
ValueAxis.classNames = Axis.classNames.concat([ValueAxis.className]);
//# sourceMappingURL=ValueAxis.js.map