org.jfree.chart3d.plot.PiePlot3D Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of org.jfree.chart3d Show documentation
Show all versions of org.jfree.chart3d Show documentation
Orson Charts is a 3D chart library for the Java platform.
/* ===========================================================
* Orson Charts : a 3D chart library for the Java(tm) platform
* ===========================================================
*
* (C)opyright 2013-2020, by Object Refinery Limited. All rights reserved.
*
* https://github.com/jfree/orson-charts
*
* 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 .
*
* [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 org.jfree.chart3d.plot;
import java.awt.Color;
import java.awt.Font;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.jfree.chart3d.internal.Args;
import org.jfree.chart3d.Chart3D;
import org.jfree.chart3d.Chart3DFactory;
import org.jfree.chart3d.ChartElementVisitor;
import org.jfree.chart3d.data.DataUtils;
import org.jfree.chart3d.data.ItemKey;
import org.jfree.chart3d.data.KeyedValuesItemKey;
import org.jfree.chart3d.data.PieDataset3D;
import org.jfree.chart3d.graphics3d.Dimension3D;
import org.jfree.chart3d.graphics3d.Dot3D;
import org.jfree.chart3d.graphics3d.Object3D;
import org.jfree.chart3d.graphics3d.World;
import org.jfree.chart3d.label.PieLabelGenerator;
import org.jfree.chart3d.label.StandardPieLabelGenerator;
import org.jfree.chart3d.legend.LegendItemInfo;
import org.jfree.chart3d.legend.StandardLegendItemInfo;
/**
* A plot for creating 3D pie charts. To create a pie chart, you can use the
* {@code createPieChart()} method in the {@link Chart3DFactory} class.
* A typical pie chart will look like this:
*
*
*
*
* 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 PiePlot3D extends AbstractPlot3D implements Serializable {
/** The default font for section labels on the chart. */
public static final Font DEFAULT_SECTION_LABEL_FONT
= new Font("Dialog", Font.PLAIN, 14);
/** The dataset. */
private PieDataset3D dataset;
/** The radius of the pie chart. */
private double radius;
/** The depth of the pie chart. */
private double depth;
/** The section color source. */
private ColorSource sectionColorSource;
/** The section label generator. */
private PieLabelGenerator sectionLabelGenerator;
/** The font source used to determine the font for section labels. */
private FontSource sectionLabelFontSource;
/**
* The color source used to determine the foreground color for section
* labels.
*/
private ColorSource sectionLabelColorSource;
/** The legend label generator. */
private PieLabelGenerator legendLabelGenerator;
/**
* The tool tip generator (can be null, in which case there will be no
* tool tips. */
private PieLabelGenerator toolTipGenerator;
/**
* The number of segments used to render 360 degrees of the pie. A higher
* number will give better output but slower performance.
*/
private int segments = 40;
/**
* Creates a new pie plot in 3D.
*
* @param dataset the dataset ({@code null} not permitted).
*/
public PiePlot3D(PieDataset3D dataset) {
Args.nullNotPermitted(dataset, "dataset");
this.dataset = dataset;
this.dataset.addChangeListener(this);
this.radius = 4.0;
this.depth = 0.5;
this.sectionColorSource = new StandardColorSource();
this.sectionLabelGenerator = new StandardPieLabelGenerator(
StandardPieLabelGenerator.KEY_ONLY_TEMPLATE);
this.sectionLabelFontSource = new StandardFontSource(
DEFAULT_SECTION_LABEL_FONT);
this.sectionLabelColorSource = new StandardColorSource(Color.BLACK);
this.legendLabelGenerator = new StandardPieLabelGenerator();
this.toolTipGenerator = new StandardPieLabelGenerator(
StandardPieLabelGenerator.PERCENT_TEMPLATE_2DP);
}
/**
* Returns the dataset.
*
* @return The dataset (never {@code null}).
*/
public PieDataset3D getDataset() {
return this.dataset;
}
/**
* Sets the dataset and notifies registered listeners that the dataset has
* been updated.
*
* @param dataset the dataset ({@code null} not permitted).
*/
public void setDataset(PieDataset3D dataset) {
Args.nullNotPermitted(dataset, "dataset");
this.dataset.removeChangeListener(this);
this.dataset = dataset;
this.dataset.addChangeListener(this);
fireChangeEvent(true);
}
/**
* Returns the radius of the pie (the default value is 8.0).
*
* @return The radius of the pie.
*/
public double getRadius() {
return this.radius;
}
/**
* Sets the radius of the pie chart and sends a change event to all
* registered listeners.
*
* @param radius the radius.
*/
public void setRadius(double radius) {
this.radius = radius;
fireChangeEvent(true);
}
/**
* Returns the depth of the pie (the default value is 2.0).
*
* @return The depth of the pie.
*/
public double getDepth() {
return this.depth;
}
/**
* Sets the depth of the pie chart and sends a change event to all
* registered listeners.
*
* @param depth the depth.
*/
public void setDepth(double depth) {
this.depth = depth;
fireChangeEvent(true);
}
/**
* Returns the color source for section colors.
*
* @return The color source (never {@code null}).
*/
public ColorSource getSectionColorSource() {
return this.sectionColorSource;
}
/**
* Sets the color source and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param source the color source ({@code null} not permitted).
*/
public void setSectionColorSource(ColorSource source) {
Args.nullNotPermitted(source, "source");
this.sectionColorSource = source;
fireChangeEvent(true);
}
/**
* Sets a new color source for the plot using the specified colors and
* sends a {@link Plot3DChangeEvent} to all registered listeners. This
* is a convenience method that is equivalent to
* {@code setSectionColorSource(new StandardColorSource(colors))}.
*
* @param colors one or more colors ({@code null} not permitted).
*
* @since 1.2
*/
public void setSectionColors(Color... colors) {
setSectionColorSource(new StandardColorSource(colors));
}
/**
* Returns the object that creates labels for each section of the pie
* chart.
*
* @return The section label generator (never {@code null}).
*
* @since 1.2
*/
public PieLabelGenerator getSectionLabelGenerator() {
return this.sectionLabelGenerator;
}
/**
* Sets the object that creates labels for each section of the pie chart,
* and sends a {@link Plot3DChangeEvent} to all registered listeners.
*
* @param generator the generator ({@code null} not permitted).
*
* @since 1.2
*/
public void setSectionLabelGenerator(PieLabelGenerator generator) {
Args.nullNotPermitted(generator, "generator");
this.sectionLabelGenerator = generator;
fireChangeEvent(false);
}
/**
* Returns the font source that is used to determine the font to use for
* the section labels.
*
* @return The font source for the section labels (never {@code null}).
*/
public FontSource getSectionLabelFontSource() {
return this.sectionLabelFontSource;
}
/**
* Sets the font source and sends a {@link Plot3DChangeEvent} to all
* registered listeners.
*
* @param source the source ({@code null} not permitted).
*/
public void setSectionLabelFontSource(FontSource source) {
Args.nullNotPermitted(source, "source");
this.sectionLabelFontSource = source;
fireChangeEvent(false);
}
/**
* Returns the color source for section labels. The default value is
* an instance of {@link StandardColorSource} that always returns
* {@code Color.BLACK}.
*
* @return The color source (never {@code null}).
*
* @see #setSectionLabelColorSource(ColorSource)
*/
public ColorSource getSectionLabelColorSource() {
return this.sectionLabelColorSource;
}
/**
* Sets the color source for the section labels and sends a
* {@link Plot3DChangeEvent} to all registered listeners.
*
* @param source the color source.
*
* @see #getSectionLabelColorSource()
*/
public void setSectionLabelColorSource(ColorSource source) {
Args.nullNotPermitted(source, "source");
this.sectionLabelColorSource = source;
fireChangeEvent(false);
}
/**
* Returns the object that creates legend labels for each section of the pie
* chart.
*
* @return The legend label generator (never {@code null}).
*
* @since 1.2
*/
public PieLabelGenerator getLegendLabelGenerator() {
return this.legendLabelGenerator;
}
/**
* Sets the object that creates legend labels for each section of the pie
* chart, and sends a {@link Plot3DChangeEvent} to all registered
* listeners.
*
* @param generator the generator ({@code null} not permitted).
*
* @since 1.2
*/
public void setLegendLabelGenerator(PieLabelGenerator generator) {
Args.nullNotPermitted(generator, "generator");
this.legendLabelGenerator = generator;
fireChangeEvent(false);
}
/**
* Returns the tool tip generator.
*
* @return The tool tip generator (possibly {@code null}).
*
* @since 1.3
*/
public PieLabelGenerator getToolTipGenerator() {
return this.toolTipGenerator;
}
/**
* Sets the tool tip generator and sends a change event to all registered
* listeners.
*
* @param generator the generator ({@code null} permitted).
*
* @since 1.3
*/
public void setToolTipGenerator(PieLabelGenerator generator) {
this.toolTipGenerator = generator;
fireChangeEvent(false);
}
/**
* Returns the dimensions for the plot. For the pie chart, it is more
* natural to specify the dimensions in terms of a radius and a depth, so
* we use those values to calculate the dimensions here.
*
* @return The dimensions for the plot.
*/
@Override
public Dimension3D getDimensions() {
return new Dimension3D(this.radius * 2, this.depth, this.radius * 2);
}
/**
* Returns the number of segments used when composing the 3D objects
* representing the pie chart. The default value is {@code 40}.
*
* @return The number of segments used to compose the pie chart.
*/
public int getSegmentCount() {
return this.segments;
}
/**
* Sets the number of segments used when composing the pie chart and
* sends a {@link Plot3DChangeEvent} to all registered listeners. A higher
* number will result in a more rounded pie chart, but will take longer
* to render.
*
* @param count the count.
*/
public void setSegmentCount(int count) {
this.segments = count;
fireChangeEvent(true);
}
/**
* 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.
*/
@Override @SuppressWarnings("unchecked")
public List getLegendInfo() {
List result = new ArrayList<>();
for (Comparable key : (List>)
this.dataset.getKeys()) {
String label = this.legendLabelGenerator.generateLabel(dataset,
key);
LegendItemInfo info = new StandardLegendItemInfo(key,
label, this.sectionColorSource.getColor(key));
result.add(info);
}
return result;
}
/**
* Adds 3D objects representing the current data for the plot to the
* specified world. After the world has been populated (or constructed) in
* this way, it is ready for rendering. This method is called by the
* {@link Chart3D} class, you won't normally call it directly.
*
* @param world the world ({@code null} not permitted).
* @param xOffset the x-offset.
* @param yOffset the y-offset.
* @param zOffset the z-offset.
*/
@Override
@SuppressWarnings("unchecked")
public void compose(World world, double xOffset, double yOffset,
double zOffset) {
double total = DataUtils.total(this.dataset);
double r = 0.0;
int count = this.dataset.getItemCount();
for (int i = 0; i < count; i++) {
Comparable key = this.dataset.getKey(i);
Number n = (Number) this.dataset.getValue(i);
if (n != null) {
double angle = Math.PI * 2 * (n.doubleValue() / total);
Color c = this.sectionColorSource.getColor(
this.dataset.getKey(i));
Object3D segment = Object3D.createPieSegment(this.radius, 0.0,
yOffset, this.depth, r, r + angle,
Math.PI / this.segments, c);
segment.setProperty(Object3D.ITEM_KEY,
new KeyedValuesItemKey(key));
world.add(segment);
r = r + angle;
}
}
}
/**
* Returns a list of label faces for the plot. These are non-visible
* objects added to the 3D model of the pie chart to track the positions
* for labels (which are added after the plot is projected and rendered).
*
* NOTE: This method is public so that it can be called by the
* {@link Chart3D} class - you won't normally call it directly.
*
* @param xOffset the x-offset.
* @param yOffset the y-offset.
* @param zOffset the z-offset.
*
* @return A list of label faces.
*/
public List getLabelFaces(double xOffset, double yOffset,
double zOffset) {
double total = DataUtils.total(this.dataset);
List result = new ArrayList<>();
// this adds the centre points
result.add(new Dot3D(0.0f, 0.0f, 0.0f, Color.RED));
result.add(new Dot3D(0.0f, (float) yOffset, 0.0f, Color.RED));
double r = 0.0;
int count = this.dataset.getItemCount();
for (int i = 0; i < count; i++) {
Number n = (Number) this.dataset.getValue(i);
double angle = 0.0;
if (n != null) {
angle = Math.PI * 2 * (n.doubleValue() / total);
}
result.addAll(Object3D.createPieLabelMarkers(this.radius * 1.2,
0.0, yOffset - this.depth * 0.05, this.depth * 1.1, r,
r + angle));
r = r + angle;
}
return result;
}
@Override
public String generateToolTipText(ItemKey itemKey) {
if (!(itemKey instanceof KeyedValuesItemKey)) {
throw new IllegalArgumentException(
"The itemKey must be a ValuesItemKey instance.");
}
KeyedValuesItemKey vik = (KeyedValuesItemKey) itemKey;
return this.toolTipGenerator.generateLabel(this.dataset, vik.getKey());
}
/**
* Receives a visitor. 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) {
visitor.visit(this);
}
/**
* Tests this plot for equality with an arbitrary object. Note that the
* plot's dataset is NOT considered in the equality test.
*
* @param obj the object ({@code null} not permitted).
*
* @return A boolean.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof PiePlot3D)) {
return false;
}
PiePlot3D that = (PiePlot3D) obj;
if (this.radius != that.radius) {
return false;
}
if (this.depth != that.depth) {
return false;
}
if (!this.sectionColorSource.equals(that.sectionColorSource)) {
return false;
}
if (!this.sectionLabelGenerator.equals(that.sectionLabelGenerator)) {
return false;
}
if (!this.sectionLabelFontSource.equals(that.sectionLabelFontSource)) {
return false;
}
if (!this.sectionLabelColorSource.equals(
that.sectionLabelColorSource)) {
return false;
}
if (!this.legendLabelGenerator.equals(that.legendLabelGenerator)) {
return false;
}
if (!this.toolTipGenerator.equals(that.toolTipGenerator)) {
return false;
}
if (this.segments != that.segments) {
return false;
}
return super.equals(obj);
}
@Override
public int hashCode() {
int hash = 5;
hash = 97 * hash + (int) (Double.doubleToLongBits(this.radius)
^ (Double.doubleToLongBits(this.radius) >>> 32));
hash = 97 * hash + (int) (Double.doubleToLongBits(this.depth)
^ (Double.doubleToLongBits(this.depth) >>> 32));
hash = 97 * hash + this.sectionColorSource.hashCode();
hash = 97 * hash + this.sectionLabelGenerator.hashCode();
hash = 97 * hash + this.sectionLabelFontSource.hashCode();
hash = 97 * hash + this.sectionLabelColorSource.hashCode();
hash = 97 * hash + this.segments;
return hash;
}
}