1616import java .util .Comparator ;
1717import java .util .List ;
1818import java .util .concurrent .atomic .AtomicReference ;
19+ import java .util .stream .IntStream ;
1920
2021import org .eclipse .chemclipse .model .statistics .ISample ;
2122import org .eclipse .chemclipse .model .statistics .IVariable ;
2223import org .eclipse .chemclipse .support .events .IChemClipseEvents ;
24+ import org .eclipse .chemclipse .swt .ui .notifier .UpdateNotifierUI ;
25+ import org .eclipse .chemclipse .swt .ui .support .Colors ;
2326import org .eclipse .chemclipse .ux .extension .ui .model .IDataUpdateListener ;
2427import org .eclipse .chemclipse .ux .extension .ui .support .DataUpdateSupport ;
2528import org .eclipse .chemclipse .ux .extension .ui .swt .IExtendedPartUI ;
3134import org .eclipse .chemclipse .xxd .process .supplier .pca .ui .Activator ;
3235import org .eclipse .chemclipse .xxd .process .supplier .pca .ui .chart2d .ScorePlotBarChart ;
3336import org .eclipse .swt .SWT ;
37+ import org .eclipse .swt .events .PaintEvent ;
3438import org .eclipse .swt .events .SelectionAdapter ;
3539import org .eclipse .swt .events .SelectionEvent ;
40+ import org .eclipse .swt .graphics .GC ;
41+ import org .eclipse .swt .graphics .Point ;
3642import org .eclipse .swt .layout .GridData ;
3743import org .eclipse .swt .layout .GridLayout ;
3844import org .eclipse .swt .widgets .Combo ;
3945import org .eclipse .swt .widgets .Composite ;
4046import org .eclipse .swt .widgets .Event ;
4147import org .eclipse .swt .widgets .Label ;
48+ import org .eclipse .swt .widgets .Scrollable ;
49+ import org .eclipse .swtchart .ICustomPaintListener ;
50+ import org .eclipse .swtchart .IPlotArea ;
4251import org .eclipse .swtchart .extensions .core .BaseChart ;
4352import org .eclipse .swtchart .extensions .core .IChartSettings ;
4453import org .eclipse .swtchart .extensions .core .IMouseSupport ;
54+ import org .eclipse .swtchart .extensions .core .UserSelection ;
4555import org .eclipse .swtchart .extensions .events .IHandledEventProcessor ;
4656
4757public class ExtendedScorePlotBarChart extends Composite implements IExtendedPartUI {
@@ -51,7 +61,7 @@ public class ExtendedScorePlotBarChart extends Composite implements IExtendedPar
5161 private EvaluationPCA evaluationPCA = null ;
5262 private int currentPC = 0 ;
5363 private Composite control ;
54- @ SuppressWarnings ( "unused" )
64+ private UserSelection userSelection = new UserSelection ();
5565 private ISamplesPCA <IVariable , ISample > samples = null ;
5666
5767 public ExtendedScorePlotBarChart (Composite parent , int style ) {
@@ -171,10 +181,7 @@ public void handleEvent(BaseChart baseChart, Event event) {
171181
172182 if (evaluationPCA != null ) {
173183
174- // Convert pixel coordinates to data coordinates
175184 double xValue = baseChart .getAxisSet ().getXAxis (BaseChart .ID_PRIMARY_X_AXIS ).getDataCoordinate (event .x );
176-
177- // Create list of sample-score pairs
178185 IResultsMVA results = evaluationPCA .getResults ();
179186 List <IResultMVA > resultList = results .getPcaResultList ();
180187 List <SampleScore > sampleScores = new ArrayList <>();
@@ -189,11 +196,8 @@ public void handleEvent(BaseChart baseChart, Event event) {
189196 }
190197 }
191198
192- // Sort by score value (descending: positive to negative)
193199 sampleScores .sort (Comparator .comparingDouble (SampleScore ::getScore ).reversed ());
194-
195200 int barIndex = (int )Math .round (xValue );
196-
197201 if (barIndex >= 0 && barIndex < sampleScores .size ()) {
198202 String sampleName = sampleScores .get (barIndex ).getSampleName ();
199203 String tooltip = "Sample: " + sampleName + "\n Value: " + String .format ("%.2f" , sampleScores .get (barIndex ).getScore ());
@@ -205,9 +209,248 @@ public void handleEvent(BaseChart baseChart, Event event) {
205209 }
206210
207211 });
212+ chartSettings .addHandledEventProcessor (new IHandledEventProcessor () {
213+
214+ @ Override
215+ public int getEvent () {
216+
217+ return IMouseSupport .EVENT_MOUSE_DOWN ;
218+ }
219+
220+ @ Override
221+ public int getButton () {
222+
223+ return IMouseSupport .MOUSE_BUTTON_LEFT ;
224+ }
225+
226+ @ Override
227+ public int getStateMask () {
228+
229+ return SWT .MOD1 ;
230+ }
231+
232+ @ Override
233+ public void handleEvent (BaseChart baseChart , Event event ) {
234+
235+ if (evaluationPCA != null ) {
236+ if (event .count == 1 ) {
237+ userSelection .setSingleClick (true );
238+ userSelection .setStartCoordinate (event .x , event .y );
239+ }
240+ }
241+ }
242+ });
243+ chartSettings .addHandledEventProcessor (new IHandledEventProcessor () {
244+
245+ @ Override
246+ public int getEvent () {
247+
248+ return IMouseSupport .EVENT_MOUSE_MOVE ;
249+ }
250+
251+ @ Override
252+ public int getButton () {
253+
254+ return IMouseSupport .MOUSE_BUTTON_NONE ;
255+ }
256+
257+ @ Override
258+ public int getStateMask () {
259+
260+ return SWT .MOD1 ;
261+ }
262+
263+ @ Override
264+ public void handleEvent (BaseChart baseChart , Event event ) {
265+
266+ if (userSelection .getStartX () > 0 && userSelection .getStartY () > 0 ) {
267+ userSelection .setStopCoordinate (event .x , event .y );
268+ }
269+ }
270+ });
271+ chartSettings .addHandledEventProcessor (new IHandledEventProcessor () {
272+
273+ @ Override
274+ public int getEvent () {
275+
276+ return IMouseSupport .EVENT_MOUSE_UP ;
277+ }
278+
279+ @ Override
280+ public int getButton () {
281+
282+ return IMouseSupport .MOUSE_BUTTON_LEFT ;
283+ }
284+
285+ @ Override
286+ public int getStateMask () {
287+
288+ return SWT .MOD1 ;
289+ }
290+
291+ @ Override
292+ public void handleEvent (BaseChart baseChart , Event event ) {
293+
294+ if (evaluationPCA != null && isBoxSelection ()) {
295+ int pXStart = (int )baseChart .getAxisSet ().getXAxis (BaseChart .ID_PRIMARY_X_AXIS ).getDataCoordinate (userSelection .getStartX ());
296+ int pXStop = (int )baseChart .getAxisSet ().getXAxis (BaseChart .ID_PRIMARY_X_AXIS ).getDataCoordinate (userSelection .getStopX ());
297+
298+ if (pXStart > pXStop ) {
299+ int flip = pXStart ;
300+ pXStart = pXStop ;
301+ pXStop = flip ;
302+ }
303+ IResultsMVA results = evaluationPCA .getResults ();
304+ List <IResultMVA > resultList = results .getPcaResultList ();
305+ List <SampleScore > sampleScores = new ArrayList <>();
306+ for (IResultMVA result : resultList ) {
307+ ISample sample = result .getSample ();
308+ double [] scoreMatrix = result .getScoreVector ();
309+
310+ if (currentPC < scoreMatrix .length ) {
311+ double score = scoreMatrix [currentPC ];
312+ String sampleName = sample .getSampleName ();
313+ sampleScores .add (new SampleScore (sampleName , score ));
314+ }
315+ }
316+ sampleScores .sort (Comparator .comparingDouble (SampleScore ::getScore ).reversed ());
317+ List <ISample > samplesHighlighted = evaluationPCA .getHighlightedSamples ();
318+ for (int i = pXStart ; i <= pXStop ; i ++) {
319+ String currentSampleName = sampleScores .get (i ).getSampleName ();
320+ int index = IntStream .range (0 , samplesHighlighted .size ()).filter (x -> samplesHighlighted .get (x ).getSampleName ().equals (currentSampleName )).findFirst ().orElse (-1 );
321+ if (index == -1 ) {
322+ samplesHighlighted .add (samples .getSamples ().stream ().filter (x -> x .getSampleName ().equals (currentSampleName )).findFirst ().get ());
323+ } else {
324+ samplesHighlighted .remove (index );
325+ }
326+
327+ }
328+
329+ if (!samplesHighlighted .isEmpty ()) {
330+ UpdateNotifierUI .update (event .display , IChemClipseEvents .TOPIC_PCA_UPDATE_HIGHLIGHT_SAMPLE , samplesHighlighted .toArray ());
331+ }
332+
333+ userSelection .reset ();
334+ userSelection .setSingleClick (false );
335+ }
336+
337+ }
338+
339+ });
340+
341+ chartSettings .addHandledEventProcessor (new IHandledEventProcessor () {
342+
343+ @ Override
344+ public int getEvent () {
345+
346+ return IMouseSupport .EVENT_MOUSE_DOUBLE_CLICK ;
347+ }
348+
349+ @ Override
350+ public int getButton () {
351+
352+ return IMouseSupport .MOUSE_BUTTON_LEFT ;
353+ }
354+
355+ @ Override
356+ public int getStateMask () {
357+
358+ return SWT .NONE ;
359+ }
360+
361+ @ Override
362+ public void handleEvent (BaseChart baseChart , Event event ) {
363+
364+ if (evaluationPCA != null ) {
365+ int clickIndex = (int )baseChart .getAxisSet ().getXAxis (BaseChart .ID_PRIMARY_X_AXIS ).getDataCoordinate (event .x );
366+
367+ List <SampleScore > sampleScores = getSampleScoreList ();
368+ List <ISample > highlightedSamples = new ArrayList <>();
369+ List <ISample > samplesHighlighted = evaluationPCA .getHighlightedSamples ();
370+
371+ String currentSampleName = sampleScores .get (clickIndex ).getSampleName ();
372+ int sampleIndex = IntStream .range (0 , samplesHighlighted .size ()).filter (x -> samplesHighlighted .get (x ).getSampleName ().equals (currentSampleName )).findFirst ().orElse (-1 );
373+ if (sampleIndex == -1 ) {
374+ highlightedSamples .add (samples .getSamples ().stream ().filter (x -> x .getSampleName ().equals (currentSampleName )).findFirst ().get ());
375+ UpdateNotifierUI .update (event .display , IChemClipseEvents .TOPIC_PCA_UPDATE_HIGHLIGHT_SAMPLE , highlightedSamples .toArray ());
376+ } else {
377+ UpdateNotifierUI .update (event .display , IChemClipseEvents .TOPIC_PCA_UPDATE_HIGHLIGHT_SAMPLE , highlightedSamples .toArray ());
378+ }
379+ }
380+ }
381+ });
382+ chartSettings .addHandledEventProcessor (new IHandledEventProcessor () {
383+
384+ @ Override
385+ public int getEvent () {
386+
387+ return IMouseSupport .EVENT_MOUSE_DOUBLE_CLICK ;
388+ }
389+
390+ @ Override
391+ public int getButton () {
392+
393+ return IMouseSupport .MOUSE_BUTTON_LEFT ;
394+ }
395+
396+ @ Override
397+ public int getStateMask () {
398+
399+ return SWT .MOD1 ;
400+ }
401+
402+ @ Override
403+ public void handleEvent (BaseChart baseChart , Event event ) {
404+
405+ if (evaluationPCA != null ) {
406+ int clickIndex = (int )baseChart .getAxisSet ().getXAxis (BaseChart .ID_PRIMARY_X_AXIS ).getDataCoordinate (event .x );
407+
408+ List <SampleScore > sampleScores = getSampleScoreList ();
409+ List <ISample > samplesHighlighted = evaluationPCA .getHighlightedSamples ();
410+
411+ String currentSampleName = sampleScores .get (clickIndex ).getSampleName ();
412+ int sampleIndex = IntStream .range (0 , samplesHighlighted .size ()).filter (x -> samplesHighlighted .get (x ).getSampleName ().equals (currentSampleName )).findFirst ().orElse (-1 );
413+ if (sampleIndex == -1 ) {
414+ samplesHighlighted .add (samples .getSamples ().stream ().filter (x -> x .getSampleName ().equals (currentSampleName )).findFirst ().get ());
415+ UpdateNotifierUI .update (event .display , IChemClipseEvents .TOPIC_PCA_UPDATE_HIGHLIGHT_SAMPLE , samplesHighlighted .toArray ());
416+ } else {
417+ samplesHighlighted .remove (sampleIndex );
418+ UpdateNotifierUI .update (event .display , IChemClipseEvents .TOPIC_PCA_UPDATE_HIGHLIGHT_SAMPLE , samplesHighlighted .toArray ());
419+ }
420+ }
421+ userSelection .reset ();
422+ }
423+ });
208424
209425 chart .applySettings (chartSettings );
210426
427+ chart .getBaseChart ().getPlotArea ().addCustomPaintListener (new ICustomPaintListener () {
428+
429+ @ Override
430+ public void paintControl (PaintEvent e ) {
431+
432+ if (userSelection .isActive ()) {
433+ int xMin = Math .min (userSelection .getStartX (), userSelection .getStopX ());
434+ int xMax = Math .max (userSelection .getStartX (), userSelection .getStopX ());
435+ int y = Math .min (userSelection .getStartY (), userSelection .getStopY ());
436+ BaseChart baseChart = chartControl .get ().getBaseChart ();
437+ IPlotArea plotArea = baseChart .getPlotArea ();
438+ Point rectangle = plotArea instanceof Scrollable scrollable ? scrollable .getSize () : plotArea .getSize ();
439+
440+ GC gc = e .gc ;
441+ gc .setBackground (Colors .RED );
442+ gc .setForeground (Colors .DARK_RED );
443+ gc .setAlpha (45 );
444+ gc .setLineStyle (SWT .LINE_DASH );
445+ gc .setLineWidth (2 );
446+ gc .drawLine (xMin , 0 , xMin , rectangle .y );
447+ gc .drawLine (xMax , 0 , xMax , rectangle .y );
448+ gc .drawLine (xMin , y , xMax , y );
449+ }
450+ }
451+
452+ });
453+
211454 chartControl .set (chart );
212455 }
213456
@@ -288,4 +531,32 @@ public double getScore() {
288531 return score ;
289532 }
290533 }
534+
535+ private boolean isBoxSelection () {
536+
537+ if (userSelection .getStartX () != 0 && userSelection .getStartY () != 0 && userSelection .getStopX () != 0 && userSelection .getStopY () != 0 ) {
538+ return true ;
539+ }
540+ return false ;
541+ }
542+
543+ private List <SampleScore > getSampleScoreList () {
544+
545+ IResultsMVA results = evaluationPCA .getResults ();
546+ List <IResultMVA > resultList = results .getPcaResultList ();
547+ List <SampleScore > sampleScores = new ArrayList <>();
548+ for (IResultMVA result : resultList ) {
549+ ISample sample = result .getSample ();
550+ double [] scoreMatrix = result .getScoreVector ();
551+
552+ if (currentPC < scoreMatrix .length ) {
553+ double score = scoreMatrix [currentPC ];
554+ String sampleName = sample .getSampleName ();
555+ sampleScores .add (new SampleScore (sampleName , score ));
556+ }
557+ }
558+
559+ sampleScores .sort (Comparator .comparingDouble (SampleScore ::getScore ).reversed ());
560+ return sampleScores ;
561+ }
291562}
0 commit comments