/* * 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.bio.structure ; import org.biojava.bio.structure.jama.Matrix; /** 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 { public Calc(){ } // 180 / pi static double RADIAN = 57.29577951 ; /** Radians per degree. * */ public final static float radiansPerDegree = (float) (2 * Math.PI / 360); /** Degrees per radian. * */ public final static float degreesPerRadian = (float) (360 / (2 * Math.PI)); /** * */ /** * calculate distance between two atoms. * * @param a an Atom object * @param b an Atom object * @return a double * @throws StructureException ... */ public static double getDistance(Atom a, Atom b) throws StructureException { 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; double dist = Math.sqrt(s); return dist ; } private static void nullCheck(Atom a) throws StructureException { if (a == null) { throw new StructureException("Atom is null!"); } } /** add two atoms ( a + b). * * @param a an Atom object * @param b an Atom object * @return an Atom object */ public static Atom add(Atom a, Atom b){ double[] coords = new double[3] ; coords[0] = a.getX() + b.getX(); coords[1] = a.getY() + b.getY(); coords[2] = a.getZ() + b.getZ(); Atom c = new AtomImpl(); c.setCoords(coords); return c ; } /** substract two atoms ( a - b). * * @param a an Atom object * @param b an Atom object * @return an Atom object * @throws StructureException ... */ public static Atom substract(Atom a, Atom b) throws StructureException { nullCheck(a) ; nullCheck(b) ; double[] coords = new double[3] ; coords[0] = a.getX() - b.getX(); coords[1] = a.getY() - b.getY(); coords[2] = a.getZ() - b.getZ(); Atom c = new AtomImpl(); c.setCoords(coords); return c ; } /** Vector product . * * @param a an Atom object * @param b an Atom object * @return an Atom object */ public static Atom vectorProduct(Atom a , Atom b){ double[] coords = new double[3]; coords[0] = a.getY() * b.getZ() - a.getZ() * b.getY(); coords[1] = a.getZ() * b.getX() - a.getX() * b.getZ(); coords[2] = a.getX() * b.getY() - a.getY() * b.getX(); Atom c = new AtomImpl(); c.setCoords(coords); return c ; } /** skalar product. * * @param a an Atom object * @param b an Atom object * @return a double */ public static double skalarProduct(Atom a, Atom b){ double skalar ; skalar = a.getX() * b.getX() + a.getY()* b.getY() + a.getZ() * b.getZ(); return skalar ; } /** amount. * * @param a an Atom object * @return a double */ public static double amount(Atom a){ return Math.sqrt(skalarProduct(a,a)); } /** angle. * * @param a an Atom object * @param b an Atom object * @return a double */ public static double angle(Atom a, Atom b){ double skalar; double angle; skalar = skalarProduct(a,b); angle = skalar/( amount(a) * amount (b) ); angle = Math.acos(angle); angle = angle * RADIAN ; return angle; } /** return the unit vector of vector a . * * @param a an Atom object * @return an Atom object */ public static Atom unitVector(Atom a) { double amount = amount(a) ; Atom U = a ; double[] coords = new double[3]; coords[0] = a.getX() / amount ; coords[1] = a.getY() / amount ; coords[2] = a.getZ() / amount ; U.setCoords(coords); return U ; } /** torsion angle * = angle between the normal vectors of the * two plains a-b-c and b-c-d. * * @param a an Atom object * @param b an Atom object * @param c an Atom object * @param d an Atom object * @return a double * @throws StructureException ... */ public static double torsionAngle(Atom a, Atom b, Atom c, Atom d) throws StructureException { Atom ab = substract(a,b); Atom cb = substract(c,b); Atom bc = substract(b,c); Atom dc = substract(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 = skalarProduct(cb,vecprod); if (val<0.0) angl = -angl ; return angl; } /** phi angle. * * @param a an AminoAcid object * @param b an AminoAcid object * @return a double * @throws StructureException ... */ public static 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(); double phi = torsionAngle(a_C,b_N,b_CA,b_C); return phi ; } /** psi angle. * * @param a an AminoAcid object * @param b an AminoAcid object * @return a double * @throws StructureException ... */ public static 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(); double psi = torsionAngle(a_N,a_CA,a_C,b_N); return psi ; } /** test if two amino acids are connected, i.e. * if the distance from C to N < 2,5 Angstrom. * * @param a an AminoAcid object * @param b an AminoAcid object * @return true if ... * @throws StructureException ... */ public static boolean isConnected(AminoAcid a, AminoAcid b) throws StructureException { Atom C = a.getC(); Atom N = b.getN(); // one could also check if the CA atoms are < 4 A... double distance = getDistance(C,N); if ( distance < 2.5) { return true ; } else { return false ; } } /** rotate a single atom aroud a rotation matrix. * matrix must be a 3x3 matrix. * @param atom atom to be rotated * @param m a rotation matrix represented as a double[3][3] array */ public static 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 ; double[] coords = new double[3] ; coords[0] = nx ; coords[1] = ny ; coords[2] = nz ; atom.setCoords(coords); } /** Rotate a structure. * * @param structure a Structure object * @param rotationmatrix an array (3x3) of double representing the rotation matrix. * @throws StructureException ... */ public static void rotate(Structure structure, double[][] rotationmatrix) throws StructureException { double[][]m = rotationmatrix; if ( m.length != 3 ) { throw new StructureException ("matrix does not have size 3x3 !"); } AtomIterator iter = new AtomIterator(structure) ; while (iter.hasNext()) { Atom atom = (Atom) iter.next() ; Calc.rotate(atom,rotationmatrix); } } /** rotate a structure . * * @param group a group object * @param rotationmatrix an array (3x3) of double representing the rotation matrix. * @throws StructureException ... */ public static void rotate(Group group, double[][] rotationmatrix) throws StructureException { double[][]m = rotationmatrix; if ( m.length != 3 ) { throw new StructureException ("matrix does not have size 3x3 !"); } AtomIterator iter = new AtomIterator(group) ; while (iter.hasNext()) { Atom atom = null ; atom = (Atom) iter.next() ; rotate(atom,rotationmatrix); } } /** Rotate an atom around a Matrix object. * * @param atom atom to be rotated * @param m rotation matrix to be applied to the atom */ public static 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); 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); } /** Rotate a group object. * * @param group a group to be rotated * @param m a Matrix object representing the translation matrix */ public static void rotate(Group group, Matrix m){ AtomIterator iter = new AtomIterator(group) ; while (iter.hasNext()) { Atom atom = (Atom) iter.next() ; rotate(atom,m); } } /** Rotate a structure object. * * @param structure the structure to be rotated * @param m rotation matrix to be applied */ public static void rotate(Structure structure, Matrix m){ AtomIterator iter = new AtomIterator(structure) ; while (iter.hasNext()) { Atom atom = (Atom) iter.next() ; rotate(atom,m); } } /** calculate structure + Matrix coodinates ... * * @param s the structure to operate on * @param matrix a Matrix object */ public static void plus(Structure s, Matrix matrix){ AtomIterator iter = new AtomIterator(s) ; Atom oldAtom = null; Atom rotOldAtom = null; while (iter.hasNext()) { Atom atom = null ; atom = (Atom) iter.next() ; try { if ( oldAtom != null){ //System.out.println("before " +getDistance(oldAtom,atom)); } } catch (Exception e){ e.printStackTrace(); } 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){ //System.out.println("after " + getDistance(rotOldAtom,atom)); } } catch (Exception e){ e.printStackTrace(); } 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 void shift(Structure structure, Atom a ){ AtomIterator iter = new AtomIterator(structure) ; while (iter.hasNext() ) { Atom atom = null ; atom = (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 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 void shift(Group group , Atom a ){ AtomIterator iter = new AtomIterator(group) ; while (iter.hasNext() ) { Atom atom = null ; atom = (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 Atom getCentroid(Atom[] atomSet){ double[] coords = new double[3]; coords[0] = 0; coords[1] = 0; coords[2] = 0 ; for (int i =0 ; i < atomSet.length; i++){ Atom a = atomSet[i]; 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; } /** 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 Atom getCenterVector(Atom[] atomSet){ Atom centroid = getCentroid(atomSet); 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 Atom[] centerAtoms(Atom[] atomSet) throws StructureException { Atom shiftVector = getCenterVector(atomSet); 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 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"); virtualCB.setFullName(" CB "); return virtualCB; } /**Gget 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 double[] getZYZEuler(Matrix m) { double m22 = m.get(2,2); double rY = (float) Math.acos(m22) * degreesPerRadian; double rZ1, rZ2; if (m22 > .999d || m22 < -.999d) { rZ1 = (double) Math.atan2(m.get(1,0), m.get(1,1)) * degreesPerRadian; rZ2 = 0; } else { rZ1 = (double) Math.atan2(m.get(2,1), -m.get(2,0)) * degreesPerRadian; rZ2 = (double) Math.atan2(m.get(1,2), m.get(0,2)) * degreesPerRadian; } 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 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 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; } }