3838import java .awt .geom .Rectangle2D ;
3939import java .util .List ;
4040
41+ import javax .swing .ButtonGroup ;
4142import javax .swing .JButton ;
4243import javax .swing .JCheckBox ;
4344import javax .swing .JColorChooser ;
4445import javax .swing .JFrame ;
4546import javax .swing .JLabel ;
4647import javax .swing .JPanel ;
48+ import javax .swing .JRadioButton ;
4749import javax .swing .JSpinner ;
4850import javax .swing .SpinnerNumberModel ;
4951import javax .swing .WindowConstants ;
@@ -83,6 +85,8 @@ public class SpotAndDivisionCountChart extends JFrame
8385
8486 private static final String DIVISION_COUNT_SLIDING_AVERAGE_WINDOW_SIZE = "divisionCountSlidingAverageWindowSize" ;
8587
88+ private static final String ONLY_SELECTED_SPOTS = "onlySelectedSpots" ;
89+
8690 private static final int SPOT_COUNT_DEFAULT_COLOR = new Color ( 230 , 159 , 0 ).getRGB (); // Dark Orange
8791
8892 private static final int DIVISION_COUNT_DEFAULT_COLOR = new Color ( 86 , 180 , 233 ).getRGB (); // Light Blue
@@ -93,9 +97,11 @@ public class SpotAndDivisionCountChart extends JFrame
9397
9498 private Color divisionCountColor ; // Light Blue
9599
96- private final static String TITLE = "Spot and Division Counts over Time" ;
97- private final static String SPOTS_COUNT_SERIES_NAME = "Spot Counts" ;
98- private final static String DIVISION_COUNT_SERIES_NAME = "Division Counts" ;
100+ private static final String TITLE = "Spot and Division Counts over Time" ;
101+
102+ private static final String SPOTS_COUNT_SERIES_NAME = "Spot Counts" ;
103+
104+ private static final String DIVISION_COUNT_SERIES_NAME = "Division Counts" ;
99105
100106 private final XYPlot plot ;
101107
@@ -107,11 +113,19 @@ public class SpotAndDivisionCountChart extends JFrame
107113
108114 private int divisionWindowSize ;
109115
116+ private boolean onlySelectedSpots ;
117+
110118 private final PrefService prefs ;
111119
112- SpotAndDivisionCountChart ( final double [] timepoints , final double [] spotCounts , final double [] divisionCounts ,
113- final PrefService prefs )
120+ private final ProjectModel projectModel ;
121+
122+ private XYSeriesCollection spotCountsSeries ;
123+
124+ private XYSeriesCollection divisionCountsSeries ;
125+
126+ SpotAndDivisionCountChart ( final ProjectModel projectModel , final PrefService prefs )
114127 {
128+ this .projectModel = projectModel ;
115129 this .prefs = prefs ;
116130
117131 this .spotCountColor = new Color (
@@ -122,12 +136,9 @@ public class SpotAndDivisionCountChart extends JFrame
122136 prefs .getInt ( SpotAndDivisionCountChart .class , SPOT_COUNT_SLIDING_AVERAGE_WINDOW_SIZE , DEFAULT_SLIDING_WINDOW_SIZE );
123137 this .divisionWindowSize =
124138 prefs .getInt ( SpotAndDivisionCountChart .class , DIVISION_COUNT_SLIDING_AVERAGE_WINDOW_SIZE , DEFAULT_SLIDING_WINDOW_SIZE );
139+ this .onlySelectedSpots = prefs .getBoolean ( SpotAndDivisionCountChart .class , ONLY_SELECTED_SPOTS , false );
125140
126- XYSeriesCollection spotCountsSeries = createSeries (
127- timepoints , spotCounts , SPOTS_COUNT_SERIES_NAME , spotWindowSize );
128-
129- XYSeriesCollection divisionCountsSeries = createSeries (
130- timepoints , divisionCounts , DIVISION_COUNT_SERIES_NAME , divisionWindowSize );
141+ updateChartData ();
131142
132143 JFreeChart chart = ChartFactory .createXYLineChart (
133144 TITLE ,
@@ -168,7 +179,7 @@ public class SpotAndDivisionCountChart extends JFrame
168179 chartPanel .setPreferredSize ( new Dimension ( 800 , 600 ) );
169180
170181 // Add color chooser and visibility controls
171- JPanel controlPanel = createControlPanel ( timepoints , spotCounts , divisionCounts );
182+ JPanel controlPanel = createControlPanel ();
172183
173184 // Set up the frame layout
174185 setLayout ( new BorderLayout () );
@@ -184,23 +195,34 @@ public class SpotAndDivisionCountChart extends JFrame
184195 repaint ();
185196 }
186197
187- public static void show ( final ProjectModel projectModel , final PrefService prefService )
198+ private void updateChartData ( )
188199 {
189- List < Triple < Integer , Integer , Integer > > divisionCounts =
190- SpotAndDivisionCount .getSpotAndDivisionsPerTimepoint ( projectModel .getModel () );
191- double [] timepoints = divisionCounts .stream ().mapToDouble ( Triple ::getLeft ).toArray ();
192- double [] spots = divisionCounts .stream ().mapToDouble ( Triple ::getMiddle ).toArray ();
193- double [] divisions = divisionCounts .stream ().mapToDouble ( Triple ::getRight ).toArray ();
200+ List < Triple < Integer , Integer , Integer > > counts =
201+ SpotAndDivisionCount .getSpotAndDivisionsPerTimepoint ( projectModel , this .onlySelectedSpots );
202+ double [] timepoints = counts .stream ().mapToDouble ( Triple ::getLeft ).toArray ();
203+ double [] spotCounts = counts .stream ().mapToDouble ( Triple ::getMiddle ).toArray ();
204+ double [] divisionCounts = counts .stream ().mapToDouble ( Triple ::getRight ).toArray ();
205+ spotCountsSeries = createSeries ( timepoints , spotCounts , SPOTS_COUNT_SERIES_NAME , spotWindowSize );
206+ divisionCountsSeries = createSeries ( timepoints , divisionCounts , DIVISION_COUNT_SERIES_NAME , divisionWindowSize );
207+
208+ if ( plot != null )
209+ {
210+ plot .setDataset ( 0 , spotCountsSeries );
211+ plot .setDataset ( 1 , divisionCountsSeries );
212+ }
213+ }
194214
195- SpotAndDivisionCountChart chart = new SpotAndDivisionCountChart ( timepoints , spots , divisions , prefService );
215+ public static void show ( final ProjectModel projectModel , final PrefService prefService )
216+ {
217+ SpotAndDivisionCountChart chart = new SpotAndDivisionCountChart ( projectModel , prefService );
196218 chart .setDefaultCloseOperation ( WindowConstants .DISPOSE_ON_CLOSE );
197219 chart .setVisible ( true );
198220 }
199221
200222 /**
201223 * Creates a control panel with color choosers and checkboxes for visibility controls.
202224 */
203- private JPanel createControlPanel ( final double [] timepoints , final double [] spotCounts , final double [] divisionCounts )
225+ private JPanel createControlPanel ()
204226 {
205227 JPanel controlPanel = new JPanel ( new MigLayout ( "fill, wrap 5" , "[grow]" , "[]10[]10[]10[]10[]" ) );
206228
@@ -237,7 +259,7 @@ private JPanel createControlPanel( final double[] timepoints, final double[] spo
237259 spotWindowSpinner .addChangeListener ( e -> {
238260 spotWindowSize = ( int ) spotWindowSpinner .getValue ();
239261 prefs .put ( SpotAndDivisionCountChart .class , SPOT_COUNT_SLIDING_AVERAGE_WINDOW_SIZE , spotWindowSize );
240- updateSlidingAverage ( timepoints , spotCounts , divisionCounts );
262+ updateChartData ( );
241263 } );
242264
243265 // Division-related controls
@@ -273,7 +295,25 @@ private JPanel createControlPanel( final double[] timepoints, final double[] spo
273295 divisionWindowSpinner .addChangeListener ( e -> {
274296 divisionWindowSize = ( int ) divisionWindowSpinner .getValue ();
275297 prefs .put ( SpotAndDivisionCountChart .class , DIVISION_COUNT_SLIDING_AVERAGE_WINDOW_SIZE , divisionWindowSize );
276- updateSlidingAverage ( timepoints , spotCounts , divisionCounts );
298+ updateChartData ();
299+ } );
300+
301+ JRadioButton allSpots = new JRadioButton ( "All spots" , !onlySelectedSpots );
302+ JRadioButton selectedSpots = new JRadioButton ( "Only selected spots" , onlySelectedSpots );
303+ ButtonGroup group = new ButtonGroup ();
304+ group .add ( allSpots );
305+ group .add ( selectedSpots );
306+ allSpots .addActionListener ( e -> {
307+ onlySelectedSpots = false ;
308+ prefs .put ( SpotAndDivisionCountChart .class , ONLY_SELECTED_SPOTS , onlySelectedSpots );
309+ updateChartData ();
310+ repaint ();
311+ } );
312+ selectedSpots .addActionListener ( e -> {
313+ this .onlySelectedSpots = true ;
314+ prefs .put ( SpotAndDivisionCountChart .class , ONLY_SELECTED_SPOTS , onlySelectedSpots );
315+ updateChartData ();
316+ repaint ();
277317 } );
278318
279319 // Add components to the control panel
@@ -289,6 +329,9 @@ private JPanel createControlPanel( final double[] timepoints, final double[] spo
289329 controlPanel .add ( new JLabel ( "Window Size:" ), "align right" );
290330 controlPanel .add ( divisionWindowSpinner , "wmax 50" );
291331
332+ controlPanel .add ( allSpots , "growx" );
333+ controlPanel .add ( selectedSpots , "span, growx" );
334+
292335 // Add description
293336 controlPanel .add ( new JLabel (
294337 "<html>This windows shows the number of spots and divisions at each timepoint together with a sliding average.<br>A division is defined as a spot with more than one outgoing edge.</html>" ),
@@ -331,27 +374,23 @@ private void updateChartColors()
331374 */
332375 private void updateAxisVisibility ()
333376 {
334- XYItemRenderer spotCountRenderer = plot .getRenderer ( 0 );
335- Boolean spotCountVisible = spotCountRenderer .getSeriesVisible ( 0 );
336- Boolean spotAverageVisible = spotCountRenderer .getSeriesVisible ( 1 );
337- boolean isSpotCountVisible = spotCountVisible != null && spotCountVisible ;
338- boolean isSpotAverageVisible = spotAverageVisible != null && spotAverageVisible ;
339- boolean showSpotAxis = isSpotCountVisible || isSpotAverageVisible ;
340- leftAxis .setLabel ( showSpotAxis ? SPOTS_COUNT_SERIES_NAME : null );
341- leftAxis .setVisible ( showSpotAxis );
342-
343- XYItemRenderer divisionCountRenderer = plot .getRenderer ( 1 );
344- Boolean divisionCountVisible = divisionCountRenderer .getSeriesVisible ( 0 );
345- Boolean divisionAverageVisible = divisionCountRenderer .getSeriesVisible ( 1 );
346- boolean isDivisionCountVisible = divisionCountVisible != null && divisionCountVisible ;
347- boolean isDivisionAverageVisible = divisionAverageVisible != null && divisionAverageVisible ;
348- boolean showDivisionAxis = isDivisionCountVisible || isDivisionAverageVisible ;
349- rightAxis .setLabel ( showDivisionAxis ? DIVISION_COUNT_SERIES_NAME : null );
350- rightAxis .setVisible ( showDivisionAxis );
351-
377+ updateAxisVisibility ( plot .getRenderer ( 0 ), leftAxis , SPOTS_COUNT_SERIES_NAME );
378+ updateAxisVisibility ( plot .getRenderer ( 1 ), rightAxis , DIVISION_COUNT_SERIES_NAME );
352379 repaint ();
353380 }
354381
382+ private void updateAxisVisibility ( final XYItemRenderer renderer , final NumberAxis axis ,
383+ final String labelIfVisible )
384+ {
385+ Boolean countVisible = renderer .getSeriesVisible ( 0 );
386+ Boolean averageVisible = renderer .getSeriesVisible ( 1 );
387+ boolean isCountVisible = countVisible != null && countVisible ;
388+ boolean isAverageVisible = averageVisible != null && averageVisible ;
389+ boolean showAxis = isCountVisible || isAverageVisible ;
390+ axis .setLabel ( showAxis ? labelIfVisible : null );
391+ axis .setVisible ( showAxis );
392+ }
393+
355394 private XYLineAndShapeRenderer createRenderer ( final Color color , final Shape shape )
356395 {
357396 XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer ();
@@ -406,20 +445,6 @@ private static XYSeriesCollection createSeries( final double[] xValues, final do
406445 return dataset ;
407446 }
408447
409- /**
410- * Updates the sliding average series based on the new window sizes.
411- */
412- private void updateSlidingAverage ( double [] timepoints , double [] spotCounts , double [] divisionCounts )
413- {
414- XYSeriesCollection spotCountsSeries = createSeries (
415- timepoints , spotCounts , SPOTS_COUNT_SERIES_NAME , spotWindowSize );
416- XYSeriesCollection divisionCountsSeries = createSeries (
417- timepoints , divisionCounts , DIVISION_COUNT_SERIES_NAME , divisionWindowSize );
418-
419- plot .setDataset ( 0 , spotCountsSeries );
420- plot .setDataset ( 1 , divisionCountsSeries );
421- }
422-
423448 private static double [] calculateSlidingAverage ( double [] values , int windowSize )
424449 {
425450 double [] result = new double [ values .length ];
0 commit comments