1 2 package com.github.mikephil.charting.renderer; 3 4 import android.graphics.Canvas; 5 import android.graphics.Color; 6 import android.graphics.Paint; 7 import android.graphics.Paint.Style; 8 9 import com.github.mikephil.charting.components.AxisBase; 10 import com.github.mikephil.charting.utils.MPPointD; 11 import com.github.mikephil.charting.utils.Transformer; 12 import com.github.mikephil.charting.utils.Utils; 13 import com.github.mikephil.charting.utils.ViewPortHandler; 14 15 /** 16 * Baseclass of all axis renderers. 17 * 18 * @author Philipp Jahoda 19 */ 20 public abstract class AxisRenderer extends Renderer { 21 22 /** base axis this axis renderer works with */ 23 protected AxisBase mAxis; 24 25 /** transformer to transform values to screen pixels and return */ 26 protected Transformer mTrans; 27 28 /** 29 * paint object for the grid lines 30 */ 31 protected Paint mGridPaint; 32 33 /** 34 * paint for the x-label values 35 */ 36 protected Paint mAxisLabelPaint; 37 38 /** 39 * paint for the line surrounding the chart 40 */ 41 protected Paint mAxisLinePaint; 42 43 /** 44 * paint used for the limit lines 45 */ 46 protected Paint mLimitLinePaint; 47 AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis)48 public AxisRenderer(ViewPortHandler viewPortHandler, Transformer trans, AxisBase axis) { 49 super(viewPortHandler); 50 51 this.mTrans = trans; 52 this.mAxis = axis; 53 54 if(mViewPortHandler != null) { 55 56 mAxisLabelPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 57 58 mGridPaint = new Paint(); 59 mGridPaint.setColor(Color.GRAY); 60 mGridPaint.setStrokeWidth(1f); 61 mGridPaint.setStyle(Style.STROKE); 62 mGridPaint.setAlpha(90); 63 64 mAxisLinePaint = new Paint(); 65 mAxisLinePaint.setColor(Color.BLACK); 66 mAxisLinePaint.setStrokeWidth(1f); 67 mAxisLinePaint.setStyle(Style.STROKE); 68 69 mLimitLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); 70 mLimitLinePaint.setStyle(Paint.Style.STROKE); 71 } 72 } 73 74 /** 75 * Returns the Paint object used for drawing the axis (labels). 76 * 77 * @return 78 */ getPaintAxisLabels()79 public Paint getPaintAxisLabels() { 80 return mAxisLabelPaint; 81 } 82 83 /** 84 * Returns the Paint object that is used for drawing the grid-lines of the 85 * axis. 86 * 87 * @return 88 */ getPaintGrid()89 public Paint getPaintGrid() { 90 return mGridPaint; 91 } 92 93 /** 94 * Returns the Paint object that is used for drawing the axis-line that goes 95 * alongside the axis. 96 * 97 * @return 98 */ getPaintAxisLine()99 public Paint getPaintAxisLine() { 100 return mAxisLinePaint; 101 } 102 103 /** 104 * Returns the Transformer object used for transforming the axis values. 105 * 106 * @return 107 */ getTransformer()108 public Transformer getTransformer() { 109 return mTrans; 110 } 111 112 /** 113 * Computes the axis values. 114 * 115 * @param min - the minimum value in the data object for this axis 116 * @param max - the maximum value in the data object for this axis 117 */ computeAxis(float min, float max, boolean inverted)118 public void computeAxis(float min, float max, boolean inverted) { 119 120 // calculate the starting and entry point of the y-labels (depending on 121 // zoom / contentrect bounds) 122 if (mViewPortHandler != null && mViewPortHandler.contentWidth() > 10 && !mViewPortHandler.isFullyZoomedOutY()) { 123 124 MPPointD p1 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentTop()); 125 MPPointD p2 = mTrans.getValuesByTouchPoint(mViewPortHandler.contentLeft(), mViewPortHandler.contentBottom()); 126 127 if (!inverted) { 128 129 min = (float) p2.y; 130 max = (float) p1.y; 131 } else { 132 133 min = (float) p1.y; 134 max = (float) p2.y; 135 } 136 137 MPPointD.recycleInstance(p1); 138 MPPointD.recycleInstance(p2); 139 } 140 141 computeAxisValues(min, max); 142 } 143 144 /** 145 * Sets up the axis values. Computes the desired number of labels between the two given extremes. 146 * 147 * @return 148 */ computeAxisValues(float min, float max)149 protected void computeAxisValues(float min, float max) { 150 151 float yMin = min; 152 float yMax = max; 153 154 int labelCount = mAxis.getLabelCount(); 155 double range = Math.abs(yMax - yMin); 156 157 if (labelCount == 0 || range <= 0 || Double.isInfinite(range)) { 158 mAxis.mEntries = new float[]{}; 159 mAxis.mCenteredEntries = new float[]{}; 160 mAxis.mEntryCount = 0; 161 return; 162 } 163 164 // Find out how much spacing (in y value space) between axis values 165 double rawInterval = range / labelCount; 166 double interval = Utils.roundToNextSignificant(rawInterval); 167 168 // If granularity is enabled, then do not allow the interval to go below specified granularity. 169 // This is used to avoid repeated values when rounding values for display. 170 if (mAxis.isGranularityEnabled()) 171 interval = interval < mAxis.getGranularity() ? mAxis.getGranularity() : interval; 172 173 // Normalize interval 174 double intervalMagnitude = Utils.roundToNextSignificant(Math.pow(10, (int) Math.log10(interval))); 175 int intervalSigDigit = (int) (interval / intervalMagnitude); 176 if (intervalSigDigit > 5) { 177 // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 178 // if it's 0.0 after floor(), we use the old value 179 interval = Math.floor(10.0 * intervalMagnitude) == 0.0 180 ? interval 181 : Math.floor(10.0 * intervalMagnitude); 182 183 } 184 185 int n = mAxis.isCenterAxisLabelsEnabled() ? 1 : 0; 186 187 // force label count 188 if (mAxis.isForceLabelsEnabled()) { 189 190 interval = (float) range / (float) (labelCount - 1); 191 mAxis.mEntryCount = labelCount; 192 193 if (mAxis.mEntries.length < labelCount) { 194 // Ensure stops contains at least numStops elements. 195 mAxis.mEntries = new float[labelCount]; 196 } 197 198 float v = min; 199 200 for (int i = 0; i < labelCount; i++) { 201 mAxis.mEntries[i] = v; 202 v += interval; 203 } 204 205 n = labelCount; 206 207 // no forced count 208 } else { 209 210 double first = interval == 0.0 ? 0.0 : Math.ceil(yMin / interval) * interval; 211 if(mAxis.isCenterAxisLabelsEnabled()) { 212 first -= interval; 213 } 214 215 double last = interval == 0.0 ? 0.0 : Utils.nextUp(Math.floor(yMax / interval) * interval); 216 217 double f; 218 int i; 219 220 if (interval != 0.0 && last != first) { 221 for (f = first; f <= last; f += interval) { 222 ++n; 223 } 224 } 225 else if (last == first && n == 0) { 226 n = 1; 227 } 228 229 mAxis.mEntryCount = n; 230 231 if (mAxis.mEntries.length < n) { 232 // Ensure stops contains at least numStops elements. 233 mAxis.mEntries = new float[n]; 234 } 235 236 for (f = first, i = 0; i < n; f += interval, ++i) { 237 238 if (f == 0.0) // Fix for negative zero case (Where value == -0.0, and 0.0 == -0.0) 239 f = 0.0; 240 241 mAxis.mEntries[i] = (float) f; 242 } 243 } 244 245 // set decimals 246 if (interval < 1) { 247 mAxis.mDecimals = (int) Math.ceil(-Math.log10(interval)); 248 } else { 249 mAxis.mDecimals = 0; 250 } 251 252 if (mAxis.isCenterAxisLabelsEnabled()) { 253 254 if (mAxis.mCenteredEntries.length < n) { 255 mAxis.mCenteredEntries = new float[n]; 256 } 257 258 float offset = (float)interval / 2f; 259 260 for (int i = 0; i < n; i++) { 261 mAxis.mCenteredEntries[i] = mAxis.mEntries[i] + offset; 262 } 263 } 264 } 265 266 /** 267 * Draws the axis labels to the screen. 268 * 269 * @param c 270 */ renderAxisLabels(Canvas c)271 public abstract void renderAxisLabels(Canvas c); 272 273 /** 274 * Draws the grid lines belonging to the axis. 275 * 276 * @param c 277 */ renderGridLines(Canvas c)278 public abstract void renderGridLines(Canvas c); 279 280 /** 281 * Draws the line that goes alongside the axis. 282 * 283 * @param c 284 */ renderAxisLine(Canvas c)285 public abstract void renderAxisLine(Canvas c); 286 287 /** 288 * Draws the LimitLines associated with this axis to the screen. 289 * 290 * @param c 291 */ renderLimitLines(Canvas c)292 public abstract void renderLimitLines(Canvas c); 293 } 294