Home | History | Annotate | Download | only in xy
      1 /*
      2  * Copyright 2012 AndroidPlot.com
      3  *
      4  *    Licensed under the Apache License, Version 2.0 (the "License");
      5  *    you may not use this file except in compliance with the License.
      6  *    You may obtain a copy of the License at
      7  *
      8  *        http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  *    Unless required by applicable law or agreed to in writing, software
     11  *    distributed under the License is distributed on an "AS IS" BASIS,
     12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  *    See the License for the specific language governing permissions and
     14  *    limitations under the License.
     15  */
     16 
     17 package com.androidplot.xy;
     18 
     19 import android.graphics.*;
     20 
     21 import com.androidplot.exception.PlotRenderException;
     22 import com.androidplot.ui.LayoutManager;
     23 import com.androidplot.ui.SizeMetrics;
     24 import com.androidplot.ui.widget.Widget;
     25 import com.androidplot.util.FontUtils;
     26 import com.androidplot.util.ValPixConverter;
     27 import com.androidplot.util.ZHash;
     28 import com.androidplot.util.ZIndexable;
     29 
     30 import java.text.DecimalFormat;
     31 import java.text.Format;
     32 
     33 /**
     34  * Displays graphical data annotated with domain and range tick markers.
     35  */
     36 public class XYGraphWidget extends Widget {
     37 
     38     public float getRangeLabelOrientation() {
     39         return rangeLabelOrientation;
     40     }
     41 
     42     public void setRangeLabelOrientation(float rangeLabelOrientation) {
     43         this.rangeLabelOrientation = rangeLabelOrientation;
     44     }
     45 
     46     public float getDomainLabelOrientation() {
     47         return domainLabelOrientation;
     48     }
     49 
     50     public void setDomainLabelOrientation(float domainLabelOrientation) {
     51         this.domainLabelOrientation = domainLabelOrientation;
     52     }
     53 
     54     /**
     55      * Will be used in a future version.
     56      */
     57     public enum XYPlotOrientation {
     58         HORIZONTAL, VERTICAL
     59     }
     60 
     61     private static final int MARKER_LABEL_SPACING = 2;
     62     private static final int CURSOR_LABEL_SPACING = 2; // space between cursor
     63     private static final String TAG = "AndroidPlot";
     64                                                        // lines and label in
     65                                                        // pixels
     66     private float domainLabelWidth = 15; // how many pixels is the area
     67                                          // allocated for domain labels
     68     private float rangeLabelWidth = 41; // ...
     69     private float domainLabelVerticalOffset = -5;
     70     private float domainLabelHorizontalOffset = 0.0f;
     71     private float rangeLabelHorizontalOffset = 1.0f;   // allows tweaking of text position
     72     private float rangeLabelVerticalOffset = 0.0f;  // allows tweaking of text position
     73 
     74     private int ticksPerRangeLabel = 1;
     75     private int ticksPerDomainLabel = 1;
     76     private float gridPaddingTop = 0;
     77     private float gridPaddingBottom = 0;
     78     private float gridPaddingLeft = 0;
     79     private float gridPaddingRight = 0;
     80     private int domainLabelTickExtension = 5;
     81     private int rangeLabelTickExtension = 5;
     82     private Paint gridBackgroundPaint;
     83     private Paint rangeGridLinePaint;
     84     private Paint rangeSubGridLinePaint;
     85     private Paint domainGridLinePaint;
     86     private Paint domainSubGridLinePaint;
     87     private Paint domainLabelPaint;
     88     private Paint rangeLabelPaint;
     89     private Paint domainCursorPaint;
     90     private Paint rangeCursorPaint;
     91     private Paint cursorLabelPaint;
     92     private Paint cursorLabelBackgroundPaint;
     93     private XYPlot plot;
     94     private Format rangeValueFormat;
     95     private Format domainValueFormat;
     96     private Paint domainOriginLinePaint;
     97     private Paint rangeOriginLinePaint;
     98     private Paint domainOriginLabelPaint;
     99     private Paint rangeOriginLabelPaint;
    100     private RectF gridRect;
    101     private RectF paddedGridRect;
    102     private float domainCursorPosition;
    103     private float rangeCursorPosition;
    104     @SuppressWarnings("FieldCanBeLocal")
    105     private boolean drawCursorLabelEnabled = true;
    106     private boolean drawMarkersEnabled = true;
    107 
    108     private boolean rangeAxisLeft = true;
    109     private boolean domainAxisBottom = true;
    110 
    111     private float rangeLabelOrientation;
    112     private float domainLabelOrientation;
    113 
    114     // TODO: consider typing this manager with a special
    115     // axisLabelRegionFormatter
    116     // private ZHash<LineRegion, AxisValueLabelFormatter> domainLabelRegions;
    117     // private ZHash<LineRegion, AxisValueLabelFormatter> rangeLabelRegions;
    118     private ZHash<RectRegion, AxisValueLabelFormatter> axisValueLabelRegions;
    119 
    120     {
    121         gridBackgroundPaint = new Paint();
    122         gridBackgroundPaint.setColor(Color.rgb(140, 140, 140));
    123         gridBackgroundPaint.setStyle(Paint.Style.FILL);
    124         rangeGridLinePaint = new Paint();
    125         rangeGridLinePaint.setColor(Color.rgb(180, 180, 180));
    126         rangeGridLinePaint.setAntiAlias(true);
    127         rangeGridLinePaint.setStyle(Paint.Style.STROKE);
    128         domainGridLinePaint = new Paint(rangeGridLinePaint);
    129         domainSubGridLinePaint = new Paint(domainGridLinePaint);
    130         rangeSubGridLinePaint = new Paint(rangeGridLinePaint);
    131         domainOriginLinePaint = new Paint();
    132         domainOriginLinePaint.setColor(Color.WHITE);
    133         domainOriginLinePaint.setAntiAlias(true);
    134         rangeOriginLinePaint = new Paint();
    135         rangeOriginLinePaint.setColor(Color.WHITE);
    136         rangeOriginLinePaint.setAntiAlias(true);
    137         domainOriginLabelPaint = new Paint();
    138         domainOriginLabelPaint.setColor(Color.WHITE);
    139         domainOriginLabelPaint.setAntiAlias(true);
    140         domainOriginLabelPaint.setTextAlign(Paint.Align.CENTER);
    141         rangeOriginLabelPaint = new Paint();
    142         rangeOriginLabelPaint.setColor(Color.WHITE);
    143         rangeOriginLabelPaint.setAntiAlias(true);
    144         rangeOriginLabelPaint.setTextAlign(Paint.Align.RIGHT);
    145         domainLabelPaint = new Paint();
    146         domainLabelPaint.setColor(Color.LTGRAY);
    147         domainLabelPaint.setAntiAlias(true);
    148         domainLabelPaint.setTextAlign(Paint.Align.CENTER);
    149         rangeLabelPaint = new Paint();
    150         rangeLabelPaint.setColor(Color.LTGRAY);
    151         rangeLabelPaint.setAntiAlias(true);
    152         rangeLabelPaint.setTextAlign(Paint.Align.RIGHT);
    153         domainCursorPaint = new Paint();
    154         domainCursorPaint.setColor(Color.YELLOW);
    155         rangeCursorPaint = new Paint();
    156         rangeCursorPaint.setColor(Color.YELLOW);
    157         cursorLabelPaint = new Paint();
    158         cursorLabelPaint.setColor(Color.YELLOW);
    159         cursorLabelBackgroundPaint = new Paint();
    160         cursorLabelBackgroundPaint.setColor(Color.argb(100, 50, 50, 50));
    161         setMarginTop(7);
    162         setMarginRight(4);
    163         setMarginBottom(4);
    164         rangeValueFormat = new DecimalFormat("0.0");
    165         domainValueFormat = new DecimalFormat("0.0");
    166         // domainLabelRegions = new ZHash<LineRegion,
    167         // AxisValueLabelFormatter>();
    168         // rangeLabelRegions = new ZHash<LineRegion, AxisValueLabelFormatter>();
    169         axisValueLabelRegions = new ZHash<RectRegion, AxisValueLabelFormatter>();
    170     }
    171 
    172     public XYGraphWidget(LayoutManager layoutManager, XYPlot plot, SizeMetrics sizeMetrics) {
    173         super(layoutManager, sizeMetrics);
    174         this.plot = plot;
    175     }
    176 
    177     public ZIndexable<RectRegion> getAxisValueLabelRegions() {
    178         return axisValueLabelRegions;
    179     }
    180 
    181     /**
    182      * Add a new Region used for rendering axis valuelabels. Note that it is
    183      * possible to add multiple Region instances which overlap, in which cast
    184      * the last region to be added will be used. It is up to the developer to
    185      * guard against this often undesireable situation.
    186      *
    187      * @param region
    188      * @param formatter
    189      */
    190     public void addAxisValueLabelRegion(RectRegion region,
    191             AxisValueLabelFormatter formatter) {
    192         axisValueLabelRegions.addToTop(region, formatter);
    193     }
    194 
    195     /**
    196      * Convenience method - wraps addAxisValueLabelRegion, using
    197      * Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY to mask off range
    198      * axis value labels.
    199      *
    200      * @param min
    201      * @param max
    202      * @param formatter
    203      *
    204      */
    205     public void addDomainAxisValueLabelRegion(double min, double max,
    206             AxisValueLabelFormatter formatter) {
    207         addAxisValueLabelRegion(new RectRegion(min, max,
    208                 Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, null),
    209                 formatter);
    210     }
    211 
    212     /**
    213      * Convenience method - wraps addAxisValueLabelRegion, using
    214      * Double.POSITIVE_INFINITY and Double.NEGATIVE_INFINITY to mask off domain
    215      * axis value labels.
    216      *
    217      * @param min
    218      * @param max
    219      * @param formatter
    220      */
    221     public void addRangeAxisValueLabelRegion(double min, double max,
    222             AxisValueLabelFormatter formatter) {
    223         addAxisValueLabelRegion(new RectRegion(Double.POSITIVE_INFINITY,
    224                 Double.NEGATIVE_INFINITY, min, max, null), formatter);
    225     }
    226 
    227     /*
    228      * public void addRangeLabelRegion(LineRegion region,
    229      * AxisValueLabelFormatter formatter) { rangeLabelRegions.addToTop(region,
    230      * formatter); }
    231      *
    232      * public boolean removeRangeLabelRegion(LineRegion region) { return
    233      * rangeLabelRegions.remove(region); }
    234      */
    235 
    236     /**
    237      * Returns the formatter associated with the first (bottom) Region
    238      * containing x and y.
    239      *
    240      * @param x
    241      * @param y
    242      * @return the formatter associated with the first (bottom) region
    243      *         containing x and y. null otherwise.
    244      */
    245     public AxisValueLabelFormatter getAxisValueLabelFormatterForVal(double x,
    246             double y) {
    247         for (RectRegion r : axisValueLabelRegions.elements()) {
    248             if (r.containsValue(x, y)) {
    249                 return axisValueLabelRegions.get(r);
    250             }
    251         }
    252         return null;
    253     }
    254 
    255     public AxisValueLabelFormatter getAxisValueLabelFormatterForDomainVal(
    256             double val) {
    257         for (RectRegion r : axisValueLabelRegions.elements()) {
    258             if (r.containsDomainValue(val)) {
    259                 return axisValueLabelRegions.get(r);
    260             }
    261         }
    262         return null;
    263     }
    264 
    265     public AxisValueLabelFormatter getAxisValueLabelFormatterForRangeVal(
    266             double val) {
    267         for (RectRegion r : axisValueLabelRegions.elements()) {
    268             if (r.containsRangeValue(val)) {
    269                 return axisValueLabelRegions.get(r);
    270             }
    271         }
    272         return null;
    273     }
    274 
    275     /**
    276      * Returns the formatter associated with the first (bottom-most) Region
    277      * containing value.
    278      *
    279      * @param value
    280      * @return
    281      */
    282     /*
    283      * public AxisValueLabelFormatter getXYAxisFormatterForRangeVal(double
    284      * value) { return getRegionContainingVal(rangeLabelRegions, value); }
    285      *//**
    286      * Returns the formatter associated with the first (bottom-most) Region
    287      * containing value.
    288      *
    289      * @param value
    290      * @return
    291      */
    292     /*
    293      * public AxisValueLabelFormatter getXYAxisFormatterForDomainVal(double
    294      * value) { return getRegionContainingVal(domainLabelRegions, value); }
    295      */
    296 
    297     /*
    298      * private AxisValueLabelFormatter getRegionContainingVal(ZHash<LineRegion,
    299      * AxisValueLabelFormatter> zhash, double val) { for (LineRegion r :
    300      * zhash.elements()) { if (r.contains(val)) { return
    301      * rangeLabelRegions.get(r); } } // nothing found return null; }
    302      */
    303 
    304     /**
    305      * Returns a RectF representing the grid area last drawn by this plot.
    306      *
    307      * @return
    308      */
    309     public RectF getGridRect() {
    310         return paddedGridRect;
    311     }
    312     private String getFormattedRangeValue(Number value) {
    313         return rangeValueFormat.format(value);
    314     }
    315 
    316     private String getFormattedDomainValue(Number value) {
    317         return domainValueFormat.format(value);
    318     }
    319 
    320     /**
    321      * Convenience method. Wraps getYVal(float)
    322      *
    323      * @param point
    324      * @return
    325      */
    326     public Double getYVal(PointF point) {
    327         return getYVal(point.y);
    328     }
    329 
    330     /**
    331      * Converts a y pixel to a y value.
    332      *
    333      * @param yPix
    334      * @return
    335      */
    336     public Double getYVal(float yPix) {
    337         if (plot.getCalculatedMinY() == null
    338                 || plot.getCalculatedMaxY() == null) {
    339             return null;
    340         }
    341         return ValPixConverter.pixToVal(yPix - paddedGridRect.top, plot
    342                 .getCalculatedMinY().doubleValue(), plot.getCalculatedMaxY()
    343                 .doubleValue(), paddedGridRect.height(), true);
    344     }
    345 
    346     /**
    347      * Convenience method. Wraps getXVal(float)
    348      *
    349      * @param point
    350      * @return
    351      */
    352     public Double getXVal(PointF point) {
    353         return getXVal(point.x);
    354     }
    355 
    356     /**
    357      * Converts an x pixel into an x value.
    358      *
    359      * @param xPix
    360      * @return
    361      */
    362     public Double getXVal(float xPix) {
    363         if (plot.getCalculatedMinX() == null
    364                 || plot.getCalculatedMaxX() == null) {
    365             return null;
    366         }
    367         return ValPixConverter.pixToVal(xPix - paddedGridRect.left, plot
    368                 .getCalculatedMinX().doubleValue(), plot.getCalculatedMaxX()
    369                 .doubleValue(), paddedGridRect.width(), false);
    370     }
    371 
    372     @Override
    373     protected void doOnDraw(Canvas canvas, RectF widgetRect)
    374             throws PlotRenderException {
    375         gridRect = getGridRect(widgetRect); // used for drawing the background
    376                                             // of the grid
    377         paddedGridRect = getPaddedGridRect(gridRect); // used for drawing lines
    378                                                       // etc.
    379         //Log.v(TAG, "gridRect :" + gridRect);
    380         //Log.v(TAG, "paddedGridRect :" + paddedGridRect);
    381         // if (!plot.isEmpty()) {
    382         // don't draw if we have no space to draw into
    383         if ((paddedGridRect.height() > 0.0f) && (paddedGridRect.width() > 0.0f)) {
    384             if (plot.getCalculatedMinX() != null
    385                     && plot.getCalculatedMaxX() != null
    386                     && plot.getCalculatedMinY() != null
    387                     && plot.getCalculatedMaxY() != null) {
    388                 drawGrid(canvas);
    389                 drawData(canvas);
    390                 drawCursors(canvas);
    391                 if (isDrawMarkersEnabled()) {
    392                     drawMarkers(canvas);
    393                 }
    394             }
    395         }
    396         // }
    397     }
    398 
    399     private RectF getGridRect(RectF widgetRect) {
    400         return new RectF(widgetRect.left + ((rangeAxisLeft)?rangeLabelWidth:1),
    401                 widgetRect.top + ((domainAxisBottom)?1:domainLabelWidth),
    402                 widgetRect.right - ((rangeAxisLeft)?1:rangeLabelWidth),
    403                 widgetRect.bottom - ((domainAxisBottom)?domainLabelWidth:1));
    404     }
    405 
    406     private RectF getPaddedGridRect(RectF gridRect) {
    407         return new RectF(gridRect.left + gridPaddingLeft, gridRect.top
    408                 + gridPaddingTop, gridRect.right - gridPaddingRight,
    409                 gridRect.bottom - gridPaddingBottom);
    410     }
    411 
    412     private void drawTickText(Canvas canvas, XYAxisType axis, Number value,
    413             float xPix, float yPix, Paint labelPaint) {
    414         AxisValueLabelFormatter rf = null;
    415         String txt = null;
    416         double v = value.doubleValue();
    417 
    418         int canvasState = canvas.save();
    419         try {
    420             switch (axis) {
    421                 case DOMAIN:
    422                     rf = getAxisValueLabelFormatterForDomainVal(v);
    423                     txt = getFormattedDomainValue(value);
    424                     canvas.rotate(getDomainLabelOrientation(), xPix, yPix);
    425                     break;
    426                 case RANGE:
    427                     rf = getAxisValueLabelFormatterForRangeVal(v);
    428                     txt = getFormattedRangeValue(value);
    429                     canvas.rotate(getRangeLabelOrientation(), xPix, yPix);
    430                     break;
    431             }
    432 
    433             // if a matching region formatter was found, create a clone
    434             // of labelPaint and use the formatter's color. Otherwise
    435             // just use labelPaint:
    436             Paint p;
    437             if (rf != null) {
    438                 // p = rf.getPaint();
    439                 p = new Paint(labelPaint);
    440                 p.setColor(rf.getColor());
    441                 // p.setColor(Color.RED);
    442             } else {
    443                 p = labelPaint;
    444             }
    445             canvas.drawText(txt, xPix, yPix, p);
    446         } finally {
    447             canvas.restoreToCount(canvasState);
    448         }
    449     }
    450 
    451     private void drawDomainTick(Canvas canvas, float xPix, Number xVal,
    452             Paint labelPaint, Paint linePaint, boolean drawLineOnly) {
    453         if (!drawLineOnly) {
    454             if (linePaint != null) {
    455                 if (domainAxisBottom){
    456                 canvas.drawLine(xPix, gridRect.top, xPix, gridRect.bottom
    457                         + domainLabelTickExtension, linePaint);
    458                 } else {
    459                     canvas.drawLine(xPix, gridRect.top - domainLabelTickExtension, xPix,
    460                             gridRect.bottom , linePaint);
    461                 }
    462             }
    463             if (labelPaint != null) {
    464                 float fontHeight = FontUtils.getFontHeight(labelPaint);
    465                 float yPix;
    466                 if (domainAxisBottom){
    467                     yPix = gridRect.bottom + domainLabelTickExtension
    468                             + domainLabelVerticalOffset + fontHeight;
    469                 } else {
    470                     yPix = gridRect.top - domainLabelTickExtension
    471                             - domainLabelVerticalOffset;
    472                 }
    473                 drawTickText(canvas, XYAxisType.DOMAIN, xVal, xPix + domainLabelHorizontalOffset, yPix,
    474                         labelPaint);
    475             }
    476         } else if (linePaint != null) {
    477 
    478             canvas.drawLine(xPix, gridRect.top, xPix, gridRect.bottom,
    479                     linePaint);
    480 
    481         }
    482     }
    483 
    484     public void drawRangeTick(Canvas canvas, float yPix, Number yVal,
    485             Paint labelPaint, Paint linePaint, boolean drawLineOnly) {
    486         if (!drawLineOnly) {
    487             if (linePaint != null) {
    488                 if (rangeAxisLeft){
    489                 canvas.drawLine(gridRect.left - rangeLabelTickExtension, yPix,
    490                         gridRect.right, yPix, linePaint);
    491                 } else {
    492                     canvas.drawLine(gridRect.left, yPix,
    493                             gridRect.right + rangeLabelTickExtension, yPix, linePaint);
    494                 }
    495             }
    496             if (labelPaint != null) {
    497                 float xPix;
    498                 if (rangeAxisLeft){
    499                     xPix = gridRect.left
    500                             - (rangeLabelTickExtension + rangeLabelHorizontalOffset);
    501                 } else {
    502                     xPix = gridRect.right
    503                             + (rangeLabelTickExtension + rangeLabelHorizontalOffset);
    504                 }
    505                 drawTickText(canvas, XYAxisType.RANGE, yVal, xPix, yPix - rangeLabelVerticalOffset,
    506                         labelPaint);
    507             }
    508         } else if (linePaint != null) {
    509             canvas.drawLine(gridRect.left, yPix, gridRect.right, yPix,
    510                     linePaint);
    511         }
    512     }
    513 
    514     /**
    515      * Draws the drid and domain/range labels for the plot.
    516      *
    517      * @param canvas
    518      */
    519     protected void drawGrid(Canvas canvas) {
    520 
    521         if (gridBackgroundPaint != null) {
    522             canvas.drawRect(gridRect, gridBackgroundPaint);
    523         }
    524 
    525         float domainOriginF;
    526         if (plot.getDomainOrigin() != null) {
    527             double domainOriginVal = plot.getDomainOrigin().doubleValue();
    528             domainOriginF = ValPixConverter.valToPix(domainOriginVal, plot
    529                     .getCalculatedMinX().doubleValue(), plot
    530                     .getCalculatedMaxX().doubleValue(), paddedGridRect.width(),
    531                     false);
    532             domainOriginF += paddedGridRect.left;
    533             // if no origin is set, use the leftmost value visible on the grid:
    534         } else {
    535             domainOriginF = paddedGridRect.left;
    536         }
    537 
    538         XYStep domainStep = XYStepCalculator.getStep(plot, XYAxisType.DOMAIN,
    539                 paddedGridRect, plot.getCalculatedMinX().doubleValue(), plot
    540                         .getCalculatedMaxX().doubleValue());
    541 
    542         // draw domain origin:
    543         if (domainOriginF >= paddedGridRect.left
    544                 && domainOriginF <= paddedGridRect.right) {
    545             if (domainOriginLinePaint != null){
    546                 domainOriginLinePaint.setTextAlign(Paint.Align.CENTER);
    547             }
    548             drawDomainTick(canvas, domainOriginF, plot.getDomainOrigin()
    549                     .doubleValue(), domainOriginLabelPaint,
    550                     domainOriginLinePaint, false);
    551         }
    552 
    553         // draw ticks LEFT of origin:
    554         {
    555             int i = 1;
    556             double xVal;
    557             float xPix = domainOriginF - domainStep.getStepPix();
    558             for (; xPix >= paddedGridRect.left; xPix = domainOriginF
    559                     - (i * domainStep.getStepPix())) {
    560                 xVal = plot.getDomainOrigin().doubleValue() - i
    561                         * domainStep.getStepVal();
    562                 if (xPix >= paddedGridRect.left && xPix <= paddedGridRect.right) {
    563                     if (i % getTicksPerDomainLabel() == 0) {
    564                         drawDomainTick(canvas, xPix, xVal, domainLabelPaint,
    565                                 domainGridLinePaint, false);
    566                     } else {
    567                         drawDomainTick(canvas, xPix, xVal, domainLabelPaint,
    568                                 domainSubGridLinePaint, true);
    569                     }
    570                 }
    571                 i++;
    572             }
    573         }
    574 
    575         // draw ticks RIGHT of origin:
    576         {
    577             int i = 1;
    578             double xVal;
    579             float xPix = domainOriginF + domainStep.getStepPix();
    580             for (; xPix <= paddedGridRect.right; xPix = domainOriginF
    581                     + (i * domainStep.getStepPix())) {
    582                 xVal = plot.getDomainOrigin().doubleValue() + i
    583                         * domainStep.getStepVal();
    584                 if (xPix >= paddedGridRect.left && xPix <= paddedGridRect.right) {
    585 
    586                     if (i % getTicksPerDomainLabel() == 0) {
    587                         drawDomainTick(canvas, xPix, xVal, domainLabelPaint,
    588                                 domainGridLinePaint, false);
    589                     } else {
    590                         drawDomainTick(canvas, xPix, xVal, domainLabelPaint,
    591                                 domainSubGridLinePaint, true);
    592                     }
    593                 }
    594                 i++;
    595             }
    596         }
    597 
    598         // draw range origin:
    599 
    600         float rangeOriginF;
    601         if (plot.getRangeOrigin() != null) {
    602             // --------- NEW WAY ------
    603             double rangeOriginD = plot.getRangeOrigin().doubleValue();
    604             rangeOriginF = ValPixConverter.valToPix(rangeOriginD, plot
    605                     .getCalculatedMinY().doubleValue(), plot
    606                     .getCalculatedMaxY().doubleValue(),
    607                     paddedGridRect.height(), true);
    608             rangeOriginF += paddedGridRect.top;
    609             // if no origin is set, use the leftmost value visible on the grid
    610         } else {
    611             rangeOriginF = paddedGridRect.bottom;
    612         }
    613 
    614         XYStep rangeStep = XYStepCalculator.getStep(plot, XYAxisType.RANGE,
    615                 paddedGridRect, plot.getCalculatedMinY().doubleValue(), plot
    616                         .getCalculatedMaxY().doubleValue());
    617 
    618         // draw range origin:
    619         if (rangeOriginF >= paddedGridRect.top
    620                 && rangeOriginF <= paddedGridRect.bottom) {
    621             if (rangeOriginLinePaint != null){
    622                 rangeOriginLinePaint.setTextAlign(Paint.Align.RIGHT);
    623             }
    624             drawRangeTick(canvas, rangeOriginF, plot.getRangeOrigin()
    625                     .doubleValue(), rangeOriginLabelPaint,
    626                     rangeOriginLinePaint, false);
    627         }
    628         // draw ticks ABOVE origin:
    629         {
    630             int i = 1;
    631             double yVal;
    632             float yPix = rangeOriginF - rangeStep.getStepPix();
    633             for (; yPix >= paddedGridRect.top; yPix = rangeOriginF
    634                     - (i * rangeStep.getStepPix())) {
    635                 yVal = plot.getRangeOrigin().doubleValue() + i
    636                         * rangeStep.getStepVal();
    637                 if (yPix >= paddedGridRect.top && yPix <= paddedGridRect.bottom) {
    638                     if (i % getTicksPerRangeLabel() == 0) {
    639                         drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,
    640                                 rangeGridLinePaint, false);
    641                     } else {
    642                         drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,
    643                                 rangeSubGridLinePaint, true);
    644                     }
    645                 }
    646                 i++;
    647             }
    648         }
    649 
    650         // draw ticks BENEATH origin:
    651         {
    652             int i = 1;
    653             double yVal;
    654             float yPix = rangeOriginF + rangeStep.getStepPix();
    655             for (; yPix <= paddedGridRect.bottom; yPix = rangeOriginF
    656                     + (i * rangeStep.getStepPix())) {
    657                 yVal = plot.getRangeOrigin().doubleValue() - i
    658                         * rangeStep.getStepVal();
    659                 if (yPix >= paddedGridRect.top && yPix <= paddedGridRect.bottom) {
    660                     if (i % getTicksPerRangeLabel() == 0) {
    661                         drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,
    662                                 rangeGridLinePaint, false);
    663                     } else {
    664                         drawRangeTick(canvas, yPix, yVal, rangeLabelPaint,
    665                                 rangeSubGridLinePaint, true);
    666                     }
    667                 }
    668                 i++;
    669             }
    670         }
    671     }
    672 
    673     /**
    674      * Renders the text associated with user defined markers
    675      *
    676      * @param canvas
    677      * @param text
    678      * @param marker
    679      * @param x
    680      * @param y
    681      */
    682     private void drawMarkerText(Canvas canvas, String text, ValueMarker marker,
    683             float x, float y) {
    684         x += MARKER_LABEL_SPACING;
    685         y -= MARKER_LABEL_SPACING;
    686         RectF textRect = new RectF(FontUtils.getStringDimensions(text,
    687                 marker.getTextPaint()));
    688         textRect.offsetTo(x, y - textRect.height());
    689 
    690         if (textRect.right > paddedGridRect.right) {
    691             textRect.offset(-(textRect.right - paddedGridRect.right), 0);
    692         }
    693 
    694         if (textRect.top < paddedGridRect.top) {
    695             textRect.offset(0, paddedGridRect.top - textRect.top);
    696         }
    697 
    698         canvas.drawText(text, textRect.left, textRect.bottom,
    699                 marker.getTextPaint());
    700 
    701     }
    702 
    703     protected void drawMarkers(Canvas canvas) {
    704         for (YValueMarker marker : plot.getYValueMarkers()) {
    705 
    706             if (marker.getValue() != null) {
    707                 double yVal = marker.getValue().doubleValue();
    708                 float yPix = ValPixConverter.valToPix(yVal, plot
    709                         .getCalculatedMinY().doubleValue(), plot
    710                         .getCalculatedMaxY().doubleValue(), paddedGridRect
    711                         .height(), true);
    712                 yPix += paddedGridRect.top;
    713                 canvas.drawLine(paddedGridRect.left, yPix,
    714                         paddedGridRect.right, yPix, marker.getLinePaint());
    715 
    716                 // String text = getFormattedRangeValue(yVal);
    717                 float xPix = marker.getTextPosition().getPixelValue(
    718                         paddedGridRect.width());
    719                 xPix += paddedGridRect.left;
    720 
    721                 if (marker.getText() != null) {
    722                     drawMarkerText(canvas, marker.getText(), marker, xPix, yPix);
    723                 } else {
    724                     drawMarkerText(canvas,
    725                             getFormattedRangeValue(marker.getValue()), marker,
    726                             xPix, yPix);
    727                 }
    728             }
    729         }
    730 
    731         for (XValueMarker marker : plot.getXValueMarkers()) {
    732             if (marker.getValue() != null) {
    733                 double xVal = marker.getValue().doubleValue();
    734                 float xPix = ValPixConverter.valToPix(xVal, plot
    735                         .getCalculatedMinX().doubleValue(), plot
    736                         .getCalculatedMaxX().doubleValue(), paddedGridRect
    737                         .width(), false);
    738                 xPix += paddedGridRect.left;
    739                 canvas.drawLine(xPix, paddedGridRect.top, xPix,
    740                         paddedGridRect.bottom, marker.getLinePaint());
    741 
    742                 // String text = getFormattedDomainValue(xVal);
    743                 float yPix = marker.getTextPosition().getPixelValue(
    744                         paddedGridRect.height());
    745                 yPix += paddedGridRect.top;
    746                 if (marker.getText() != null) {
    747                     drawMarkerText(canvas, marker.getText(), marker, xPix, yPix);
    748                 } else {
    749                     drawMarkerText(canvas,
    750                             getFormattedDomainValue(marker.getValue()), marker,
    751                             xPix, yPix);
    752                 }
    753             }
    754         }
    755     }
    756 
    757     protected void drawCursors(Canvas canvas) {
    758         boolean hasDomainCursor = false;
    759         // draw the domain cursor:
    760         if (domainCursorPaint != null
    761                 && domainCursorPosition <= paddedGridRect.right
    762                 && domainCursorPosition >= paddedGridRect.left) {
    763             hasDomainCursor = true;
    764             canvas.drawLine(domainCursorPosition, paddedGridRect.top,
    765                     domainCursorPosition, paddedGridRect.bottom,
    766                     domainCursorPaint);
    767         }
    768 
    769         boolean hasRangeCursor = false;
    770         // draw the range cursor:
    771         if (rangeCursorPaint != null
    772                 && rangeCursorPosition >= paddedGridRect.top
    773                 && rangeCursorPosition <= paddedGridRect.bottom) {
    774             hasRangeCursor = true;
    775             canvas.drawLine(paddedGridRect.left, rangeCursorPosition,
    776                     paddedGridRect.right, rangeCursorPosition, rangeCursorPaint);
    777         }
    778 
    779         if (drawCursorLabelEnabled && cursorLabelPaint != null
    780                 && hasRangeCursor && hasDomainCursor) {
    781 
    782             String label = "X="
    783                     + getDomainValueFormat().format(getDomainCursorVal());
    784             label += " Y=" + getRangeValueFormat().format(getRangeCursorVal());
    785 
    786             // convert the label dimensions rect into floating-point:
    787             RectF cursorRect = new RectF(FontUtils.getPackedStringDimensions(
    788                     label, cursorLabelPaint));
    789             cursorRect.offsetTo(domainCursorPosition, rangeCursorPosition
    790                     - cursorRect.height());
    791 
    792             // if we are too close to the right edge of the plot, we will move
    793             // the
    794             // label to the left side of our cursor:
    795             if (cursorRect.right >= paddedGridRect.right) {
    796                 cursorRect.offsetTo(domainCursorPosition - cursorRect.width(),
    797                         cursorRect.top);
    798             }
    799 
    800             // same thing for the top edge of the plot:
    801             // dunno why but these rects can have negative values for top and
    802             // bottom.
    803             if (cursorRect.top <= paddedGridRect.top) {
    804                 cursorRect.offsetTo(cursorRect.left, rangeCursorPosition);
    805             }
    806 
    807             if (cursorLabelBackgroundPaint != null) {
    808                 canvas.drawRect(cursorRect, cursorLabelBackgroundPaint);
    809             }
    810 
    811             canvas.drawText(label, cursorRect.left, cursorRect.bottom,
    812                     cursorLabelPaint);
    813         }
    814     }
    815 
    816     /**
    817      * Draws lines and points for each element in the series.
    818      *
    819      * @param canvas
    820      * @throws PlotRenderException
    821      */
    822     protected void drawData(Canvas canvas) throws PlotRenderException {
    823         // TODO: iterate through a XYSeriesRenderer list
    824 
    825         // int canvasState = canvas.save();
    826         try {
    827             canvas.save(Canvas.ALL_SAVE_FLAG);
    828             canvas.clipRect(gridRect, android.graphics.Region.Op.INTERSECT);
    829             for (XYSeriesRenderer renderer : plot.getRendererList()) {
    830                 renderer.render(canvas, paddedGridRect);
    831             }
    832             // canvas.restoreToCount(canvasState);
    833         } finally {
    834             canvas.restore();
    835         }
    836     }
    837 
    838     protected void drawPoint(Canvas canvas, PointF point, Paint paint) {
    839         canvas.drawPoint(point.x, point.y, paint);
    840     }
    841 
    842     public float getDomainLabelWidth() {
    843         return domainLabelWidth;
    844     }
    845 
    846     public void setDomainLabelWidth(float domainLabelWidth) {
    847         this.domainLabelWidth = domainLabelWidth;
    848     }
    849 
    850     public float getRangeLabelWidth() {
    851         return rangeLabelWidth;
    852     }
    853 
    854     public void setRangeLabelWidth(float rangeLabelWidth) {
    855         this.rangeLabelWidth = rangeLabelWidth;
    856     }
    857 
    858     public float getDomainLabelVerticalOffset() {
    859         return domainLabelVerticalOffset;
    860     }
    861 
    862     public void setDomainLabelVerticalOffset(float domainLabelVerticalOffset) {
    863         this.domainLabelVerticalOffset = domainLabelVerticalOffset;
    864     }
    865 
    866     public float getDomainLabelHorizontalOffset() {
    867         return domainLabelHorizontalOffset;
    868     }
    869 
    870     public void setDomainLabelHorizontalOffset(float domainLabelHorizontalOffset) {
    871         this.domainLabelHorizontalOffset = domainLabelHorizontalOffset;
    872     }
    873 
    874     public float getRangeLabelHorizontalOffset() {
    875         return rangeLabelHorizontalOffset;
    876     }
    877 
    878     public void setRangeLabelHorizontalOffset(float rangeLabelHorizontalOffset) {
    879         this.rangeLabelHorizontalOffset = rangeLabelHorizontalOffset;
    880     }
    881 
    882     public float getRangeLabelVerticalOffset() {
    883         return rangeLabelVerticalOffset;
    884     }
    885 
    886     public void setRangeLabelVerticalOffset(float rangeLabelVerticalOffset) {
    887         this.rangeLabelVerticalOffset = rangeLabelVerticalOffset;
    888     }
    889 
    890     public Paint getGridBackgroundPaint() {
    891         return gridBackgroundPaint;
    892     }
    893 
    894     public void setGridBackgroundPaint(Paint gridBackgroundPaint) {
    895         this.gridBackgroundPaint = gridBackgroundPaint;
    896     }
    897 
    898     public Paint getDomainLabelPaint() {
    899         return domainLabelPaint;
    900     }
    901 
    902     public void setDomainLabelPaint(Paint domainLabelPaint) {
    903         this.domainLabelPaint = domainLabelPaint;
    904     }
    905 
    906     public Paint getRangeLabelPaint() {
    907         return rangeLabelPaint;
    908     }
    909 
    910     public void setRangeLabelPaint(Paint rangeLabelPaint) {
    911         this.rangeLabelPaint = rangeLabelPaint;
    912     }
    913 
    914     /**
    915      * Get the paint used to draw the domain grid line.
    916      */
    917     public Paint getDomainGridLinePaint() {
    918         return domainGridLinePaint;
    919     }
    920 
    921     /**
    922      * Set the paint used to draw the domain grid line.
    923      * @param gridLinePaint
    924      */
    925     public void setDomainGridLinePaint(Paint gridLinePaint) {
    926         this.domainGridLinePaint = gridLinePaint;
    927     }
    928 
    929     /**
    930      * Get the paint used to draw the range grid line.
    931      */
    932     public Paint getRangeGridLinePaint() {
    933         return rangeGridLinePaint;
    934     }
    935 
    936     /**
    937      * Get the paint used to draw the domain grid line.
    938      */
    939     public Paint getDomainSubGridLinePaint() {
    940         return domainSubGridLinePaint;
    941     }
    942 
    943     /**
    944      * Set the paint used to draw the domain grid line.
    945      * @param gridLinePaint
    946      */
    947     public void setDomainSubGridLinePaint(Paint gridLinePaint) {
    948         this.domainSubGridLinePaint = gridLinePaint;
    949     }
    950 
    951     /**
    952      * Set the Paint used to draw the range grid line.
    953      * @param gridLinePaint
    954      */
    955     public void setRangeGridLinePaint(Paint gridLinePaint) {
    956         this.rangeGridLinePaint = gridLinePaint;
    957     }
    958 
    959     /**
    960      * Get the paint used to draw the range grid line.
    961      */
    962     public Paint getRangeSubGridLinePaint() {
    963         return rangeSubGridLinePaint;
    964     }
    965 
    966     /**
    967      * Set the Paint used to draw the range grid line.
    968      * @param gridLinePaint
    969      */
    970     public void setRangeSubGridLinePaint(Paint gridLinePaint) {
    971         this.rangeSubGridLinePaint = gridLinePaint;
    972     }
    973 
    974     // TODO: make a generic renderer queue.
    975 
    976     public Format getRangeValueFormat() {
    977         return rangeValueFormat;
    978     }
    979 
    980     public void setRangeValueFormat(Format rangeValueFormat) {
    981         this.rangeValueFormat = rangeValueFormat;
    982     }
    983 
    984     public Format getDomainValueFormat() {
    985         return domainValueFormat;
    986     }
    987 
    988     public void setDomainValueFormat(Format domainValueFormat) {
    989         this.domainValueFormat = domainValueFormat;
    990     }
    991 
    992     public int getDomainLabelTickExtension() {
    993         return domainLabelTickExtension;
    994     }
    995 
    996     public void setDomainLabelTickExtension(int domainLabelTickExtension) {
    997         this.domainLabelTickExtension = domainLabelTickExtension;
    998     }
    999 
   1000     public int getRangeLabelTickExtension() {
   1001         return rangeLabelTickExtension;
   1002     }
   1003 
   1004     public void setRangeLabelTickExtension(int rangeLabelTickExtension) {
   1005         this.rangeLabelTickExtension = rangeLabelTickExtension;
   1006     }
   1007 
   1008     public int getTicksPerRangeLabel() {
   1009         return ticksPerRangeLabel;
   1010     }
   1011 
   1012     public void setTicksPerRangeLabel(int ticksPerRangeLabel) {
   1013         this.ticksPerRangeLabel = ticksPerRangeLabel;
   1014     }
   1015 
   1016     public int getTicksPerDomainLabel() {
   1017         return ticksPerDomainLabel;
   1018     }
   1019 
   1020     public void setTicksPerDomainLabel(int ticksPerDomainLabel) {
   1021         this.ticksPerDomainLabel = ticksPerDomainLabel;
   1022     }
   1023 
   1024     public void setGridPaddingTop(float gridPaddingTop) {
   1025         this.gridPaddingTop = gridPaddingTop;
   1026     }
   1027 
   1028     public float getGridPaddingBottom() {
   1029         return gridPaddingBottom;
   1030     }
   1031 
   1032     public void setGridPaddingBottom(float gridPaddingBottom) {
   1033         this.gridPaddingBottom = gridPaddingBottom;
   1034     }
   1035 
   1036     public float getGridPaddingLeft() {
   1037         return gridPaddingLeft;
   1038     }
   1039 
   1040     public void setGridPaddingLeft(float gridPaddingLeft) {
   1041         this.gridPaddingLeft = gridPaddingLeft;
   1042     }
   1043 
   1044     public float getGridPaddingRight() {
   1045         return gridPaddingRight;
   1046     }
   1047 
   1048     public void setGridPaddingRight(float gridPaddingRight) {
   1049         this.gridPaddingRight = gridPaddingRight;
   1050     }
   1051 
   1052     public float getGridPaddingTop() {
   1053         return gridPaddingTop;
   1054     }
   1055 
   1056     public void setGridPadding(float left, float top, float right, float bottom) {
   1057         setGridPaddingLeft(left);
   1058         setGridPaddingTop(top);
   1059         setGridPaddingRight(right);
   1060         setGridPaddingBottom(bottom);
   1061     }
   1062 
   1063     public Paint getDomainOriginLinePaint() {
   1064         return domainOriginLinePaint;
   1065     }
   1066 
   1067     public void setDomainOriginLinePaint(Paint domainOriginLinePaint) {
   1068         this.domainOriginLinePaint = domainOriginLinePaint;
   1069     }
   1070 
   1071     public Paint getRangeOriginLinePaint() {
   1072         return rangeOriginLinePaint;
   1073     }
   1074 
   1075     public void setRangeOriginLinePaint(Paint rangeOriginLinePaint) {
   1076         this.rangeOriginLinePaint = rangeOriginLinePaint;
   1077     }
   1078 
   1079     public Paint getDomainOriginLabelPaint() {
   1080         return domainOriginLabelPaint;
   1081     }
   1082 
   1083     public void setDomainOriginLabelPaint(Paint domainOriginLabelPaint) {
   1084         this.domainOriginLabelPaint = domainOriginLabelPaint;
   1085     }
   1086 
   1087     public Paint getRangeOriginLabelPaint() {
   1088         return rangeOriginLabelPaint;
   1089     }
   1090 
   1091     public void setRangeOriginLabelPaint(Paint rangeOriginLabelPaint) {
   1092         this.rangeOriginLabelPaint = rangeOriginLabelPaint;
   1093     }
   1094 
   1095     public void setCursorPosition(float x, float y) {
   1096         setDomainCursorPosition(x);
   1097         setRangeCursorPosition(y);
   1098     }
   1099 
   1100     public void setCursorPosition(PointF point) {
   1101         setCursorPosition(point.x, point.y);
   1102     }
   1103 
   1104     public float getDomainCursorPosition() {
   1105         return domainCursorPosition;
   1106     }
   1107 
   1108     public Double getDomainCursorVal() {
   1109         return getXVal(getDomainCursorPosition());
   1110     }
   1111 
   1112     public void setDomainCursorPosition(float domainCursorPosition) {
   1113         this.domainCursorPosition = domainCursorPosition;
   1114     }
   1115 
   1116     public float getRangeCursorPosition() {
   1117         return rangeCursorPosition;
   1118     }
   1119 
   1120     public Double getRangeCursorVal() {
   1121         return getYVal(getRangeCursorPosition());
   1122     }
   1123 
   1124     public void setRangeCursorPosition(float rangeCursorPosition) {
   1125         this.rangeCursorPosition = rangeCursorPosition;
   1126     }
   1127 
   1128     public Paint getCursorLabelPaint() {
   1129         return cursorLabelPaint;
   1130     }
   1131 
   1132     public void setCursorLabelPaint(Paint cursorLabelPaint) {
   1133         this.cursorLabelPaint = cursorLabelPaint;
   1134     }
   1135 
   1136     public Paint getCursorLabelBackgroundPaint() {
   1137         return cursorLabelBackgroundPaint;
   1138     }
   1139 
   1140     public void setCursorLabelBackgroundPaint(Paint cursorLabelBackgroundPaint) {
   1141         this.cursorLabelBackgroundPaint = cursorLabelBackgroundPaint;
   1142     }
   1143 
   1144     public boolean isDrawMarkersEnabled() {
   1145         return drawMarkersEnabled;
   1146     }
   1147 
   1148     public void setDrawMarkersEnabled(boolean drawMarkersEnabled) {
   1149         this.drawMarkersEnabled = drawMarkersEnabled;
   1150     }
   1151 
   1152     public boolean isRangeAxisLeft() {
   1153         return rangeAxisLeft;
   1154     }
   1155 
   1156     public void setRangeAxisLeft(boolean rangeAxisLeft) {
   1157         this.rangeAxisLeft = rangeAxisLeft;
   1158     }
   1159 
   1160     public boolean isDomainAxisBottom() {
   1161         return domainAxisBottom;
   1162     }
   1163 
   1164     public void setDomainAxisBottom(boolean domainAxisBottom) {
   1165         this.domainAxisBottom = domainAxisBottom;
   1166     }
   1167 
   1168     /*
   1169      * set the position of the range axis labels.  Set the labelPaint textSizes before setting this.
   1170      * This call sets the various vertical and horizontal offsets and widths to good defaults.
   1171      *
   1172      * @param rangeAxisLeft axis labels are on the left hand side not the right hand side.
   1173      * @param rangeAxisOverlay axis labels are overlaid on the plot, not external to it.
   1174      * @param tickSize the size of the tick extensions for none overlaid axis.
   1175      * @param maxLableString Sample label representing the biggest size space needs to be allocated for.
   1176      */
   1177     public void setRangeAxisPosition(boolean rangeAxisLeft, boolean rangeAxisOverlay, int tickSize, String maxLableString){
   1178         setRangeAxisLeft(rangeAxisLeft);
   1179 
   1180         if (rangeAxisOverlay) {
   1181             setRangeLabelWidth(1);    // needs to be at least 1 to display grid line.
   1182             setRangeLabelHorizontalOffset(-2.0f);
   1183             setRangeLabelVerticalOffset(2.0f);    // get above the line
   1184             Paint p = getRangeLabelPaint();
   1185             if (p != null) {
   1186                 p.setTextAlign(((rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));
   1187             }
   1188             Paint po = getRangeOriginLabelPaint();
   1189             if (po != null) {
   1190                 po.setTextAlign(((rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));
   1191             }
   1192             setRangeLabelTickExtension(0);
   1193         } else {
   1194             setRangeLabelWidth(1);    // needs to be at least 1 to display grid line.
   1195                                       // if we have a paint this gets bigger.
   1196             setRangeLabelHorizontalOffset(1.0f);
   1197             setRangeLabelTickExtension(tickSize);
   1198             Paint p = getRangeLabelPaint();
   1199             if (p != null) {
   1200                 p.setTextAlign(((!rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));
   1201                 Rect r = FontUtils.getPackedStringDimensions(maxLableString,p);
   1202                 setRangeLabelVerticalOffset(r.top/2);
   1203                 setRangeLabelWidth(r.right + getRangeLabelTickExtension());
   1204             }
   1205             Paint po = getRangeOriginLabelPaint();
   1206             if (po != null) {
   1207                 po.setTextAlign(((!rangeAxisLeft)?Paint.Align.LEFT:Paint.Align.RIGHT));
   1208             }
   1209         }
   1210     }
   1211 
   1212     /*
   1213      * set the position of the domain axis labels.  Set the labelPaint textSizes before setting this.
   1214      * This call sets the various vertical and horizontal offsets and widths to good defaults.
   1215      *
   1216      * @param domainAxisBottom axis labels are on the bottom not the top of the plot.
   1217      * @param domainAxisOverlay axis labels are overlaid on the plot, not external to it.
   1218      * @param tickSize the size of the tick extensions for non overlaid axis.
   1219      * @param maxLableString Sample label representing the biggest size space needs to be allocated for.
   1220      */
   1221     public void setDomainAxisPosition(boolean domainAxisBottom, boolean domainAxisOverlay, int tickSize, String maxLabelString){
   1222         setDomainAxisBottom(domainAxisBottom);
   1223         if (domainAxisOverlay) {
   1224             setDomainLabelWidth(1);    // needs to be at least 1 to display grid line.
   1225             setDomainLabelVerticalOffset(2.0f);    // get above the line
   1226             setDomainLabelTickExtension(0);
   1227             Paint p = getDomainLabelPaint();
   1228             if (p != null) {
   1229                 Rect r = FontUtils.getPackedStringDimensions(maxLabelString,p);
   1230                 if (domainAxisBottom){
   1231                     setDomainLabelVerticalOffset(2 * r.top);
   1232                 } else {
   1233                     setDomainLabelVerticalOffset(r.top - 1.0f);
   1234                 }
   1235             }
   1236         } else {
   1237             setDomainLabelWidth(1);    // needs to be at least 1 to display grid line.
   1238                                        // if we have a paint this gets bigger.
   1239             setDomainLabelTickExtension(tickSize);
   1240             Paint p = getDomainLabelPaint();
   1241             if (p != null) {
   1242                 float fontHeight = FontUtils.getFontHeight(p);
   1243                 if (domainAxisBottom){
   1244                     setDomainLabelVerticalOffset(-4.0f);
   1245                 } else {
   1246                     setDomainLabelVerticalOffset(+1.0f);
   1247                 }
   1248                 setDomainLabelWidth(fontHeight + getDomainLabelTickExtension());
   1249             }
   1250         }
   1251     }
   1252 }
   1253