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 duarte_j 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 identified 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 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 public String toXML() throws IOException{ 176 177 StringWriter sw = new StringWriter(); 178 PrintWriter writer = new PrintWriter(sw); 179 180 PrettyXMLWriter xml = new PrettyXMLWriter(new PrintWriter(writer)); 181 182 toXML(xml); 183 184 xml.close(); 185 writer.close(); 186 sw.close(); 187 return sw.toString(); 188 } 189 190 public void toXML(PrettyXMLWriter xml) throws IOException{ 191 xml.openTag("transformation"); 192 xml.attribute("index",id); 193 194 xml.openTag("matrix"); 195 196 for ( int i = 0 ; i<3 ; i++){ 197 for ( int j = 0 ; j<3 ;j++){ 198 xml.attribute("m" + (i+1) + (j+1), String.format("%.8f",transformation.getElement(i,j))); 199 } 200 } 201 xml.closeTag("matrix"); 202 203 xml.openTag("shift"); 204 for ( int i = 0 ; i<3 ; i++) { 205 xml.attribute("v"+(i+1),String.format("%.8f", transformation.getElement(i,3))); 206 } 207 xml.closeTag("shift"); 208 209 xml.closeTag("transformation"); 210 211 } 212 213 public static BiologicalAssemblyTransformation fromXML(String xml) 214 throws SAXException, 215 IOException, 216 ParserConfigurationException{ 217 218 219 List<BiologicalAssemblyTransformation> transformations = fromMultiXML(xml); 220 221 if ( transformations.size() > 0) 222 return transformations.get(0); 223 224 else 225 return null; 226 } 227 228 public static List<BiologicalAssemblyTransformation> fromMultiXML(String xml) throws ParserConfigurationException, SAXException, IOException{ 229 230 231 List<BiologicalAssemblyTransformation> transformations = new ArrayList<BiologicalAssemblyTransformation>(); 232 233 // read the XML of a string and returns a ModelTransformationmatrix 234 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); 235 DocumentBuilder db = factory.newDocumentBuilder(); 236 237 InputSource inStream = new InputSource(); 238 inStream.setCharacterStream(new StringReader(xml)); 239 Document doc = db.parse(inStream); 240 241 // normalize text representation 242 doc.getDocumentElement().normalize();; 243 244 NodeList listOfTransforms = doc.getElementsByTagName("transformation"); 245 246 for(int pos=0; pos<listOfTransforms.getLength() ; pos++) { 247 Node rootElement = listOfTransforms.item(pos); 248 249 BiologicalAssemblyTransformation max = new BiologicalAssemblyTransformation(); 250 251 max.id = getAttribute(rootElement,"index"); 252 max.chainId = getAttribute(rootElement,"chainId"); 253 254 NodeList listOfChildren = rootElement.getChildNodes(); 255 256 257 for(int i=0; i<listOfChildren.getLength() ; i++) 258 { 259 // and now the matrix ... 260 Node block = listOfChildren.item(i); 261 262 // we only look at blocks. 263 if ( block.getNodeName().equals("matrix")) 264 max.setRotationMatrix(getMatrixFromXML(block)); 265 266 if ( block.getNodeName().equals("shift")) 267 max.setTranslation(getVectorFromXML(block)); 268 269 } 270 271 transformations.add(max); 272 } 273 274 return transformations; 275 } 276 277 private static double[] getVectorFromXML(Node block) { 278 double[] d = new double[3]; 279 for ( int i = 0 ; i<3 ; i++){ 280 d[i] = Float.parseFloat(getAttribute(block, "v" + (i+1) )); 281 } 282 return d; 283 } 284 285 private static double[][] getMatrixFromXML(Node block) { 286 double[][] m = new double[3][3]; 287 for ( int i = 0 ; i<3 ; i++){ 288 for ( int j = 0 ; j<3 ;j++){ 289 // TODO check is this matrix is populated correctly 290 String val = getAttribute(block, "m" + (j+1)+(i+1)); 291 m[j][i] = Double.parseDouble(val); 292 } 293 } 294 return m; 295 } 296 297 private static String getAttribute(Node node, String attr){ 298 if( ! node.hasAttributes()) 299 return null; 300 301 NamedNodeMap atts = node.getAttributes(); 302 303 if ( atts == null) 304 return null; 305 306 Node att = atts.getNamedItem(attr); 307 if ( att == null) 308 return null; 309 310 String value = att.getTextContent(); 311 312 return value; 313 314 } 315 316 /* (non-Javadoc) 317 * @see java.lang.Object#toString() 318 */ 319 @Override 320 public String toString() { 321 return "BiologicalAssemblyTransformation [id=" + id + ", chainId=" 322 + chainId + ", rotation=" + rotMatrixToString(transformation) + ", translation=" 323 + translVecToString(transformation) + "]"; 324 } 325 326 public static String rotMatrixToString(Matrix4d m) { 327 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); 328 } 329 330 public static String translVecToString(Matrix4d m) { 331 return String.format("(%5.2f %5.2f %5.2f)", m.m03, m.m13, m.m23); 332 } 333 334}