package jas.hist; import jas.plot.CoordinateTransformation; import jas.plot.DateCoordinateTransformation; import jas.plot.DoubleCoordinateTransformation; import jas.plot.MutableLegendEntry; import jas.plot.Overlay; import jas.plot.OverlayContainer; import jas.plot.PlotGraphics; import jas.plot.StringCoordinateTransformation; import java.awt.BasicStroke; class OneDOverlay implements Overlay, MutableLegendEntry { private JASHist1DHistogramData source; private OverlayContainer container; private double[] data; private double[] dataX; private String[] labels; private double[] minus; private double[] plus; private double xmax; private double xmin; private static final float[][] lineStyles = {null, { 1, 5 },{ 4, 6 },{ 6, 4, 2, 4 }}; OneDOverlay(JASHist1DHistogramData d) { this.source = d; } public void setTitle(String newTitle) { source.setLegendText(newTitle); } public String getTitle() { return source.getLegendText(); } public void containerNotify(OverlayContainer c) { this.container = c; } public void paint(PlotGraphics g, boolean isPrinting) { JASHist1DHistogramStyle style = source.style; double[] binEdges = null; if (source.getDataSource() instanceof Rebinnable1DVariableHistogramData) binEdges = ((Rebinnable1DVariableHistogramData) source.getDataSource()).getBinEdges(); CoordinateTransformation xp = container.getXTransformation(); final CoordinateTransformation yp = container.getYTransformation(source.getYAxis()); if (xp instanceof DateCoordinateTransformation) { xp = new DateTransformationConverter((DateCoordinateTransformation) xp); } if (xp instanceof DoubleCoordinateTransformation && yp instanceof DoubleCoordinateTransformation) { final DoubleCoordinateTransformation xt = (DoubleCoordinateTransformation) xp; final DoubleCoordinateTransformation yt = (DoubleCoordinateTransformation) yp; final int bins = data.length; final double binWidthFixed = (xmax - xmin) / bins; final double pixelWidth = (xt.convert(xmax) - xt.convert(xmin)) / bins; final boolean outline = pixelWidth > 5; double errorBarWidth = Math.min(binWidthFixed, (3 * binWidthFixed) / pixelWidth); g.setTransformation(xt, yt); double x = xmin; double oldx = xmin; // y0 is used as the base of histogram bars. It should // be 0 unless there is a suppressed 0 on the Y axis. double y0 = 0; if (yt.getPlotMin() > y0) { y0 = yt.getPlotMin(); } if (yt.getPlotMax() < y0) { y0 = yt.getPlotMax(); } double oldy = y0; double[] lpbx = null; double[] lpby = null; int lpbn = 0; if (style.getShowLinesBetweenPoints() || style.getShowDataPoints()) { // Bottleneck: Created each time paint is called lpbx = new double[bins]; lpby = new double[bins]; } // Note, we want to skip points which have y == NAN. This takes some // care for things such as histogram bars and lines between points. for (int i = 0; i < bins; i++) { double y = data[i]; double binWidth = (binEdges == null) ? binWidthFixed : binEdges[i+1] - binEdges[i]; x = (dataX == null) ? (x + binWidth) : dataX[i]; BasicStroke s = new BasicStroke(style.getHistogramBarLineWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getHistogramBarLineStyle()],0); g.setStroke(s); if (style.getShowHistogramBars()) { if (!Double.isNaN(y)) { g.setColor(style.getHistogramBarColor()); if (style.getHistogramFill()) { g.fillRect(oldx, y0, x, y); g.setColor(style.getHistogramBarLineColor()); if (outline) { g.drawRect(oldx, y0, x, y); } else { g.drawLine(oldx, oldy, oldx, y); g.drawLine(oldx, y, x, y); } } else { g.setColor(style.getHistogramBarLineColor()); g.drawLine(oldx, oldy, oldx, y); g.drawLine(oldx, y, x, y); } } else { if (!style.getHistogramFill() || !outline) { g.drawLine(oldx, oldy, oldx, y0); } } } g.setStroke(null); if (style.getShowErrorBars() && !Double.isNaN(y)) { g.setColor(style.getErrorBarColor()); final double xm = (binWidth == 0) ? x : (oldx + (binWidth / 2)); double xe = (style.getErrorBarDecoration() >= 0) ? (double) (style.getErrorBarDecoration() * binWidth/2) : Math.min(binWidth, (3 * binWidth) / pixelWidth); final double yplus = data[i] + plus[i]; final double yminus = data[i] - minus[i]; s = new BasicStroke(style.getErrorBarWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getErrorBarStyle()],0); g.setStroke(s); if (!Double.isNaN(yplus) && !Double.isNaN(yminus) && yminus != yplus) { g.drawLine(xm, yplus, xm, yminus); if ((outline && style.getErrorBarDecoration() < 0) || style.getErrorBarDecoration() > 0) { BasicStroke ss = new BasicStroke(style.getErrorBarWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10); g.setStroke(ss); g.drawLine(xm - xe, yplus, xm + xe, yplus); g.drawLine(xm - xe, yminus, xm + xe, yminus); } } g.setStroke(null); } if (lpbx != null) { if (Double.isNaN(y)) { if (lpbn > 0) { if (style.getShowLinesBetweenPoints()) { g.setColor(style.getLineColor()); s = new BasicStroke(style.getLinesBetweenPointsWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getLinesBetweenPointsStyle()],0); g.setStroke(s); g.drawPolyLine(lpbx, lpby, lpbn); g.setStroke(null); } if (style.getShowDataPoints()) { g.setColor(style.getDataPointColor()); g.drawPolySymbol(lpbx, lpby, style.getDataPointSize(), style.getDataPointStyle(), lpbn); } lpbn = 0; } } else { lpbx[lpbn] = (dataX == null) ? (oldx + (binWidth / 2)) : x; lpby[lpbn++] = y; } } oldx = x; oldy = (y == Double.NaN) ? y0 : y; } if (lpbn > 0) { if (style.getShowLinesBetweenPoints()) { g.setColor(style.getLineColor()); BasicStroke s = new BasicStroke(style.getLinesBetweenPointsWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getLinesBetweenPointsStyle()],0); g.setStroke(s); g.drawPolyLine(lpbx, lpby, lpbn); g.setStroke(null); } if (style.getShowDataPoints()) { g.setColor(style.getDataPointColor()); g.drawPolySymbol(lpbx, lpby, style.getDataPointSize(), style.getDataPointStyle(), lpbn); } } } else if (xp instanceof StringCoordinateTransformation && yp instanceof DoubleCoordinateTransformation) { final StringCoordinateTransformation xt = (StringCoordinateTransformation) xp; final DoubleCoordinateTransformation yt = (DoubleCoordinateTransformation) yp; final int bins = labels.length; final double binWidth = xt.binWidth(); boolean outline = binWidth > 5; g.setTransformation(null, yt); // y0 is used as the base of histogram bars. It should // be 0 unless there is a suppressed 0 on the Y axis. double y0 = 0; if (yt.getPlotMin() > y0) { y0 = yt.getPlotMin(); } if (yt.getPlotMax() < y0) { y0 = yt.getPlotMax(); } double oldy = yt.getPlotMin(); double[] lpbx = null; double[] lpby = null; int lpbn = 0; if (style.getShowLinesBetweenPoints() || style.getShowDataPoints()) { // Bottleneck: Created each time paint is called lpbx = new double[bins]; lpby = new double[bins]; } for (int i = 0; i < bins; i++) { double x = xt.convert(labels[i]) + (binWidth / 2); double oldx = x - binWidth; double y = data[i]; BasicStroke s = new BasicStroke(style.getHistogramBarLineWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getHistogramBarLineStyle()],0); g.setStroke(s); if (style.getShowHistogramBars()) { if (!Double.isNaN(y)) { g.setColor(style.getHistogramBarColor()); if (style.getHistogramFill()) { g.fillRect(oldx, oldy, x, y); g.setColor(style.getHistogramBarLineColor()); if (outline) { g.drawRect(oldx, oldy, x, y); } else { g.drawLine(oldx, oldy, oldx, y); g.drawLine(oldx, y, x, y); } } else { g.setColor(style.getHistogramBarLineColor()); g.drawLine(oldx, oldy, oldx, y); g.drawLine(oldx, y, x, y); } } else { if (!style.getHistogramFill() || !outline) { g.drawLine(oldx, oldy, oldx, y0); } } } g.setStroke(null); if (style.getShowErrorBars() && !Double.isNaN(y)) { s = new BasicStroke(style.getErrorBarWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getErrorBarStyle()],0); g.setStroke(s); g.setColor(style.getErrorBarColor()); double xm = x - (binWidth / 2); double xe = (style.getErrorBarDecoration() >= 0) ? (double) (style.getErrorBarDecoration() * binWidth / 2) : Math.min(3, binWidth / 2); double yplus = data[i] + plus[i]; double yminus = data[i] - minus[i]; if (!Double.isNaN(yplus) && !Double.isNaN(yminus) && yminus != yplus) { g.drawLine(xm, yplus, xm, yminus); if ((outline && style.getErrorBarDecoration() < 0) || style.getErrorBarDecoration() > 0) { BasicStroke ss = new BasicStroke(style.getErrorBarWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10); g.setStroke(ss); g.drawLine(xm - xe, yplus, xm + xe, yplus); g.drawLine(xm - xe, yminus, xm + xe, yminus); } } g.setStroke(null); } if (lpbx != null) { if (Double.isNaN(y)) { if (lpbn > 0) { if (style.getShowLinesBetweenPoints()) { g.setColor(style.getLineColor()); s = new BasicStroke(style.getLinesBetweenPointsWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getLinesBetweenPointsStyle()],0); g.setStroke(s); g.drawPolyLine(lpbx, lpby, lpbn); g.setStroke(null); } if (style.getShowDataPoints()) { g.setColor(style.getDataPointColor()); g.drawPolySymbol(lpbx, lpby, style.getDataPointSize(), style.getDataPointStyle(), lpbn); } lpbn = 0; } } else { lpbx[lpbn] = (dataX == null) ? (oldx + (binWidth / 2)) : x; lpby[lpbn++] = y; } } oldx = x; // oldy = (y == Double.NaN) ? y0 : y; } if (lpbn > 0) { if (style.getShowLinesBetweenPoints()) { g.setColor(style.getLineColor()); BasicStroke s = new BasicStroke(style.getLinesBetweenPointsWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getLinesBetweenPointsStyle()],0); g.setStroke(s); g.drawPolyLine(lpbx, lpby, lpbn); g.setStroke(null); } if (style.getShowDataPoints()) { g.setColor(style.getDataPointColor()); g.drawPolySymbol(lpbx, lpby, style.getDataPointSize(), style.getDataPointStyle(), lpbn); } } } } public void paintIcon(PlotGraphics g, int width, int height) { JASHist1DHistogramStyle style = source.style; if (style.getShowDataPoints()) { g.setColor(style.getDataPointColor()); g.drawSymbol( width / 2, height / 2, width / 2, style.getDataPointStyle()); } else if (style.getShowHistogramBars()) { if ( style.getHistogramFill() ) { g.setColor(style.getHistogramBarColor()); g.fillRect(1, 1, width - 2, height - 2); } else { g.setColor(style.getHistogramBarLineColor()); float flw = (float) style.getHistogramBarLineWidth()*3.0f; if (flw > (width/2)) flw = (float) (width/2 -1); BasicStroke s = new BasicStroke(flw,BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getHistogramBarLineStyle()],0); g.setStroke(s); g.drawLine(1, height/2, width-2, height/2); g.setStroke(null); } } else if (style.getShowLinesBetweenPoints()) { g.setColor(style.getLineColor()); float flw = (float) style.getHistogramBarLineWidth()*3.0f; if (flw > (width/2)) flw = (width > 2) ? (float) (width/2 -1) : 1; BasicStroke s = new BasicStroke(flw,BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getLinesBetweenPointsStyle()],0); g.setStroke(s); g.drawLine(1, height/2, width-2, height/2); g.setStroke(null); } else if (style.getShowErrorBars()) { g.setColor(style.getErrorBarColor()); BasicStroke s = new BasicStroke(style.getErrorBarWidth(),BasicStroke.CAP_SQUARE,BasicStroke.JOIN_ROUND,10,lineStyles[style.getErrorBarStyle()],0); g.setStroke(s); g.drawLine(1, height/2, width-2, height/2); g.setStroke(null); } else if (style.getHistogramFill()) { g.setColor(style.getHistogramBarColor()); g.fillRect(1, 1, width - 2, height - 2); } } public boolean titleIsChanged() { return source.isLegendChanged(); } void setData(double[] data, double[] plusError, double[] minusError, double xMin, double xMax) { this.data = data; this.plus = plusError; this.minus = minusError; this.xmin = xMin; this.xmax = xMax; } void setData(double[] x, double[] y, double[] plusError, double[] minusError) { this.dataX = x; this.data = y; this.plus = plusError; this.minus = minusError; } void setData(double[] data, double[] plusError, double[] minusError, String[] labels) { this.data = data; this.plus = plusError; this.minus = minusError; this.labels = labels; } }