blob: a4fa2d89a6ca6d385dd7c391c92c819c403a33c4 [file] [log] [blame]
package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Paint.Align;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View.OnTouchListener;
import com.github.mikephil.charting.listener.PieRadarChartTouchListener;
import com.github.mikephil.charting.utils.Legend.LegendPosition;
import com.github.mikephil.charting.utils.Utils;
/**
* Baseclass of PieChart and RadarChart.
*
* @author Philipp Jahoda
*/
public abstract class PieRadarChartBase extends Chart {
/** holds the current rotation angle of the chart */
protected float mChartAngle = 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 piecharts 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);
// calculate how many digits are needed
calcFormats();
prepareLegend();
}
/**
* calculates the required number of digits for the y-legend and for the
* values that might be drawn in the chart (if enabled)
*/
private void calcFormats() {
// -1 means calculate digits
if (mValueDigitsToUse == -1) {
if (mOriginalData.getXValCount() <= 1)
mValueFormatDigits = 0;
else
mValueFormatDigits = Utils.getPieFormatDigits(mDeltaY);
}
else
mValueFormatDigits = mValueDigitsToUse;
}
@Override
public void notifyDataSetChanged() {
// TODO Auto-generated method stub
}
@Override
protected void drawAdditional() {
// TODO Auto-generated method stub
}
@Override
protected void calculateOffsets() {
if (mLegend == null)
return;
// setup offsets for legend
if (mLegend.getPosition() == LegendPosition.RIGHT_OF_CHART) {
mLegend.setOffsetRight(mLegend.getMaximumEntryLength(mLegendLabelPaint));
mLegendLabelPaint.setTextAlign(Align.LEFT);
} else if (mLegend.getPosition() == LegendPosition.BELOW_CHART_LEFT
|| mLegend.getPosition() == LegendPosition.BELOW_CHART_RIGHT
|| mLegend.getPosition() == LegendPosition.BELOW_CHART_CENTER) {
mLegend.setOffsetBottom(mLegendLabelPaint.getTextSize() * 3.5f);
}
if (mDrawLegend) {
mOffsetBottom = Math.max(mOffsetBottom, mLegend.getOffsetBottom());
mOffsetRight = Math.max(mOffsetRight, mLegend.getOffsetRight() / 3 * 2);
}
mLegend.setOffsetTop(mOffsetTop);
mLegend.setOffsetLeft(mOffsetLeft);
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 -= mChartAngle;
}
/**
* 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) {
mChartAngle = getAngleForPoint(x, y);
// take the offset into consideration
mChartAngle -= mStartAngle;
// keep the angle >= 0 and <= 360
mChartAngle = (mChartAngle + 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 EAST, 90°
* is SOUTH, ...
*
* @param x
* @param y
* @return
*/
public float getAngleForPoint(float x, float y) {
PointF c = getCenter();
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 = getCenter();
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(int angle) {
angle = (int) Math.abs(angle % 360);
mChartAngle = angle;
}
/**
* gets the current rotation angle of the pie chart
*
* @return
*/
public float getRotationAngle() {
return mChartAngle;
}
/**
* 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;
}
}