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}