Skip to content

Commit 979f553

Browse files
author
Stefan Hahmann
committed
Add UI elements to allow the user to interactively change colors, visibilities and sliding average window sizes
1 parent 5d2795a commit 979f553

1 file changed

Lines changed: 180 additions & 25 deletions

File tree

src/main/java/org/mastodon/mamut/tomancak/divisioncount/SpotAndDivisionCountChart.java

Lines changed: 180 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -29,44 +29,65 @@
2929
package org.mastodon.mamut.tomancak.divisioncount;
3030

3131
import java.awt.BasicStroke;
32+
import java.awt.BorderLayout;
3233
import java.awt.Color;
3334
import java.awt.Dimension;
3435
import java.awt.Font;
3536
import java.awt.Shape;
3637
import java.awt.geom.Ellipse2D;
3738
import java.awt.geom.Rectangle2D;
3839

40+
import javax.swing.JButton;
41+
import javax.swing.JCheckBox;
42+
import javax.swing.JColorChooser;
3943
import javax.swing.JFrame;
44+
import javax.swing.JLabel;
45+
import javax.swing.JPanel;
46+
import javax.swing.JSpinner;
47+
import javax.swing.SpinnerNumberModel;
4048

49+
import net.miginfocom.swing.MigLayout;
4150
import org.jfree.chart.ChartFactory;
4251
import org.jfree.chart.ChartPanel;
4352
import org.jfree.chart.JFreeChart;
4453
import org.jfree.chart.axis.NumberAxis;
4554
import org.jfree.chart.plot.PlotOrientation;
4655
import org.jfree.chart.plot.XYPlot;
56+
import org.jfree.chart.renderer.xy.XYItemRenderer;
4757
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
4858
import org.jfree.data.xy.XYSeries;
4959
import org.jfree.data.xy.XYSeriesCollection;
5060

5161
class SpotAndDivisionCountChart extends JFrame
5262
{
53-
private final static Color DIVISION_COUNT_COLOR = new Color( 86, 180, 233 ); // Light Blue
63+
private Color divisionCountColor = new Color( 86, 180, 233 ); // Light Blue
5464

55-
private final static Color SPOT_COUNT_COLOR = new Color( 230, 159, 0 ); // Dark Orange
65+
private Color spotCountColor = new Color( 230, 159, 0 ); // Dark Orange
5666

5767
private final static String TITLE = "Spot and Division Counts over Time";
58-
5968
private final static String SPOTS_COUNT_SERIES_NAME = "Spot Counts";
60-
6169
private final static String DIVISION_COUNT_SERIES_NAME = "Division Counts";
6270

71+
private final XYPlot plot;
72+
73+
private final NumberAxis leftAxis;
74+
75+
private final NumberAxis rightAxis;
76+
77+
private int spotWindowSize;
78+
79+
private int divisionWindowSize;
80+
6381
SpotAndDivisionCountChart( double[] timepoints, double[] spotCounts, double[] divisionCounts, int windowSize )
6482
{
83+
this.spotWindowSize = windowSize;
84+
this.divisionWindowSize = windowSize;
85+
6586
XYSeriesCollection spotCountsSeries = createSeries(
66-
timepoints, spotCounts, SPOTS_COUNT_SERIES_NAME, windowSize );
87+
timepoints, spotCounts, SPOTS_COUNT_SERIES_NAME, spotWindowSize );
6788

6889
XYSeriesCollection divisionCountsSeries = createSeries(
69-
timepoints, divisionCounts, DIVISION_COUNT_SERIES_NAME, windowSize );
90+
timepoints, divisionCounts, DIVISION_COUNT_SERIES_NAME, divisionWindowSize );
7091

7192
JFreeChart chart = ChartFactory.createXYLineChart(
7293
TITLE,
@@ -79,49 +100,176 @@ class SpotAndDivisionCountChart extends JFrame
79100
false
80101
);
81102

82-
XYPlot plot = chart.getXYPlot();
103+
plot = chart.getXYPlot();
83104

84105
// Customize the left y-axis (Spot Counts)
85-
NumberAxis leftAxis = ( NumberAxis ) plot.getRangeAxis();
86-
customizeAxis( leftAxis, SPOTS_COUNT_SERIES_NAME, SPOT_COUNT_COLOR );
106+
leftAxis = ( NumberAxis ) plot.getRangeAxis();
107+
customizeAxis( leftAxis, SPOTS_COUNT_SERIES_NAME, spotCountColor );
87108
plot.setRangeAxis( 0, leftAxis );
88109
plot.setDataset( 0, spotCountsSeries );
89110
plot.mapDatasetToRangeAxis( 0, 0 );
90111

91112
// Add the right y-axis (Division Counts)
92-
NumberAxis rightAxis = new NumberAxis();
93-
customizeAxis( rightAxis, DIVISION_COUNT_SERIES_NAME, DIVISION_COUNT_COLOR );
113+
rightAxis = new NumberAxis();
114+
customizeAxis( rightAxis, DIVISION_COUNT_SERIES_NAME, divisionCountColor );
94115
plot.setRangeAxis( 1, rightAxis );
95116
plot.setDataset( 1, divisionCountsSeries );
96117
plot.mapDatasetToRangeAxis( 1, 1 );
97118

98119
XYLineAndShapeRenderer spotCountRenderer = createRenderer(
99-
SPOT_COUNT_COLOR, new Rectangle2D.Double( -2, -2, 4, 4 ) );
120+
spotCountColor, new Rectangle2D.Double( -2, -2, 4, 4 ) );
100121
plot.setRenderer( 0, spotCountRenderer );
101122
XYLineAndShapeRenderer divisionCountRenderer = createRenderer(
102-
DIVISION_COUNT_COLOR, new Ellipse2D.Double( -2, -2, 4, 4 ) );
123+
divisionCountColor, new Ellipse2D.Double( -2, -2, 4, 4 ) );
103124
plot.setRenderer( 1, divisionCountRenderer );
104125

105126
// Set up the chart panel
106127
ChartPanel chartPanel = new ChartPanel( chart );
107128
chartPanel.setPreferredSize( new Dimension( 800, 600 ) );
108-
setContentPane( chartPanel );
109-
setChartColors( plot );
129+
130+
// Add color chooser and visibility controls
131+
JPanel controlPanel = createControlPanel( timepoints, spotCounts, divisionCounts );
132+
133+
// Set up the frame layout
134+
setLayout( new BorderLayout() );
135+
add( chartPanel, BorderLayout.CENTER );
136+
add( controlPanel, BorderLayout.SOUTH );
110137

111138
// Set up the frame
112139
setTitle( TITLE );
113-
setSize( 800, 600 );
140+
setSize( 800, 700 );
114141
setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
115142
setLocationRelativeTo( null );
116143
}
117144

118145
/**
119-
* Creates a reusable renderer for datasets.
120-
*
121-
* @param color The color for the raw data points and the line.
122-
* @param shape The shape for the raw data points.
123-
* @return A configured XYLineAndShapeRenderer.
146+
* Creates a control panel with color choosers and checkboxes for visibility controls.
124147
*/
148+
private JPanel createControlPanel( final double[] timepoints, final double[] spotCounts, final double[] divisionCounts )
149+
{
150+
JPanel controlPanel = new JPanel( new MigLayout( "wrap 5" ) );
151+
152+
// Spot-related controls
153+
JButton spotColorButton = new JButton( "Choose Spot Color" );
154+
spotColorButton.addActionListener( e -> {
155+
Color newColor = JColorChooser.showDialog( null, "Choose Spot Color", spotCountColor );
156+
if ( newColor != null )
157+
{
158+
spotCountColor = newColor;
159+
updateChartColors();
160+
}
161+
} );
162+
163+
JCheckBox showSpotCounts = new JCheckBox( "Show Spot Counts", true );
164+
JCheckBox showSpotAverage = new JCheckBox( "Show Spot Sliding Average", true );
165+
166+
showSpotCounts.addActionListener( e -> {
167+
plot.getRenderer( 0 ).setSeriesVisible( 0, showSpotCounts.isSelected() );
168+
updateAxisVisibility();
169+
} );
170+
171+
showSpotAverage.addActionListener( e -> {
172+
plot.getRenderer( 0 ).setSeriesVisible( 1, showSpotAverage.isSelected() );
173+
updateAxisVisibility();
174+
} );
175+
176+
JSpinner spotWindowSpinner = new JSpinner( new SpinnerNumberModel( spotWindowSize, 1, Integer.MAX_VALUE, 1 ) );
177+
spotWindowSpinner.addChangeListener( e -> {
178+
spotWindowSize = ( int ) spotWindowSpinner.getValue();
179+
updateSlidingAverage( timepoints, spotCounts, divisionCounts );
180+
} );
181+
182+
// Division-related controls
183+
JButton divisionColorButton = new JButton( "Choose Division Color" );
184+
divisionColorButton.addActionListener( e -> {
185+
Color newColor = JColorChooser.showDialog( null, "Choose Division Color", divisionCountColor );
186+
if ( newColor != null )
187+
{
188+
divisionCountColor = newColor;
189+
updateChartColors();
190+
}
191+
} );
192+
193+
JCheckBox showDivisionCounts = new JCheckBox( "Show Division Counts", true );
194+
JCheckBox showDivisionAverage = new JCheckBox( "Show Division Sliding Average", true );
195+
196+
showDivisionCounts.addActionListener( e -> {
197+
plot.getRenderer( 1 ).setSeriesVisible( 0, showDivisionCounts.isSelected() );
198+
updateAxisVisibility();
199+
} );
200+
201+
showDivisionAverage.addActionListener( e -> {
202+
plot.getRenderer( 1 ).setSeriesVisible( 1, showDivisionAverage.isSelected() );
203+
updateAxisVisibility();
204+
} );
205+
206+
JSpinner divisionWindowSpinner = new JSpinner( new SpinnerNumberModel( divisionWindowSize, 1, Integer.MAX_VALUE, 1 ) );
207+
divisionWindowSpinner.addChangeListener( e -> {
208+
divisionWindowSize = ( int ) divisionWindowSpinner.getValue();
209+
updateSlidingAverage( timepoints, spotCounts, divisionCounts );
210+
} );
211+
212+
// Add components to the control panel
213+
controlPanel.add( spotColorButton, "growx" );
214+
controlPanel.add( showSpotCounts, "growx" );
215+
controlPanel.add( showSpotAverage, "growx" );
216+
controlPanel.add( new JLabel( "Window Size:" ), "align right" );
217+
controlPanel.add( spotWindowSpinner, "growx" );
218+
219+
controlPanel.add( divisionColorButton, "growx" );
220+
controlPanel.add( showDivisionCounts, "growx" );
221+
controlPanel.add( showDivisionAverage, "growx" );
222+
controlPanel.add( new JLabel( "Window Size:" ), "align right" );
223+
controlPanel.add( divisionWindowSpinner, "growx" );
224+
225+
return controlPanel;
226+
}
227+
228+
/**
229+
* Updates the chart colors based on the user's selection.
230+
*/
231+
private void updateChartColors()
232+
{
233+
// Update the renderer colors
234+
XYLineAndShapeRenderer spotCountRenderer = createRenderer(
235+
spotCountColor, new Rectangle2D.Double( -2, -2, 4, 4 ) );
236+
plot.setRenderer( 0, spotCountRenderer );
237+
238+
XYLineAndShapeRenderer divisionCountRenderer = createRenderer(
239+
divisionCountColor, new Ellipse2D.Double( -2, -2, 4, 4 ) );
240+
plot.setRenderer( 1, divisionCountRenderer );
241+
242+
// Update axis label colors
243+
customizeAxis( leftAxis, SPOTS_COUNT_SERIES_NAME, spotCountColor );
244+
customizeAxis( rightAxis, DIVISION_COUNT_SERIES_NAME, divisionCountColor );
245+
246+
// Repaint the chart
247+
repaint();
248+
}
249+
250+
/**
251+
* Updates the visibility of the axis labels based on the state of the checkboxes.
252+
*/
253+
private void updateAxisVisibility()
254+
{
255+
XYItemRenderer spotCountRenderer = plot.getRenderer( 0 );
256+
Boolean spotCountVisible = spotCountRenderer.getSeriesVisible( 0 );
257+
Boolean spotAverageVisible = spotCountRenderer.getSeriesVisible( 1 );
258+
boolean showSpotAxis = spotCountVisible != null && spotCountVisible || spotAverageVisible != null && spotAverageVisible;
259+
leftAxis.setLabel( showSpotAxis ? SPOTS_COUNT_SERIES_NAME : null );
260+
leftAxis.setVisible( showSpotAxis );
261+
262+
XYItemRenderer divisionCountRenderer = plot.getRenderer( 1 );
263+
Boolean divisionCountVisible = divisionCountRenderer.getSeriesVisible( 0 );
264+
Boolean divisionAverageVisible = divisionCountRenderer.getSeriesVisible( 1 );
265+
boolean showDivisionAxis =
266+
divisionCountVisible != null && divisionCountVisible || divisionAverageVisible != null && divisionAverageVisible;
267+
rightAxis.setLabel( showDivisionAxis ? DIVISION_COUNT_SERIES_NAME : null );
268+
rightAxis.setVisible( showDivisionAxis );
269+
270+
repaint();
271+
}
272+
125273
private XYLineAndShapeRenderer createRenderer( final Color color, final Shape shape )
126274
{
127275
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer();
@@ -176,11 +324,18 @@ private static XYSeriesCollection createSeries( final double[] xValues, final do
176324
return dataset;
177325
}
178326

179-
private static void setChartColors( final XYPlot plot )
327+
/**
328+
* Updates the sliding average series based on the new window sizes.
329+
*/
330+
private void updateSlidingAverage( double[] timepoints, double[] spotCounts, double[] divisionCounts )
180331
{
181-
plot.setBackgroundPaint( Color.WHITE );
182-
plot.setDomainGridlinePaint( Color.BLACK );
183-
plot.setRangeGridlinePaint( Color.BLACK );
332+
XYSeriesCollection spotCountsSeries = createSeries(
333+
timepoints, spotCounts, SPOTS_COUNT_SERIES_NAME, spotWindowSize );
334+
XYSeriesCollection divisionCountsSeries = createSeries(
335+
timepoints, divisionCounts, DIVISION_COUNT_SERIES_NAME, divisionWindowSize );
336+
337+
plot.setDataset( 0, spotCountsSeries );
338+
plot.setDataset( 1, divisionCountsSeries );
184339
}
185340

186341
private static double[] calculateSlidingAverage( double[] values, int windowSize )

0 commit comments

Comments
 (0)