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 import android.util.Pair;
     21 import com.androidplot.exception.PlotRenderException;
     22 import com.androidplot.util.ValPixConverter;
     23 
     24 import java.util.ArrayList;
     25 import java.util.List;
     26 
     27 /**
     28  * Renders a point as a line with the vertices marked.  Requires 2 or more points to
     29  * be rendered.
     30  */
     31 public class LineAndPointRenderer<FormatterType extends LineAndPointFormatter> extends XYSeriesRenderer<FormatterType> {
     32 
     33     public LineAndPointRenderer(XYPlot plot) {
     34         super(plot);
     35     }
     36 
     37     @Override
     38     public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
     39 
     40 
     41         List<XYSeries> seriesList = getPlot().getSeriesListForRenderer(this.getClass());
     42         if (seriesList != null) {
     43             for (XYSeries series : seriesList) {
     44                 //synchronized(series) {
     45                     drawSeries(canvas, plotArea, series, getFormatter(series));
     46                 //}
     47             }
     48         }
     49     }
     50 
     51     @Override
     52     public void doDrawLegendIcon(Canvas canvas, RectF rect, LineAndPointFormatter formatter) {
     53         // horizontal icon:
     54         float centerY = rect.centerY();
     55         float centerX = rect.centerX();
     56 
     57         if(formatter.getFillPaint() != null) {
     58             canvas.drawRect(rect, formatter.getFillPaint());
     59         }
     60         if(formatter.getLinePaint() != null) {
     61             canvas.drawLine(rect.left, rect.bottom, rect.right, rect.top, formatter.getLinePaint());
     62         }
     63 
     64         if(formatter.getVertexPaint() != null) {
     65             canvas.drawPoint(centerX, centerY, formatter.getVertexPaint());
     66         }
     67     }
     68 
     69     /**
     70      * This method exists for StepRenderer to override without having to duplicate any
     71      * additional code.
     72      */
     73     protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {
     74 
     75         path.lineTo(thisPoint.x, thisPoint.y);
     76     }
     77 
     78 
     79     protected void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, LineAndPointFormatter formatter) {
     80         PointF thisPoint;
     81         PointF lastPoint = null;
     82         PointF firstPoint = null;
     83         Paint  linePaint = formatter.getLinePaint();
     84 
     85         //PointF lastDrawn = null;
     86         Path path = null;
     87         ArrayList<Pair<PointF, Integer>> points = new ArrayList<Pair<PointF, Integer>>(series.size());
     88         for (int i = 0; i < series.size(); i++) {
     89             Number y = series.getY(i);
     90             Number x = series.getX(i);
     91 
     92             if (y != null && x != null) {
     93                 thisPoint = ValPixConverter.valToPix(
     94                         x,
     95                         y,
     96                         plotArea,
     97                         getPlot().getCalculatedMinX(),
     98                         getPlot().getCalculatedMaxX(),
     99                         getPlot().getCalculatedMinY(),
    100                         getPlot().getCalculatedMaxY());
    101                 points.add(new Pair<PointF, Integer>(thisPoint, i));
    102                 //appendToPath(path, thisPoint, lastPoint);
    103             } else {
    104                 thisPoint = null;
    105             }
    106 
    107             if(linePaint != null && thisPoint != null) {
    108 
    109                 // record the first point of the new Path
    110                 if(firstPoint == null) {
    111                     path = new Path();
    112                     firstPoint = thisPoint;
    113                     // create our first point at the bottom/x position so filling
    114                     // will look good
    115                     path.moveTo(firstPoint.x, firstPoint.y);
    116                 }
    117 
    118                 if(lastPoint != null) {
    119                     appendToPath(path, thisPoint, lastPoint);
    120                 }
    121 
    122                 lastPoint = thisPoint;
    123             } else {
    124                 if(lastPoint != null) {
    125                     renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
    126                 }
    127                 firstPoint = null;
    128                 lastPoint = null;
    129             }
    130         }
    131         if(linePaint != null && firstPoint != null) {
    132             renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
    133         }
    134 
    135         // TODO: benchmark this against drawPoints(float[]);
    136         Paint vertexPaint = formatter.getVertexPaint();
    137         PointLabelFormatter plf = formatter.getPointLabelFormatter();
    138         if (vertexPaint != null || plf != null) {
    139             for (Pair<PointF, Integer> p : points) {
    140             	PointLabeler pointLabeler = formatter.getPointLabeler();
    141 
    142                 // if vertexPaint is available, draw vertex:
    143                 if(vertexPaint != null) {
    144                     canvas.drawPoint(p.first.x, p.first.y, formatter.getVertexPaint());
    145                 }
    146 
    147                 // if textPaint and pointLabeler are available, draw point's text label:
    148                 if(plf != null && pointLabeler != null) {
    149                     canvas.drawText(pointLabeler.getLabel(series, p.second), p.first.x + plf.hOffset, p.first.y + plf.vOffset, plf.getTextPaint());
    150                 }
    151             }
    152         }
    153     }
    154 
    155     protected void renderPath(Canvas canvas, RectF plotArea, Path path, PointF firstPoint, PointF lastPoint, LineAndPointFormatter formatter) {
    156         Path outlinePath = new Path(path);
    157 
    158         // determine how to close the path for filling purposes:
    159         // We always need to calculate this path because it is also used for
    160         // masking off for region highlighting.
    161         switch (formatter.getFillDirection()) {
    162             case BOTTOM:
    163                 path.lineTo(lastPoint.x, plotArea.bottom);
    164                 path.lineTo(firstPoint.x, plotArea.bottom);
    165                 path.close();
    166                 break;
    167             case TOP:
    168                 path.lineTo(lastPoint.x, plotArea.top);
    169                 path.lineTo(firstPoint.x, plotArea.top);
    170                 path.close();
    171                 break;
    172             case RANGE_ORIGIN:
    173                 float originPix = ValPixConverter.valToPix(
    174                         getPlot().getRangeOrigin().doubleValue(),
    175                         getPlot().getCalculatedMinY().doubleValue(),
    176                         getPlot().getCalculatedMaxY().doubleValue(),
    177                         plotArea.height(),
    178                         true);
    179                 originPix += plotArea.top;
    180 
    181                 path.lineTo(lastPoint.x, originPix);
    182                 path.lineTo(firstPoint.x, originPix);
    183                 path.close();
    184                 break;
    185             default:
    186                 throw new UnsupportedOperationException("Fill direction not yet implemented: " + formatter.getFillDirection());
    187         }
    188 
    189         if (formatter.getFillPaint() != null) {
    190             canvas.drawPath(path, formatter.getFillPaint());
    191         }
    192 
    193 
    194         //}
    195 
    196         // draw any visible regions on top of the base region:
    197         double minX = getPlot().getCalculatedMinX().doubleValue();
    198         double maxX = getPlot().getCalculatedMaxX().doubleValue();
    199         double minY = getPlot().getCalculatedMinY().doubleValue();
    200         double maxY = getPlot().getCalculatedMaxY().doubleValue();
    201 
    202         // draw each region:
    203         for (RectRegion r : RectRegion.regionsWithin(formatter.getRegions().elements(), minX, maxX, minY, maxY)) {
    204             XYRegionFormatter f = formatter.getRegionFormatter(r);
    205             RectF regionRect = r.getRectF(plotArea, minX, maxX, minY, maxY);
    206             if (regionRect != null) {
    207                 try {
    208                 canvas.save(Canvas.ALL_SAVE_FLAG);
    209                 canvas.clipPath(path);
    210                 canvas.drawRect(regionRect, f.getPaint());
    211                 } finally {
    212                     canvas.restore();
    213                 }
    214             }
    215         }
    216 
    217         // finally we draw the outline path on top of everything else:
    218         if(formatter.getLinePaint() != null) {
    219             canvas.drawPath(outlinePath, formatter.getLinePaint());
    220         }
    221 
    222         path.rewind();
    223     }
    224 }
    225