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.

424 lines (423 loc) 14.6 kB
import { ObservableArray } from '@nativescript/core'; import { BaseDataSet } from './BaseDataSet'; import { Utils } from '../utils/Utils'; /** * Determines how to round DataSet index values for * {@link DataSet#getEntryIndex(float, float, Rounding)} DataSet.getEntryIndex()} * when an exact x-index is not found. */ export var Rounding; (function (Rounding) { Rounding[Rounding["UP"] = 0] = "UP"; Rounding[Rounding["DOWN"] = 1] = "DOWN"; Rounding[Rounding["CLOSEST"] = 2] = "CLOSEST"; })(Rounding || (Rounding = {})); /** * The DataSet class represents one group or type of entries (Entry) in the * Chart that belong together. It is designed to logically separate different * groups of values inside the Chart (e.g. the values for a specific line in the * LineChart, or the values of a specific group of bars in the BarChart). * */ export class DataSet extends BaseDataSet { /** * Creates a new DataSet object with the given values (entries) it represents. Also, a * label that describes the DataSet can be specified. The label can also be * used to retrieve the DataSet from a ChartData object. * * @param values * @param label */ constructor(values, label, xProperty, yProperty) { super(label, xProperty, yProperty); /** * the entries that this DataSet represents / holds together */ this.mValues = null; /** * maximum y-value in the value array */ this.mYMax = -Infinity; /** * minimum y-value in the value array */ this.mYMin = Infinity; /** * maximum x-value in the value array */ this.mXMax = -Infinity; /** * minimum x-value in the value array */ this.mXMin = Infinity; this.mCanCalculateMinMax = true; this.mValues = values; this.updateGetEntryForIndex(); } toString() { return `${this.constructor.name}[${this.label}]`; } init() { if (!this.mValues) this.mValues = []; this.updateGetEntryForIndex(); if (this.mValues.length > 0) { for (let index = 0, e; index < this.mValues.length; index++) { e = this.getEntryForIndex(index); this.initEntryData(e); this.calcMinMaxForEntry(e, index); } } } batchEntryOperations(cb) { this.mCanCalculateMinMax = false; cb(); this.mCanCalculateMinMax = true; this.calcMinMax(); } calcMinMax() { if (!this.mCanCalculateMinMax) { return; } this.mYMax = -Infinity; this.mYMin = Infinity; this.mXMax = -Infinity; this.mXMin = Infinity; if (!this.mValues || this.mValues.length === 0) return; for (let index = 0, e; index < this.mValues.length; index++) { e = this.getEntryForIndex(index); this.calcMinMaxForEntry(e, index); } } initEntryData(e) { } calcMinMaxYRange(fromX, toX) { if (!this.mValues || this.mValues.length === 0) return; this.mYMax = -Infinity; this.mYMin = Infinity; const indexFrom = this.getEntryIndexForXValue(fromX, NaN, Rounding.DOWN); const indexTo = this.getEntryIndexForXValue(toX, NaN, Rounding.UP); for (let i = indexFrom; i <= indexTo; i++) { // only recalculate y this.calcMinMaxY(this.getEntryForIndex(i)); } } /** * Updates the min and max x and y value of this DataSet based on the given Entry. * * @param e */ calcMinMaxForEntry(e, index) { if (!e) return; this.calcMinMaxX(e, index); this.calcMinMaxY(e, index); } calcMinMaxX(e, index) { if (!e) { if (!this.mValues || this.mValues.length === 0) return; this.mYMax = -Infinity; this.mYMin = Infinity; this.mXMax = -Infinity; this.mXMin = Infinity; for (let index = 0, e; index < this.mValues.length; index++) { e = this.getEntryForIndex(index); this.calcMinMaxForEntry(e, index); } } else { const x = this.getEntryXValue(e, index); this.mXMin = Math.min(x, this.mXMin); this.mXMax = Math.max(x, this.mXMax); } } calcMinMaxY(e, index) { const y = e[this.yProperty] || 0; this.mYMin = Math.min(y, this.mYMin); this.mYMax = Math.max(y, this.mYMax); } getInternalValues() { return this.mValues; } get entryCount() { return this.getInternalValues().length; } /** * Returns the array of entries that this DataSet represents. */ get values() { return this.mValues; } /** * Sets the array of entries that this DataSet represents, and calls notifyDataSetChanged() */ set values(values) { this.mValues = values; this.updateGetEntryForIndex(); this.notifyDataSetChanged(); } get yMin() { return this.mYMin - this.spaceBottom; } get yMax() { return this.mYMax + this.spaceTop; } get xMin() { return this.mXMin; } get xMax() { return this.mXMax; } addEntryOrdered(e) { if (!e) return; if (!this.mValues) { this.mValues = []; } let addedIndex = this.mValues.length; if (this.mValues.length > 0 && this.xProperty && this.getEntryForIndex(this.mValues.length - 1) > e[this.xProperty]) { addedIndex = this.getEntryIndexForXValue(e[this.xProperty], this.getEntryYValue(e), Rounding.UP); this.mValues.splice(addedIndex, 0, e); } else { this.mValues.push(e); } this.calcMinMaxForEntry(e, addedIndex); } clear() { this.mValues = []; this.notifyDataSetChanged(); this.updateGetEntryForIndex(); } addEntry(e) { if (!e) return false; let values = this.mValues; if (!values) { values = this.mValues = []; } const length = values.length; this.calcMinMaxForEntry(e, length); // add the entry values.push(e); return true; } removeEntry(e) { if (!e) return false; if (!this.mValues) return false; // remove the entry const index = this.mValues.indexOf(e); if (index >= 0) { this.mValues.splice(index, 1); this.calcMinMax(); } return index >= 0; } removeEntryAtIndex(index) { if (!this.mValues) return false; if (index >= 0 && index < this.mValues.length) { this.mValues.splice(index, 1); this.calcMinMax(); } return index >= 0; } getEntryIndex(e) { return this.getInternalValues().indexOf(e); } getEntryYValue(e) { return e[this.yProperty]; } getEntryForXValue(xValue, closestToY, rounding = Rounding.CLOSEST) { const index = this.getEntryIndexForXValue(xValue, closestToY, rounding); if (index > -1) return this.getEntryForIndex(index); return null; } getEntryAndIndexForXValue(xValue, closestToY, rounding = Rounding.CLOSEST) { const index = this.getEntryIndexForXValue(xValue, closestToY, rounding); if (index > -1) return { entry: this.getEntryForIndex(index), index }; return null; } updateGetEntryForIndex() { const internalValues = this.getInternalValues(); if (internalValues instanceof ObservableArray) { this.getEntryForIndex = function (index) { return internalValues.getItem(index); }; } else { this.getEntryForIndex = function (index) { return internalValues[index]; }; } } getEntryForIndex(index) { const internalValues = this.getInternalValues(); return internalValues instanceof ObservableArray ? internalValues.getItem(index) : internalValues[index]; } getEntryIndexForXValue(xValue, closestToY, rounding) { const values = this.getInternalValues(); if (!values || values.length === 0) return -1; let low = 0; let high = values.length - 1; let closest = high; let m, e, e1; while (low < high) { m = Math.floor((low + high) / 2); e = Utils.getArrayItem(values, m); e1 = Utils.getArrayItem(values, m + 1); const d1 = this.getEntryXValue(e, m) - xValue, d2 = this.getEntryXValue(e1, m + 1) - xValue, ad1 = Math.abs(d1), ad2 = Math.abs(d2); if (ad2 < ad1) { // [m + 1] is closer to xValue // Search in an higher place low = m + 1; } else if (ad1 < ad2) { // [m] is closer to xValue // Search in a lower place high = m; } else { // We have multiple sequential x-value with same distance if (d1 >= 0.0) { // Search in a lower place high = m; } else if (d1 < 0.0) { // Search in an higher place low = m + 1; } } closest = high; } if (closest !== -1) { let e = Utils.getArrayItem(values, closest); const closestXValue = this.getEntryXValue(e, closest); if (rounding === Rounding.UP) { // If rounding up, and found x-value is lower than specified x, and we can go upper... if (closestXValue < xValue && closest < values.length - 1) { ++closest; } } else if (rounding === Rounding.DOWN) { // If rounding down, and found x-value is upper than specified x, and we can go lower... if (closestXValue > xValue && closest > 0) { --closest; } } // Search by closest to y-value if (closest >= 1 && !isNaN(closestToY)) { e = Utils.getArrayItem(values, closest - 1); if (e) { xValue = this.getEntryXValue(e, closest - 1); while (closest > 0 && xValue === closestXValue) closest -= 1; let closestYValue = this.getEntryYValue(Utils.getArrayItem(values, closest)); let closestYIndex = closest; // eslint-disable-next-line no-constant-condition while (true) { closest += 1; if (closest >= values.length) break; e = Utils.getArrayItem(values, closest); if (!e) break; xValue = this.getEntryXValue(e, closest); if (xValue !== closestXValue) break; if (Math.abs(this.getEntryYValue(e) - closestToY) < Math.abs(closestYValue - closestToY)) { closestYValue = closestToY; closestYIndex = closest; } } closest = closestYIndex; } } } return closest; } getEntriesForXValue(xValue) { const entries = []; const values = this.getInternalValues(); let low = 0; let high = values.length - 1; let m, e, e1, mXValue; while (low <= high) { m = Math.floor((high + low) / 2); e = Utils.getArrayItem(values, m); const yVal = this.getEntryYValue(e); mXValue = this.getEntryXValue(e, m); // if we have a match if (yVal !== null && yVal !== undefined && xValue === mXValue) { while (m > 0 && this.getEntryXValue(Utils.getArrayItem(values, m - 1), m - 1) === xValue) m--; high = values.length; // loop over all "equal" entries for (; m < high; m++) { e = Utils.getArrayItem(values, m); mXValue = this.getEntryXValue(e, m); if (mXValue === xValue) { entries.push(e); } else { break; } } break; } else { if (xValue > mXValue) low = m + 1; else high = m - 1; } } return entries; } getEntriesAndIndexesForXValue(xValue) { const entries = []; const values = this.getInternalValues(); let low = 0; let high = values.length - 1; let entry, mXValue; while (low <= high) { let m = Math.floor((high + low) / 2); entry = Utils.getArrayItem(values, m); const yVal = this.getEntryYValue(entry); mXValue = this.getEntryXValue(entry, m); // if we have a match if (yVal !== null && yVal !== undefined && xValue === mXValue) { while (m > 0 && this.getEntryXValue(Utils.getArrayItem(values, m - 1), m - 1) === xValue) m--; high = values.length; // loop over all "equal" entries for (; m < high; m++) { entry = Utils.getArrayItem(values, m); mXValue = this.getEntryXValue(entry, m); if (mXValue === xValue) { entries.push({ entry, index: m }); } else { break; } } break; } else { if (xValue > mXValue) low = m + 1; else high = m - 1; } } return entries; } } //# sourceMappingURL=DataSet.js.map