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.symmetry.core;
022
023import org.biojava.nbio.structure.Atom;
024import org.biojava.nbio.structure.Chain;
025import org.biojava.nbio.structure.cluster.SubunitCluster;
026import org.biojava.nbio.structure.geometry.CalcPoint;
027import org.biojava.nbio.structure.geometry.MomentsOfInertia;
028import org.biojava.nbio.structure.symmetry.utils.SymmetryTools;
029
030import javax.vecmath.Point3d;
031import javax.vecmath.Vector3d;
032
033import java.util.*;
034import java.util.stream.Collectors;
035
036/**
037 * A bean to represent information about the set of {@link Subunit} being
038 * considered for symmetry detection. This class is a helper for the
039 * {@link QuatSymmetryDetector} algorithm, since it calculates and caches the
040 * {@link MomentsOfInertia} and the centroids of each Subunit.
041 * 
042 * @author Peter Rose
043 * @author Aleix Lafita
044 * 
045 */
046public class QuatSymmetrySubunits {
047
048        private List<Point3d[]> caCoords = new ArrayList<Point3d[]>();
049        private List<Point3d> originalCenters = new ArrayList<Point3d>();
050        private List<Point3d> centers = new ArrayList<Point3d>();
051        private List<Vector3d> unitVectors = new ArrayList<Vector3d>();
052
053        private List<Integer> folds = new ArrayList<Integer>();
054        private List<Integer> clusterIds = new ArrayList<Integer>();
055        private List<SubunitCluster> clusters;
056
057        private Point3d centroid;
058        private MomentsOfInertia momentsOfInertia = new MomentsOfInertia();
059
060        /**
061         * Converts the List of {@link SubunitCluster} to a Subunit object.
062         * 
063         * @param clusters
064         *            List of SubunitCluster
065         */
066        public QuatSymmetrySubunits(List<SubunitCluster> clusters) {
067
068                this.clusters = clusters;
069
070                // Loop through all subunits in the clusters and fill Lists
071                for (int c = 0; c < clusters.size(); c++) {
072
073                        for (int s = 0; s < clusters.get(c).size(); s++) {
074
075                                clusterIds.add(c);
076                                Atom[] atoms = clusters.get(c).getAlignedAtomsSubunit(s);
077
078                                // Convert atoms to points
079                                Point3d[] points = new Point3d[atoms.length];
080                                for (int i = 0; i < atoms.length; i++)
081                                        points[i] = atoms[i].getCoordsAsPoint3d();
082
083                                caCoords.add(points);
084                        }
085                }
086
087                // List number of members in each cluster
088                List<Integer> stoichiometries = clusters.stream().map(c -> c.size())
089                                .collect(Collectors.toList());
090                folds = SymmetryTools.getValidFolds(stoichiometries);
091        }
092
093        public List<Point3d[]> getTraces() {
094                return caCoords;
095        }
096
097        public List<Integer> getClusterIds() {
098                return clusterIds;
099        }
100
101        /**
102         * This method is provisional and should only be used for coloring Subunits.
103         * A new coloring schema has to be implemented to allow the coloring of
104         * Subunits, without implying one Subunit = one Chain.
105         * 
106         * @return A List of the Chain Ids of each Subunit
107         */
108        public List<String> getChainIds() {
109                
110                List<String> chains = new ArrayList<String>(getSubunitCount());
111
112                // Loop through all subunits in the clusters and fill Lists
113                for (int c = 0; c < clusters.size(); c++) {
114                        for (int s = 0; s < clusters.get(c).size(); s++)
115                                chains.add(clusters.get(c).getSubunits().get(s).getName());
116                }
117                
118                return chains;
119        }
120
121        /**
122         * This method is provisional and should only be used for coloring Subunits.
123         * A new coloring schema has to be implemented to allow the coloring of
124         * Subunits, without implying one Subunit = one Chain.
125         * 
126         * @return A List of the Model number of each Subunit
127         */
128        public List<Integer> getModelNumbers() {
129                
130                List<Integer> models = new ArrayList<Integer>(getSubunitCount());
131
132                // Loop through all subunits in the clusters and fill Lists
133                for (int c = 0; c < clusters.size(); c++) {
134                        for (int s = 0; s < clusters.get(c).size(); s++) {
135
136                                Atom[] atoms = clusters.get(c).getAlignedAtomsSubunit(s);
137
138                                // TODO guess them chain and model (very ugly)
139                                Chain chain = atoms[0].getGroup().getChain();
140
141                                int model = 0;
142                                for (int m = 0; m < chain.getStructure().nrModels(); m++) {
143                                        if (chain.getStructure().getModel(m).contains(chain)) {
144                                                model = m;
145                                                break;
146                                        }
147                                }
148                                models.add(model);
149                        }
150                }
151                return models;
152        }
153
154        public int getSubunitCount() {
155                run();
156                if (centers == null) {
157                        return 0;
158                }
159                return centers.size();
160        }
161
162        public List<Integer> getFolds() {
163                return folds;
164        }
165
166        public int getCalphaCount() {
167                int count = 0;
168                for (Point3d[] trace : caCoords) {
169                        count += trace.length;
170                }
171                return count;
172        }
173
174        public int getLargestSubunit() {
175                int index = -1;
176                int maxLength = 0;
177                for (int i = 0; i < caCoords.size(); i++) {
178                        int length = caCoords.get(i).length;
179                        if (length > maxLength) {
180                                index = i;
181                        }
182                }
183                return index;
184        }
185
186        public List<Point3d> getCenters() {
187                run();
188                return centers;
189        }
190
191        public List<Vector3d> getUnitVectors() {
192                run();
193                return unitVectors;
194        }
195
196        public List<Point3d> getOriginalCenters() {
197                run();
198                return originalCenters;
199        }
200
201        public Point3d getCentroid() {
202                run();
203                return centroid;
204        }
205
206        public MomentsOfInertia getMomentsOfInertia() {
207                run();
208                return momentsOfInertia;
209        }
210
211        private void run() {
212                if (centers.size() > 0) {
213                        return;
214                }
215                calcOriginalCenters();
216                calcCentroid();
217                calcCenters();
218                calcMomentsOfIntertia();
219        }
220
221        private void calcOriginalCenters() {
222                for (Point3d[] trace : caCoords) {
223                        Point3d com = CalcPoint.centroid(trace);
224                        originalCenters.add(com);
225                }
226        }
227
228        private void calcCentroid() {
229                Point3d[] orig = originalCenters.toArray(new Point3d[originalCenters
230                                .size()]);
231                centroid = CalcPoint.centroid(orig);
232        }
233
234        private void calcCenters() {
235                for (Point3d p : originalCenters) {
236                        Point3d c = new Point3d(p);
237                        c.sub(centroid);
238                        centers.add(c);
239                        Vector3d v = new Vector3d(c);
240                        v.normalize();
241                        unitVectors.add(v);
242                }
243        }
244
245        public Point3d getLowerBound() {
246                Point3d lower = new Point3d();
247                for (Point3d p : centers) {
248                        if (p.x < lower.x) {
249                                lower.x = p.x;
250                        }
251                        if (p.y < lower.y) {
252                                lower.y = p.y;
253                        }
254                        if (p.z < lower.z) {
255                                lower.z = p.z;
256                        }
257                }
258                return lower;
259        }
260
261        public Point3d getUpperBound() {
262                Point3d upper = new Point3d();
263                for (Point3d p : centers) {
264                        if (p.x > upper.x) {
265                                upper.x = p.x;
266                        }
267                        if (p.y > upper.y) {
268                                upper.y = p.y;
269                        }
270                        if (p.z > upper.z) {
271                                upper.z = p.z;
272                        }
273                }
274                return upper;
275        }
276
277        private void calcMomentsOfIntertia() {
278                for (Point3d[] trace : caCoords) {
279                        for (Point3d p : trace) {
280                                momentsOfInertia.addPoint(p, 1.0f);
281                        }
282                }
283        }
284
285}