Skip to content

Commit

Permalink
Merge pull request #1862 from lorenzgerber/selection_update
Browse files Browse the repository at this point in the history
PCA: Update Feature Table Selection from Loading Plot dblclick
  • Loading branch information
eselmeister authored Sep 11, 2024
2 parents a1c29af + 8521d6f commit d86ec87
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,19 @@
* Contributors:
* Jan Holy - initial API and implementation
* Philip Wenig - get rid of JavaFX, feature selection
* Lorenz Gerber - update feature table selection from loading plot
*******************************************************************************/
package org.eclipse.chemclipse.xxd.process.supplier.pca.ui.parts;

import java.util.List;

import jakarta.inject.Inject;

import org.eclipse.chemclipse.xxd.process.supplier.pca.model.EvaluationPCA;
import org.eclipse.chemclipse.xxd.process.supplier.pca.ui.swt.ExtendedFeatureListUI;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;

import jakarta.inject.Inject;

public class FeatureTablePart extends AbstractPartPCA<ExtendedFeatureListUI> {

@Inject
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
*
* Contributors:
* Philip Wenig - initial API and implementation
* Lorenz Gerber - update feature table selection from loading plot
*******************************************************************************/
package org.eclipse.chemclipse.xxd.process.supplier.pca.ui.swt;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.chemclipse.model.statistics.IVariable;
Expand All @@ -30,6 +32,8 @@
import org.eclipse.chemclipse.swt.ui.components.InformationUI;
import org.eclipse.chemclipse.swt.ui.components.SearchSupportUI;
import org.eclipse.chemclipse.swt.ui.notifier.UpdateNotifierUI;
import org.eclipse.chemclipse.ux.extension.xxd.ui.part.support.DataUpdateSupport;
import org.eclipse.chemclipse.ux.extension.xxd.ui.part.support.IDataUpdateListener;
import org.eclipse.chemclipse.ux.extension.xxd.ui.swt.IExtendedPartUI;
import org.eclipse.chemclipse.ux.extension.xxd.ui.swt.ISettingsHandler;
import org.eclipse.chemclipse.xxd.process.supplier.pca.core.ProcessorPCA;
Expand All @@ -38,9 +42,11 @@
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.Feature;
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.FeatureDataMatrix;
import org.eclipse.chemclipse.xxd.process.supplier.pca.preferences.PreferenceSupplier;
import org.eclipse.chemclipse.xxd.process.supplier.pca.ui.Activator;
import org.eclipse.chemclipse.xxd.process.supplier.pca.ui.preferences.PreferencePage;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
Expand All @@ -61,11 +67,37 @@ public class ExtendedFeatureListUI extends Composite implements IExtendedPartUI
//
private EvaluationPCA evaluationPCA = null;
private FeatureDataMatrix featureDataMatrix = null;
//
private Composite control;

public ExtendedFeatureListUI(Composite parent, int style) {

super(parent, style);
createControl();
//
DataUpdateSupport dataUpdateSupport = new DataUpdateSupport(Activator.getDefault().getEventBroker());
dataUpdateSupport.subscribe(IChemClipseEvents.TOPIC_PCA_UPDATE_RESULT, IChemClipseEvents.EVENT_BROKER_DATA);
dataUpdateSupport.add(new IDataUpdateListener() {

@Override
public void update(String topic, List<Object> objects) {

if(evaluationPCA != null) {
if(DataUpdateSupport.isVisible(control)) {
if(IChemClipseEvents.TOPIC_PCA_UPDATE_RESULT.equals(topic)) {
if(objects.size() == 1) {
Object object = objects.get(0);
if(object instanceof Feature feature) {
if(evaluationPCA.getFeatureDataMatrix().getFeatures().contains(feature)) {
listControl.get().setSelection(new StructuredSelection(feature));
}
}
}
}
}
}
}
});
}

public void setInput(EvaluationPCA evaluationPCA) {
Expand All @@ -91,6 +123,7 @@ private void createControl() {
createToolbarInfo(this);
//
initialize();
control = this;
}

private void initialize() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2020, 2022 Lablicate GmbH.
* Copyright (c) 2020, 2024 Lablicate GmbH.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
Expand All @@ -8,29 +8,50 @@
*
* Contributors:
* Philip Wenig - initial API and implementation
* Lorenz Gerber - update feature table selection from loading plot
*******************************************************************************/
package org.eclipse.chemclipse.xxd.process.supplier.pca.ui.swt;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import org.eclipse.chemclipse.model.statistics.IVariable;
import org.eclipse.chemclipse.numeric.core.IPoint;
import org.eclipse.chemclipse.numeric.core.Point;
import org.eclipse.chemclipse.support.events.IChemClipseEvents;
import org.eclipse.chemclipse.swt.ui.notifier.UpdateNotifierUI;
import org.eclipse.chemclipse.ux.extension.xxd.ui.swt.IExtendedPartUI;
import org.eclipse.chemclipse.ux.extension.xxd.ui.swt.ISettingsHandler;
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.EvaluationPCA;
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.Feature;
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.FeatureDelta;
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.IAnalysisSettings;
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.IResultPCA;
import org.eclipse.chemclipse.xxd.process.supplier.pca.model.IResultsPCA;
import org.eclipse.chemclipse.xxd.process.supplier.pca.ui.chart2d.LoadingsPlot;
import org.eclipse.chemclipse.xxd.process.supplier.pca.ui.preferences.PreferencePage;
import org.eclipse.chemclipse.xxd.process.supplier.pca.ui.preferences.PreferencePageLoadingPlot;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swtchart.Range;
import org.eclipse.swtchart.extensions.core.BaseChart;
import org.eclipse.swtchart.extensions.core.IChartSettings;
import org.eclipse.swtchart.extensions.core.IMouseSupport;
import org.eclipse.swtchart.extensions.events.IHandledEventProcessor;

public class ExtendedLoadingsPlot extends Composite implements IExtendedPartUI {

private AtomicReference<LoadingsPlot> plotControl = new AtomicReference<>();
private PrincipalComponentUI principalComponentUI;
private AtomicReference<PrincipalComponentUI> principalComponentControl = new AtomicReference<>();
//
private EvaluationPCA evaluationPCA = null;

Expand Down Expand Up @@ -68,7 +89,7 @@ private void createToolbarMain(Composite parent) {
composite.setLayoutData(gridData);
composite.setLayout(new GridLayout(2, false));
//
principalComponentUI = createPrincipalComponentUI(composite);
createPrincipalComponentUI(composite);
createSettingsButton(composite);
}

Expand All @@ -77,10 +98,104 @@ private void createPlot(Composite parent) {
LoadingsPlot plot = new LoadingsPlot(parent, SWT.BORDER);
plot.setLayoutData(new GridData(GridData.FILL_BOTH));
//
IChartSettings chartSettings = plot.getChartSettings();
chartSettings.addHandledEventProcessor(new IHandledEventProcessor() {

@Override
public int getEvent() {

return IMouseSupport.EVENT_MOUSE_DOUBLE_CLICK;
}

@Override
public int getButton() {

return IMouseSupport.MOUSE_BUTTON_LEFT;
}

@Override
public int getStateMask() {

return SWT.NONE;
}

@Override
public void handleEvent(BaseChart baseChart, Event event) {

if(evaluationPCA != null) {
/*
* Determine the x|y coordinates.
*/
Rectangle rectangle = baseChart.getPlotArea().getBounds();
int x = event.x;
int y = event.y;
int width = rectangle.width;
int height = rectangle.height;
/*
* Calculate the selected point.
*/
Range rangeX = baseChart.getAxisSet().getXAxis(BaseChart.ID_PRIMARY_X_AXIS).getRange();
Range rangeY = baseChart.getAxisSet().getYAxis(BaseChart.ID_PRIMARY_Y_AXIS).getRange();
double pX = rangeX.lower + (rangeX.upper - rangeX.lower) * ((1.0d / width) * x);
double pY = rangeY.lower + (rangeY.upper - rangeY.lower) * ((1.0d / height) * y);
/*
* Map the result deltas.
*/
PrincipalComponentUI principalComponentUI = principalComponentControl.get();
int pcX = principalComponentUI.getPCX();
int pcY = principalComponentUI.getPCY();
IResultsPCA<? extends IResultPCA, ? extends IVariable> resultsPCA = evaluationPCA.getResults();
List<FeatureDelta> featureDeltas = new ArrayList<>();
//
// Here need to prepare a result object with loading vectors per variable
//
for(int i = 0; i < resultsPCA.getExtractedVariables().size(); i++) {
double[] variableLoading = getVariableLoading(resultsPCA, i);
IPoint pointResult = getPoint(variableLoading, pcX, pcY, i);
double deltaX = Math.abs(pointResult.getX() - pX);
double deltaY = Math.abs(pointResult.getY() - pY);
featureDeltas.add(new FeatureDelta(evaluationPCA.getFeatureDataMatrix().getFeatures().get(i), deltaX, deltaY));
}
/*
* Get the closest result.
*/
if(!featureDeltas.isEmpty()) {
Collections.sort(featureDeltas, Comparator.comparing(FeatureDelta::getDeltaX).thenComparing(FeatureDelta::getDeltaY));
FeatureDelta featureDelta = featureDeltas.get(0);
Feature feature = featureDelta.getFeature();
UpdateNotifierUI.update(event.display, IChemClipseEvents.TOPIC_PCA_UPDATE_RESULT, feature);
}
}
}
});
plot.applySettings(chartSettings);
//
plotControl.set(plot);
}

private PrincipalComponentUI createPrincipalComponentUI(Composite parent) {
private double[] getVariableLoading(IResultsPCA<? extends IResultPCA, ? extends IVariable> results, int number) {

double[] variableLoading = new double[results.getLoadingVectors().size()];
for(int i = 0; i < results.getLoadingVectors().size(); i++) {
variableLoading[i] = results.getLoadingVectors().get(i)[number];
}
return variableLoading;
}

private IPoint getPoint(double[] variableLoading, int pcX, int pcY, int i) {

double rX = 0;
if(pcX != 0) {
rX = variableLoading[pcX - 1]; // e.g. 0 = PC1
} else {
rX = i;
}
double rY = variableLoading[pcY - 1]; // e.g. 1 = PC2
//
return new Point(rX, rY);
}

private void createPrincipalComponentUI(Composite parent) {

PrincipalComponentUI principalComponentUI = new PrincipalComponentUI(parent, SWT.NONE, PrincipalComponentUI.SPINNER_X | PrincipalComponentUI.SPINNER_Y);
principalComponentUI.setSelectionListener(new ISelectionListenerPCs() {
Expand All @@ -92,7 +207,7 @@ public void update(int pcX, int pcY, int pcZ) {
}
});
//
return principalComponentUI;
principalComponentControl.set(principalComponentUI);
}

private void createSettingsButton(Composite parent) {
Expand All @@ -109,13 +224,15 @@ public void apply(Display display) {

private void applySettings() {

PrincipalComponentUI principalComponentUI = principalComponentControl.get();
int pcX = principalComponentUI.getPCX();
int pcY = principalComponentUI.getPCY();
updatePlot(pcX, pcY);
}

private void updateWidgets() {

PrincipalComponentUI principalComponentUI = principalComponentControl.get();
if(evaluationPCA != null) {
IAnalysisSettings analysisSettings = evaluationPCA.getSamples().getAnalysisSettings();
principalComponentUI.setInput(analysisSettings);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (c) 2024 Lablicate GmbH.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Lorenz Gerber - initial API and implementation
*******************************************************************************/
package org.eclipse.chemclipse.xxd.process.supplier.pca.model;

import java.util.Objects;

public class FeatureDelta {

private Feature feature = null;
private double deltaX = 0;
private double deltaY = 0;

public FeatureDelta(Feature feature, double deltaX, double deltaY) {

this.feature = feature;
this.deltaX = deltaX;
this.deltaY = deltaY;
}

public Feature getFeature() {

return feature;
}

public double getDeltaX() {

return deltaX;
}

public double getDeltaY() {

return deltaY;
}

@Override
public int hashCode() {

return Objects.hash(feature);
}

@Override
public boolean equals(Object obj) {

if(this == obj)
return true;
if(obj == null)
return false;
if(getClass() != obj.getClass())
return false;
FeatureDelta other = (FeatureDelta)obj;
return Objects.equals(feature, other.feature);
}

@Override
public String toString() {

return "FeatureDelta [featurePCA=" + feature + ", deltaX=" + deltaX + ", deltaY=" + deltaY + "]";
}
}

0 comments on commit d86ec87

Please sign in to comment.