@Override
  public void drawData(Canvas c) {

    BubbleData bubbleData = mChart.getBubbleData();

    for (IBubbleDataSet set : bubbleData.getDataSets()) {

      if (set.isVisible() && set.getEntryCount() > 0) drawDataSet(c, set);
    }
  }
  @Override
  public void drawHighlighted(Canvas c, Highlight[] indices) {

    BubbleData bubbleData = mChart.getBubbleData();

    float phaseX = mAnimator.getPhaseX();
    float phaseY = mAnimator.getPhaseY();

    for (Highlight indice : indices) {

      IBubbleDataSet dataSet = bubbleData.getDataSetByIndex(indice.getDataSetIndex());

      if (dataSet == null || !dataSet.isHighlightEnabled()) continue;

      BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX);
      BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX);

      int minx = dataSet.getEntryIndex(entryFrom);
      int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount());

      final BubbleEntry entry = (BubbleEntry) bubbleData.getEntryForHighlight(indice);
      if (entry == null || entry.getXIndex() != indice.getXIndex()) continue;

      Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());

      sizeBuffer[0] = 0f;
      sizeBuffer[2] = 1f;

      trans.pointValuesToPixel(sizeBuffer);

      // calcualte the full width of 1 step on the x-axis
      final float maxBubbleWidth = Math.abs(sizeBuffer[2] - sizeBuffer[0]);
      final float maxBubbleHeight =
          Math.abs(mViewPortHandler.contentBottom() - mViewPortHandler.contentTop());
      final float referenceSize = Math.min(maxBubbleHeight, maxBubbleWidth);

      pointBuffer[0] = (float) (entry.getXIndex() - minx) * phaseX + (float) minx;
      pointBuffer[1] = (float) (entry.getVal()) * phaseY;
      trans.pointValuesToPixel(pointBuffer);

      float shapeHalf = getShapeSize(entry.getSize(), dataSet.getMaxSize(), referenceSize) / 2f;

      if (!mViewPortHandler.isInBoundsTop(pointBuffer[1] + shapeHalf)
          || !mViewPortHandler.isInBoundsBottom(pointBuffer[1] - shapeHalf)) continue;

      if (!mViewPortHandler.isInBoundsLeft(pointBuffer[0] + shapeHalf)) continue;

      if (!mViewPortHandler.isInBoundsRight(pointBuffer[0] - shapeHalf)) break;

      if (indice.getXIndex() < minx || indice.getXIndex() >= maxx) continue;

      final int originalColor = dataSet.getColor(entry.getXIndex());

      Color.RGBToHSV(
          Color.red(originalColor),
          Color.green(originalColor),
          Color.blue(originalColor),
          _hsvBuffer);
      _hsvBuffer[2] *= 0.5f;
      final int color = Color.HSVToColor(Color.alpha(originalColor), _hsvBuffer);

      mHighlightPaint.setColor(color);
      mHighlightPaint.setStrokeWidth(dataSet.getHighlightCircleWidth());
      c.drawCircle(pointBuffer[0], pointBuffer[1], shapeHalf, mHighlightPaint);
    }
  }
  @Override
  public void drawValues(Canvas c) {

    BubbleData bubbleData = mChart.getBubbleData();

    if (bubbleData == null) return;

    // if values are drawn
    if (bubbleData.getYValCount()
        < (int) (Math.ceil((float) (mChart.getMaxVisibleCount()) * mViewPortHandler.getScaleX()))) {

      final List<IBubbleDataSet> dataSets = bubbleData.getDataSets();

      float lineHeight = Utils.calcTextHeight(mValuePaint, "1");

      for (int i = 0; i < dataSets.size(); i++) {

        IBubbleDataSet dataSet = dataSets.get(i);

        if (!dataSet.isDrawValuesEnabled() || dataSet.getEntryCount() == 0) continue;

        // apply the text-styling defined by the DataSet
        applyValueTextStyle(dataSet);

        final float phaseX = mAnimator.getPhaseX();
        final float phaseY = mAnimator.getPhaseY();

        BubbleEntry entryFrom = dataSet.getEntryForXIndex(mMinX);
        BubbleEntry entryTo = dataSet.getEntryForXIndex(mMaxX);

        int minx = dataSet.getEntryIndex(entryFrom);
        int maxx = Math.min(dataSet.getEntryIndex(entryTo) + 1, dataSet.getEntryCount());

        final float[] positions =
            mChart
                .getTransformer(dataSet.getAxisDependency())
                .generateTransformedValuesBubble(dataSet, phaseX, phaseY, minx, maxx);

        for (int j = 0; j < positions.length; j += 2) {

          final float alpha = phaseX == 1 ? phaseY : phaseX;
          int valueTextColor = dataSet.getValueTextColor(j / 2);
          valueTextColor =
              Color.argb(
                  Math.round(255.f * alpha),
                  Color.red(valueTextColor),
                  Color.green(valueTextColor),
                  Color.blue(valueTextColor));

          float x = positions[j];
          float y = positions[j + 1];

          if (!mViewPortHandler.isInBoundsRight(x)) break;

          if ((!mViewPortHandler.isInBoundsLeft(x) || !mViewPortHandler.isInBoundsY(y))) continue;

          BubbleEntry entry = dataSet.getEntryForIndex(j / 2 + minx);

          drawValue(
              c,
              dataSet.getValueFormatter(),
              entry.getSize(),
              entry,
              i,
              x,
              y + (0.5f * lineHeight),
              valueTextColor);
        }
      }
    }
  }