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 if( atoms.length == 0) { 080 throw new IllegalArgumentException("No aligned atoms in subunit"); 081 } 082 083 Point3d[] points = Calc.atomsToPoints(atoms); 084 085 caCoords.add(points); 086 } 087 } 088 089 // List number of members in each cluster 090 List<Integer> stoichiometries = clusters.stream().map(c -> c.size()) 091 .collect(Collectors.toList()); 092 folds = SymmetryTools.getValidFolds(stoichiometries); 093 } 094 095 public List<Point3d[]> getTraces() { 096 return caCoords; 097 } 098 099 public List<Integer> getClusterIds() { 100 return clusterIds; 101 } 102 103 /** 104 * This method is provisional and should only be used for coloring Subunits. 105 * A new coloring schema has to be implemented to allow the coloring of 106 * Subunits, without implying one Subunit = one Chain. 107 * 108 * @return A List of the Chain Ids of each Subunit 109 */ 110 public List<String> getChainIds() { 111 112 List<String> chains = new ArrayList<>(getSubunitCount()); 113 114 // Loop through all subunits in the clusters and fill Lists 115 for (int c = 0; c < clusters.size(); c++) { 116 for (int s = 0; s < clusters.get(c).size(); s++) 117 chains.add(clusters.get(c).getSubunits().get(s).getName()); 118 } 119 120 return chains; 121 } 122 123 /** 124 * This method is provisional and should only be used for coloring Subunits. 125 * A new coloring schema has to be implemented to allow the coloring of 126 * Subunits, without implying one Subunit = one Chain. 127 * 128 * @return A List of the Model number of each Subunit 129 */ 130 public List<Integer> getModelNumbers() { 131 132 List<Integer> models = new ArrayList<>(getSubunitCount()); 133 134 // Loop through all subunits in the clusters and fill Lists 135 for (int c = 0; c < clusters.size(); c++) { 136 for (int s = 0; s < clusters.get(c).size(); s++) { 137 138 Atom[] atoms = clusters.get(c).getAlignedAtomsSubunit(s); 139 140 // TODO guess them chain and model (very ugly) 141 Chain chain = atoms[0].getGroup().getChain(); 142 143 int model = 0; 144 for (int m = 0; m < chain.getStructure().nrModels(); m++) { 145 if (chain.getStructure().getModel(m).contains(chain)) { 146 model = m; 147 break; 148 } 149 } 150 models.add(model); 151 } 152 } 153 return models; 154 } 155 156 public int getSubunitCount() { 157 run(); 158 if (centers == null) { 159 return 0; 160 } 161 return centers.size(); 162 } 163 164 public List<Integer> getFolds() { 165 return folds; 166 } 167 168 public int getCalphaCount() { 169 int count = 0; 170 for (Point3d[] trace : caCoords) { 171 count += trace.length; 172 } 173 return count; 174 } 175 176 public int getLargestSubunit() { 177 int index = -1; 178 int maxLength = 0; 179 for (int i = 0; i < caCoords.size(); i++) { 180 int length = caCoords.get(i).length; 181 if (length > maxLength) { 182 index = i; 183 } 184 } 185 return index; 186 } 187 188 public List<Point3d> getCenters() { 189 run(); 190 return centers; 191 } 192 193 public List<Vector3d> getUnitVectors() { 194 run(); 195 return unitVectors; 196 } 197 198 public List<Point3d> getOriginalCenters() { 199 run(); 200 return originalCenters; 201 } 202 203 public Point3d getCentroid() { 204 run(); 205 return centroid; 206 } 207 208 public MomentsOfInertia getMomentsOfInertia() { 209 run(); 210 return momentsOfInertia; 211 } 212 213 private void run() { 214 if (centers.size() > 0) { 215 return; 216 } 217 calcOriginalCenters(); 218 calcCentroid(); 219 calcCenters(); 220 calcMomentsOfInertia(); 221 } 222 223 private void calcOriginalCenters() { 224 for (Point3d[] trace : caCoords) { 225 Point3d com = CalcPoint.centroid(trace); 226 originalCenters.add(com); 227 } 228 } 229 230 private void calcCentroid() { 231 Point3d[] orig = originalCenters.toArray(new Point3d[originalCenters 232 .size()]); 233 centroid = CalcPoint.centroid(orig); 234 } 235 236 private void calcCenters() { 237 for (Point3d p : originalCenters) { 238 Point3d c = new Point3d(p); 239 c.sub(centroid); 240 centers.add(c); 241 Vector3d v = new Vector3d(c); 242 v.normalize(); 243 unitVectors.add(v); 244 } 245 } 246 247 public Point3d getLowerBound() { 248 Point3d lower = new Point3d(); 249 for (Point3d p : centers) { 250 if (p.x < lower.x) { 251 lower.x = p.x; 252 } 253 if (p.y < lower.y) { 254 lower.y = p.y; 255 } 256 if (p.z < lower.z) { 257 lower.z = p.z; 258 } 259 } 260 return lower; 261 } 262 263 public Point3d getUpperBound() { 264 Point3d upper = new Point3d(); 265 for (Point3d p : centers) { 266 if (p.x > upper.x) { 267 upper.x = p.x; 268 } 269 if (p.y > upper.y) { 270 upper.y = p.y; 271 } 272 if (p.z > upper.z) { 273 upper.z = p.z; 274 } 275 } 276 return upper; 277 } 278 279 private void calcMomentsOfInertia() { 280 for (Point3d[] trace : caCoords) { 281 for (Point3d p : trace) { 282 momentsOfInertia.addPoint(p, 1.0f); 283 } 284 } 285 } 286 287}