blob: 12f96f08d0d8173dc598e680a1033973946473f9 [file] [log] [blame]
/*
* Copyright 2012 AndroidPlot.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.androidplot.xy;
import android.graphics.*;
import android.util.Pair;
import com.androidplot.exception.PlotRenderException;
import com.androidplot.util.ValPixConverter;
import java.util.ArrayList;
import java.util.List;
/**
* Renders a point as a line with the vertices marked. Requires 2 or more points to
* be rendered.
*/
public class LineAndPointRenderer<FormatterType extends LineAndPointFormatter> extends XYSeriesRenderer<FormatterType> {
public LineAndPointRenderer(XYPlot plot) {
super(plot);
}
@Override
public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
List<XYSeries> seriesList = getPlot().getSeriesListForRenderer(this.getClass());
if (seriesList != null) {
for (XYSeries series : seriesList) {
//synchronized(series) {
drawSeries(canvas, plotArea, series, getFormatter(series));
//}
}
}
}
@Override
public void doDrawLegendIcon(Canvas canvas, RectF rect, LineAndPointFormatter formatter) {
// horizontal icon:
float centerY = rect.centerY();
float centerX = rect.centerX();
if(formatter.getFillPaint() != null) {
canvas.drawRect(rect, formatter.getFillPaint());
}
if(formatter.getLinePaint() != null) {
canvas.drawLine(rect.left, rect.bottom, rect.right, rect.top, formatter.getLinePaint());
}
if(formatter.getVertexPaint() != null) {
canvas.drawPoint(centerX, centerY, formatter.getVertexPaint());
}
}
/**
* This method exists for StepRenderer to override without having to duplicate any
* additional code.
*/
protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {
path.lineTo(thisPoint.x, thisPoint.y);
}
protected void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, LineAndPointFormatter formatter) {
PointF thisPoint;
PointF lastPoint = null;
PointF firstPoint = null;
Paint linePaint = formatter.getLinePaint();
//PointF lastDrawn = null;
Path path = null;
ArrayList<Pair<PointF, Integer>> points = new ArrayList<Pair<PointF, Integer>>(series.size());
for (int i = 0; i < series.size(); i++) {
Number y = series.getY(i);
Number x = series.getX(i);
if (y != null && x != null) {
thisPoint = ValPixConverter.valToPix(
x,
y,
plotArea,
getPlot().getCalculatedMinX(),
getPlot().getCalculatedMaxX(),
getPlot().getCalculatedMinY(),
getPlot().getCalculatedMaxY());
points.add(new Pair<PointF, Integer>(thisPoint, i));
//appendToPath(path, thisPoint, lastPoint);
} else {
thisPoint = null;
}
if(linePaint != null && thisPoint != null) {
// record the first point of the new Path
if(firstPoint == null) {
path = new Path();
firstPoint = thisPoint;
// create our first point at the bottom/x position so filling
// will look good
path.moveTo(firstPoint.x, firstPoint.y);
}
if(lastPoint != null) {
appendToPath(path, thisPoint, lastPoint);
}
lastPoint = thisPoint;
} else {
if(lastPoint != null) {
renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
}
firstPoint = null;
lastPoint = null;
}
}
if(linePaint != null && firstPoint != null) {
renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
}
// TODO: benchmark this against drawPoints(float[]);
Paint vertexPaint = formatter.getVertexPaint();
PointLabelFormatter plf = formatter.getPointLabelFormatter();
if (vertexPaint != null || plf != null) {
for (Pair<PointF, Integer> p : points) {
PointLabeler pointLabeler = formatter.getPointLabeler();
// if vertexPaint is available, draw vertex:
if(vertexPaint != null) {
canvas.drawPoint(p.first.x, p.first.y, formatter.getVertexPaint());
}
// if textPaint and pointLabeler are available, draw point's text label:
if(plf != null && pointLabeler != null) {
canvas.drawText(pointLabeler.getLabel(series, p.second), p.first.x + plf.hOffset, p.first.y + plf.vOffset, plf.getTextPaint());
}
}
}
}
protected void renderPath(Canvas canvas, RectF plotArea, Path path, PointF firstPoint, PointF lastPoint, LineAndPointFormatter formatter) {
Path outlinePath = new Path(path);
// determine how to close the path for filling purposes:
// We always need to calculate this path because it is also used for
// masking off for region highlighting.
switch (formatter.getFillDirection()) {
case BOTTOM:
path.lineTo(lastPoint.x, plotArea.bottom);
path.lineTo(firstPoint.x, plotArea.bottom);
path.close();
break;
case TOP:
path.lineTo(lastPoint.x, plotArea.top);
path.lineTo(firstPoint.x, plotArea.top);
path.close();
break;
case RANGE_ORIGIN:
float originPix = ValPixConverter.valToPix(
getPlot().getRangeOrigin().doubleValue(),
getPlot().getCalculatedMinY().doubleValue(),
getPlot().getCalculatedMaxY().doubleValue(),
plotArea.height(),
true);
originPix += plotArea.top;
path.lineTo(lastPoint.x, originPix);
path.lineTo(firstPoint.x, originPix);
path.close();
break;
default:
throw new UnsupportedOperationException("Fill direction not yet implemented: " + formatter.getFillDirection());
}
if (formatter.getFillPaint() != null) {
canvas.drawPath(path, formatter.getFillPaint());
}
//}
// draw any visible regions on top of the base region:
double minX = getPlot().getCalculatedMinX().doubleValue();
double maxX = getPlot().getCalculatedMaxX().doubleValue();
double minY = getPlot().getCalculatedMinY().doubleValue();
double maxY = getPlot().getCalculatedMaxY().doubleValue();
// draw each region:
for (RectRegion r : RectRegion.regionsWithin(formatter.getRegions().elements(), minX, maxX, minY, maxY)) {
XYRegionFormatter f = formatter.getRegionFormatter(r);
RectF regionRect = r.getRectF(plotArea, minX, maxX, minY, maxY);
if (regionRect != null) {
try {
canvas.save();
canvas.clipPath(path);
canvas.drawRect(regionRect, f.getPaint());
} finally {
canvas.restore();
}
}
}
// finally we draw the outline path on top of everything else:
if(formatter.getLinePaint() != null) {
canvas.drawPath(outlinePath, formatter.getLinePaint());
}
path.rewind();
}
}