
org.biojava.nbio.structure.Calc Maven / Gradle / Ivy
Show all versions of biojava-structure Show documentation
/*
* BioJava development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public Licence. This should
* be distributed with the code. If you do not have a copy,
* see:
*
* http://www.gnu.org/copyleft/lesser.html
*
* Copyright for this code is held jointly by the individual
* authors. These should be listed in @author doc comments.
*
* For more information on the BioJava project and its aims,
* or to join the biojava-l mailing list, visit the home page
* at:
*
* http://www.biojava.org/
*
* Created on 08.05.2004
*
*/
package org.biojava.nbio.structure ;
import org.biojava.nbio.structure.jama.Matrix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.vecmath.Matrix3d;
import javax.vecmath.Matrix4d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;
/**
* Utility operations on Atoms, AminoAcids, etc.
*
* Currently the coordinates of an Atom are stored as an array
* of size 3 (double[3]). It would be more powerful to use Point3D from
* javax.vecmath, but unfortunately this is not a part of standard
* java installations, since it comes with java3d. So to keep things
* simple at the moment biojava does not depend on java3d.
*
* @author Andreas Prlic
* @since 1.4
* @version %I% %G%
*/
public class Calc {
private final static Logger logger = LoggerFactory.getLogger(Calc.class);
/**
* calculate distance between two atoms.
*
* @param a an Atom object
* @param b an Atom object
* @return a double
*/
public static final double getDistance(Atom a, Atom b) {
double x = a.getX() - b.getX();
double y = a.getY() - b.getY();
double z = a.getZ() - b.getZ();
double s = x * x + y * y + z * z;
return Math.sqrt(s);
}
/**
* Will calculate the square of distances between two atoms. This will be
* faster as it will not perform the final square root to get the actual
* distance. Use this if doing large numbers of distance comparisons - it is
* marginally faster than getDistance().
*
* @param a an Atom object
* @param b an Atom object
* @return a double
*/
public static double getDistanceFast(Atom a, Atom b) {
double x = a.getX() - b.getX();
double y = a.getY() - b.getY();
double z = a.getZ() - b.getZ();
return x * x + y * y + z * z;
}
public static final Atom invert(Atom a) {
double[] coords = new double[]{0.0,0.0,0.0} ;
Atom zero = new AtomImpl();
zero.setCoords(coords);
return subtract(zero, a);
}
/** add two atoms ( a + b).
*
* @param a an Atom object
* @param b an Atom object
* @return an Atom object
*/
public static final Atom add(Atom a, Atom b){
Atom c = new AtomImpl();
c.setX( a.getX() + b.getX() );
c.setY( a.getY() + b.getY() );
c.setZ( a.getZ() + b.getZ() );
return c ;
}
/** subtract two atoms ( a - b).
*
* @param a an Atom object
* @param b an Atom object
* @return n new Atom object representing the difference
*/
public static final Atom subtract(Atom a, Atom b) {
Atom c = new AtomImpl();
c.setX( a.getX() - b.getX() );
c.setY( a.getY() - b.getY() );
c.setZ( a.getZ() - b.getZ() );
return c ;
}
/** Vector product (cross product).
*
* @param a an Atom object
* @param b an Atom object
* @return an Atom object
*/
public static final Atom vectorProduct(Atom a , Atom b){
Atom c = new AtomImpl();
c.setX( a.getY() * b.getZ() - a.getZ() * b.getY() ) ;
c.setY( a.getZ() * b.getX() - a.getX() * b.getZ() ) ;
c.setZ( a.getX() * b.getY() - a.getY() * b.getX() ) ;
return c ;
}
/**
* Scalar product (dot product).
*
* @param a an Atom object
* @param b an Atom object
* @return a double
*/
public static final double scalarProduct(Atom a, Atom b) {
return a.getX() * b.getX() + a.getY() * b.getY() + a.getZ() * b.getZ();
}
/**
* Gets the length of the vector (2-norm)
*
* @param a an Atom object
* @return Square root of the sum of the squared elements
*/
public static final double amount(Atom a){
return Math.sqrt(scalarProduct(a,a));
}
/**
* Gets the angle between two vectors
*
* @param a an Atom object
* @param b an Atom object
* @return Angle between a and b in degrees, in range [0,180].
* If either vector has length 0 then angle is not defined and NaN is returned
*/
public static final double angle(Atom a, Atom b){
Vector3d va = new Vector3d(a.getCoords());
Vector3d vb = new Vector3d(b.getCoords());
return Math.toDegrees(va.angle(vb));
}
/**
* Returns the unit vector of vector a .
*
* @param a an Atom object
* @return an Atom object
*/
public static final Atom unitVector(Atom a) {
double amount = amount(a) ;
double[] coords = new double[3];
coords[0] = a.getX() / amount ;
coords[1] = a.getY() / amount ;
coords[2] = a.getZ() / amount ;
a.setCoords(coords);
return a;
}
/**
* Calculate the torsion angle, i.e. the angle between the normal vectors of the
* two plains a-b-c and b-c-d.
* See http://en.wikipedia.org/wiki/Dihedral_angle
* @param a an Atom object
* @param b an Atom object
* @param c an Atom object
* @param d an Atom object
* @return the torsion angle in degrees, in range +-[0,180].
* If either first 3 or last 3 atoms are colinear then torsion angle is not defined and NaN is returned
*/
public static final double torsionAngle(Atom a, Atom b, Atom c, Atom d) {
Atom ab = subtract(a,b);
Atom cb = subtract(c,b);
Atom bc = subtract(b,c);
Atom dc = subtract(d,c);
Atom abc = vectorProduct(ab,cb);
Atom bcd = vectorProduct(bc,dc);
double angl = angle(abc,bcd) ;
/* calc the sign: */
Atom vecprod = vectorProduct(abc,bcd);
double val = scalarProduct(cb,vecprod);
if (val<0.0) angl = -angl ;
return angl;
}
/**
* Calculate the phi angle.
*
* @param a an AminoAcid object
* @param b an AminoAcid object
* @return a double
* @throws StructureException if aminoacids not connected or if any of the 4 needed atoms missing
*/
public static final double getPhi(AminoAcid a, AminoAcid b) throws StructureException {
if ( ! isConnected(a,b)){
throw new StructureException("can not calc Phi - AminoAcids are not connected!") ;
}
Atom a_C = a.getC();
Atom b_N = b.getN();
Atom b_CA = b.getCA();
Atom b_C = b.getC();
// C and N were checked in isConnected already
if (b_CA==null) throw new StructureException("Can not calculate Phi, CA atom is missing");
return torsionAngle(a_C,b_N,b_CA,b_C);
}
/**
* Calculate the psi angle.
*
* @param a an AminoAcid object
* @param b an AminoAcid object
* @return a double
* @throws StructureException if aminoacids not connected or if any of the 4 needed atoms missing
*/
public static final double getPsi(AminoAcid a, AminoAcid b) throws StructureException {
if ( ! isConnected(a,b)) {
throw new StructureException("can not calc Psi - AminoAcids are not connected!") ;
}
Atom a_N = a.getN();
Atom a_CA = a.getCA();
Atom a_C = a.getC();
Atom b_N = b.getN();
// C and N were checked in isConnected already
if (a_CA==null) throw new StructureException("Can not calculate Psi, CA atom is missing");
return torsionAngle(a_N,a_CA,a_C,b_N);
}
/**
* Test if two amino acids are connected, i.e.
* if the distance from C to N < 2.5 Angstrom.
*
* If one of the AminoAcids has an atom missing, returns false.
*
* @param a an AminoAcid object
* @param b an AminoAcid object
* @return true if ...
*/
public static final boolean isConnected(AminoAcid a, AminoAcid b) {
Atom C = null ;
Atom N = null;
C = a.getC();
N = b.getN();
if ( C == null || N == null)
return false;
// one could also check if the CA atoms are < 4 A...
double distance = getDistance(C,N);
return distance < 2.5;
}
/**
* Rotate a single Atom aroud a rotation matrix.
* The rotation Matrix must be a pre-multiplication 3x3 Matrix.
*
* If the matrix is indexed m[row][col], then the matrix will be
* pre-multiplied (y=atom*M)
* @param atom atom to be rotated
* @param m a rotation matrix represented as a double[3][3] array
*/
public static final void rotate(Atom atom, double[][] m){
double x = atom.getX();
double y = atom.getY() ;
double z = atom.getZ();
double nx = m[0][0] * x + m[0][1] * y + m[0][2] * z ;
double ny = m[1][0] * x + m[1][1] * y + m[1][2] * z ;
double nz = m[2][0] * x + m[2][1] * y + m[2][2] * z ;
atom.setX(nx);
atom.setY(ny);
atom.setZ(nz);
}
/**
* Rotate a structure.
* The rotation Matrix must be a pre-multiplication Matrix.
*
* @param structure a Structure object
* @param rotationmatrix an array (3x3) of double
* representing the rotation matrix.
* @throws StructureException ...
*/
public static final void rotate(Structure structure,
double[][] rotationmatrix) throws StructureException {
if ( rotationmatrix.length != 3 ) {
throw new StructureException ("matrix does not have size 3x3 !");
}
AtomIterator iter = new AtomIterator(structure) ;
while (iter.hasNext()) {
Atom atom = iter.next() ;
Calc.rotate(atom,rotationmatrix);
}
}
/**
* Rotate a Group.
* The rotation Matrix must be a pre-multiplication Matrix.
*
* @param group a group object
* @param rotationmatrix an array (3x3) of double
* representing the rotation matrix.
* @throws StructureException ...
*/
public static final void rotate(Group group,
double[][] rotationmatrix) throws StructureException {
if ( rotationmatrix.length != 3 ) {
throw new StructureException ("matrix does not have size 3x3 !");
}
AtomIterator iter = new AtomIterator(group) ;
while (iter.hasNext()) {
Atom atom = null ;
atom = iter.next() ;
rotate(atom,rotationmatrix);
}
}
/**
* Rotate an Atom around a Matrix object.
* The rotation Matrix must be a pre-multiplication Matrix.
*
* @param atom atom to be rotated
* @param m rotation matrix to be applied to the atom
*/
public static final void rotate(Atom atom, Matrix m){
double x = atom.getX();
double y = atom.getY();
double z = atom.getZ();
double[][] ad = new double[][]{{x,y,z}};
Matrix am = new Matrix(ad);
Matrix na = am.times(m);
atom.setX(na.get(0,0));
atom.setY(na.get(0,1));
atom.setZ(na.get(0,2));
}
/**
* Rotate a group object.
* The rotation Matrix must be a pre-multiplication Matrix.
*
* @param group a group to be rotated
* @param m a Matrix object representing the rotation matrix
*/
public static final void rotate(Group group, Matrix m){
AtomIterator iter = new AtomIterator(group) ;
while (iter.hasNext()) {
Atom atom = iter.next() ;
rotate(atom,m);
}
}
/**
* Rotate a structure object.
* The rotation Matrix must be a pre-multiplication Matrix.
*
* @param structure the structure to be rotated
* @param m rotation matrix to be applied
*/
public static final void rotate(Structure structure, Matrix m){
AtomIterator iter = new AtomIterator(structure) ;
while (iter.hasNext()) {
Atom atom = iter.next() ;
rotate(atom,m);
}
}
/**
* Transform an array of atoms at once.
* The transformation Matrix must be a post-multiplication Matrix.
*
* @param ca array of Atoms to shift
* @param t transformation Matrix4d
*/
public static void transform(Atom[] ca, Matrix4d t) {
for (Atom atom : ca) Calc.transform(atom, t);
}
/**
* Transforms an atom object, given a Matrix4d (i.e. the vecmath library
* double-precision 4x4 rotation+translation matrix).
* The transformation Matrix must be a post-multiplication Matrix.
*
* @param atom
* @param m
*/
public static final void transform (Atom atom, Matrix4d m) {
Point3d p = new Point3d(atom.getX(),atom.getY(),atom.getZ());
m.transform(p);
atom.setX(p.x);
atom.setY(p.y);
atom.setZ(p.z);
}
/**
* Transforms a group object, given a Matrix4d (i.e. the vecmath library
* double-precision 4x4 rotation+translation matrix).
* The transformation Matrix must be a post-multiplication Matrix.
*
* @param group
* @param m
*/
public static final void transform (Group group, Matrix4d m) {
AtomIterator iter = new AtomIterator(group) ;
while (iter.hasNext()) {
Atom atom = iter.next() ;
transform(atom,m);
}
}
/**
* Transforms a structure object, given a Matrix4d (i.e. the vecmath library
* double-precision 4x4 rotation+translation matrix).
* The transformation Matrix must be a post-multiplication Matrix.
*
* @param structure
* @param m
*/
public static final void transform (Structure structure, Matrix4d m) {
AtomIterator iter = new AtomIterator(structure) ;
while (iter.hasNext()) {
Atom atom = iter.next() ;
transform(atom,m);
}
}
/**
* Transforms a chain object, given a Matrix4d (i.e. the vecmath library
* double-precision 4x4 rotation+translation matrix).
* The transformation Matrix must be a post-multiplication Matrix.
*
* @param chain
* @param m
*/
public static final void transform (Chain chain, Matrix4d m) {
for (Group g:chain.getAtomGroups()) {
for (Atom atom: g.getAtoms()) {
transform(atom,m);
}
}
}
/**
* Translates an atom object, given a Vector3d (i.e. the vecmath library
* double-precision 3-d vector)
* @param atom
* @param v
*/
public static final void translate (Atom atom, Vector3d v) {
atom.setX(atom.getX()+v.x);
atom.setY(atom.getY()+v.y);
atom.setZ(atom.getZ()+v.z);
}
/**
* Translates a group object, given a Vector3d (i.e. the vecmath library
* double-precision 3-d vector)
* @param group
* @param v
*/
public static final void translate (Group group, Vector3d v) {
AtomIterator iter = new AtomIterator(group) ;
while (iter.hasNext()) {
Atom atom = iter.next() ;
translate(atom,v);
}
}
/**
* Translates a chain object, given a Vector3d (i.e. the vecmath library
* double-precision 3-d vector)
* @param chain
* @param v
*/
public static final void translate (Chain chain, Vector3d v) {
for (Group g:chain.getAtomGroups()) {
for (Atom atom: g.getAtoms()) {
translate(atom,v);
}
}
}
/**
* Translates a Structure object, given a Vector3d (i.e. the vecmath library
* double-precision 3-d vector)
* @param structure
* @param v
*/
public static final void translate (Structure structure, Vector3d v) {
AtomIterator iter = new AtomIterator(structure) ;
while (iter.hasNext()) {
Atom atom = iter.next() ;
translate(atom,v);
}
}
/** calculate structure + Matrix coodinates ...
*
* @param s the structure to operate on
* @param matrix a Matrix object
*/
public static final void plus(Structure s, Matrix matrix){
AtomIterator iter = new AtomIterator(s) ;
Atom oldAtom = null;
Atom rotOldAtom = null;
while (iter.hasNext()) {
Atom atom = null ;
atom = iter.next() ;
try {
if ( oldAtom != null){
logger.debug("before {}", getDistance(oldAtom,atom));
}
} catch (Exception e){
logger.error("Exception: ", e);
}
oldAtom = (Atom)atom.clone();
double x = atom.getX();
double y = atom.getY() ;
double z = atom.getZ();
double[][] ad = new double[][]{{x,y,z}};
Matrix am = new Matrix(ad);
Matrix na = am.plus(matrix);
double[] coords = new double[3] ;
coords[0] = na.get(0,0);
coords[1] = na.get(0,1);
coords[2] = na.get(0,2);
atom.setCoords(coords);
try {
if ( rotOldAtom != null){
logger.debug("after {}", getDistance(rotOldAtom,atom));
}
} catch (Exception e){
logger.error("Exception: ", e);
}
rotOldAtom = (Atom) atom.clone();
}
}
/** shift a structure with a vector.
*
* @param structure a Structure object
* @param a an Atom object representing a shift vector
*/
public static final void shift(Structure structure, Atom a ){
AtomIterator iter = new AtomIterator(structure) ;
while (iter.hasNext() ) {
Atom atom = null ;
atom = iter.next() ;
Atom natom = add(atom,a);
double x = natom.getX();
double y = natom.getY() ;
double z = natom.getZ();
atom.setX(x);
atom.setY(y);
atom.setZ(z);
}
}
/** Shift a vector.
*
* @param a vector a
* @param b vector b
*/
public static final void shift(Atom a, Atom b){
Atom natom = add(a,b);
double x = natom.getX();
double y = natom.getY() ;
double z = natom.getZ();
a.setX(x);
a.setY(y);
a.setZ(z);
}
/** Shift a Group with a vector.
*
* @param group a group object
* @param a an Atom object representing a shift vector
*/
public static final void shift(Group group , Atom a ){
AtomIterator iter = new AtomIterator(group) ;
while (iter.hasNext() ) {
Atom atom = null ;
atom = iter.next() ;
Atom natom = add(atom,a);
double x = natom.getX();
double y = natom.getY() ;
double z = natom.getZ();
atom.setX(x);
atom.setY(y);
atom.setZ(z);
}
}
/** Returns the center of mass of the set of atoms.
* @param atomSet a set of Atoms
* @return an Atom representing the Centroid of the set of atoms
*/
public static final Atom getCentroid(Atom[] atomSet){
double[] coords = new double[3];
coords[0] = 0;
coords[1] = 0;
coords[2] = 0 ;
for (Atom a : atomSet) {
coords[0] += a.getX();
coords[1] += a.getY();
coords[2] += a.getZ();
}
int n = atomSet.length;
coords[0] = coords[0] / n;
coords[1] = coords[1] / n;
coords[2] = coords[2] / n;
Atom vec = new AtomImpl();
vec.setCoords(coords);
return vec;
}
public static Atom centerOfMass(Atom[] points) {
Atom center = new AtomImpl();
float totalMass = 0.0f;
for (Atom a : points) {
float mass = a.getElement().getAtomicMass();
totalMass += mass;
center = scaleAdd(mass, a, center);
}
center = scaleEquals(center, 1.0f/totalMass);
return center;
}
/**
* Multiply elements of a by s (in place)
* @param a
* @param s
* @return the modified a
*/
public static Atom scaleEquals(Atom a, double s) {
double x = a.getX();
double y = a.getY();
double z = a.getZ();
x *= s;
y *= s;
z *= s;
//Atom b = new AtomImpl();
a.setX(x);
a.setY(y);
a.setZ(z);
return a;
}
/**
* Multiply elements of a by s
* @param a
* @param s
* @return A new Atom with s*a
*/
public static Atom scale(Atom a, double s) {
double x = a.getX();
double y = a.getY();
double z = a.getZ();
Atom b = new AtomImpl();
b.setX(x*s);
b.setY(y*s);
b.setZ(z*s);
return b;
}
/**
* Perform linear transformation s*X+B, and store the result in b
* @param s Amount to scale x
* @param x Input coordinate
* @param b Vector to translate (will be modified)
* @return b, after modification
*/
public static Atom scaleAdd(double s, Atom x, Atom b){
double xc = s*x.getX() + b.getX();
double yc = s*x.getY() + b.getY();
double zc = s*x.getZ() + b.getZ();
//Atom a = new AtomImpl();
b.setX(xc);
b.setY(yc);
b.setZ(zc);
return b;
}
/** Returns the Vector that needs to be applied to shift a set of atoms
* to the Centroid.
* @param atomSet array of Atoms
* @return the vector needed to shift the set of atoms to its geometric center
*/
public static final Atom getCenterVector(Atom[] atomSet){
Atom centroid = getCentroid(atomSet);
return getCenterVector(atomSet,centroid);
}
/** Returns the Vector that needs to be applied to shift a set of atoms
* to the Centroid, if the centroid is already known
* @param atomSet array of Atoms
* @return the vector needed to shift the set of atoms to its geometric center
*/
public static final Atom getCenterVector(Atom[] atomSet, Atom centroid){
double[] coords = new double[3];
coords[0] = 0 - centroid.getX();
coords[1] = 0 - centroid.getY();
coords[2] = 0 - centroid.getZ();
Atom shiftVec = new AtomImpl();
shiftVec.setCoords(coords);
return shiftVec;
}
/** Center the atoms at the Centroid.
* @param atomSet a set of Atoms
* @return an Atom representing the Centroid of the set of atoms
* @throws StructureException
* */
public static final Atom[] centerAtoms(Atom[] atomSet) throws StructureException {
Atom centroid = getCentroid(atomSet);
return centerAtoms(atomSet, centroid);
}
/** Center the atoms at the Centroid, if the centroid is already know.
* @param atomSet a set of Atoms
* @return an Atom representing the Centroid of the set of atoms
* @throws StructureException
* */
public static final Atom[] centerAtoms(Atom[] atomSet, Atom centroid) throws StructureException {
Atom shiftVector = getCenterVector(atomSet, centroid);
Atom[] newAtoms = new AtomImpl[atomSet.length];
for (int i =0 ; i < atomSet.length; i++){
Atom a = atomSet[i];
Atom n = add(a,shiftVector);
newAtoms[i] = n ;
}
return newAtoms;
}
/** creates a virtual C-beta atom. this might be needed when working with GLY
*
* thanks to Peter Lackner for a python template of this method.
* @param amino the amino acid for which a "virtual" CB atom should be calculated
* @return a "virtual" CB atom
* @throws StructureException
*/
public static final Atom createVirtualCBAtom(AminoAcid amino)
throws StructureException{
AminoAcid ala = StandardAminoAcid.getAminoAcid("ALA");
Atom aN = ala.getN();
Atom aCA = ala.getCA();
Atom aC = ala.getC();
Atom aCB = ala.getCB();
Atom[] arr1 = new Atom[3];
arr1[0] = aN;
arr1[1] = aCA;
arr1[2] = aC;
Atom[] arr2 = new Atom[3];
arr2[0] = amino.getN();
arr2[1] = amino.getCA();
arr2[2] = amino.getC();
// ok now we got the two arrays, do a SVD:
SVDSuperimposer svd = new SVDSuperimposer(arr2,arr1);
Matrix rotMatrix = svd.getRotation();
Atom tranMatrix = svd.getTranslation();
Calc.rotate(aCB,rotMatrix);
Atom virtualCB = Calc.add(aCB,tranMatrix);
virtualCB.setName("CB");
return virtualCB;
}
/**
* Gets euler angles for a matrix given in ZYZ convention.
* (as e.g. used by Jmol)
*
* @param m the rotation matrix
* @return the euler values for a rotation around Z, Y, Z in degrees...
*/
public static final double[] getZYZEuler(Matrix m) {
double m22 = m.get(2,2);
double rY = Math.toDegrees(Math.acos(m22));
double rZ1, rZ2;
if (m22 > .999d || m22 < -.999d) {
rZ1 = Math.toDegrees(Math.atan2(m.get(1,0), m.get(1,1)));
rZ2 = 0;
} else {
rZ1 = Math.toDegrees(Math.atan2(m.get(2,1), -m.get(2,0)));
rZ2 = Math.toDegrees(Math.atan2(m.get(1,2), m.get(0,2)));
}
return new double[] {rZ1,rY,rZ2};
}
/** Convert a rotation Matrix to Euler angles.
* This conversion uses conventions as described on page:
* http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm
* Coordinate System: right hand
* Positive angle: right hand
* Order of euler angles: heading first, then attitude, then bank
*
* @param m the rotation matrix
* @return a array of three doubles containing the three euler angles in radians
*/
public static final double[] getXYZEuler(Matrix m){
double heading, attitude, bank;
// Assuming the angles are in radians.
if (m.get(1,0) > 0.998) { // singularity at north pole
heading = Math.atan2(m.get(0,2),m.get(2,2));
attitude = Math.PI/2;
bank = 0;
} else if (m.get(1,0) < -0.998) { // singularity at south pole
heading = Math.atan2(m.get(0,2),m.get(2,2));
attitude = -Math.PI/2;
bank = 0;
} else {
heading = Math.atan2(-m.get(2,0),m.get(0,0));
bank = Math.atan2(-m.get(1,2),m.get(1,1));
attitude = Math.asin(m.get(1,0));
}
return new double[] { heading, attitude, bank };
}
/** This conversion uses NASA standard aeroplane conventions as described on page:
* http://www.euclideanspace.com/maths/geometry/rotations/euler/index.htm
* Coordinate System: right hand
* Positive angle: right hand
* Order of euler angles: heading first, then attitude, then bank.
* matrix row column ordering:
* [m00 m01 m02]
* [m10 m11 m12]
* [m20 m21 m22]
* @param heading in radians
* @param attitude in radians
* @param bank in radians
* @return the rotation matrix */
public static final Matrix matrixFromEuler(double heading, double attitude, double bank) {
// Assuming the angles are in radians.
double ch = Math.cos(heading);
double sh = Math.sin(heading);
double ca = Math.cos(attitude);
double sa = Math.sin(attitude);
double cb = Math.cos(bank);
double sb = Math.sin(bank);
Matrix m = new Matrix(3,3);
m.set(0,0, ch * ca);
m.set(0,1, sh*sb - ch*sa*cb);
m.set(0,2, ch*sa*sb + sh*cb);
m.set(1,0, sa);
m.set(1,1, ca*cb);
m.set(1,2, -ca*sb);
m.set(2,0, -sh*ca);
m.set(2,1, sh*sa*cb + ch*sb);
m.set(2,2, -sh*sa*sb + ch*cb);
return m;
}
/**
* Calculates the angle from centerPt to targetPt in degrees.
* The return should range from [0,360), rotating CLOCKWISE,
* 0 and 360 degrees represents NORTH,
* 90 degrees represents EAST, etc...
*
* Assumes all points are in the same coordinate space. If they are not,
* you will need to call SwingUtilities.convertPointToScreen or equivalent
* on all arguments before passing them to this function.
*
* @param centerPt Point we are rotating around.
* @param targetPt Point we want to calculate the angle to.
* @return angle in degrees. This is the angle from centerPt to targetPt.
*/
public static double calcRotationAngleInDegrees(Atom centerPt, Atom targetPt)
{
// calculate the angle theta from the deltaY and deltaX values
// (atan2 returns radians values from [-PI,PI])
// 0 currently points EAST.
// NOTE: By preserving Y and X param order to atan2, we are expecting
// a CLOCKWISE angle direction.
double theta = Math.atan2(targetPt.getY() - centerPt.getY(), targetPt.getX() - centerPt.getX());
// rotate the theta angle clockwise by 90 degrees
// (this makes 0 point NORTH)
// NOTE: adding to an angle rotates it clockwise.
// subtracting would rotate it counter-clockwise
theta += Math.PI/2.0;
// convert from radians to degrees
// this will give you an angle from [0->270],[-180,0]
double angle = Math.toDegrees(theta);
// convert to positive range [0-360)
// since we want to prevent negative angles, adjust them now.
// we can assume that atan2 will not return a negative value
// greater than one partial rotation
if (angle < 0) {
angle += 360;
}
return angle;
}
public static void main(String[] args){
Atom a =new AtomImpl();
a.setX(0);
a.setY(0);
a.setZ(0);
Atom b = new AtomImpl();
b.setX(1);
b.setY(1);
b.setZ(0);
logger.info("Angle between atoms: ", calcRotationAngleInDegrees(a, b));
}
public static void rotate(Atom[] ca, Matrix matrix) {
for (Atom atom : ca) Calc.rotate(atom, matrix);
}
/**
* Shift an array of atoms at once.
* @param ca array of Atoms to shift
* @param b reference Atom vector
*/
public static void shift(Atom[] ca, Atom b) {
for (Atom atom : ca) Calc.shift(atom, b);
}
/**
* Convert JAMA rotation and translation to a Vecmath transformation matrix.
* Because the JAMA matrix is a pre-multiplication matrix and the Vecmath
* matrix is a post-multiplication one, the rotation matrix is transposed to
* ensure that the transformation they produce is the same.
*
* @param rot 3x3 Rotation matrix
* @param trans 3x1 Translation matrix
* @return 4x4 transformation matrix
*/
public static Matrix4d getTransformation(Matrix rot, Matrix trans) {
return new Matrix4d( new Matrix3d(rot.getColumnPackedCopy()),
new Vector3d(trans.getColumnPackedCopy()),
1.0);
}
/**
* Convert JAMA rotation and translation to a Vecmath transformation matrix.
* Because the JAMA matrix is a pre-multiplication matrix and the Vecmath
* matrix is a post-multiplication one, the rotation matrix is transposed to
* ensure that the transformation they produce is the same.
*
* @param rot 3x3 Rotation matrix
* @param trans 3x1 translation vector in Atom coordinates
* @return 4x4 transformation matrix
*/
public static Matrix4d getTransformation(Matrix rot, Atom trans) {
return new Matrix4d( new Matrix3d(rot.getColumnPackedCopy()),
new Vector3d(trans.getCoords()),
1.0);
}
/**
* Convert Vecmath transformation into a JAMA rotation matrix.
* Because the JAMA matrix is a pre-multiplication matrix and the Vecmath
* matrix is a post-multiplication one, the rotation matrix is transposed to
* ensure that the transformation they produce is the same.
*
* @param transform Matrix4d with transposed rotation matrix
* @return
*/
public static Matrix getRotationMatrix(Matrix4d transform){
Matrix rot = new Matrix(3,3);
for (int i=0;i<3;i++) {
for (int j=0;j<3;j++) {
rot.set(j, i, transform.getElement(i, j)); //transposed
}
}
return rot;
}
/**
* Extract the translational vector of a Vecmath transformation.
*
* @param transform Matrix4d
* @return Atom shift vector
*/
public static Atom getTranslationVector(Matrix4d transform){
Atom transl = new AtomImpl();
double[] coords = {transform.m03, transform.m13, transform.m23};
transl.setCoords(coords);
return transl;
}
/**
* Convert an array of atoms into an array of vecmath points
* @param atoms list of atoms
* @return list of Point3ds storing the x,y,z coordinates of each atom
*/
public static Point3d[] atomsToPoints(Atom[] atoms) {
Point3d[] points = new Point3d[atoms.length];
for(int i = 0; i< atoms.length;i++) {
points[i] = new Point3d(atoms[i].getCoords());
}
return points;
}
}