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}