blob: 324bc8b44c2a0456c282dad7df32176927779027 [file] [log] [blame]
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;
}
}