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.geometry.CalcPoint;
024import javax.vecmath.Point3d;
025
026import java.util.*;
027import java.util.Map.Entry;
028
029public class HelicalRepeatUnit {
030        private QuatSymmetrySubunits subunits = null;
031        private List<Point3d> repeatUnitCenters = new ArrayList<Point3d>();
032        private List<Point3d[]> repeatUnits = new ArrayList<Point3d[]>();
033        private List<List<Integer>> repeatUnitIndices = new ArrayList<List<Integer>>();
034        private Map<Integer[], Integer> interactingNeighbors = Collections.emptyMap();
035
036public HelicalRepeatUnit(QuatSymmetrySubunits subunits) {
037        this.subunits = subunits;
038}
039
040public List<Point3d> getRepeatUnitCenters() {
041        if (repeatUnitCenters.isEmpty()) {
042                run();
043        }
044        return repeatUnitCenters;
045}
046
047public List<Point3d[]> getRepeatUnits() {
048        if (repeatUnits.isEmpty()) {
049                run();
050        }
051        return repeatUnits;
052}
053
054public List<List<Integer>> getRepeatUnitIndices() {
055        return repeatUnitIndices;
056}
057
058public Map<Integer[], Integer> getInteractingRepeatUnits() {
059        if (interactingNeighbors.isEmpty()) {
060                run();
061        }
062        return interactingNeighbors;
063}
064
065private void run() {
066        this.repeatUnitCenters = calcRepeatUnitCenters();
067        if (this.repeatUnitCenters.size() == 0) {
068                return;
069        }
070        this.repeatUnits = calcRepeatUnits();
071        this.interactingNeighbors = findInteractingNeigbors();
072}
073
074private List<Point3d> calcRepeatUnitCenters() {
075        
076        // TODO why do we use models here? it should not matter. Setting to 0 all
077        List<Integer> models = new ArrayList<Integer>(subunits.getSubunitCount());
078        for (int s = 0; s <subunits.getSubunitCount(); s++)
079                models.add(0);
080        Set<Integer> uniqueModels = new HashSet<Integer>(Arrays.asList(1));
081        
082        int modelCount = uniqueModels.size();
083        List<Integer> folds = this.subunits.getFolds();
084        int maxFold = folds.get(folds.size()-1);
085
086        List<Point3d> repeatCenters = new ArrayList<Point3d>();
087        List<Point3d> centers = subunits.getCenters();
088
089//      if (modelCount == maxFold && subunits.getSubunitCount() > 3) {
090        if (maxFold%modelCount == 0 && modelCount > 1 && subunits.getSubunitCount() > 3) {
091//              System.out.println("calcRepeatUnitCenters case 1");
092                for (int i = 0; i < modelCount; i++) {
093                        List<Integer> subunitIndices = new ArrayList<Integer>();
094                        Point3d p = new Point3d();
095                        int count = 0;
096//                      System.out.println("Models: " + models.size());
097                        for (int j = 0; j < models.size(); j++) {
098                                if (models.get(j) == i) {
099                                        p.add(centers.get(j));
100                                        subunitIndices.add(j);
101                                        count++;
102                                }
103                        }
104//                      System.out.println("count: " + count);
105                        p.scale(1.0/count);
106//                      System.out.println("Orig Repeat unit: " + p);
107                        repeatCenters.add(p);
108                        repeatUnitIndices.add(subunitIndices);
109                }
110        } else {
111//              System.out.println("calcRepeatUnitCenters case21");
112                // TODO need to group subunits into repeating groups
113                // Case of 3B5U: A14, but seems to form (A2)*7 and symmetry related subunits don't have direct contact
114                List<Integer> sequenceClusterIds = subunits.getClusterIds();
115                for (int i = 0; i < subunits.getSubunitCount(); i++) {
116                        List<Integer> subunitIndices = new ArrayList<Integer>(1);
117                        if (sequenceClusterIds.get(i) == 0) {
118                                repeatCenters.add(new Point3d(centers.get(i)));
119//                              System.out.println("Orig Repeat unit: " + centers.get(i));
120                                subunitIndices.add(i);
121                                repeatUnitIndices.add(subunitIndices);
122                        }
123                }
124        }
125
126        // helixes should have at least 3 repeat centers
127//      System.out.println("Number of repeat centers: " + repeatCenters.size());
128        if (repeatCenters.size() < 3) {
129                repeatCenters.clear();
130        }
131
132        return repeatCenters;
133}
134
135private List<Point3d[]> calcRepeatUnits() {
136        
137        // TODO why do we use models here? it should not matter. Setting to 0 all
138        List<Integer> models = new ArrayList<Integer>(
139                        subunits.getSubunitCount());
140        for (int s = 0; s < subunits.getSubunitCount(); s++)
141                models.add(0);
142        Set<Integer> uniqueModels = new HashSet<Integer>(Arrays.asList(1));
143                
144        int modelCount = uniqueModels.size();
145        List<Integer> folds = this.subunits.getFolds();
146        int maxFold = folds.get(folds.size()-1);
147
148        List<Point3d[]> repeatTraces = new ArrayList<Point3d[]>();
149        List<Point3d[]> traces = subunits.getTraces();
150
151//      if (modelCount == maxFold && subunitCount > 3) {
152        if (maxFold%modelCount == 0 && modelCount > 1 && subunits.getSubunitCount() > 3) {
153                for (int i = 0; i < modelCount; i++) {
154                        List<Point3d> coords = new ArrayList<Point3d>();
155                        for (int j = 0; j < models.size(); j++) {
156                                if (models.get(j) == i) {
157                                        coords.addAll(Arrays.asList(traces.get(j)));
158                                }
159                        }
160                        Point3d[] x = new Point3d[coords.size()];
161                        coords.toArray(x);
162//                      repeatTraces.add(x); // make sure we don't accidently change the original coordinates
163                        repeatTraces.add(CalcPoint.clonePoint3dArray(x));
164                }
165        } else {
166                List<Integer> sequenceClusterIds = subunits.getClusterIds();
167                for (int i = 0; i < subunits.getSubunitCount(); i++) {
168                        if (sequenceClusterIds.get(i) == 0) {
169                                Point3d[] x = subunits.getTraces().get(i);
170                                repeatTraces.add(CalcPoint.clonePoint3dArray(x));
171                        }
172                }
173        }
174
175//      for (int i = 0; i < repeatTraces.size(); i++) {
176//              System.out.println("Repeat " + i);
177//              System.out.println(Arrays.toString(repeatTraces.get(i)));
178//      }
179        return repeatTraces;
180}
181
182private Map<Integer[], Integer> findInteractingNeigbors() {
183        Map<Integer[], Integer>  contactMap = new HashMap<Integer[], Integer>();
184
185        Map<Integer, List<Integer[]>> distanceMap = findClosestPairs(8);
186        for (List<Integer[]> pairs: distanceMap.values())
187        for (Integer[] pair: pairs) {
188                Integer contacts = calcContactNumber(repeatUnits.get(pair[0]), repeatUnits.get(pair[1]));
189//              System.out.println("contacts: " + pair[0] + "-" + pair[1] + ": " + contacts);
190                if (contacts > 0) {
191                        contactMap.put(pair, contacts);
192                }
193        }
194
195        return contactMap;
196}
197
198private Map<Integer, List<Integer[]>> findClosestPairs(int maxNeighbors) {
199        Map<Integer, List<Integer[]>>  reducedMap = new TreeMap<Integer, List<Integer[]>>();
200
201        Map<Integer, List<Integer[]>>  distanceMap = new TreeMap<Integer, List<Integer[]>>();
202        int nCenters = repeatUnitCenters.size();
203//      System.out.println("repeatUnitCenters: " + repeatUnitCenters);
204
205        for (int i = 0; i < nCenters-1; i++) {
206                for (int j = i+1; j < nCenters; j++) {
207                        float dist = (float)repeatUnitCenters.get(i).distance(repeatUnitCenters.get(j));
208                        // round to 2 digits precision
209//                      System.out.println("dist pair: " + i + "-" + j + ": " + dist);
210                        dist *= 100;
211                        int intDist = Math.round(dist);
212                        List<Integer[]> pairs = distanceMap.get(intDist);
213                        // save only one representative pair for each distance
214                        if (pairs == null) {
215                                pairs = new ArrayList<Integer[]>();
216                        }
217                        Integer[] pair = new Integer[2];
218                        pair[0] = i;
219                        pair[1] = j;
220                        pairs.add(pair);
221                        distanceMap.put(intDist, pairs);
222                }
223        }
224
225        int count = 0;
226        for (Entry<Integer, List<Integer[]>> entry: distanceMap.entrySet()) {
227                if (! (reducedMap.containsKey(entry.getKey())) ) {
228                        reducedMap.put(entry.getKey(), entry.getValue());
229//                      System.out.println("dist pair: " + entry.getValue() + ": " + entry.getKey());
230                        count++;
231                        if (count == maxNeighbors) {
232                                break;
233                        }
234                }
235        }
236        distanceMap.clear();
237
238        return reducedMap;
239}
240
241private static int calcContactNumber(Point3d[] a, Point3d[] b) {
242        int contacts = 0;
243        for (Point3d pa : a) {
244                for (Point3d pb : b) {
245                        if (pa.distance(pb) < 10) {
246                                contacts++;
247                        }
248                }
249        }
250        return contacts;
251}
252}