package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import com.github.mikephil.charting.data.ChartData;
import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.listener.PieRadarChartTouchListener;
import com.nineoldandroids.animation.ObjectAnimator;
/**
* Baseclass of PieChart and RadarChart.
*
* @author Philipp Jahoda
*/
public abstract class PieRadarChartBase<T extends ChartData<? extends DataSet<? extends Entry>>>
extends Chart<T> {
/** holds the current rotation angle of the chart */
protected float mRotationAngle = 270f;
/** flag that indicates if rotation is enabled or not */
private boolean mRotateEnabled = true;
/** the pie- and radarchart touchlistener */
private OnTouchListener mListener;
public PieRadarChartBase(Context context) {
super(context);
}
public PieRadarChartBase(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PieRadarChartBase(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void init() {
super.init();
mListener = new PieRadarChartTouchListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// use the pie- and radarchart listener own listener
if (mTouchEnabled && mListener != null)
return mListener.onTouch(this, event);
else
return super.onTouchEvent(event);
}
@Override
public void prepare() {
if (mDataNotSet)
return;
calcMinMax(false);
prepareLegend();
}
@Override
public void notifyDataSetChanged() {
// TODO Auto-generated method stub
}
@Override
protected void drawAdditional() {
// TODO Auto-generated method stub
}
/**
* Applys the newly calculated offsets to the matrices.
*/
protected void applyCalculatedOffsets() {
prepareContentRect();
float scaleX = (float) ((getWidth() - mOffsetLeft - mOffsetRight) / mDeltaX);
float scaleY = (float) ((getHeight() - mOffsetBottom - mOffsetTop) / mDeltaY);
Matrix val = new Matrix();
val.postTranslate(0, -mYChartMin);
val.postScale(scaleX, -scaleY);
mMatrixValueToPx.set(val);
Matrix offset = new Matrix();
offset.postTranslate(mOffsetLeft, getHeight() - mOffsetBottom);
mMatrixOffset.set(offset);
}
/** the angle where the dragging started */
private float mStartAngle = 0f;
/**
* sets the starting angle of the rotation, this is only used by the touch
* listener, x and y is the touch position
*
* @param x
* @param y
*/
public void setStartAngle(float x, float y) {
mStartAngle = getAngleForPoint(x, y);
// take the current angle into consideration when starting a new drag
mStartAngle -= mRotationAngle;
}
/**
* updates the view rotation depending on the given touch position, also
* takes the starting angle into consideration
*
* @param x
* @param y
*/
public void updateRotation(float x, float y) {
mRotationAngle = getAngleForPoint(x, y);
// take the offset into consideration
mRotationAngle -= mStartAngle;
// keep the angle >= 0 and <= 360
mRotationAngle = (mRotationAngle + 360f) % 360f;
}
/**
* returns the angle relative to the chart center for the given point on the
* chart in degrees. The angle is always between 0 and 360°, 0° is NORTH,
* 90° is EAST, ...
*
* @param x
* @param y
* @return
*/
public float getAngleForPoint(float x, float y) {
PointF c = getCenterOffsets();
double tx = x - c.x, ty = y - c.y;
double length = Math.sqrt(tx * tx + ty * ty);
double r = Math.acos(ty / length);
float angle = (float) Math.toDegrees(r);
if (x > c.x)
angle = 360f - angle;
// add 90° because chart starts EAST
angle = angle + 90f;
// neutralize overflow
if (angle > 360f)
angle = angle - 360f;
return angle;
}
/**
* Returns the distance of a certain point on the chart to the center of the
* chart.
*
* @param x
* @param y
* @return
*/
public float distanceToCenter(float x, float y) {
PointF c = getCenterOffsets();
float dist = 0f;
float xDist = 0f;
float yDist = 0f;
if (x > c.x) {
xDist = x - c.x;
} else {
xDist = c.x - x;
}
if (y > c.y) {
yDist = y - c.y;
} else {
yDist = c.y - y;
}
// pythagoras
dist = (float) Math.sqrt(Math.pow(xDist, 2.0) + Math.pow(yDist, 2.0));
return dist;
}
/**
* Returns the xIndex for the given angle around the center of the chart.
* Returns -1 if not found / outofbounds.
*
* @param angle
* @return
*/
public abstract int getIndexForAngle(float angle);
/**
* Set an offset for the rotation of the RadarChart in degrees. Default 270f
* --> top (NORTH)
*
* @param angle
*/
public void setRotationAngle(float angle) {
angle = (int) Math.abs(angle % 360);
mRotationAngle = angle;
}
/**
* gets the current rotation angle of the pie chart
*
* @return
*/
public float getRotationAngle() {
return mRotationAngle;
}
/**
* Set this to true to enable the rotation / spinning of the chart by touch.
* Set it to false to disable it. Default: true
*
* @param enabled
*/
public void setRotationEnabled(boolean enabled) {
mRotateEnabled = enabled;
}
/**
* Returns true if rotation of the chart by touch is enabled, false if not.
*
* @return
*/
public boolean isRotationEnabled() {
return mRotateEnabled;
}
/**
* returns the diameter of the pie- or radar-chart
*
* @return
*/
public float getDiameter() {
if (mContentRect == null)
return 0;
else
return Math.min(mContentRect.width(), mContentRect.height());
}
/**
* Returns the radius of the chart in pixels.
*
* @return
*/
public abstract float getRadius();
/**
* set a new (e.g. custom) charttouchlistener NOTE: make sure to
* setTouchEnabled(true); if you need touch gestures on the chart
*
* @param l
*/
public void setOnTouchListener(OnTouchListener l) {
this.mListener = l;
}
/**
* ################ ################ ################ ################
*/
/** CODE BELOW THIS RELATED TO ANIMATION */
/** objectanimator used for animating values on y-axis */
private ObjectAnimator mSpinAnimator;
/**
* Applys a spin animation to the Chart.
*
* @param durationmillis
* @param fromangle
* @param toangle
*/
public void spin(int durationmillis, float fromangle, float toangle) {
mRotationAngle = fromangle;
mSpinAnimator = ObjectAnimator.ofFloat(this, "rotationAngle", fromangle, toangle);
mSpinAnimator.setDuration(durationmillis);
mSpinAnimator.addUpdateListener(this);
mSpinAnimator.start();
}
}