From 59ed4b6493906ab721ff9bd9a9c7fecb86a24139 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Tue, 18 Dec 2018 18:04:29 +0100 Subject: [PATCH 1/6] WIP: depend on SNAPSHOT version of scijava-plot --- pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pom.xml b/pom.xml index 2e6f74e..3adfad2 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 From f5cec9695079e174facb98bb09b8831aa81e4f48 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Tue, 18 Dec 2018 18:07:37 +0100 Subject: [PATCH 2/6] Add converters XYPlot, CategoryChart -> JFreeChart --- pom.xml | 9 + .../viewer/plot/jfreechart/AwtLineStyles.java | 100 +++++++ .../plot/jfreechart/AwtMarkerStyles.java | 162 +++++++++++ .../jfreechart/CategoryChartConverter.java | 61 ++++ .../jfreechart/CategoryChartGenerator.java | 261 ++++++++++++++++++ .../plot/jfreechart/RendererModifier.java | 106 +++++++ .../swing/viewer/plot/jfreechart/Utils.java | 112 ++++++++ .../plot/jfreechart/XYPlotConverter.java | 61 ++++ .../plot/jfreechart/XYPlotGenerator.java | 107 +++++++ 9 files changed, 979 insertions(+) create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtLineStyles.java create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtMarkerStyles.java create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/CategoryChartConverter.java create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/CategoryChartGenerator.java create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/RendererModifier.java create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/Utils.java create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/XYPlotConverter.java create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/XYPlotGenerator.java diff --git a/pom.xml b/pom.xml index 3adfad2..3de6080 100644 --- a/pom.xml +++ b/pom.xml @@ -149,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/viewer/plot/jfreechart/AwtLineStyles.java b/src/main/java/org/scijava/ui/swing/viewer/plot/jfreechart/AwtLineStyles.java new file mode 100644 index 0000000..8584bfa --- /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 0000000..999ad56 --- /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 0000000..a8211e8 --- /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 0000000..0a0f0fb --- /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> 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 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 0000000..31883cb --- /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 0000000..3aeed9c --- /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 0000000..7878794 --- /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 0000000..5d2eb19 --- /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); + } + +} From 7f32a03c36710d56b2aae19f0cca3a866e989822 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Tue, 18 Dec 2018 18:09:40 +0100 Subject: [PATCH 3/6] Add viewer to display plots They depend on converters Plot -> JFreeChart, and the ChartPanel which is part of the JFreeChart library. --- .../viewer/plot/SwingPlotDisplayPanel.java | 121 ++++++++++++++++++ .../viewer/plot/SwingPlotDisplayViewer.java | 76 +++++++++++ 2 files changed, 197 insertions(+) create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/SwingPlotDisplayPanel.java create mode 100644 src/main/java/org/scijava/ui/swing/viewer/plot/SwingPlotDisplayViewer.java 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 0000000..ae1b0cb --- /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 0000000..774bbcd --- /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)); + } + +} From c9e8b028d3a3925c84393ec084a53382d776b9aa Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Tue, 18 Dec 2018 18:10:18 +0100 Subject: [PATCH 4/6] Add demos on how to display plots --- .../ui/swing/viewer/plot/AllDemos.java | 49 ++++++++++ .../ui/swing/viewer/plot/BoxPlotDemo.java | 82 ++++++++++++++++ .../swing/viewer/plot/CategoryChartDemo.java | 76 +++++++++++++++ .../ui/swing/viewer/plot/ChartDemo.java | 54 +++++++++++ .../ui/swing/viewer/plot/LineStyleDemo.java | 73 ++++++++++++++ .../viewer/plot/LogarithmicAxisDemo.java | 70 ++++++++++++++ .../ui/swing/viewer/plot/MarkerStyleDemo.java | 71 ++++++++++++++ .../viewer/plot/SortingCategoriesDemo.java | 94 +++++++++++++++++++ .../ui/swing/viewer/plot/XYPlotDemo.java | 69 ++++++++++++++ 9 files changed, 638 insertions(+) create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/AllDemos.java create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/BoxPlotDemo.java create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/CategoryChartDemo.java create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/ChartDemo.java create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/LineStyleDemo.java create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/LogarithmicAxisDemo.java create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/MarkerStyleDemo.java create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/SortingCategoriesDemo.java create mode 100644 src/test/java/org/scijava/ui/swing/viewer/plot/XYPlotDemo.java 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 0000000..1635d2d --- /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 0000000..b826ab2 --- /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 0000000..8c643cb --- /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 0000000..4d044d8 --- /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 0000000..71a8767 --- /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 0000000..6143427 --- /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 0000000..9a2c0be --- /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 0000000..c60efa7 --- /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 0000000..08453c2 --- /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(); + } +} From 971edd6c5208501df5e3b3e19e30fd5387be40d2 Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Tue, 18 Dec 2018 18:13:33 +0100 Subject: [PATCH 5/6] Add PlotToSvgIOPlugin This allows to store a plot as SVG with the SciJava IOService. --- .../ui/swing/plot/io/PlotToSvgIOPlugin.java | 64 +++++++++++++++++++ .../ui/swing/plot/io/PlotToSvgDemo.java | 54 ++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 src/main/java/org/scijava/ui/swing/plot/io/PlotToSvgIOPlugin.java create mode 100644 src/test/java/org/scijava/ui/swing/plot/io/PlotToSvgDemo.java 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 0000000..6273afc --- /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/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 0000000..8eef5f6 --- /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; + } +} From 18ecd192de2d6497b2a4348316ee58bc5ef75e1a Mon Sep 17 00:00:00 2001 From: Matthias Arzt Date: Tue, 18 Dec 2018 18:17:55 +0100 Subject: [PATCH 6/6] Add convert Plot -> BufferedImage The converter is based on converters Plot -> JFreeChart --- .../PlotToBufferedImageConverter.java | 63 +++++++++++++++++++ .../PlotToBufferedImageConverterTest.java | 31 +++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/main/java/org/scijava/ui/swing/plot/converter/PlotToBufferedImageConverter.java create mode 100644 src/test/java/org/scijava/ui/swing/plot/converter/PlotToBufferedImageConverterTest.java 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 0000000..305ae93 --- /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/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 0000000..fe87813 --- /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(); + } +}