package com.github.mikephil.charting.highlight;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarLineScatterCandleBubbleData;
import com.github.mikephil.charting.data.DataSet;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.dataprovider.BarLineScatterCandleBubbleDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.utils.MPPointD;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Philipp Jahoda on 21/07/15.
*/
public class ChartHighlighter<T extends BarLineScatterCandleBubbleDataProvider> implements IHighlighter
{
/**
* instance of the data-provider
*/
protected T mChart;
/**
* buffer for storing previously highlighted values
*/
protected List<Highlight> mHighlightBuffer = new ArrayList<Highlight>();
public ChartHighlighter(T chart) {
this.mChart = chart;
}
@Override
public Highlight getHighlight(float x, float y) {
MPPointD pos = getValsForTouch(x, y);
float xVal = (float) pos.x;
MPPointD.recycleInstance(pos);
Highlight high = getHighlightForX(xVal, x, y);
return high;
}
/**
* Returns a recyclable MPPointD instance.
* Returns the corresponding xPos for a given touch-position in pixels.
*
* @param x
* @param y
* @return
*/
protected MPPointD getValsForTouch(float x, float y) {
// take any transformer to determine the x-axis value
MPPointD pos = mChart.getTransformer(YAxis.AxisDependency.LEFT).getValuesByTouchPoint(x, y);
return pos;
}
/**
* Returns the corresponding Highlight for a given xVal and x- and y-touch position in pixels.
*
* @param xVal
* @param x
* @param y
* @return
*/
protected Highlight getHighlightForX(float xVal, float x, float y) {
List<Highlight> closestValues = getHighlightsAtXValue(xVal, x, y);
if(closestValues.isEmpty()) {
return null;
}
float leftAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.LEFT);
float rightAxisMinDist = getMinimumDistance(closestValues, y, YAxis.AxisDependency.RIGHT);
YAxis.AxisDependency axis = leftAxisMinDist < rightAxisMinDist ? YAxis.AxisDependency.LEFT : YAxis.AxisDependency.RIGHT;
Highlight detail = getClosestHighlightByPixel(closestValues, x, y, axis, mChart.getMaxHighlightDistance());
return detail;
}
/**
* Returns the minimum distance from a touch value (in pixels) to the
* closest value (in pixels) that is displayed in the chart.
*
* @param closestValues
* @param pos
* @param axis
* @return
*/
protected float getMinimumDistance(List<Highlight> closestValues, float pos, YAxis.AxisDependency axis) {
float distance = Float.MAX_VALUE;
for (int i = 0; i < closestValues.size(); i++) {
Highlight high = closestValues.get(i);
if (high.getAxis() == axis) {
float tempDistance = Math.abs(getHighlightPos(high) - pos);
if (tempDistance < distance) {
distance = tempDistance;
}
}
}
return distance;
}
protected float getHighlightPos(Highlight h) {
return h.getYPx();
}
/**
* Returns a list of Highlight objects representing the entries closest to the given xVal.
* The returned list contains two objects per DataSet (closest rounding up, closest rounding down).
*
* @param xVal the transformed x-value of the x-touch position
* @param x touch position
* @param y touch position
* @return
*/
protected List<Highlight> getHighlightsAtXValue(float xVal, float x, float y) {
mHighlightBuffer.clear();
BarLineScatterCandleBubbleData data = getData();
if (data == null)
return mHighlightBuffer;
for (int i = 0, dataSetCount = data.getDataSetCount(); i < dataSetCount; i++) {
IDataSet dataSet = data.getDataSetByIndex(i);
// don't include DataSets that cannot be highlighted
if (!dataSet.isHighlightEnabled())
continue;
mHighlightBuffer.addAll(buildHighlights(dataSet, i, xVal, DataSet.Rounding.CLOSEST));
}
return mHighlightBuffer;
}
/**
* An array of `Highlight` objects corresponding to the selected xValue and dataSetIndex.
*
* @param set
* @param dataSetIndex
* @param xVal
* @param rounding
* @return
*/
protected List<Highlight> buildHighlights(IDataSet set, int dataSetIndex, float xVal, DataSet.Rounding rounding) {
ArrayList<Highlight> highlights = new ArrayList<>();
//noinspection unchecked
List<Entry> entries = set.getEntriesForXValue(xVal);
if (entries.size() == 0) {
// Try to find closest x-value and take all entries for that x-value
final Entry closest = set.getEntryForXValue(xVal, Float.NaN, rounding);
if (closest != null)
{
//noinspection unchecked
entries = set.getEntriesForXValue(closest.getX());
}
}
if (entries.size() == 0)
return highlights;
for (Entry e : entries) {
MPPointD pixels = mChart.getTransformer(
set.getAxisDependency()).getPixelForValues(e.getX(), e.getY());
highlights.add(new Highlight(
e.getX(), e.getY(),
(float) pixels.x, (float) pixels.y,
dataSetIndex, set.getAxisDependency()));
}
return highlights;
}
/**
* Returns the Highlight of the DataSet that contains the closest value on the
* y-axis.
*
* @param closestValues contains two Highlight objects per DataSet closest to the selected x-position (determined by
* rounding up an down)
* @param x
* @param y
* @param axis the closest axis
* @param minSelectionDistance
* @return
*/
public Highlight getClosestHighlightByPixel(List<Highlight> closestValues, float x, float y,
YAxis.AxisDependency axis, float minSelectionDistance) {
Highlight closest = null;
float distance = minSelectionDistance;
for (int i = 0; i < closestValues.size(); i++) {
Highlight high = closestValues.get(i);
if (axis == null || high.getAxis() == axis) {
float cDistance = getDistance(x, y, high.getXPx(), high.getYPx());
if (cDistance < distance) {
closest = high;
distance = cDistance;
}
}
}
return closest;
}
/**
* Calculates the distance between the two given points.
*
* @param x1
* @param y1
* @param x2
* @param y2
* @return
*/
protected float getDistance(float x1, float y1, float x2, float y2) {
//return Math.abs(y1 - y2);
//return Math.abs(x1 - x2);
return (float) Math.hypot(x1 - x2, y1 - y2);
}
protected BarLineScatterCandleBubbleData getData() {
return mChart.getData();
}
}