/* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2016, by Object Refinery Limited. All rights reserved.
*
* http://www.object-refinery.com/orsoncharts/index.html
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* If you do not wish to be bound by the terms of the GPL, an alternative
* commercial license can be purchased. For details, please see visit the
* Orson Charts home page:
*
* http://www.object-refinery.com/orsoncharts/index.html
*
*/
package com.orsoncharts.plot;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Paint;
import java.awt.Stroke;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.orsoncharts.Chart3D;
import com.orsoncharts.ChartElementVisitor;
import com.orsoncharts.axis.Axis3DChangeEvent;
import com.orsoncharts.axis.Axis3DChangeListener;
import com.orsoncharts.axis.CategoryAxis3D;
import com.orsoncharts.axis.ValueAxis3D;
import com.orsoncharts.data.Dataset3DChangeEvent;
import com.orsoncharts.data.ItemKey;
import com.orsoncharts.data.KeyedValues3DItemKey;
import com.orsoncharts.data.category.CategoryDataset3D;
import com.orsoncharts.graphics3d.Dimension3D;
import com.orsoncharts.graphics3d.World;
import com.orsoncharts.label.CategoryLabelGenerator;
import com.orsoncharts.label.CategoryItemLabelGenerator;
import com.orsoncharts.label.StandardCategoryLabelGenerator;
import com.orsoncharts.label.StandardCategoryItemLabelGenerator;
import com.orsoncharts.legend.LegendItemInfo;
import com.orsoncharts.legend.StandardLegendItemInfo;
import com.orsoncharts.renderer.Renderer3DChangeEvent;
import com.orsoncharts.renderer.Renderer3DChangeListener;
import com.orsoncharts.renderer.category.CategoryRenderer3D;
import com.orsoncharts.util.ArgChecks;
import com.orsoncharts.util.ObjectUtils;
import com.orsoncharts.util.SerialUtils;
/**
* A 3D plot with two category axes (x and z) and a numerical y-axis that can
* display data from a {@link CategoryDataset3D}.
* <br><br>
* The plot implements several listener interfaces so that it can receive
* notification of changes to its dataset, axes and renderer. When change
* events are received, the plot passes on a {@link Plot3DChangeEvent} to the
* {@link Chart3D} instance that owns the plot. This event chain is the
* mechanism that ensures that charts are repainted whenever the dataset
* changes, or when changes are made to the configuration of any chart
* component.
* <br><br>
* NOTE: This class is serializable, but the serialization format is subject
* to change in future releases and should not be relied upon for persisting
* instances of this class.
*/
@SuppressWarnings("serial")
public class CategoryPlot3D extends AbstractPlot3D
implements Axis3DChangeListener, Renderer3DChangeListener,
Serializable {
private static Stroke DEFAULT_GRIDLINE_STROKE = new BasicStroke(0.5f,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1f,
new float[] { 3f, 3f }, 0f);
/** The dataset. */
private CategoryDataset3D dataset;
/** The renderer (never {@code null}). */
private CategoryRenderer3D renderer;
/** The row axis. */
private CategoryAxis3D rowAxis;
/** The column axis. */
private CategoryAxis3D columnAxis;
/** The value axis. */
private ValueAxis3D valueAxis;
/** Are gridlines shown for the row (z) axis? */
private boolean gridlinesVisibleForRows;
/** The paint for the row axis gridlines (never {@code null}). */
private transient Paint gridlinePaintForRows;
/** The stroke for the row axis gridlines (never {@code null}). */
private transient Stroke gridlineStrokeForRows;
/** Are gridlines shown for the column (x) axis? */
private boolean gridlinesVisibleForColumns;
/** The paint for the column axis gridlines (never {@code null}). */
private transient Paint gridlinePaintForColumns;
/** The stroke for the column axis gridlines (never {@code null}). */
private transient Stroke gridlineStrokeForColumns;
/** Are gridlines shown for the value axis? */
private boolean gridlinesVisibleForValues;
/** The paint for the value axis gridlines (never {@code null}). */
private transient Paint gridlinePaintForValues;
/** The stroke for the value axis gridlines (never {@code null}). */
private transient Stroke gridlineStrokeForValues;
/** The legend label generator. */
private CategoryLabelGenerator legendLabelGenerator;
/**
* A special attribute to provide control over the y-dimension for the
* plot when the plot dimensions are auto-calculated. The default value
* is {@code null}.
*
* @since 1.2
*/
private Double yDimensionOverride;
/**
* The tool tip generator (if null there will be no tool tips).
*
* @since 1.3
*/
private CategoryItemLabelGenerator toolTipGenerator;
/**
* Creates a new plot with the supplied dataset, renderer and axes.
*
* @param dataset the dataset ({@code null} not permitted).
* @param renderer the renderer ({@code null} not permitted).
* @param rowAxis the row axis ({@code null} not permitted).
* @param columnAxis the column axis ({@code null} not permitted).
* @param valueAxis the value axis ({@code null} not permitted).
*/
public CategoryPlot3D(CategoryDataset3D dataset,
CategoryRenderer3D renderer, CategoryAxis3D rowAxis,
CategoryAxis3D columnAxis, ValueAxis3D valueAxis) {
ArgChecks.nullNotPermitted(dataset, "dataset");
ArgChecks.nullNotPermitted(renderer, "renderer");
ArgChecks.nullNotPermitted(rowAxis, "rowAxis");
ArgChecks.nullNotPermitted(columnAxis, "columnAxis");
ArgChecks.nullNotPermitted(valueAxis, "valueAxis");
this.dataset = dataset;
this.dataset.addChangeListener(this);
this.dimensions = calculateDimensions();
this.renderer = renderer;
this.renderer.setPlot(this);
this.renderer.addChangeListener(this);
this.rowAxis = rowAxis;
this.rowAxis.addChangeListener(this);
this.columnAxis = columnAxis;
this.columnAxis.addChangeListener(this);
this.valueAxis = valueAxis;
this.valueAxis.addChangeListener(this);
this.rowAxis.configureAsRowAxis(this);
this.columnAxis.configureAsColumnAxis(this);
this.valueAxis.configureAsValueAxis(this);
this.gridlinesVisibleForValues = true;
this.gridlinesVisibleForColumns = false;
this.gridlinesVisibleForRows = false;
this.gridlinePaintForRows = Color.WHITE;
this.gridlinePaintForColumns = Color.WHITE;
this.gridlinePaintForValues = Color.WHITE;
this.gridlineStrokeForRows = DEFAULT_GRIDLINE_STROKE;
this.gridlineStrokeForColumns = DEFAULT_GRIDLINE_STROKE;
this.gridlineStrokeForValues = DEFAULT_GRIDLINE_STROKE;
this.legendLabelGenerator = new StandardCategoryLabelGenerator();
this.yDimensionOverride = null;
this.toolTipGenerator = new StandardCategoryItemLabelGenerator();
}
/**
* Sets the flag that controls whether the plot's dimensions are
* automatically calculated and, if {@code true}, sends a change
* event to all registered listeners.
*
* @param auto the new flag value.
*
* @since 1.2
*/
public void setAutoAdjustDimensions(boolean auto) {
this.autoAdjustDimensions = auto;
if (auto) {
this.dimensions = calculateDimensions();
fireChangeEvent(true);
}
}
/**
* Sets the dimensions (in 3D space) for the plot, resets the
* {@code autoAdjustDimensions} flag to {@code false}, and sends
* a {@link Plot3DChangeEvent} to all registered listeners.
*
* @param dimensions the dimensions ({@code null} not permitted).
*
* @see Plot3D#getDimensions()
*/
public void setDimensions(Dimension3D dimensions) {
ArgChecks.nullNotPermitted(dimensions, "dimensions");
this.dimensions = dimensions;
this.autoAdjustDimensions = false;
fireChangeEvent(true);
}
/**
* Returns the dataset for the chart.
*
* @return The dataset (never {@code null}).
*/
public CategoryDataset3D getDataset() {
return this.dataset;
}
/**
* Sets the dataset and sends a {@link Plot3DChangeEvent} to all registered
* listeners. When you call this method, the axes will be reconfigured for
* the new data.
*
* @param dataset the dataset ({@code null} not permitted).
*/
public void setDataset(CategoryDataset3D dataset) {
ArgChecks.nullNotPermitted(dataset, "dataset");
this.dataset.removeChangeListener(this);
this.dataset = dataset;
this.dataset.addChangeListener(this);
// we send ourselves a dataset change event since this will
// reconfigure the axes then trigger the required plot change event
datasetChanged(new Dataset3DChangeEvent(this, this.dataset));
}
/**
* Returns the renderer (very often you will need to cast this to a
* specific class to make customisations).
*
* @return The renderer (never {@code null}).
*/
public CategoryRenderer3D getRenderer() {
return this.renderer;
}
/**
* Sets the renderer and sends a change event to all registered listeners.
*
* @param renderer the renderer ({@code null} not permitted).
*/
public void setRenderer(CategoryRenderer3D renderer) {
ArgChecks.nullNotPermitted(renderer, "renderer");
this.renderer.removeChangeListener(this);
this.renderer = renderer;
this.renderer.addChangeListener(this);
// a new renderer might mean the axis range needs changing...
this.valueAxis.configureAsValueAxis(this);
fireChangeEvent(true);
}
/**
* Returns the row axis.
*
* @return The row axis.
*/
public CategoryAxis3D getRowAxis() {
return this.rowAxis;
}
/**
* Sets the row axis and sends a {@link Plot3DChangeEvent} to all
* registered listeners. The row axis is equivalent to the z-axis.
*
* @param axis the row axis ({@code null} not permitted).
*/
public void setRowAxis(CategoryAxis3D axis) {
ArgChecks.nullNotPermitted(axis, "axis");
this.rowAxis.removeChangeListener(this);
this.rowAxis = axis;
this.rowAxis.addChangeListener(this);
fireChangeEvent(true);
}
/**
* Returns the column axis.
*
* @return The column axis (never {@code null}).
*/
public CategoryAxis3D getColumnAxis() {
return this.columnAxis;
}
/**
* Sets the column axis and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param axis the new axis ({@code null} not permitted).
*
* @see #setRowAxis(com.orsoncharts.axis.CategoryAxis3D)
* @see #setValueAxis(com.orsoncharts.axis.ValueAxis3D)
*
*/
public void setColumnAxis(CategoryAxis3D axis) {
ArgChecks.nullNotPermitted(axis, "axis");
this.columnAxis.removeChangeListener(this);
this.columnAxis = axis;
this.columnAxis.addChangeListener(this);
fireChangeEvent(true);
}
/**
* Returns the value axis (the vertical axis in the plot).
*
* @return The value axis (never {@code null}).
*/
public ValueAxis3D getValueAxis() {
return this.valueAxis;
}
/**
* Sets the value axis and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param axis the axis ({@code null} not permitted).
*/
public void setValueAxis(ValueAxis3D axis) {
ArgChecks.nullNotPermitted(axis, "axis");
this.valueAxis.removeChangeListener(this);
this.valueAxis = axis;
this.valueAxis.configureAsValueAxis(this);
this.valueAxis.addChangeListener(this);
fireChangeEvent(true);
}
/**
* Returns {@code true} if gridlines are shown for the column axis
* and {@code false} otherwise. The default value is {@code false}.
*
* @return A boolean.
*/
public boolean getGridlinesVisibleForRows() {
return this.gridlinesVisibleForRows;
}
/**
* Sets the flag that controls whether or not gridlines are shown for the
* row axis and sends a {@link Plot3DChangeEvent} to all registered
* listeners.
*
* @param visible the new flag value.
*/
public void setGridlinesVisibleForRows(boolean visible) {
this.gridlinesVisibleForRows = visible;
fireChangeEvent(false);
}
/**
* Returns the paint used to draw the gridlines for the row axis, if they
* are visible.
*
* @return The paint (never {@code null}).
*/
public Paint getGridlinePaintForRows() {
return this.gridlinePaintForRows;
}
/**
* Sets the paint used for the row axis gridlines and sends a
* {@link Plot3DChangeEvent} to all registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*/
public void setGridlinePaintForRows(Paint paint) {
ArgChecks.nullNotPermitted(paint, "paint");
this.gridlinePaintForRows = paint;
fireChangeEvent(false);
}
/**
* Returns the stroke for the gridlines associated with the row axis.
* The default value is {@code BasicStroke(0.5f, BasicStroke.CAP_ROUND,
* BasicStroke.JOIN_ROUND, 1f, new float[] { 3f, 3f }, 0f)}.
*
* @return The stroke (never {@code null}).
*/
public Stroke getGridlineStrokeForRows() {
return this.gridlineStrokeForRows;
}
/**
* Sets the stroke used to draw the gridlines for the row axis, if they
* are visible, and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param stroke the stroke ({@code null} not permitted).
*/
public void setGridlineStrokeForRows(Stroke stroke) {
ArgChecks.nullNotPermitted(stroke, "stroke");
this.gridlineStrokeForRows = stroke;
fireChangeEvent(false);
}
/**
* Returns {@code true} if gridlines are shown for the column axis
* and {@code false} otherwise. The default value is {@code false}.
*
* @return A boolean.
*/
public boolean getGridlinesVisibleForColumns() {
return this.gridlinesVisibleForColumns;
}
/**
* Sets the flag that controls whether or not gridlines are shown for the
* column axis and sends a {@link Plot3DChangeEvent} to all registered
* listeners.
*
* @param visible the new flag value.
*/
public void setGridlinesVisibleForColumns(boolean visible) {
this.gridlinesVisibleForColumns = visible;
fireChangeEvent(false);
}
/**
* Returns {@code true} if gridlines are shown for the column axis
* and {@code false} otherwise. The default value is {@code true}.
*
* @return A boolean.
*/
public boolean getGridlinesVisibleForValues() {
return this.gridlinesVisibleForValues;
}
/**
* Sets the flag that controls whether or not gridlines are shown for the
* value axis and sends a {@link Plot3DChangeEvent} to all registered
* listeners.
*
* @param visible the new flag value.
*/
public void setGridlinesVisibleForValues(boolean visible) {
this.gridlinesVisibleForValues = visible;
fireChangeEvent(false);
}
/**
* Returns the paint for the gridlines associated with the value axis.
* The default value is {@code Color.WHITE}.
*
* @return The paint for value axis gridlines (never {@code null}).
*/
public Paint getGridlinePaintForValues() {
return this.gridlinePaintForValues;
}
/**
* Sets the paint used for the value axis gridlines and sends a
* {@link Plot3DChangeEvent} to all registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*/
public void setGridlinePaintForValues(Paint paint) {
ArgChecks.nullNotPermitted(paint, "paint");
this.gridlinePaintForValues = paint;
fireChangeEvent(false);
}
/**
* Returns the stroke for the gridlines associated with the value axis.
* The default value is {@code BasicStroke(0.5f, BasicStroke.CAP_ROUND,
* BasicStroke.JOIN_ROUND, 1f, new float[] { 3f, 3f }, 0f)}.
*
* @return The stroke (never {@code null}).
*/
public Stroke getGridlineStrokeForValues() {
return this.gridlineStrokeForValues;
}
/**
* Sets the stroke used to draw the grid lines for the value axis, if
* they are visible, and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param stroke the stroke ({@code null} not permitted).
*/
public void setGridlineStrokeForValues(Stroke stroke) {
ArgChecks.nullNotPermitted(stroke, "stroke");
this.gridlineStrokeForValues = stroke;
fireChangeEvent(false);
}
/**
* Returns the paint used to draw the grid lines for the column axis, if
* they are visible. The default value is {@code Color.WHITE}.
*
* @return The paint (never {@code null}).
*/
public Paint getGridlinePaintForColumns() {
return this.gridlinePaintForColumns;
}
/**
* Sets the paint used to draw the grid lines for the column axis, if
* they are visible, and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*/
public void setGridlinePaintForColumns(Paint paint) {
ArgChecks.nullNotPermitted(paint, "paint");
this.gridlinePaintForColumns = paint;
fireChangeEvent(false);
}
/**
* Returns the stroke for the gridlines associated with the column axis.
* The default value is {@code BasicStroke(0.5f, BasicStroke.CAP_ROUND,
* BasicStroke.JOIN_ROUND, 1f, new float[] { 3f, 3f }, 0f)}.
*
* @return The stroke (never {@code null}).
*/
public Stroke getGridlineStrokeForColumns() {
return this.gridlineStrokeForColumns;
}
/**
* Sets the stroke used to draw the grid lines for the column axis, if
* they are visible, and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param stroke the stroke ({@code null} not permitted).
*/
public void setGridlineStrokeForColumns(Stroke stroke) {
ArgChecks.nullNotPermitted(stroke, "stroke");
this.gridlineStrokeForColumns = stroke;
fireChangeEvent(false);
}
/**
* Returns the legend label generator, an object that converts key values
* in the dataset into corresponding strings for presentation in the chart.
*
* @return The legend label generator (never {@code null}).
*
* @since 1.2
*/
public CategoryLabelGenerator getLegendLabelGenerator() {
return this.legendLabelGenerator;
}
/**
* Sets the legend label generator and sends a {@link Plot3DChangeEvent}
* to all registered listeners.
*
* @param generator the generator ({@code null} not permitted).
*
* @since 1.2
*/
public void setLegendLabelGenerator(CategoryLabelGenerator generator) {
ArgChecks.nullNotPermitted(generator, "generator");
this.legendLabelGenerator = generator;
fireChangeEvent(false);
}
/**
* Returns the y-dimension override. The default value is {@code null},
* which means that when the plot dimensions are automatically calculated,
* the height of the plot will be set to the greater of the width and
* the depth.
*
* @return The y-dimension override (possibly {@code null}).
*
* @since 1.2
*/
public Double getYDimensionOverride() {
return this.yDimensionOverride;
}
/**
* Sets the y-dimension override and, if the {@code autoAdjustDimensions}
* flag is set, recalculates the dimensions and sends a
* {@link Plot3DChangeEvent} to all registered listeners.
*
* @param dim the new y-dimension override ({@code null} permitted).
*
* @since 1.2
*/
public void setYDimensionOverride(Double dim) {
this.yDimensionOverride = dim;
if (this.autoAdjustDimensions) {
this.dimensions = calculateDimensions();
fireChangeEvent(true);
}
}
/**
* Returns the tool tip generator. This is an object that calculates and
* returns a string (that will be used as the tool tip) for any given
* data value in the dataset.
*
* @return The tool tip generator (possibly {@code null}).
*
* @since 1.3
*/
public CategoryItemLabelGenerator getToolTipGenerator() {
return this.toolTipGenerator;
}
/**
* Sets the tool tip generator and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param generator the new generator ({@code null} permitted).
*
* @since 1.3
*/
public void setToolTipGenerator(CategoryItemLabelGenerator generator) {
this.toolTipGenerator = generator;
fireChangeEvent(false);
}
/**
* Returns a list containing legend item info, typically one item for
* each series in the chart. This is intended for use in the construction
* of a chart legend.
*
* @return A list containing legend item info (possibly empty but never
* {@code null}).
*/
@Override
@SuppressWarnings("unchecked") // we don't know the dataset generic types
public List<LegendItemInfo> getLegendInfo() {
List<LegendItemInfo> result = new ArrayList<LegendItemInfo>();
List<Comparable<?>> keys = this.dataset.getSeriesKeys();
for (Comparable<?> key : keys) {
int series = this.dataset.getSeriesIndex(key);
Color color = this.renderer.getColorSource().getLegendColor(series);
String seriesLabel = this.legendLabelGenerator.generateSeriesLabel(
this.dataset, key);
LegendItemInfo info = new StandardLegendItemInfo(key,
seriesLabel, color);
result.add(info);
}
return result;
}
@Override
public void compose(World world, double xOffset, double yOffset,
double zOffset) {
for (int series = 0; series < this.dataset.getSeriesCount(); series++) {
for (int row = 0; row < this.dataset.getRowCount(); row++) {
for (int column = 0; column < this.dataset.getColumnCount();
column++) {
this.renderer.composeItem(this.dataset, series, row, column,
world, getDimensions(), xOffset, yOffset, zOffset);
}
}
}
}
@Override
public String generateToolTipText(ItemKey itemKey) {
if (!(itemKey instanceof KeyedValues3DItemKey)) {
throw new IllegalArgumentException(
"The itemKey must be a Values3DItemKey instance.");
}
KeyedValues3DItemKey vik = (KeyedValues3DItemKey) itemKey;
return this.toolTipGenerator.generateItemLabel(dataset,
vik.getSeriesKey(), vik.getRowKey(), vik.getColumnKey());
}
/**
* Accepts a visitor for the plot. This method first calls the
* {@code receive()} method for each of the plot's axes and the renderer,
* then performs the visitor's function on the plot. This is a general
* purpose mechanism, but the main use is to apply chart style changes
* across all the elements of a chart.
*
* @param visitor the visitor ({@code null} not permitted).
*
* @since 1.2
*/
@Override
public void receive(ChartElementVisitor visitor) {
this.columnAxis.receive(visitor);
this.rowAxis.receive(visitor);
this.valueAxis.receive(visitor);
this.renderer.receive(visitor);
visitor.visit(this);
}
/**
* Tests this plot for equality with an arbitrary object.
*
* @param obj the object ({@code null} permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof CategoryPlot3D)) {
return false;
}
CategoryPlot3D that = (CategoryPlot3D) obj;
if (this.gridlinesVisibleForRows != that.gridlinesVisibleForRows) {
return false;
}
if (!this.gridlineStrokeForRows.equals(that.gridlineStrokeForRows)) {
return false;
}
if (!ObjectUtils.equalsPaint(this.gridlinePaintForRows,
that.gridlinePaintForRows)) {
return false;
}
if (this.gridlinesVisibleForColumns
!= that.gridlinesVisibleForColumns) {
return false;
}
if (!this.gridlineStrokeForColumns.equals(
that.gridlineStrokeForColumns)) {
return false;
}
if (!ObjectUtils.equalsPaint(this.gridlinePaintForColumns,
that.gridlinePaintForColumns)) {
return false;
}
if (this.gridlinesVisibleForValues != that.gridlinesVisibleForValues) {
return false;
}
if (!this.gridlineStrokeForValues.equals(that.gridlineStrokeForValues)) {
return false;
}
if (!ObjectUtils.equalsPaint(this.gridlinePaintForValues,
that.gridlinePaintForValues)) {
return false;
}
if (!this.legendLabelGenerator.equals(that.legendLabelGenerator)) {
return false;
}
if (!ObjectUtils.equals(this.yDimensionOverride,
that.yDimensionOverride)) {
return false;
}
if (!ObjectUtils.equals(this.toolTipGenerator, that.toolTipGenerator)) {
return false;
}
return super.equals(obj);
}
/**
* Receives notification of a change to the dataset and handles this by
* adjusting the plot dimensions (according to the setting of the
* {@code autoAdjustDimensions} flag), reconfiguring the axes, and
* propagating a {@code Plot3DChangeEvent}.
*
* @param event the change event.
*/
@Override
public void datasetChanged(Dataset3DChangeEvent event) {
// update the category axis labels
// and the value axis range
if (this.autoAdjustDimensions) {
this.dimensions = calculateDimensions();
}
this.columnAxis.configureAsColumnAxis(this);
this.rowAxis.configureAsRowAxis(this);
this.valueAxis.configureAsValueAxis(this);
super.datasetChanged(event); // propagates a plot change event
}
/**
* Returns the dimensions for the plot that best suit the current data
* values. The x-dimension is set to the number of columns in the
* dataset and the z-dimension is set to the number of rows in the dataset.
* For the y-dimension, the code first checks the
* {@code yDimensionOverride} attribute to see if a specific value is
* requested...and if not, the minimum of the x and z dimensions will be
* used.
*
* @return The dimensions (never {@code null}).
*/
private Dimension3D calculateDimensions() {
double depth = Math.max(1.0, this.dataset.getRowCount() + 1);
double width = Math.max(1.0, this.dataset.getColumnCount() + 1);
double height = Math.max(1.0, Math.min(width, depth));
if (this.yDimensionOverride != null) {
height = this.yDimensionOverride.doubleValue();
}
return new Dimension3D(width, height, depth);
}
/**
* Receives notification that one of the axes has been changed. This will
* trigger a {@link Plot3DChangeEvent} that will usually cause the chart
* to be repainted.
*
* @param event the change event.
*/
@Override
public void axisChanged(Axis3DChangeEvent event) {
// for now we just fire a plot change event which will flow up the
// chain and eventually trigger a chart repaint
fireChangeEvent(event.requiresWorldUpdate());
}
/**
* Receives notification that the renderer has been modified in some way.
* This will trigger a {@link Plot3DChangeEvent} that will usually cause
* the chart to be repainted.
*
* @param event information about the event.
*/
@Override
public void rendererChanged(Renderer3DChangeEvent event) {
// for now we just fire a plot change event which will flow up the
// chain and eventually trigger a chart repaint
fireChangeEvent(event.requiresWorldUpdate());
}
/**
* 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();
SerialUtils.writePaint(this.gridlinePaintForRows, stream);
SerialUtils.writePaint(this.gridlinePaintForColumns, stream);
SerialUtils.writePaint(this.gridlinePaintForValues, stream);
SerialUtils.writeStroke(this.gridlineStrokeForRows, stream);
SerialUtils.writeStroke(this.gridlineStrokeForColumns, stream);
SerialUtils.writeStroke(this.gridlineStrokeForValues, 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.gridlinePaintForRows = SerialUtils.readPaint(stream);
this.gridlinePaintForColumns = SerialUtils.readPaint(stream);
this.gridlinePaintForValues = SerialUtils.readPaint(stream);
this.gridlineStrokeForRows = SerialUtils.readStroke(stream);
this.gridlineStrokeForColumns = SerialUtils.readStroke(stream);
this.gridlineStrokeForValues = SerialUtils.readStroke(stream);
}
}