content: 
  package com.github.mikephil.charting.data.filter;
  
  import com.github.mikephil.charting.data.Entry;
  
  import java.util.ArrayList;
  
  /**
   * Implemented according to Wiki-Pseudocode {@link}
   * http://en.wikipedia.org/wiki/Ramer�Douglas�Peucker_algorithm
   * 
   * @author Philipp Baldauf & Phliipp Jahoda
   */
  public class Approximator {
  
      /** the type of filtering algorithm to use */
      private ApproximatorType mType = ApproximatorType.DOUGLAS_PEUCKER;
  
      /** the tolerance to be filtered with */
      private double mTolerance = 0;
  
      private float mScaleRatio = 1f;
      private float mDeltaRatio = 1f;
  
      /**
       * array that contains "true" on all indices that will be kept after
       * filtering
       */
      private boolean[] keep;
  
      /** enums for the different types of filtering algorithms */
      public enum ApproximatorType {
          NONE, DOUGLAS_PEUCKER
      }
  
      /**
       * Initializes the approximator with type NONE
       */
      public Approximator() {
          this.mType = ApproximatorType.NONE;
      }
  
      /**
       * Initializes the approximator with the given type and tolerance. If
       * toleranec <= 0, no filtering will be done.
       * 
       * @param type
       */
      public Approximator(ApproximatorType type, double tolerance) {
          setup(type, tolerance);
      }
  
      /**
       * sets type and tolerance, if tolerance <= 0, no filtering will be done
       * 
       * @param type
       * @param tolerance
       */
      public void setup(ApproximatorType type, double tolerance) {
          mType = type;
          mTolerance = tolerance;
      }
  
      /**
       * sets the tolerance for the Approximator. When using the
       * Douglas-Peucker-Algorithm, the tolerance is an angle in degrees, that
       * will trigger the filtering.
       */
      public void setTolerance(double tolerance) {
          mTolerance = tolerance;
      }
  
      /**
       * Sets the filtering algorithm that should be used.
       * 
       * @param type
       */
      public void setType(ApproximatorType type) {
          this.mType = type;
      }
  
      /**
       * Sets the ratios for x- and y-axis, as well as the ratio of the scale
       * levels
       * 
       * @param deltaRatio
       * @param scaleRatio
       */
      public void setRatios(float deltaRatio, float scaleRatio) {
          mDeltaRatio = deltaRatio;
          mScaleRatio = scaleRatio;
      }
  
      /**
       * Filters according to type. Uses the pre set set tolerance
       * 
       * @param points the points to filter
       * @return
       */
      public ArrayList<Entry> filter(ArrayList<Entry> points) {
          return filter(points, mTolerance);
      }
  
      /**
       * Filters according to type.
       * 
       * @param points the points to filter
       * @param tolerance the angle in degrees that will trigger the filtering
       * @return
       */
      public ArrayList<Entry> filter(ArrayList<Entry> points, double tolerance) {
  
          if (tolerance <= 0)
              return points;
  
          keep = new boolean[points.size()];
  
          switch (mType) {
              case DOUGLAS_PEUCKER:
                  return reduceWithDouglasPeuker(points, tolerance);
              case NONE:
                  return points;
              default:
                  return points;
          }
      }
  
      /**
       * uses the douglas peuker algorithm to reduce the given arraylist of
       * entries
       * 
       * @param entries
       * @param epsilon
       * @return
       */
      private ArrayList<Entry> reduceWithDouglasPeuker(ArrayList<Entry> entries, double epsilon) {
          // if a shape has 2 or less points it cannot be reduced
          if (epsilon <= 0 || entries.size() < 3) {
              return entries;
          }
  
          // first and last always stay
          keep[0] = true;
          keep[entries.size() - 1] = true;
  
          // first and last entry are entry point to recursion
          algorithmDouglasPeucker(entries, epsilon, 0, entries.size() - 1);
  
          // create a new array with series, only take the kept ones
          ArrayList<Entry> reducedEntries = new ArrayList<Entry>();
          for (int i = 0; i < entries.size(); i++) {
              if (keep[i]) {
                  Entry curEntry = entries.get(i);
                  reducedEntries.add(new Entry(curEntry.getVal(), curEntry.getXIndex()));
              }
          }
          return reducedEntries;
      }
  
      /**
       * apply the Douglas-Peucker-Reduction to an ArrayList of Entry with a given
       * epsilon (tolerance)
       * 
       * @param entries
       * @param epsilon as y-value
       * @param start
       * @param end
       */
      private void algorithmDouglasPeucker(ArrayList<Entry> entries, double epsilon, int start,
              int end) {
          if (end <= start + 1) {
              // recursion finished
              return;
          }
  
          // find the greatest distance between start and endpoint
          int maxDistIndex = 0;
          double distMax = 0;
  
          Entry firstEntry = entries.get(start);
          Entry lastEntry = entries.get(end);
  
          for (int i = start + 1; i < end; i++) {
              double dist = calcAngleBetweenLines(firstEntry, lastEntry, firstEntry, entries.get(i));
  
              // keep the point with the greatest distance
              if (dist > distMax) {
                  distMax = dist;
                  maxDistIndex = i;
              }
          }
  
          // Log.i("maxangle", "" + distMax);
  
          if (distMax > epsilon) {
              // keep max dist point
              keep[maxDistIndex] = true;
  
              // recursive call
              algorithmDouglasPeucker(entries, epsilon, start, maxDistIndex);
              algorithmDouglasPeucker(entries, epsilon, maxDistIndex, end);
          } // else don't keep the point...
      }
  
      /**
       * calculate the distance between a line between two entries and an entry
       * (point)
       * 
       * @param startEntry line startpoint
       * @param endEntry line endpoint
       * @param entryPoint the point to which the distance is measured from the
       *            line
       * @return
       */
      public double calcPointToLineDistance(Entry startEntry, Entry endEntry, Entry entryPoint) {
  
          float xDiffEndStart = (float) endEntry.getXIndex() - (float) startEntry.getXIndex();
          float xDiffEntryStart = (float) entryPoint.getXIndex() - (float) startEntry.getXIndex();
  
          double normalLength = Math.sqrt((xDiffEndStart)
                  * (xDiffEndStart)
                  + (endEntry.getVal() - startEntry.getVal())
                  * (endEntry.getVal() - startEntry.getVal()));
          return Math.abs((xDiffEntryStart)
                  * (endEntry.getVal() - startEntry.getVal())
                  - (entryPoint.getVal() - startEntry.getVal())
                  * (xDiffEndStart))
                  / normalLength;
      }
  
      /**
       * Calculates the angle between two given lines. The provided Entry objects
       * mark the starting and end points of the lines.
       * 
       * @param start1
       * @param end1
       * @param start2
       * @param end2
       * @return
       */
      public double calcAngleBetweenLines(Entry start1, Entry end1, Entry start2, Entry end2) {
  
          double angle1 = calcAngleWithRatios(start1, end1);
          double angle2 = calcAngleWithRatios(start2, end2);
  
          return Math.abs(angle1 - angle2);
      }
  
      /**
       * calculates the angle between two Entries (points) in the chart taking
       * ratios into consideration
       * 
       * @param p1
       * @param p2
       * @return
       */
      public double calcAngleWithRatios(Entry p1, Entry p2) {
  
          float dx = p2.getXIndex() * mDeltaRatio - p1.getXIndex() * mDeltaRatio;
          float dy = p2.getVal() * mScaleRatio - p1.getVal() * mScaleRatio;
          double angle = Math.atan2(dy, dx) * 180.0 / Math.PI;
  
          return angle;
      }
  
      /**
       * calculates the angle between two Entries (points) in the chart
       * 
       * @param p1
       * @param p2
       * @return
       */
      public double calcAngle(Entry p1, Entry p2) {
  
          float dx = p2.getXIndex() - p1.getXIndex();
          float dy = p2.getVal() - p1.getVal();
          double angle = Math.atan2(dy, dx) * 180.0 / Math.PI;
  
          return angle;
      }
  }
  