UNPKG

@nativescript-community/ui-chart

Version:

A powerful chart / graph plugin, supporting line, bar, pie, radar, bubble, and candlestick charts as well as scaling, panning and animations.

215 lines (214 loc) 8.15 kB
import { Align } from '@nativescript-community/ui-canvas'; import { Utils } from '../utils/Utils'; import { Renderer } from './Renderer'; /** * Baseclass of all axis renderers. * */ export class AxisRenderer extends Renderer { constructor(viewPortHandler, trans, axis) { super(viewPortHandler); this.transformer = trans; this.mAxis = axis; } /** * Returns the Paint object that is used for drawing the axis-line that goes * alongside the axis. */ get axisLinePaint() { if (!this.mAxisLinePaint) { this.mAxisLinePaint = Utils.getTemplatePaint('black-stroke'); } return this.mAxisLinePaint; } get limitLinePaint() { if (!this.mLimitLinePaint) { this.mLimitLinePaint = Utils.getTemplatePaint('black-stroke'); } return this.mLimitLinePaint; } /** * Returns the Paint object used for drawing the axis (labels). */ get axisLabelsPaint() { if (!this.mAxisLabelPaint) { this.mAxisLabelPaint = this.createAxisLabelsPaint(); } return this.mAxisLabelPaint; } createAxisLabelsPaint() { const paint = Utils.getTemplatePaint('black-fill'); paint.setTextAlign(Align.LEFT); return paint; } /** * Returns the Paint object that is used for drawing the grid-lines of the * axis. */ get gridPaint() { if (!this.mGridPaint) { this.mGridPaint = Utils.getTemplatePaint('grid'); } return this.mGridPaint; } getCurrentMinMax(min, max, inverted) { if (min === undefined || max === undefined || inverted === undefined) { const axis = this.mAxis; if (min === undefined) { min = axis.axisMinimum; } if (max === undefined) { max = axis.axisMaximum; } if (inverted === undefined) { inverted = axis['isInverted'] ? axis['isInverted']() : false; } } const viewPortHandler = this.mViewPortHandler; if (viewPortHandler?.contentRect.width() > 10 && !viewPortHandler.isFullyZoomedOutY()) { const rect = this.mAxis.ignoreOffsets ? viewPortHandler.chartRect : viewPortHandler.contentRect; const p1 = this.transformer.getValuesByTouchPoint(rect.left, rect.top); const p2 = this.transformer.getValuesByTouchPoint(rect.left, rect.bottom); if (!inverted) { min = p2.y; max = p1.y; } else { min = p1.y; max = p2.y; } } return { min, max }; } /** * Computes the axis values. * * @param min - the minimum value in the data object for this axis * @param max - the maximum value in the data object for this axis */ computeAxis(min, max, inverted) { const axis = this.mAxis; if (!axis.enabled) { return; } // calculate the starting and entry polet of the y-labels (depending on // zoom / contentrect bounds) const result = this.getCurrentMinMax(min, max, inverted); this.computeAxisValues(result.min, result.max); } /** * Sets up the axis values. Computes the desired number of labels between the two given extremes. */ computeAxisValues(min, max) { const axis = this.mAxis; const yMin = min; const yMax = max; const labelCount = Math.max(0, axis.labelCount); const range = Math.abs(yMax - yMin); if (labelCount === 0 || range <= 0 || !Number.isFinite(range)) { axis.mEntries = []; axis.mLabels = []; axis.mCenteredEntries = []; axis.mEntryCount = 0; return; } // Find out how much spacing (in y value space) between axis values const rawInterval = range / (labelCount - 1); let interval = axis.forcedInterval > 0 ? axis.forcedInterval : axis.ensureLastLabel ? rawInterval : Utils.roundToNextSignificant(rawInterval); // If granularity is enabled, then do not allow the interval to go below specified granularity. // This is used to avoid repeated values when rounding values for display. if (axis.granularity && interval < axis.granularity) { interval = axis.granularity; } // Normalize interval const intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, Math.log10(interval))); const intervalSigDigit = interval / intervalMagnitude; if (intervalSigDigit > 5) { // Use one order of magnitude higher, to avoid intervals like 0.9 or // 90 interval = Math.floor(10 * intervalMagnitude); } let n = axis.centerAxisLabels ? 1 : 0; const formatter = axis.valueFormatter; // force label count if (axis.forceLabelsEnabled) { interval = range / (labelCount - 1); axis.mEntryCount = Math.floor(labelCount); if (axis.mEntries.length < axis.mEntryCount) { // Ensure stops contains at least numStops elements. axis.mEntries = []; axis.mLabels = []; } let v = min; for (let i = 0; i < axis.mEntryCount; i++) { if (axis.ensureLastLabel && i === axis.mEntryCount - 1) { v = max; } axis.mEntries[i] = v; axis.mLabels[i] = formatter.getAxisLabel(v, axis); v += interval; } n = axis.mEntryCount; // no forced count } else { // if we use Math.ceil(yMin / interval) * interval and the min value is 20 // then we will see 0 as axis first when it should be 20 // let first = interval === 0 ? 0 : Math.ceil(yMin / interval) * interval; let first = interval === 0 ? 0 : Math.ceil(yMin / interval) * interval; if (axis.centerAxisLabels) { first -= interval; } // use Math.floor(yMax / interval) + 1 instead of // Math.floor(yMax / interval) to make sure the axis is showed "above" the higghest value let last = interval === 0 ? 0 : Utils.nextUp(Math.floor(yMax / interval) * interval); if (axis.ensureLastLabel && last < max) { last = Math.min(max, last + interval); } let f; let i; if (interval !== 0) { for (f = first; f <= last; f += interval) { ++n; } } // if (axis.ensureLastLabel && (n - 1) * interval < last) { // n++; // } axis.mEntryCount = n; if (axis.mEntries.length < n) { // Ensure stops contains at least numStops elements. axis.mEntries = []; axis.mLabels = []; } for (f = first, i = 0; i <= n; f += interval, ++i) { if (f === 0.0) { // Fix for negative zero case (Where value === -0.0, and 0.0 === -0.0) f = 0.0; } else if (!axis.allowLastLabelAboveMax && f > max) { f = max; } axis.mEntries[i] = f; axis.mLabels[i] = formatter.getAxisLabel(f, axis); } } // set decimals if (interval < 1) { axis.mDecimals = Math.ceil(-Math.log10(interval)); } else { axis.mDecimals = 0; } if (axis.centerAxisLabels) { if (axis.mCenteredEntries.length < n) { axis.mCenteredEntries = []; } const offset = interval / 2; for (let i = 0; i < n; i++) { axis.mCenteredEntries[i] = axis.mEntries[i] + offset; } } } } //# sourceMappingURL=AxisRenderer.js.map