Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PCA: Update Feature Table Selection from Loading Plot dblclick #1862

Merged
merged 1 commit into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 + "]";
}
}