@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
JavaScript
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