package com.github.mikephil.charting.utils; import android.graphics.Matrix; import android.graphics.Path; import android.graphics.RectF; import com.github.mikephil.charting.data.BarData; import com.github.mikephil.charting.data.CandleEntry; import com.github.mikephil.charting.data.Entry; import java.util.List; /** * Transformer class that contains all matrices and is responsible for * transforming values into pixels on the screen and backwards. * * @author Philipp Jahoda */ public class Transformer { /** * matrix to map the values to the screen pixels */ protected Matrix mMatrixValueToPx = new Matrix(); /** * matrix for handling the different offsets of the chart */ protected Matrix mMatrixOffset = new Matrix(); protected ViewPortHandler mViewPortHandler; public Transformer(ViewPortHandler viewPortHandler) { 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 */ public void prepareMatrixValuePx(float xChartMin, float deltaX, float deltaY, float yChartMin) { float scaleX = (float) ((mViewPortHandler.contentWidth()) / deltaX); float scaleY = (float) ((mViewPortHandler.contentHeight()) / deltaY); // setup all matrices mMatrixValueToPx.reset(); mMatrixValueToPx.postTranslate(-xChartMin, -yChartMin); mMatrixValueToPx.postScale(scaleX, -scaleY); } /** * Prepares the matrix that contains all offsets. * * @param inverted */ public void prepareMatrixOffset(boolean inverted) { mMatrixOffset.reset(); // offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom); if (!inverted) mMatrixOffset.postTranslate(mViewPortHandler.offsetLeft(), mViewPortHandler.getChartHeight() - mViewPortHandler.offsetBottom()); else { mMatrixOffset .setTranslate(mViewPortHandler.offsetLeft(), -mViewPortHandler.offsetTop()); mMatrixOffset.postScale(1.0f, -1.0f); } // mMatrixOffset.set(offset); // mMatrixOffset.reset(); // // mMatrixOffset.postTranslate(mOffsetLeft, getHeight() - // mOffsetBottom); } /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the SCATTERCHART. * * @param entries * @return */ public float[] generateTransformedValuesScatter(List<? extends Entry> entries, float phaseY) { float[] valuePoints = new float[entries.size() * 2]; for (int j = 0; j < valuePoints.length; j += 2) { Entry e = entries.get(j / 2); if (e != null) { valuePoints[j] = e.getXIndex(); valuePoints[j + 1] = e.getVal() * phaseY; } } getValueToPixelMatrix().mapPoints(valuePoints); return valuePoints; } /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the BUBBLECHART. * * @param entries * @return */ public float[] generateTransformedValuesBubble(List<? extends Entry> entries, float phaseX, float phaseY, int from, int to) { final int count = (int) Math.ceil(to - from) * 2; // (int) Math.ceil((to - from) * phaseX) * 2; float[] valuePoints = new float[count]; for (int j = 0; j < count; j += 2) { Entry e = entries.get(j / 2 + from); if (e != null) { valuePoints[j] = (float) (e.getXIndex() - from) * phaseX + from; valuePoints[j + 1] = e.getVal() * phaseY; } } getValueToPixelMatrix().mapPoints(valuePoints); return valuePoints; } /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the LINECHART. * * @param entries * @return */ public float[] generateTransformedValuesLine(List<? extends Entry> entries, float phaseX, float phaseY, int from, int to) { final int count = (int) Math.ceil((to - from) * phaseX) * 2; float[] valuePoints = new float[count]; for (int j = 0; j < count; j += 2) { Entry e = entries.get(j / 2 + from); if (e != null) { valuePoints[j] = e.getXIndex(); valuePoints[j + 1] = e.getVal() * phaseY; } } getValueToPixelMatrix().mapPoints(valuePoints); return valuePoints; } /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the CANDLESTICKCHART. * * @param entries * @return */ public float[] generateTransformedValuesCandle(List<CandleEntry> entries, float phaseX, float phaseY, int from, int to) { final int count = (int) Math.ceil((to - from) * phaseX) * 2; float[] valuePoints = new float[count]; for (int j = 0; j < count; j += 2) { CandleEntry e = entries.get(j / 2 + from); if (e != null) { valuePoints[j] = e.getXIndex(); valuePoints[j + 1] = e.getHigh() * phaseY; } } getValueToPixelMatrix().mapPoints(valuePoints); return valuePoints; } /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the BARCHART. * * @param entries * @param dataSet the dataset index * @return */ public float[] generateTransformedValuesBarChart(List<? extends Entry> entries, int dataSet, BarData bd, float phaseY) { float[] valuePoints = new float[entries.size() * 2]; int setCount = bd.getDataSetCount(); float space = bd.getGroupSpace(); for (int j = 0; j < valuePoints.length; j += 2) { Entry e = entries.get(j / 2); int i = e.getXIndex(); // calculate the x-position, depending on datasetcount float x = e.getXIndex() + i * (setCount - 1) + dataSet + space * i + space / 2f; float y = e.getVal(); valuePoints[j] = x; valuePoints[j + 1] = y * phaseY; } getValueToPixelMatrix().mapPoints(valuePoints); return valuePoints; } /** * Transforms an List of Entry into a float array containing the x and * y values transformed with all matrices for the BARCHART. * * @param entries * @param dataSet the dataset index * @return */ public float[] generateTransformedValuesHorizontalBarChart(List<? extends Entry> entries, int dataSet, BarData bd, float phaseY) { float[] valuePoints = new float[entries.size() * 2]; int setCount = bd.getDataSetCount(); float space = bd.getGroupSpace(); for (int j = 0; j < valuePoints.length; j += 2) { Entry e = entries.get(j / 2); int i = e.getXIndex(); // calculate the x-position, depending on datasetcount float x = i + i * (setCount - 1) + dataSet + space * i + space / 2f; float y = e.getVal(); valuePoints[j] = y * phaseY; valuePoints[j + 1] = x; } getValueToPixelMatrix().mapPoints(valuePoints); return valuePoints; } /** * transform a path with all the given matrices VERY IMPORTANT: keep order * to value-touch-offset * * @param path */ public void pathValueToPixel(Path path) { path.transform(mMatrixValueToPx); path.transform(mViewPortHandler.getMatrixTouch()); path.transform(mMatrixOffset); } /** * Transforms multiple paths will all matrices. * * @param paths */ public void pathValuesToPixel(List<Path> paths) { for (int i = 0; i < paths.size(); i++) { pathValueToPixel(paths.get(i)); } } /** * Transform an array of points with all matrices. VERY IMPORTANT: Keep * matrix order "value-touch-offset" when transforming. * * @param pts */ public void pointValuesToPixel(float[] pts) { mMatrixValueToPx.mapPoints(pts); mViewPortHandler.getMatrixTouch().mapPoints(pts); mMatrixOffset.mapPoints(pts); } /** * Transform a rectangle with all matrices. * * @param r */ public void rectValueToPixel(RectF r) { mMatrixValueToPx.mapRect(r); mViewPortHandler.getMatrixTouch().mapRect(r); mMatrixOffset.mapRect(r); } /** * Transform a rectangle with all matrices with potential animation phases. * * @param r * @param phaseY */ public void rectValueToPixel(RectF r, float phaseY) { // multiply the height of the rect with the phase r.top *= phaseY; r.bottom *= phaseY; mMatrixValueToPx.mapRect(r); mViewPortHandler.getMatrixTouch().mapRect(r); mMatrixOffset.mapRect(r); } /** * Transform a rectangle with all matrices with potential animation phases. * * @param r */ public void rectValueToPixelHorizontal(RectF r) { mMatrixValueToPx.mapRect(r); mViewPortHandler.getMatrixTouch().mapRect(r); mMatrixOffset.mapRect(r); } /** * Transform a rectangle with all matrices with potential animation phases. * * @param r * @param phaseY */ public void rectValueToPixelHorizontal(RectF r, float phaseY) { // multiply the height of the rect with the phase r.left *= phaseY; r.right *= phaseY; mMatrixValueToPx.mapRect(r); mViewPortHandler.getMatrixTouch().mapRect(r); mMatrixOffset.mapRect(r); } /** * transforms multiple rects with all matrices * * @param rects */ public void rectValuesToPixel(List<RectF> rects) { Matrix m = getValueToPixelMatrix(); for (int i = 0; i < rects.size(); i++) m.mapRect(rects.get(i)); } /** * Transforms the given array of touch positions (pixels) (x, y, x, y, ...) * into values on the chart. * * @param pixels */ public void pixelsToValue(float[] pixels) { Matrix tmp = new Matrix(); // invert all matrixes to convert back to the original value mMatrixOffset.invert(tmp); tmp.mapPoints(pixels); mViewPortHandler.getMatrixTouch().invert(tmp); tmp.mapPoints(pixels); mMatrixValueToPx.invert(tmp); tmp.mapPoints(pixels); } /** * Returns the x and y values in the chart at the given touch point * (encapsulated in a PointD). This method transforms pixel coordinates to * coordinates / values in the chart. This is the opposite method to * getPixelsForValues(...). * * @param x * @param y * @return */ public PointD getValuesByTouchPoint(float x, float y) { // create an array of the touch-point float[] pts = new float[2]; pts[0] = x; pts[1] = y; pixelsToValue(pts); double xTouchVal = pts[0]; double yTouchVal = pts[1]; return new PointD(xTouchVal, yTouchVal); } public Matrix getValueMatrix() { return mMatrixValueToPx; } public Matrix getOffsetMatrix() { return mMatrixOffset; } private Matrix mMBuffer1 = new Matrix(); public Matrix getValueToPixelMatrix() { mMBuffer1.set(mMatrixValueToPx); mMBuffer1.postConcat(mViewPortHandler.mMatrixTouch); mMBuffer1.postConcat(mMatrixOffset); return mMBuffer1; } private Matrix mMBuffer2 = new Matrix(); public Matrix getPixelToValueMatrix() { getValueToPixelMatrix().invert(mMBuffer2); return mMBuffer2; } }