package com.github.mikephil.charting.charts;
import android.content.Context;
import android.graphics.Path;
import android.util.AttributeSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.ScatterData;
import com.github.mikephil.charting.data.ScatterDataSet;
import java.util.ArrayList;
/**
* The ScatterChart. Draws dots, triangles, squares and custom shapes into the
* chartview.
*
* @author Philipp Jahoda
*/
public class ScatterChart extends BarLineChartBase<ScatterData> {
/** enum that defines the shape that is drawn where the values are */
public enum ScatterShape {
CROSS, TRIANGLE, CIRCLE, SQUARE, CUSTOM
}
public ScatterChart(Context context) {
super(context);
}
public ScatterChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ScatterChart(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void prepareContentRect() {
if(isEmpty()) {
super.prepareContentRect();
} else {
float offset = mData.getGreatestShapeSize() / 2f;
mContentRect.set(mOffsetLeft - offset,
mOffsetTop,
getWidth() - mOffsetRight + offset,
getHeight() - mOffsetBottom);
}
}
@Override
protected void calcMinMax(boolean fixedValues) {
super.calcMinMax(fixedValues);
if (mDeltaX == 0 && mData.getYValCount() > 0)
mDeltaX = 1;
}
@Override
protected void drawData() {
ArrayList<ScatterDataSet> dataSets = mData.getDataSets();
for (int i = 0; i < mData.getDataSetCount(); i++) {
ScatterDataSet dataSet = dataSets.get(i);
ArrayList<Entry> entries = dataSet.getYVals();
float shapeHalf = dataSet.getScatterShapeSize() / 2f;
float[] valuePoints = mTrans.generateTransformedValuesLineScatter(entries, mPhaseY);
ScatterShape shape = dataSet.getScatterShape();
for (int j = 0; j < valuePoints.length * mPhaseX; j += 2) {
if (isOffContentRight(valuePoints[j]))
break;
// make sure the lines don't do shitty things outside bounds
if (j != 0 && isOffContentLeft(valuePoints[j - 1])
&& isOffContentTop(valuePoints[j + 1])
&& isOffContentBottom(valuePoints[j + 1]))
continue;
// Set the color for the currently drawn value. If the index is
// out of bounds, reuse colors.
mRenderPaint.setColor(dataSet.getColor(j));
if (shape == ScatterShape.SQUARE) {
mDrawCanvas.drawRect(valuePoints[j] - shapeHalf,
valuePoints[j + 1] - shapeHalf, valuePoints[j]
+ shapeHalf, valuePoints[j + 1]
+ shapeHalf, mRenderPaint);
} else if (shape == ScatterShape.CIRCLE) {
mDrawCanvas.drawCircle(valuePoints[j], valuePoints[j + 1], shapeHalf,
mRenderPaint);
} else if (shape == ScatterShape.CROSS) {
mDrawCanvas.drawLine(valuePoints[j] - shapeHalf, valuePoints[j + 1],
valuePoints[j] + shapeHalf,
valuePoints[j + 1], mRenderPaint);
mDrawCanvas.drawLine(valuePoints[j], valuePoints[j + 1] - shapeHalf,
valuePoints[j], valuePoints[j + 1]
+ shapeHalf, mRenderPaint);
} else if (shape == ScatterShape.TRIANGLE) {
// create a triangle path
Path tri = new Path();
tri.moveTo(valuePoints[j], valuePoints[j + 1] - shapeHalf);
tri.lineTo(valuePoints[j] + shapeHalf, valuePoints[j + 1] + shapeHalf);
tri.lineTo(valuePoints[j] - shapeHalf, valuePoints[j + 1] + shapeHalf);
tri.close();
mDrawCanvas.drawPath(tri, mRenderPaint);
} else if (shape == ScatterShape.CUSTOM) {
Path customShape = dataSet.getCustomScatterShape();
if (customShape == null)
return;
// transform the provided custom path
mTrans.pathValueToPixel(customShape);
mDrawCanvas.drawPath(customShape, mRenderPaint);
}
}
}
}
@Override
protected void drawValues() {
// if values are drawn
if (mDrawYValues && mData.getYValCount() < mMaxVisibleCount * mTrans.getScaleX()) {
ArrayList<ScatterDataSet> dataSets = mData
.getDataSets();
for (int i = 0; i < mData.getDataSetCount(); i++) {
ScatterDataSet dataSet = dataSets.get(i);
ArrayList<Entry> entries = dataSet.getYVals();
float[] positions = mTrans.generateTransformedValuesLineScatter(entries, mPhaseY);
float shapeSize = dataSet.getScatterShapeSize();
for (int j = 0; j < positions.length * mPhaseX; j += 2) {
if (isOffContentRight(positions[j]))
break;
if (isOffContentLeft(positions[j]) || isOffContentTop(positions[j + 1])
|| isOffContentBottom(positions[j + 1]))
continue;
float val = entries.get(j / 2).getVal();
if (mDrawUnitInChart) {
mDrawCanvas.drawText(mValueFormatter.getFormattedValue(val) + mUnit,
positions[j],
positions[j + 1] - shapeSize, mValuePaint);
} else {
mDrawCanvas.drawText(mValueFormatter.getFormattedValue(val), positions[j],
positions[j + 1] - shapeSize,
mValuePaint);
}
}
}
}
}
@Override
protected void drawHighlights() {
for (int i = 0; i < mIndicesToHightlight.length; i++) {
ScatterDataSet set = mData.getDataSetByIndex(mIndicesToHightlight[i]
.getDataSetIndex());
if (set == null)
continue;
mHighlightPaint.setColor(set.getHighLightColor());
int xIndex = mIndicesToHightlight[i].getXIndex(); // get the
// x-position
if (xIndex > mDeltaX * mPhaseX)
continue;
float y = set.getYValForXIndex(xIndex) * mPhaseY; // get the
// y-position
float[] pts = new float[] {
xIndex, mYChartMax, xIndex, mYChartMin, 0, y, mDeltaX, y
};
mTrans.pointValuesToPixel(pts);
// draw the highlight lines
mDrawCanvas.drawLines(pts, mHighlightPaint);
}
}
@Override
protected void drawAdditional() {
}
/**
* Returns all possible predefined scattershapes.
*
* @return
*/
public static ScatterShape[] getAllPossibleShapes() {
return new ScatterShape[] {
ScatterShape.SQUARE, ScatterShape.CIRCLE, ScatterShape.TRIANGLE, ScatterShape.CROSS
};
}
}