16224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala/*
26224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Copyright 2012 AndroidPlot.com
36224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *
46224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *    Licensed under the Apache License, Version 2.0 (the "License");
56224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *    you may not use this file except in compliance with the License.
66224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *    You may obtain a copy of the License at
76224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *
86224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *        http://www.apache.org/licenses/LICENSE-2.0
96224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *
106224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *    Unless required by applicable law or agreed to in writing, software
116224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *    distributed under the License is distributed on an "AS IS" BASIS,
126224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *    See the License for the specific language governing permissions and
146224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala *    limitations under the License.
156224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala */
166224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
176224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalapackage com.androidplot.xy;
186224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
196224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport android.graphics.*;
206224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport android.util.Pair;
216224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport com.androidplot.exception.PlotRenderException;
226224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport com.androidplot.util.ValPixConverter;
236224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
246224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport java.util.ArrayList;
256224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalaimport java.util.List;
266224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
276224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala/**
286224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * Renders a point as a line with the vertices marked.  Requires 2 or more points to
296224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala * be rendered.
306224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala */
316224eda509d436a575f801942337da92a6c18767Eino-Ville Talvalapublic class LineAndPointRenderer<FormatterType extends LineAndPointFormatter> extends XYSeriesRenderer<FormatterType> {
326224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
336224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    public LineAndPointRenderer(XYPlot plot) {
346224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        super(plot);
356224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    }
366224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
376224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    @Override
386224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
396224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
406224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
416224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        List<XYSeries> seriesList = getPlot().getSeriesListForRenderer(this.getClass());
426224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        if (seriesList != null) {
436224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            for (XYSeries series : seriesList) {
446224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                //synchronized(series) {
456224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    drawSeries(canvas, plotArea, series, getFormatter(series));
466224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                //}
476224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            }
486224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
496224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    }
506224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
516224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    @Override
526224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    public void doDrawLegendIcon(Canvas canvas, RectF rect, LineAndPointFormatter formatter) {
536224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        // horizontal icon:
546224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        float centerY = rect.centerY();
556224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        float centerX = rect.centerX();
566224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
576224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        if(formatter.getFillPaint() != null) {
586224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            canvas.drawRect(rect, formatter.getFillPaint());
596224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
606224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        if(formatter.getLinePaint() != null) {
616224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            canvas.drawLine(rect.left, rect.bottom, rect.right, rect.top, formatter.getLinePaint());
626224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
636224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
646224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        if(formatter.getVertexPaint() != null) {
656224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            canvas.drawPoint(centerX, centerY, formatter.getVertexPaint());
666224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
676224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    }
686224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
696224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    /**
706224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala     * This method exists for StepRenderer to override without having to duplicate any
716224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala     * additional code.
726224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala     */
736224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    protected void appendToPath(Path path, PointF thisPoint, PointF lastPoint) {
746224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
756224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        path.lineTo(thisPoint.x, thisPoint.y);
766224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    }
776224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
786224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
796224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    protected void drawSeries(Canvas canvas, RectF plotArea, XYSeries series, LineAndPointFormatter formatter) {
806224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        PointF thisPoint;
816224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        PointF lastPoint = null;
826224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        PointF firstPoint = null;
836224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        Paint  linePaint = formatter.getLinePaint();
846224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
856224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        //PointF lastDrawn = null;
866224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        Path path = null;
876224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        ArrayList<Pair<PointF, Integer>> points = new ArrayList<Pair<PointF, Integer>>(series.size());
886224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        for (int i = 0; i < series.size(); i++) {
896224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            Number y = series.getY(i);
906224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            Number x = series.getX(i);
916224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
926224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            if (y != null && x != null) {
936224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                thisPoint = ValPixConverter.valToPix(
946224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        x,
956224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        y,
966224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        plotArea,
976224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        getPlot().getCalculatedMinX(),
986224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        getPlot().getCalculatedMaxX(),
996224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        getPlot().getCalculatedMinY(),
1006224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        getPlot().getCalculatedMaxY());
1016224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                points.add(new Pair<PointF, Integer>(thisPoint, i));
1026224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                //appendToPath(path, thisPoint, lastPoint);
1036224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            } else {
1046224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                thisPoint = null;
1056224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            }
1066224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1076224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            if(linePaint != null && thisPoint != null) {
1086224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1096224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                // record the first point of the new Path
1106224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                if(firstPoint == null) {
1116224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    path = new Path();
1126224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    firstPoint = thisPoint;
1136224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    // create our first point at the bottom/x position so filling
1146224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    // will look good
1156224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    path.moveTo(firstPoint.x, firstPoint.y);
1166224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                }
1176224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1186224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                if(lastPoint != null) {
1196224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    appendToPath(path, thisPoint, lastPoint);
1206224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                }
1216224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1226224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                lastPoint = thisPoint;
1236224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            } else {
1246224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                if(lastPoint != null) {
1256224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
1266224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                }
1276224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                firstPoint = null;
1286224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                lastPoint = null;
1296224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            }
1306224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
1316224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        if(linePaint != null && firstPoint != null) {
1326224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            renderPath(canvas, plotArea, path, firstPoint, lastPoint, formatter);
1336224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
1346224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1356224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        // TODO: benchmark this against drawPoints(float[]);
1366224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        Paint vertexPaint = formatter.getVertexPaint();
1376224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        PointLabelFormatter plf = formatter.getPointLabelFormatter();
1386224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        if (vertexPaint != null || plf != null) {
1396224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            for (Pair<PointF, Integer> p : points) {
1406224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            	PointLabeler pointLabeler = formatter.getPointLabeler();
1416224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1426224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                // if vertexPaint is available, draw vertex:
1436224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                if(vertexPaint != null) {
1446224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    canvas.drawPoint(p.first.x, p.first.y, formatter.getVertexPaint());
1456224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                }
1466224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1476224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                // if textPaint and pointLabeler are available, draw point's text label:
1486224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                if(plf != null && pointLabeler != null) {
1496224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    canvas.drawText(pointLabeler.getLabel(series, p.second), p.first.x + plf.hOffset, p.first.y + plf.vOffset, plf.getTextPaint());
1506224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                }
1516224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            }
1526224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
1536224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    }
1546224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1556224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    protected void renderPath(Canvas canvas, RectF plotArea, Path path, PointF firstPoint, PointF lastPoint, LineAndPointFormatter formatter) {
1566224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        Path outlinePath = new Path(path);
1576224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1586224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        // determine how to close the path for filling purposes:
1596224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        // We always need to calculate this path because it is also used for
1606224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        // masking off for region highlighting.
1616224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        switch (formatter.getFillDirection()) {
1626224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            case BOTTOM:
1636224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.lineTo(lastPoint.x, plotArea.bottom);
1646224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.lineTo(firstPoint.x, plotArea.bottom);
1656224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.close();
1666224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                break;
1676224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            case TOP:
1686224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.lineTo(lastPoint.x, plotArea.top);
1696224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.lineTo(firstPoint.x, plotArea.top);
1706224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.close();
1716224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                break;
1726224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            case RANGE_ORIGIN:
1736224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                float originPix = ValPixConverter.valToPix(
1746224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        getPlot().getRangeOrigin().doubleValue(),
1756224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        getPlot().getCalculatedMinY().doubleValue(),
1766224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        getPlot().getCalculatedMaxY().doubleValue(),
1776224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        plotArea.height(),
1786224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                        true);
1796224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                originPix += plotArea.top;
1806224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1816224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.lineTo(lastPoint.x, originPix);
1826224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.lineTo(firstPoint.x, originPix);
1836224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                path.close();
1846224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                break;
1856224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            default:
1866224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                throw new UnsupportedOperationException("Fill direction not yet implemented: " + formatter.getFillDirection());
1876224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
1886224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1896224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        if (formatter.getFillPaint() != null) {
1906224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            canvas.drawPath(path, formatter.getFillPaint());
1916224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
1926224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1936224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1946224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        //}
1956224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
1966224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        // draw any visible regions on top of the base region:
1976224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        double minX = getPlot().getCalculatedMinX().doubleValue();
1986224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        double maxX = getPlot().getCalculatedMaxX().doubleValue();
1996224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        double minY = getPlot().getCalculatedMinY().doubleValue();
2006224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        double maxY = getPlot().getCalculatedMaxY().doubleValue();
2016224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
2026224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        // draw each region:
2036224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        for (RectRegion r : RectRegion.regionsWithin(formatter.getRegions().elements(), minX, maxX, minY, maxY)) {
2046224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            XYRegionFormatter f = formatter.getRegionFormatter(r);
2056224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            RectF regionRect = r.getRectF(plotArea, minX, maxX, minY, maxY);
2066224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            if (regionRect != null) {
2076224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                try {
2086224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                canvas.save(Canvas.ALL_SAVE_FLAG);
2096224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                canvas.clipPath(path);
2106224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                canvas.drawRect(regionRect, f.getPaint());
2116224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                } finally {
2126224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                    canvas.restore();
2136224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala                }
2146224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            }
2156224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
2166224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
2176224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        // finally we draw the outline path on top of everything else:
2186224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        if(formatter.getLinePaint() != null) {
2196224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala            canvas.drawPath(outlinePath, formatter.getLinePaint());
2206224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        }
2216224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala
2226224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala        path.rewind();
2236224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala    }
2246224eda509d436a575f801942337da92a6c18767Eino-Ville Talvala}
225