blob: ab294376730894716eca16576a206ecc75ef28ba [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,
<<<<<<< HEAD
* Sebastien Gemme
*
=======
* Sebastien Gemme - initial API and implementation
*
>>>>>>> refs/heads/eclipse_pa
* SPDX-License-Identifier: EPL-1.0
*
*******************************************************************************/
package org.eclipse.apogy.common.topology.util;
import java.util.ArrayList;
import java.util.List;
import javax.vecmath.Matrix3d;
import javax.vecmath.Matrix4d;
import javax.vecmath.Vector3d;
import org.eclipse.apogy.common.topology.ApogyCommonTopologyFacade;
import org.eclipse.apogy.common.topology.ApogyCommonTopologyPackage;
import org.eclipse.apogy.common.topology.Node;
import org.eclipse.apogy.common.topology.PositionNode;
import org.eclipse.apogy.common.topology.RotationNode;
import org.eclipse.apogy.common.topology.TransformNode;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
public class NodeRelativePoseListener {
private Adapter adapter = null;
private final List<Node> nodes = new ArrayList<Node>();
private final double minimumDistanceDeltaMeters = 1E-6;
private final double minimumOrientationDeltaRad = 0.01;
private Matrix4d previousPose = null;
private Matrix4d newPose = null;
private Node fromNode;
private Node toNode;
/**
* Method called when a change in relative pose is detected. Sub-classes should
* override this method.
*
* @param newPose The new pose. Can be null if to or from node is null.
*/
public void relativePoseChanged(Matrix4d newPose) {
}
public Node getFromNode() {
return this.fromNode;
}
public void setFromNode(Node fromNode) {
if (this.fromNode != fromNode) {
this.fromNode = fromNode;
updateNodeList();
relativePoseChanged(computeNewPose());
}
}
public Node getToNode() {
return this.toNode;
}
public void setToNode(Node toNode) {
if (this.toNode != toNode) {
this.toNode = toNode;
updateNodeList();
relativePoseChanged(computeNewPose());
}
}
public Matrix4d getNewPose() {
return this.newPose;
}
/**
* Dispose of this NodeRelativePoseListener. The adapter is removed from all
* node and the adapter is then set to null. Should be called when the
* NodeRelativePoseListener is no longer needed.
*/
public void dispose() {
unregisterAdapter();
this.fromNode = null;
this.toNode = null;
this.adapter = null;
}
private boolean hasPoseChanged(Matrix4d newPose, Matrix4d newPreviousPose) {
boolean result = false;
if (this.previousPose == null) {
if (newPose != null)
this.previousPose = new Matrix4d(newPose);
result = true;
} else if (newPose != null && this.previousPose != null) {
if (!result) {
// First checks is translation has occured.
Vector3d previousPosition = new Vector3d();
Vector3d newPosition = new Vector3d();
this.previousPose.get(previousPosition);
newPose.get(newPosition);
newPosition.sub(previousPosition);
if (newPosition.length() > this.minimumDistanceDeltaMeters) {
result = true;
}
}
if (!result) {
// Then checks if rotation has occured.
Matrix3d previousRot = new Matrix3d();
Matrix3d newRot = new Matrix3d();
this.previousPose.get(previousRot);
newPose.get(newRot);
Vector3d previousOrientation = new Vector3d(1, 0, 0);
Vector3d newOrientation = new Vector3d(1, 0, 0);
previousRot.transform(previousOrientation);
newRot.transform(newOrientation);
if (Math.abs(previousOrientation.angle(newOrientation)) > this.minimumOrientationDeltaRad) {
result = true;
}
}
}
if (newPose != null) {
this.previousPose = new Matrix4d(newPose);
} else {
this.previousPose = new Matrix4d();
this.previousPose.setIdentity();
}
return result;
}
private void updatePose(Matrix4d newPose) {
if (hasPoseChanged(newPose, this.previousPose)) {
this.newPose = newPose;
relativePoseChanged(newPose);
}
}
private void updateNodeList() {
// Unregister from previous nodes.
unregisterAdapter();
// Clears list of nodes.
this.nodes.clear();
// If both from and to Node are defined.
if (this.fromNode != null && this.toNode != null) {
Node currentNode = this.fromNode;
while (currentNode != null) {
if (currentNode instanceof PositionNode || currentNode instanceof RotationNode) {
if (!this.nodes.contains(currentNode))
this.nodes.add(currentNode);
}
currentNode = currentNode.getParent();
}
// Gets the lineage of to node.
currentNode = this.toNode;
while (currentNode != null) {
if (currentNode instanceof PositionNode || currentNode instanceof RotationNode) {
if (!this.nodes.contains(currentNode))
this.nodes.add(currentNode);
}
currentNode = currentNode.getParent();
}
}
// Register to new nodes.
registerAdapter();
}
private void registerAdapter() {
for (Node node : this.nodes) {
node.eAdapters().add(getAdapter());
}
}
private void unregisterAdapter() {
for (Node node : this.nodes) {
node.eAdapters().remove(getAdapter());
}
}
private Adapter getAdapter() {
if (this.adapter == null) {
this.adapter = new AdapterImpl() {
@Override
public void notifyChanged(Notification msg) {
if (msg.getNotifier() instanceof TransformNode) {
int featureId = msg.getFeatureID(TransformNode.class);
if (featureId == ApogyCommonTopologyPackage.TRANSFORM_NODE__POSITION
|| featureId == ApogyCommonTopologyPackage.TRANSFORM_NODE__ROTATION_MATRIX) {
updatePose(computeNewPose());
} else if (featureId == ApogyCommonTopologyPackage.TRANSFORM_NODE__PARENT) {
updateNodeList();
updatePose(computeNewPose());
}
} else if (msg.getNotifier() instanceof PositionNode) {
int featureId = msg.getFeatureID(PositionNode.class);
if (featureId == ApogyCommonTopologyPackage.POSITION_NODE__POSITION) {
updatePose(computeNewPose());
} else if (featureId == ApogyCommonTopologyPackage.POSITION_NODE__PARENT) {
updateNodeList();
updatePose(computeNewPose());
}
} else if (msg.getNotifier() instanceof RotationNode) {
int featureId = msg.getFeatureID(RotationNode.class);
if (featureId == ApogyCommonTopologyPackage.ROTATION_NODE__ROTATION_MATRIX) {
updatePose(computeNewPose());
} else if (featureId == ApogyCommonTopologyPackage.ROTATION_NODE__PARENT) {
updateNodeList();
updatePose(computeNewPose());
}
}
}
};
}
return this.adapter;
}
protected Matrix4d computeNewPose() {
if (this.fromNode != null && this.toNode != null) {
return ApogyCommonTopologyFacade.INSTANCE.expressInFrame(this.fromNode, this.toNode);
} else {
return null;
}
}
}