blob: 57b950ec3d1d4de93c9185f9afe4a1b81c1e3e6c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Agence spatiale canadienne / Canadian Space Agency
* 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:
* Pierre Allard,
* Regent L'Archeveque - initial API and implementation
*
* SPDX-License-Identifier: EPL-1.0
*******************************************************************************/
package org.eclipse.apogy.core.ui.composites;
import java.text.DecimalFormat;
import javax.vecmath.Point3d;
import org.eclipse.apogy.common.math.ApogyCommonMathFacade;
import org.eclipse.apogy.common.math.ApogyCommonMathFactory;
import org.eclipse.apogy.common.math.Matrix4x4;
import org.eclipse.apogy.common.math.Tuple3d;
import org.eclipse.apogy.common.math.ui.composites.Tuple3dComposite;
import org.eclipse.apogy.core.ApogyCorePackage;
import org.eclipse.apogy.core.ApogySystemApiAdapter;
import org.eclipse.apogy.core.PoseProvider;
import org.eclipse.apogy.core.invocator.ApogyCoreInvocatorFacade;
import org.eclipse.apogy.core.invocator.VariableFeatureReference;
import org.eclipse.apogy.core.invocator.VariablesList;
import org.eclipse.apogy.core.invocator.ui.composites.VariableFeatureReferenceComposite;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
public class ApogySystemPoseComposite extends ApogySystemApiAdapterBasedComposite {
public static final String DEGREE_STRING = "\u00b0";
public static final int ATTITUDE_INDICATOR_WIDTH = 200;
public static final int ATTITUDE_INDICATOR_HEIGHT = 200;
private Adapter poseAdapter;
private VariablesList variables;
// private VariableFeatureReference variableFeatureReference;
private Tuple3d previousPosition = null;
private final Tuple3d position = ApogyCommonMathFactory.eINSTANCE.createTuple3d();
private final Tuple3d orientation = ApogyCommonMathFactory.eINSTANCE.createTuple3d();
private double minimumDeltaPosition = 0.05;
private double tripDistance = 0.0;
private final DecimalFormat distanceFormat = new DecimalFormat("0.00");
private Text txtVariableFeatureReference;
private Text txtTripDistance;
private Button btnTripReset;
private Button btnTripSet;
private Tuple3dComposite translationComposite;
private Tuple3dComposite rotationComposite;
public ApogySystemPoseComposite(Composite parent, int style) {
this(parent, style, null, null, 0.0);
}
public ApogySystemPoseComposite(Composite parent, int style, VariablesList variables,
VariableFeatureReference variableFeatureReference, double tripDistance) {
super(parent, style);
this.tripDistance = tripDistance;
this.variables = variables;
GridLayout layout = new GridLayout(4, false);
setLayout(layout);
Label lblVariableFeatureReference = new Label(this, SWT.NONE);
lblVariableFeatureReference.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
lblVariableFeatureReference.setText("Variable Feature : ");
this.txtVariableFeatureReference = new Text(this, SWT.BORDER);
GridData gd_txtVariableFeatureReference = new GridData(SWT.FILL, SWT.CENTER, false, false, 2, 1);
gd_txtVariableFeatureReference.widthHint = 300;
this.txtVariableFeatureReference.setLayoutData(gd_txtVariableFeatureReference);
Button btnSelectVariableFeatureReference = new Button(this, SWT.PUSH);
btnSelectVariableFeatureReference.setText("Select...");
btnSelectVariableFeatureReference.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
final Shell shell = Display.getCurrent().getActiveShell();
Dialog dialog = new Dialog(shell) {
VariableFeatureReferenceComposite vfrComposite = null;
@Override
protected Control createDialogArea(Composite parent) {
Composite area = (Composite) super.createDialogArea(parent);
this.vfrComposite = new VariableFeatureReferenceComposite(area, SWT.NONE);
this.vfrComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1));
this.vfrComposite.set(ApogySystemPoseComposite.this.variables,
ApogySystemPoseComposite.this.getVariableFeatureReference());
return area;
}
@Override
public boolean close() {
if (this.vfrComposite != null && !this.vfrComposite.isDisposed()) {
this.vfrComposite.set(null, null);
this.vfrComposite.dispose();
}
return super.close();
}
};
int result = dialog.open();
if (result == Window.OK) {
// Update displayed VFR.
updateDisplayedVariableFeatureReference();
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
// Trip Distance.
Label lblPOdometer = new Label(this, SWT.NONE);
lblPOdometer.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
lblPOdometer.setText("Trip Odometer (m):");
this.txtTripDistance = new Text(this, SWT.BORDER | SWT.SINGLE);
this.txtTripDistance.setText(this.distanceFormat.format(this.tripDistance));
this.txtTripDistance.setEnabled(false);
GridData gd_lblTripdistancelabel = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
gd_lblTripdistancelabel.widthHint = 150;
gd_lblTripdistancelabel.minimumWidth = 150;
this.txtTripDistance.setLayoutData(gd_lblTripdistancelabel);
this.btnTripReset = new Button(this, SWT.NONE);
this.btnTripReset.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
this.btnTripReset.setText("Reset");
this.btnTripReset.setEnabled(false);
this.btnTripReset.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
setTripDistance(0.0);
// txtTripDistance.setText(distanceFormat.format(0.0));
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
this.btnTripSet = new Button(this, SWT.NONE);
this.btnTripSet.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1));
this.btnTripSet.setText("Set To Value");
this.btnTripSet.setEnabled(false);
this.btnTripSet.addSelectionListener(new SelectionListener() {
@Override
public void widgetSelected(SelectionEvent e) {
OdometrySetValueDialog dialog = new OdometrySetValueDialog(getShell());
if (dialog.open() == Window.OK) {
setTripDistance(dialog.tripDistanceValue);
}
}
@Override
public void widgetDefaultSelected(SelectionEvent e) {
}
});
// Translation
Label lblPosition = new Label(this, SWT.NONE);
lblPosition.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
lblPosition.setAlignment(SWT.RIGHT);
lblPosition.setText("Position (m):");
this.translationComposite = new Tuple3dComposite(this, SWT.NONE, "0.00");
this.translationComposite.setEnabled(false);
GridData gd_translationComposite = new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1);
gd_translationComposite.minimumWidth = 300;
gd_translationComposite.widthHint = 300;
this.translationComposite.setLayoutData(gd_translationComposite);
this.translationComposite.setTuple3d(this.position);
this.translationComposite.setEnableEditing(false);
// Rotation
Label lblOrientation = new Label(this, SWT.NONE);
lblOrientation.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
lblOrientation.setAlignment(SWT.RIGHT);
lblOrientation.setText("Orientation (" + DEGREE_STRING + "):");
this.rotationComposite = new Tuple3dComposite(this, SWT.NONE, "0.0");
this.rotationComposite.setEnabled(false);
GridData gd_rotationComposite = new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1);
gd_rotationComposite.minimumWidth = 300;
gd_rotationComposite.widthHint = 300;
this.rotationComposite.setLayoutData(gd_rotationComposite);
this.rotationComposite.setTuple3d(this.orientation);
this.rotationComposite.setEnableEditing(false);
setVariableFeatureReference(variableFeatureReference);
updateDisplayedVariableFeatureReference();
addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
// Unregister adapter from apogySystemApiAdapter if required.
if (getApogySystemApiAdapter() != null) {
getApogySystemApiAdapter().eAdapters().remove(getPoseAdapter());
}
// Clear the Tuple3D composites.
if (!ApogySystemPoseComposite.this.translationComposite.isDisposed())
ApogySystemPoseComposite.this.translationComposite.setTuple3d(null);
if (!ApogySystemPoseComposite.this.rotationComposite.isDisposed())
ApogySystemPoseComposite.this.rotationComposite.setTuple3d(null);
}
});
}
public double getTripDistance() {
return this.tripDistance;
}
public void setTripDistance(double tripDistance) {
this.tripDistance = tripDistance;
// Do this on UI Thread.
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
// Update distance displays
if (ApogySystemPoseComposite.this.txtTripDistance != null
&& !ApogySystemPoseComposite.this.txtTripDistance.isDisposed()) {
ApogySystemPoseComposite.this.txtTripDistance
.setText(ApogySystemPoseComposite.this.distanceFormat.format(tripDistance));
}
}
});
}
@Override
protected void apogySystemApiAdapterChanged(final ApogySystemApiAdapter oldApogySystemApiAdapter,
final ApogySystemApiAdapter newApogySystemApiAdapter) {
// Do this on UI Thread.
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
updateApogySystemApiAdapter(oldApogySystemApiAdapter, newApogySystemApiAdapter);
}
});
}
public void setVariableList(final VariablesList variables) {
this.variables = variables;
}
@Override
public void setVariableFeatureReference(VariableFeatureReference newVariableFeatureReference) {
super.setVariableFeatureReference(newVariableFeatureReference);
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
updateDisplayedVariableFeatureReference();
}
});
}
/**
* Returns the minimum distance between two position update that should be
* considered significant enough to update the trip odometer distance.
*
* @return minimum distance, in meters.
*/
public double getMinimumDeltaPosition() {
return this.minimumDeltaPosition;
}
/**
* Sets the minimum distance between two position update that should be
* considered significant enough to update the trip odometer distance.
*
* @param minimumDeltaPosition The minimum distance, in meters.
*/
public void setMinimumDeltaPosition(double minimumDeltaPosition) {
this.minimumDeltaPosition = minimumDeltaPosition;
}
protected void updateApogySystemApiAdapter(ApogySystemApiAdapter oldApogySystemApiAdapter,
ApogySystemApiAdapter newApogySystemApiAdapter) {
// Unregister adapter from previous apogySystemApiAdapter is required.
if (oldApogySystemApiAdapter != null) {
oldApogySystemApiAdapter.eAdapters().remove(getPoseAdapter());
}
if (newApogySystemApiAdapter != null) {
// Updates pose.
updatePose(newApogySystemApiAdapter.getPoseTransform());
// Register adapter to new apogySystemApiAdapter.
newApogySystemApiAdapter.eAdapters().add(getPoseAdapter());
}
// Enables / Disable displays.
setDisplaysEnabled(newApogySystemApiAdapter != null);
}
protected void setDisplaysEnabled(boolean enabled) {
if (this.translationComposite != null && !this.translationComposite.isDisposed()) {
this.translationComposite.setEnabled(enabled);
}
if (this.rotationComposite != null && !this.rotationComposite.isDisposed()) {
this.rotationComposite.setEnabled(enabled);
}
if (this.txtTripDistance != null && !this.txtTripDistance.isDisposed()) {
this.txtTripDistance.setEnabled(enabled);
}
if (this.btnTripReset != null && !this.btnTripReset.isDisposed()) {
this.btnTripReset.setEnabled(enabled);
}
if (this.btnTripSet != null && !this.btnTripSet.isDisposed()) {
this.btnTripSet.setEnabled(enabled);
}
}
protected void updateDisplayedVariableFeatureReference() {
if (this.txtVariableFeatureReference != null && !this.txtVariableFeatureReference.isDisposed()) {
String text = ApogyCoreInvocatorFacade.INSTANCE
.getVariableFeatureReferenceString(this.getVariableFeatureReference());
this.txtVariableFeatureReference.setText(text);
}
}
protected void updatePose(Matrix4x4 newPose) {
if (newPose != null) {
// Updates Position.
Tuple3d pos = ApogyCommonMathFacade.INSTANCE.extractPosition(newPose);
this.position.setX(pos.getX());
this.position.setY(pos.getY());
this.position.setZ(pos.getZ());
// Updates Orientation.
Tuple3d ori = ApogyCommonMathFacade.INSTANCE.extractOrientation(newPose);
this.orientation.setX(Math.toDegrees(ori.getX()));
this.orientation.setY(Math.toDegrees(ori.getY()));
this.orientation.setZ(Math.toDegrees(ori.getZ()));
// Update distance.
if (this.previousPosition != null) {
Point3d p0 = new Point3d(this.previousPosition.asTuple3d());
Point3d p1 = new Point3d(pos.asTuple3d());
double distanceDelta = p0.distance(p1);
// Checks if the displacement is significant enough.
if (distanceDelta >= this.minimumDeltaPosition) {
this.tripDistance += distanceDelta;
this.previousPosition = EcoreUtil.copy(this.position);
}
} else {
this.previousPosition = EcoreUtil.copy(this.position);
}
// Update distance displays
if (this.txtTripDistance != null && !this.txtTripDistance.isDisposed()) {
this.txtTripDistance.setText(this.distanceFormat.format(this.tripDistance));
}
}
}
protected Adapter getPoseAdapter() {
if (this.poseAdapter == null) {
this.poseAdapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
if (msg.getNotifier() instanceof PoseProvider) {
int featureID = msg.getFeatureID(PoseProvider.class);
switch (featureID) {
case ApogyCorePackage.POSE_PROVIDER__POSE_TRANSFORM:
if (msg.getNewValue() instanceof Matrix4x4) {
final Matrix4x4 newPose = (Matrix4x4) msg.getNewValue();
// Do this on UI Thread.
if (!ApogySystemPoseComposite.this.isDisposed()) {
getDisplay().asyncExec(new Runnable() {
@Override
public void run() {
updatePose(newPose);
}
});
}
}
break;
default:
break;
}
}
}
};
}
return this.poseAdapter;
}
private class OdometrySetValueDialog extends Dialog {
public double tripDistanceValue = 0.0;
public OdometrySetValueDialog(Shell parentShell) {
super(parentShell);
}
@Override
protected Control createDialogArea(Composite parent) {
Composite container = (Composite) super.createDialogArea(parent);
container.setLayout(new GridLayout(2, false));
Label label = new Label(container, SWT.NONE);
label.setText("Trip distance (m) :");
Text text = new Text(container, SWT.BORDER);
text.setText("0.0");
GridData gd = new GridData(SWT.LEFT, SWT.CENTER, false, false);
gd.minimumWidth = 250;
gd.widthHint = 250;
text.setLayoutData(gd);
text.addKeyListener(new KeyListener() {
Color color = new Color(getDisplay(), text.getBackground().getRed(), text.getBackground().getGreen(),
text.getBackground().getBlue());
@Override
public void keyReleased(KeyEvent e) {
try {
OdometrySetValueDialog.this.tripDistanceValue = Double.parseDouble(text.getText());
text.setBackground(this.color);
} catch (Exception ex) {
text.setBackground(new Color(getDisplay(), 255, 0, 0));
}
}
@Override
public void keyPressed(KeyEvent e) {
try {
OdometrySetValueDialog.this.tripDistanceValue = Double.parseDouble(text.getText());
text.setBackground(this.color);
} catch (Exception ex) {
text.setBackground(new Color(getDisplay(), 255, 0, 0));
}
}
});
return container;
}
// overriding this methods allows you to set the
// title of the custom dialog
@Override
protected void configureShell(Shell newShell) {
super.configureShell(newShell);
newShell.setText("Set current trip distance value");
}
@Override
protected Point getInitialSize() {
return new Point(450, 300);
}
}
}