diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java index 77d4aaf98f..2e0b6fd29c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/components/XAxis.java @@ -47,6 +47,11 @@ public class XAxis extends AxisBase { */ private boolean mAvoidFirstLastClipping = false; + /** + * minimum spacing between labels in pixels + */ + protected float mSpaceBetweenLabelsMin = 0; + /** * the position of the x-labels relative to the chart */ @@ -65,6 +70,19 @@ public XAxis() { mYOffset = Utils.convertDpToPixel(4.f); // -3 } + /** + * Returns minimum spacing between labels in pixels + */ + public float getSpaceBetweenLabelsMin() { return mSpaceBetweenLabelsMin; } + + /** + * Sets minimum spacing between labels in pixels + */ + public void setSpaceBetweenLabelsMin(float space) + { + mSpaceBetweenLabelsMin = space; + } + /** * returns the position of the x-labels */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java index 72ea2d17c8..c959c3a36e 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java @@ -7,7 +7,6 @@ import android.graphics.Paint.Style; import com.github.mikephil.charting.components.AxisBase; -import com.github.mikephil.charting.utils.MPPointD; import com.github.mikephil.charting.utils.Transformer; import com.github.mikephil.charting.utils.Utils; import com.github.mikephil.charting.utils.ViewPortHandler; @@ -115,73 +114,26 @@ public Transformer getTransformer() { * @param min - the minimum value in the data object for this axis * @param max - the maximum value in the data object for this axis */ - public void computeAxis(float min, float max, boolean inverted) { + public abstract void computeAxis(float min, float max, boolean inverted); - // calculate the starting and entry point of the y-labels (depending on - // zoom / contentrect bounds) - if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { - - MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); - MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); - - if (!inverted) { - - min = (float) p2.y; - max = (float) p1.y; - } else { - - min = (float) p1.y; - max = (float) p2.y; - } - - MPPointD.recycleInstance(p1); - MPPointD.recycleInstance(p2); - } - - computeAxisValues(min, max); - } /** - * Sets up the axis values. Computes the desired number of labels between the two given extremes. - * - * @return + * Compute axis interval and desired number of labels between the two given extremes + * @param min + * @param max */ - protected void computeAxisValues(float min, float max) { - - float yMin = min; - float yMax = max; - - int labelCount = mAxis.getLabelCount(); - double range = Math.abs(yMax - yMin); - - if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { - mAxis.mEntries = new float[]{}; - mAxis.mCenteredEntries = new float[]{}; - mAxis.mEntryCount = 0; - return; - } - - // Find out how much spacing (in y value space) between axis values - double rawInterval = range / labelCount; - double interval = Utils.roundToNextSignificant(rawInterval); - - // If granularity is enabled, then do not allow the interval to go below specified granularity. - // This is used to avoid repeated values when rounding values for display. - if (mAxis.isGranularityEnabled()) - interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; - - // Normalize interval - double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); - int intervalSigDigit = (int) (interval / intervalMagnitude); - if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 - // if it's 0.0 after floor(), we use the old value - interval = Math.floor(10.0 * intervalMagnitude) == 0.0 - ? interval - : Math.floor(10.0 * intervalMagnitude); - - } + protected abstract void computeAxisInterval(float min, float max); + /** + * Setup axis entries and decimals based on the previously computed interval and labels count + * @param min + * @param max + * @param labelCount + * @param range + * @param interval + */ + protected void computeAxisValues(float min, float max, int labelCount, double range, double interval) { + int n = mAxis.isCenterAxisLabelsEnabled() ? 1 : 0; // force label count @@ -207,12 +159,12 @@ protected void computeAxisValues(float min, float max) { // no forced count } else { - double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; + double first = interval == 0.0 ? 0.0 : Math.ceil(min / interval) * interval; if(mAxis.isCenterAxisLabelsEnabled()) { first -= interval; } - double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); + double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(max / interval) * interval); double f; int i; @@ -262,7 +214,7 @@ else if (last == first && n == 0) { } } } - + /** * Draws the axis labels to the screen. * diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java index 8adb56c73a..e882664678 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.java @@ -45,7 +45,7 @@ public void computeAxis(float min, float max, boolean inverted) { // calculate the starting and entry point of the y-labels (depending on // zoom / contentrect bounds) - if (mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { + if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutX()) { MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentRight(), mViewPortHandler.contentTop()); @@ -64,14 +64,59 @@ public void computeAxis(float min, float max, boolean inverted) { MPPointD.recycleInstance(p2); } - computeAxisValues(min, max); + computeAxisInterval(min, max); } @Override - protected void computeAxisValues(float min, float max) { - super.computeAxisValues(min, max); + protected void computeAxisInterval(float min, float max) { + // Compute the longest label width first computeSize(); + + int labelCount = mXAxis.getLabelCount(); + double range = Math.abs(max - min); + + if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { + mXAxis.mEntries = new float[]{}; + mXAxis.mCenteredEntries = new float[]{}; + mXAxis.mEntryCount = 0; + return; + } + + // Find out how much spacing (in x value space) between axis values + double rawInterval = range / labelCount; + double interval = Utils.roundToNextSignificant(rawInterval); + + // Do not allow the interval go below the label width with spacing. + double labelMaxWidth = (mXAxis.mLabelRotatedWidth + mXAxis.getSpaceBetweenLabelsMin()) * range / mViewPortHandler.contentWidth(); + if (interval < labelMaxWidth) + interval = labelMaxWidth; + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mXAxis.isGranularityEnabled()) + interval = interval < mXAxis.getGranularity() ? mXAxis.getGranularity() : interval; + + // Perform rounding once again after interval was probably adjusted by label width and/or granularity checks. + rawInterval = interval; + interval = Utils.roundToNextSignificant(rawInterval); + + // Normalize interval + double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); + int intervalSigDigit = (int) (interval / intervalMagnitude); + if (intervalSigDigit > 5) { + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + // if it's 0.0 after floor(), we use the old value + interval = Math.floor(10.0 * intervalMagnitude) == 0.0 + ? interval + : Math.floor(10.0 * intervalMagnitude); + + // If rounded down to current significant, round up to avoid label overlapping + } else if (interval < rawInterval) { + interval = (intervalSigDigit + 1) * intervalMagnitude; + } + + computeAxisValues(min, max, labelCount, range, interval); } protected void computeSize() { @@ -91,7 +136,6 @@ protected void computeSize() { labelHeight, mXAxis.getLabelRotationAngle()); - mXAxis.mLabelWidth = Math.round(labelWidth); mXAxis.mLabelHeight = Math.round(labelHeight); mXAxis.mLabelRotatedWidth = Math.round(labelRotatedSize.width); diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java index 86047cf1b8..3657fecc2f 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java @@ -55,7 +55,7 @@ public void computeAxis(float min, float max, boolean inverted) { MPPointD.recycleInstance(p2); } - computeAxisValues(min, max); + computeAxisInterval(min, max); } @Override diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java index 53cca7ee03..fc5306c83d 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.java @@ -41,6 +41,69 @@ public YAxisRenderer(ViewPortHandler viewPortHandler, YAxis yAxis, Transformer t } } + @Override + public void computeAxis(float min, float max, boolean inverted) { + + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { + + MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); + MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); + + if (!inverted) { + + min = (float) p2.y; + max = (float) p1.y; + } else { + + min = (float) p1.y; + max = (float) p2.y; + } + + MPPointD.recycleInstance(p1); + MPPointD.recycleInstance(p2); + } + + computeAxisInterval(min, max); + } + + @Override + protected void computeAxisInterval(float min, float max) { + + int labelCount = mYAxis.getLabelCount(); + double range = Math.abs(max - min); + + if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { + mYAxis.mEntries = new float[]{}; + mYAxis.mCenteredEntries = new float[]{}; + mYAxis.mEntryCount = 0; + return; + } + + // Find out how much spacing (in y value space) between axis values + double rawInterval = range / labelCount; + double interval = Utils.roundToNextSignificant(rawInterval); + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if (mYAxis.isGranularityEnabled()) + interval = interval < mYAxis.getGranularity() ? mYAxis.getGranularity() : interval; + + // Normalize interval + double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); + int intervalSigDigit = (int) (interval / intervalMagnitude); + if (intervalSigDigit > 5) { + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + // if it's 0.0 after floor(), we use the old value + interval = Math.floor(10.0 * intervalMagnitude) == 0.0 + ? interval + : Math.floor(10.0 * intervalMagnitude); + } + + computeAxisValues(min, max, labelCount, range, interval); + } + /** * draws the y-axis labels to the screen */ diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java index fedf8054a1..a7bee4e952 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.java @@ -57,7 +57,7 @@ public void computeAxis(float yMin, float yMax, boolean inverted) { MPPointD.recycleInstance(p2); } - computeAxisValues(yMin, yMax); + computeAxisInterval(yMin, yMax); } /** diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java index f7b1ad9e87..1b4087f217 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.java @@ -2,7 +2,6 @@ import android.graphics.Canvas; import android.graphics.Path; -import android.graphics.PointF; import com.github.mikephil.charting.charts.RadarChart; import com.github.mikephil.charting.components.LimitLine; @@ -24,123 +23,11 @@ public YAxisRendererRadarChart(ViewPortHandler viewPortHandler, YAxis yAxis, Rad } @Override - protected void computeAxisValues(float min, float max) { - - float yMin = min; - float yMax = max; - - int labelCount = mAxis.getLabelCount(); - double range = Math.abs(yMax - yMin); - - if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { - mAxis.mEntries = new float[]{}; - mAxis.mCenteredEntries = new float[]{}; - mAxis.mEntryCount = 0; - return; - } - - // Find out how much spacing (in y value space) between axis values - double rawInterval = range / labelCount; - double interval = Utils.roundToNextSignificant(rawInterval); - - // If granularity is enabled, then do not allow the interval to go below specified granularity. - // This is used to avoid repeated values when rounding values for display. - if (mAxis.isGranularityEnabled()) - interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; - - // Normalize interval - double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); - int intervalSigDigit = (int) (interval / intervalMagnitude); - if (intervalSigDigit > 5) { - // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 - // if it's 0.0 after floor(), we use the old value - interval = Math.floor(10.0 * intervalMagnitude) == 0.0 - ? interval - : Math.floor(10.0 * intervalMagnitude); - } - - boolean centeringEnabled = mAxis.isCenterAxisLabelsEnabled(); - int n = centeringEnabled ? 1 : 0; - - // force label count - if (mAxis.isForceLabelsEnabled()) { - - float step = (float) range / (float) (labelCount - 1); - mAxis.mEntryCount = labelCount; - - if (mAxis.mEntries.length < labelCount) { - // Ensure stops contains at least numStops elements. - mAxis.mEntries = new float[labelCount]; - } - - float v = min; - - for (int i = 0; i < labelCount; i++) { - mAxis.mEntries[i] = v; - v += step; - } - - n = labelCount; - - // no forced count - } else { - - double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; - if (centeringEnabled) { - first -= interval; - } - - double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); - - double f; - int i; - - if (interval != 0.0) { - for (f = first; f <= last; f += interval) { - ++n; - } - } - - n++; - - mAxis.mEntryCount = n; - - if (mAxis.mEntries.length < n) { - // Ensure stops contains at least numStops elements. - mAxis.mEntries = new float[n]; - } - - for (f = first, i = 0; i < n; f += interval, ++i) { - - if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) - f = 0.0; - - mAxis.mEntries[i] = (float) f; - } - } - - // set decimals - if (interval < 1) { - mAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); - } else { - mAxis.mDecimals = 0; - } - - if (centeringEnabled) { - - if (mAxis.mCenteredEntries.length < n) { - mAxis.mCenteredEntries = new float[n]; - } - - float offset = (mAxis.mEntries[1] - mAxis.mEntries[0]) / 2f; - - for (int i = 0; i < n; i++) { - mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; - } - } + protected void computeAxisInterval(float min, float max) { + super.computeAxisInterval(min, max); mAxis.mAxisMinimum = mAxis.mEntries[0]; - mAxis.mAxisMaximum = mAxis.mEntries[n-1]; + mAxis.mAxisMaximum = mAxis.mEntries[mAxis.mEntries.length-1]; mAxis.mAxisRange = Math.abs(mAxis.mAxisMaximum - mAxis.mAxisMinimum); }