From 2f2a3a427208d77ec4c341b7e32d50cf8f66f6cb Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Fri, 9 Jul 2021 03:57:27 +0200 Subject: [PATCH 1/3] Improved appearance plot --- .../openrocket/gui/plot/SimulationPlot.java | 248 ++++++++++-------- 1 file changed, 136 insertions(+), 112 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java index 00a92addb..299f62fa5 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java @@ -36,7 +36,7 @@ import org.jfree.chart.LegendItemSource; import org.jfree.chart.annotations.XYImageAnnotation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; -import org.jfree.chart.block.LineBorder; +import org.jfree.chart.block.BlockBorder; import org.jfree.chart.plot.DefaultDrawingSupplier; import org.jfree.chart.plot.Marker; import org.jfree.chart.plot.PlotOrientation; @@ -63,28 +63,28 @@ import org.jfree.ui.TextAnchor; */ @SuppressWarnings("serial") public class SimulationPlot { - + private static final float PLOT_STROKE_WIDTH = 1.5f; - + private final JFreeChart chart; - + private final PlotConfiguration config; private final Simulation simulation; private final PlotConfiguration filled; - + private final List eventList; private final List renderers = new ArrayList(); - + private final LegendItems legendItems; - + int branchCount; - + void setShowPoints(boolean showPoints) { for (ModifiedXYItemRenderer r : renderers) { r.setBaseShapesVisible(showPoints); } } - + void setShowBranch(int branch) { XYPlot plot = (XYPlot) chart.getPlot(); int datasetcount = plot.getDatasetCount(); @@ -98,12 +98,12 @@ public class SimulationPlot { } drawDomainMarkers(branch); } - + SimulationPlot(Simulation simulation, PlotConfiguration config, boolean initialShowPoints) { this.simulation = simulation; this.config = config; this.branchCount = simulation.getSimulatedData().getBranchCount(); - + this.chart = ChartFactory.createXYLineChart( //// Simulated flight /*title*/simulation.getName(), @@ -114,40 +114,42 @@ public class SimulationPlot { /*legend*/false, /*tooltips*/true, /*urls*/false - ); - + ); + + chart.getTitle().setFont(new Font("Dialog", Font.BOLD, 23)); + chart.setBackgroundPaint(new Color(240, 240, 240)); this.legendItems = new LegendItems(); LegendTitle legend = new LegendTitle(legendItems); legend.setMargin(new RectangleInsets(1.0, 1.0, 1.0, 1.0)); - legend.setFrame(new LineBorder()); - legend.setBackgroundPaint(Color.white); + legend.setFrame(BlockBorder.NONE); + legend.setBackgroundPaint(new Color(240, 240, 240)); legend.setPosition(RectangleEdge.BOTTOM); chart.addSubtitle(legend); - + chart.addSubtitle(new TextTitle(config.getName())); - + // Fill the auto-selections based on first branch selected. FlightDataBranch mainBranch = simulation.getSimulatedData().getBranch(0); this.filled = config.fillAutoAxes(mainBranch); List axes = filled.getAllAxes(); - + // Create the data series for both axes XYSeriesCollection[] data = new XYSeriesCollection[2]; data[0] = new XYSeriesCollection(); data[1] = new XYSeriesCollection(); - + // Get the domain axis type final FlightDataType domainType = filled.getDomainAxisType(); final Unit domainUnit = filled.getDomainAxisUnit(); if (domainType == null) { throw new IllegalArgumentException("Domain axis type not specified."); } - + // Get plot length (ignore trailing NaN's) int typeCount = filled.getTypeCount(); - + int seriesCount = 0; - + // Create the XYSeries objects from the flight data and store into the collections String[] axisLabel = new String[2]; for (int i = 0; i < typeCount; i++) { @@ -156,11 +158,11 @@ public class SimulationPlot { Unit unit = filled.getUnit(i); int axis = filled.getAxis(i); String name = getLabel(type, unit); - + List seriesNames = Util.generateSeriesLabels(simulation); - + // Populate data for each branch. - + // The primary branch (branchIndex = 0) is easy since all the data is copied { int branchIndex = 0; @@ -180,48 +182,57 @@ public class SimulationPlot { for (int branchIndex = 1; branchIndex < branchCount; branchIndex++) { FlightDataBranch primaryBranch = simulation.getSimulatedData().getBranch(0); FlightDataBranch thisBranch = simulation.getSimulatedData().getBranch(branchIndex); - + // Get first time index used in secondary branch; double firstSampleTime = thisBranch.get(FlightDataType.TYPE_TIME).get(0); - + XYSeries series = new XYSeries(seriesCount++, false, true); series.setDescription(thisBranch.getBranchName() + ": " + name); - + // Copy the first points from the primaryBranch. List primaryT = primaryBranch.get(FlightDataType.TYPE_TIME); List primaryx = primaryBranch.get(domainType); List primaryy = primaryBranch.get(type); - + for (int j = 0; j < primaryT.size(); j++) { if (primaryT.get(j) >= firstSampleTime) { break; } series.add(domainUnit.toUnit(primaryx.get(j)), unit.toUnit(primaryy.get(j))); } - + // Now copy all the data from the secondary branch List plotx = thisBranch.get(domainType); List ploty = thisBranch.get(type); - + int pointCount = plotx.size(); for (int j = 0; j < pointCount; j++) { series.add(domainUnit.toUnit(plotx.get(j)), unit.toUnit(ploty.get(j))); } data[axis].addSeries(series); } - + // Update axis label if (axisLabel[axis] == null) axisLabel[axis] = type.getName(); else axisLabel[axis] += "; " + type.getName(); } - + // Add the data and formatting to the plot XYPlot plot = chart.getXYPlot(); plot.setDomainPannable(true); plot.setRangePannable(true); - + + // Plot appearance + plot.setBackgroundPaint(Color.white); + plot.setAxisOffset(new RectangleInsets(0, 0, 0, 0)); + plot.setRangeGridlinesVisible(true); + plot.setRangeGridlinePaint(Color.lightGray); + + plot.setDomainGridlinesVisible(true); + plot.setDomainGridlinePaint(Color.lightGray); + int axisno = 0; for (int i = 0; i < 2; i++) { // Check whether axis has any data @@ -233,19 +244,30 @@ public class SimulationPlot { axis.setLabel(axisLabel[i]); // axis.setRange(axes.get(i).getMinValue(), axes.get(i).getMaxValue()); plot.setRangeAxis(axisno, axis); - + axis.setLabelFont(new Font("Dialog", Font.BOLD, 14)); + double domainMin = data[i].getDomainLowerBound(true); double domainMax = data[i].getDomainUpperBound(true); - + plot.setDomainAxis(new PresetNumberAxis(domainMin, domainMax)); - + // Add data and map to the axis + Color[] colors = {new Color(0,114,189), // Colors for data lines + new Color(217,83,25), + new Color(237,177,32), + new Color(126,49,142), + new Color(119,172,48), + new Color(77,190,238), + new Color(162,20,47)}; plot.setDataset(axisno, data[i]); ModifiedXYItemRenderer r = new ModifiedXYItemRenderer(branchCount); renderers.add(r); plot.setRenderer(axisno, r); r.setBaseShapesVisible(initialShowPoints); r.setBaseShapesFilled(true); + r.setSeriesPaint(0, colors[i]); + r.setSeriesPaint(1, colors[i+2]); + r.setSeriesPaint(2, colors[i+4]); for (int j = 0; j < data[i].getSeriesCount(); j++) { Stroke lineStroke = new BasicStroke(PLOT_STROKE_WIDTH); r.setSeriesStroke(j, lineStroke); @@ -261,30 +283,30 @@ public class SimulationPlot { Stroke lineStroke = r.getSeriesStroke(j); this.legendItems.lineStrokes.add(lineStroke); } - + plot.mapDatasetToRangeAxis(axisno, axisno); axisno++; } } - + plot.getDomainAxis().setLabel(getLabel(domainType, domainUnit)); plot.addDomainMarker(new ValueMarker(0)); plot.addRangeMarker(new ValueMarker(0)); - - - + + plot.getDomainAxis().setLabelFont(new Font("Dialog", Font.BOLD, 14)); + // Create list of events to show (combine event too close to each other) this.eventList = buildEventInfo(); - + // Create the event markers drawDomainMarkers(-1); - + } - + JFreeChart getJFreeChart() { return chart; } - + private String getLabel(FlightDataType type, Unit unit) { String name = type.getName(); if (unit != null && !UnitGroup.UNITS_NONE.contains(unit) && @@ -292,16 +314,16 @@ public class SimulationPlot { name += " (" + unit.getUnit() + ")"; return name; } - + private void drawDomainMarkers(int stage) { XYPlot plot = chart.getXYPlot(); FlightDataBranch mainBranch = simulation.getSimulatedData().getBranch(0); - + // Clear existing domain markers plot.clearDomainMarkers(); - + // Construct domain marker lists collapsing based on time. - + List eventTimes = new ArrayList(); List eventLabels = new ArrayList(); List eventColors = new ArrayList(); @@ -316,21 +338,21 @@ public class SimulationPlot { if (stage >= 0 && stage != info.stage) { continue; } - + double t = info.time; FlightEvent.Type type = info.event.getType(); - + if (Math.abs(t - prevTime) <= 0.05) { - + if (!typeSet.contains(type)) { text = text + ", " + type.toString(); color = EventGraphics.getEventColor(type); image = EventGraphics.getEventImage(type); typeSet.add(type); } - + } else { - + if (text != null) { eventTimes.add(prevTime); eventLabels.add(text); @@ -344,7 +366,7 @@ public class SimulationPlot { typeSet.clear(); typeSet.add(type); } - + } if (text != null) { eventTimes.add(prevTime); @@ -353,58 +375,59 @@ public class SimulationPlot { eventImages.add(image); } } - + // Plot the markers if (config.getDomainAxisType() == FlightDataType.TYPE_TIME) { - + // Domain time is plotted as vertical markers for (int i = 0; i < eventTimes.size(); i++) { double t = eventTimes.get(i); String event = eventLabels.get(i); Color color = eventColors.get(i); - + ValueMarker m = new ValueMarker(t); m.setLabel(event); m.setPaint(color); m.setLabelPaint(color); m.setAlpha(0.7f); + m.setLabelFont(new Font("Dialog", Font.PLAIN, 13)); plot.addDomainMarker(m); } - + } else { - + // Other domains are plotted as image annotations List time = mainBranch.get(FlightDataType.TYPE_TIME); List domain = mainBranch.get(config.getDomainAxisType()); - + LinearInterpolator domainInterpolator = new LinearInterpolator(time, domain); - + for (int i = 0; i < eventTimes.size(); i++) { double t = eventTimes.get(i); String event = eventLabels.get(i); Image image = eventImages.get(i); - + if (image == null) continue; - + double xcoord = domainInterpolator.getValue(t); for (int index = 0; index < config.getTypeCount(); index++) { FlightDataType type = config.getType(index); List range = mainBranch.get(type); - + LinearInterpolator rangeInterpolator = new LinearInterpolator(time, range); // Image annotations are not supported on the right-side axis // TODO: LOW: Can this be achieved by JFreeChart? if (filled.getAxis(index) != SimulationPlotPanel.LEFT) { continue; } - + double ycoord = rangeInterpolator.getValue(t); - + // Convert units xcoord = config.getDomainAxisUnit().toUnit(xcoord); ycoord = config.getUnit(index).toUnit(ycoord); - + XYImageAnnotation annotation = new XYImageAnnotation(xcoord, ycoord, image, RectangleAnchor.CENTER); annotation.setToolTipText(event); @@ -413,10 +436,10 @@ public class SimulationPlot { } } } - + private List buildEventInfo() { ArrayList eventList = new ArrayList(); - + for (int branch = 0; branch < branchCount; branch++) { List events = simulation.getSimulatedData().getBranch(branch).getEvents(); for (FlightEvent event : events) { @@ -430,9 +453,9 @@ public class SimulationPlot { } } } - + Collections.sort(eventList, new Comparator() { - + @Override public int compare(EventDisplayInfo o1, EventDisplayInfo o2) { if (o1.time < o2.time) @@ -441,20 +464,20 @@ public class SimulationPlot { return 0; return 1; } - + }); - + return eventList; - + } - + private static class LegendItems implements LegendItemSource { - + private final List lineLabels = new ArrayList(); private final List linePaints = new ArrayList(); private final List lineStrokes = new ArrayList(); private final List pointShapes = new ArrayList(); - + @Override public LegendItemCollection getLegendItems() { LegendItemCollection c = new LegendItemCollection(); @@ -474,30 +497,30 @@ public class SimulationPlot { boolean lineVisible = true; Stroke lineStroke = lineStrokes.get(i); Paint linePaint = linePaints.get(i); - + Shape legendLine = new Line2D.Double(-7.0, 0.0, 7.0, 0.0); - + LegendItem result = new LegendItem(label, description, toolTipText, urlText, shapeIsVisible, shape, shapeIsFilled, fillPaint, shapeOutlineVisible, outlinePaint, outlineStroke, lineVisible, legendLine, lineStroke, linePaint); - + c.add(result); i++; } return c; } } - + /** * A modification to the standard renderer that renders the domain marker * labels vertically instead of horizontally. - * + * * This class is special in that it assumes the data series are added to it * in a specific order. In particular they must be "by parameter by stage". * Assuming that three series are chosen (a, b, c) and the rocket has 2 stages, the * data series are added in this order: - * + * * series a stage 0 * series a stage 1 * series b stage 0 @@ -506,68 +529,68 @@ public class SimulationPlot { * series c stage 1 */ private static class ModifiedXYItemRenderer extends XYLineAndShapeRenderer { - + private final int branchCount; - + private ModifiedXYItemRenderer(int branchCount) { this.branchCount = branchCount; } - + @Override public Paint lookupSeriesPaint(int series) { return super.lookupSeriesPaint(series / branchCount); } - + @Override public Paint lookupSeriesFillPaint(int series) { return super.lookupSeriesFillPaint(series / branchCount); } - + @Override public Paint lookupSeriesOutlinePaint(int series) { return super.lookupSeriesOutlinePaint(series / branchCount); } - + @Override public Stroke lookupSeriesStroke(int series) { return super.lookupSeriesStroke(series / branchCount); } - + @Override public Stroke lookupSeriesOutlineStroke(int series) { return super.lookupSeriesOutlineStroke(series / branchCount); } - + @Override public Shape lookupSeriesShape(int series) { return DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE[series % branchCount % DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE.length]; } - + @Override public Shape lookupLegendShape(int series) { return DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE[series % branchCount % DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE.length]; } - + @Override public Font lookupLegendTextFont(int series) { return super.lookupLegendTextFont(series / branchCount); } - + @Override public Paint lookupLegendTextPaint(int series) { return super.lookupLegendTextPaint(series / branchCount); } - + @Override public void drawDomainMarker(Graphics2D g2, XYPlot plot, ValueAxis domainAxis, Marker marker, Rectangle2D dataArea) { - + if (!(marker instanceof ValueMarker)) { // Use parent for all others super.drawDomainMarker(g2, plot, domainAxis, marker, dataArea); return; } - + /* * Draw the normal marker, but with rotated text. * Copied from the overridden method. @@ -578,9 +601,9 @@ public class SimulationPlot { if (!range.contains(value)) { return; } - + double v = domainAxis.valueToJava2D(value, dataArea, plot.getDomainAxisEdge()); - + PlotOrientation orientation = plot.getOrientation(); Line2D line = null; if (orientation == PlotOrientation.HORIZONTAL) { @@ -588,14 +611,14 @@ public class SimulationPlot { } else { line = new Line2D.Double(v, dataArea.getMinY(), v, dataArea.getMaxY()); } - + final Composite originalComposite = g2.getComposite(); g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, marker .getAlpha())); g2.setPaint(marker.getPaint()); g2.setStroke(marker.getStroke()); g2.draw(line); - + String label = marker.getLabel(); RectangleAnchor anchor = marker.getLabelAnchor(); if (label != null) { @@ -605,7 +628,7 @@ public class SimulationPlot { Point2D coordinates = calculateDomainMarkerTextAnchorPoint(g2, orientation, dataArea, line.getBounds2D(), marker .getLabelOffset(), LengthAdjustmentType.EXPAND, anchor); - + // Changed: TextAnchor textAnchor = TextAnchor.TOP_RIGHT; TextUtilities.drawRotatedString(label, g2, (float) coordinates.getX() + 2, @@ -614,24 +637,24 @@ public class SimulationPlot { } g2.setComposite(originalComposite); } - + } - + private static class PresetNumberAxis extends NumberAxis { private final double min; private final double max; - + public PresetNumberAxis(double min, double max) { this.min = min; this.max = max; autoAdjustRange(); } - + @Override protected void autoAdjustRange() { this.setRange(min, max); } - + @Override public void setRange(Range range) { double lowerValue = range.getLowerBound(); @@ -643,13 +666,14 @@ public class SimulationPlot { } super.setRange(new Range(lowerValue, upperValue)); } - + } - + private static class EventDisplayInfo { int stage; double time; FlightEvent event; } - + } + From 65158bc06569cc7134e456e1d867612affc2150e Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Fri, 9 Jul 2021 03:58:57 +0200 Subject: [PATCH 2/3] Added plot data tooltip --- .../sf/openrocket/gui/plot/SimulationChart.java | 2 +- .../sf/openrocket/gui/plot/SimulationPlot.java | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationChart.java b/swing/src/net/sf/openrocket/gui/plot/SimulationChart.java index fa3932d5a..f7a19036a 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationChart.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationChart.java @@ -57,7 +57,7 @@ public class SimulationChart extends ChartPanel { /* tooltips */true); this.setMouseWheelEnabled(true); this.setEnforceFileExtensions(true); - this.setInitialDelay(500); + this.setInitialDelay(50); this.setBorder(BorderFactory.createLineBorder(Color.GRAY, 1)); } diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java index 299f62fa5..6db472325 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java @@ -13,6 +13,7 @@ import java.awt.Stroke; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -37,6 +38,7 @@ import org.jfree.chart.annotations.XYImageAnnotation; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.ValueAxis; import org.jfree.chart.block.BlockBorder; +import org.jfree.chart.labels.StandardXYToolTipGenerator; import org.jfree.chart.plot.DefaultDrawingSupplier; import org.jfree.chart.plot.Marker; import org.jfree.chart.plot.PlotOrientation; @@ -47,6 +49,7 @@ import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.title.LegendTitle; import org.jfree.chart.title.TextTitle; import org.jfree.data.Range; +import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.text.TextUtilities; @@ -145,6 +148,17 @@ public class SimulationPlot { throw new IllegalArgumentException("Domain axis type not specified."); } + // Custom tooltip generator + StandardXYToolTipGenerator tooltipGenerator = new StandardXYToolTipGenerator() { + final DecimalFormat f = new DecimalFormat("##.00"); + @Override + public String generateToolTip(XYDataset dataset, int series, int item) { + return String.format("Item: %d
Y: %s
X: %s", + item, f.format(dataset.getYValue(series, item)), + f.format(dataset.getXValue(series, item))); + } + }; + // Get plot length (ignore trailing NaN's) int typeCount = filled.getTypeCount(); @@ -262,6 +276,7 @@ public class SimulationPlot { plot.setDataset(axisno, data[i]); ModifiedXYItemRenderer r = new ModifiedXYItemRenderer(branchCount); renderers.add(r); + r.setBaseToolTipGenerator(tooltipGenerator); plot.setRenderer(axisno, r); r.setBaseShapesVisible(initialShowPoints); r.setBaseShapesFilled(true); From 97e2a3fa2a8c8e07243938a3d419ed5a4de355a7 Mon Sep 17 00:00:00 2001 From: Sibo Van Gool Date: Sat, 17 Jul 2021 12:57:06 +0200 Subject: [PATCH 3/3] Improved plot tooltip information --- .../openrocket/gui/plot/SimulationPlot.java | 67 ++++++++++++------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java b/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java index 6db472325..76bb6a75c 100644 --- a/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java +++ b/swing/src/net/sf/openrocket/gui/plot/SimulationPlot.java @@ -14,11 +14,9 @@ import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.simulation.SimulationPlotPanel; @@ -49,6 +47,7 @@ import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.chart.title.LegendTitle; import org.jfree.chart.title.TextTitle; import org.jfree.data.Range; +import org.jfree.data.xy.XYDataItem; import org.jfree.data.xy.XYDataset; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; @@ -148,17 +147,6 @@ public class SimulationPlot { throw new IllegalArgumentException("Domain axis type not specified."); } - // Custom tooltip generator - StandardXYToolTipGenerator tooltipGenerator = new StandardXYToolTipGenerator() { - final DecimalFormat f = new DecimalFormat("##.00"); - @Override - public String generateToolTip(XYDataset dataset, int series, int item) { - return String.format("Item: %d
Y: %s
X: %s", - item, f.format(dataset.getYValue(series, item)), - f.format(dataset.getXValue(series, item))); - } - }; - // Get plot length (ignore trailing NaN's) int typeCount = filled.getTypeCount(); @@ -248,6 +236,13 @@ public class SimulationPlot { plot.setDomainGridlinePaint(Color.lightGray); int axisno = 0; + Color[] colors = {new Color(0,114,189), // Colors for data lines + new Color(217,83,25), + new Color(237,177,32), + new Color(126,49,142), + new Color(119,172,48), + new Color(77,190,238), + new Color(162,20,47)}; for (int i = 0; i < 2; i++) { // Check whether axis has any data if (data[i].getSeriesCount() > 0) { @@ -265,14 +260,40 @@ public class SimulationPlot { plot.setDomainAxis(new PresetNumberAxis(domainMin, domainMax)); + // Custom tooltip generator + int finalAxisno = axisno; + StandardXYToolTipGenerator tooltipGenerator = new StandardXYToolTipGenerator() { + final DecimalFormat f = new DecimalFormat("0.00"); + @Override + public String generateToolTip(XYDataset dataset, int series, int item) { + XYSeries ser = data[finalAxisno].getSeries(series); + String name = ser.getDescription(); + // Extract the unit from the last part of the series description, between parenthesis + Matcher m = Pattern.compile(".*\\((.*?)\\)").matcher(name); + String unit_y = ""; + if (m.find()) { + unit_y = m.group(1); + } + String unit_x = domainUnit.getUnit(); + String ord_end = "th"; // Ordinal number ending (1'st', 2'nd'...) + if (item % 10 == 1) + ord_end = "st"; + else if (item % 10 == 2) + ord_end = "nd"; + else if (item % 10 == 3) + ord_end = "rd"; + return String.format("" + + "%s
" + + "Y: %s %s
" + + "X: %s %s
" + + "%d%s sample" + + "", + name, f.format(dataset.getYValue(series, item)), unit_y, + f.format(dataset.getXValue(series, item)), unit_x, item, ord_end); + } + }; + // Add data and map to the axis - Color[] colors = {new Color(0,114,189), // Colors for data lines - new Color(217,83,25), - new Color(237,177,32), - new Color(126,49,142), - new Color(119,172,48), - new Color(77,190,238), - new Color(162,20,47)}; plot.setDataset(axisno, data[i]); ModifiedXYItemRenderer r = new ModifiedXYItemRenderer(branchCount); renderers.add(r);