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.*;
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.List;
028
029
030
031
032
033/**
034 * @author Peter Rose
035 *
036 *
037 */
038public class BioAssemblyTools {
039
040
041        /**
042         * Checks if the passed in expression is a unary operator expression
043         * Example: (1,2,3) or (1-60) are unary operator expressions
044         *          (1-60)(61-88) is a binary operator expression, representing
045         *          a cartesian product of the two parenthesised lists
046         *
047         * @param expression
048         * @return true if expression is a unary operator expression
049         */
050        public static boolean isUnaryExpression(String expression) {
051                int first = expression.indexOf("(");
052                int last = expression.lastIndexOf("(");
053                if (first < 0 || last < 0) {
054                        return true;
055                }
056                return ! (first == 0 && last > first);
057        }
058
059        public static List<String> parseUnaryOperatorExpression(String operatorExpression) throws IllegalArgumentException {
060                return parseSubExpression(operatorExpression);
061        }
062
063        private static List<String> parseSubExpression(String expression) throws IllegalArgumentException {
064                // remove parenthesis, if any
065                String tmp = expression.replace("(", "");
066                tmp = tmp.replace(")", "");
067
068                // separate the operators
069                List<String> components = null;
070                try {
071                        components = Arrays.asList(tmp.split(","));
072                } catch (Exception e) {
073                        throw new IllegalArgumentException("Invalid oper_expression: " + expression);
074                }
075
076                // expand ranges if present, i.e. 1-60 -> 1, 2, 3, ..., 60
077                List<String> operators = new ArrayList<String>();
078                for (String component : components) {
079                        if (component.contains("-")) {
080                                operators.addAll(expandRange(component));
081                        } else {
082                                operators.add(component);
083                        }
084                }
085                return operators;
086        }
087
088        /**
089         * Expands a range expression, i.e. (1-6) to a list 1,2,3,4,5,6
090         * @param expression the expression to be expanded
091         * @return list of items in range
092         * @throws IllegalArgumentException
093         */
094        private static List<String> expandRange(String expression) throws IllegalArgumentException {
095                int first = 0;
096                int last = 0;
097                try {
098                        String[] range = expression.split("-");
099                        first = Integer.parseInt(range[0]);
100                        last = Integer.parseInt(range[1]);
101                } catch (Exception e) {
102                        throw new IllegalArgumentException("Invalid range specification in oper_expression: " + expression);
103                }
104
105                List<String> expandedExpression = new ArrayList<String>(last-first+1);
106                for (int i = first; i <= last; i++) {
107                        expandedExpression.add(String.valueOf(i));
108                }
109                return expandedExpression;
110        }
111
112        public static List<OrderedPair<String>> parseBinaryOperatorExpression(String expression)
113                        throws IllegalArgumentException {
114                // split operator expression, i.e. (1,2,3)(4,5) into two subexpressions
115                String[] subExpressions = null;
116                try {
117                        subExpressions = expression.split("\\)\\(");
118                } catch (Exception e) {
119                        throw new IllegalArgumentException("Invalid oper_expression: " + expression);
120                }
121                if (subExpressions.length != 2) {
122                        throw new IllegalArgumentException("Invalid oper_expression: " + expression);
123                }
124                List<String> leftSide = parseSubExpression(subExpressions[0]);
125                List<String> rightSide = parseSubExpression(subExpressions[1]);
126
127                // form the cartesian product of the two lists
128                CartesianProduct<String> product = new CartesianProduct<String>(leftSide, rightSide);
129                return product.getOrderedPairs();
130        }
131
132        public static double[][]  getBiologicalMoleculeBounds(Structure asymStructure,List<BiologicalAssemblyTransformation> transformations) {
133                final double coordinateBounds[][] = new double[2][3];
134                coordinateBounds[0][0] = Double.MAX_VALUE;  // min x
135                coordinateBounds[0][1] = Double.MAX_VALUE;  // min y
136                coordinateBounds[0][2] = Double.MAX_VALUE;  // min z
137                coordinateBounds[1][0] = Double.MIN_VALUE;  // max x
138                coordinateBounds[1][1] = Double.MIN_VALUE;  // max y
139                coordinateBounds[1][2] = Double.MIN_VALUE;  // max z
140
141                double[] transformedCoords = new double[3];
142
143                Atom[] atoms = StructureTools.getAllAtomArray(asymStructure);
144
145                for ( Atom a : atoms) {
146
147                        Chain c = a.getGroup().getChain();
148                        String intChainID = c.getInternalChainID();
149                        if ( intChainID == null)
150                                intChainID = c.getChainID();
151
152                        for (BiologicalAssemblyTransformation m: transformations) {
153                                if ( ! m.getChainId().equals(intChainID))
154                                        continue;
155                                double[] coords = a.getCoords();
156                                transformedCoords[0] = coords[0];
157                                transformedCoords[1] = coords[1];
158                                transformedCoords[2] = coords[2];
159
160                                if (transformedCoords[0] < coordinateBounds[0][0] ) {
161                                        coordinateBounds[0][0] = transformedCoords[0];  // min x
162                                }
163
164                                if (transformedCoords[1] < coordinateBounds[0][1] ) {
165                                        coordinateBounds[0][1] = transformedCoords[1];  // min y
166                                }
167
168                                if (transformedCoords[2] < coordinateBounds[0][2] ) {
169                                        coordinateBounds[0][2] = transformedCoords[2];  // min z
170                                }
171
172                                if (transformedCoords[0] > coordinateBounds[1][0] ) {
173                                        coordinateBounds[1][0] = transformedCoords[0];  // max x
174                                }
175
176                                if (transformedCoords[1] > coordinateBounds[1][1] ) {
177                                        coordinateBounds[1][1] = transformedCoords[1];  // max y
178                                }
179
180                                if (transformedCoords[2] > coordinateBounds[1][2] ) {
181                                        coordinateBounds[1][2] = transformedCoords[2];  // max z
182                                }
183                        }
184                }
185                return coordinateBounds;
186        }
187        public static double[][] getAtomCoordinateBounds(Structure s){
188
189                org.biojava.nbio.structure.Atom[] atoms = StructureTools.getAllAtomArray(s);
190                int atomCount = atoms.length;
191                final double coordinateBounds[][] = new double[2][3];
192                if ( atomCount <= 0 ) {
193                        return coordinateBounds;
194                }
195
196                Atom a = atoms[0];
197
198                coordinateBounds[0][0] = a.getX();  // min x
199                coordinateBounds[0][1] = a.getY();  // min y
200                coordinateBounds[0][2] = a.getZ();  // min z
201                coordinateBounds[1][0] = a.getX();  // max x
202                coordinateBounds[1][1] = a.getY();  // max y
203                coordinateBounds[1][2] = a.getZ();  // max z
204
205                for ( int i=1; i<atomCount; i++ )
206                {
207                        a =atoms[i];
208
209                        if ( a.getX() < coordinateBounds[0][0] ) {
210                                coordinateBounds[0][0] = a.getX();  // min x
211                        }
212
213                        if ( a.getY() < coordinateBounds[0][1] ) {
214                                coordinateBounds[0][1] = a.getY();  // min y
215                        }
216
217                        if ( a.getZ() < coordinateBounds[0][2] ) {
218                                coordinateBounds[0][2] = a.getZ();  // min z
219                        }
220
221                        if ( a.getX() > coordinateBounds[1][0] ) {
222                                coordinateBounds[1][0] = a.getX();  // max x
223                        }
224
225                        if ( a.getY() > coordinateBounds[1][1] ) {
226                                coordinateBounds[1][1] = a.getY();  // max y
227                        }
228
229                        if ( a.getZ() > coordinateBounds[1][2] ) {
230                                coordinateBounds[1][2] = a.getZ();  // max z
231                        }
232                }
233
234                return coordinateBounds;
235        }
236
237        /**
238         * Returns the maximum extend of the structure in the x, y, or z direction.
239         * @param structure
240         * @return maximum extend
241         */
242        public static double getMaximumExtend( final Structure structure ) {
243                double[][] bounds = getAtomCoordinateBounds(structure);
244                double xMax = Math.abs(bounds[0][0] - bounds[1][0]);
245                double yMax = Math.abs(bounds[0][1] - bounds[1][1]);
246                double zMax = Math.abs(bounds[0][2] - bounds[1][2]);
247                return Math.max(xMax, Math.max(yMax, zMax));
248        }
249
250        /**
251         * Returns the maximum extend of the biological molecule in the x, y, or z direction.
252         * @param structure
253         * @return maximum extend
254         */
255        public static double getBiologicalMoleculeMaximumExtend( final Structure structure,List<BiologicalAssemblyTransformation> transformations ) {
256                double[][] bounds = getBiologicalMoleculeBounds(structure, transformations);
257                double xMax = Math.abs(bounds[0][0] - bounds[1][0]);
258                double yMax = Math.abs(bounds[0][1] - bounds[1][1]);
259                double zMax = Math.abs(bounds[0][2] - bounds[1][2]);
260                return Math.max(xMax, Math.max(yMax, zMax));
261        }
262
263        /**
264         * Returns the centroid of the biological molecule.
265         * @param structure
266         * @return centroid
267         * @throws IllegalArgumentException if structure is null
268         */
269
270        public static double[] getBiologicalMoleculeCentroid( final Structure asymUnit,List<BiologicalAssemblyTransformation> transformations ) throws IllegalArgumentException {
271                if ( asymUnit == null ) {
272                        throw new IllegalArgumentException( "null structure" );
273                }
274
275                Atom[] atoms = StructureTools.getAllAtomArray(asymUnit);
276                int atomCount = atoms.length;
277                double[] centroid = new double[3];
278
279                if ( atomCount <= 0 ) {
280                        return centroid;
281                }
282
283                if ( transformations.size() == 0) {
284                        return Calc.getCentroid(atoms).getCoords();
285
286                }
287
288
289
290                int count = 0;
291                double[] transformedCoordinate = new double[3];
292
293                for (int i = 0; i < atomCount; i++)
294                {
295                        Atom atom = atoms[i];
296                        Chain chain = atom.getGroup().getChain();
297                        String intChainID = chain.getInternalChainID();
298                        if ( intChainID == null)
299                                intChainID = chain.getChainID();
300
301
302                        for (BiologicalAssemblyTransformation m: transformations) {
303                                if (!  m.getChainId().equals(intChainID))
304                                        continue;
305
306                                double[] coords = atom.getCoords();
307                                transformedCoordinate[0] = coords[0];
308                                transformedCoordinate[1] = coords[1];
309                                transformedCoordinate[2] = coords[2];
310                                m.transformPoint(transformedCoordinate);
311                                centroid[0] += transformedCoordinate[0];
312                                centroid[1] += transformedCoordinate[1];
313                                centroid[2] += transformedCoordinate[2];
314                                count++;
315                        }
316                }
317
318
319
320                centroid[0] /= count;
321                centroid[1] /= count;
322                centroid[2] /= count;
323
324                return centroid;
325        }
326
327        /** reduce a structure to a Calpha representation only
328         *
329         * @param orig
330         * @return
331         * @deprecated Use the more generic {@link #getReducedStructure(Structure)}
332         */
333        @Deprecated
334        public static Structure getReducedCAStructure(Structure orig){
335                Structure s = new StructureImpl();
336                s.setPDBHeader(orig.getPDBHeader());
337                for ( Chain c : orig.getChains()){
338
339                        Chain c1 = new ChainImpl();
340                        c1.setChainID(c.getChainID());
341                        s.addChain(c1);
342
343                        for (Group g : c.getAtomGroups()){
344
345                                try {
346                                        Atom a = g.getAtom(StructureTools.CA_ATOM_NAME);
347                                        if ( a != null){
348
349                                                Group g1 = (Group)g.clone();
350                                                g1.clearAtoms();
351                                                g1.addAtom(a);
352                                                c1.addGroup(g1);
353
354                                        }
355                                } catch (Exception e){}
356                        }
357
358                }
359                return s;
360        }
361        /** reduce a structure to a single-atom representation (e.g. CA atoms
362         *
363         * @param orig
364         * @return
365         * @since Biojava 4.1.0
366         */
367        public static Structure getReducedStructure(Structure orig){
368                Structure s = new StructureImpl();
369                s.setPDBHeader(orig.getPDBHeader());
370                for ( Chain c : orig.getChains()){
371
372                        Chain c1 = new ChainImpl();
373                        c1.setChainID(c.getChainID());
374                        s.addChain(c1);
375
376                        for (Group g : c.getAtomGroups()){
377
378                                try {
379                                        Atom a = null;
380                                        switch(g.getType()) {
381                                        case AMINOACID:
382                                                a = g.getAtom(StructureTools.CA_ATOM_NAME);
383                                                break;
384                                        case NUCLEOTIDE:
385                                                a = g.getAtom(StructureTools.NUCLEOTIDE_REPRESENTATIVE);
386                                                break;
387                                        default:
388                                                //omit group
389                                        }
390                                        if ( a != null){
391
392                                                Group g1 = (Group)g.clone();
393                                                g1.clearAtoms();
394                                                g1.addAtom(a);
395                                                c1.addGroup(g1);
396
397                                        }
398                                } catch (Exception e){}
399                        }
400
401                }
402                return s;
403        }
404
405}