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