package nl.tudelft.simulation.dsol.swing.charts.xy; import java.awt.Color; import java.awt.Container; import java.awt.GradientPaint; import java.io.Serializable; import java.rmi.RemoteException; import javax.naming.NamingException; import javax.swing.BorderFactory; import javax.swing.border.EtchedBorder; import org.djutils.event.EventProducerInterface; import org.djutils.event.EventTypeInterface; import org.djutils.event.ref.ReferenceType; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.JFreeChart; import org.jfree.chart.axis.LogarithmicAxis; import org.jfree.chart.plot.PlotOrientation; import nl.tudelft.simulation.dsol.simulators.SimulatorInterface; import nl.tudelft.simulation.dsol.statistics.SimPersistent; import nl.tudelft.simulation.dsol.swing.Swingable; import nl.tudelft.simulation.language.filters.FilterInterface; import nl.tudelft.simulation.naming.context.ContextInterface; import nl.tudelft.simulation.naming.context.util.ContextUtil; /** * The xyChart specifies the xyChart in DSOL. *

* Copyright (c) 2002-2022 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See * for project information https://simulation.tudelft.nl. The DSOL * project is distributed under a three-clause BSD-style license, which can be found at * * https://simulation.tudelft.nl/dsol/3.0/license.html. *

* @author Peter Jacobs */ public class XYChart implements Swingable, Serializable { /** */ private static final long serialVersionUID = 20200108L; /** x-axis is linear y-axis is linear. */ public static final short XLINEAR_YLINEAR = 0; /** x-axis is linear y-axis is logarithmic. */ public static final short XLINEAR_YLOGARITHMIC = 1; /** x-axis is logarithmic y-axis is linear. */ public static final short XLOGARITHMIC_YLINEAR = 2; /** x-axis is logarithmic y-axis is logarithmic. */ public static final short XLOGARITHMIC_YLOGARITHMIC = 3; /** LABEL_X_AXIS is the label on the X-axis. */ private static final String LABEL_X_AXIS = "X"; /** LABEL_Y_AXIS is the label on the Y-axis. */ private static final String LABEL_Y_AXIS = "value"; /** chart refers to the chart. */ protected JFreeChart chart = null; /** the simulator. */ final SimulatorInterface simulator; /** dataset refers to the dataset. */ protected XYDataset dataset = new XYDataset(); /** the axis type of the chart. */ protected short axisType = XLINEAR_YLINEAR; /** the period to show on the domain axis. */ private double period = Double.POSITIVE_INFINITY; /** * constructs a new XYChart and bind it to the replication context. * @param simulator SimulatorInterface; the simulator * @param title String; the title * @param domain double[]; the domain * @param range double[]; the range * @param axisType short; the type of the axsis */ public XYChart(final SimulatorInterface simulator, final String title, final double[] domain, final double[] range, final short axisType) { this.simulator = simulator; this.chart = ChartFactory.createXYLineChart(title, LABEL_X_AXIS, LABEL_Y_AXIS, this.dataset, PlotOrientation.VERTICAL, true, true, true); this.chart.setBackgroundPaint(new GradientPaint(0.0F, 0.0F, Color.white, 1000F, 0.0F, Color.blue)); this.axisType = axisType; switch (this.axisType) { case XYChart.XLINEAR_YLOGARITHMIC: this.chart.getXYPlot().setRangeAxis(new LogarithmicAxis(XYChart.LABEL_Y_AXIS)); break; case XYChart.XLOGARITHMIC_YLINEAR: this.chart.getXYPlot().setDomainAxis(new LogarithmicAxis(XYChart.LABEL_X_AXIS)); break; case XYChart.XLOGARITHMIC_YLOGARITHMIC: this.chart.getXYPlot().setDomainAxis(new LogarithmicAxis(XYChart.LABEL_X_AXIS)); this.chart.getXYPlot().setRangeAxis(new LogarithmicAxis(XYChart.LABEL_Y_AXIS)); break; default: break; } if (domain != null) { if (Double.isNaN(domain[0])) { this.chart.getXYPlot().getDomainAxis().setAutoRange(true); this.period = domain[1]; } else { this.chart.getXYPlot().getDomainAxis().setAutoRange(false); this.chart.getXYPlot().getDomainAxis().setLowerBound(domain[0]); this.chart.getXYPlot().getDomainAxis().setUpperBound(domain[1]); } } else { this.chart.getXYPlot().getDomainAxis().setAutoRange(true); } if (range != null) { this.chart.getXYPlot().getRangeAxis().setAutoRange(false); this.chart.getXYPlot().getRangeAxis().setLowerBound(range[0]); this.chart.getXYPlot().getRangeAxis().setUpperBound(range[1]); } else { this.chart.getXYPlot().getRangeAxis().setAutoRange(true); } this.dataset.addChangeListener(this.chart.getXYPlot()); this.getChart().fireChartChanged(); // bind to context try { ContextInterface context = ContextUtil.lookupOrCreateSubContext(simulator.getReplication().getContext(), "charts"); context.bindObject(this); } catch (NamingException | RemoteException exception) { simulator.getLogger().always().warn(exception, ""); } } /** * constructs a new XYChart that is registered in the simulator-provided jndi context. * @param simulator SimulatorInterface<?, ?, ?>; the simulator * @param title String; the title */ public XYChart(final SimulatorInterface simulator, final String title) { this(simulator, title, new double[] {0, simulator.getReplication().getRunLength().doubleValue()}); } /** * constructs a new XYChart that is registered in the simulator-provided jndi context. * @param simulator SimulatorInterface<?, ?, ?>; the simulator * @param title String; the title * @param axisType short; the axisType to use. */ public XYChart(final SimulatorInterface simulator, final String title, final short axisType) { this(simulator, title, new double[] {0, simulator.getReplication().getRunLength().doubleValue()}, axisType); } /** * constructs a new XYChart that is registered in the simulator-provided jndi context. * @param simulator SimulatorInterface<?, ?, ?>; the simulator * @param title String; the title * @param domain double[]; the domain */ public XYChart(final SimulatorInterface simulator, final String title, final double[] domain) { this(simulator, title, domain, null, XYChart.XLINEAR_YLINEAR); } /** * constructs a new XYChart that is registered in the simulator-provided jndi context. * @param simulator SimulatorInterface<?, ?, ?>; the simulator * @param title String; the title * @param period double; the period */ public XYChart(final SimulatorInterface simulator, final String title, final double period) { this(simulator, title, new double[] {Double.NaN, period}, null, XYChart.XLINEAR_YLINEAR); } /** * constructs a new XYChart that is registered in the simulator-provided jndi context. * @param simulator SimulatorInterface<?, ?, ?>; the simulator * @param title String; the title * @param domain double[]; the domain * @param axisType short; the axisType to use. */ public XYChart(final SimulatorInterface simulator, final String title, final double[] domain, final short axisType) { this(simulator, title, domain, null, axisType); } /** * constructs a new XYChart that is registered in the simulator-provided jndi context. * @param simulator SimulatorInterface<?, ?, ?>; the simulator * @param title String; the title * @param period double; the period * @param axisType short; the axisType to use. */ public XYChart(final SimulatorInterface simulator, final String title, final double period, final short axisType) { this(simulator, title, new double[] {Double.NaN, period}, null, axisType); } /** * constructs a new XYChart that is registered in the simulator-provided jndi context. * @param simulator SimulatorInterface<?, ?, ?>; the simulator * @param title String; the title * @param domain double[]; the domain * @param range double[]; the range */ public XYChart(final SimulatorInterface simulator, final String title, final double[] domain, final double[] range) { this(simulator, title, domain, range, XYChart.XLINEAR_YLINEAR); } /** * constructs a new XYChart that is registered in the simulator-provided jndi context. * @param simulator SimulatorInterface<?, ?, ?>; the simulator * @param title String; the title * @param period double; the period * @param range double[]; the range */ public XYChart(final SimulatorInterface simulator, final String title, final double period, final double[] range) { this(simulator, title, new double[] {Double.NaN, period}, range, XYChart.XLINEAR_YLINEAR); } /** * adds a tally to the xyChart. * @param persistent Persistent; the persistent */ public void add(final SimPersistent persistent) { XYSeries set = new XYSeries(persistent.getDescription(), this.simulator, this.axisType, this.period); persistent.addListener(set, SimPersistent.TIMED_OBSERVATION_ADDED_EVENT, ReferenceType.STRONG); this.getDataset().addSeries(set); } /** * adds an eventProducer to the xyChart. * @param description String; the description of the eventProducer * @param source EventProducerInterface; the source * @param eventType EventType; the event * @throws RemoteException on network failure */ public void add(final String description, final EventProducerInterface source, final EventTypeInterface eventType) throws RemoteException { XYSeries set = new XYSeries(description, this.simulator, this.axisType, this.period); source.addListener(set, eventType, EventProducerInterface.FIRST_POSITION, ReferenceType.STRONG); this.getDataset().addSeries(set); } /** * Set the label for the X-axis of the XY plot. * @param xLabel String; the new label for the X axis * @return the chart for method chaining */ public XYChart setLabelXAxis(final String xLabel) { getChart().getXYPlot().getDomainAxis().setLabel(xLabel); return this; } /** * Set the label for the Y-axis of the XY plot. * @param yLabel String; the new label for the X axis * @return the chart for method chaining */ public XYChart setLabelYAxis(final String yLabel) { getChart().getXYPlot().getRangeAxis().setLabel(yLabel); return this; } /** * returns the chart. * @return JFreeChart */ public JFreeChart getChart() { return this.chart; } /** {@inheritDoc} */ @Override public Container getSwingPanel() { ChartPanel result = new ChartPanel(this.chart); result.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); result.setMouseZoomable(true, false); return result; } /** * returns the dataset of a xyChart. * @return HistogramSeries */ public XYDataset getDataset() { return this.dataset; } /** * applies a filter on the chart. * @param filter FilterInterface; the filter to apply */ public void setFilter(final FilterInterface filter) { this.dataset.setFilter(filter); } /** {@inheritDoc} */ @Override public String toString() { return getChart().getTitle().getText(); } }