content: 
  package com.github.mikephil.charting.renderer;
  
  import android.graphics.Matrix;
  import android.graphics.Path;
  import android.graphics.RectF;
  import android.view.View;
  
  import com.github.mikephil.charting.data.BarData;
  import com.github.mikephil.charting.data.Entry;
  import com.github.mikephil.charting.interfaces.ChartInterface;
  import com.github.mikephil.charting.utils.Utils;
  
  import java.util.ArrayList;
  
  /**
   * 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();
  
      /** matrix used for touch events */
      protected final Matrix mMatrixTouch = new Matrix();
  
      /** if set to true, the y-axis is inverted and low values start at the top */
      private boolean mInvertYAxis = false;
  
      /** minimum scale value on the y-axis */
      private float mMinScaleY = 1f;
  
      /** minimum scale value on the x-axis */
      private float mMinScaleX = 1f;
  
      /** contains the current scale factor of the x-axis */
      private float mScaleX = 1f;
  
      /** contains the current scale factor of the y-axis */
      private float mScaleY = 1f;
  
      /** offset that allows the chart to be dragged over its bounds on the x-axis */
      private float mTransOffsetX = 0f;
  
      /** offset that allows the chart to be dragged over its bounds on the x-axis */
      private float mTransOffsetY = 0f;
  
      public Transformer() {
  
      }
  
      /**
       * Prepares the matrix that transforms values to pixels.
       * 
       * @param chart
       */
      public void prepareMatrixValuePx(ChartInterface chart) {
  
          float scaleX = (float) ((chart.getWidth() - chart.getOffsetRight() - chart
                  .getOffsetLeft()) / chart.getDeltaX());
          float scaleY = (float) ((chart.getHeight() - chart.getOffsetTop() - chart
                  .getOffsetBottom()) / chart.getDeltaY());
  
          // setup all matrices
          mMatrixValueToPx.reset();
          mMatrixValueToPx.postTranslate(0, -chart.getYChartMin());
          mMatrixValueToPx.postScale(scaleX, -scaleY);
      }
  
      /**
       * Prepares the matrix that contains all offsets.
       * 
       * @param chart
       */
      public void prepareMatrixOffset(ChartInterface chart) {
  
          mMatrixOffset.reset();
  
          // offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom);
  
          if (!mInvertYAxis)
              mMatrixOffset.postTranslate(chart.getOffsetLeft(),
                      chart.getHeight() - chart.getOffsetBottom());
          else {
              mMatrixOffset.setTranslate(chart.getOffsetLeft(), -chart.getOffsetTop());
              mMatrixOffset.postScale(1.0f, -1.0f);
          }
  
          // mMatrixOffset.set(offset);
  
          // mMatrixOffset.reset();
          //
          // mMatrixOffset.postTranslate(mOffsetLeft, getHeight() -
          // mOffsetBottom);
      }
  
      /**
       * Transforms an arraylist of Entry into a float array containing the x and
       * y values transformed with all matrices for the LINECHART or SCATTERCHART.
       * 
       * @param entries
       * @return
       */
      public float[] generateTransformedValuesLineScatter(ArrayList<? 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;
              }
          }
  
          pointValuesToPixel(valuePoints);
  
          return valuePoints;
      }
  
      /**
       * Transforms an arraylist 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(ArrayList<? 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);
  
              // calculate the x-position, depending on datasetcount
              float x = e.getXIndex() + (j / 2 * (setCount - 1)) + dataSet + 0.5f + space * (j / 2)
                      + space / 2f;
              float y = e.getVal();
  
              valuePoints[j] = x;
              valuePoints[j + 1] = y * phaseY;
          }
  
          pointValuesToPixel(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(mMatrixTouch);
          path.transform(mMatrixOffset);
      }
  
      /**
       * Transforms multiple paths will all matrices.
       * 
       * @param paths
       */
      public void pathValuesToPixel(ArrayList<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);
          mMatrixTouch.mapPoints(pts);
          mMatrixOffset.mapPoints(pts);
      }
  
      /**
       * Transform a rectangle with all matrices.
       * 
       * @param r
       */
      public void rectValueToPixel(RectF r) {
  
          mMatrixValueToPx.mapRect(r);
          mMatrixTouch.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
          if (r.top > 0)
              r.top *= phaseY;
          else
              r.bottom *= phaseY;
  
          mMatrixValueToPx.mapRect(r);
          mMatrixTouch.mapRect(r);
          mMatrixOffset.mapRect(r);
      }
  
      /**
       * transforms multiple rects with all matrices
       * 
       * @param rects
       */
      public void rectValuesToPixel(ArrayList<RectF> rects) {
  
          for (int i = 0; i < rects.size(); i++)
              rectValueToPixel(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);
  
          mMatrixTouch.invert(tmp);
          tmp.mapPoints(pixels);
  
          mMatrixValueToPx.invert(tmp);
          tmp.mapPoints(pixels);
      }
  
      /**
       * ################ ################ ################ ################
       */
      /** CODE BELOW THIS RELATED TO SCALING AND GESTURES */
  
      /**
       * Zooms in by 1.4f, x and y are the coordinates (in pixels) of the zoom
       * center.
       * 
       * @param x
       * @param y
       */
      public Matrix zoomIn(float x, float y) {
  
          Matrix save = new Matrix();
          save.set(mMatrixTouch);
  
          save.postScale(1.4f, 1.4f, x, y);
  
          return save;
      }
  
      /**
       * Zooms out by 0.7f, x and y are the coordinates (in pixels) of the zoom
       * center.
       */
      public Matrix zoomOut(float x, float y) {
  
          Matrix save = new Matrix();
          save.set(mMatrixTouch);
  
          save.postScale(0.7f, 0.7f, x, y);
  
          return save;
      }
  
      /**
       * Zooms in or out by the given scale factor. x and y are the coordinates
       * (in pixels) of the zoom center.
       * 
       * @param scaleX if < 1f --> zoom out, if > 1f --> zoom in
       * @param scaleY if < 1f --> zoom out, if > 1f --> zoom in
       * @param x
       * @param y
       */
      public Matrix zoom(float scaleX, float scaleY, float x, float y) {
  
          Matrix save = new Matrix();
          save.set(mMatrixTouch);
  
          // Log.i(LOG_TAG, "Zooming, x: " + x + ", y: " + y);
  
          save.postScale(scaleX, scaleY, x, y);
  
          return save;
      }
  
      /**
       * Resets all zooming and dragging and makes the chart fit exactly it's
       * bounds.
       */
      public Matrix fitScreen() {
  
          Matrix save = new Matrix();
          save.set(mMatrixTouch);
  
          float[] vals = new float[9];
  
          save.getValues(vals);
  
          // reset all translations and scaling
          vals[Matrix.MTRANS_X] = 0f;
          vals[Matrix.MTRANS_Y] = 0f;
          vals[Matrix.MSCALE_X] = 1f;
          vals[Matrix.MSCALE_Y] = 1f;
  
          save.setValues(vals);
  
          return save;
      }
  
      /**
       * If this is set to true, the y-axis is inverted which means that low
       * values are on top of the chart, high values on bottom.
       * 
       * @param enabled
       */
      public void setInvertYAxisEnabled(boolean enabled) {
          mInvertYAxis = enabled;
      }
  
      /**
       * If this returns true, the y-axis is inverted.
       * 
       * @return
       */
      public boolean isInvertYAxisEnabled() {
          return mInvertYAxis;
      }
  
      /**
       * Centers the viewport around the specified position (x-index and y-value)
       * in the chart. Centering the viewport outside the bounds of the chart is
       * not possible. Makes most sense in combination with the
       * setScaleMinima(...) method.
       * 
       * @param pts the position to center view viewport to
       * @param chart
       * @return save
       */
      public synchronized void centerViewPort(final float[] pts, final ChartInterface chart) {
  
          final View v = chart.getChartView();
  
          v.post(new Runnable() {
  
              @Override
              public void run() {
                  Matrix save = new Matrix();
                  save.set(mMatrixTouch);
  
                  pointValuesToPixel(pts);
  
                  final float x = pts[0] - chart.getOffsetLeft();
                  final float y = pts[1] - chart.getOffsetTop();
  
                  save.postTranslate(-x, -y);
  
                  refresh(save, chart);
              }
          });
      }
  
      /**
       * call this method to refresh the graph with a given matrix
       * 
       * @param newMatrix
       * @return
       */
      public Matrix refresh(Matrix newMatrix, ChartInterface chart) {
  
          mMatrixTouch.set(newMatrix);
  
          // make sure scale and translation are within their bounds
          limitTransAndScale(mMatrixTouch, chart.getContentRect());
  
          chart.getChartView().invalidate();
  
          newMatrix.set(mMatrixTouch);
          return newMatrix;
      }
  
      /**
       * limits the maximum scale and X translation of the given matrix
       * 
       * @param matrix
       */
      private void limitTransAndScale(Matrix matrix, RectF content) {
  
          float[] vals = new float[9];
          matrix.getValues(vals);
  
          float curTransX = vals[Matrix.MTRANS_X];
          float curScaleX = vals[Matrix.MSCALE_X];
  
          float curTransY = vals[Matrix.MTRANS_Y];
          float curScaleY = vals[Matrix.MSCALE_Y];
  
          // min scale-x is 1f
          mScaleX = Math.max(mMinScaleX, curScaleX);
  
          // min scale-y is 1f
          mScaleY = Math.max(mMinScaleY, curScaleY);
  
          float width = 0f;
          float height = 0f;
  
          if (content != null) {
              width = content.width();
              height = content.height();
          }
  
          float maxTransX = -width * (mScaleX - 1f);
          float newTransX = Math.min(Math.max(curTransX, maxTransX - mTransOffsetX), mTransOffsetX);
  
          // if(curScaleX < mMinScaleX) {
          // newTransX = (-width * (mScaleX - 1f)) / 2f;
          // }
  
          float maxTransY = height * (mScaleY - 1f);
          float newTransY = Math.max(Math.min(curTransY, maxTransY + mTransOffsetY), -mTransOffsetY);
  
          // if(curScaleY < mMinScaleY) {
          // newTransY = (height * (mScaleY - 1f)) / 2f;
          // }
  
          vals[Matrix.MTRANS_X] = newTransX;
          vals[Matrix.MSCALE_X] = mScaleX;
  
          vals[Matrix.MTRANS_Y] = newTransY;
          vals[Matrix.MSCALE_Y] = mScaleY;
  
          matrix.setValues(vals);
      }
  
      /**
       * Sets the minimum scale values for both axes. This limits the extent to
       * which the user can zoom-out.
       * 
       * @param scaleXmin
       * @param scaleYmin
       */
      public void setScaleMinima(float scaleXmin, float scaleYmin, ChartInterface chart) {
  
          if (scaleXmin < 1f)
              scaleXmin = 1f;
          if (scaleYmin < 1f)
              scaleYmin = 1f;
  
          mMinScaleX = scaleXmin;
          mMinScaleY = scaleYmin;
  
          Matrix save = zoom(mMinScaleX, mMinScaleY, 0f, 0f);
          refresh(save, chart);
      }
  
      // /**
      // * transforms the given rect objects with the touch matrix only
      // *
      // * @param paths
      // */
      // public void transformRectsTouch(ArrayList<RectF> rects) {
      // for (int i = 0; i < rects.size(); i++) {
      // mMatrixTouch.mapRect(rects.get(i));
      // }
      // }
      //
      // /**
      // * transforms the given path objects with the touch matrix only
      // *
      // * @param paths
      // */
      // public void transformPathsTouch(ArrayList<Path> paths) {
      // for (int i = 0; i < paths.size(); i++) {
      // paths.get(i).transform(mMatrixTouch);
      // }
      // }
  
      public Matrix getTouchMatrix() {
          return mMatrixTouch;
      }
  
      public Matrix getValueMatrix() {
          return mMatrixValueToPx;
      }
  
      public Matrix getOffsetMatrix() {
          return mMatrixOffset;
      }
  
      /**
       * returns the current x-scale factor
       */
      public float getScaleX() {
          return mScaleX;
      }
  
      /**
       * returns the current y-scale factor
       */
      public float getScaleY() {
          return mScaleY;
      }
  
      /**
       * if the chart is fully zoomed out, return true
       * 
       * @return
       */
      public boolean isFullyZoomedOut() {
  
          if (isFullyZoomedOutX() && isFullyZoomedOutY())
              return true;
          else
              return false;
      }
  
      /**
       * Returns true if the chart is fully zoomed out on it's y-axis (vertical).
       * 
       * @return
       */
      public boolean isFullyZoomedOutY() {
          if (mScaleY > mMinScaleY || mMinScaleY > 1f)
              return false;
          else
              return true;
      }
  
      /**
       * Returns true if the chart is fully zoomed out on it's x-axis
       * (horizontal).
       * 
       * @return
       */
      public boolean isFullyZoomedOutX() {
          if (mScaleX > mMinScaleX || mMinScaleX > 1f)
              return false;
          else
              return true;
      }
  
      /**
       * Set an offset in dp that allows the user to drag the chart over it's
       * bounds on the x-axis.
       * 
       * @param offset
       */
      public void setDragOffsetX(float offset) {
          mTransOffsetX = Utils.convertDpToPixel(offset);
      }
  
      /**
       * Set an offset in dp that allows the user to drag the chart over it's
       * bounds on the y-axis.
       * 
       * @param offset
       */
      public void setDragOffsetY(float offset) {
          mTransOffsetY = Utils.convertDpToPixel(offset);
      }
  
      /**
       * Returns true if both drag offsets (x and y) are zero or smaller.
       * 
       * @return
       */
      public boolean hasNoDragOffset() {
          return mTransOffsetX <= 0 && mTransOffsetY <= 0 ? true : false;
      }
  }
  