3636import java .awt .Shape ;
3737import java .awt .geom .Ellipse2D ;
3838import java .awt .geom .Rectangle2D ;
39+ import java .util .List ;
3940
4041import javax .swing .JButton ;
4142import javax .swing .JCheckBox ;
4546import javax .swing .JPanel ;
4647import javax .swing .JSpinner ;
4748import javax .swing .SpinnerNumberModel ;
49+ import javax .swing .WindowConstants ;
4850
4951import net .miginfocom .swing .MigLayout ;
52+
53+ import org .apache .commons .lang3 .tuple .Triple ;
5054import org .jfree .chart .ChartFactory ;
5155import org .jfree .chart .ChartPanel ;
5256import org .jfree .chart .JFreeChart ;
5761import org .jfree .chart .renderer .xy .XYLineAndShapeRenderer ;
5862import org .jfree .data .xy .XYSeries ;
5963import org .jfree .data .xy .XYSeriesCollection ;
64+ import org .mastodon .mamut .ProjectModel ;
65+ import org .scijava .prefs .PrefService ;
6066
61- class SpotAndDivisionCountChart extends JFrame
67+ public class SpotAndDivisionCountChart extends JFrame
6268{
63- private Color divisionCountColor = new Color ( 86 , 180 , 233 ); // Light Blue
6469
65- private Color spotCountColor = new Color ( 230 , 159 , 0 ); // Dark Orange
70+ private static final String SPOT_COLOR = "spotColor" ;
71+
72+ private static final String DIVISION_COLOR = "divisionColor" ;
73+
74+ private static final String SPOT_COUNT_VISIBILITY = "spotCountVisibility" ;
75+
76+ private static final String DIVISION_COUNT_VISIBILITY = "divisionCountVisibility" ;
77+
78+ private static final String SPOT_COUNT_AVERAGE_VISIBILITY = "spotCountAverageVisibility" ;
79+
80+ private static final String DIVISION_COUNT_AVERAGE_VISIBILITY = "divisionCountAverageVisibility" ;
81+
82+ private static final String SPOT_COUNT_SLIDING_AVERAGE_WINDOW_SIZE = "spotCountSlidingAverageWindowSize" ;
83+
84+ private static final String DIVISION_COUNT_SLIDING_AVERAGE_WINDOW_SIZE = "divisionCountSlidingAverageWindowSize" ;
85+
86+ private static final int SPOT_COUNT_DEFAULT_COLOR = new Color ( 230 , 159 , 0 ).getRGB (); // Dark Orange
87+
88+ private static final int DIVISION_COUNT_DEFAULT_COLOR = new Color ( 86 , 180 , 233 ).getRGB (); // Light Blue
89+
90+ private static final int DEFAULT_SLIDING_WINDOW_SIZE = 10 ;
91+
92+ private Color spotCountColor ; // Dark Orange
93+
94+ private Color divisionCountColor ; // Light Blue
6695
6796 private final static String TITLE = "Spot and Division Counts over Time" ;
6897 private final static String SPOTS_COUNT_SERIES_NAME = "Spot Counts" ;
@@ -78,10 +107,21 @@ class SpotAndDivisionCountChart extends JFrame
78107
79108 private int divisionWindowSize ;
80109
81- SpotAndDivisionCountChart ( double [] timepoints , double [] spotCounts , double [] divisionCounts , int windowSize )
110+ private final PrefService prefs ;
111+
112+ SpotAndDivisionCountChart ( final double [] timepoints , final double [] spotCounts , final double [] divisionCounts ,
113+ final PrefService prefs )
82114 {
83- this .spotWindowSize = windowSize ;
84- this .divisionWindowSize = windowSize ;
115+ this .prefs = prefs ;
116+
117+ this .spotCountColor = new Color (
118+ prefs .getInt ( SpotAndDivisionCountChart .class , SPOT_COLOR , SPOT_COUNT_DEFAULT_COLOR ) );
119+ this .divisionCountColor = new Color (
120+ prefs .getInt ( SpotAndDivisionCountChart .class , DIVISION_COLOR , DIVISION_COUNT_DEFAULT_COLOR ) );
121+ this .spotWindowSize =
122+ prefs .getInt ( SpotAndDivisionCountChart .class , SPOT_COUNT_SLIDING_AVERAGE_WINDOW_SIZE , DEFAULT_SLIDING_WINDOW_SIZE );
123+ this .divisionWindowSize =
124+ prefs .getInt ( SpotAndDivisionCountChart .class , DIVISION_COUNT_SLIDING_AVERAGE_WINDOW_SIZE , DEFAULT_SLIDING_WINDOW_SIZE );
85125
86126 XYSeriesCollection spotCountsSeries = createSeries (
87127 timepoints , spotCounts , SPOTS_COUNT_SERIES_NAME , spotWindowSize );
@@ -140,14 +180,29 @@ class SpotAndDivisionCountChart extends JFrame
140180 setSize ( 800 , 700 );
141181 setDefaultCloseOperation ( JFrame .DISPOSE_ON_CLOSE );
142182 setLocationRelativeTo ( null );
183+
184+ repaint ();
185+ }
186+
187+ public static void show ( final ProjectModel projectModel , final PrefService prefService )
188+ {
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 ();
194+
195+ SpotAndDivisionCountChart chart = new SpotAndDivisionCountChart ( timepoints , spots , divisions , prefService );
196+ chart .setDefaultCloseOperation ( WindowConstants .DISPOSE_ON_CLOSE );
197+ chart .setVisible ( true );
143198 }
144199
145200 /**
146201 * Creates a control panel with color choosers and checkboxes for visibility controls.
147202 */
148203 private JPanel createControlPanel ( final double [] timepoints , final double [] spotCounts , final double [] divisionCounts )
149204 {
150- JPanel controlPanel = new JPanel ( new MigLayout ( "wrap 5" ) );
205+ JPanel controlPanel = new JPanel ( new MigLayout ( "fill, wrap 5" , "[grow]" , "[]10[]10[]10[]10[] " ) );
151206
152207 // Spot-related controls
153208 JButton spotColorButton = new JButton ( "Choose Spot Color" );
@@ -156,26 +211,32 @@ private JPanel createControlPanel( final double[] timepoints, final double[] spo
156211 if ( newColor != null )
157212 {
158213 spotCountColor = newColor ;
214+ prefs .put ( SpotAndDivisionCountChart .class , SPOT_COLOR , spotCountColor .getRGB () );
159215 updateChartColors ();
160216 }
161217 } );
162218
163- JCheckBox showSpotCounts = new JCheckBox ( "Show Spot Counts" , true );
164- JCheckBox showSpotAverage = new JCheckBox ( "Show Spot Sliding Average" , true );
219+ boolean spotCountVisibility = prefs .getBoolean ( SpotAndDivisionCountChart .class , SPOT_COUNT_VISIBILITY , true );
220+ boolean spotCountAverageVisibility = prefs .getBoolean ( SpotAndDivisionCountChart .class , SPOT_COUNT_AVERAGE_VISIBILITY , true );
221+ JCheckBox showSpotCounts = new JCheckBox ( "Show Spot Counts" , spotCountVisibility );
222+ JCheckBox showSpotAverage = new JCheckBox ( "Show Spot Sliding Average" , spotCountAverageVisibility );
165223
166224 showSpotCounts .addActionListener ( e -> {
167225 plot .getRenderer ( 0 ).setSeriesVisible ( 0 , showSpotCounts .isSelected () );
226+ prefs .put ( SpotAndDivisionCountChart .class , SPOT_COUNT_VISIBILITY , showSpotCounts .isSelected () );
168227 updateAxisVisibility ();
169228 } );
170229
171230 showSpotAverage .addActionListener ( e -> {
172231 plot .getRenderer ( 0 ).setSeriesVisible ( 1 , showSpotAverage .isSelected () );
232+ prefs .put ( SpotAndDivisionCountChart .class , SPOT_COUNT_AVERAGE_VISIBILITY , showSpotAverage .isSelected () );
173233 updateAxisVisibility ();
174234 } );
175235
176236 JSpinner spotWindowSpinner = new JSpinner ( new SpinnerNumberModel ( spotWindowSize , 1 , Integer .MAX_VALUE , 1 ) );
177237 spotWindowSpinner .addChangeListener ( e -> {
178238 spotWindowSize = ( int ) spotWindowSpinner .getValue ();
239+ prefs .put ( SpotAndDivisionCountChart .class , SPOT_COUNT_SLIDING_AVERAGE_WINDOW_SIZE , spotWindowSize );
179240 updateSlidingAverage ( timepoints , spotCounts , divisionCounts );
180241 } );
181242
@@ -186,26 +247,32 @@ private JPanel createControlPanel( final double[] timepoints, final double[] spo
186247 if ( newColor != null )
187248 {
188249 divisionCountColor = newColor ;
250+ prefs .put ( SpotAndDivisionCountChart .class , DIVISION_COLOR , divisionCountColor .getRGB () );
189251 updateChartColors ();
190252 }
191253 } );
192254
193- JCheckBox showDivisionCounts = new JCheckBox ( "Show Division Counts" , true );
194- JCheckBox showDivisionAverage = new JCheckBox ( "Show Division Sliding Average" , true );
255+ boolean divisionCountVisibility = prefs .getBoolean ( SpotAndDivisionCountChart .class , DIVISION_COUNT_VISIBILITY , true );
256+ boolean divisionAverageVisibility = prefs .getBoolean ( SpotAndDivisionCountChart .class , DIVISION_COUNT_AVERAGE_VISIBILITY , true );
257+ JCheckBox showDivisionCounts = new JCheckBox ( "Show Division Counts" , divisionCountVisibility );
258+ JCheckBox showDivisionAverage = new JCheckBox ( "Show Division Sliding Average" , divisionAverageVisibility );
195259
196260 showDivisionCounts .addActionListener ( e -> {
197261 plot .getRenderer ( 1 ).setSeriesVisible ( 0 , showDivisionCounts .isSelected () );
262+ prefs .put ( SpotAndDivisionCountChart .class , DIVISION_COUNT_VISIBILITY , showDivisionCounts .isSelected () );
198263 updateAxisVisibility ();
199264 } );
200265
201266 showDivisionAverage .addActionListener ( e -> {
202267 plot .getRenderer ( 1 ).setSeriesVisible ( 1 , showDivisionAverage .isSelected () );
268+ prefs .put ( SpotAndDivisionCountChart .class , DIVISION_COUNT_AVERAGE_VISIBILITY , showDivisionAverage .isSelected () );
203269 updateAxisVisibility ();
204270 } );
205271
206272 JSpinner divisionWindowSpinner = new JSpinner ( new SpinnerNumberModel ( divisionWindowSize , 1 , Integer .MAX_VALUE , 1 ) );
207273 divisionWindowSpinner .addChangeListener ( e -> {
208274 divisionWindowSize = ( int ) divisionWindowSpinner .getValue ();
275+ prefs .put ( SpotAndDivisionCountChart .class , DIVISION_COUNT_SLIDING_AVERAGE_WINDOW_SIZE , divisionWindowSize );
209276 updateSlidingAverage ( timepoints , spotCounts , divisionCounts );
210277 } );
211278
@@ -214,13 +281,25 @@ private JPanel createControlPanel( final double[] timepoints, final double[] spo
214281 controlPanel .add ( showSpotCounts , "growx" );
215282 controlPanel .add ( showSpotAverage , "growx" );
216283 controlPanel .add ( new JLabel ( "Window Size:" ), "align right" );
217- controlPanel .add ( spotWindowSpinner , "growx " );
284+ controlPanel .add ( spotWindowSpinner , "wmax 50 " );
218285
219286 controlPanel .add ( divisionColorButton , "growx" );
220287 controlPanel .add ( showDivisionCounts , "growx" );
221288 controlPanel .add ( showDivisionAverage , "growx" );
222289 controlPanel .add ( new JLabel ( "Window Size:" ), "align right" );
223- controlPanel .add ( divisionWindowSpinner , "growx" );
290+ controlPanel .add ( divisionWindowSpinner , "wmax 50" );
291+
292+ // Add description
293+ controlPanel .add ( new JLabel (
294+ "<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>" ),
295+ "span 5" );
296+
297+ plot .getRenderer ( 0 ).setSeriesVisible ( 0 , spotCountVisibility );
298+ plot .getRenderer ( 0 ).setSeriesVisible ( 1 , spotCountAverageVisibility );
299+ plot .getRenderer ( 1 ).setSeriesVisible ( 0 , divisionCountVisibility );
300+ plot .getRenderer ( 1 ).setSeriesVisible ( 1 , divisionAverageVisibility );
301+
302+ updateAxisVisibility ();
224303
225304 return controlPanel ;
226305 }
0 commit comments