org.jfree.chart.axis.Axis Maven / Gradle / Ivy
Show all versions of jfreechart Show documentation
/* ===========================================================
* JFreeChart : a free chart library for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2000-present, by David Gilbert 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.]
*
* ---------
* Axis.java
* ---------
* (C) Copyright 2000-present, by David Gilbert and Contributors.
*
* Original Author: David Gilbert;
* Contributor(s): Bill Kelemen;
* Nicolas Brodu;
* Peter Kolb (patches 1934255 and 2603321);
* Andrew Mickish (patch 1870189);
*
*/
package org.jfree.chart.axis;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.text.AttributedString;
import java.util.Arrays;
import java.util.EventListener;
import java.util.List;
import java.util.Objects;
import javax.swing.event.EventListenerList;
import org.jfree.chart.entity.AxisEntity;
import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.event.AxisChangeEvent;
import org.jfree.chart.event.AxisChangeListener;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.chart.text.AttributedStringUtils;
import org.jfree.chart.text.TextUtils;
import org.jfree.chart.ui.RectangleEdge;
import org.jfree.chart.ui.RectangleInsets;
import org.jfree.chart.ui.TextAnchor;
import org.jfree.chart.util.AttrStringUtils;
import org.jfree.chart.util.PaintUtils;
import org.jfree.chart.util.Args;
import org.jfree.chart.util.SerialUtils;
/**
* The base class for all axes in JFreeChart. Subclasses are divided into
* those that display values ({@link ValueAxis}) and those that display
* categories ({@link CategoryAxis}).
*/
public abstract class Axis implements Cloneable, Serializable {
/** For serialization. */
private static final long serialVersionUID = 7719289504573298271L;
/** The default axis visibility ({@code true}). */
public static final boolean DEFAULT_AXIS_VISIBLE = true;
/** The default axis label font ({@code Font("SansSerif", Font.PLAIN, 12)}). */
public static final Font DEFAULT_AXIS_LABEL_FONT = new Font(
"SansSerif", Font.PLAIN, 12);
/** The default axis label paint ({@code Color.BLACK}). */
public static final Paint DEFAULT_AXIS_LABEL_PAINT = Color.BLACK;
/** The default axis label insets ({@code RectangleInsets(3.0, 3.0, 3.0, 3.0)}). */
public static final RectangleInsets DEFAULT_AXIS_LABEL_INSETS
= new RectangleInsets(3.0, 3.0, 3.0, 3.0);
/** The default axis line paint ({@code Color.GRAY}). */
public static final Paint DEFAULT_AXIS_LINE_PAINT = Color.GRAY;
/** The default axis line stroke ({@code BasicStroke(0.5f)}). */
public static final Stroke DEFAULT_AXIS_LINE_STROKE = new BasicStroke(0.5f);
/** The default tick labels visibility ({@code true}). */
public static final boolean DEFAULT_TICK_LABELS_VISIBLE = true;
/** The default tick label font ({@code Font("SansSerif", Font.PLAIN, 10)}). */
public static final Font DEFAULT_TICK_LABEL_FONT = new Font("SansSerif",
Font.PLAIN, 10);
/** The default tick label paint ({@code Color.BLACK}). */
public static final Paint DEFAULT_TICK_LABEL_PAINT = Color.BLACK;
/** The default tick label insets ({@code RectangleInsets(2.0, 4.0, 2.0, 4.0)}). */
public static final RectangleInsets DEFAULT_TICK_LABEL_INSETS
= new RectangleInsets(2.0, 4.0, 2.0, 4.0);
/** The default tick marks visible ({@code true}). */
public static final boolean DEFAULT_TICK_MARKS_VISIBLE = true;
/** The default tick stroke ({@code BasicStroke(0.5f)}). */
public static final Stroke DEFAULT_TICK_MARK_STROKE = new BasicStroke(0.5f);
/** The default tick paint ({@code Color.GRAY}). */
public static final Paint DEFAULT_TICK_MARK_PAINT = Color.GRAY;
/** The default tick mark inside length ({@code 0.0f}). */
public static final float DEFAULT_TICK_MARK_INSIDE_LENGTH = 0.0f;
/** The default tick mark outside length ({@code 2.0f}). */
public static final float DEFAULT_TICK_MARK_OUTSIDE_LENGTH = 2.0f;
/** A flag indicating whether or not the axis is visible. */
private boolean visible;
/** The label for the axis. */
private String label;
/**
* An attributed label for the axis (overrides label if non-null).
* We have to use this override method to preserve the API compatibility.
*/
private transient AttributedString attributedLabel;
/** The font for displaying the axis label. */
private Font labelFont;
/** The paint for drawing the axis label. */
private transient Paint labelPaint;
/** The insets for the axis label. */
private RectangleInsets labelInsets;
/** The label angle. */
private double labelAngle;
/** The axis label location (new in 1.0.16). */
private AxisLabelLocation labelLocation;
/** A flag that controls whether or not the axis line is visible. */
private boolean axisLineVisible;
/** The stroke used for the axis line. */
private transient Stroke axisLineStroke;
/** The paint used for the axis line. */
private transient Paint axisLinePaint;
/**
* A flag that indicates whether or not tick labels are visible for the
* axis.
*/
private boolean tickLabelsVisible;
/** The font used to display the tick labels. */
private Font tickLabelFont;
/** The color used to display the tick labels. */
private transient Paint tickLabelPaint;
/** The blank space around each tick label. */
private RectangleInsets tickLabelInsets;
/**
* A flag that indicates whether or not major tick marks are visible for
* the axis.
*/
private boolean tickMarksVisible;
/**
* The length of the major tick mark inside the data area (zero
* permitted).
*/
private float tickMarkInsideLength;
/**
* The length of the major tick mark outside the data area (zero
* permitted).
*/
private float tickMarkOutsideLength;
/**
* A flag that indicates whether or not minor tick marks are visible for the
* axis.
*/
private boolean minorTickMarksVisible;
/**
* The length of the minor tick mark inside the data area (zero permitted).
*/
private float minorTickMarkInsideLength;
/**
* The length of the minor tick mark outside the data area (zero permitted).
*/
private float minorTickMarkOutsideLength;
/** The stroke used to draw tick marks. */
private transient Stroke tickMarkStroke;
/** The paint used to draw tick marks. */
private transient Paint tickMarkPaint;
/** The fixed (horizontal or vertical) dimension for the axis. */
private double fixedDimension;
/**
* A reference back to the plot that the axis is assigned to (can be
* {@code null}).
*/
private transient Plot plot;
/** Storage for registered listeners. */
private transient EventListenerList listenerList;
/**
* Constructs an axis with the specific label and default values for other
* attributes.
*
* @param label the axis label ({@code null} permitted).
*/
protected Axis(String label) {
this.label = label;
this.visible = DEFAULT_AXIS_VISIBLE;
this.labelFont = DEFAULT_AXIS_LABEL_FONT;
this.labelPaint = DEFAULT_AXIS_LABEL_PAINT;
this.labelInsets = DEFAULT_AXIS_LABEL_INSETS;
this.labelAngle = 0.0;
this.labelLocation = AxisLabelLocation.MIDDLE;
this.axisLineVisible = true;
this.axisLinePaint = DEFAULT_AXIS_LINE_PAINT;
this.axisLineStroke = DEFAULT_AXIS_LINE_STROKE;
this.tickLabelsVisible = DEFAULT_TICK_LABELS_VISIBLE;
this.tickLabelFont = DEFAULT_TICK_LABEL_FONT;
this.tickLabelPaint = DEFAULT_TICK_LABEL_PAINT;
this.tickLabelInsets = DEFAULT_TICK_LABEL_INSETS;
this.tickMarksVisible = DEFAULT_TICK_MARKS_VISIBLE;
this.tickMarkStroke = DEFAULT_TICK_MARK_STROKE;
this.tickMarkPaint = DEFAULT_TICK_MARK_PAINT;
this.tickMarkInsideLength = DEFAULT_TICK_MARK_INSIDE_LENGTH;
this.tickMarkOutsideLength = DEFAULT_TICK_MARK_OUTSIDE_LENGTH;
this.minorTickMarksVisible = false;
this.minorTickMarkInsideLength = 0.0f;
this.minorTickMarkOutsideLength = 2.0f;
this.plot = null;
this.listenerList = new EventListenerList();
}
/**
* Returns {@code true} if the axis is visible, and
* {@code false} otherwise.
*
* @return A boolean.
*
* @see #setVisible(boolean)
*/
public boolean isVisible() {
return this.visible;
}
/**
* Sets a flag that controls whether or not the axis is visible and sends
* an {@link AxisChangeEvent} to all registered listeners.
*
* @param flag the flag.
*
* @see #isVisible()
*/
public void setVisible(boolean flag) {
if (flag != this.visible) {
this.visible = flag;
fireChangeEvent();
}
}
/**
* Returns the label for the axis.
*
* @return The label for the axis ({@code null} possible).
*
* @see #getLabelFont()
* @see #getLabelPaint()
* @see #setLabel(String)
*/
public String getLabel() {
return this.label;
}
/**
* Sets the label for the axis and sends an {@link AxisChangeEvent} to all
* registered listeners.
*
* @param label the new label ({@code null} permitted).
*
* @see #getLabel()
* @see #setLabelFont(Font)
* @see #setLabelPaint(Paint)
*/
public void setLabel(String label) {
this.label = label;
fireChangeEvent();
}
/**
* Returns the attributed label (the returned value is a copy, so
* modifying it will not impact the state of the axis). The default value
* is {@code null}.
*
* @return The attributed label (possibly {@code null}).
*/
public AttributedString getAttributedLabel() {
if (this.attributedLabel != null) {
return new AttributedString(this.attributedLabel.getIterator());
} else {
return null;
}
}
/**
* Sets the attributed label for the axis and sends an
* {@link AxisChangeEvent} to all registered listeners. This is a
* convenience method that converts the string into an
* {@code AttributedString} using the current font attributes.
*
* @param label the label ({@code null} permitted).
*/
public void setAttributedLabel(String label) {
setAttributedLabel(createAttributedLabel(label));
}
/**
* Sets the attributed label for the axis and sends an
* {@link AxisChangeEvent} to all registered listeners.
*
* @param label the label ({@code null} permitted).
*/
public void setAttributedLabel(AttributedString label) {
if (label != null) {
this.attributedLabel = new AttributedString(label.getIterator());
} else {
this.attributedLabel = null;
}
fireChangeEvent();
}
/**
* Creates and returns an {@code AttributedString} with the specified
* text and the labelFont and labelPaint applied as attributes.
*
* @param label the label ({@code null} permitted).
*
* @return An attributed string or {@code null}.
*/
public AttributedString createAttributedLabel(String label) {
if (label == null) {
return null;
}
AttributedString s = new AttributedString(label);
s.addAttributes(this.labelFont.getAttributes(), 0, label.length());
return s;
}
/**
* Returns the font for the axis label.
*
* @return The font (never {@code null}).
*
* @see #setLabelFont(Font)
*/
public Font getLabelFont() {
return this.labelFont;
}
/**
* Sets the font for the axis label and sends an {@link AxisChangeEvent}
* to all registered listeners.
*
* @param font the font ({@code null} not permitted).
*
* @see #getLabelFont()
*/
public void setLabelFont(Font font) {
Args.nullNotPermitted(font, "font");
if (!this.labelFont.equals(font)) {
this.labelFont = font;
fireChangeEvent();
}
}
/**
* Returns the color/shade used to draw the axis label.
*
* @return The paint (never {@code null}).
*
* @see #setLabelPaint(Paint)
*/
public Paint getLabelPaint() {
return this.labelPaint;
}
/**
* Sets the paint used to draw the axis label and sends an
* {@link AxisChangeEvent} to all registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*
* @see #getLabelPaint()
*/
public void setLabelPaint(Paint paint) {
Args.nullNotPermitted(paint, "paint");
this.labelPaint = paint;
fireChangeEvent();
}
/**
* Returns the insets for the label (that is, the amount of blank space
* that should be left around the label).
*
* @return The label insets (never {@code null}).
*
* @see #setLabelInsets(RectangleInsets)
*/
public RectangleInsets getLabelInsets() {
return this.labelInsets;
}
/**
* Sets the insets for the axis label, and sends an {@link AxisChangeEvent}
* to all registered listeners.
*
* @param insets the insets ({@code null} not permitted).
*
* @see #getLabelInsets()
*/
public void setLabelInsets(RectangleInsets insets) {
setLabelInsets(insets, true);
}
/**
* Sets the insets for the axis label, and sends an {@link AxisChangeEvent}
* to all registered listeners.
*
* @param insets the insets ({@code null} not permitted).
* @param notify notify listeners?
*/
public void setLabelInsets(RectangleInsets insets, boolean notify) {
Args.nullNotPermitted(insets, "insets");
if (!insets.equals(this.labelInsets)) {
this.labelInsets = insets;
if (notify) {
fireChangeEvent();
}
}
}
/**
* Returns the angle of the axis label.
*
* @return The angle (in radians).
*
* @see #setLabelAngle(double)
*/
public double getLabelAngle() {
return this.labelAngle;
}
/**
* Sets the angle for the label and sends an {@link AxisChangeEvent} to all
* registered listeners.
*
* @param angle the angle (in radians).
*
* @see #getLabelAngle()
*/
public void setLabelAngle(double angle) {
this.labelAngle = angle;
fireChangeEvent();
}
/**
* Returns the location of the axis label. The default is
* {@link AxisLabelLocation#MIDDLE}.
*
* @return The location of the axis label (never {@code null}).
*/
public AxisLabelLocation getLabelLocation() {
return this.labelLocation;
}
/**
* Sets the axis label location and sends an {@link AxisChangeEvent} to
* all registered listeners.
*
* @param location the new location ({@code null} not permitted).
*/
public void setLabelLocation(AxisLabelLocation location) {
Args.nullNotPermitted(location, "location");
this.labelLocation = location;
fireChangeEvent();
}
/**
* A flag that controls whether or not the axis line is drawn.
*
* @return A boolean.
*
* @see #getAxisLinePaint()
* @see #getAxisLineStroke()
* @see #setAxisLineVisible(boolean)
*/
public boolean isAxisLineVisible() {
return this.axisLineVisible;
}
/**
* Sets a flag that controls whether or not the axis line is visible and
* sends an {@link AxisChangeEvent} to all registered listeners.
*
* @param visible the flag.
*
* @see #isAxisLineVisible()
* @see #setAxisLinePaint(Paint)
* @see #setAxisLineStroke(Stroke)
*/
public void setAxisLineVisible(boolean visible) {
this.axisLineVisible = visible;
fireChangeEvent();
}
/**
* Returns the paint used to draw the axis line.
*
* @return The paint (never {@code null}).
*
* @see #setAxisLinePaint(Paint)
*/
public Paint getAxisLinePaint() {
return this.axisLinePaint;
}
/**
* Sets the paint used to draw the axis line and sends an
* {@link AxisChangeEvent} to all registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*
* @see #getAxisLinePaint()
*/
public void setAxisLinePaint(Paint paint) {
Args.nullNotPermitted(paint, "paint");
this.axisLinePaint = paint;
fireChangeEvent();
}
/**
* Returns the stroke used to draw the axis line.
*
* @return The stroke (never {@code null}).
*
* @see #setAxisLineStroke(Stroke)
*/
public Stroke getAxisLineStroke() {
return this.axisLineStroke;
}
/**
* Sets the stroke used to draw the axis line and sends an
* {@link AxisChangeEvent} to all registered listeners.
*
* @param stroke the stroke ({@code null} not permitted).
*
* @see #getAxisLineStroke()
*/
public void setAxisLineStroke(Stroke stroke) {
Args.nullNotPermitted(stroke, "stroke");
this.axisLineStroke = stroke;
fireChangeEvent();
}
/**
* Returns a flag indicating whether or not the tick labels are visible.
*
* @return The flag.
*
* @see #getTickLabelFont()
* @see #getTickLabelPaint()
* @see #setTickLabelsVisible(boolean)
*/
public boolean isTickLabelsVisible() {
return this.tickLabelsVisible;
}
/**
* Sets the flag that determines whether or not the tick labels are
* visible and sends an {@link AxisChangeEvent} to all registered
* listeners.
*
* @param flag the flag.
*
* @see #isTickLabelsVisible()
* @see #setTickLabelFont(Font)
* @see #setTickLabelPaint(Paint)
*/
public void setTickLabelsVisible(boolean flag) {
if (flag != this.tickLabelsVisible) {
this.tickLabelsVisible = flag;
fireChangeEvent();
}
}
/**
* Returns the flag that indicates whether or not the minor tick marks are
* showing.
*
* @return The flag that indicates whether or not the minor tick marks are
* showing.
*
* @see #setMinorTickMarksVisible(boolean)
*/
public boolean isMinorTickMarksVisible() {
return this.minorTickMarksVisible;
}
/**
* Sets the flag that indicates whether or not the minor tick marks are
* showing and sends an {@link AxisChangeEvent} to all registered
* listeners.
*
* @param flag the flag.
*
* @see #isMinorTickMarksVisible()
*/
public void setMinorTickMarksVisible(boolean flag) {
if (flag != this.minorTickMarksVisible) {
this.minorTickMarksVisible = flag;
fireChangeEvent();
}
}
/**
* Returns the font used for the tick labels (if showing).
*
* @return The font (never {@code null}).
*
* @see #setTickLabelFont(Font)
*/
public Font getTickLabelFont() {
return this.tickLabelFont;
}
/**
* Sets the font for the tick labels and sends an {@link AxisChangeEvent}
* to all registered listeners.
*
* @param font the font ({@code null} not allowed).
*
* @see #getTickLabelFont()
*/
public void setTickLabelFont(Font font) {
Args.nullNotPermitted(font, "font");
if (!this.tickLabelFont.equals(font)) {
this.tickLabelFont = font;
fireChangeEvent();
}
}
/**
* Returns the color/shade used for the tick labels.
*
* @return The paint used for the tick labels.
*
* @see #setTickLabelPaint(Paint)
*/
public Paint getTickLabelPaint() {
return this.tickLabelPaint;
}
/**
* Sets the paint used to draw tick labels (if they are showing) and
* sends an {@link AxisChangeEvent} to all registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*
* @see #getTickLabelPaint()
*/
public void setTickLabelPaint(Paint paint) {
Args.nullNotPermitted(paint, "paint");
this.tickLabelPaint = paint;
fireChangeEvent();
}
/**
* Returns the insets for the tick labels.
*
* @return The insets (never {@code null}).
*
* @see #setTickLabelInsets(RectangleInsets)
*/
public RectangleInsets getTickLabelInsets() {
return this.tickLabelInsets;
}
/**
* Sets the insets for the tick labels and sends an {@link AxisChangeEvent}
* to all registered listeners.
*
* @param insets the insets ({@code null} not permitted).
*
* @see #getTickLabelInsets()
*/
public void setTickLabelInsets(RectangleInsets insets) {
Args.nullNotPermitted(insets, "insets");
if (!this.tickLabelInsets.equals(insets)) {
this.tickLabelInsets = insets;
fireChangeEvent();
}
}
/**
* Returns the flag that indicates whether or not the tick marks are
* showing.
*
* @return The flag that indicates whether or not the tick marks are
* showing.
*
* @see #setTickMarksVisible(boolean)
*/
public boolean isTickMarksVisible() {
return this.tickMarksVisible;
}
/**
* Sets the flag that indicates whether or not the tick marks are showing
* and sends an {@link AxisChangeEvent} to all registered listeners.
*
* @param flag the flag.
*
* @see #isTickMarksVisible()
*/
public void setTickMarksVisible(boolean flag) {
if (flag != this.tickMarksVisible) {
this.tickMarksVisible = flag;
fireChangeEvent();
}
}
/**
* Returns the inside length of the tick marks.
*
* @return The length.
*
* @see #getTickMarkOutsideLength()
* @see #setTickMarkInsideLength(float)
*/
public float getTickMarkInsideLength() {
return this.tickMarkInsideLength;
}
/**
* Sets the inside length of the tick marks and sends
* an {@link AxisChangeEvent} to all registered listeners.
*
* @param length the new length.
*
* @see #getTickMarkInsideLength()
*/
public void setTickMarkInsideLength(float length) {
this.tickMarkInsideLength = length;
fireChangeEvent();
}
/**
* Returns the outside length of the tick marks.
*
* @return The length.
*
* @see #getTickMarkInsideLength()
* @see #setTickMarkOutsideLength(float)
*/
public float getTickMarkOutsideLength() {
return this.tickMarkOutsideLength;
}
/**
* Sets the outside length of the tick marks and sends
* an {@link AxisChangeEvent} to all registered listeners.
*
* @param length the new length.
*
* @see #getTickMarkInsideLength()
*/
public void setTickMarkOutsideLength(float length) {
this.tickMarkOutsideLength = length;
fireChangeEvent();
}
/**
* Returns the stroke used to draw tick marks.
*
* @return The stroke (never {@code null}).
*
* @see #setTickMarkStroke(Stroke)
*/
public Stroke getTickMarkStroke() {
return this.tickMarkStroke;
}
/**
* Sets the stroke used to draw tick marks and sends
* an {@link AxisChangeEvent} to all registered listeners.
*
* @param stroke the stroke ({@code null} not permitted).
*
* @see #getTickMarkStroke()
*/
public void setTickMarkStroke(Stroke stroke) {
Args.nullNotPermitted(stroke, "stroke");
if (!this.tickMarkStroke.equals(stroke)) {
this.tickMarkStroke = stroke;
fireChangeEvent();
}
}
/**
* Returns the paint used to draw tick marks (if they are showing).
*
* @return The paint (never {@code null}).
*
* @see #setTickMarkPaint(Paint)
*/
public Paint getTickMarkPaint() {
return this.tickMarkPaint;
}
/**
* Sets the paint used to draw tick marks and sends an
* {@link AxisChangeEvent} to all registered listeners.
*
* @param paint the paint ({@code null} not permitted).
*
* @see #getTickMarkPaint()
*/
public void setTickMarkPaint(Paint paint) {
Args.nullNotPermitted(paint, "paint");
this.tickMarkPaint = paint;
fireChangeEvent();
}
/**
* Returns the inside length of the minor tick marks.
*
* @return The length.
*
* @see #getMinorTickMarkOutsideLength()
* @see #setMinorTickMarkInsideLength(float)
*/
public float getMinorTickMarkInsideLength() {
return this.minorTickMarkInsideLength;
}
/**
* Sets the inside length of the minor tick marks and sends
* an {@link AxisChangeEvent} to all registered listeners.
*
* @param length the new length.
*
* @see #getMinorTickMarkInsideLength()
*/
public void setMinorTickMarkInsideLength(float length) {
this.minorTickMarkInsideLength = length;
fireChangeEvent();
}
/**
* Returns the outside length of the minor tick marks.
*
* @return The length.
*
* @see #getMinorTickMarkInsideLength()
* @see #setMinorTickMarkOutsideLength(float)
*/
public float getMinorTickMarkOutsideLength() {
return this.minorTickMarkOutsideLength;
}
/**
* Sets the outside length of the minor tick marks and sends
* an {@link AxisChangeEvent} to all registered listeners.
*
* @param length the new length.
*
* @see #getMinorTickMarkInsideLength()
*/
public void setMinorTickMarkOutsideLength(float length) {
this.minorTickMarkOutsideLength = length;
fireChangeEvent();
}
/**
* Returns the plot that the axis is assigned to. This method will return
* {@code null} if the axis is not currently assigned to a plot.
*
* @return The plot that the axis is assigned to (possibly {@code null}).
*
* @see #setPlot(Plot)
*/
public Plot getPlot() {
return this.plot;
}
/**
* Sets a reference to the plot that the axis is assigned to.
*
* This method is used internally, you shouldn't need to call it yourself.
*
* @param plot the plot.
*
* @see #getPlot()
*/
public void setPlot(Plot plot) {
this.plot = plot;
configure();
}
/**
* Returns the fixed dimension for the axis.
*
* @return The fixed dimension.
*
* @see #setFixedDimension(double)
*/
public double getFixedDimension() {
return this.fixedDimension;
}
/**
* Sets the fixed dimension for the axis.
*
* This is used when combining more than one plot on a chart. In this case,
* there may be several axes that need to have the same height or width so
* that they are aligned. This method is used to fix a dimension for the
* axis (the context determines whether the dimension is horizontal or
* vertical).
*
* @param dimension the fixed dimension.
*
* @see #getFixedDimension()
*/
public void setFixedDimension(double dimension) {
this.fixedDimension = dimension;
}
/**
* Configures the axis to work with the current plot. Override this method
* to perform any special processing (such as auto-rescaling).
*/
public abstract void configure();
/**
* Estimates the space (height or width) required to draw the axis.
*
* @param g2 the graphics device.
* @param plot the plot that the axis belongs to.
* @param plotArea the area within which the plot (including axes) should
* be drawn.
* @param edge the axis location.
* @param space space already reserved.
*
* @return The space required to draw the axis (including pre-reserved
* space).
*/
public abstract AxisSpace reserveSpace(Graphics2D g2, Plot plot,
Rectangle2D plotArea,
RectangleEdge edge,
AxisSpace space);
/**
* Draws the axis on a Java 2D graphics device (such as the screen or a
* printer).
*
* @param g2 the graphics device ({@code null} not permitted).
* @param cursor the cursor location (determines where to draw the axis).
* @param plotArea the area within which the axes and plot should be drawn.
* @param dataArea the area within which the data should be drawn.
* @param edge the axis location ({@code null} not permitted).
* @param plotState collects information about the plot
* ({@code null} permitted).
*
* @return The axis state (never {@code null}).
*/
public abstract AxisState draw(Graphics2D g2, double cursor,
Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge,
PlotRenderingInfo plotState);
/**
* Calculates the positions of the ticks for the axis, storing the results
* in the tick list (ready for drawing).
*
* @param g2 the graphics device.
* @param state the axis state.
* @param dataArea the area inside the axes.
* @param edge the edge on which the axis is located.
*
* @return The list of ticks.
*/
public abstract List refreshTicks(Graphics2D g2, AxisState state,
Rectangle2D dataArea, RectangleEdge edge);
/**
* Creates an entity for the axis and adds it to the rendering info.
* If {@code plotState} is {@code null}, this means that rendering info
* is not being collected so this method simply returns without doing
* anything.
*
* @param cursor the initial cursor value.
* @param state the axis state after completion of the drawing with a
* possibly updated cursor position.
* @param dataArea the data area.
* @param edge the edge ({@code null} not permitted).
* @param plotState the PlotRenderingInfo from which a reference to the
* entity collection can be obtained ({@code null} permitted).
*/
protected void createAndAddEntity(double cursor, AxisState state,
Rectangle2D dataArea, RectangleEdge edge,
PlotRenderingInfo plotState) {
Args.nullNotPermitted(edge, "edge");
if (plotState == null || plotState.getOwner() == null) {
return; // no need to create entity if we can't save it anyways...
}
Rectangle2D hotspot = null;
if (edge.equals(RectangleEdge.TOP)) {
hotspot = new Rectangle2D.Double(dataArea.getX(),
state.getCursor(), dataArea.getWidth(),
cursor - state.getCursor());
}
else if (edge.equals(RectangleEdge.BOTTOM)) {
hotspot = new Rectangle2D.Double(dataArea.getX(), cursor,
dataArea.getWidth(), state.getCursor() - cursor);
}
else if (edge.equals(RectangleEdge.LEFT)) {
hotspot = new Rectangle2D.Double(state.getCursor(),
dataArea.getY(), cursor - state.getCursor(),
dataArea.getHeight());
}
else if (edge.equals(RectangleEdge.RIGHT)) {
hotspot = new Rectangle2D.Double(cursor, dataArea.getY(),
state.getCursor() - cursor, dataArea.getHeight());
}
EntityCollection e = plotState.getOwner().getEntityCollection();
if (e != null) {
e.add(new AxisEntity(hotspot, this));
}
}
/**
* Registers an object for notification of changes to the axis.
*
* @param listener the object that is being registered.
*
* @see #removeChangeListener(AxisChangeListener)
*/
public void addChangeListener(AxisChangeListener listener) {
this.listenerList.add(AxisChangeListener.class, listener);
}
/**
* Deregisters an object for notification of changes to the axis.
*
* @param listener the object to deregister.
*
* @see #addChangeListener(AxisChangeListener)
*/
public void removeChangeListener(AxisChangeListener listener) {
this.listenerList.remove(AxisChangeListener.class, listener);
}
/**
* Returns {@code true} if the specified object is registered with
* the dataset as a listener. Most applications won't need to call this
* method, it exists mainly for use by unit testing code.
*
* @param listener the listener.
*
* @return A boolean.
*/
public boolean hasListener(EventListener listener) {
List list = Arrays.asList(this.listenerList.getListenerList());
return list.contains(listener);
}
/**
* Notifies all registered listeners that the axis has changed.
* The AxisChangeEvent provides information about the change.
*
* @param event information about the change to the axis.
*/
protected void notifyListeners(AxisChangeEvent event) {
Object[] listeners = this.listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == AxisChangeListener.class) {
((AxisChangeListener) listeners[i + 1]).axisChanged(event);
}
}
}
/**
* Sends an {@link AxisChangeEvent} to all registered listeners.
*/
protected void fireChangeEvent() {
notifyListeners(new AxisChangeEvent(this));
}
/**
* Returns a rectangle that encloses the axis label. This is typically
* used for layout purposes (it gives the maximum dimensions of the label).
*
* @param g2 the graphics device.
* @param edge the edge of the plot area along which the axis is measuring.
*
* @return The enclosing rectangle.
*/
protected Rectangle2D getLabelEnclosure(Graphics2D g2, RectangleEdge edge) {
Rectangle2D result = new Rectangle2D.Double();
Rectangle2D bounds = null;
if (this.attributedLabel != null) {
TextLayout layout = new TextLayout(
this.attributedLabel.getIterator(),
g2.getFontRenderContext());
bounds = layout.getBounds();
} else {
String axisLabel = getLabel();
if (axisLabel != null && !axisLabel.equals("")) {
FontMetrics fm = g2.getFontMetrics(getLabelFont());
bounds = TextUtils.getTextBounds(axisLabel, g2, fm);
}
}
if (bounds != null) {
RectangleInsets insets = getLabelInsets();
bounds = insets.createOutsetRectangle(bounds);
double angle = getLabelAngle();
if (edge == RectangleEdge.LEFT || edge == RectangleEdge.RIGHT) {
angle = angle - Math.PI / 2.0;
}
double x = bounds.getCenterX();
double y = bounds.getCenterY();
AffineTransform transformer
= AffineTransform.getRotateInstance(angle, x, y);
Shape labelBounds = transformer.createTransformedShape(bounds);
result = labelBounds.getBounds2D();
}
return result;
}
/**
* Returns the x-coordinate for the point to which the axis label should
* be aligned.
*
* @param location the axis label location ({@code null} not permitted).
* @param dataArea the display area in which the data will be rendered ({@code null} not permitted).
*
* @return The x-coordinate.
*/
protected double labelLocationX(AxisLabelLocation location,
Rectangle2D dataArea) {
if (location.equals(AxisLabelLocation.HIGH_END)) {
return dataArea.getMaxX();
}
if (location.equals(AxisLabelLocation.MIDDLE)) {
return dataArea.getCenterX();
}
if (location.equals(AxisLabelLocation.LOW_END)) {
return dataArea.getMinX();
}
throw new RuntimeException("Unexpected AxisLabelLocation: " + location);
}
/**
* Returns the y-coordinate for the point to which the axis label should
* be aligned.
*
* @param location the location ({@code null} not permitted).
* @param dataArea the data area ({@code null} not permitted).
*
* @return The y-coordinate.
*/
protected double labelLocationY(AxisLabelLocation location,
Rectangle2D dataArea) {
if (location.equals(AxisLabelLocation.HIGH_END)) {
return dataArea.getMinY();
}
if (location.equals(AxisLabelLocation.MIDDLE)) {
return dataArea.getCenterY();
}
if (location.equals(AxisLabelLocation.LOW_END)) {
return dataArea.getMaxY();
}
throw new RuntimeException("Unexpected AxisLabelLocation: " + location);
}
/**
* Returns the appropriate horizontal text anchor for the specified axis
* location.
*
* @param location the location ({@code null} not permitted).
*
* @return The text anchor (never {@code null}).
*/
protected TextAnchor labelAnchorH(AxisLabelLocation location) {
if (location.equals(AxisLabelLocation.HIGH_END)) {
return TextAnchor.CENTER_RIGHT;
}
if (location.equals(AxisLabelLocation.MIDDLE)) {
return TextAnchor.CENTER;
}
if (location.equals(AxisLabelLocation.LOW_END)) {
return TextAnchor.CENTER_LEFT;
}
throw new RuntimeException("Unexpected AxisLabelLocation: " + location);
}
/**
* Returns the appropriate vertical text anchor for the specified axis
* location.
*
* @param location the location ({@code null} not permitted).
*
* @return The text anchor (never {@code null}).
*/
protected TextAnchor labelAnchorV(AxisLabelLocation location) {
if (location.equals(AxisLabelLocation.HIGH_END)) {
return TextAnchor.CENTER_RIGHT;
}
if (location.equals(AxisLabelLocation.MIDDLE)) {
return TextAnchor.CENTER;
}
if (location.equals(AxisLabelLocation.LOW_END)) {
return TextAnchor.CENTER_LEFT;
}
throw new RuntimeException("Unexpected AxisLabelLocation: " + location);
}
/**
* Draws the axis label.
*
* @param label the label text.
* @param g2 the graphics device.
* @param plotArea the plot area.
* @param dataArea the area inside the axes.
* @param edge the location of the axis.
* @param state the axis state ({@code null} not permitted).
*
* @return Information about the axis.
*/
protected AxisState drawLabel(String label, Graphics2D g2,
Rectangle2D plotArea, Rectangle2D dataArea, RectangleEdge edge,
AxisState state) {
// it is unlikely that 'state' will be null, but check anyway...
Args.nullNotPermitted(state, "state");
if ((label == null) || (label.equals(""))) {
return state;
}
Font font = getLabelFont();
RectangleInsets insets = getLabelInsets();
g2.setFont(font);
g2.setPaint(getLabelPaint());
FontMetrics fm = g2.getFontMetrics();
Rectangle2D labelBounds = TextUtils.getTextBounds(label, g2, fm);
if (edge == RectangleEdge.TOP) {
AffineTransform t = AffineTransform.getRotateInstance(
getLabelAngle(), labelBounds.getCenterX(),
labelBounds.getCenterY());
Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
labelBounds = rotatedLabelBounds.getBounds2D();
double labelx = labelLocationX(this.labelLocation, dataArea);
double labely = state.getCursor() - insets.getBottom()
- labelBounds.getHeight() / 2.0;
TextAnchor anchor = labelAnchorH(this.labelLocation);
TextUtils.drawRotatedString(label, g2, (float) labelx,
(float) labely, anchor, getLabelAngle(), TextAnchor.CENTER);
state.cursorUp(insets.getTop() + labelBounds.getHeight()
+ insets.getBottom());
}
else if (edge == RectangleEdge.BOTTOM) {
AffineTransform t = AffineTransform.getRotateInstance(
getLabelAngle(), labelBounds.getCenterX(),
labelBounds.getCenterY());
Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
labelBounds = rotatedLabelBounds.getBounds2D();
double labelx = labelLocationX(this.labelLocation, dataArea);
double labely = state.getCursor()
+ insets.getTop() + labelBounds.getHeight() / 2.0;
TextAnchor anchor = labelAnchorH(this.labelLocation);
TextUtils.drawRotatedString(label, g2, (float) labelx,
(float) labely, anchor, getLabelAngle(), TextAnchor.CENTER);
state.cursorDown(insets.getTop() + labelBounds.getHeight()
+ insets.getBottom());
}
else if (edge == RectangleEdge.LEFT) {
AffineTransform t = AffineTransform.getRotateInstance(
getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(),
labelBounds.getCenterY());
Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
labelBounds = rotatedLabelBounds.getBounds2D();
double labelx = state.getCursor()
- insets.getRight() - labelBounds.getWidth() / 2.0;
double labely = labelLocationY(this.labelLocation, dataArea);
TextAnchor anchor = labelAnchorV(this.labelLocation);
TextUtils.drawRotatedString(label, g2, (float) labelx,
(float) labely, anchor, getLabelAngle() - Math.PI / 2.0,
anchor);
state.cursorLeft(insets.getLeft() + labelBounds.getWidth()
+ insets.getRight());
}
else if (edge == RectangleEdge.RIGHT) {
AffineTransform t = AffineTransform.getRotateInstance(
getLabelAngle() + Math.PI / 2.0,
labelBounds.getCenterX(), labelBounds.getCenterY());
Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
labelBounds = rotatedLabelBounds.getBounds2D();
double labelx = state.getCursor()
+ insets.getLeft() + labelBounds.getWidth() / 2.0;
double labely = labelLocationY(this.labelLocation, dataArea);
TextAnchor anchor = labelAnchorV(this.labelLocation);
TextUtils.drawRotatedString(label, g2, (float) labelx,
(float) labely, anchor, getLabelAngle() + Math.PI / 2.0,
anchor);
state.cursorRight(insets.getLeft() + labelBounds.getWidth()
+ insets.getRight());
}
return state;
}
/**
* Draws the axis label.
*
* @param label the label text.
* @param g2 the graphics device.
* @param plotArea the plot area.
* @param dataArea the area inside the axes.
* @param edge the location of the axis.
* @param state the axis state ({@code null} not permitted).
*
* @return Information about the axis.
*/
protected AxisState drawAttributedLabel(AttributedString label,
Graphics2D g2, Rectangle2D plotArea, Rectangle2D dataArea,
RectangleEdge edge, AxisState state) {
// it is unlikely that 'state' will be null, but check anyway...
Args.nullNotPermitted(state, "state");
if (label == null) {
return state;
}
RectangleInsets insets = getLabelInsets();
g2.setFont(getLabelFont());
g2.setPaint(getLabelPaint());
TextLayout layout = new TextLayout(this.attributedLabel.getIterator(),
g2.getFontRenderContext());
Rectangle2D labelBounds = layout.getBounds();
if (edge == RectangleEdge.TOP) {
AffineTransform t = AffineTransform.getRotateInstance(
getLabelAngle(), labelBounds.getCenterX(),
labelBounds.getCenterY());
Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
labelBounds = rotatedLabelBounds.getBounds2D();
double labelx = labelLocationX(this.labelLocation, dataArea);
double labely = state.getCursor() - insets.getBottom()
- labelBounds.getHeight() / 2.0;
TextAnchor anchor = labelAnchorH(this.labelLocation);
AttrStringUtils.drawRotatedString(label, g2, (float) labelx,
(float) labely, anchor, getLabelAngle(), TextAnchor.CENTER);
state.cursorUp(insets.getTop() + labelBounds.getHeight()
+ insets.getBottom());
}
else if (edge == RectangleEdge.BOTTOM) {
AffineTransform t = AffineTransform.getRotateInstance(
getLabelAngle(), labelBounds.getCenterX(),
labelBounds.getCenterY());
Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
labelBounds = rotatedLabelBounds.getBounds2D();
double labelx = labelLocationX(this.labelLocation, dataArea);
double labely = state.getCursor()
+ insets.getTop() + labelBounds.getHeight() / 2.0;
TextAnchor anchor = labelAnchorH(this.labelLocation);
AttrStringUtils.drawRotatedString(label, g2, (float) labelx,
(float) labely, anchor, getLabelAngle(), TextAnchor.CENTER);
state.cursorDown(insets.getTop() + labelBounds.getHeight()
+ insets.getBottom());
}
else if (edge == RectangleEdge.LEFT) {
AffineTransform t = AffineTransform.getRotateInstance(
getLabelAngle() - Math.PI / 2.0, labelBounds.getCenterX(),
labelBounds.getCenterY());
Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
labelBounds = rotatedLabelBounds.getBounds2D();
double labelx = state.getCursor()
- insets.getRight() - labelBounds.getWidth() / 2.0;
double labely = labelLocationY(this.labelLocation, dataArea);
TextAnchor anchor = labelAnchorV(this.labelLocation);
AttrStringUtils.drawRotatedString(label, g2, (float) labelx,
(float) labely, anchor, getLabelAngle() - Math.PI / 2.0,
anchor);
state.cursorLeft(insets.getLeft() + labelBounds.getWidth()
+ insets.getRight());
}
else if (edge == RectangleEdge.RIGHT) {
AffineTransform t = AffineTransform.getRotateInstance(
getLabelAngle() + Math.PI / 2.0,
labelBounds.getCenterX(), labelBounds.getCenterY());
Shape rotatedLabelBounds = t.createTransformedShape(labelBounds);
labelBounds = rotatedLabelBounds.getBounds2D();
double labelx = state.getCursor()
+ insets.getLeft() + labelBounds.getWidth() / 2.0;
double labely = labelLocationY(this.labelLocation, dataArea);
TextAnchor anchor = labelAnchorV(this.labelLocation);
AttrStringUtils.drawRotatedString(label, g2, (float) labelx,
(float) labely, anchor, getLabelAngle() + Math.PI / 2.0,
anchor);
state.cursorRight(insets.getLeft() + labelBounds.getWidth()
+ insets.getRight());
}
return state;
}
/**
* Draws an axis line at the current cursor position and edge.
*
* @param g2 the graphics device.
* @param cursor the cursor position.
* @param dataArea the data area.
* @param edge the edge.
*/
protected void drawAxisLine(Graphics2D g2, double cursor,
Rectangle2D dataArea, RectangleEdge edge) {
Line2D axisLine = null;
double x = dataArea.getX();
double y = dataArea.getY();
if (edge == RectangleEdge.TOP) {
axisLine = new Line2D.Double(x, cursor, dataArea.getMaxX(), cursor);
} else if (edge == RectangleEdge.BOTTOM) {
axisLine = new Line2D.Double(x, cursor, dataArea.getMaxX(), cursor);
} else if (edge == RectangleEdge.LEFT) {
axisLine = new Line2D.Double(cursor, y, cursor, dataArea.getMaxY());
} else if (edge == RectangleEdge.RIGHT) {
axisLine = new Line2D.Double(cursor, y, cursor, dataArea.getMaxY());
}
g2.setPaint(this.axisLinePaint);
g2.setStroke(this.axisLineStroke);
Object saved = g2.getRenderingHint(RenderingHints.KEY_STROKE_CONTROL);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_NORMALIZE);
g2.draw(axisLine);
g2.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, saved);
}
/**
* Returns a clone of the axis.
*
* @return A clone.
*
* @throws CloneNotSupportedException if some component of the axis does
* not support cloning.
*/
@Override
public Object clone() throws CloneNotSupportedException {
Axis clone = (Axis) super.clone();
// It's up to the plot which clones up to restore the correct references
clone.plot = null;
clone.listenerList = new EventListenerList();
return clone;
}
/**
* Tests this axis for equality with another object.
*
* @param obj the object ({@code null} permitted).
*
* @return {@code true} or {@code false}.
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof Axis)) {
return false;
}
Axis that = (Axis) obj;
if (this.visible != that.visible) {
return false;
}
if (!Objects.equals(this.label, that.label)) {
return false;
}
if (!AttributedStringUtils.equal(this.attributedLabel,
that.attributedLabel)) {
return false;
}
if (!Objects.equals(this.labelFont, that.labelFont)) {
return false;
}
if (!PaintUtils.equal(this.labelPaint, that.labelPaint)) {
return false;
}
if (!Objects.equals(this.labelInsets, that.labelInsets)) {
return false;
}
if (this.labelAngle != that.labelAngle) {
return false;
}
if (!this.labelLocation.equals(that.labelLocation)) {
return false;
}
if (this.axisLineVisible != that.axisLineVisible) {
return false;
}
if (!Objects.equals(this.axisLineStroke, that.axisLineStroke)) {
return false;
}
if (!PaintUtils.equal(this.axisLinePaint, that.axisLinePaint)) {
return false;
}
if (this.tickLabelsVisible != that.tickLabelsVisible) {
return false;
}
if (!Objects.equals(this.tickLabelFont, that.tickLabelFont)) {
return false;
}
if (!PaintUtils.equal(this.tickLabelPaint, that.tickLabelPaint)) {
return false;
}
if (!Objects.equals(this.tickLabelInsets, that.tickLabelInsets)) {
return false;
}
if (this.tickMarksVisible != that.tickMarksVisible) {
return false;
}
if (this.tickMarkInsideLength != that.tickMarkInsideLength) {
return false;
}
if (this.tickMarkOutsideLength != that.tickMarkOutsideLength) {
return false;
}
if (!PaintUtils.equal(this.tickMarkPaint, that.tickMarkPaint)) {
return false;
}
if (!Objects.equals(this.tickMarkStroke, that.tickMarkStroke)) {
return false;
}
if (this.minorTickMarksVisible != that.minorTickMarksVisible) {
return false;
}
if (this.minorTickMarkInsideLength != that.minorTickMarkInsideLength) {
return false;
}
if (this.minorTickMarkOutsideLength
!= that.minorTickMarkOutsideLength) {
return false;
}
if (this.fixedDimension != that.fixedDimension) {
return false;
}
return true;
}
/**
* Returns a hash code for this instance.
*
* @return A hash code.
*/
@Override
public int hashCode() {
int hash = 3;
if (this.label != null) {
hash = 83 * hash + this.label.hashCode();
}
return hash;
}
/**
* 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.writeAttributedString(this.attributedLabel, stream);
SerialUtils.writePaint(this.labelPaint, stream);
SerialUtils.writePaint(this.tickLabelPaint, stream);
SerialUtils.writeStroke(this.axisLineStroke, stream);
SerialUtils.writePaint(this.axisLinePaint, stream);
SerialUtils.writeStroke(this.tickMarkStroke, stream);
SerialUtils.writePaint(this.tickMarkPaint, 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.attributedLabel = SerialUtils.readAttributedString(stream);
this.labelPaint = SerialUtils.readPaint(stream);
this.tickLabelPaint = SerialUtils.readPaint(stream);
this.axisLineStroke = SerialUtils.readStroke(stream);
this.axisLinePaint = SerialUtils.readPaint(stream);
this.tickMarkStroke = SerialUtils.readStroke(stream);
this.tickMarkPaint = SerialUtils.readPaint(stream);
this.listenerList = new EventListenerList();
}
}