⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cyclicnumberaxis.java

📁 java图形利器
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
/* ===========================================================
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
 * USA.  
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
 * in the United States and other countries.]
 *
 * ---------------------
 * CyclicNumberAxis.java
 * ---------------------
 * (C) Copyright 2003, 2004, by Nicolas Brodu and Contributors.
 *
 * Original Author:  Nicolas Brodu;
 * Contributor(s):   David Gilbert (for Object Refinery Limited);
 *
 * $Id: CyclicNumberAxis.java,v 1.10.2.2 2005/10/25 20:37:34 mungady Exp $
 *
 * Changes
 * -------
 * 19-Nov-2003 : Initial import to JFreeChart from the JSynoptic project (NB);
 * 16-Mar-2004 : Added plotState to draw() method (DG);
 * 07-Apr-2004 : Modifed text bounds calculation (DG);
 * 21-Apr-2005 : Replaced Insets with RectangleInsets, removed redundant
 *               argument in selectAutoTickUnit() (DG);
 * 22-Apr-2005 : Renamed refreshHorizontalTicks() --> refreshTicksHorizontal
 *               (for consistency with other classes) and removed unused
 *               parameters (DG);
 * 08-Jun-2005 : Fixed equals() method to handle GradientPaint (DG);
 *
 */

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.Stroke;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.text.NumberFormat;
import java.util.List;

import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotRenderingInfo;
import org.jfree.data.Range;
import org.jfree.io.SerialUtilities;
import org.jfree.text.TextUtilities;
import org.jfree.ui.RectangleEdge;
import org.jfree.ui.TextAnchor;
import org.jfree.util.ObjectUtilities;
import org.jfree.util.PaintUtilities;

/**
This class extends NumberAxis and handles cycling.
 
Traditional representation of data in the range x0..x1
<pre>
|-------------------------|
x0                       x1
</pre> 

Here, the range bounds are at the axis extremities.
With cyclic axis, however, the time is split in 
"cycles", or "time frames", or the same duration : the period.

A cycle axis cannot by definition handle a larger interval 
than the period : <pre>x1 - x0 >= period</pre>. Thus, at most a full 
period can be represented with such an axis.

The cycle bound is the number between x0 and x1 which marks 
the beginning of new time frame:
<pre>
|---------------------|----------------------------|
x0                   cb                           x1
<---previous cycle---><-------current cycle-------->
</pre>

It is actually a multiple of the period, plus optionally 
a start offset: <pre>cb = n * period + offset</pre>

Thus, by definition, two consecutive cycle bounds 
period apart, which is precisely why it is called a 
period.

The visual representation of a cyclic axis is like that:
<pre>
|----------------------------|---------------------|
cb                         x1|x0                  cb
<-------current cycle--------><---previous cycle--->
</pre>

The cycle bound is at the axis ends, then current 
cycle is shown, then the last cycle. When using 
dynamic data, the visual effect is the current cycle 
erases the last cycle as x grows. Then, the next cycle 
bound is reached, and the process starts over, erasing 
the previous cycle.

A Cyclic item renderer is provided to do exactly this.

 */
public class CyclicNumberAxis extends NumberAxis {

    /** The default axis line stroke. */
    public static Stroke DEFAULT_ADVANCE_LINE_STROKE = new BasicStroke(1.0f);
    
    /** The default axis line paint. */
    public static final Paint DEFAULT_ADVANCE_LINE_PAINT = Color.gray;
    
    /** The offset. */
    protected double offset;
    
    /** The period.*/
    protected double period;
    
    /** ??. */
    protected boolean boundMappedToLastCycle;
    
    /** A flag that controls whether or not the advance line is visible. */
    protected boolean advanceLineVisible;

    /** The advance line stroke. */
    protected transient Stroke advanceLineStroke = DEFAULT_ADVANCE_LINE_STROKE;
    
    /** The advance line paint. */
    protected transient Paint advanceLinePaint;
    
    private transient boolean internalMarkerWhenTicksOverlap;
    private transient Tick internalMarkerCycleBoundTick;
    
    /** 
     * Creates a CycleNumberAxis with the given period.
     * 
     * @param period  the period.
     */
    public CyclicNumberAxis(double period) {
        this(period, 0.0);
    }

    /** 
     * Creates a CycleNumberAxis with the given period and offset.
     * 
     * @param period  the period.
     * @param offset  the offset.
     */
    public CyclicNumberAxis(double period, double offset) {
        this(period, offset, null);
    }

    /** 
     * Creates a named CycleNumberAxis with the given period.
     * 
     * @param period  the period.
     * @param label  the label.
     */
    public CyclicNumberAxis(double period, String label) {
        this(0, period, label);
    }
    
    /** 
     * Creates a named CycleNumberAxis with the given period and offset.
     * 
     * @param period  the period.
     * @param offset  the offset.
     * @param label  the label.
     */
    public CyclicNumberAxis(double period, double offset, String label) {
        super(label);
        this.period = period;
        this.offset = offset;
        setFixedAutoRange(period);
        this.advanceLineVisible = true;
        this.advanceLinePaint = DEFAULT_ADVANCE_LINE_PAINT;
    }
        
    /**
     * The advance line is the line drawn at the limit of the current cycle, 
     * when erasing the previous cycle. 
     * 
     * @return A boolean.
     */
    public boolean isAdvanceLineVisible() {
        return this.advanceLineVisible;
    }
    
    /**
     * The advance line is the line drawn at the limit of the current cycle, 
     * when erasing the previous cycle. 
     * 
     * @param visible  the flag.
     */
    public void setAdvanceLineVisible(boolean visible) {
        this.advanceLineVisible = visible;
    }
    
    /**
     * The advance line is the line drawn at the limit of the current cycle, 
     * when erasing the previous cycle. 
     * 
     * @return The paint (never <code>null</code>).
     */
    public Paint getAdvanceLinePaint() {
        return this.advanceLinePaint;
    }

    /**
     * The advance line is the line drawn at the limit of the current cycle, 
     * when erasing the previous cycle. 
     * 
     * @param paint  the paint (<code>null</code> not permitted).
     */
    public void setAdvanceLinePaint(Paint paint) {
        if (paint == null) {
            throw new IllegalArgumentException("Null 'paint' argument.");
        }
        this.advanceLinePaint = paint;
    }
    
    /**
     * The advance line is the line drawn at the limit of the current cycle, 
     * when erasing the previous cycle. 
     * 
     * @return The stroke (never <code>null</code>).
     */
    public Stroke getAdvanceLineStroke() {
        return this.advanceLineStroke;
    }
    /**
     * The advance line is the line drawn at the limit of the current cycle, 
     * when erasing the previous cycle. 
     * 
     * @param stroke  the stroke (<code>null</code> not permitted).
     */
    public void setAdvanceLineStroke(Stroke stroke) {
        if (stroke == null) {
            throw new IllegalArgumentException("Null 'stroke' argument.");
        }
        this.advanceLineStroke = stroke;
    }
    
    /**
     * The cycle bound can be associated either with the current or with the 
     * last cycle.  It's up to the user's choice to decide which, as this is 
     * just a convention.  By default, the cycle bound is mapped to the current
     * cycle.
     * <br>
     * Note that this has no effect on visual appearance, as the cycle bound is
     * mapped successively for both axis ends. Use this function for correct 
     * results in translateValueToJava2D. 
     *  
     * @return <code>true</code> if the cycle bound is mapped to the last 
     *         cycle, <code>false</code> if it is bound to the current cycle 
     *         (default)
     */
    public boolean isBoundMappedToLastCycle() {
        return this.boundMappedToLastCycle;
    }
    
    /**
     * The cycle bound can be associated either with the current or with the 
     * last cycle.  It's up to the user's choice to decide which, as this is 
     * just a convention. By default, the cycle bound is mapped to the current 
     * cycle. 
     * <br>
     * Note that this has no effect on visual appearance, as the cycle bound is
     * mapped successively for both axis ends. Use this function for correct 
     * results in valueToJava2D.
     *  
     * @param boundMappedToLastCycle Set it to true to map the cycle bound to 
     *        the last cycle.
     */
    public void setBoundMappedToLastCycle(boolean boundMappedToLastCycle) {
        this.boundMappedToLastCycle = boundMappedToLastCycle;
    }
    
    /**
     * Selects a tick unit when the axis is displayed horizontally.
     * 
     * @param g2  the graphics device.
     * @param drawArea  the drawing area.
     * @param dataArea  the data area.
     * @param edge  the side of the rectangle on which the axis is displayed.
     */
    protected void selectHorizontalAutoTickUnit(Graphics2D g2,
                                                Rectangle2D drawArea, 
                                                Rectangle2D dataArea,
                                                RectangleEdge edge) {

        double tickLabelWidth 
            = estimateMaximumTickLabelWidth(g2, getTickUnit());
        
        // Compute number of labels
        double n = getRange().getLength() 
                   * tickLabelWidth / dataArea.getWidth();

        setTickUnit(
            (NumberTickUnit) getStandardTickUnits().getCeilingTickUnit(n), 
            false, false
        );
        
     }

    /**
     * Selects a tick unit when the axis is displayed vertically.
     * 
     * @param g2  the graphics device.
     * @param drawArea  the drawing area.
     * @param dataArea  the data area.
     * @param edge  the side of the rectangle on which the axis is displayed.
     */
    protected void selectVerticalAutoTickUnit(Graphics2D g2,
                                                Rectangle2D drawArea, 
                                                Rectangle2D dataArea,
                                                RectangleEdge edge) {

        double tickLabelWidth 
            = estimateMaximumTickLabelWidth(g2, getTickUnit());

        // Compute number of labels
        double n = getRange().getLength() 
                   * tickLabelWidth / dataArea.getHeight();

        setTickUnit(
            (NumberTickUnit) getStandardTickUnits().getCeilingTickUnit(n), 
            false, false
        );
        
     }

    /** 
     * A special Number tick that also hold information about the cycle bound 
     * mapping for this tick.  This is especially useful for having a tick at 
     * each axis end with the cycle bound value.  See also 
     * isBoundMappedToLastCycle()
     */
    protected static class CycleBoundTick extends NumberTick {
        
        /** Map to last cycle. */
        public boolean mapToLastCycle;
        
        /**
         * Creates a new tick.
         * 
         * @param mapToLastCycle  map to last cycle?
         * @param number  the number.
         * @param label  the label.
         * @param textAnchor  the text anchor.
         * @param rotationAnchor  the rotation anchor.
         * @param angle  the rotation angle.
         */
        public CycleBoundTick(boolean mapToLastCycle, Number number, 
                              String label, TextAnchor textAnchor,
                              TextAnchor rotationAnchor, double angle) {
            super(number, label, textAnchor, rotationAnchor, angle);
            this.mapToLastCycle = mapToLastCycle;
        }
    }
    
    /**
     * Calculates the anchor point for a tick.
     * 
     * @param tick  the tick.
     * @param cursor  the cursor.
     * @param dataArea  the data area.
     * @param edge  the side on which the axis is displayed.
     * 
     * @return The anchor point.
     */
    protected float[] calculateAnchorPoint(ValueTick tick, double cursor, 
                                           Rectangle2D dataArea, 
                                           RectangleEdge edge) {
        if (tick instanceof CycleBoundTick) {
            boolean mapsav = this.boundMappedToLastCycle;
            this.boundMappedToLastCycle 
                = ((CycleBoundTick) tick).mapToLastCycle;
            float[] ret = super.calculateAnchorPoint(
                tick, cursor, dataArea, edge
            );
            this.boundMappedToLastCycle = mapsav;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -