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); Object.defineProperty(this, "_dirtyExtremes", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "_dirtySelectionExtremes", { enumerable: true, configurable: true, writable: true, value: false }); Object.defineProperty(this, "_deltaMinMax", { enumerable: true, configurable: true, writable: true, value: 1 }); Object.defineProperty(this, "_minReal", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_maxReal", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_baseValue", { enumerable: true, configurable: true, writable: true, value: 0 }); Object.defineProperty(this, "_syncDp", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "_minLogAdjusted", { enumerable: true, configurable: true, writable: true, value: 1 }); } /** * @ignore */ markDirtyExtremes() { this._dirtyExtremes = true; this.markDirty(); } /** * @ignore */ markDirtySelectionExtremes() { this._dirtySelectionExtremes = true; this.markDirty(); } _afterNew() { this._settings.themeTags = $utils.mergeTags(this._settings.themeTags, ["axis"]); this.setPrivateRaw("name", "value"); this.addTag("value"); super._afterNew(); } _prepareChildren() { 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) { this._syncDp = new MultiDisposer([ syncWithAxis.onPrivate("selectionMinFinal", () => { this._dirtySelectionExtremes = true; }), syncWithAxis.onPrivate("selectionMaxFinal", () => { this._dirtySelectionExtremes = true; }) ]); } } let someDirty = false; if (this.isDirty("min") || this.isDirty("max") || this.isDirty("maxPrecision") || this.isDirty("numberFormat")) { someDirty = true; this.ghostLabel.set("text", ""); } //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; } if (this._dirtySelectionExtremes && !this._isPanning && this.get("autoZoom", true)) { 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")) { this._handleRangeChange(); this._prepareAxisItems(); this._updateAxisRanges(); } this._baseValue = this.baseValue(); } _groupData() { } _formatText(value) { const numberFormat = this.get("numberFormat"); const formatter = this.getNumberFormatter(); let text = ""; if (numberFormat) { text = formatter.format(value, numberFormat); } 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; let value = selectionMin - step; let differencePower = 1; let minLog = min; if (logarithmic) { value = this._minLogAdjusted; if (value < selectionMin) { while (value < selectionMin) { value += step; } } minLog = value; if (minLog <= 0) { minLog = 1; if (step < 1) { minLog = step; } } differencePower = Math.log(selectionMax - step) * Math.LOG10E - Math.log(minLog) * Math.LOG10E; if (differencePower > 2) { value = Math.pow(10, Math.log(minLog) * Math.LOG10E - 5); } } /// 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); dataItem.setRaw("value", value); const label = dataItem.get("label"); if (label) { label.set("text", this._formatText(value)); } this._prepareDataItem(dataItem); let nextValue = value; if (!logarithmic) { nextValue += step; } else { if (differencePower > 2) { nextValue = Math.pow(10, Math.log(minLog) * Math.LOG10E + i - 5); } 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); minorDataItem.setRaw("value", minorValue); const minorLabel = minorDataItem.get("label"); if (minorLabel) { if (minorLabelsEnabled) { minorLabel.set("text", this._formatText(minorValue)); } else { minorLabel.setPrivate("visible", false); } } this._prepareDataItem(minorDataItem); 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) { // 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++; 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)); 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); let 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) { for (let i = startIndex; i < endIndex; i++) { let sum = 0; let total = 0; $array.each(this.series, (series) => { if (!series.get("excludeFromTotal")) { let dataItem = series.dataItems[i]; if (dataItem) { let value = dataItem.get(fieldWorking) * series.get(vc); if (!$type.isNaN(value)) { sum += value; total += Math.abs(value); } } } }); $array.each(this.series, (series) => { if (!series.get("excludeFromTotal")) { let dataItem = series.dataItems[i]; if (dataItem) { let value = dataItem.get(fieldWorking) * series.get(vc); if (!$type.isNaN(value)) { dataItem.set((field + "Total"), total); dataItem.set((field + "Sum"), sum); dataItem.set((field + "TotalPercent"), 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; selectionMin -= (selectionMax - selectionMin) * extraMin; selectionMax += (selectionMax - selectionMin) * 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) { if ($type.isNumber(minDefined)) { selectionMin = Math.max(selectionMin, minDefined); } if ($type.isNumber(maxDefined)) { selectionMax = Math.min(selectionMax, maxDefined); } } if (selectionStrictMinMax) { selectionMin = selectionMinReal - (selectionMax - selectionMin) * extraMin; selectionMax = selectionMaxReal + (selectionMax - selectionMin) * 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("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; } const initialMin = min; const initialMax = max; // adapter let minAdapted = this.adapters.fold("min", min); let maxAdapted = this.adapters.fold("max", max); 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; } min -= (max - min) * extraMin; max += (max - min) * 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; } 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; } } 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", 0); const max = this.getPrivate("maxFinal", 0); 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 count = Math.round(syncMax - syncMin) / syncStep; let currentCount = Math.round((max - min) / step); let gridCount = this.get("renderer").gridCount(); if ($type.isNumber(count) && $type.isNumber(currentCount)) { let synced = false; let c = 0; let diff = (max - min) * 0.01; let omin = min; let omax = max; let ostep = step; while (synced != true) { synced = this._checkSync(omin, omax, ostep, count); c++; if (c > 500) { synced = true; } if (!synced) { if (c / 3 == Math.round(c / 3)) { omin = min - diff * c; if (min >= 0 && omin < 0) { omin = 0; } } else { omax = max + diff * c; if (omax <= 0 && omax > 0) { omax = 0; } } let minMaxStep = this._adjustMinMax(omin, omax, gridCount, true); omin = minMaxStep.min; omax = minMaxStep.max; ostep = minMaxStep.step; } else { min = omin; max = omax; step = ostep; } } } } return { min: min, max: max, step: step }; } /** * Returns `true` if axis needs to be resunced with some other axis. */ _checkSync(min, max, step, count) { let currentCount = (max - min) / step; for (let i = 1; i < count; i++) { if ($math.round(currentCount / i, 1) == count || currentCount * i == count) { return true; } } return false; } /** * 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; } } Object.defineProperty(ValueAxis, "className", { enumerable: true, configurable: true, writable: true, value: "ValueAxis" }); Object.defineProperty(ValueAxis, "classNames", { enumerable: true, configurable: true, writable: true, value: Axis.classNames.concat([ValueAxis.className]) }); //# sourceMappingURL=ValueAxis.js.map