JFreechart Диаграмма Ганта с закругленными полосами - PullRequest
0 голосов
/ 24 сентября 2018

Я пытаюсь сделать диаграмму Ганта с закругленными концами.Я делаю это, потому что я хотел бы добавить точки и серии на график.Иногда у диаграммы Ганта есть «событие», а иногда у диаграммы есть «серия».Мне удалось создать эффект закругленной колонны.

Если вы ссылаетесь на GanttChart.java, вы увидите примечание, что вы можете использовать ЛИБО CustomBarRenderer.java ИЛИ пользовательский рендерер, который вы видите во встроенном коде, чтобы получить тот же эффект на рисунке.

Gantt Chart Picture

В моем идеальном сценарии график будет рисоваться с закруглением на правом конце синего столбца, а нижний столбец будет округляться с обоих концов.Есть ли способ настроить столбцы на диаграмме Ганта в JFreechart, как я описываю?Я добавил весь соответствующий код, кроме банок, если вы хотите помочь мне с этой проблемой.

TaskNumeric.java

import java.util.Date;
import org.jfree.data.gantt.Task;

/**
 *
 * @author hacky
 */
public class TaskNumeric extends Task {

    public TaskNumeric(String description, long start, long end) {
        super(description, new Date(start), new Date(end));
    }

    public static TaskNumeric duration(String description, long start, long duration) {
        return new TaskNumeric(description, start, start + duration);
    }

}

GanttChart.java

import java.awt.Color;
import java.awt.Shape;
import java.awt.geom.Ellipse2D;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.renderer.category.CategoryItemRenderer;
import org.jfree.data.category.IntervalCategoryDataset;
import org.jfree.data.gantt.TaskSeries;
import org.jfree.data.gantt.TaskSeriesCollection;

public class GanttChart {

    public JFreeChart chart;
    public ChartPanel panel;
    TaskSeries series1 = new TaskSeries("Datapoint");
    TaskSeries series2 = new TaskSeries("Series");

    public GanttChart() {
        // Create dataset
        IntervalCategoryDataset dataset = getCategoryDataset();

        // Create chart
        chart = ChartFactory.createGanttChart(
                "Gantt Chart Example | WWW.BORAJI.COM", // Chart title
                "Software Development Phases", // X-Axis Label
                "Timeline", // Y-Axis Label
                dataset);
        CategoryPlot plot = chart.getCategoryPlot();
        Shape shape = new Ellipse2D.Double(-.5, -.5, 1, 1);

        //this gives the rounded column effect
        plot.setRenderer(new CustomBarRenderer());

        //this also gives the rounded column effect if i use one or the other it doesnt matter
        CustomBarRenderer r = (CustomBarRenderer) plot.getRenderer();
        CategoryItemRenderer renderer = (CategoryItemRenderer) r;
        renderer.setBaseShape(shape);
        renderer.setSeriesShape(0, shape);
        renderer.setSeriesPaint(0, Color.BLUE);
        renderer.setSeriesShape(1, shape);
        renderer.setSeriesPaint(1, Color.GRAY);
        renderer.setSeriesShape(2, shape);
        renderer.setSeriesPaint(2, Color.CYAN);

        DateAxis axis = (DateAxis) plot.getRangeAxis();
        axis.setDateFormatOverride(new SimpleDateFormat("SSS"));
        axis.setMaximumDate(new Date(10));

        panel = new ChartPanel(chart);
    }

    private IntervalCategoryDataset getCategoryDataset() {

        series1.add(new TaskNumeric("Event 1",
                0,
                1
        ));

        series2.add(new TaskNumeric("Event 2",
                0,
                10
        ));

        TaskSeriesCollection dataset = new TaskSeriesCollection();
        dataset.add(series1);
        dataset.add(series2);
        return dataset;
    }
}

Annotation.java

import annotationsgraphtest.GanttChart;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

public class Annotation {

    public static JSONArray annotationEventsJSON = new JSONArray();
    public static JSONObject annotationsJSON = new JSONObject();

    public static String annotationFile = "annotations.json";

    public static GanttChart annotationChart = new GanttChart();
}

Main.java

import annotation.Annotation;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import net.miginfocom.swing.MigLayout;

/**
 *
 * @author hacky
 */
public class AnnotationsGraphTest {

    private static JFrame MAIN_FRAME = new JFrame();
    private static JPanel guinew = new JPanel();

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here

        //graphInterface.setNumFrames();
        //Annotation.loadAnnotations();
        //graphInterface.makeGraphPanels("Annotations", guinew);
        guinew.add(Annotation.annotationChart.panel);
        setupMainFrame();
    }

    public static void setupMainFrame() {
        MAIN_FRAME.setLayout(new MigLayout(
                "insets 0, gap 0, wrap", // Layout Constraints
                "[fill, grow]", // Column constraints
                "[fill, grow]")); // Row constraints
        MAIN_FRAME.getContentPane().setBackground(Color.black);
        MAIN_FRAME.setPreferredSize(new Dimension(1024, 768));
        MAIN_FRAME.setSize(1024,768);

        MAIN_FRAME.setTitle("Dynamic Data Annotator");

        MAIN_FRAME.setResizable(false);
        MAIN_FRAME.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        //add them
        MAIN_FRAME.add(guinew);
        MAIN_FRAME.revalidate();
        MAIN_FRAME.repaint();

        //need to add a loading popup
        MAIN_FRAME.setVisible(true);
    }
}

CustomBarRenderer.java (Это дает ту же функциональность, что вы видите выше)

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;

import org.jfree.chart.entity.EntityCollection;
import org.jfree.chart.event.RendererChangeEvent;
import org.jfree.chart.labels.CategoryItemLabelGenerator;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.renderer.category.CategoryItemRendererState;
import org.jfree.chart.renderer.category.GanttRenderer;
import org.jfree.data.category.CategoryDataset;
import org.jfree.data.gantt.GanttCategoryDataset;
import org.jfree.ui.RectangleEdge;

/**
 * A renderer for simple Gantt charts.  The example shown
 * here is generated by the <code>GanttDemo1.java</code> program
 * included in the JFreeChart Demo Collection:
 * <br><br>
 * <img src="../../../../../images/GanttRendererSample.png"
 * alt="GanttRendererSample.png">
 */
public class CustomBarRenderer extends GanttRenderer {

    /** For serialization. */
    private static final long serialVersionUID = -4010349116350119512L;

    /** The paint for displaying the percentage complete. */
    private transient Paint completePaint;

    /** The paint for displaying the incomplete part of a task. */
    private transient Paint incompletePaint;

    /**
     * Controls the starting edge of the progress indicator (expressed as a
     * percentage of the overall bar width).
     */
    private double startPercent;

    /**
     * Controls the ending edge of the progress indicator (expressed as a
     * percentage of the overall bar width).
     */
    private double endPercent;

    /**
     * Creates a new renderer.
     */
    public CustomBarRenderer() {
        super();
    }

    /**
     * Returns the paint used to show the percentage complete.
     *
     * @return The paint (never <code>null</code>.
     *
     * @see #setCompletePaint(Paint)
     */
    public Paint getCompletePaint() {
        return this.completePaint;
    }

    /**
     * Sets the paint used to show the percentage complete and sends a
     * {@link RendererChangeEvent} to all registered listeners.
     *
     * @param paint  the paint (<code>null</code> not permitted).
     *
     * @see #getCompletePaint()
     */
    public void setCompletePaint(Paint paint) {
        if (paint == null) {
            throw new IllegalArgumentException("Null 'paint' argument.");
        }
        this.completePaint = paint;
        fireChangeEvent();
    }

    /**
     * Returns the paint used to show the percentage incomplete.
     *
     * @return The paint (never <code>null</code>).
     *
     * @see #setCompletePaint(Paint)
     */
    public Paint getIncompletePaint() {
        return this.incompletePaint;
    }

    /**
     * Sets the paint used to show the percentage incomplete and sends a
     * {@link RendererChangeEvent} to all registered listeners.
     *
     * @param paint  the paint (<code>null</code> not permitted).
     *
     * @see #getIncompletePaint()
     */
    public void setIncompletePaint(Paint paint) {
        if (paint == null) {
            throw new IllegalArgumentException("Null 'paint' argument.");
        }
        this.incompletePaint = paint;
        fireChangeEvent();
    }

    /**
     * Returns the position of the start of the progress indicator, as a
     * percentage of the bar width.
     *
     * @return The start percent.
     *
     * @see #setStartPercent(double)
     */
    public double getStartPercent() {
        return this.startPercent;
    }

    /**
     * Sets the position of the start of the progress indicator, as a
     * percentage of the bar width, and sends a {@link RendererChangeEvent} to
     * all registered listeners.
     *
     * @param percent  the percent.
     *
     * @see #getStartPercent()
     */
    public void setStartPercent(double percent) {
        this.startPercent = percent;
        fireChangeEvent();
    }

    /**
     * Returns the position of the end of the progress indicator, as a
     * percentage of the bar width.
     *
     * @return The end percent.
     *
     * @see #setEndPercent(double)
     */
    public double getEndPercent() {
        return this.endPercent;
    }

    /**
     * Sets the position of the end of the progress indicator, as a percentage
     * of the bar width, and sends a {@link RendererChangeEvent} to all
     * registered listeners.
     *
     * @param percent  the percent.
     *
     * @see #getEndPercent()
     */
    public void setEndPercent(double percent) {
        this.endPercent = percent;
        fireChangeEvent();
    }

    /**
     * Draws the bar for a single (series, category) data item.
     *
     * @param g2  the graphics device.
     * @param state  the renderer state.
     * @param dataArea  the data area.
     * @param plot  the plot.
     * @param domainAxis  the domain axis.
     * @param rangeAxis  the range axis.
     * @param dataset  the dataset.
     * @param row  the row index (zero-based).
     * @param column  the column index (zero-based).
     * @param pass  the pass index.
     */
    @Override
    public void drawItem(Graphics2D g2,
                         CategoryItemRendererState state,
                         Rectangle2D dataArea,
                         CategoryPlot plot,
                         CategoryAxis domainAxis,
                         ValueAxis rangeAxis,
                         CategoryDataset dataset,
                         int row,
                         int column,
                         int pass) {

         if (dataset instanceof GanttCategoryDataset) {
             GanttCategoryDataset gcd = (GanttCategoryDataset) dataset;
             drawTasks(g2, state, dataArea, plot, domainAxis, rangeAxis, gcd,
                     row, column);
         }
         else {  // let the superclass handle it...
             super.drawItem(g2, state, dataArea, plot, domainAxis, rangeAxis,
                     dataset, row, column, pass);
         }

     }

    /**
     * Draws the tasks/subtasks for one item.
     *
     * @param g2  the graphics device.
     * @param state  the renderer state.
     * @param dataArea  the data plot area.
     * @param plot  the plot.
     * @param domainAxis  the domain axis.
     * @param rangeAxis  the range axis.
     * @param dataset  the data.
     * @param row  the row index (zero-based).
     * @param column  the column index (zero-based).
     */
    protected void drawTasks(Graphics2D g2,
                             CategoryItemRendererState state,
                             Rectangle2D dataArea,
                             CategoryPlot plot,
                             CategoryAxis domainAxis,
                             ValueAxis rangeAxis,
                             GanttCategoryDataset dataset,
                             int row,
                             int column) {

        int count = dataset.getSubIntervalCount(row, column);
        if (count == 0) {
            drawTask(g2, state, dataArea, plot, domainAxis, rangeAxis,
                    dataset, row, column);
        }

        PlotOrientation orientation = plot.getOrientation();
        for (int subinterval = 0; subinterval < count; subinterval++) {

            RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();

            // value 0
            Number value0 = dataset.getStartValue(row, column, subinterval);
            if (value0 == null) {
                return;
            }
            double translatedValue0 = rangeAxis.valueToJava2D(
                    value0.doubleValue(), dataArea, rangeAxisLocation);

            // value 1
            Number value1 = dataset.getEndValue(row, column, subinterval);
            if (value1 == null) {
                return;
            }
            double translatedValue1 = rangeAxis.valueToJava2D(
                    value1.doubleValue(), dataArea, rangeAxisLocation);

            if (translatedValue1 < translatedValue0) {
                double temp = translatedValue1;
                translatedValue1 = translatedValue0;
                translatedValue0 = temp;
            }

            double rectStart = calculateBarW0(plot, plot.getOrientation(),
                    dataArea, domainAxis, state, row, column);
            double rectLength = Math.abs(translatedValue1 - translatedValue0);
            double rectBreadth = state.getBarWidth();

            // DRAW THE BARS...
            RoundRectangle2D bar = null;
            RectangleEdge barBase = null;
            if (plot.getOrientation() == PlotOrientation.HORIZONTAL) {
                bar = new RoundRectangle2D.Double(translatedValue0, rectStart,
                        rectLength, rectBreadth,10,10);
                barBase = RectangleEdge.LEFT;
            }
            else if (plot.getOrientation() == PlotOrientation.VERTICAL) {
                bar = new RoundRectangle2D.Double(rectStart, translatedValue0,
                        rectBreadth, rectLength,10,10);
                barBase = RectangleEdge.BOTTOM;
            }

            RoundRectangle2D completeBar = null;
            RoundRectangle2D incompleteBar = null;
            Number percent = dataset.getPercentComplete(row, column,
                    subinterval);
            double start = getStartPercent();
            double end = getEndPercent();
            if (percent != null) {
                double p = percent.doubleValue();
                if (orientation == PlotOrientation.HORIZONTAL) {
                    completeBar = new RoundRectangle2D.Double(translatedValue0,
                            rectStart + start * rectBreadth, rectLength * p,
                            rectBreadth * (end - start),10,10);
                    incompleteBar = new RoundRectangle2D.Double(translatedValue0
                            + rectLength * p, rectStart + start * rectBreadth,
                            rectLength * (1 - p), rectBreadth * (end - start),10,10);
                }
                else if (orientation == PlotOrientation.VERTICAL) {
                    completeBar = new RoundRectangle2D.Double(rectStart + start
                            * rectBreadth, translatedValue0 + rectLength
                            * (1 - p), rectBreadth * (end - start),
                            rectLength * p,10,10);
                    incompleteBar = new RoundRectangle2D.Double(rectStart + start
                            * rectBreadth, translatedValue0, rectBreadth
                            * (end - start), rectLength * (1 - p),10,10);
                }

            }

            if (getShadowsVisible()) {
                getBarPainter().paintBarShadow(g2, this, row, column, bar,
                        barBase, true);
            }
            getBarPainter().paintBar(g2, this, row, column, bar, barBase);

            if (completeBar != null) {
                g2.setPaint(getCompletePaint());
                g2.fill(completeBar);
            }
            if (incompleteBar != null) {
                g2.setPaint(getIncompletePaint());
                g2.fill(incompleteBar);
            }
            if (isDrawBarOutline()
                    && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
                g2.setStroke(getItemStroke(row, column));
                g2.setPaint(getItemOutlinePaint(row, column));
                g2.draw(bar);
            }

            if (subinterval == count - 1) {
                // submit the current data point as a crosshair candidate
                //int datasetIndex = plot.findDatasetIndex(dataset);
                Comparable columnKey = dataset.getColumnKey(column);
                Comparable rowKey = dataset.getRowKey(row);
                double xx = domainAxis.getCategorySeriesMiddle(columnKey,
                        rowKey, dataset, getItemMargin(), dataArea,
                        plot.getDomainAxisEdge());
                /*updateCrosshairValues(state.getCrosshairState(),
                        dataset.getRowKey(row), dataset.getColumnKey(column),
                        value1.doubleValue(), datasetIndex, xx,
                        translatedValue1, orientation);*/

            }
            // collect entity and tool tip information...
            if (state.getInfo() != null) {
                EntityCollection entities = state.getEntityCollection();
                if (entities != null) {
                    addItemEntity(entities, dataset, row, column, bar);
                }
            }
        }
    }

    /**
     * Draws a single task.
     *
     * @param g2  the graphics device.
     * @param state  the renderer state.
     * @param dataArea  the data plot area.
     * @param plot  the plot.
     * @param domainAxis  the domain axis.
     * @param rangeAxis  the range axis.
     * @param dataset  the data.
     * @param row  the row index (zero-based).
     * @param column  the column index (zero-based).
     */
    protected void drawTask(Graphics2D g2,
                            CategoryItemRendererState state,
                            Rectangle2D dataArea,
                            CategoryPlot plot,
                            CategoryAxis domainAxis,
                            ValueAxis rangeAxis,
                            GanttCategoryDataset dataset,
                            int row,
                            int column) {

        PlotOrientation orientation = plot.getOrientation();
        RectangleEdge rangeAxisLocation = plot.getRangeAxisEdge();

        // Y0
        Number value0 = dataset.getEndValue(row, column);
        if (value0 == null) {
            return;
        }
        double java2dValue0 = rangeAxis.valueToJava2D(value0.doubleValue(),
                dataArea, rangeAxisLocation);

        // Y1
        Number value1 = dataset.getStartValue(row, column);
        if (value1 == null) {
            return;
        }
        double java2dValue1 = rangeAxis.valueToJava2D(value1.doubleValue(),
                dataArea, rangeAxisLocation);

        if (java2dValue1 < java2dValue0) {
            double temp = java2dValue1;
            java2dValue1 = java2dValue0;
            java2dValue0 = temp;
            value1 = value0;
        }

        double rectStart = calculateBarW0(plot, orientation, dataArea,
                domainAxis, state, row, column);
        double rectBreadth = state.getBarWidth();
        double rectLength = Math.abs(java2dValue1 - java2dValue0);

        Rectangle2D bar = null;
        RectangleEdge barBase = null;
        if (orientation == PlotOrientation.HORIZONTAL) {
            bar = new Rectangle2D.Double(java2dValue0, rectStart, rectLength,
                    rectBreadth);
            barBase = RectangleEdge.LEFT;
        }
        else if (orientation == PlotOrientation.VERTICAL) {
            bar = new Rectangle2D.Double(rectStart, java2dValue1, rectBreadth,
                    rectLength);
            barBase = RectangleEdge.BOTTOM;
        }

        Rectangle2D completeBar = null;
        Rectangle2D incompleteBar = null;
        Number percent = dataset.getPercentComplete(row, column);
        double start = getStartPercent();
        double end = getEndPercent();
        if (percent != null) {
            double p = percent.doubleValue();
            if (plot.getOrientation() == PlotOrientation.HORIZONTAL) {
                completeBar = new Rectangle2D.Double(java2dValue0,
                        rectStart + start * rectBreadth, rectLength * p,
                        rectBreadth * (end - start));
                incompleteBar = new Rectangle2D.Double(java2dValue0
                        + rectLength * p, rectStart + start * rectBreadth,
                        rectLength * (1 - p), rectBreadth * (end - start));
            }
            else if (plot.getOrientation() == PlotOrientation.VERTICAL) {
                completeBar = new Rectangle2D.Double(rectStart + start
                        * rectBreadth, java2dValue1 + rectLength * (1 - p),
                        rectBreadth * (end - start), rectLength * p);
                incompleteBar = new Rectangle2D.Double(rectStart + start
                        * rectBreadth, java2dValue1, rectBreadth * (end
                        - start), rectLength * (1 - p));
            }

        }

        if (getShadowsVisible()) {
            getBarPainter().paintBarShadow(g2, this, row, column, bar,
                    barBase, true);
        }
        getBarPainter().paintBar(g2, this, row, column, bar, barBase);

        if (completeBar != null) {
            g2.setPaint(getCompletePaint());
            g2.fill(completeBar);
        }
        if (incompleteBar != null) {
            g2.setPaint(getIncompletePaint());
            g2.fill(incompleteBar);
        }

        // draw the outline...
        if (isDrawBarOutline()
                && state.getBarWidth() > BAR_OUTLINE_WIDTH_THRESHOLD) {
            Stroke stroke = getItemOutlineStroke(row, column);
            Paint paint = getItemOutlinePaint(row, column);
            if (stroke != null && paint != null) {
                g2.setStroke(stroke);
                g2.setPaint(paint);
                g2.draw(bar);
            }
        }

        CategoryItemLabelGenerator generator = getItemLabelGenerator(row,
                column);
        if (generator != null && isItemLabelVisible(row, column)) {
            drawItemLabel(g2, dataset, row, column, plot, generator, bar,
                    false);
        }

        // submit the current data point as a crosshair candidate
        //int datasetIndex = plot.findDatasetIndex(dataset);
        Comparable columnKey = dataset.getColumnKey(column);
        Comparable rowKey = dataset.getRowKey(row);
        double xx = domainAxis.getCategorySeriesMiddle(columnKey, rowKey,
                dataset, getItemMargin(), dataArea, plot.getDomainAxisEdge());
       /* updateCrosshairValues(state.getCrosshairState(),
                dataset.getRowKey(row), dataset.getColumnKey(column),
                value1.doubleValue(), datasetIndex, xx, java2dValue1,
                orientation);*/

        // collect entity and tool tip information...
        EntityCollection entities = state.getEntityCollection();
        if (entities != null) {
            addItemEntity(entities, dataset, row, column, bar);
        }
    }

    /**
     * Returns the Java2D coordinate for the middle of the specified data item.
     *
     * @param rowKey  the row key.
     * @param columnKey  the column key.
     * @param dataset  the dataset.
     * @param axis  the axis.
     * @param area  the drawing area.
     * @param edge  the edge along which the axis lies.
     *
     * @return The Java2D coordinate.
     *
     * @since 1.0.11
     */
    @Override
    public double getItemMiddle(Comparable rowKey, Comparable columnKey,
            CategoryDataset dataset, CategoryAxis axis, Rectangle2D area,
            RectangleEdge edge) {
        return super.getItemMiddle(rowKey, columnKey, dataset, axis, area, edge);
    }

    /**
     * Tests this renderer for equality with an arbitrary object.
     *
     * @param obj  the object (<code>null</code> permitted).
     *
     * @return A boolean.
     */
    @Override
    public boolean equals(Object obj) {

        return super.equals(obj);
    }


}
...