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 17package com.androidplot.xy; 18 19import android.graphics.*; 20import android.util.Pair; 21import com.androidplot.exception.PlotRenderException; 22import com.androidplot.util.ValPixConverter; 23 24import java.util.ArrayList; 25import 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 */ 31public 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