001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojava.nbio.structure.quaternary; 022 023import org.biojava.nbio.structure.xtal.CrystalCell; 024import org.biojava.nbio.structure.xtal.CrystalTransform; 025import org.biojava.nbio.core.util.PrettyXMLWriter; 026import org.w3c.dom.Document; 027import org.w3c.dom.NamedNodeMap; 028import org.w3c.dom.Node; 029import org.w3c.dom.NodeList; 030import org.xml.sax.InputSource; 031import org.xml.sax.SAXException; 032 033import javax.vecmath.Matrix4d; 034import javax.vecmath.Point3d; 035import javax.xml.parsers.DocumentBuilder; 036import javax.xml.parsers.DocumentBuilderFactory; 037import javax.xml.parsers.ParserConfigurationException; 038import java.io.*; 039import java.util.ArrayList; 040import java.util.List; 041 042/** 043 * The transformation needed for generation of biological assemblies 044 * from the contents of a PDB/mmCIF file. It contains both the actual 045 * transformation (rotation+translation) and the chain identifier to 046 * which it should be applied. 047 * 048 * @author Peter Rose 049 * @author Andreas Prlic 050 * @author rickb 051 * @author Jose Duarte 052 * @see CrystalTransform 053 */ 054public class BiologicalAssemblyTransformation implements Cloneable, Serializable { 055 056 private static final long serialVersionUID = -6388503076022480391L; 057 058 private String id; 059 private String chainId; 060 private Matrix4d transformation; 061 062 /** 063 * Default Constructor 064 */ 065 public BiologicalAssemblyTransformation() { 066 // we initialize to identity so that setting rotation and translation work properly 067 transformation = new Matrix4d(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1); 068 } 069 070 /** 071 * Copy Constructor 072 * 073 * @param src 074 */ 075 public BiologicalAssemblyTransformation(final BiologicalAssemblyTransformation src) 076 { 077 this.transformation = new Matrix4d(src.transformation); 078 this.id = src.getId(); 079 this.chainId = src.getChainId(); 080 } 081 082 083 /** 084 * Sets the identifier for this biological assembly transformation. This is usually 085 * the model number used in the biological assembly files. 086 * @param id 087 */ 088 public void setId(String id) { 089 this.id = id; 090 } 091 092 /** 093 * Returns the identifier for this biological assembly transformation. 094 * @return biological assembly transformation identifier 095 */ 096 public String getId() { 097 return id; 098 } 099 100 /** 101 * Sets the chain identifier (asym id) that this transformation should be applied to. 102 * @param chainId 103 */ 104 public void setChainId(String chainId) { 105 this.chainId = chainId; 106 } 107 108 /** 109 * Returns the chain identifier (asym id) that this transformation should be applied to. 110 * @return chain identifier 111 */ 112 public String getChainId() { 113 return this.chainId; 114 } 115 116 /** 117 * Sets the transformation using a 4x4 transformation matrix 118 * @param transformation 119 */ 120 public void setTransformationMatrix(Matrix4d transformation) { 121 this.transformation = transformation; 122 } 123 124 /** 125 * Return the transformation (both rotational and translational component) as a 4x4 transformation matrix. 126 * The transformation is in orthonormal (cartesian coordinates). If required to be converted to 127 * crystal coordinates then use {@link CrystalCell#transfToCrystal(Matrix4d)} 128 * Note that this is a reference to the variable, thus it remains linked to this object's transformation field. 129 * The user must deep copy it if need changing it. 130 * @return 4x4 transformation matrix 131 */ 132 public Matrix4d getTransformationMatrix() { 133 return transformation; 134 } 135 136 public void setRotationMatrix(double[][] m) { 137 for (int i=0;i<3;i++) { 138 for (int j=0;j<3;j++) { 139 this.transformation.setElement(i, j, m[i][j]); 140 } 141 } 142 } 143 144 public void setTranslation(double[] t) { 145 for (int i=0;i<3;i++) { 146 this.transformation.setElement(i, 3, t[i]); 147 } 148 } 149 150 /** 151 * Applies the transformation to given point. 152 */ 153 public void transformPoint(final double[] point) { 154 Point3d p = new Point3d(point[0],point[1],point[2]); 155 transformation.transform(p); 156 point[0] = p.x; 157 point[1] = p.y; 158 point[2] = p.z; 159 } 160 161 /** 162 * Returns the combination (product) of two biological assembly transformations. 163 * @param matrix1 164 * @param matrix2 165 * @return combined transformation 166 */ 167 public static BiologicalAssemblyTransformation combine(BiologicalAssemblyTransformation matrix1, BiologicalAssemblyTransformation matrix2) { 168 Matrix4d transformation = new Matrix4d(matrix1.transformation); 169 transformation.mul(matrix2.transformation); 170 BiologicalAssemblyTransformation combined = new BiologicalAssemblyTransformation(); 171 combined.setTransformationMatrix(transformation); 172 return combined; 173 } 174 175 /** 176 * Tells whether this transformation is in identity. 177 * @return 178 */ 179 public boolean isIdentity() { 180 return transformation.epsilonEquals(new Matrix4d(1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1), 0.00000000001); 181 } 182 183 public String toXML() throws IOException{ 184 185 StringWriter sw = new StringWriter(); 186 PrintWriter writer = new PrintWriter(sw); 187 188 PrettyXMLWriter xml = new PrettyXMLWriter(new PrintWriter(writer)); 189 190 toXML(xml); 191 192 xml.close(); 193 writer.close(); 194 sw.close(); 195 return sw.toString(); 196 } 197 198 public void toXML(PrettyXMLWriter xml) throws IOException{ 199 xml.openTag("transformation"); 200 xml.attribute("index",id); 201 202 xml.openTag("matrix"); 203 204 for ( int i = 0 ; i<3 ; i++){ 205 for ( int j = 0 ; j<3 ;j++){ 206 xml.attribute("m" + (i+1) + (j+1), String.format("%.8f",transformation.getElement(i,j))); 207 } 208 } 209 xml.closeTag("matrix"); 210 211 xml.openTag("shift"); 212 for ( int i = 0 ; i<3 ; i++) { 213 xml.attribute("v"+(i+1),String.format("%.8f", transformation.getElement(i,3))); 214 } 215 xml.closeTag("shift"); 216 217 xml.closeTag("transformation"); 218 219 } 220 221 public static BiologicalAssemblyTransformation fromXML(String xml) 222 throws SAXException, 223 IOException, 224 ParserConfigurationException{ 225 226 227 List<BiologicalAssemblyTransformation> transformations = fromMultiXML(xml); 228 229 if ( transformations.size() > 0) 230 return transformations.get(0); 231 232 else 233 return null; 234 } 235 236 public static List<BiologicalAssemblyTransformation> fromMultiXML(String xml) throws ParserConfigurationException, SAXException, IOException{ 237 238 239 List<BiologicalAssemblyTransformation> transformations = new ArrayList<BiologicalAssemblyTransformation>(); 240 241 // read the XML of a string and returns a ModelTransformationmatrix 242 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 243 DocumentBuilder db = factory.newDocumentBuilder(); 244 245 InputSource inStream = new InputSource(); 246 inStream.setCharacterStream(new StringReader(xml)); 247 Document doc = db.parse(inStream); 248 249 // normalize text representation 250 doc.getDocumentElement().normalize();; 251 252 NodeList listOfTransforms = doc.getElementsByTagName("transformation"); 253 254 for(int pos=0; pos<listOfTransforms.getLength() ; pos++) { 255 Node rootElement = listOfTransforms.item(pos); 256 257 BiologicalAssemblyTransformation max = new BiologicalAssemblyTransformation(); 258 259 max.id = getAttribute(rootElement,"index"); 260 max.chainId = getAttribute(rootElement,"chainName"); 261 262 NodeList listOfChildren = rootElement.getChildNodes(); 263 264 265 for(int i=0; i<listOfChildren.getLength() ; i++) 266 { 267 // and now the matrix ... 268 Node block = listOfChildren.item(i); 269 270 // we only look at blocks. 271 if ( block.getNodeName().equals("matrix")) 272 max.setRotationMatrix(getMatrixFromXML(block)); 273 274 if ( block.getNodeName().equals("shift")) 275 max.setTranslation(getVectorFromXML(block)); 276 277 } 278 279 transformations.add(max); 280 } 281 282 return transformations; 283 } 284 285 private static double[] getVectorFromXML(Node block) { 286 double[] d = new double[3]; 287 for ( int i = 0 ; i<3 ; i++){ 288 d[i] = Float.parseFloat(getAttribute(block, "v" + (i+1) )); 289 } 290 return d; 291 } 292 293 private static double[][] getMatrixFromXML(Node block) { 294 double[][] m = new double[3][3]; 295 for ( int i = 0 ; i<3 ; i++){ 296 for ( int j = 0 ; j<3 ;j++){ 297 // TODO check is this matrix is populated correctly 298 String val = getAttribute(block, "m" + (j+1)+(i+1)); 299 m[j][i] = Double.parseDouble(val); 300 } 301 } 302 return m; 303 } 304 305 private static String getAttribute(Node node, String attr){ 306 if( ! node.hasAttributes()) 307 return null; 308 309 NamedNodeMap atts = node.getAttributes(); 310 311 if ( atts == null) 312 return null; 313 314 Node att = atts.getNamedItem(attr); 315 if ( att == null) 316 return null; 317 318 String value = att.getTextContent(); 319 320 return value; 321 322 } 323 324 /* (non-Javadoc) 325 * @see java.lang.Object#toString() 326 */ 327 @Override 328 public String toString() { 329 return "BiologicalAssemblyTransformation [id=" + id + ", chainId=" 330 + chainId + ", rotation=" + rotMatrixToString(transformation) + ", translation=" 331 + translVecToString(transformation) + "]"; 332 } 333 334 public static String rotMatrixToString(Matrix4d m) { 335 return String.format("(%5.2f %5.2f %5.2f, %5.2f %5.2f %5.2f, %5.2f %5.2f %5.2f)", m.m00, m.m01, m.m02, m.m10, m.m11, m.m12, m.m20, m.m21, m.m22); 336 } 337 338 public static String translVecToString(Matrix4d m) { 339 return String.format("(%5.2f %5.2f %5.2f)", m.m03, m.m13, m.m23); 340 } 341 342}