| |
| package com.github.mikephil.charting.utils; |
| |
| import android.graphics.Matrix; |
| import android.graphics.PointF; |
| import android.graphics.RectF; |
| |
| import com.github.mikephil.charting.charts.Chart; |
| |
| public class ViewPortHandler { |
| |
| /** matrix used for touch events */ |
| protected final Matrix mMatrixTouch = new Matrix(); |
| |
| /** this rectangle defines the area in which graph values can be drawn */ |
| protected RectF mContentRect = new RectF(); |
| |
| protected float mChartWidth = 0f; |
| protected float mChartHeight = 0f; |
| |
| /** 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 ViewPortHandler() { |
| |
| } |
| |
| /** |
| * Sets the width and height of the chart. |
| * |
| * @param width |
| * @param height |
| */ |
| public void setChartDimens(float width, float height) { |
| mChartHeight = height; |
| mChartWidth = width; |
| |
| if (mContentRect.width() <= 0 || mContentRect.height() <= 0) |
| mContentRect.set(0, 0, width, height); |
| } |
| |
| public void restrainViewPort(float offsetLeft, float offsetTop, float offsetRight, |
| float offsetBottom) { |
| mContentRect.set(offsetLeft, offsetTop, mChartWidth - offsetRight, mChartHeight |
| - offsetBottom); |
| } |
| |
| public float offsetLeft() { |
| return mContentRect.left; |
| } |
| |
| public float offsetRight() { |
| return mChartWidth - mContentRect.right; |
| } |
| |
| public float offsetTop() { |
| return mContentRect.top; |
| } |
| |
| public float offsetBottom() { |
| return mChartHeight - mContentRect.bottom; |
| } |
| |
| public float contentTop() { |
| return mContentRect.top; |
| } |
| |
| public float contentLeft() { |
| return mContentRect.left; |
| } |
| |
| public float contentRight() { |
| return mContentRect.right; |
| } |
| |
| public float contentBottom() { |
| return mContentRect.bottom; |
| } |
| |
| public float contentWidth() { |
| return mContentRect.width(); |
| } |
| |
| public float contentHeight() { |
| return mContentRect.height(); |
| } |
| |
| public RectF getContentRect() { |
| return mContentRect; |
| } |
| |
| public PointF getContentCenter() { |
| return new PointF(mContentRect.centerX(), mContentRect.centerY()); |
| } |
| |
| public float getChartHeight() { |
| return mChartHeight; |
| } |
| |
| public float getChartWidth() { |
| return mChartWidth; |
| } |
| |
| /** |
| * ################ ################ ################ ################ |
| */ |
| /** 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; |
| } |
| |
| /** |
| * 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[] transformedPts, final Chart<?> chart) { |
| |
| Matrix save = new Matrix(); |
| save.set(mMatrixTouch); |
| |
| final float x = transformedPts[0] - offsetLeft(); |
| final float y = transformedPts[1] - offsetTop(); |
| |
| save.postTranslate(-x, -y); |
| |
| refresh(save, chart, false); |
| |
| // final View v = chart.getChartView(); |
| // |
| // v.post(new Runnable() { |
| // |
| // @Override |
| // public void run() { |
| // Matrix save = new Matrix(); |
| // save.set(mMatrixTouch); |
| // |
| // final float x = transformedPts[0] - offsetLeft(); |
| // final float y = transformedPts[1] - offsetTop(); |
| // |
| // save.postTranslate(-x, -y); |
| // |
| // refresh(save, chart, false); |
| // } |
| // }); |
| } |
| |
| /** |
| * call this method to refresh the graph with a given matrix |
| * |
| * @param newMatrix |
| * @return |
| */ |
| public Matrix refresh(Matrix newMatrix, Chart<?> chart, boolean invalidate) { |
| |
| mMatrixTouch.set(newMatrix); |
| |
| // make sure scale and translation are within their bounds |
| limitTransAndScale(mMatrixTouch, mContentRect); |
| |
| chart.invalidate(); |
| |
| newMatrix.set(mMatrixTouch); |
| return newMatrix; |
| } |
| |
| /** |
| * limits the maximum scale and X translation of the given matrix |
| * |
| * @param matrix |
| */ |
| public 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); |
| } |
| |
| public void setMinimumScaleX(float xScale) { |
| |
| if (xScale < 1f) |
| xScale = 1f; |
| |
| mMinScaleX = xScale; |
| |
| limitTransAndScale(mMatrixTouch, mContentRect); |
| } |
| |
| public void setMinimumScaleY(float yScale) { |
| |
| if (yScale < 1f) |
| yScale = 1f; |
| |
| mMinScaleY = yScale; |
| |
| limitTransAndScale(mMatrixTouch, mContentRect); |
| } |
| |
| /** |
| * Returns the charts-touch matrix used for translation and scale on touch. |
| * |
| * @return |
| */ |
| public Matrix getMatrixTouch() { |
| return mMatrixTouch; |
| } |
| |
| /** |
| * ################ ################ ################ ################ |
| */ |
| /** BELOW METHODS FOR BOUNDS CHECK */ |
| |
| public boolean isInBoundsX(float x) { |
| if (isInBoundsLeft(x) && isInBoundsRight(x)) |
| return true; |
| else |
| return false; |
| } |
| |
| public boolean isInBoundsY(float y) { |
| if (isInBoundsTop(y) && isInBoundsBottom(y)) |
| return true; |
| else |
| return false; |
| } |
| |
| public boolean isInBounds(float x, float y) { |
| if (isInBoundsX(x) && isInBoundsY(y)) |
| return true; |
| else |
| return false; |
| } |
| |
| public boolean isInBoundsLeft(float x) { |
| return mContentRect.left <= x ? true : false; |
| } |
| |
| public boolean isInBoundsRight(float x) { |
| return mContentRect.right >= x ? true : false; |
| } |
| |
| public boolean isInBoundsTop(float y) { |
| return mContentRect.top <= y ? true : false; |
| } |
| |
| public boolean isInBoundsBottom(float y) { |
| return mContentRect.bottom >= y ? true : false; |
| } |
| |
| /** |
| * 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; |
| } |
| } |