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.

356 lines (355 loc) 12.6 kB
import { Matrix } from '@nativescript-community/ui-canvas'; import { Utils } from './Utils'; /** * Transformer class that contains all matrices and is responsible for * transforming values into pixels on the screen and backwards. * */ export class Transformer { constructor(viewPortHandler) { /** * matrix to map the values to the screen pixels */ this.mMatrixValueToPx = new Matrix(); /** * matrix for handling the different offsets of the chart */ this.mMatrixOffset = new Matrix(); this.mMBuffer1 = new Matrix(); this.mMBuffer2 = new Matrix(); this.mPixelToValueMatrixBuffer = new Matrix(); this.mViewPortHandler = viewPortHandler; } /** * Prepares the matrix that transforms values to pixels. Calculates the * scale factors from the charts size and offsets. * * @param xChartMin * @param deltaX * @param deltaY * @param yChartMin */ prepareMatrixValuePx(xChartMin, deltaX, deltaY, yChartMin) { let scaleX = this.mViewPortHandler.contentRect.width() / deltaX; let scaleY = this.mViewPortHandler.contentRect.height() / deltaY; if (!Number.isFinite(scaleX) || isNaN(scaleX)) { scaleX = 0; } if (!Number.isFinite(scaleY) || isNaN(scaleY)) { scaleY = 0; } // setup all matrices this.mMatrixValueToPx.reset(); this.mMatrixValueToPx.postTranslate(-xChartMin, -yChartMin); this.mMatrixValueToPx.postScale(scaleX, -scaleY); } /** * Prepares the matrix that contains all offsets. * * @param inverted */ prepareMatrixOffset(inverted) { this.mMatrixOffset.reset(); // offset.postTranslate(mOffsetLeft, getHeight() - this.mOffsetBottom); if (!inverted) this.mMatrixOffset.postTranslate(this.mViewPortHandler.offsetLeft, this.mViewPortHandler.chartHeight - this.mViewPortHandler.offsetBottom); else { this.mMatrixOffset.setTranslate(this.mViewPortHandler.offsetLeft, -this.mViewPortHandler.offsetTop); this.mMatrixOffset.postScale(1.0, -1.0); } } /** * Transforms an List of Entry into a let array containing the x and * y values transformed with all matrices for the SCATTERCHART. * * @param data * @return */ generateTransformedValuesScatter(dataSet, phaseX, phaseY, from, to) { const count = ((to - from) * phaseX + 1) * 2; if (!this.mValuePointsForGenerateTransformedValuesScatter || this.mValuePointsForGenerateTransformedValuesScatter.length !== count) { this.mValuePointsForGenerateTransformedValuesScatter = Utils.createArrayBuffer(count); } const valuePoints = this.mValuePointsForGenerateTransformedValuesScatter; const yKey = dataSet.yProperty; for (let j = 0; j < count; j += 2) { const index = j / 2 + from; const e = dataSet.getEntryForIndex(index); if (e) { valuePoints[j] = dataSet.getEntryXValue(e, index); valuePoints[j + 1] = e[yKey] * phaseY; } else { valuePoints[j] = 0; valuePoints[j + 1] = 0; } } const points = Utils.pointsFromBuffer(valuePoints); this.mapPoints(this.getValueToPixelMatrix(), points); return { points, count }; } /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the BUBBLECHART. * * @param data * @return */ generateTransformedValuesBubble(dataSet, phaseY, from, to) { const count = (to - from + 1) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; if (!this.mValuePointsForGenerateTransformedValuesScatter || this.mValuePointsForGenerateTransformedValuesBubble.length !== count) { this.mValuePointsForGenerateTransformedValuesBubble = Utils.createArrayBuffer(count); } const valuePoints = this.mValuePointsForGenerateTransformedValuesBubble; const yKey = dataSet.yProperty; for (let j = 0; j < count; j += 2) { const index = j / 2 + from; const e = dataSet.getEntryForIndex(index); if (e) { valuePoints[j] = dataSet.getEntryXValue(e, index); valuePoints[j + 1] = e[yKey] * phaseY; } else { valuePoints[j] = 0; valuePoints[j + 1] = 0; } } const points = Utils.pointsFromBuffer(valuePoints); this.mapPoints(this.getValueToPixelMatrix(), points); return { points, count }; } /** * Transforms an List of Entry into a let array containing the x and * y values transformed with all matrices for the BUBBLECHART. * * @param data * @return */ generateTransformedValues(dataSet, phaseX, phaseY, from, to) { const count = Math.ceil((to - from) * phaseX + 1) * 2; // let count = (to - from + 1) * 2; // Math.ceil((to - from) * phaseX) * 2; if (!this.mValuePointsForGenerateTransformedValues || this.mValuePointsForGenerateTransformedValues.length < count) { this.mValuePointsForGenerateTransformedValues = Utils.createArrayBuffer(count); } // let valuePoints = this.valuePointsForGenerateTransformedValues; const valuePoints = this.mValuePointsForGenerateTransformedValues; const yKey = dataSet.yProperty; for (let j = 0; j < count; j += 2) { const index = j / 2 + from; const e = dataSet.getEntryForIndex(index); if (e) { valuePoints[j] = dataSet.getEntryXValue(e, index); valuePoints[j + 1] = e[yKey] * phaseY; } else { valuePoints[j] = 0; valuePoints[j + 1] = 0; } } const points = Utils.pointsFromBuffer(valuePoints); this.mapPoints(this.getValueToPixelMatrix(), points); return { points, count }; } /** * Transforms an List of Entry into a let array containing the x and * y values transformed with all matrices for the CANDLESTICKCHART. * * @param data * @return */ generateTransformedValuesCandle(dataSet, phaseX, phaseY, from, to) { const count = ((to - from) * phaseX + 1) * 2; if (!this.mValuePointsForGenerateTransformedValuesCandle || this.mValuePointsForGenerateTransformedValuesCandle.length !== count) { this.mValuePointsForGenerateTransformedValuesCandle = Utils.createArrayBuffer(count); } const valuePoints = this.mValuePointsForGenerateTransformedValuesCandle; for (let j = 0, e, index; j < count; j += 2) { index = j / 2 + from; e = dataSet.getEntryForIndex(index); const highProperty = dataSet.highProperty; if (e) { valuePoints[j] = dataSet.getEntryXValue(e, index); valuePoints[j + 1] = e[highProperty] * phaseY; } else { valuePoints[j] = 0; valuePoints[j + 1] = 0; } } const points = Utils.pointsFromBuffer(valuePoints); this.mapPoints(this.getValueToPixelMatrix(), points); return { points, count }; } /** * transform a path with all the given matrices VERY IMPORTANT: keep order * to value-touch-offset * * @param path */ pathValueToPixel(path) { const tmp = this.getValueToPixelMatrix(); path.transform(tmp); // path.transform(this.mMatrixValueToPx); // path.transform(this.mViewPortHandler.getMatrixTouch()); // path.transform(this.mMatrixOffset); } /** * Transforms multiple paths will all matrices. * * @param paths */ pathValuesToPixel(paths) { for (let i = 0; i < paths.length; i++) { this.pathValueToPixel(paths[i]); } } mapPoints(matrix, pts) { if (__ANDROID__ && ArrayBuffer.isView(pts)) { matrix['mapPointsBuffer'](pts); } else { matrix.mapPoints(pts); } } /** * Transform an array of points with all matrices. VERY IMPORTANT: Keep * matrix order "value-touch-offset" when transforming. * * @param pts */ pointValuesToPixel(pts) { // this.mMatrixValueToPx.mapPoints(pts); // this.mViewPortHandler.getMatrixTouch().mapPoints(pts); // this.mMatrixOffset.mapPoints(pts); this.mapPoints(this.getValueToPixelMatrix(), pts); } /** * Transform a rectangle with all matrices. * * @param r */ rectValueToPixel(r) { // this.mMatrixValueToPx.mapRect(r); // this.mViewPortHandler.getMatrixTouch().mapRect(r); // this.mMatrixOffset.mapRect(r); const tmp = this.getValueToPixelMatrix(); tmp.mapRect(r); } /** * Transform a rectangle with all matrices with potential animation phases. * * @param r * @param phaseY */ rectToPixelPhase(r, phaseY) { // multiply the height of the rect with the phase if (r.top > 0) { r.top = r.bottom + phaseY * (r.top - r.bottom); } else { r.bottom = r.top + phaseY * (r.bottom - r.top); } this.rectValueToPixel(r); } rectToPixelPhaseHorizontal(r, phaseY) { // multiply the height of the rect with the phase if (r.left > 0) { r.left = r.right + phaseY * (r.left - r.right); } else { r.right = r.left + phaseY * (r.right - r.left); } this.rectValueToPixel(r); } /** * Transform a rectangle with all matrices with potential animation phases. * * @param r * @param phaseY */ rectValueToPixelHorizontal(r, phaseY) { // multiply the height of the rect with the phase if (phaseY !== undefined) { r.left *= phaseY; r.right *= phaseY; } this.rectValueToPixel(r); } /** * transforms multiple rects with all matrices * * @param rects */ rectValuesToPixel(rects) { const m = this.getValueToPixelMatrix(); for (let i = 0; i < rects.length; i++) m.mapRect(rects[i]); } /** * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) * into values on the chart. * * @param pixels */ pixelsToValue(pixels) { const tmp = this.getPixelToValueMatrix(); this.mapPoints(tmp, pixels); } /** * Returns a recyclable MPPointD instance. * returns the x and y values in the chart at the given touch point * (encapsulated in a MPPointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelForValues(...). * * @param x * @param y * @return */ getValuesByTouchPoint(x, y, outputPoint) { if (!outputPoint) { outputPoint = { x: 0, y: 0 }; } const buffer = Utils.getTempArray(2); buffer[0] = x; buffer[1] = y; this.pixelsToValue(buffer); outputPoint.x = buffer[0]; outputPoint.y = buffer[1]; return outputPoint; } /** * Returns a recyclable MPPointD instance. * Returns the x and y coordinates (pixels) for a given x and y value in the chart. * * @param x * @param y * @return */ getPixelForValues(x, y) { const buffer = Utils.getTempArray(2); buffer[0] = x; buffer[1] = y; this.pointValuesToPixel(buffer); const xPx = buffer[0]; const yPx = buffer[1]; return { x: xPx, y: yPx }; } getValueMatrix() { return this.mMatrixValueToPx; } getOffsetMatrix() { return this.mMatrixOffset; } getValueToPixelMatrix() { this.mMBuffer1.set(this.mMatrixValueToPx); this.mMBuffer1.postConcat(this.mViewPortHandler.mMatrixTouch); this.mMBuffer1.postConcat(this.mMatrixOffset); return this.mMBuffer1; } getPixelToValueMatrix() { this.getValueToPixelMatrix().invert(this.mMBuffer2); return this.mMBuffer2; } } //# sourceMappingURL=Transformer.js.map