diff --git a/pom.xml b/pom.xml
index 2e6f74ea..3de60804 100644
--- a/pom.xml
+++ b/pom.xml
@@ -119,6 +119,11 @@ Wisconsin-Madison.
org.scijava
scijava-table
+
+ org.scijava
+ scijava-plot
+ 0.1.2-SNAPSHOT
+
org.scijava
scijava-ui-awt
@@ -144,6 +149,15 @@ Wisconsin-Madison.
jdatepicker
1.3.2
+
+ org.jfree
+ jfreechart
+
+
+ org.jfree
+ jfreesvg
+ 3.2
+
diff --git a/src/main/java/org/scijava/ui/swing/plot/converter/PlotToBufferedImageConverter.java b/src/main/java/org/scijava/ui/swing/plot/converter/PlotToBufferedImageConverter.java
new file mode 100644
index 00000000..305ae93c
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/plot/converter/PlotToBufferedImageConverter.java
@@ -0,0 +1,63 @@
+package org.scijava.ui.swing.plot.converter;
+
+import org.jfree.chart.JFreeChart;
+import org.scijava.Priority;
+import org.scijava.convert.AbstractConverter;
+import org.scijava.convert.ConversionRequest;
+import org.scijava.convert.ConvertService;
+import org.scijava.convert.Converter;
+import org.scijava.plot.Plot;
+import org.scijava.plugin.Parameter;
+import org.scijava.plugin.Plugin;
+
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+
+/**
+ * Converter plugin, that converts an {@link Plot} to {@link BufferedImage}.
+ *
+ * @author Matthias Arzt
+ * @see ConvertService
+ */
+@Plugin(type = Converter.class, priority = Priority.NORMAL_PRIORITY)
+public class PlotToBufferedImageConverter extends AbstractConverter
+{
+
+ @Parameter
+ ConvertService convertService;
+
+ @Override
+ public boolean canConvert(ConversionRequest request) {
+ return request.destClass().isAssignableFrom( BufferedImage.class ) &&
+ Plot.class.isAssignableFrom( request.sourceClass() ) &&
+ convertService.supports(new ConversionRequest(
+ request.sourceObject(), request.sourceType(), JFreeChart.class));
+ }
+
+ @Override
+ public T convert(Object o, Class aClass) {
+ if(o instanceof Plot && BufferedImage.class.equals(aClass)) {
+ @SuppressWarnings("unchecked")
+ T t = (T) toBufferedImage((Plot) o);
+ return t;
+ }
+ return null;
+ }
+
+ private BufferedImage toBufferedImage(Plot plot) {
+ BufferedImage image = new BufferedImage( plot.getPreferredWidth(), plot.getPreferredHeight(), BufferedImage.TYPE_INT_ARGB );
+ JFreeChart chart = convertService.convert(plot, JFreeChart.class);
+ chart.draw(image.createGraphics(), new Rectangle2D.Float(0, 0, image.getWidth(), image.getHeight()));
+ return image;
+ }
+
+ @Override
+ public Class getOutputType() {
+ return BufferedImage.class;
+ }
+
+ @Override
+ public Class getInputType() {
+ return Plot.class;
+ }
+}
diff --git a/src/main/java/org/scijava/ui/swing/plot/io/PlotToSvgIOPlugin.java b/src/main/java/org/scijava/ui/swing/plot/io/PlotToSvgIOPlugin.java
new file mode 100644
index 00000000..6273afca
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/plot/io/PlotToSvgIOPlugin.java
@@ -0,0 +1,64 @@
+package org.scijava.ui.swing.plot.io;
+
+import org.scijava.plot.Plot;
+import org.jfree.chart.JFreeChart;
+import org.jfree.graphics2d.svg.SVGGraphics2D;
+import org.jfree.graphics2d.svg.SVGUtils;
+import org.scijava.convert.ConvertService;
+import org.scijava.io.AbstractIOPlugin;
+import org.scijava.io.IOPlugin;
+import org.scijava.plugin.Parameter;
+import org.scijava.plugin.Plugin;
+
+import java.awt.*;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Plugin that can write {@link Plot} as SVG file.
+ *
+ * @author Matthias Arzt
+ */
+@Plugin(type = IOPlugin.class)
+public class PlotToSvgIOPlugin extends AbstractIOPlugin {
+
+ @Parameter
+ ConvertService convertService;
+
+ @Override
+ public boolean supportsOpen(String source) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsSave(String destination) {
+ return destination.endsWith(".svg");
+ }
+
+ @Override
+ public boolean supportsSave(Object data, String destination) {
+ return supportsSave(destination) &&
+ data instanceof Plot &&
+ convertService.supports(data, JFreeChart.class);
+ }
+
+ @Override
+ public Plot open(String source) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void save(Plot data, String destination) throws IOException {
+ if(!supportsSave(data, destination))
+ throw new IllegalArgumentException();
+ JFreeChart chart = convertService.convert(data, JFreeChart.class);
+ SVGGraphics2D g = new SVGGraphics2D(data.getPreferredWidth(), data.getPreferredWidth());
+ chart.draw(g, new Rectangle(0, 0, g.getWidth(), g.getHeight()));
+ SVGUtils.writeToSVG(new File(destination), g.getSVGElement());
+ }
+
+ @Override
+ public Class getDataType() {
+ return Plot.class;
+ }
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/SwingPlotDisplayPanel.java b/src/main/java/org/scijava/ui/swing/viewer/plot/SwingPlotDisplayPanel.java
new file mode 100644
index 00000000..ae1b0cbd
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/SwingPlotDisplayPanel.java
@@ -0,0 +1,121 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.JFreeChart;
+import org.scijava.convert.ConvertService;
+import org.scijava.plot.Plot;
+import org.scijava.ui.viewer.DisplayWindow;
+import org.scijava.ui.viewer.plot.PlotDisplay;
+import org.scijava.ui.viewer.plot.PlotDisplayPanel;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.Objects;
+
+/**
+ * A JFreeChart-driven display panel for {@link Plot}s.
+ *
+ * @author Curtis Rueden
+ */
+public class SwingPlotDisplayPanel extends JPanel implements PlotDisplayPanel
+{
+
+ // -- instance variables --
+
+ private final DisplayWindow window;
+ private final PlotDisplay display;
+ private final ConvertService convertService;
+ private Dimension prefferedSize;
+
+ // -- constructor --
+
+ public SwingPlotDisplayPanel(final PlotDisplay display,
+ final DisplayWindow window, final ConvertService convertService)
+ {
+ this.display = display;
+ this.window = window;
+ this.convertService = convertService;
+ setLayout(new BorderLayout());
+ initPreferredSize();
+ setupChart();
+ window.setContent(this);
+ }
+
+ private void initPreferredSize() {
+ Plot plot = display.get(0);
+ prefferedSize = new Dimension(plot.getPreferredWidth(), plot.getPreferredHeight());
+ }
+
+ private void setupChart() {
+ final JFreeChart chart = convertToJFreeChart(display.get(0));
+ add(new ChartPanel(chart));
+ }
+
+ private JFreeChart convertToJFreeChart(Plot plot) {
+ return Objects.requireNonNull(convertService.convert(plot, JFreeChart.class));
+ }
+
+ public static boolean supports(Plot abstractPlot, ConvertService convertService) {
+ return convertService.supports(abstractPlot, JFreeChart.class);
+ }
+
+ // -- PlotDisplayPanel methods --
+
+ @Override
+ public PlotDisplay getDisplay() {
+ return display;
+ }
+
+ // -- DisplayPanel methods --
+
+ @Override
+ public DisplayWindow getWindow() {
+ return window;
+ }
+
+ @Override
+ public void redoLayout() { }
+
+ @Override
+ public void setLabel(final String s) { }
+
+ @Override
+ public void redraw() { }
+
+ @Override
+ public Dimension getPreferredSize() {
+ return prefferedSize;
+ }
+
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/SwingPlotDisplayViewer.java b/src/main/java/org/scijava/ui/swing/viewer/plot/SwingPlotDisplayViewer.java
new file mode 100644
index 00000000..774bbcdd
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/SwingPlotDisplayViewer.java
@@ -0,0 +1,76 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.plot.Plot;
+import org.scijava.ui.viewer.plot.AbstractPlotDisplayViewer;
+import org.scijava.convert.ConvertService;
+import org.scijava.display.Display;
+import org.scijava.plugin.Parameter;
+import org.scijava.plugin.Plugin;
+import org.scijava.ui.UserInterface;
+import org.scijava.ui.swing.SwingUI;
+import org.scijava.ui.viewer.DisplayViewer;
+import org.scijava.ui.viewer.DisplayWindow;
+import org.scijava.ui.viewer.plot.PlotDisplay;
+
+/**
+ * A Swing {@link Plot} display viewer, which displays plots using JFreeChart.
+ *
+ * @author Curtis Rueden
+ */
+@Plugin(type = DisplayViewer.class)
+public class SwingPlotDisplayViewer extends AbstractPlotDisplayViewer {
+
+ @Parameter
+ ConvertService convertService;
+
+ @Override
+ public boolean isCompatible(final UserInterface ui) {
+ return ui instanceof SwingUI;
+ }
+
+ @Override
+ public boolean canView(final Display> d) {
+ if(! (d instanceof PlotDisplay ))
+ return false;
+ Plot plot = ((PlotDisplay) d).get(0);
+ return SwingPlotDisplayPanel.supports(plot, convertService);
+ }
+
+ @Override
+ public void view(final DisplayWindow w, final Display> d) {
+ super.view(w, d);
+ setPanel(new SwingPlotDisplayPanel(getDisplay(), w, convertService));
+ }
+
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtLineStyles.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtLineStyles.java
new file mode 100644
index 00000000..8584bfa2
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtLineStyles.java
@@ -0,0 +1,100 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot.jfreechart;
+
+import org.scijava.plot.LineStyle;
+
+import java.awt.*;
+
+/**
+ * @author Matthias Arzt
+ */
+
+class AwtLineStyles {
+
+ private final boolean visible;
+
+ private final BasicStroke stroke;
+
+ private AwtLineStyles(boolean visible, BasicStroke stroke) {
+ this.visible = visible;
+ this.stroke = stroke;
+ }
+
+ public boolean isVisible() {
+ return visible;
+ }
+
+ public BasicStroke getStroke() {
+ return stroke;
+ }
+
+ public static AwtLineStyles getInstance(LineStyle style) {
+ if(style != null)
+ switch (style) {
+ case SOLID:
+ return solid;
+ case DASH:
+ return dash;
+ case DOT:
+ return dot;
+ case NONE:
+ return none;
+ }
+ return solid;
+ }
+
+ // --- Helper Constants ---
+
+ private static AwtLineStyles solid = new AwtLineStyles(true, Strokes.solid);
+
+ private static AwtLineStyles dash = new AwtLineStyles(true, Strokes.dash);
+
+ private static AwtLineStyles dot = new AwtLineStyles(true, Strokes.dot);
+
+ private static AwtLineStyles none = new AwtLineStyles(false, Strokes.none);
+
+ static class Strokes {
+
+ private static BasicStroke solid = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
+
+ private static BasicStroke dash = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
+ .0f, new float[]{6.0f, 6.0f}, 0.0f);
+
+ private static BasicStroke dot = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
+ .0f, new float[]{0.6f, 4.0f}, 0.0f);
+
+ private static BasicStroke none = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND,
+ .0f, new float[]{0.0f, 100.0f}, 0.0f);
+ }
+
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtMarkerStyles.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtMarkerStyles.java
new file mode 100644
index 00000000..999ad568
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtMarkerStyles.java
@@ -0,0 +1,162 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot.jfreechart;
+
+import org.scijava.plot.MarkerStyle;
+
+import java.awt.*;
+import java.awt.geom.Ellipse2D;
+import java.awt.geom.Path2D;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * @author Matthias Arzt
+ */
+
+class AwtMarkerStyles {
+
+ private final boolean visible;
+
+ private final boolean filled;
+
+ private final Shape shape;
+
+ private AwtMarkerStyles(boolean visible, boolean filled, Shape shape) {
+ this.visible = visible;
+ this.filled = filled;
+ this.shape = shape;
+ }
+
+ public boolean isVisible() {
+ return visible;
+ }
+
+ public boolean isFilled() {
+ return filled;
+ }
+
+ public Shape getShape() {
+ return shape;
+ }
+
+ public static AwtMarkerStyles getInstance(MarkerStyle style) {
+ if(style != null)
+ switch (style) {
+ case NONE:
+ return none;
+ case PLUS:
+ return plus;
+ case X:
+ return x;
+ case STAR:
+ return star;
+ case SQUARE:
+ return square;
+ case FILLEDSQUARE:
+ return filledSquare;
+ case CIRCLE:
+ return circle;
+ case FILLEDCIRCLE:
+ return filledCircle;
+ }
+ return square;
+ }
+
+ // --- Helper Constants ---
+
+ private static AwtMarkerStyles none = new AwtMarkerStyles(false, false, null);
+
+ private static AwtMarkerStyles plus = new AwtMarkerStyles(true, false, Shapes.plus);
+
+ private static AwtMarkerStyles x = new AwtMarkerStyles(true, false, Shapes.x);
+
+ private static AwtMarkerStyles star = new AwtMarkerStyles(true, false, Shapes.star);
+
+ private static AwtMarkerStyles square = new AwtMarkerStyles(true, false, Shapes.square);
+
+ private static AwtMarkerStyles filledSquare = new AwtMarkerStyles(true, true, Shapes.square);
+
+ private static AwtMarkerStyles circle = new AwtMarkerStyles(true, false, Shapes.circle);
+
+ private static AwtMarkerStyles filledCircle = new AwtMarkerStyles(true, true, Shapes.circle);
+
+
+ static private class Shapes {
+
+ private static Shape x = getAwtXShape();
+
+ private static Shape plus = getAwtPlusShape();
+
+ private static Shape star = getAwtStarShape();
+
+ private static Shape square = new Rectangle2D.Double(-3.0, -3.0, 6.0, 6.0);
+
+ private static Shape circle = new Ellipse2D.Double(-3.0, -3.0, 6.0, 6.0);
+
+ private static Shape getAwtXShape() {
+ final Path2D p = new Path2D.Double();
+ final double s = 3.0;
+ p.moveTo(-s, -s);
+ p.lineTo(s, s);
+ p.moveTo(s, -s);
+ p.lineTo(-s, s);
+ return p;
+ }
+
+ private static Shape getAwtPlusShape() {
+ final Path2D p = new Path2D.Double();
+ final double t = 4.0;
+ p.moveTo(0, -t);
+ p.lineTo(0, t);
+ p.moveTo(t, 0);
+ p.lineTo(-t, 0);
+ return p;
+ }
+
+ private static Shape getAwtStarShape() {
+ final Path2D p = new Path2D.Double();
+ final double s = 3.0;
+ p.moveTo(-s, -s);
+ p.lineTo(s, s);
+ p.moveTo(s, -s);
+ p.lineTo(-s, s);
+ final double t = 4.0;
+ p.moveTo(0, -t);
+ p.lineTo(0, t);
+ p.moveTo(t, 0);
+ p.lineTo(-t, 0);
+ return p;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/CategoryChartConverter.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/CategoryChartConverter.java
new file mode 100644
index 00000000..a8211e82
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/CategoryChartConverter.java
@@ -0,0 +1,61 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot.jfreechart;
+
+import org.scijava.plot.CategoryChart;
+import org.jfree.chart.JFreeChart;
+import org.scijava.Priority;
+import org.scijava.convert.AbstractConverter;
+import org.scijava.convert.Converter;
+import org.scijava.plugin.Plugin;
+
+/**
+ * @author Matthias Arzt
+ */
+@Plugin(type = Converter.class, priority = Priority.NORMAL_PRIORITY)
+public class CategoryChartConverter extends AbstractConverter {
+ @SuppressWarnings("unchecked")
+ @Override
+ public T convert(Object o, Class aClass) {
+ return (T) CategoryChartGenerator.run((CategoryChart) o);
+ }
+
+ @Override
+ public Class getOutputType() {
+ return JFreeChart.class;
+ }
+
+ @Override
+ public Class getInputType() {
+ return CategoryChart.class;
+ }
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/CategoryChartGenerator.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/CategoryChartGenerator.java
new file mode 100644
index 00000000..0a0f0fb5
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/CategoryChartGenerator.java
@@ -0,0 +1,261 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot.jfreechart;
+
+import org.scijava.plot.BarSeries;
+import org.scijava.plot.BoxSeries;
+import org.scijava.plot.CategoryChart;
+import org.scijava.plot.CategoryChartItem;
+import org.scijava.plot.LineSeries;
+import org.scijava.plot.SeriesStyle;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.CategoryAxis;
+import org.jfree.chart.axis.CategoryLabelPositions;
+import org.jfree.chart.plot.CategoryPlot;
+import org.jfree.chart.renderer.category.*;
+import org.jfree.data.category.DefaultCategoryDataset;
+import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
+import org.scijava.ui.awt.AWTColors;
+import org.scijava.util.ColorRGB;
+
+import java.util.*;
+
+/**
+ * @author Matthias Arzt
+ */
+class CategoryChartGenerator {
+
+ private final CategoryChart chart;
+
+ private final Utils.SortedLabelFactory labelFactory = new Utils.SortedLabelFactory();
+
+ private final CategoryPlot jfcPlot = new CategoryPlot();
+
+ private final LineAndBarDataset lineData;
+
+ private final LineAndBarDataset barData;
+
+ private final BoxDataset boxData;
+
+ private CategoryChartGenerator(CategoryChart chart) {
+ this.chart = chart;
+ List categoryList = setupCategoryList();
+ lineData = new LineAndBarDataset(new LineAndShapeRenderer(), categoryList);
+ barData = new LineAndBarDataset(createFlatBarRenderer(), categoryList);
+ boxData = new BoxDataset(categoryList);
+ }
+
+ public static JFreeChart run(CategoryChart chart) {
+ return new CategoryChartGenerator(chart).getJFreeChart();
+ }
+
+ private List setupCategoryList() {
+ List> categories = chart.getCategories();
+ List categoryList = new ArrayList<>(categories.size());
+ Utils.SortedLabelFactory categoryFactory = new Utils.SortedLabelFactory();
+ for(Object category : categories)
+ categoryList.add(categoryFactory.newLabel(category));
+ return categoryList;
+ }
+
+ private JFreeChart getJFreeChart() {
+ jfcPlot.setDomainAxis(new CategoryAxis(chart.categoryAxis().getLabel()));
+ jfcPlot.getDomainAxis().setCategoryLabelPositions(CategoryLabelPositions.UP_45);
+ jfcPlot.setRangeAxis( Utils.getJFreeChartAxis(chart.numberAxis()));
+ processAllSeries();
+ lineData.addDatasetToPlot(0);
+ boxData.addDatasetToPlot(1);
+ barData.addDatasetToPlot(2);
+ return Utils.setupJFreeChart(chart.getTitle(), jfcPlot);
+ }
+
+ static private BarRenderer createFlatBarRenderer() {
+ BarRenderer jfcBarRenderer = new BarRenderer();
+ jfcBarRenderer.setBarPainter(new StandardBarPainter());
+ jfcBarRenderer.setShadowVisible(false);
+ return jfcBarRenderer;
+ }
+
+ private void processAllSeries() {
+ for(CategoryChartItem series : chart.getItems()) {
+ if(series instanceof BarSeries )
+ barData.addSeries((BarSeries) series);
+ if(series instanceof LineSeries )
+ lineData.addSeries((LineSeries) series);
+ if(series instanceof BoxSeries )
+ boxData.addBoxSeries((BoxSeries) series);
+ }
+ }
+
+ private class BoxDataset {
+
+ private final DefaultBoxAndWhiskerCategoryDataset jfcDataset;
+
+ private final BoxAndWhiskerRenderer jfcRenderer;
+
+ private final List categoryList;
+
+ BoxDataset(List categoryList) {
+ jfcDataset = new DefaultBoxAndWhiskerCategoryDataset();
+ jfcRenderer = new BoxAndWhiskerRenderer();
+ jfcRenderer.setFillBox(false);
+ this.categoryList = categoryList;
+ setCategories();
+ }
+
+ void addBoxSeries(BoxSeries series) {
+ Utils.SortedLabel uniqueLabel = labelFactory.newLabel(series.getLabel());
+ setSeriesData(uniqueLabel, series.getValues());
+ setSeriesVisibility(uniqueLabel, true, series.getLegendVisible());
+ setSeriesColor(uniqueLabel, series.getColor());
+ }
+
+
+ private void setCategories() {
+ Utils.SortedLabel uniqueLabel = labelFactory.newLabel("dummy");
+ for(Utils.SortedLabel category : categoryList)
+ jfcDataset.add(Collections.emptyList(), uniqueLabel, category);
+ setSeriesVisibility(uniqueLabel, false, false);
+ }
+
+ private void setSeriesData(Utils.SortedLabel uniqueLabel, Map, ? extends Collection> data) {
+ for(Utils.SortedLabel category : categoryList) {
+ Collection value = data.get(category.getLabel());
+ if(value != null)
+ jfcDataset.add(new ArrayList<>(value), uniqueLabel, category);
+ }
+ }
+
+ private void setSeriesColor(Utils.SortedLabel uniqueLabel, ColorRGB color) {
+ if(color == null)
+ return;
+ int index = jfcDataset.getRowIndex(uniqueLabel);
+ if(index < 0)
+ return;
+ jfcRenderer.setSeriesPaint(index, AWTColors.getColor(color));
+ }
+
+ private void setSeriesVisibility(Utils.SortedLabel uniqueLabel, boolean seriesVsisible, boolean legendVisible) {
+ int index = jfcDataset.getRowIndex(uniqueLabel);
+ if(index < 0)
+ return;
+ jfcRenderer.setSeriesVisible(index, seriesVsisible, false);
+ jfcRenderer.setSeriesVisibleInLegend(index, legendVisible, false);
+ }
+
+
+ void addDatasetToPlot(int datasetIndex) {
+ jfcPlot.setDataset(datasetIndex, jfcDataset);
+ jfcPlot.setRenderer(datasetIndex, jfcRenderer);
+ }
+
+ }
+
+
+ private class LineAndBarDataset {
+
+ private final DefaultCategoryDataset jfcDataset;
+
+ private final AbstractCategoryItemRenderer jfcRenderer;
+
+ private final List categoryList;
+
+ LineAndBarDataset(AbstractCategoryItemRenderer renderer, List categoryList) {
+ jfcDataset = new DefaultCategoryDataset();
+ jfcRenderer = renderer;
+ this.categoryList = categoryList;
+ setCategories();
+ }
+
+ private void setCategories() {
+ Utils.SortedLabel uniqueLabel = labelFactory.newLabel("dummy");
+ for(Utils.SortedLabel category : categoryList)
+ jfcDataset.addValue(0.0, uniqueLabel, category);
+ setSeriesVisibility(uniqueLabel, false, false);
+ }
+
+ void addSeries(BarSeries series) {
+ Utils.SortedLabel uniqueLabel = labelFactory.newLabel(series.getLabel());
+ addSeriesData(uniqueLabel, series.getValues());
+ setSeriesColor(uniqueLabel, series.getColor());
+ setSeriesVisibility(uniqueLabel, true, series.getLegendVisible());
+ }
+
+ void addSeries(LineSeries series) {
+ Utils.SortedLabel uniqueLabel = labelFactory.newLabel(series.getLabel());
+ addSeriesData(uniqueLabel, series.getValues());
+ setSeriesStyle(uniqueLabel, series.getStyle());
+ setSeriesVisibility(uniqueLabel, true, series.getLegendVisible());
+ }
+
+ private void setSeriesVisibility(Utils.SortedLabel uniqueLabel, boolean seriesVsisible, boolean legendVisible) {
+ int index = jfcDataset.getRowIndex(uniqueLabel);
+ if(index < 0)
+ return;
+ jfcRenderer.setSeriesVisible(index, seriesVsisible, false);
+ jfcRenderer.setSeriesVisibleInLegend(index, legendVisible, false);
+ }
+
+ private void addSeriesData(Utils.SortedLabel uniqueLabel, Map, Double> values) {
+ for(Utils.SortedLabel category : categoryList) {
+ Double value = values.get(category.getLabel());
+ if(value != null)
+ jfcDataset.addValue(value, uniqueLabel, category);
+ }
+ }
+
+ private void setSeriesStyle(Utils.SortedLabel uniqueLabel, SeriesStyle style) {
+ if(style == null)
+ return;
+ int index = jfcDataset.getRowIndex(uniqueLabel);
+ if(index < 0)
+ return;
+ RendererModifier.wrap(jfcRenderer).setSeriesStyle(index, style);
+ }
+
+ private void setSeriesColor(Utils.SortedLabel uniqueLabel, ColorRGB style) {
+ if(style == null)
+ return;
+ int index = jfcDataset.getRowIndex(uniqueLabel);
+ if(index < 0)
+ return;
+ RendererModifier.wrap(jfcRenderer).setSeriesColor(index, style);
+ }
+
+ void addDatasetToPlot(int datasetIndex) {
+ jfcPlot.setDataset(datasetIndex, jfcDataset);
+ jfcPlot.setRenderer(datasetIndex, jfcRenderer);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/RendererModifier.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/RendererModifier.java
new file mode 100644
index 00000000..31883cb0
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/RendererModifier.java
@@ -0,0 +1,106 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot.jfreechart;
+
+import org.scijava.plot.LineStyle;
+import org.scijava.plot.MarkerStyle;
+import org.scijava.plot.SeriesStyle;
+import org.jfree.chart.renderer.AbstractRenderer;
+import org.jfree.chart.renderer.category.LineAndShapeRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.scijava.ui.awt.AWTColors;
+import org.scijava.util.ColorRGB;
+
+/**
+ * @author Matthias Arzt
+ */
+class RendererModifier {
+
+ final AbstractRenderer renderer;
+
+ private RendererModifier(AbstractRenderer renderer) {
+ this.renderer = renderer;
+ }
+
+ static public RendererModifier wrap(AbstractRenderer renderer) {
+ return new RendererModifier(renderer);
+ }
+
+ public void setSeriesStyle(int index, SeriesStyle style) {
+ if(style == null)
+ return;
+ setSeriesColor(index, style.getColor());
+ setSeriesLineStyle(index, style.getLineStyle());
+ setSeriesMarkerStyle(index, style.getMarkerStyle());
+ }
+
+ public void setSeriesColor(int index, ColorRGB color) {
+ if (color == null)
+ return;
+ renderer.setSeriesPaint(index, AWTColors.getColor(color));
+ }
+
+ public void setSeriesLineStyle(int index, LineStyle style) {
+ AwtLineStyles line = AwtLineStyles.getInstance(style);
+ setSeriesLinesVisible(index, line.isVisible());
+ renderer.setSeriesStroke(index, line.getStroke());
+ }
+
+ public void setSeriesMarkerStyle(int index, MarkerStyle style) {
+ AwtMarkerStyles marker = AwtMarkerStyles.getInstance(style);
+ setSeriesShapesVisible(index, marker.isVisible());
+ setSeriesShapesFilled(index, marker.isFilled());
+ renderer.setSeriesShape(index, marker.getShape());
+ }
+
+ private void setSeriesLinesVisible(int index, boolean visible) {
+ if(renderer instanceof LineAndShapeRenderer)
+ ((LineAndShapeRenderer) renderer).setSeriesLinesVisible(index, visible);
+ if(renderer instanceof XYLineAndShapeRenderer)
+ ((XYLineAndShapeRenderer) renderer).setSeriesLinesVisible(index, visible);
+ }
+
+ private void setSeriesShapesVisible(int index, boolean visible) {
+ if(renderer instanceof LineAndShapeRenderer)
+ ((LineAndShapeRenderer) renderer).setSeriesShapesVisible(index, visible);
+ if(renderer instanceof XYLineAndShapeRenderer)
+ ((XYLineAndShapeRenderer) renderer).setSeriesShapesVisible(index, visible);
+ }
+
+ private void setSeriesShapesFilled(int index, boolean filled) {
+ if(renderer instanceof LineAndShapeRenderer)
+ ((LineAndShapeRenderer) renderer).setSeriesShapesFilled(index, filled);
+ if(renderer instanceof XYLineAndShapeRenderer)
+ ((XYLineAndShapeRenderer) renderer).setSeriesShapesFilled(index, filled);
+ }
+
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/Utils.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/Utils.java
new file mode 100644
index 00000000..3aeed9cf
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/Utils.java
@@ -0,0 +1,112 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot.jfreechart;
+
+import org.scijava.plot.NumberAxis;
+import org.scijava.plot.NumberAxis.RangeStrategy;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.LogAxis;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.block.BlockBorder;
+import org.jfree.chart.plot.Plot;
+
+import java.awt.*;
+import java.util.Objects;
+
+/**
+ * @author Matthias Arzt
+ */
+class Utils {
+
+ static JFreeChart setupJFreeChart(String title, Plot plot) {
+ JFreeChart chart = new JFreeChart(plot);
+ chart.setTitle(title);
+ chart.setBackgroundPaint(Color.WHITE);
+ chart.getLegend().setFrame(BlockBorder.NONE);
+ return chart;
+ }
+
+ static ValueAxis getJFreeChartAxis(NumberAxis v) {
+ if(v.isLogarithmic())
+ return getJFreeChartLogarithmicAxis(v);
+ else
+ return getJFreeCharLinearAxis(v);
+ }
+
+ static ValueAxis getJFreeChartLogarithmicAxis(NumberAxis v) {
+ LogAxis axis = new LogAxis(v.getLabel());
+ switch (v.getRangeStrategy()) {
+ case MANUAL:
+ axis.setRange(v.getMin(), v.getMax());
+ break;
+ default:
+ axis.setAutoRange(true);
+ }
+ return axis;
+ }
+
+ static ValueAxis getJFreeCharLinearAxis(NumberAxis v) {
+ org.jfree.chart.axis.NumberAxis axis = new org.jfree.chart.axis.NumberAxis(v.getLabel());
+ switch(v.getRangeStrategy()) {
+ case MANUAL:
+ axis.setRange(v.getMin(), v.getMax());
+ break;
+ case AUTO:
+ axis.setAutoRange(true);
+ axis.setAutoRangeIncludesZero(false);
+ break;
+ case AUTO_INCLUDE_ZERO:
+ axis.setAutoRange(true);
+ axis.setAutoRangeIncludesZero(true);
+ break;
+ default:
+ axis.setAutoRange(true);
+ }
+ return axis;
+ }
+
+ static class SortedLabelFactory {
+ private int n;
+ SortedLabelFactory() { n = 0; }
+ SortedLabel newLabel(Object label) { return new SortedLabel(n++, label); }
+ }
+
+ static class SortedLabel implements Comparable {
+ private final Object label;
+ private final int id;
+ SortedLabel(final int id, final Object label) { this.label = Objects.requireNonNull(label); this.id = id; }
+ @Override public String toString() { return label.toString(); }
+ @Override public int compareTo(SortedLabel o) { return Integer.compare(id, o.id); }
+ public Object getLabel() { return label; }
+ }
+
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/XYPlotConverter.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/XYPlotConverter.java
new file mode 100644
index 00000000..78787944
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/XYPlotConverter.java
@@ -0,0 +1,61 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot.jfreechart;
+
+import org.scijava.plot.XYPlot;
+import org.jfree.chart.JFreeChart;
+import org.scijava.Priority;
+import org.scijava.convert.AbstractConverter;
+import org.scijava.convert.Converter;
+import org.scijava.plugin.Plugin;
+
+/**
+ * @author Matthias.Arzt
+ */
+@Plugin(type = Converter.class, priority = Priority.NORMAL_PRIORITY)
+public class XYPlotConverter extends AbstractConverter {
+ @SuppressWarnings("unchecked")
+ @Override
+ public T convert(Object o, Class aClass) {
+ return (T) XYPlotGenerator.run((XYPlot) o);
+ }
+
+ @Override
+ public Class getOutputType() {
+ return JFreeChart.class;
+ }
+
+ @Override
+ public Class getInputType() {
+ return XYPlot.class;
+ }
+}
diff --git a/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/XYPlotGenerator.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/XYPlotGenerator.java
new file mode 100644
index 00000000..5d2eb19b
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/XYPlotGenerator.java
@@ -0,0 +1,107 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot.jfreechart;
+
+import org.scijava.plot.SeriesStyle;
+import org.scijava.plot.XYPlot;
+import org.scijava.plot.XYPlotItem;
+import org.scijava.plot.XYSeries;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import static org.scijava.ui.swing.viewer.plot.jfreechart.Utils.*;
+
+/**
+ * @author Matthias Arzt
+ */
+class XYPlotGenerator {
+
+ private final XYPlot xyPlot;
+
+ private final SortedLabelFactory sortedLabelFactory = new SortedLabelFactory();
+
+ private final org.jfree.chart.plot.XYPlot jfcPlot = new org.jfree.chart.plot.XYPlot();
+
+ private final XYSeriesCollection jfcDataSet = new XYSeriesCollection();
+
+ private final XYLineAndShapeRenderer jfcRenderer = new XYLineAndShapeRenderer();
+
+ private XYPlotGenerator(XYPlot xyPlot) {
+ this.xyPlot = xyPlot;
+ }
+
+ public static JFreeChart run(XYPlot xyPlot) { return new XYPlotGenerator(xyPlot).getJFreeChart();
+ }
+
+ private JFreeChart getJFreeChart() {
+ jfcPlot.setDataset(jfcDataSet);
+ jfcPlot.setDomainAxis(getJFreeChartAxis(xyPlot.xAxis()));
+ jfcPlot.setRangeAxis(getJFreeChartAxis(xyPlot.yAxis()));
+ jfcPlot.setRenderer(jfcRenderer);
+ addAllSeries();
+ return Utils.setupJFreeChart(xyPlot.getTitle(), jfcPlot);
+ }
+
+ private void addAllSeries() {
+ for(XYPlotItem series : xyPlot.getItems())
+ if(series instanceof XYSeries )
+ addSeries((XYSeries) series);
+ }
+
+ private void addSeries(XYSeries series) {
+ SortedLabel uniqueLabel = sortedLabelFactory.newLabel(series.getLabel());
+ addSeriesData(uniqueLabel, series.getXValues(), series.getYValues());
+ setSeriesStyle(uniqueLabel, series.getStyle(), series.getLegendVisible());
+ }
+
+ private void addSeriesData(SortedLabel uniqueLabel, Collection xs, Collection ys) {
+ org.jfree.data.xy.XYSeries series = new org.jfree.data.xy.XYSeries(uniqueLabel, false, true);
+ Iterator xi = xs.iterator();
+ Iterator yi = ys.iterator();
+ while (xi.hasNext() && yi.hasNext())
+ series.add(xi.next(), yi.next());
+ jfcDataSet.addSeries(series);
+ }
+
+ private void setSeriesStyle(SortedLabel label, SeriesStyle style, boolean legendVisible) {
+ if (style == null)
+ return;
+ int index = jfcDataSet.getSeriesIndex(label);
+ RendererModifier.wrap(jfcRenderer).setSeriesStyle(index, style);
+ jfcRenderer.setSeriesVisibleInLegend(index, legendVisible);
+ }
+
+}
diff --git a/src/test/java/org/scijava/ui/swing/plot/converter/PlotToBufferedImageConverterTest.java b/src/test/java/org/scijava/ui/swing/plot/converter/PlotToBufferedImageConverterTest.java
new file mode 100644
index 00000000..fe878138
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/plot/converter/PlotToBufferedImageConverterTest.java
@@ -0,0 +1,31 @@
+package org.scijava.ui.swing.plot.converter;
+
+import org.junit.Test;
+import org.scijava.Context;
+import org.scijava.convert.ConvertService;
+import org.scijava.plot.PlotService;
+import org.scijava.plot.XYPlot;
+
+import java.awt.image.BufferedImage;
+
+import static org.junit.Assert.assertEquals;
+
+public class PlotToBufferedImageConverterTest
+{
+
+ @Test
+ public void test() {
+ // setup
+ Context context = new Context( PlotService.class, ConvertService.class );
+ PlotService plotService = context.service( PlotService.class );
+ ConvertService convertService = context.service( ConvertService.class );
+ XYPlot plot = plotService.newXYPlot();
+ // process
+ BufferedImage image = convertService.convert( plot, BufferedImage.class );
+ // test
+ assertEquals(plot.getPreferredWidth(), image.getWidth());
+ assertEquals(plot.getPreferredHeight(), image.getHeight());
+ // dispose
+ context.dispose();
+ }
+}
diff --git a/src/test/java/org/scijava/ui/swing/plot/io/PlotToSvgDemo.java b/src/test/java/org/scijava/ui/swing/plot/io/PlotToSvgDemo.java
new file mode 100644
index 00000000..8eef5f6f
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/plot/io/PlotToSvgDemo.java
@@ -0,0 +1,54 @@
+package org.scijava.ui.swing.plot.io;
+
+import org.scijava.Context;
+import org.scijava.io.IOService;
+import org.scijava.plot.Plot;
+import org.scijava.plot.PlotService;
+import org.scijava.plot.XYPlot;
+import org.scijava.plot.XYSeries;
+import org.scijava.plugin.Parameter;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * @author
+ */
+public class PlotToSvgDemo {
+
+ @Parameter
+ private PlotService plotService;
+
+ @Parameter
+ private IOService ioService;
+
+ public static void main(String... args) throws IOException {
+ PlotToSvgDemo demo = new PlotToSvgDemo();
+ new Context().inject(demo);
+ demo.run();
+ }
+
+ private void run() throws IOException {
+ Path path = Paths.get(System.getProperty("user.home"), "chart.svg");
+ Plot plot = getExamplePlot();
+ ioService.save(plot, path.toString());
+ System.out.println("Plot saved as " + path.toString());
+ }
+
+ private Plot getExamplePlot() {
+ XYPlot plot = plotService.newXYPlot();
+ plot.setTitle("Hello World!");
+ plot.xAxis().setLabel("x");
+ plot.yAxis().setLabel("y");
+ List xs = IntStream.rangeClosed(0, 100).mapToObj(x -> (double) x * 2. * Math.PI / 100.).collect(Collectors.toList());
+ List ys = xs.stream().map(Math::sin).collect(Collectors.toList());
+ XYSeries series = plot.addXYSeries();
+ series.setLabel("y = sin(x)");
+ series.setValues( xs, ys );
+ return plot;
+ }
+}
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/AllDemos.java b/src/test/java/org/scijava/ui/swing/viewer/plot/AllDemos.java
new file mode 100644
index 00000000..1635d2d3
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/AllDemos.java
@@ -0,0 +1,49 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+/**
+ * @author Matthias Arzt
+ */
+public class AllDemos {
+
+ public static void main(final String... args) {
+ new BoxPlotDemo().run();
+ new CategoryChartDemo().run();
+ new LineStyleDemo().run();
+ new LogarithmicAxisDemo().run();
+ new MarkerStyleDemo().run();
+ new SortingCategoriesDemo().run();
+ new XYPlotDemo().run();
+ }
+
+}
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/BoxPlotDemo.java b/src/test/java/org/scijava/ui/swing/viewer/plot/BoxPlotDemo.java
new file mode 100644
index 00000000..b826ab2b
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/BoxPlotDemo.java
@@ -0,0 +1,82 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.plot.BoxSeries;
+import org.scijava.plot.CategoryChart;
+import org.scijava.util.Colors;
+
+import java.util.*;
+
+/**
+ * @author Matthias Arzt
+ */
+class BoxPlotDemo extends ChartDemo{
+
+ public void run() {
+ CategoryChart chart = plotService.newCategoryChart();
+
+ Map> randomData1 = new TreeMap<>();
+ randomData1.put("A", collectionOfRandomNumbers(10));
+ randomData1.put("B", collectionOfRandomNumbers(20));
+ randomData1.put("C", collectionOfRandomNumbers(30));
+
+ BoxSeries boxSeries1 = chart.addBoxSeries();
+ boxSeries1.setLabel("boxes1");
+ boxSeries1.setValues(randomData1);
+ boxSeries1.setColor(Colors.CYAN);
+
+ Map> randomData2 = new TreeMap<>();
+ randomData2.put("A", collectionOfRandomNumbers(10));
+ randomData2.put("B", collectionOfRandomNumbers(20));
+ randomData2.put("C", collectionOfRandomNumbers(30));
+
+ BoxSeries boxSeries2 = chart.addBoxSeries();
+ boxSeries2.setLabel("boxes2");
+ boxSeries2.setValues(randomData2);
+ boxSeries2.setColor(Colors.BLACK);
+
+ ui.show(chart);
+ }
+
+ private static Collection collectionOfRandomNumbers(int size) {
+ Random rand = new Random();
+ Vector result = new Vector<>(size);
+ for(int i = 0; i < size; i++)
+ result.add(rand.nextGaussian()*20);
+ return result;
+ }
+
+ public static void main(final String... args) {
+ new BoxPlotDemo().run();
+ }
+}
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/CategoryChartDemo.java b/src/test/java/org/scijava/ui/swing/viewer/plot/CategoryChartDemo.java
new file mode 100644
index 00000000..8c643cb9
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/CategoryChartDemo.java
@@ -0,0 +1,76 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.plot.BarSeries;
+import org.scijava.plot.CategoryChart;
+import org.scijava.plot.LineSeries;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Matthias Arzt
+ */
+class CategoryChartDemo extends ChartDemo{
+
+ public void run() {
+
+ CategoryChart chart = plotService.newCategoryChart();
+ chart.categoryAxis().setManualCategories(Arrays.asList("one wheel", "bicycle", "car"));
+
+ Map wheelsData = new HashMap<>();
+ wheelsData.put("one wheel", 1.0);
+ wheelsData.put("bicycle", 2.0);
+ wheelsData.put("car", 4.0);
+
+ LineSeries lineSeries = chart.addLineSeries();
+ lineSeries.setLabel("wheels");
+ lineSeries.setValues(wheelsData);
+
+ Map speedData = new HashMap<>();
+ speedData.put("one wheel", 10.0);
+ speedData.put("bicycle", 30.0);
+ speedData.put("car", 200.0);
+
+ BarSeries barSeries = chart.addBarSeries();
+ barSeries.setLabel("speed");
+ barSeries.setValues(speedData);
+
+ ui.show(chart);
+ }
+
+ public static void main(final String... args) {
+ new CategoryChartDemo().run();
+ }
+}
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/ChartDemo.java b/src/test/java/org/scijava/ui/swing/viewer/plot/ChartDemo.java
new file mode 100644
index 00000000..4d044d84
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/ChartDemo.java
@@ -0,0 +1,54 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.Context;
+import org.scijava.plot.PlotService;
+import org.scijava.plugin.Parameter;
+import org.scijava.ui.UIService;
+
+/**
+ * @author Matthias Arzt
+ */
+class ChartDemo {
+
+ @Parameter
+ public UIService ui;
+
+ @Parameter
+ public PlotService plotService;
+
+ ChartDemo() {
+ new Context().inject( this );
+ }
+
+}
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/LineStyleDemo.java b/src/test/java/org/scijava/ui/swing/viewer/plot/LineStyleDemo.java
new file mode 100644
index 00000000..71a87671
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/LineStyleDemo.java
@@ -0,0 +1,73 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.plot.LineStyle;
+import org.scijava.plot.MarkerStyle;
+import org.scijava.plot.XYPlot;
+import org.scijava.plot.XYSeries;
+import org.scijava.util.Colors;
+
+import java.util.Arrays;
+
+/**
+ * @author Matthias Arzt
+ */
+class LineStyleDemo extends ChartDemo {
+
+
+ public void run() {
+ LineStyle[] lineStyles = LineStyle.values();
+
+ XYPlot plot = plotService.newXYPlot();
+ plot.setTitle("Line Styles");
+ plot.xAxis().setManualRange(-1.0, 2.0);
+ plot.yAxis().setManualRange(-1.0, (double) lineStyles.length);
+
+ for(int i = 0; i < lineStyles.length; i++)
+ addSeries(plot, i, lineStyles[i]);
+
+ ui.show(plot);
+ }
+
+ private void addSeries(XYPlot plot, double y, LineStyle lineStyle) {
+ XYSeries series = plot.addXYSeries();
+ series.setLabel(lineStyle.toString());
+ series.setValues(Arrays.asList(0.0,1.0), Arrays.asList(y,y));
+ series.setStyle(plotService.newSeriesStyle(Colors.BLACK, lineStyle, MarkerStyle.CIRCLE));
+ }
+
+ public static void main(final String... args) {
+ new LineStyleDemo().run();
+ }
+
+}
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/LogarithmicAxisDemo.java b/src/test/java/org/scijava/ui/swing/viewer/plot/LogarithmicAxisDemo.java
new file mode 100644
index 00000000..6143427b
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/LogarithmicAxisDemo.java
@@ -0,0 +1,70 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.plot.XYPlot;
+import org.scijava.plot.XYSeries;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Matthias Arzt
+ */
+class LogarithmicAxisDemo extends ChartDemo {
+
+ public void run() {
+
+ XYPlot plot = plotService.newXYPlot();
+ plot.setTitle("Logarithmic");
+ plot.xAxis().setAutoRange();
+ plot.yAxis().setAutoRange();
+ plot.yAxis().setLogarithmic(true);
+
+ List xs = new ArrayList<>();
+ List ys = new ArrayList<>();
+ for(double x = 0; x < 10; x += 0.1) {
+ xs.add(x);
+ ys.add(Math.exp(Math.sin(x)));
+ }
+
+ XYSeries series = plot.addXYSeries();
+ series.setLabel("exp(sin(x))");
+ series.setValues(xs, ys);
+
+ ui.show(plot);
+ }
+
+ public static void main(final String... args) {
+ new LogarithmicAxisDemo().run();
+ }
+}
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/MarkerStyleDemo.java b/src/test/java/org/scijava/ui/swing/viewer/plot/MarkerStyleDemo.java
new file mode 100644
index 00000000..9a2c0be8
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/MarkerStyleDemo.java
@@ -0,0 +1,71 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.plot.MarkerStyle;
+import org.scijava.plot.XYPlot;
+import org.scijava.plot.XYSeries;
+
+import java.util.Arrays;
+
+/**
+ * @author Matthias Arzt
+ */
+class MarkerStyleDemo extends ChartDemo {
+
+ public void run() {
+ MarkerStyle[] markerStyles = MarkerStyle.values();
+
+ XYPlot plot = plotService.newXYPlot();
+ plot.setTitle("Marker Styles");
+ plot.xAxis().setManualRange(-1.0, 2.0);
+ plot.yAxis().setManualRange(-1.0, (double) markerStyles.length);
+
+ for(int i = 0; i < markerStyles.length; i++)
+ addSeries(plot, i, markerStyles[i]);
+
+ ui.show(plot);
+ }
+
+ private void addSeries(XYPlot plot, double y, MarkerStyle markerStyle) {
+ XYSeries series = plot.addXYSeries();
+ series.setLabel(markerStyle.toString());
+ series.setValues(Arrays.asList(0.0, 1.0), Arrays.asList(y,y));
+ series.setStyle(plotService.newSeriesStyle(null, null, markerStyle));
+ }
+
+ public static void main(final String... args) {
+ new MarkerStyleDemo().run();
+ }
+
+}
+
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/SortingCategoriesDemo.java b/src/test/java/org/scijava/ui/swing/viewer/plot/SortingCategoriesDemo.java
new file mode 100644
index 00000000..c60efa77
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/SortingCategoriesDemo.java
@@ -0,0 +1,94 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.plot.BarSeries;
+import org.scijava.plot.CategoryAxis;
+import org.scijava.plot.CategoryChart;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * @author Matthias Arzt
+ */
+
+class SortingCategoriesDemo extends ChartDemo {
+
+ public void run() {
+ showSortedCategoryChart( axis -> {
+ axis.setManualCategories(Arrays.asList("a","c","b"));
+ axis.setLabel("acb");
+ } );
+ showSortedCategoryChart( axis -> {
+ axis.setManualCategories(Arrays.asList("a","g","c","b"));
+ axis.setLabel("agcb");
+ } );
+ showSortedCategoryChart( axis -> {
+ axis.setManualCategories(Arrays.asList("d","c","a","b"));
+ axis.setOrder( String::compareTo );
+ axis.setLabel("abcd");
+ } );
+ showSortedCategoryChart( axis -> {
+ axis.setManualCategories(Collections.emptyList());
+ axis.setOrder( String::compareTo );
+ axis.setLabel("empty");
+ } );
+ }
+
+ private interface AxisManipulator {
+ void manipulate( CategoryAxis axis );
+ }
+
+ private void showSortedCategoryChart(AxisManipulator categoryAxisManipulator) {
+ CategoryChart chart = plotService.newCategoryChart();
+ categoryAxisManipulator.manipulate(chart.categoryAxis());
+
+ Map data = new TreeMap<>();
+ data.put("a", 1.0);
+ data.put("b", 2.0);
+ data.put("c", 3.0);
+ data.put("d", 4.0);
+
+ BarSeries bars = chart.addBarSeries();
+ bars.setValues(data);
+
+ ui.show(chart);
+ }
+
+ public static void main(final String... args) {
+ new SortingCategoriesDemo().run();
+ }
+
+}
diff --git a/src/test/java/org/scijava/ui/swing/viewer/plot/XYPlotDemo.java b/src/test/java/org/scijava/ui/swing/viewer/plot/XYPlotDemo.java
new file mode 100644
index 00000000..08453c23
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/viewer/plot/XYPlotDemo.java
@@ -0,0 +1,69 @@
+/*
+ * #%L
+ * ImageJ software for multidimensional image processing and analysis.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.viewer.plot;
+
+import org.scijava.plot.XYPlot;
+import org.scijava.plot.XYSeries;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Matthias Arzt
+ */
+class XYPlotDemo extends ChartDemo {
+
+ public void run() {
+ XYPlot plot = plotService.newXYPlot();
+ plot.setTitle("A series forming a circle.");
+ plot.xAxis().setAutoRange();
+ plot.yAxis().setAutoRange();
+ plot.setPreferredSize(400, 400);
+
+ List xs = new ArrayList<>();
+ List ys = new ArrayList<>();
+ for(double t = 0; t < 2 * Math.PI; t += 0.1) {
+ xs.add(Math.sin(t));
+ ys.add(Math.cos(t));
+ }
+
+ XYSeries series = plot.addXYSeries();
+ series.setLabel("circle");
+ series.setValues(xs, ys);
+
+ ui.show(plot);
+ }
+
+ public static void main(final String... args) {
+ new XYPlotDemo().run();
+ }
+}