- /* ===========================================================
- * JFreeChart : a free chart library for the Java(tm) platform
- * ===========================================================
- *
- * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
- *
- * Project Info: http://www.jfree.org/jfreechart/index.html
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or
- * (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
- * License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
- *
- * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
- * in the United States and other countries.]
- *
- * --------------------------
- * BoxAndWhiskerRenderer.java
- * --------------------------
- * (C) Copyright 2003-2005, by David Browning and Contributors.
- *
- * Original Author: David Browning (for the Australian Institute of Marine
- * Science);
- * Contributor(s): David Gilbert (for Object Refinery Limited);
- * Tim Bardzil;
- *
- * $Id: BoxAndWhiskerRenderer.java,v 1.5 2005/03/09 11:45:38 mungady Exp $
- *
- * Changes
- * -------
- * 21-Aug-2003 : Version 1, contributed by David Browning (for the Australian
- * Institute of Marine Science);
- * 01-Sep-2003 : Incorporated outlier and farout symbols for low values
- * also (DG);
- * 08-Sep-2003 : Changed ValueAxis API (DG);
- * 16-Sep-2003 : Changed ChartRenderingInfo --> PlotRenderingInfo (DG);
- * 07-Oct-2003 : Added renderer state (DG);
- * 12-Nov-2003 : Fixed casting bug reported by Tim Bardzil (DG);
- * 13-Nov-2003 : Added drawHorizontalItem() method contributed by Tim
- * Bardzil (DG);
- * 25-Apr-2004 : Added fillBox attribute, equals() method and added
- * serialization code (DG);
- * 29-Apr-2004 : Changed drawing of upper and lower shadows - see bug report
- * 944011 (DG);
- * 05-Nov-2004 : Modified drawItem() signature (DG);
- * 09-Mar-2005 : Override getLegendItem() method so that legend item shapes
- * are shown as blocks (DG);
- *
- */
- package org.jfree.chart.renderer.category;
- import java.awt.Color;
- import java.awt.Graphics2D;
- import java.awt.Paint;
- import java.awt.Shape;
- import java.awt.Stroke;
- import java.awt.geom.Ellipse2D;
- import java.awt.geom.Line2D;
- import java.awt.geom.Point2D;
- import java.awt.geom.Rectangle2D;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import java.io.Serializable;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.Iterator;
- import java.util.List;
- import org.jfree.chart.LegendItem;
- import org.jfree.chart.axis.CategoryAxis;
- import org.jfree.chart.axis.ValueAxis;
- import org.jfree.chart.entity.CategoryItemEntity;
- import org.jfree.chart.entity.EntityCollection;
- import org.jfree.chart.event.RendererChangeEvent;
- import org.jfree.chart.labels.CategoryToolTipGenerator;
- import org.jfree.chart.plot.CategoryPlot;
- import org.jfree.chart.plot.PlotOrientation;
- import org.jfree.chart.plot.PlotRenderingInfo;
- import org.jfree.chart.renderer.Outlier;
- import org.jfree.chart.renderer.OutlierList;
- import org.jfree.chart.renderer.OutlierListCollection;
- import org.jfree.data.category.CategoryDataset;
- import org.jfree.data.statistics.BoxAndWhiskerCategoryDataset;
- import org.jfree.io.SerialUtilities;
- import org.jfree.ui.RectangleEdge;
- import org.jfree.util.ObjectUtilities;
- import org.jfree.util.PublicCloneable;
- /**
- * A box-and-whisker renderer.
- */
- public class BoxAndWhiskerRenderer extends AbstractCategoryItemRenderer
- implements Cloneable, PublicCloneable,
- Serializable {
- /** The color used to paint the median line and average marker. */
- private transient Paint artifactPaint;
- /** A flag that controls whether or not the box is filled. */
- private boolean fillBox;
- /** The margin between items (boxes) within a category. */
- private double itemMargin;
- /**
- * Default constructor.
- */
- public BoxAndWhiskerRenderer() {
- this.artifactPaint = Color.black;
- this.fillBox = true;
- this.itemMargin = 0.20;
- }
- /**
- * Returns the paint used to color the median and average markers.
- *
- * @return A paint.
- */
- public Paint getArtifactPaint() {
- return this.artifactPaint;
- }
- /**
- * Sets the paint used to color the median and average markers.
- *
- * @param paint the paint.
- */
- public void setArtifactPaint(Paint paint) {
- this.artifactPaint = paint;
- }
- /**
- * Returns the flag that controls whether or not the box is filled.
- *
- * @return A boolean.
- */
- public boolean getFillBox() {
- return this.fillBox;
- }
- /**
- * Sets the flag that controls whether or not the box is filled and sends a
- * {@link RendererChangeEvent} to all registered listeners.
- *
- * @param flag the flag.
- */
- public void setFillBox(boolean flag) {
- this.fillBox = flag;
- notifyListeners(new RendererChangeEvent(this));
- }
- /**
- * Returns the item margin. This is a percentage of the available space
- * that is allocated to the space between items in the chart.
- *
- * @return The margin.
- */
- public double getItemMargin() {
- return this.itemMargin;
- }
- /**
- * Sets the item margin.
- *
- * @param margin the margin.
- */
- public void setItemMargin(double margin) {
- this.itemMargin = margin;
- }
- /**
- * Returns a legend item for a series.
- *
- * @param datasetIndex the dataset index (zero-based).
- * @param series the series index (zero-based).
- *
- * @return The legend item.
- */
- public LegendItem getLegendItem(int datasetIndex, int series) {
- CategoryPlot cp = getPlot();
- if (cp == null) {
- return null;
- }
- CategoryDataset dataset;
- dataset = cp.getDataset(datasetIndex);
- String label = dataset.getRowKey(series).toString();
- String description = label;
- Shape shape = new Rectangle2D.Double(-4.0, -4.0, 8.0, 8.0);
- Paint paint = getSeriesPaint(series);
- Paint outlinePaint = getSeriesOutlinePaint(series);
- Stroke outlineStroke = getSeriesOutlineStroke(series);
- return new LegendItem(
- label, description, shape, paint, outlineStroke, outlinePaint
- );
- }
- /**
- * Initialises the renderer. This method gets called once at the start of
- * the process of drawing a chart.
- *
- * @param g2 the graphics device.
- * @param dataArea the area in which the data is to be plotted.
- * @param plot the plot.
- * @param rendererIndex the renderer index.
- * @param info collects chart rendering information for return to caller.
- *
- * @return The renderer state.
- */
- public CategoryItemRendererState initialise(Graphics2D g2,
- Rectangle2D dataArea,
- CategoryPlot plot,
- int rendererIndex,
- PlotRenderingInfo info) {
- CategoryItemRendererState state = super.initialise(
- g2, dataArea, plot, rendererIndex, info
- );
- // calculate the box width
- CategoryAxis domainAxis = getDomainAxis(plot, rendererIndex);
- CategoryDataset dataset = plot.getDataset(rendererIndex);
- if (dataset != null) {
- int columns = dataset.getColumnCount();
- int rows = dataset.getRowCount();
- double space = 0.0;
- PlotOrientation orientation = plot.getOrientation();
- if (orientation == PlotOrientation.HORIZONTAL) {
- space = dataArea.getHeight();
- }
- else if (orientation == PlotOrientation.VERTICAL) {
- space = dataArea.getWidth();
- }
- double categoryMargin = 0.0;
- double currentItemMargin = 0.0;
- if (columns > 1) {
- categoryMargin = domainAxis.getCategoryMargin();
- }
- if (rows > 1) {
- currentItemMargin = getItemMargin();
- }
- double used = space * (1 - domainAxis.getLowerMargin()
- - domainAxis.getUpperMargin()
- - categoryMargin - currentItemMargin);
- if ((rows * columns) > 0) {
- state.setBarWidth(
- used / (dataset.getColumnCount() * dataset.getRowCount())
- );
- }
- else {
- state.setBarWidth(used);
- }
- }
- return state;
- }
- /**
- * Draw a single data item.
- *
- * @param g2 the graphics device.
- * @param state the renderer state.
- * @param dataArea the area in which the data is drawn.
- * @param plot the plot.
- * @param domainAxis the domain axis.
- * @param rangeAxis the range axis.
- * @param dataset the data.
- * @param row the row index (zero-based).
- * @param column the column index (zero-based).
- * @param pass the pass index.
- */
- public void drawItem(Graphics2D g2,
- CategoryItemRendererState state,
- Rectangle2D dataArea,
- CategoryPlot plot,
- CategoryAxis domainAxis,
- ValueAxis rangeAxis,
- CategoryDataset dataset,
- int row,
- int column,
- int pass) {
- if (!(dataset instanceof BoxAndWhiskerCategoryDataset)) {
- throw new IllegalArgumentException(
- "BoxAndWhiskerRenderer.drawItem() : the data should be of type "
- + "BoxAndWhiskerCategoryDataset only."
- );
- }
- PlotOrientation orientation = plot.getOrientation();
- if (orientation == PlotOrientation.HORIZONTAL) {
- drawHorizontalItem(
- g2, state, dataArea, plot, domainAxis, rangeAxis,
- dataset, row, column
- );
- }
- else if (orientation == PlotOrientation.VERTICAL) {
- drawVerticalItem(
- g2, state, dataArea, plot, domainAxis, rangeAxis,
- dataset, row, column
- );
- }
- }
- /**
- * Draws the visual representation of a single data item when the plot has
- * a horizontal orientation.
- *
- * @param g2 the graphics device.
- * @param state the renderer state.
- * @param dataArea the area within which the plot is being drawn.
- * @param plot the plot (can be used to obtain standard color
- * information etc).
- * @param domainAxis the domain axis.
- * @param rangeAxis the range axis.
- * @param dataset the dataset.
- * @param row the row index (zero-based).
- * @param column the column index (zero-based).
- */
- public void drawHorizontalItem(Graphics2D g2,
- CategoryItemRendererState state,
- Rectangle2D dataArea,
- CategoryPlot plot,
- CategoryAxis domainAxis,
- ValueAxis rangeAxis,
- CategoryDataset dataset,
- int row,
- int column) {
- BoxAndWhiskerCategoryDataset bawDataset
- = (BoxAndWhiskerCategoryDataset) dataset;
- double categoryEnd = domainAxis.getCategoryEnd(
- column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
- );
- double categoryStart = domainAxis.getCategoryStart(
- column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
- );
- double categoryWidth = Math.abs(categoryEnd - categoryStart);
- double yy = categoryStart;
- int seriesCount = getRowCount();
- int categoryCount = getColumnCount();
- if (seriesCount > 1) {
- double seriesGap = dataArea.getWidth() * getItemMargin()
- / (categoryCount * (seriesCount - 1));
- double usedWidth = (state.getBarWidth() * seriesCount)
- + (seriesGap * (seriesCount - 1));
- // offset the start of the boxes if the total width used is smaller
- // than the category width
- double offset = (categoryWidth - usedWidth) / 2;
- yy = yy + offset + (row * (state.getBarWidth() + seriesGap));
- }
- else {
- // offset the start of the box if the box width is smaller than
- // the category width
- double offset = (categoryWidth - state.getBarWidth()) / 2;
- yy = yy + offset;
- }
- Paint p = this.getItemPaint(row, column);
- if (p != null) {
- g2.setPaint(p);
- }
- Stroke s = getItemStroke(row, column);
- g2.setStroke(s);
- RectangleEdge location = plot.getRangeAxisEdge();
- Number xQ1 = bawDataset.getQ1Value(row, column);
- Number xQ3 = bawDataset.getQ3Value(row, column);
- Number xMax = bawDataset.getMaxRegularValue(row, column);
- Number xMin = bawDataset.getMinRegularValue(row, column);
- Shape box = null;
- if (xQ1 != null && xQ3 != null && xMax != null && xMin != null) {
- double xxQ1 = rangeAxis.valueToJava2D(
- xQ1.doubleValue(), dataArea, location
- );
- double xxQ3 = rangeAxis.valueToJava2D(
- xQ3.doubleValue(), dataArea, location
- );
- double xxMax = rangeAxis.valueToJava2D(
- xMax.doubleValue(), dataArea, location
- );
- double xxMin = rangeAxis.valueToJava2D(
- xMin.doubleValue(), dataArea, location
- );
- double yymid = yy + state.getBarWidth() / 2.0;
- // draw the upper shadow...
- g2.draw(new Line2D.Double(xxMax, yymid, xxQ3, yymid));
- g2.draw(
- new Line2D.Double(xxMax, yy, xxMax, yy + state.getBarWidth())
- );
- // draw the lower shadow...
- g2.draw(new Line2D.Double(xxMin, yymid, xxQ1, yymid));
- g2.draw(
- new Line2D.Double(xxMin, yy, xxMin, yy + state.getBarWidth())
- );
- // draw the box...
- box = new Rectangle2D.Double(
- Math.min(xxQ1, xxQ3), yy, Math.abs(xxQ1 - xxQ3),
- state.getBarWidth()
- );
- if (this.fillBox) {
- g2.fill(box);
- }
- g2.draw(box);
- }
- g2.setPaint(this.artifactPaint);
- double aRadius = 0; // average radius
- // draw mean - SPECIAL AIMS REQUIREMENT...
- Number xMean = bawDataset.getMeanValue(row, column);
- if (xMean != null) {
- double xxMean = rangeAxis.valueToJava2D(
- xMean.doubleValue(), dataArea, location
- );
- aRadius = state.getBarWidth() / 4;
- Ellipse2D.Double avgEllipse = new Ellipse2D.Double(
- xxMean - aRadius, yy + aRadius, aRadius * 2, aRadius * 2
- );
- g2.fill(avgEllipse);
- g2.draw(avgEllipse);
- }
- // draw median...
- Number xMedian = bawDataset.getMedianValue(row, column);
- if (xMedian != null) {
- double xxMedian = rangeAxis.valueToJava2D(
- xMedian.doubleValue(), dataArea, location
- );
- g2.draw(
- new Line2D.Double(
- xxMedian, yy, xxMedian, yy + state.getBarWidth()
- )
- );
- }
- // collect entity and tool tip information...
- if (state.getInfo() != null) {
- EntityCollection entities
- = state.getInfo().getOwner().getEntityCollection();
- if (entities != null) {
- String tip = null;
- CategoryToolTipGenerator tipster
- = getToolTipGenerator(row, column);
- if (tipster != null) {
- tip = tipster.generateToolTip(dataset, row, column);
- }
- String url = null;
- if (getItemURLGenerator(row, column) != null) {
- url = getItemURLGenerator(row, column).generateURL(
- dataset, row, column
- );
- }
- CategoryItemEntity entity = new CategoryItemEntity(
- box, tip, url, dataset, row, dataset.getColumnKey(column),
- column
- );
- entities.add(entity);
- }
- }
- }
- /**
- * Draws the visual representation of a single data item when the plot has
- * a vertical orientation.
- *
- * @param g2 the graphics device.
- * @param state the renderer state.
- * @param dataArea the area within which the plot is being drawn.
- * @param plot the plot (can be used to obtain standard color information
- * etc).
- * @param domainAxis the domain axis.
- * @param rangeAxis the range axis.
- * @param dataset the dataset.
- * @param row the row index (zero-based).
- * @param column the column index (zero-based).
- */
- public void drawVerticalItem(Graphics2D g2,
- CategoryItemRendererState state,
- Rectangle2D dataArea,
- CategoryPlot plot,
- CategoryAxis domainAxis,
- ValueAxis rangeAxis,
- CategoryDataset dataset,
- int row,
- int column) {
- BoxAndWhiskerCategoryDataset bawDataset
- = (BoxAndWhiskerCategoryDataset) dataset;
- double categoryEnd = domainAxis.getCategoryEnd(
- column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
- );
- double categoryStart = domainAxis.getCategoryStart(
- column, getColumnCount(), dataArea, plot.getDomainAxisEdge()
- );
- double categoryWidth = categoryEnd - categoryStart;
- double xx = categoryStart;
- int seriesCount = getRowCount();
- int categoryCount = getColumnCount();
- if (seriesCount > 1) {
- double seriesGap = dataArea.getWidth() * getItemMargin()
- / (categoryCount * (seriesCount - 1));
- double usedWidth = (state.getBarWidth() * seriesCount)
- + (seriesGap * (seriesCount - 1));
- // offset the start of the boxes if the total width used is smaller
- // than the category width
- double offset = (categoryWidth - usedWidth) / 2;
- xx = xx + offset + (row * (state.getBarWidth() + seriesGap));
- }
- else {
- // offset the start of the box if the box width is smaller than the
- // category width
- double offset = (categoryWidth - state.getBarWidth()) / 2;
- xx = xx + offset;
- }
- double yyAverage = 0.0;
- double yyOutlier;
- Paint p = this.getItemPaint(row, column);
- if (p != null) {
- g2.setPaint(p);
- }
- Stroke s = getItemStroke(row, column);
- g2.setStroke(s);
- double aRadius = 0; // average radius
- RectangleEdge location = plot.getRangeAxisEdge();
- Number yQ1 = bawDataset.getQ1Value(row, column);
- Number yQ3 = bawDataset.getQ3Value(row, column);
- Number yMax = bawDataset.getMaxRegularValue(row, column);
- Number yMin = bawDataset.getMinRegularValue(row, column);
- Shape box = null;
- if (yQ1 != null && yQ3 != null && yMax != null && yMin != null) {
- double yyQ1 = rangeAxis.valueToJava2D(
- yQ1.doubleValue(), dataArea, location
- );
- double yyQ3 = rangeAxis.valueToJava2D(
- yQ3.doubleValue(), dataArea, location
- );
- double yyMax = rangeAxis.valueToJava2D(
- yMax.doubleValue(), dataArea, location
- );
- double yyMin = rangeAxis.valueToJava2D(
- yMin.doubleValue(), dataArea, location
- );
- double xxmid = xx + state.getBarWidth() / 2.0;
- // draw the upper shadow...
- g2.draw(new Line2D.Double(xxmid, yyMax, xxmid, yyQ3));
- g2.draw(
- new Line2D.Double(xx, yyMax, xx + state.getBarWidth(), yyMax)
- );
- // draw the lower shadow...
- g2.draw(new Line2D.Double(xxmid, yyMin, xxmid, yyQ1));
- g2.draw(
- new Line2D.Double(xx, yyMin, xx + state.getBarWidth(), yyMin)
- );
- // draw the body...
- box = new Rectangle2D.Double(
- xx, Math.min(yyQ1, yyQ3), state.getBarWidth(),
- Math.abs(yyQ1 - yyQ3)
- );
- if (this.fillBox) {
- g2.fill(box);
- }
- g2.draw(box);
- }
- g2.setPaint(this.artifactPaint);
- // draw mean - SPECIAL AIMS REQUIREMENT...
- Number yMean = bawDataset.getMeanValue(row, column);
- if (yMean != null) {
- yyAverage = rangeAxis.valueToJava2D(
- yMean.doubleValue(), dataArea, location
- );
- aRadius = state.getBarWidth() / 4;
- Ellipse2D.Double avgEllipse = new Ellipse2D.Double(
- xx + aRadius, yyAverage - aRadius, aRadius * 2, aRadius * 2
- );
- g2.fill(avgEllipse);
- g2.draw(avgEllipse);
- }
- // draw median...
- Number yMedian = bawDataset.getMedianValue(row, column);
- if (yMedian != null) {
- double yyMedian = rangeAxis.valueToJava2D(
- yMedian.doubleValue(), dataArea, location
- );
- g2.draw(
- new Line2D.Double(
- xx, yyMedian, xx + state.getBarWidth(), yyMedian
- )
- );
- }
- // draw yOutliers...
- double maxAxisValue = rangeAxis.valueToJava2D(
- rangeAxis.getUpperBound(), dataArea, location
- ) + aRadius;
- double minAxisValue = rangeAxis.valueToJava2D(
- rangeAxis.getLowerBound(), dataArea, location
- ) - aRadius;
- g2.setPaint(p);
- // draw outliers
- double oRadius = state.getBarWidth() / 3; // outlier radius
- List outliers = new ArrayList();
- OutlierListCollection outlierListCollection
- = new OutlierListCollection();
- // From outlier array sort out which are outliers and put these into a
- // list If there are any farouts, set the flag on the
- // OutlierListCollection
- List yOutliers = bawDataset.getOutliers(row, column);
- if (yOutliers != null) {
- for (int i = 0; i < yOutliers.size(); i++) {
- double outlier = ((Number) yOutliers.get(i)).doubleValue();
- Number minOutlier = bawDataset.getMinOutlier(row, column);
- Number maxOutlier = bawDataset.getMaxOutlier(row, column);
- Number minRegular = bawDataset.getMinRegularValue(row, column);
- Number maxRegular = bawDataset.getMaxRegularValue(row, column);
- if (outlier > maxOutlier.doubleValue()) {
- outlierListCollection.setHighFarOut(true);
- }
- else if (outlier < minOutlier.doubleValue()) {
- outlierListCollection.setLowFarOut(true);
- }
- else if (outlier > maxRegular.doubleValue()) {
- yyOutlier = rangeAxis.valueToJava2D(
- outlier, dataArea, location
- );
- outliers.add(
- new Outlier(
- xx + state.getBarWidth() / 2.0, yyOutlier, oRadius
- )
- );
- }
- else if (outlier < minRegular.doubleValue()) {
- yyOutlier = rangeAxis.valueToJava2D(
- outlier, dataArea, location
- );
- outliers.add(
- new Outlier(
- xx + state.getBarWidth() / 2.0, yyOutlier, oRadius
- )
- );
- }
- Collections.sort(outliers);
- }
- // Process outliers. Each outlier is either added to the
- // appropriate outlier list or a new outlier list is made
- for (Iterator iterator = outliers.iterator(); iterator.hasNext();) {
- Outlier outlier = (Outlier) iterator.next();
- outlierListCollection.add(outlier);
- }
- for (Iterator iterator = outlierListCollection.iterator();
- iterator.hasNext();) {
- OutlierList list = (OutlierList) iterator.next();
- Outlier outlier = list.getAveragedOutlier();
- Point2D point = outlier.getPoint();
- if (list.isMultiple()) {
- drawMultipleEllipse(
- point, state.getBarWidth(), oRadius, g2
- );
- }
- else {
- drawEllipse(point, oRadius, g2);
- }
- }
- // draw farout indicators
- if (outlierListCollection.isHighFarOut()) {
- drawHighFarOut(
- aRadius / 2.0, g2, xx + state.getBarWidth() / 2.0,
- maxAxisValue
- );
- }
- if (outlierListCollection.isLowFarOut()) {
- drawLowFarOut(
- aRadius / 2.0, g2, xx + state.getBarWidth() / 2.0,
- minAxisValue
- );
- }
- }
- // collect entity and tool tip information...
- if (state.getInfo() != null) {
- EntityCollection entities
- = state.getInfo().getOwner().getEntityCollection();
- if (entities != null) {
- String tip = null;
- CategoryToolTipGenerator tipster
- = getToolTipGenerator(row, column);
- if (tipster != null) {
- tip = tipster.generateToolTip(dataset, row, column);
- }
- String url = null;
- if (getItemURLGenerator(row, column) != null) {
- url = getItemURLGenerator(row, column).generateURL(
- dataset, row, column
- );
- }
- CategoryItemEntity entity = new CategoryItemEntity(
- box, tip, url, dataset, row, dataset.getColumnKey(column),
- column
- );
- entities.add(entity);
- }
- }
- }
- /**
- * Draws a dot to represent an outlier.
- *
- * @param point the location.
- * @param oRadius the radius.
- * @param g2 the graphics device.
- */
- private void drawEllipse(Point2D point, double oRadius, Graphics2D g2) {
- Ellipse2D dot = new Ellipse2D.Double(
- point.getX() + oRadius / 2, point.getY(), oRadius, oRadius
- );
- g2.draw(dot);
- }
- /**
- * Draws two dots to represent the average value of more than one outlier.
- *
- * @param point the location
- * @param boxWidth the box width.
- * @param oRadius the radius.
- * @param g2 the graphics device.
- */
- private void drawMultipleEllipse(Point2D point, double boxWidth,
- double oRadius, Graphics2D g2) {
- Ellipse2D dot1 = new Ellipse2D.Double(
- point.getX() - (boxWidth / 2) + oRadius, point.getY(),
- oRadius, oRadius
- );
- Ellipse2D dot2 = new Ellipse2D.Double(
- point.getX() + (boxWidth / 2), point.getY(), oRadius, oRadius
- );
- g2.draw(dot1);
- g2.draw(dot2);
- }
- /**
- * Draws a triangle to indicate the presence of far-out values.
- *
- * @param aRadius the radius.
- * @param g2 the graphics device.
- * @param xx the x coordinate.
- * @param m the y coordinate.
- */
- private void drawHighFarOut(double aRadius, Graphics2D g2, double xx,
- double m) {
- double side = aRadius * 2;
- g2.draw(new Line2D.Double(xx - side, m + side, xx + side, m + side));
- g2.draw(new Line2D.Double(xx - side, m + side, xx, m));
- g2.draw(new Line2D.Double(xx + side, m + side, xx, m));
- }
- /**
- * Draws a triangle to indicate the presence of far-out values.
- *
- * @param aRadius the radius.
- * @param g2 the graphics device.
- * @param xx the x coordinate.
- * @param m the y coordinate.
- */
- private void drawLowFarOut(double aRadius, Graphics2D g2, double xx,
- double m) {
- double side = aRadius * 2;
- g2.draw(new Line2D.Double(xx - side, m - side, xx + side, m - side));
- g2.draw(new Line2D.Double(xx - side, m - side, xx, m));
- g2.draw(new Line2D.Double(xx + side, m - side, xx, m));
- }
- /**
- * Tests this renderer for equality with an arbitrary object.
- *
- * @param obj the object (<code>null</code> permitted).
- *
- * @return <code>true</code> or <code>false</code>.
- */
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj instanceof BoxAndWhiskerRenderer && super.equals(obj)) {
- BoxAndWhiskerRenderer r = (BoxAndWhiskerRenderer) obj;
- if (!ObjectUtilities.equal(r.artifactPaint, this.artifactPaint)) {
- return false;
- }
- if (!(r.fillBox == this.fillBox)) {
- return false;
- }
- if (!(r.itemMargin == this.itemMargin)) {
- return false;
- }
- return true;
- }
- return false;
- }
- /**
- * Provides serialization support.
- *
- * @param stream the output stream.
- *
- * @throws IOException if there is an I/O error.
- */
- private void writeObject(ObjectOutputStream stream) throws IOException {
- stream.defaultWriteObject();
- SerialUtilities.writePaint(this.artifactPaint, stream);
- }
- /**
- * Provides serialization support.
- *
- * @param stream the input stream.
- *
- * @throws IOException if there is an I/O error.
- * @throws ClassNotFoundException if there is a classpath problem.
- */
- private void readObject(ObjectInputStream stream)
- throws IOException, ClassNotFoundException {
- stream.defaultReadObject();
- this.artifactPaint = SerialUtilities.readPaint(stream);
- }
- }