diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java index a3d9502dc4..18d796b4c4 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/charts/HorizontalBarChart.java @@ -55,7 +55,7 @@ protected void init() { mAxisRendererLeft = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisLeft, mLeftAxisTransformer); mAxisRendererRight = new YAxisRendererHorizontalBarChart(mViewPortHandler, mAxisRight, mRightAxisTransformer); - mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer, this); + mXAxisRenderer = new XAxisRendererHorizontalBarChart(mViewPortHandler, mXAxis, mLeftAxisTransformer); } private RectF mOffsetsBuffer = new RectF(); 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 deleted file mode 100644 index 1c44231cee..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.java +++ /dev/null @@ -1,303 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -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; - -/** - * Baseclass of all axis renderers. - * - * @author Philipp Jahoda - */ -public abstract class AxisRenderer extends Renderer { - - /** base axis this axis renderer works with */ - protected AxisBase mAxis; - - /** transformer to transform values to screen pixels and return */ - protected Transformer mTrans; - - /** - * paint object for the grid lines - */ - protected Paint mGridPaint; - - /** - * paint for the x-label values - */ - protected Paint mAxisLabelPaint; - - /** - * paint for the line surrounding the chart - */ - protected Paint mAxisLinePaint; - - /** - * paint used for the limit lines - */ - protected Paint mLimitLinePaint; - - public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis) { - super(viewPortHandler); - - this.mTrans = trans; - this.mAxis = axis; - - if(this.viewPortHandler != null) { - - mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - - mGridPaint = new Paint(); - mGridPaint.setColor(Color.GRAY); - mGridPaint.setStrokeWidth(1f); - mGridPaint.setStyle(Style.STROKE); - mGridPaint.setAlpha(90); - - mAxisLinePaint = new Paint(); - mAxisLinePaint.setColor(Color.BLACK); - mAxisLinePaint.setStrokeWidth(1f); - mAxisLinePaint.setStyle(Style.STROKE); - - mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mLimitLinePaint.setStyle(Paint.Style.STROKE); - } - } - - /** - * Returns the Paint object used for drawing the axis (labels). - * - * @return - */ - public Paint getPaintAxisLabels() { - return mAxisLabelPaint; - } - - /** - * Returns the Paint object that is used for drawing the grid-lines of the - * axis. - * - * @return - */ - public Paint getPaintGrid() { - return mGridPaint; - } - - /** - * Returns the Paint object that is used for drawing the axis-line that goes - * alongside the axis. - * - * @return - */ - public Paint getPaintAxisLine() { - return mAxisLinePaint; - } - - /** - * Returns the Transformer object used for transforming the axis values. - * - * @return - */ - public Transformer getTransformer() { - return mTrans; - } - - /** - * Computes the axis values. - * - * @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) { - - // calculate the starting and entry point of the y-labels (depending on - // zoom / contentrect bounds) - if (viewPortHandler != null && viewPortHandler.contentWidth() > 10 && !viewPortHandler.isFullyZoomedOutY()) { - - MPPointD p1 = mTrans.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentTop()); - MPPointD p2 = mTrans.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.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 - */ - 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); - - } - - int n = mAxis.isCenterAxisLabelsEnabled() ? 1 : 0; - - // force label count - if (mAxis.isForceLabelsEnabled()) { - - interval = (float) range / (float) (labelCount - 1); - // When force label is enabled - // If granularity is enabled, then do not allow the interval to go below specified granularity. - if (mAxis.isGranularityEnabled()) - interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; - - mAxis.mEntryCount = 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 += interval; - } - - n = labelCount; - - // no forced count - } else { - - double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; - if(mAxis.isCenterAxisLabelsEnabled()) { - first -= interval; - } - - double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); - - double f; - int i; - - if (interval != 0.0 && last != first) { - for (f = first; f <= last; f += interval) { - ++n; - } - } - else if (last == first && n == 0) { - n = 1; - } - - mAxis.mEntryCount = n; - - 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 (mAxis.isCenterAxisLabelsEnabled()) { - - if (mAxis.mCenteredEntries.length < n) { - mAxis.mCenteredEntries = new float[n]; - } - - float offset = (float)interval / 2f; - - for (int i = 0; i < n; i++) { - mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; - } - } - } - - /** - * Draws the axis labels to the screen. - * - * @param c - */ - public abstract void renderAxisLabels(Canvas c); - - /** - * Draws the grid lines belonging to the axis. - * - * @param c - */ - public abstract void renderGridLines(Canvas c); - - /** - * Draws the line that goes alongside the axis. - * - * @param c - */ - public abstract void renderAxisLine(Canvas c); - - /** - * Draws the LimitLines associated with this axis to the screen. - * - * @param c - */ - public abstract void renderLimitLines(Canvas c); - - /** - * Sets the text color to use for the labels. Make sure to use - * getResources().getColor(...) when using a color from the resources. - * - * @param color - */ - public void setTextColor(int color) { - mAxis.setTextColor(color); - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.kt new file mode 100644 index 0000000000..9d79a70b54 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/AxisRenderer.kt @@ -0,0 +1,279 @@ +package com.github.mikephil.charting.renderer + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +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 +import kotlin.math.abs +import kotlin.math.ceil +import kotlin.math.floor +import kotlin.math.log10 +import kotlin.math.pow + +/** + * Baseclass of all axis renderers. + */ +abstract class AxisRenderer( + viewPortHandler: ViewPortHandler, + /** transformer to transform values to screen pixels and return */ + var transformer: Transformer?, + /** base axis this axis renderer works with */ + @JvmField + protected var axis: AxisBase +) : Renderer(viewPortHandler) { + /** + * Returns the Transformer object used for transforming the axis values. + * + * @return + */ + + /** + * Returns the Paint object that is used for drawing the grid-lines of the + * axis. + * + * @return + */ + /** + * paint object for the grid lines + */ + var paintGrid: Paint? = null + protected set + + /** + * Returns the Paint object used for drawing the axis (labels). + * + * @return + */ + /** + * paint for the x-label values + */ + public var paintAxisLabels: Paint? = null + protected set + + /** + * Returns the Paint object that is used for drawing the axis-line that goes + * alongside the axis. + * + * @return + */ + /** + * paint for the line surrounding the chart + */ + var paintAxisLine: Paint? = null + protected set + + /** + * paint used for the limit lines + */ + @JvmField + protected var limitLinePaint: Paint? = null + + init { + paintAxisLabels = Paint(Paint.ANTI_ALIAS_FLAG) + + paintGrid = Paint() + paintGrid!!.color = Color.GRAY + paintGrid!!.strokeWidth = 1f + paintGrid!!.style = Paint.Style.STROKE + paintGrid!!.alpha = 90 + + paintAxisLine = Paint() + paintAxisLine!!.color = Color.BLACK + paintAxisLine!!.strokeWidth = 1f + paintAxisLine!!.style = Paint.Style.STROKE + + limitLinePaint = Paint(Paint.ANTI_ALIAS_FLAG) + limitLinePaint!!.style = Paint.Style.STROKE + } + + /** + * Computes the axis values. + * + * @param min - the minimum value in the data object for this axis + * @param max - the maximum value in the data object for this axis + */ + open fun computeAxis(min: Float, max: Float, inverted: Boolean) { + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + + var minLocal = min + var maxLocal = max + + if (viewPortHandler.contentWidth() > 10 && !viewPortHandler.isFullyZoomedOutY) { + transformer?.let { + val p1 = it.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentTop()) + val p2 = it.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentBottom()) + + if (!inverted) { + minLocal = p2.y.toFloat() + maxLocal = p1.y.toFloat() + } else { + minLocal = p1.y.toFloat() + maxLocal = p2.y.toFloat() + } + + MPPointD.recycleInstance(p1) + MPPointD.recycleInstance(p2) + } + } + computeAxisValues(minLocal, maxLocal) + } + + /** + * Sets up the axis values. Computes the desired number of labels between the two given extremes. + * + * @return + */ + protected open fun computeAxisValues(min: Float, max: Float) { + + val labelCount = axis.labelCount + val range = abs((max - min).toDouble()) + + if (labelCount == 0 || range <= 0 || java.lang.Double.isInfinite(range)) { + axis.mEntries = floatArrayOf() + axis.mCenteredEntries = floatArrayOf() + axis.mEntryCount = 0 + return + } + + // Find out how much spacing (in y value space) between axis values + val rawInterval = range / labelCount + var interval = Utils.roundToNextSignificant(rawInterval).toDouble() + + // 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 (axis.isGranularityEnabled) interval = if (interval < axis.granularity) axis.granularity.toDouble() else interval + + // Normalize interval + val intervalMagnitude = Utils.roundToNextSignificant(10.0.pow(log10(interval).toInt().toDouble())).toDouble() + val intervalSigDigit = (interval / intervalMagnitude).toInt() + 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 = if (floor(10.0 * intervalMagnitude) == 0.0) + interval + else floor(10.0 * intervalMagnitude) + } + + var n = if (axis.isCenterAxisLabelsEnabled) 1 else 0 + + // force label count + if (axis.isForceLabelsEnabled) { + interval = (range.toFloat() / (labelCount - 1).toFloat()).toDouble() + // When force label is enabled + // If granularity is enabled, then do not allow the interval to go below specified granularity. + if (axis.isGranularityEnabled) interval = if (interval < axis.granularity) axis.granularity.toDouble() else interval + + axis.mEntryCount = labelCount + + // Ensure stops contains at least numStops elements. + axis.mEntries = FloatArray(labelCount) + + var v = min + + for (i in 0.. 10 && !viewPortHandler.isFullyZoomedOutX()) { - - MPPointD p1 = mTrans.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentTop()); - MPPointD p2 = mTrans.getValuesByTouchPoint(viewPortHandler.contentRight(), viewPortHandler.contentTop()); - - if (inverted) { - - min = (float) p2.x; - max = (float) p1.x; - } else { - - min = (float) p1.x; - max = (float) p2.x; - } - - MPPointD.recycleInstance(p1); - MPPointD.recycleInstance(p2); - } - - computeAxisValues(min, max); - } - - @Override - protected void computeAxisValues(float min, float max) { - super.computeAxisValues(min, max); - - computeSize(); - } - - protected void computeSize() { - - String longest = mXAxis.getLongestLabel(); - - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - - final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest); - - final float labelWidth = labelSize.width; - final float labelHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q"); - - final FSize labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees( - labelWidth, - labelHeight, - mXAxis.getLabelRotationAngle()); - - - mXAxis.mLabelWidth = Math.round(labelRotatedSize.width); - mXAxis.mLabelHeight = Math.round(labelRotatedSize.height); - - FSize.recycleInstance(labelRotatedSize); - FSize.recycleInstance(labelSize); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) - return; - - float yoffset = mXAxis.getYOffset(); - - mAxisLabelPaint.setColor(mXAxis.getTextColor()); - - MPPointF pointF = MPPointF.getInstance(0,0); - if (mXAxis.getPosition() == XAxisPosition.TOP) { - pointF.x = 0.5f; - pointF.y = 1.0f; - drawLabels(c, viewPortHandler.contentTop() - yoffset, pointF); - - } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - pointF.x = 0.5f; - pointF.y = 1.0f; - drawLabels(c, viewPortHandler.contentTop() + yoffset + mXAxis.mLabelHeight, pointF); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - pointF.x = 0.5f; - pointF.y = 0.0f; - drawLabels(c, viewPortHandler.contentBottom() + yoffset, pointF); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - pointF.x = 0.5f; - pointF.y = 0.0f; - drawLabels(c, viewPortHandler.contentBottom() - yoffset - mXAxis.mLabelHeight, pointF); - - } else { // BOTH SIDED - pointF.x = 0.5f; - pointF.y = 1.0f; - drawLabels(c, viewPortHandler.contentTop() - yoffset, pointF); - pointF.x = 0.5f; - pointF.y = 0.0f; - drawLabels(c, viewPortHandler.contentBottom() + yoffset, pointF); - } - MPPointF.recycleInstance(pointF); - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) - return; - - mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); - mAxisLinePaint.setPathEffect(mXAxis.getAxisLineDashPathEffect()); - - if (mXAxis.getPosition() == XAxisPosition.TOP - || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(viewPortHandler.contentLeft(), - viewPortHandler.contentTop(), viewPortHandler.contentRight(), - viewPortHandler.contentTop(), mAxisLinePaint); - } - - if (mXAxis.getPosition() == XAxisPosition.BOTTOM - || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(viewPortHandler.contentLeft(), - viewPortHandler.contentBottom(), viewPortHandler.contentRight(), - viewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - /** - * draws the x-labels on the specified y-position - * - * @param pos - */ - protected void drawLabels(Canvas c, float pos, MPPointF anchor) { - - final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - - float[] positions; - - if (mXAxis.isShowSpecificPositions()) { - positions = new float[mXAxis.getSpecificPositions().length * 2]; - for (int i = 0; i < positions.length; i += 2) { - positions[i] = mXAxis.getSpecificPositions()[i / 2]; - } - } else { - positions = new float[mXAxis.mEntryCount * 2]; - for (int i = 0; i < positions.length; i += 2) { - // only fill x values - if (centeringEnabled) { - positions[i] = mXAxis.mCenteredEntries[i / 2]; - } else { - positions[i] = mXAxis.mEntries[i / 2]; - } - } - } - - mTrans.pointValuesToPixel(positions); - - for (int i = 0; i < positions.length; i += 2) { - - float x = positions[i]; - - if (viewPortHandler.isInBoundsX(x)) { - - String label = mXAxis.isShowSpecificPositions() ? - mXAxis.getValueFormatter().getFormattedValue(mXAxis.getSpecificPositions()[i / 2], mXAxis) - : mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); - - if (mXAxis.isAvoidFirstLastClippingEnabled()) { - - // avoid clipping of the last - if (i / 2 == mXAxis.mEntryCount - 1 && mXAxis.mEntryCount > 1) { - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - - if (width > viewPortHandler.offsetRight() * 2 - && x + width > viewPortHandler.getChartWidth()) - x -= width / 2; - - // avoid clipping of the first - } else if (i == 0) { - - float width = Utils.calcTextWidth(mAxisLabelPaint, label); - x += width / 2; - } - } - - drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees); - } - } - } - - protected void drawLabel(Canvas c, String formattedLabel, float x, float y, MPPointF anchor, float angleDegrees) { - Utils.drawXAxisValue(c, formattedLabel, x, y, mAxisLabelPaint, anchor, angleDegrees); - } - protected Path mRenderGridLinesPath = new Path(); - protected float[] mRenderGridLinesBuffer = new float[2]; - @Override - public void renderGridLines(Canvas c) { - - if (!mXAxis.isDrawGridLinesEnabled() || !mXAxis.isEnabled()) - return; - - int clipRestoreCount = c.save(); - c.clipRect(getGridClippingRect()); - - if (mAxis.isShowSpecificPositions()) { - if (mRenderGridLinesBuffer.length != mAxis.getSpecificPositions().length * 2) { - mRenderGridLinesBuffer = new float[mXAxis.getSpecificPositions().length * 2]; - } - } else { - if (mRenderGridLinesBuffer.length != mAxis.mEntryCount * 2) { - mRenderGridLinesBuffer = new float[mXAxis.mEntryCount * 2]; - } - } - float[] positions = mRenderGridLinesBuffer; - - for (int i = 0; i < positions.length; i += 2) { - if (mAxis.isShowSpecificPositions()) { - positions[i] = mXAxis.getSpecificPositions()[i / 2]; - positions[i + 1] = mXAxis.getSpecificPositions()[i / 2]; - } else { - positions[i] = mXAxis.mEntries[i / 2]; - positions[i + 1] = mXAxis.mEntries[i / 2]; - } - } - - mTrans.pointValuesToPixel(positions); - - setupGridPaint(); - - Path gridLinePath = mRenderGridLinesPath; - gridLinePath.reset(); - - for (int i = 0; i < positions.length; i += 2) { - drawGridLine(c, positions[i], positions[i + 1], gridLinePath); - } - - c.restoreToCount(clipRestoreCount); - } - - protected RectF mGridClippingRect = new RectF(); - - public RectF getGridClippingRect() { - mGridClippingRect.set(viewPortHandler.getContentRect()); - mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); - return mGridClippingRect; - } - - /** - * Draws the grid line at the specified position using the provided path. - * - * @param c - * @param x - * @param y - * @param gridLinePath - */ - protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { - - gridLinePath.moveTo(x, viewPortHandler.contentBottom()); - gridLinePath.lineTo(x, viewPortHandler.contentTop()); - - // draw a path because lines don't support dashing on lower android versions - c.drawPath(gridLinePath, mGridPaint); - - gridLinePath.reset(); - } - - protected float[] mRenderLimitLinesBuffer = new float[2]; - protected RectF mLimitLineClippingRect = new RectF(); - - /** - * Draws the LimitLines associated with this axis to the screen. - * - * @param c - */ - @Override - public void renderLimitLines(Canvas c) { - - List limitLines = mXAxis.getLimitLines(); - - if (limitLines == null || limitLines.size() <= 0) - return; - - float[] position = mRenderLimitLinesBuffer; - position[0] = 0; - position[1] = 0; - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - if (!l.isEnabled()) - continue; - - int clipRestoreCount = c.save(); - mLimitLineClippingRect.set(viewPortHandler.getContentRect()); - mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f); - c.clipRect(mLimitLineClippingRect); - - position[0] = l.getLimit(); - position[1] = 0.f; - - mTrans.pointValuesToPixel(position); - - renderLimitLineLine(c, l, position); - renderLimitLineLabel(c, l, position, 2.f + l.getYOffset()); - - c.restoreToCount(clipRestoreCount); - } - } - - float[] mLimitLineSegmentsBuffer = new float[4]; - private Path mLimitLinePath = new Path(); - - public void renderLimitLineLine(Canvas c, LimitLine limitLine, float[] position) { - mLimitLineSegmentsBuffer[0] = position[0]; - mLimitLineSegmentsBuffer[1] = viewPortHandler.contentTop(); - mLimitLineSegmentsBuffer[2] = position[0]; - mLimitLineSegmentsBuffer[3] = viewPortHandler.contentBottom(); - - mLimitLinePath.reset(); - mLimitLinePath.moveTo(mLimitLineSegmentsBuffer[0], mLimitLineSegmentsBuffer[1]); - mLimitLinePath.lineTo(mLimitLineSegmentsBuffer[2], mLimitLineSegmentsBuffer[3]); - - mLimitLinePaint.setStyle(Paint.Style.STROKE); - mLimitLinePaint.setColor(limitLine.getLineColor()); - mLimitLinePaint.setStrokeWidth(limitLine.getLineWidth()); - mLimitLinePaint.setPathEffect(limitLine.getDashPathEffect()); - - c.drawPath(mLimitLinePath, mLimitLinePaint); - } - - public void renderLimitLineLabel(Canvas c, LimitLine limitLine, float[] position, float yOffset) { - String label = limitLine.getLabel(); - - // if drawing the limit-value label is enabled - if (label != null && !label.equals("")) { - - mLimitLinePaint.setStyle(limitLine.getTextStyle()); - mLimitLinePaint.setPathEffect(null); - mLimitLinePaint.setColor(limitLine.getTextColor()); - mLimitLinePaint.setStrokeWidth(0.5f); - mLimitLinePaint.setTextSize(limitLine.getTextSize()); - - - float xOffset = limitLine.getLineWidth() + limitLine.getXOffset(); - - final LimitLine.LimitLabelPosition labelPosition = limitLine.getLabelPosition(); - - if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_TOP) { - - final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, position[0] + xOffset, viewPortHandler.contentTop() + yOffset + labelLineHeight, - mLimitLinePaint); - } else if (labelPosition == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { - - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, position[0] + xOffset, viewPortHandler.contentBottom() - yOffset, mLimitLinePaint); - } else if (labelPosition == LimitLine.LimitLabelPosition.LEFT_TOP) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); - c.drawText(label, position[0] - xOffset, viewPortHandler.contentTop() + yOffset + labelLineHeight, - mLimitLinePaint); - } else { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, position[0] - xOffset, viewPortHandler.contentBottom() - yOffset, mLimitLinePaint); - } - } - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.kt new file mode 100644 index 0000000000..f6c05c05d0 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.kt @@ -0,0 +1,403 @@ +package com.github.mikephil.charting.renderer + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Paint.Align +import android.graphics.Path +import android.graphics.RectF +import com.github.mikephil.charting.components.LimitLine +import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.XAxis.XAxisPosition +import com.github.mikephil.charting.utils.FSize +import com.github.mikephil.charting.utils.MPPointD +import com.github.mikephil.charting.utils.MPPointF +import com.github.mikephil.charting.utils.Transformer +import com.github.mikephil.charting.utils.Utils +import com.github.mikephil.charting.utils.ViewPortHandler + +open class XAxisRenderer( + viewPortHandler: ViewPortHandler, + @JvmField protected var xAxis: XAxis, + trans: Transformer? +) : + AxisRenderer(viewPortHandler, trans, xAxis) { + protected fun setupGridPaint() { + paintGrid!!.color = xAxis.gridColor + paintGrid!!.strokeWidth = xAxis.gridLineWidth + paintGrid!!.setPathEffect(xAxis.gridDashPathEffect) + } + + override fun computeAxis(min: Float, max: Float, inverted: Boolean) { + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + + var minLocal = min + var maxLocal = max + if (viewPortHandler.contentWidth() > 10 && !viewPortHandler.isFullyZoomedOutX) { + val p1 = transformer!!.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentTop()) + val p2 = transformer!!.getValuesByTouchPoint(viewPortHandler.contentRight(), viewPortHandler.contentTop()) + + if (inverted) { + minLocal = p2.x.toFloat() + maxLocal = p1.x.toFloat() + } else { + minLocal = p1.x.toFloat() + maxLocal = p2.x.toFloat() + } + + MPPointD.recycleInstance(p1) + MPPointD.recycleInstance(p2) + } + + computeAxisValues(minLocal, maxLocal) + } + + override fun computeAxisValues(min: Float, max: Float) { + super.computeAxisValues(min, max) + + computeSize() + } + + protected open fun computeSize() { + val longest = xAxis.longestLabel + + paintAxisLabels!!.setTypeface(xAxis.typeface) + paintAxisLabels!!.textSize = xAxis.textSize + + val labelSize = Utils.calcTextSize(paintAxisLabels, longest) + + val labelWidth = labelSize.width + val labelHeight = Utils.calcTextHeight(paintAxisLabels, "Q").toFloat() + + val labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees( + labelWidth, + labelHeight, + xAxis.labelRotationAngle + ) + + + xAxis.mLabelWidth = Math.round(labelRotatedSize.width) + xAxis.mLabelHeight = Math.round(labelRotatedSize.height) + + FSize.recycleInstance(labelRotatedSize) + FSize.recycleInstance(labelSize) + } + + override fun renderAxisLabels(c: Canvas) { + if (!xAxis.isEnabled || !xAxis.isDrawLabelsEnabled) return + + val yoffset = xAxis.yOffset + + paintAxisLabels!!.color = xAxis.textColor + + val pointF = MPPointF.getInstance(0f, 0f) + if (xAxis.position == XAxisPosition.TOP) { + pointF.x = 0.5f + pointF.y = 1.0f + drawLabels(c, viewPortHandler.contentTop() - yoffset, pointF) + } else if (xAxis.position == XAxisPosition.TOP_INSIDE) { + pointF.x = 0.5f + pointF.y = 1.0f + drawLabels(c, viewPortHandler.contentTop() + yoffset + xAxis.mLabelHeight, pointF) + } else if (xAxis.position == XAxisPosition.BOTTOM) { + pointF.x = 0.5f + pointF.y = 0.0f + drawLabels(c, viewPortHandler.contentBottom() + yoffset, pointF) + } else if (xAxis.position == XAxisPosition.BOTTOM_INSIDE) { + pointF.x = 0.5f + pointF.y = 0.0f + drawLabels(c, viewPortHandler.contentBottom() - yoffset - xAxis.mLabelHeight, pointF) + } else { // BOTH SIDED + pointF.x = 0.5f + pointF.y = 1.0f + drawLabels(c, viewPortHandler.contentTop() - yoffset, pointF) + pointF.x = 0.5f + pointF.y = 0.0f + drawLabels(c, viewPortHandler.contentBottom() + yoffset, pointF) + } + MPPointF.recycleInstance(pointF) + } + + override fun renderAxisLine(c: Canvas) { + if (!xAxis.isDrawAxisLineEnabled || !xAxis.isEnabled) return + + paintAxisLine!!.color = xAxis.axisLineColor + paintAxisLine!!.strokeWidth = xAxis.axisLineWidth + paintAxisLine!!.setPathEffect(xAxis.axisLineDashPathEffect) + + if (xAxis.position == XAxisPosition.TOP || xAxis.position == XAxisPosition.TOP_INSIDE || xAxis.position == XAxisPosition.BOTH_SIDED) { + c.drawLine( + viewPortHandler.contentLeft(), + viewPortHandler.contentTop(), viewPortHandler.contentRight(), + viewPortHandler.contentTop(), paintAxisLine!! + ) + } + + if (xAxis.position == XAxisPosition.BOTTOM || xAxis.position == XAxisPosition.BOTTOM_INSIDE || xAxis.position == XAxisPosition.BOTH_SIDED) { + c.drawLine( + viewPortHandler.contentLeft(), + viewPortHandler.contentBottom(), viewPortHandler.contentRight(), + viewPortHandler.contentBottom(), paintAxisLine!! + ) + } + } + + /** + * draws the x-labels on the specified y-position + * + * @param pos + */ + protected open fun drawLabels(c: Canvas?, pos: Float, anchor: MPPointF?) { + val labelRotationAngleDegrees = xAxis.labelRotationAngle + val centeringEnabled = xAxis.isCenterAxisLabelsEnabled + + val positions: FloatArray + + if (xAxis.isShowSpecificPositions) { + positions = FloatArray(xAxis.specificPositions.size * 2) + var i = 0 + while (i < positions.size) { + positions[i] = xAxis.specificPositions[i / 2] + i += 2 + } + } else { + positions = FloatArray(xAxis.mEntryCount * 2) + var i = 0 + while (i < positions.size) { + // only fill x values + if (centeringEnabled) { + positions[i] = xAxis.mCenteredEntries[i / 2] + } else { + positions[i] = xAxis.mEntries[i / 2] + } + i += 2 + } + } + + transformer!!.pointValuesToPixel(positions) + + var i = 0 + while (i < positions.size) { + var x = positions[i] + + if (viewPortHandler.isInBoundsX(x)) { + val label = if (xAxis.isShowSpecificPositions) + xAxis.valueFormatter.getFormattedValue(xAxis.specificPositions[i / 2], xAxis) + else + xAxis.valueFormatter.getFormattedValue(xAxis.mEntries[i / 2], xAxis) + + if (xAxis.isAvoidFirstLastClippingEnabled) { + // avoid clipping of the last + + if (i / 2 == xAxis.mEntryCount - 1 && xAxis.mEntryCount > 1) { + val width = Utils.calcTextWidth(paintAxisLabels, label).toFloat() + + if (width > viewPortHandler.offsetRight() * 2 + && x + width > viewPortHandler.chartWidth + ) x -= width / 2 + + // avoid clipping of the first + } else if (i == 0) { + val width = Utils.calcTextWidth(paintAxisLabels, label).toFloat() + x += width / 2 + } + } + + drawLabel(c, label, x, pos, anchor, labelRotationAngleDegrees) + } + i += 2 + } + } + + protected fun drawLabel(c: Canvas?, formattedLabel: String?, x: Float, y: Float, anchor: MPPointF?, angleDegrees: Float) { + Utils.drawXAxisValue(c, formattedLabel, x, y, paintAxisLabels, anchor, angleDegrees) + } + + protected var mRenderGridLinesPath: Path = Path() + protected var mRenderGridLinesBuffer: FloatArray = FloatArray(2) + override fun renderGridLines(c: Canvas) { + if (!xAxis.isDrawGridLinesEnabled || !xAxis.isEnabled) return + + val clipRestoreCount = c.save() + c.clipRect(gridClippingRect!!) + + if (axis.isShowSpecificPositions) { + if (mRenderGridLinesBuffer.size != axis.specificPositions.size * 2) { + mRenderGridLinesBuffer = FloatArray(xAxis.specificPositions.size * 2) + } + } else { + if (mRenderGridLinesBuffer.size != axis.mEntryCount * 2) { + mRenderGridLinesBuffer = FloatArray(xAxis.mEntryCount * 2) + } + } + val positions = mRenderGridLinesBuffer + + run { + var i = 0 + while (i < positions.size) { + if (axis.isShowSpecificPositions) { + positions[i] = xAxis.specificPositions[i / 2] + positions[i + 1] = xAxis.specificPositions[i / 2] + } else { + positions[i] = xAxis.mEntries[i / 2] + positions[i + 1] = xAxis.mEntries[i / 2] + } + i += 2 + } + } + + transformer!!.pointValuesToPixel(positions) + + setupGridPaint() + + val gridLinePath = mRenderGridLinesPath + gridLinePath.reset() + + var i = 0 + while (i < positions.size) { + drawGridLine(c, positions[i], positions[i + 1], gridLinePath) + i += 2 + } + + c.restoreToCount(clipRestoreCount) + } + + @JvmField + protected var mGridClippingRect: RectF = RectF() + + open val gridClippingRect: RectF? + get() { + mGridClippingRect.set(viewPortHandler.contentRect) + mGridClippingRect.inset(-axis.gridLineWidth, 0f) + return mGridClippingRect + } + + /** + * Draws the grid line at the specified position using the provided path. + * + * @param c + * @param x + * @param y + * @param gridLinePath + */ + protected open fun drawGridLine(c: Canvas, x: Float, y: Float, gridLinePath: Path) { + gridLinePath.moveTo(x, viewPortHandler.contentBottom()) + gridLinePath.lineTo(x, viewPortHandler.contentTop()) + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, paintGrid!!) + + gridLinePath.reset() + } + + @JvmField + protected var mRenderLimitLinesBuffer: FloatArray = FloatArray(2) + + @JvmField + protected var mLimitLineClippingRect: RectF = RectF() + + /** + * Draws the LimitLines associated with this axis to the screen. + * + * @param c + */ + override fun renderLimitLines(c: Canvas) { + val limitLines = xAxis.limitLines + + if (limitLines == null || limitLines.size <= 0) return + + val position = mRenderLimitLinesBuffer + position[0] = 0f + position[1] = 0f + + for (i in limitLines.indices) { + val l = limitLines[i] + + if (!l.isEnabled) continue + + val clipRestoreCount = c.save() + mLimitLineClippingRect.set(viewPortHandler.contentRect) + mLimitLineClippingRect.inset(-l.lineWidth, 0f) + c.clipRect(mLimitLineClippingRect) + + position[0] = l.limit + position[1] = 0f + + transformer!!.pointValuesToPixel(position) + + renderLimitLineLine(c, l, position) + renderLimitLineLabel(c, l, position, 2f + l.yOffset) + + c.restoreToCount(clipRestoreCount) + } + } + + var mLimitLineSegmentsBuffer: FloatArray = FloatArray(4) + private val mLimitLinePath = Path() + + init { + paintAxisLabels!!.color = Color.BLACK + paintAxisLabels!!.textAlign = Align.CENTER + paintAxisLabels!!.textSize = Utils.convertDpToPixel(10f) + } + + fun renderLimitLineLine(c: Canvas, limitLine: LimitLine, position: FloatArray) { + mLimitLineSegmentsBuffer[0] = position[0] + mLimitLineSegmentsBuffer[1] = viewPortHandler.contentTop() + mLimitLineSegmentsBuffer[2] = position[0] + mLimitLineSegmentsBuffer[3] = viewPortHandler.contentBottom() + + mLimitLinePath.reset() + mLimitLinePath.moveTo(mLimitLineSegmentsBuffer[0], mLimitLineSegmentsBuffer[1]) + mLimitLinePath.lineTo(mLimitLineSegmentsBuffer[2], mLimitLineSegmentsBuffer[3]) + + limitLinePaint!!.style = Paint.Style.STROKE + limitLinePaint!!.color = limitLine.lineColor + limitLinePaint!!.strokeWidth = limitLine.lineWidth + limitLinePaint!!.setPathEffect(limitLine.dashPathEffect) + + c.drawPath(mLimitLinePath, limitLinePaint!!) + } + + fun renderLimitLineLabel(c: Canvas, limitLine: LimitLine, position: FloatArray, yOffset: Float) { + val label = limitLine.label + + // if drawing the limit-value label is enabled + if (label != null && label != "") { + limitLinePaint!!.style = limitLine.textStyle + limitLinePaint!!.setPathEffect(null) + limitLinePaint!!.color = limitLine.textColor + limitLinePaint!!.strokeWidth = 0.5f + limitLinePaint!!.textSize = limitLine.textSize + + + val xOffset = limitLine.lineWidth + limitLine.xOffset + + val labelPosition = limitLine.labelPosition + + if (labelPosition == LimitLabelPosition.RIGHT_TOP) { + val labelLineHeight = Utils.calcTextHeight(limitLinePaint, label).toFloat() + limitLinePaint!!.textAlign = Align.LEFT + c.drawText( + label, position[0] + xOffset, viewPortHandler.contentTop() + yOffset + labelLineHeight, + limitLinePaint!! + ) + } else if (labelPosition == LimitLabelPosition.RIGHT_BOTTOM) { + limitLinePaint!!.textAlign = Align.LEFT + c.drawText(label, position[0] + xOffset, viewPortHandler.contentBottom() - yOffset, limitLinePaint!!) + } else if (labelPosition == LimitLabelPosition.LEFT_TOP) { + limitLinePaint!!.textAlign = Align.RIGHT + val labelLineHeight = Utils.calcTextHeight(limitLinePaint, label).toFloat() + c.drawText( + label, position[0] - xOffset, viewPortHandler.contentTop() + yOffset + labelLineHeight, + limitLinePaint!! + ) + } else { + limitLinePaint!!.textAlign = Align.RIGHT + c.drawText(label, position[0] - xOffset, viewPortHandler.contentBottom() - yOffset, limitLinePaint!!) + } + } + } +} 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 deleted file mode 100644 index d469f81437..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.java +++ /dev/null @@ -1,308 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.Path; -import android.graphics.RectF; - -import com.github.mikephil.charting.charts.BarChart; -import com.github.mikephil.charting.components.LimitLine; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.components.XAxis.XAxisPosition; -import com.github.mikephil.charting.utils.FSize; -import com.github.mikephil.charting.utils.MPPointF; -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; - -import java.util.List; - -public class XAxisRendererHorizontalBarChart extends XAxisRenderer { - - protected BarChart mChart; - - public XAxisRendererHorizontalBarChart(ViewPortHandler viewPortHandler, XAxis xAxis, - Transformer trans, BarChart chart) { - super(viewPortHandler, xAxis, trans); - - this.mChart = chart; - } - - @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 (viewPortHandler.contentWidth() > 10 && !viewPortHandler.isFullyZoomedOutY()) { - - MPPointD p1 = mTrans.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentBottom()); - MPPointD p2 = mTrans.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentTop()); - - 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); - } - - @Override - protected void computeSize() { - - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - - String longest = mXAxis.getLongestLabel(); - - final FSize labelSize = Utils.calcTextSize(mAxisLabelPaint, longest); - - final float labelWidth = (int)(labelSize.width + mXAxis.getXOffset() * 3.5f); - final float labelHeight = labelSize.height; - - final FSize labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees( - labelWidth, - labelHeight, - mXAxis.getLabelRotationAngle()); - - mXAxis.mLabelWidth = Math.round(labelRotatedSize.width); - mXAxis.mLabelHeight = Math.round(labelRotatedSize.height); - - FSize.recycleInstance(labelRotatedSize); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) - return; - - float xoffset = mXAxis.getXOffset(); - - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setColor(mXAxis.getTextColor()); - - MPPointF pointF = MPPointF.getInstance(0,0); - - if (mXAxis.getPosition() == XAxisPosition.TOP) { - pointF.x = 0.0f; - pointF.y = 0.5f; - drawLabels(c, viewPortHandler.contentRight() + xoffset, pointF); - - } else if (mXAxis.getPosition() == XAxisPosition.TOP_INSIDE) { - pointF.x = 1.0f; - pointF.y = 0.5f; - drawLabels(c, viewPortHandler.contentRight() - xoffset, pointF); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM) { - pointF.x = 1.0f; - pointF.y = 0.5f; - drawLabels(c, viewPortHandler.contentLeft() - xoffset, pointF); - - } else if (mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE) { - pointF.x = 1.0f; - pointF.y = 0.5f; - drawLabels(c, viewPortHandler.contentLeft() + xoffset, pointF); - - } else { // BOTH SIDED - pointF.x = 0.0f; - pointF.y = 0.5f; - drawLabels(c, viewPortHandler.contentRight() + xoffset, pointF); - pointF.x = 1.0f; - pointF.y = 0.5f; - drawLabels(c, viewPortHandler.contentLeft() - xoffset, pointF); - } - - MPPointF.recycleInstance(pointF); - } - - @Override - protected void drawLabels(Canvas c, float pos, MPPointF anchor) { - - final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - boolean centeringEnabled = mXAxis.isCenterAxisLabelsEnabled(); - - float[] positions = new float[mXAxis.mEntryCount * 2]; - - for (int i = 0; i < positions.length; i += 2) { - - // only fill x values - if (centeringEnabled) { - positions[i + 1] = mXAxis.mCenteredEntries[i / 2]; - } else { - positions[i + 1] = mXAxis.mEntries[i / 2]; - } - } - - mTrans.pointValuesToPixel(positions); - - for (int i = 0; i < positions.length; i += 2) { - - float y = positions[i + 1]; - - if (viewPortHandler.isInBoundsY(y)) { - - String label = mXAxis.getValueFormatter().getFormattedValue(mXAxis.mEntries[i / 2], mXAxis); - drawLabel(c, label, pos, y, anchor, labelRotationAngleDegrees); - } - } - } - - @Override - public RectF getGridClippingRect() { - mGridClippingRect.set(viewPortHandler.getContentRect()); - mGridClippingRect.inset(0.f, -mAxis.getGridLineWidth()); - return mGridClippingRect; - } - - @Override - protected void drawGridLine(Canvas c, float x, float y, Path gridLinePath) { - - gridLinePath.moveTo(viewPortHandler.contentRight(), y); - gridLinePath.lineTo(viewPortHandler.contentLeft(), y); - - // draw a path because lines don't support dashing on lower android versions - c.drawPath(gridLinePath, mGridPaint); - - gridLinePath.reset(); - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mXAxis.isDrawAxisLineEnabled() || !mXAxis.isEnabled()) - return; - - mAxisLinePaint.setColor(mXAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mXAxis.getAxisLineWidth()); - - if (mXAxis.getPosition() == XAxisPosition.TOP - || mXAxis.getPosition() == XAxisPosition.TOP_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(viewPortHandler.contentRight(), - viewPortHandler.contentTop(), viewPortHandler.contentRight(), - viewPortHandler.contentBottom(), mAxisLinePaint); - } - - if (mXAxis.getPosition() == XAxisPosition.BOTTOM - || mXAxis.getPosition() == XAxisPosition.BOTTOM_INSIDE - || mXAxis.getPosition() == XAxisPosition.BOTH_SIDED) { - c.drawLine(viewPortHandler.contentLeft(), - viewPortHandler.contentTop(), viewPortHandler.contentLeft(), - viewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - protected Path mRenderLimitLinesPathBuffer = new Path(); - /** - * Draws the LimitLines associated with this axis to the screen. - * This is the standard YAxis renderer using the XAxis limit lines. - * - * @param c - */ - @Override - public void renderLimitLines(Canvas c) { - - List limitLines = mXAxis.getLimitLines(); - - if (limitLines == null || limitLines.size() <= 0) - return; - - float[] pts = mRenderLimitLinesBuffer; - pts[0] = 0; - pts[1] = 0; - - Path limitLinePath = mRenderLimitLinesPathBuffer; - limitLinePath.reset(); - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - if(!l.isEnabled()) - continue; - - int clipRestoreCount = c.save(); - mLimitLineClippingRect.set(viewPortHandler.getContentRect()); - mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); - c.clipRect(mLimitLineClippingRect); - - mLimitLinePaint.setStyle(Paint.Style.STROKE); - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - - pts[1] = l.getLimit(); - - mTrans.pointValuesToPixel(pts); - - limitLinePath.moveTo(viewPortHandler.contentLeft(), pts[1]); - limitLinePath.lineTo(viewPortHandler.contentRight(), pts[1]); - - c.drawPath(limitLinePath, mLimitLinePaint); - limitLinePath.reset(); - // c.drawLines(pts, mLimitLinePaint); - - String label = l.getLabel(); - - // if drawing the limit-value label is enabled - if (label != null && !label.equals("")) { - - mLimitLinePaint.setStyle(l.getTextStyle()); - mLimitLinePaint.setPathEffect(null); - mLimitLinePaint.setColor(l.getTextColor()); - mLimitLinePaint.setStrokeWidth(0.5f); - mLimitLinePaint.setTextSize(l.getTextSize()); - - final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); - float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); - float yOffset = l.getLineWidth() + labelLineHeight + l.getYOffset(); - - final LimitLine.LimitLabelPosition position = l.getLabelPosition(); - - if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, - viewPortHandler.contentRight() - xOffset, - pts[1] - yOffset + labelLineHeight, mLimitLinePaint); - - } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, - viewPortHandler.contentRight() - xOffset, - pts[1] + yOffset, mLimitLinePaint); - - } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { - - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, - viewPortHandler.contentLeft() + xOffset, - pts[1] - yOffset + labelLineHeight, mLimitLinePaint); - - } else { - - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, - viewPortHandler.offsetLeft() + xOffset, - pts[1] + yOffset, mLimitLinePaint); - } - } - - c.restoreToCount(clipRestoreCount); - } - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.kt new file mode 100644 index 0000000000..248f3a1755 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererHorizontalBarChart.kt @@ -0,0 +1,279 @@ +package com.github.mikephil.charting.renderer + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Paint.Align +import android.graphics.Path +import android.graphics.RectF +import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.components.XAxis.XAxisPosition +import com.github.mikephil.charting.utils.FSize +import com.github.mikephil.charting.utils.MPPointD +import com.github.mikephil.charting.utils.MPPointF +import com.github.mikephil.charting.utils.Transformer +import com.github.mikephil.charting.utils.Utils +import com.github.mikephil.charting.utils.ViewPortHandler + +class XAxisRendererHorizontalBarChart( + viewPortHandler: ViewPortHandler, xAxis: XAxis, + trans: Transformer? +) : XAxisRenderer(viewPortHandler, xAxis, trans) { + + protected var renderLimitLinesPathBuffer: Path = Path() + + override fun computeAxis(min: Float, max: Float, inverted: Boolean) { + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + + var minLocal = min + var maxLocal = max + if (viewPortHandler.contentWidth() > 10 && !viewPortHandler.isFullyZoomedOutY) { + val p1 = transformer!!.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentBottom()) + val p2 = transformer!!.getValuesByTouchPoint(viewPortHandler.contentLeft(), viewPortHandler.contentTop()) + + if (inverted) { + minLocal = p2.y.toFloat() + maxLocal = p1.y.toFloat() + } else { + minLocal = p1.y.toFloat() + maxLocal = p2.y.toFloat() + } + + MPPointD.recycleInstance(p1) + MPPointD.recycleInstance(p2) + } + + computeAxisValues(minLocal, maxLocal) + } + + override fun computeSize() { + paintAxisLabels!!.setTypeface(xAxis.typeface) + paintAxisLabels!!.textSize = xAxis.textSize + + val longest = xAxis.longestLabel + + val labelSize = Utils.calcTextSize(paintAxisLabels, longest) + + val labelWidth = (labelSize.width + xAxis.xOffset * 3.5f).toInt().toFloat() + val labelHeight = labelSize.height + + val labelRotatedSize = Utils.getSizeOfRotatedRectangleByDegrees( + labelWidth, + labelHeight, + xAxis.labelRotationAngle + ) + + xAxis.mLabelWidth = Math.round(labelRotatedSize.width) + xAxis.mLabelHeight = Math.round(labelRotatedSize.height) + + FSize.recycleInstance(labelRotatedSize) + } + + override fun renderAxisLabels(c: Canvas) { + if (!xAxis.isEnabled || !xAxis.isDrawLabelsEnabled) return + + val xoffset = xAxis.xOffset + + paintAxisLabels!!.setTypeface(xAxis.typeface) + paintAxisLabels!!.textSize = xAxis.textSize + paintAxisLabels!!.color = xAxis.textColor + + val pointF = MPPointF.getInstance(0f, 0f) + + if (xAxis.position == XAxisPosition.TOP) { + pointF.x = 0.0f + pointF.y = 0.5f + drawLabels(c, viewPortHandler.contentRight() + xoffset, pointF) + } else if (xAxis.position == XAxisPosition.TOP_INSIDE) { + pointF.x = 1.0f + pointF.y = 0.5f + drawLabels(c, viewPortHandler.contentRight() - xoffset, pointF) + } else if (xAxis.position == XAxisPosition.BOTTOM) { + pointF.x = 1.0f + pointF.y = 0.5f + drawLabels(c, viewPortHandler.contentLeft() - xoffset, pointF) + } else if (xAxis.position == XAxisPosition.BOTTOM_INSIDE) { + pointF.x = 1.0f + pointF.y = 0.5f + drawLabels(c, viewPortHandler.contentLeft() + xoffset, pointF) + } else { // BOTH SIDED + pointF.x = 0.0f + pointF.y = 0.5f + drawLabels(c, viewPortHandler.contentRight() + xoffset, pointF) + pointF.x = 1.0f + pointF.y = 0.5f + drawLabels(c, viewPortHandler.contentLeft() - xoffset, pointF) + } + + MPPointF.recycleInstance(pointF) + } + + override fun drawLabels(c: Canvas?, pos: Float, anchor: MPPointF?) { + val labelRotationAngleDegrees = xAxis.labelRotationAngle + val centeringEnabled = xAxis.isCenterAxisLabelsEnabled + + val positions = FloatArray(xAxis.mEntryCount * 2) + + run { + var i = 0 + while (i < positions.size) { + // only fill x values + if (centeringEnabled) { + positions[i + 1] = xAxis.mCenteredEntries[i / 2] + } else { + positions[i + 1] = xAxis.mEntries[i / 2] + } + i += 2 + } + } + + transformer!!.pointValuesToPixel(positions) + + var i = 0 + while (i < positions.size) { + val y = positions[i + 1] + + if (viewPortHandler.isInBoundsY(y)) { + val label = xAxis.valueFormatter.getFormattedValue(xAxis.mEntries[i / 2], xAxis) + drawLabel(c, label, pos, y, anchor, labelRotationAngleDegrees) + } + i += 2 + } + } + + override val gridClippingRect: RectF + get() { + mGridClippingRect.set(viewPortHandler.contentRect) + mGridClippingRect.inset(0f, -axis.gridLineWidth) + return mGridClippingRect + } + + override fun drawGridLine(c: Canvas, x: Float, y: Float, gridLinePath: Path) { + gridLinePath.moveTo(viewPortHandler.contentRight(), y) + gridLinePath.lineTo(viewPortHandler.contentLeft(), y) + + // draw a path because lines don't support dashing on lower android versions + c.drawPath(gridLinePath, paintGrid!!) + + gridLinePath.reset() + } + + override fun renderAxisLine(c: Canvas) { + if (!xAxis.isDrawAxisLineEnabled || !xAxis.isEnabled) return + + paintAxisLine!!.color = xAxis.axisLineColor + paintAxisLine!!.strokeWidth = xAxis.axisLineWidth + + if (xAxis.position == XAxisPosition.TOP || xAxis.position == XAxisPosition.TOP_INSIDE || xAxis.position == XAxisPosition.BOTH_SIDED) { + c.drawLine( + viewPortHandler.contentRight(), + viewPortHandler.contentTop(), viewPortHandler.contentRight(), + viewPortHandler.contentBottom(), paintAxisLine!! + ) + } + + if (xAxis.position == XAxisPosition.BOTTOM || xAxis.position == XAxisPosition.BOTTOM_INSIDE || xAxis.position == XAxisPosition.BOTH_SIDED) { + c.drawLine( + viewPortHandler.contentLeft(), + viewPortHandler.contentTop(), viewPortHandler.contentLeft(), + viewPortHandler.contentBottom(), paintAxisLine!! + ) + } + } + + /** + * Draws the LimitLines associated with this axis to the screen. + * This is the standard YAxis renderer using the XAxis limit lines. + * + * @param c + */ + override fun renderLimitLines(c: Canvas) { + val limitLines = xAxis.limitLines + + if (limitLines == null || limitLines.size <= 0) return + + val pts = mRenderLimitLinesBuffer + pts[0] = 0f + pts[1] = 0f + + val limitLinePath = renderLimitLinesPathBuffer + limitLinePath.reset() + + for (i in limitLines.indices) { + val l = limitLines[i] + + if (!l.isEnabled) continue + + val clipRestoreCount = c.save() + mLimitLineClippingRect.set(viewPortHandler.contentRect) + mLimitLineClippingRect.inset(0f, -l.lineWidth) + c.clipRect(mLimitLineClippingRect) + + limitLinePaint!!.style = Paint.Style.STROKE + limitLinePaint!!.color = l.lineColor + limitLinePaint!!.strokeWidth = l.lineWidth + limitLinePaint!!.setPathEffect(l.dashPathEffect) + + pts[1] = l.limit + + transformer!!.pointValuesToPixel(pts) + + limitLinePath.moveTo(viewPortHandler.contentLeft(), pts[1]) + limitLinePath.lineTo(viewPortHandler.contentRight(), pts[1]) + + c.drawPath(limitLinePath, limitLinePaint!!) + limitLinePath.reset() + + // c.drawLines(pts, mLimitLinePaint); + val label = l.label + + // if drawing the limit-value label is enabled + if (label != null && label != "") { + limitLinePaint!!.style = l.textStyle + limitLinePaint!!.setPathEffect(null) + limitLinePaint!!.color = l.textColor + limitLinePaint!!.strokeWidth = 0.5f + limitLinePaint!!.textSize = l.textSize + + val labelLineHeight = Utils.calcTextHeight(limitLinePaint, label).toFloat() + val xOffset = Utils.convertDpToPixel(4f) + l.xOffset + val yOffset = l.lineWidth + labelLineHeight + l.yOffset + + val position = l.labelPosition + + if (position == LimitLabelPosition.RIGHT_TOP) { + limitLinePaint!!.textAlign = Align.RIGHT + c.drawText( + label, + viewPortHandler.contentRight() - xOffset, + pts[1] - yOffset + labelLineHeight, limitLinePaint!! + ) + } else if (position == LimitLabelPosition.RIGHT_BOTTOM) { + limitLinePaint!!.textAlign = Align.RIGHT + c.drawText( + label, + viewPortHandler.contentRight() - xOffset, + pts[1] + yOffset, limitLinePaint!! + ) + } else if (position == LimitLabelPosition.LEFT_TOP) { + limitLinePaint!!.textAlign = Align.LEFT + c.drawText( + label, + viewPortHandler.contentLeft() + xOffset, + pts[1] - yOffset + labelLineHeight, limitLinePaint!! + ) + } else { + limitLinePaint!!.textAlign = Align.LEFT + c.drawText( + label, + viewPortHandler.offsetLeft() + xOffset, + pts[1] + yOffset, limitLinePaint!! + ) + } + } + + c.restoreToCount(clipRestoreCount) + } + } +} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java deleted file mode 100644 index c547849274..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.java +++ /dev/null @@ -1,71 +0,0 @@ - -package com.github.mikephil.charting.renderer; - -import android.graphics.Canvas; -import android.graphics.PointF; - -import com.github.mikephil.charting.charts.RadarChart; -import com.github.mikephil.charting.components.XAxis; -import com.github.mikephil.charting.utils.MPPointF; -import com.github.mikephil.charting.utils.Utils; -import com.github.mikephil.charting.utils.ViewPortHandler; - -public class XAxisRendererRadarChart extends XAxisRenderer { - - private RadarChart mChart; - - public XAxisRendererRadarChart(ViewPortHandler viewPortHandler, XAxis xAxis, RadarChart chart) { - super(viewPortHandler, xAxis, null); - - mChart = chart; - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mXAxis.isEnabled() || !mXAxis.isDrawLabelsEnabled()) - return; - - final float labelRotationAngleDegrees = mXAxis.getLabelRotationAngle(); - final MPPointF drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f); - - mAxisLabelPaint.setTypeface(mXAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mXAxis.getTextSize()); - mAxisLabelPaint.setColor(mXAxis.getTextColor()); - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - MPPointF center = mChart.getCenterOffsets(); - MPPointF pOut = MPPointF.getInstance(0,0); - for (int i = 0; i < mChart.getData().getMaxEntryCountSet().getEntryCount(); i++) { - - String label = mXAxis.getValueFormatter().getFormattedValue(i, mXAxis); - - float angle = (sliceangle * i + mChart.getRotationAngle()) % 360f; - - Utils.getPosition(center, mChart.getYRange() * factor - + mXAxis.mLabelWidth / 2f, angle, pOut); - - drawLabel(c, label, pOut.x, pOut.y - mXAxis.mLabelHeight / 2.f, - drawLabelAnchor, labelRotationAngleDegrees); - } - - MPPointF.recycleInstance(center); - MPPointF.recycleInstance(pOut); - MPPointF.recycleInstance(drawLabelAnchor); - } - - /** - * XAxis LimitLines on RadarChart not yet supported. - * - * @param c - */ - @Override - public void renderLimitLines(Canvas c) { - // this space intentionally left blank - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.kt new file mode 100644 index 0000000000..98c5829aeb --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRendererRadarChart.kt @@ -0,0 +1,54 @@ +package com.github.mikephil.charting.renderer + +import android.graphics.Canvas +import com.github.mikephil.charting.charts.RadarChart +import com.github.mikephil.charting.components.XAxis +import com.github.mikephil.charting.utils.MPPointF +import com.github.mikephil.charting.utils.Utils +import com.github.mikephil.charting.utils.ViewPortHandler + +class XAxisRendererRadarChart(viewPortHandler: ViewPortHandler, xAxis: XAxis, private val chart: RadarChart) : XAxisRenderer(viewPortHandler, xAxis, null) { + override fun renderAxisLabels(c: Canvas) { + if (!xAxis.isEnabled || !xAxis.isDrawLabelsEnabled) + return + + val labelRotationAngleDegrees = xAxis.labelRotationAngle + val drawLabelAnchor = MPPointF.getInstance(0.5f, 0.25f) + + paintAxisLabels!!.setTypeface(xAxis.typeface) + paintAxisLabels!!.textSize = xAxis.textSize + paintAxisLabels!!.color = xAxis.textColor + + val sliceAngle = chart.sliceAngle + + // calculate the factor that is needed for transforming the value to + // pixels + val factor = chart.factor + + val center = chart.centerOffsets + val pOut = MPPointF.getInstance(0f, 0f) + for (i in 0.. limitLines = mYAxis.getLimitLines(); - - if (limitLines == null || limitLines.size() <= 0) - return; - - float[] pts = mRenderLimitLinesBuffer; - pts[0] = 0; - pts[1] = 0; - Path limitLinePath = mRenderLimitLines; - limitLinePath.reset(); - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - if (!l.isEnabled()) - continue; - - int clipRestoreCount = c.save(); - mLimitLineClippingRect.set(viewPortHandler.getContentRect()); - mLimitLineClippingRect.inset(0.f, -l.getLineWidth()); - c.clipRect(mLimitLineClippingRect); - - mLimitLinePaint.setStyle(Paint.Style.STROKE); - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - - pts[1] = l.getLimit(); - - mTrans.pointValuesToPixel(pts); - - limitLinePath.moveTo(viewPortHandler.contentLeft(), pts[1]); - limitLinePath.lineTo(viewPortHandler.contentRight(), pts[1]); - - c.drawPath(limitLinePath, mLimitLinePaint); - limitLinePath.reset(); - // c.drawLines(pts, mLimitLinePaint); - - String label = l.getLabel(); - - // if drawing the limit-value label is enabled - if (label != null && !label.isEmpty()) { - - mLimitLinePaint.setStyle(l.getTextStyle()); - mLimitLinePaint.setPathEffect(null); - mLimitLinePaint.setColor(l.getTextColor()); - mLimitLinePaint.setTypeface(l.getTypeface()); - mLimitLinePaint.setStrokeWidth(0.5f); - mLimitLinePaint.setTextSize(l.getTextSize()); - - final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); - float xOffset = Utils.convertDpToPixel(4f) + l.getXOffset(); - float yOffset = l.getLineWidth() + labelLineHeight + l.getYOffset(); - - final LimitLine.LimitLabelPosition position = l.getLabelPosition(); - - if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, - viewPortHandler.contentRight() - xOffset, - pts[1] - yOffset + labelLineHeight, mLimitLinePaint); - - } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, - viewPortHandler.contentRight() - xOffset, - pts[1] + yOffset, mLimitLinePaint); - - } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { - - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, - viewPortHandler.contentLeft() + xOffset, - pts[1] - yOffset + labelLineHeight, mLimitLinePaint); - - } else { - - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, - viewPortHandler.offsetLeft() + xOffset, - pts[1] + yOffset, mLimitLinePaint); - } - } - - c.restoreToCount(clipRestoreCount); - } - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.kt new file mode 100644 index 0000000000..081913c41f --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRenderer.kt @@ -0,0 +1,388 @@ +package com.github.mikephil.charting.renderer + +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Paint.Align +import android.graphics.Path +import android.graphics.RectF +import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition +import com.github.mikephil.charting.components.YAxis +import com.github.mikephil.charting.components.YAxis.AxisDependency +import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition +import com.github.mikephil.charting.utils.Transformer +import com.github.mikephil.charting.utils.Utils +import com.github.mikephil.charting.utils.ViewPortHandler + +open class YAxisRenderer(viewPortHandler: ViewPortHandler, @JvmField protected var yAxis: YAxis, trans: Transformer?) : + AxisRenderer(viewPortHandler, trans, yAxis) { + @JvmField + protected var zeroLinePaint: Paint? = null + + /** + * Return the axis label x position based on axis dependency and label position + * @param dependency + * @param labelPosition + * @return + */ + private fun calculateAxisLabelsXPosition(dependency: AxisDependency, labelPosition: YAxisLabelPosition): Float { + val viewPortBase = if (dependency == AxisDependency.LEFT) viewPortHandler.offsetLeft() else viewPortHandler.contentRight() + val xOffset = yAxis.xOffset * (if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) -1 else 1) + + return viewPortBase + xOffset + } + + /** + * Return the text align based on axis dependency and label position + * @param dependency + * @param labelPosition + * @return + */ + private fun getAxisLabelTextAlign(dependency: AxisDependency, labelPosition: YAxisLabelPosition): Align { + if ((dependency == AxisDependency.LEFT) xor (labelPosition == YAxisLabelPosition.OUTSIDE_CHART)) { + return Align.LEFT + } + + return Align.RIGHT + } + + /** + * draws the y-axis labels to the screen + */ + override fun renderAxisLabels(c: Canvas) { + if (!yAxis.isEnabled || !yAxis.isDrawLabelsEnabled) return + + val positions = transformedPositions + + paintAxisLabels?.let { + it.setTypeface(yAxis.typeface) + it.textSize = yAxis.textSize + it.color = yAxis.textColor + + val yOffset = Utils.calcTextHeight(it, "A") / 2.5f + yAxis.yOffset + + val dependency = yAxis.axisDependency + val labelPosition = yAxis.labelPosition + + val xPos = calculateAxisLabelsXPosition(dependency, labelPosition) + it.textAlign = getAxisLabelTextAlign(dependency, labelPosition) + + drawYLabels(c, xPos, positions, yOffset) + } + } + + override fun renderAxisLine(c: Canvas) { + if (!yAxis.isEnabled || !yAxis.isDrawAxisLineEnabled) return + + paintAxisLine?.color = yAxis.axisLineColor + paintAxisLine?.strokeWidth = yAxis.axisLineWidth + + paintAxisLine?.let { + if (yAxis.axisDependency == AxisDependency.LEFT) { + c.drawLine( + viewPortHandler.contentLeft(), viewPortHandler.contentTop(), viewPortHandler.contentLeft(), + viewPortHandler.contentBottom(), it + ) + } else { + c.drawLine( + viewPortHandler.contentRight(), viewPortHandler.contentTop(), viewPortHandler.contentRight(), + viewPortHandler.contentBottom(), it + ) + } + } + } + + /** + * draws the y-labels on the specified x-position + * + * @param fixedPosition + * @param positions + */ + protected open fun drawYLabels(c: Canvas, fixedPosition: Float, positions: FloatArray, offset: Float) { + val from: Int + val to: Int + + if (yAxis.isShowSpecificPositions) { + from = 0 + to = if (yAxis.isDrawTopYLabelEntryEnabled) + yAxis.specificPositions.size + else + (yAxis.specificPositions.size - 1) + } else { + from = if (yAxis.isDrawBottomYLabelEntryEnabled) 0 else 1 + to = if (yAxis.isDrawTopYLabelEntryEnabled) + yAxis.mEntryCount + else + (yAxis.mEntryCount - 1) + } + + val xOffset = yAxis.labelXOffset + + // draw + for (i in from.. 10 && !viewPortHandler.isFullyZoomedOutX()) { - - MPPointD p1 = mTrans.getValuesByTouchPoint(viewPortHandler.contentLeft(), - viewPortHandler.contentTop()); - MPPointD p2 = mTrans.getValuesByTouchPoint(viewPortHandler.contentRight(), - viewPortHandler.contentTop()); - - if (!inverted) { - yMin = (float) p1.x; - yMax = (float) p2.x; - } else { - yMin = (float) p2.x; - yMax = (float) p1.x; - } - - MPPointD.recycleInstance(p1); - MPPointD.recycleInstance(p2); - } - - computeAxisValues(yMin, yMax); - } - - /** - * draws the y-axis labels to the screen - */ - @Override - public void renderAxisLabels(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) - return; - - float[] positions = getTransformedPositions(); - - mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setColor(mYAxis.getTextColor()); - mAxisLabelPaint.setTextAlign(Align.CENTER); - - float baseYOffset = Utils.convertDpToPixel(2.5f); - float textHeight = Utils.calcTextHeight(mAxisLabelPaint, "Q"); - - AxisDependency dependency = mYAxis.getAxisDependency(); - YAxisLabelPosition labelPosition = mYAxis.getLabelPosition(); - - float yPos = 0f; - - if (dependency == AxisDependency.LEFT) { - - if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - yPos = viewPortHandler.contentTop() - baseYOffset; - } else { - yPos = viewPortHandler.contentTop() - baseYOffset; - } - - } else { - - if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { - yPos = viewPortHandler.contentBottom() + textHeight + baseYOffset; - } else { - yPos = viewPortHandler.contentBottom() + textHeight + baseYOffset; - } - } - - drawYLabels(c, yPos, positions, mYAxis.getYOffset()); - } - - @Override - public void renderAxisLine(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawAxisLineEnabled()) - return; - - mAxisLinePaint.setColor(mYAxis.getAxisLineColor()); - mAxisLinePaint.setStrokeWidth(mYAxis.getAxisLineWidth()); - - if (mYAxis.getAxisDependency() == AxisDependency.LEFT) { - c.drawLine(viewPortHandler.contentLeft(), - viewPortHandler.contentTop(), viewPortHandler.contentRight(), - viewPortHandler.contentTop(), mAxisLinePaint); - } else { - c.drawLine(viewPortHandler.contentLeft(), - viewPortHandler.contentBottom(), viewPortHandler.contentRight(), - viewPortHandler.contentBottom(), mAxisLinePaint); - } - } - - /** - * draws the y-labels on the specified x-position - * - * @param fixedPosition - * @param positions - */ - @Override - protected void drawYLabels(Canvas c, float fixedPosition, float[] positions, float offset) { - - mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setColor(mYAxis.getTextColor()); - - final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; - final int to = mYAxis.isDrawTopYLabelEntryEnabled() - ? mYAxis.mEntryCount - : (mYAxis.mEntryCount - 1); - - float xOffset = mYAxis.getLabelXOffset(); - - for (int i = from; i < to; i++) { - - String text = mYAxis.getFormattedLabel(i); - - c.drawText(text, - positions[i * 2], - fixedPosition - offset + xOffset, - mAxisLabelPaint); - } - } - - @Override - protected float[] getTransformedPositions() { - - if(mGetTransformedPositionsBuffer.length != mYAxis.mEntryCount * 2) { - mGetTransformedPositionsBuffer = new float[mYAxis.mEntryCount * 2]; - } - float[] positions = mGetTransformedPositionsBuffer; - - for (int i = 0; i < positions.length; i += 2) { - // only fill x values, y values are not needed for x-labels - positions[i] = mYAxis.mEntries[i / 2]; - } - - mTrans.pointValuesToPixel(positions); - return positions; - } - - @Override - public RectF getGridClippingRect() { - mGridClippingRect.set(viewPortHandler.getContentRect()); - mGridClippingRect.inset(-mAxis.getGridLineWidth(), 0.f); - return mGridClippingRect; - } - - @Override - protected Path linePath(Path p, int i, float[] positions) { - - p.moveTo(positions[i], viewPortHandler.contentTop()); - p.lineTo(positions[i], viewPortHandler.contentBottom()); - - return p; - } - - protected Path mDrawZeroLinePathBuffer = new Path(); - - @Override - protected void drawZeroLine(Canvas c) { - - int clipRestoreCount = c.save(); - mZeroLineClippingRect.set(viewPortHandler.getContentRect()); - mZeroLineClippingRect.inset(-mYAxis.getZeroLineWidth(), 0.f); - c.clipRect(mLimitLineClippingRect); - - // draw zero line - MPPointD pos = mTrans.getPixelForValues(0f, 0f); - - mZeroLinePaint.setColor(mYAxis.getZeroLineColor()); - mZeroLinePaint.setStrokeWidth(mYAxis.getZeroLineWidth()); - - Path zeroLinePath = mDrawZeroLinePathBuffer; - zeroLinePath.reset(); - - zeroLinePath.moveTo((float) pos.x - 1, viewPortHandler.contentTop()); - zeroLinePath.lineTo((float) pos.x - 1, viewPortHandler.contentBottom()); - - // draw a path because lines don't support dashing on lower android versions - c.drawPath(zeroLinePath, mZeroLinePaint); - - c.restoreToCount(clipRestoreCount); - } - - protected Path mRenderLimitLinesPathBuffer = new Path(); - protected float[] mRenderLimitLinesBuffer = new float[4]; - /** - * Draws the LimitLines associated with this axis to the screen. - * This is the standard XAxis renderer using the YAxis limit lines. - * - * @param c - */ - @Override - public void renderLimitLines(Canvas c) { - - List limitLines = mYAxis.getLimitLines(); - - if (limitLines == null || limitLines.size() <= 0) - return; - - float[] pts = mRenderLimitLinesBuffer; - pts[0] = 0; - pts[1] = 0; - pts[2] = 0; - pts[3] = 0; - Path limitLinePath = mRenderLimitLinesPathBuffer; - limitLinePath.reset(); - - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - if (!l.isEnabled()) - continue; - - int clipRestoreCount = c.save(); - mLimitLineClippingRect.set(viewPortHandler.getContentRect()); - mLimitLineClippingRect.inset(-l.getLineWidth(), 0.f); - c.clipRect(mLimitLineClippingRect); - - pts[0] = l.getLimit(); - pts[2] = l.getLimit(); - - mTrans.pointValuesToPixel(pts); - - pts[1] = viewPortHandler.contentTop(); - pts[3] = viewPortHandler.contentBottom(); - - limitLinePath.moveTo(pts[0], pts[1]); - limitLinePath.lineTo(pts[2], pts[3]); - - mLimitLinePaint.setStyle(Paint.Style.STROKE); - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - - c.drawPath(limitLinePath, mLimitLinePaint); - limitLinePath.reset(); - - String label = l.getLabel(); - - // if drawing the limit-value label is enabled - if (label != null && !label.equals("")) { - - mLimitLinePaint.setStyle(l.getTextStyle()); - mLimitLinePaint.setPathEffect(null); - mLimitLinePaint.setColor(l.getTextColor()); - mLimitLinePaint.setTypeface(l.getTypeface()); - mLimitLinePaint.setStrokeWidth(0.5f); - mLimitLinePaint.setTextSize(l.getTextSize()); - - float xOffset = l.getLineWidth() + l.getXOffset(); - float yOffset = Utils.convertDpToPixel(2f) + l.getYOffset(); - - final LimitLine.LimitLabelPosition position = l.getLabelPosition(); - - if (position == LimitLine.LimitLabelPosition.RIGHT_TOP) { - - final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, pts[0] + xOffset, viewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); - } else if (position == LimitLine.LimitLabelPosition.RIGHT_BOTTOM) { - - mLimitLinePaint.setTextAlign(Align.LEFT); - c.drawText(label, pts[0] + xOffset, viewPortHandler.contentBottom() - yOffset, mLimitLinePaint); - } else if (position == LimitLine.LimitLabelPosition.LEFT_TOP) { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - final float labelLineHeight = Utils.calcTextHeight(mLimitLinePaint, label); - c.drawText(label, pts[0] - xOffset, viewPortHandler.contentTop() + yOffset + labelLineHeight, mLimitLinePaint); - } else { - - mLimitLinePaint.setTextAlign(Align.RIGHT); - c.drawText(label, pts[0] - xOffset, viewPortHandler.contentBottom() - yOffset, mLimitLinePaint); - } - } - - c.restoreToCount(clipRestoreCount); - } - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.kt new file mode 100644 index 0000000000..1ae3fa6ac1 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererHorizontalBarChart.kt @@ -0,0 +1,296 @@ +package com.github.mikephil.charting.renderer + +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Paint.Align +import android.graphics.Path +import android.graphics.RectF +import com.github.mikephil.charting.components.LimitLine.LimitLabelPosition +import com.github.mikephil.charting.components.YAxis +import com.github.mikephil.charting.components.YAxis.AxisDependency +import com.github.mikephil.charting.components.YAxis.YAxisLabelPosition +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 + +class YAxisRendererHorizontalBarChart( + viewPortHandler: ViewPortHandler, yAxis: YAxis, + trans: Transformer? +) : YAxisRenderer(viewPortHandler, yAxis, trans) { + + protected var drawZeroLinePathBuffer: Path = Path() + + /** + * Computes the axis values. + * + * @param min - the minimum y-value in the data object for this axis + * @param max - the maximum y-value in the data object for this axis + */ + override fun computeAxis(min: Float, max: Float, inverted: Boolean) { + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + + var yMin = min + var yMax = max + if (viewPortHandler.contentHeight() > 10 && !viewPortHandler.isFullyZoomedOutX) { + val p1 = transformer!!.getValuesByTouchPoint( + viewPortHandler.contentLeft(), + viewPortHandler.contentTop() + ) + val p2 = transformer!!.getValuesByTouchPoint( + viewPortHandler.contentRight(), + viewPortHandler.contentTop() + ) + + if (!inverted) { + yMin = p1.x.toFloat() + yMax = p2.x.toFloat() + } else { + yMin = p2.x.toFloat() + yMax = p1.x.toFloat() + } + + MPPointD.recycleInstance(p1) + MPPointD.recycleInstance(p2) + } + + computeAxisValues(yMin, yMax) + } + + /** + * draws the y-axis labels to the screen + */ + override fun renderAxisLabels(c: Canvas) { + if (!yAxis.isEnabled || !yAxis.isDrawLabelsEnabled) return + + val positions = transformedPositions + + paintAxisLabels!!.setTypeface(yAxis.typeface) + paintAxisLabels!!.textSize = yAxis.textSize + paintAxisLabels!!.color = yAxis.textColor + paintAxisLabels!!.textAlign = Align.CENTER + + val baseYOffset = Utils.convertDpToPixel(2.5f) + val textHeight = Utils.calcTextHeight(paintAxisLabels, "Q").toFloat() + + val dependency = yAxis.axisDependency + val labelPosition = yAxis.labelPosition + + val yPos: Float = if (dependency == AxisDependency.LEFT) { + if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { + viewPortHandler.contentTop() - baseYOffset + } else { + viewPortHandler.contentTop() - baseYOffset + } + } else { + if (labelPosition == YAxisLabelPosition.OUTSIDE_CHART) { + viewPortHandler.contentBottom() + textHeight + baseYOffset + } else { + viewPortHandler.contentBottom() + textHeight + baseYOffset + } + } + + drawYLabels(c, yPos, positions, yAxis.yOffset) + } + + override fun renderAxisLine(c: Canvas) { + if (!yAxis.isEnabled || !yAxis.isDrawAxisLineEnabled) return + + paintAxisLine!!.color = yAxis.axisLineColor + paintAxisLine!!.strokeWidth = yAxis.axisLineWidth + + if (yAxis.axisDependency == AxisDependency.LEFT) { + c.drawLine( + viewPortHandler.contentLeft(), + viewPortHandler.contentTop(), viewPortHandler.contentRight(), + viewPortHandler.contentTop(), paintAxisLine!! + ) + } else { + c.drawLine( + viewPortHandler.contentLeft(), + viewPortHandler.contentBottom(), viewPortHandler.contentRight(), + viewPortHandler.contentBottom(), paintAxisLine!! + ) + } + } + + /** + * draws the y-labels on the specified x-position + * + * @param fixedPosition + * @param positions + */ + override fun drawYLabels(c: Canvas, fixedPosition: Float, positions: FloatArray, offset: Float) { + paintAxisLabels!!.setTypeface(yAxis.typeface) + paintAxisLabels!!.textSize = yAxis.textSize + paintAxisLabels!!.color = yAxis.textColor + + val from = if (yAxis.isDrawBottomYLabelEntryEnabled) 0 else 1 + val to = if (yAxis.isDrawTopYLabelEntryEnabled) + yAxis.mEntryCount + else + (yAxis.mEntryCount - 1) + + val xOffset = yAxis.labelXOffset + + for (i in from.. 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; - } - } - - mAxis.mAxisMinimum = mAxis.mEntries[0]; - mAxis.mAxisMaximum = mAxis.mEntries[n-1]; - mAxis.mAxisRange = Math.abs(mAxis.mAxisMaximum - mAxis.mAxisMinimum); - } - - @Override - public void renderAxisLabels(Canvas c) { - - if (!mYAxis.isEnabled() || !mYAxis.isDrawLabelsEnabled()) - return; - - mAxisLabelPaint.setTypeface(mYAxis.getTypeface()); - mAxisLabelPaint.setTextSize(mYAxis.getTextSize()); - mAxisLabelPaint.setColor(mYAxis.getTextColor()); - - MPPointF center = mChart.getCenterOffsets(); - MPPointF pOut = MPPointF.getInstance(0,0); - float factor = mChart.getFactor(); - - final int from = mYAxis.isDrawBottomYLabelEntryEnabled() ? 0 : 1; - final int to = mYAxis.isDrawTopYLabelEntryEnabled() - ? mYAxis.mEntryCount - : (mYAxis.mEntryCount - 1); - - float xOffset = mYAxis.getLabelXOffset(); - - for (int j = from; j < to; j++) { - - float r = (mYAxis.mEntries[j] - mYAxis.mAxisMinimum) * factor; - - Utils.getPosition(center, r, mChart.getRotationAngle(), pOut); - - String label = mYAxis.getFormattedLabel(j); - - c.drawText(label, pOut.x + xOffset, pOut.y, mAxisLabelPaint); - } - MPPointF.recycleInstance(center); - MPPointF.recycleInstance(pOut); - } - - private Path mRenderLimitLinesPathBuffer = new Path(); - @Override - public void renderLimitLines(Canvas c) { - - List limitLines = mYAxis.getLimitLines(); - - if (limitLines == null) - return; - - float sliceangle = mChart.getSliceAngle(); - - // calculate the factor that is needed for transforming the value to - // pixels - float factor = mChart.getFactor(); - - MPPointF center = mChart.getCenterOffsets(); - MPPointF pOut = MPPointF.getInstance(0,0); - for (int i = 0; i < limitLines.size(); i++) { - - LimitLine l = limitLines.get(i); - - if (!l.isEnabled()) - continue; - - mLimitLinePaint.setColor(l.getLineColor()); - mLimitLinePaint.setPathEffect(l.getDashPathEffect()); - mLimitLinePaint.setStrokeWidth(l.getLineWidth()); - - float r = (l.getLimit() - mChart.getYChartMin()) * factor; - - Path limitPath = mRenderLimitLinesPathBuffer; - limitPath.reset(); - - - for (int j = 0; j < mChart.getData().getMaxEntryCountSet().getEntryCount(); j++) { - - Utils.getPosition(center, r, sliceangle * j + mChart.getRotationAngle(), pOut); - - if (j == 0) - limitPath.moveTo(pOut.x, pOut.y); - else - limitPath.lineTo(pOut.x, pOut.y); - } - limitPath.close(); - - c.drawPath(limitPath, mLimitLinePaint); - } - MPPointF.recycleInstance(center); - MPPointF.recycleInstance(pOut); - } -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.kt new file mode 100644 index 0000000000..e2a0afcbfd --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/YAxisRendererRadarChart.kt @@ -0,0 +1,208 @@ +package com.github.mikephil.charting.renderer + +import android.graphics.Canvas +import android.graphics.Path +import com.github.mikephil.charting.charts.RadarChart +import com.github.mikephil.charting.components.YAxis +import com.github.mikephil.charting.utils.MPPointF +import com.github.mikephil.charting.utils.Utils +import com.github.mikephil.charting.utils.ViewPortHandler +import kotlin.math.abs +import kotlin.math.ceil +import kotlin.math.floor +import kotlin.math.log10 +import kotlin.math.pow + +class YAxisRendererRadarChart(viewPortHandler: ViewPortHandler, yAxis: YAxis, private val chart: RadarChart) : YAxisRenderer(viewPortHandler, yAxis, null) { + + private val renderLimitLinesPathBuffer = Path() + + override fun computeAxisValues(min: Float, max: Float) { + + val labelCount = axis.labelCount + val range = abs((max - min).toDouble()) + + if (labelCount == 0 || range <= 0 || java.lang.Double.isInfinite(range)) { + axis.mEntries = floatArrayOf() + axis.mCenteredEntries = floatArrayOf() + axis.mEntryCount = 0 + return + } + + // Find out how much spacing (in y value space) between axis values + val rawInterval = range / labelCount + var interval = Utils.roundToNextSignificant(rawInterval).toDouble() + + // 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 (axis.isGranularityEnabled) interval = if (interval < axis.granularity) axis.granularity.toDouble() else interval + + // Normalize interval + val intervalMagnitude = Utils.roundToNextSignificant(10.0.pow(log10(interval).toInt().toDouble())).toDouble() + val intervalSigDigit = (interval / intervalMagnitude).toInt() + 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 = if (floor(10.0 * intervalMagnitude) == 0.0) + interval + else floor(10.0 * intervalMagnitude) + } + + val centeringEnabled = axis.isCenterAxisLabelsEnabled + var n = if (centeringEnabled) 1 else 0 + + // force label count + if (axis.isForceLabelsEnabled) { + val step = range.toFloat() / (labelCount - 1).toFloat() + axis.mEntryCount = labelCount + + if (axis.mEntries.size < labelCount) { + // Ensure stops contains at least numStops elements. + axis.mEntries = FloatArray(labelCount) + } + + var v = min + + for (i in 0..