1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2021-12-27 10:22:45 -0600 (Mon, 27 Dec 2021) $
4  * $Revision: 22278 $
5 
6  *
7  * Copyright (C) 2003-2005  The Jmol Development Team
8  *
9  * Contact: jmol-developers@lists.sf.net
10  *
11  *  This library is free software; you can redistribute it and/or
12  *  modify it under the terms of the GNU Lesser General Public
13  *  License as published by the Free Software Foundation; either
14  *  version 2.1 of the License, or (at your option) any later version.
15  *
16  *  This library is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  *  Lesser General Public License for more details.
20  *
21  *  You should have received a copy of the GNU Lesser General Public
22  *  License along with this library; if not, write to the Free Software
23  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24  */
25 
26 package org.jmol.modelset;
27 
28 import java.util.Enumeration;
29 import java.util.Hashtable;
30 import java.util.Iterator;
31 import java.util.Map;
32 import java.util.Properties;
33 
34 import org.jmol.api.AtomIndexIterator;
35 import org.jmol.api.Interface;
36 import org.jmol.api.JmolModulationSet;
37 import org.jmol.api.SymmetryInterface;
38 import org.jmol.atomdata.AtomData;
39 import org.jmol.atomdata.RadiusData;
40 import org.jmol.bspt.Bspf;
41 import org.jmol.bspt.CubeIterator;
42 import org.jmol.c.PAL;
43 import org.jmol.c.STR;
44 import org.jmol.c.VDW;
45 import org.jmol.modelsetbio.BioModel;
46 import org.jmol.script.ScriptCompiler;
47 import org.jmol.script.T;
48 import org.jmol.shape.Shape;
49 import org.jmol.util.BSUtil;
50 import org.jmol.util.BoxInfo;
51 import org.jmol.util.Edge;
52 import org.jmol.util.Elements;
53 import org.jmol.util.Escape;
54 import org.jmol.util.JmolMolecule;
55 import org.jmol.util.Logger;
56 import org.jmol.util.Point3fi;
57 import org.jmol.util.Rectangle;
58 import org.jmol.util.SimpleUnitCell;
59 import org.jmol.util.Tensor;
60 import org.jmol.util.Vibration;
61 import org.jmol.viewer.JC;
62 import org.jmol.viewer.JmolAsyncException;
63 import org.jmol.viewer.ShapeManager;
64 import org.jmol.viewer.TransformManager;
65 import org.jmol.viewer.Viewer;
66 
67 import javajs.util.A4;
68 import javajs.util.AU;
69 import javajs.util.BS;
70 import javajs.util.Lst;
71 import javajs.util.M3;
72 import javajs.util.M4;
73 import javajs.util.Measure;
74 import javajs.util.P3;
75 import javajs.util.P4;
76 import javajs.util.PT;
77 import javajs.util.Quat;
78 import javajs.util.SB;
79 import javajs.util.T3;
80 import javajs.util.V3;
81 
82 
83 /*
84  * A class always created using new ModelLoader(...)
85  *
86  * Merged with methods in Mmset and ModelManager 10/2007  Jmol 11.3.32
87  *
88  * ModelLoader simply pulls out all private classes that are
89  * necessary only for file loading (and structure recalculation).
90  *
91  * What is left here are all the methods that are
92  * necessary AFTER a model is loaded, when it is being
93  * accessed by Viewer, primarily.
94  *
95  * Please:
96  *
97  * 1) designate any methods used only here as private
98  * 2) designate any methods accessed only by ModelLoader as protected
99  * 3) designate any methods accessed within modelset as nothing
100  * 4) designate any methods accessed only by Viewer as public
101  *
102  * Bob Hanson, 5/2007, 10/2007
103  *
104  */
105 public class ModelSet extends BondCollection {
106 
107   public boolean haveBioModels;
108 
109   protected BS bsSymmetry;
110 
111   public String modelSetName;
112 
113   public Model[] am;
114   /**
115    * model count
116    */
117   public int mc;
118 
119   public SymmetryInterface[] unitCells;
120   public boolean haveUnitCells;
121 
122   protected final Atom[] closest;
123 
124   protected int[] modelNumbers; // from adapter -- possibly PDB MODEL record; possibly modelFileNumber
125   public int[] modelFileNumbers; // file * 1000000 + modelInFile (1-based)
126   public String[] modelNumbersForAtomLabel, modelNames, frameTitles;
127 
128   protected BS[] elementsPresent;
129 
130   protected boolean isXYZ;
131 
132   public Properties modelSetProperties;
133   public Map<String, Object> msInfo;
134 
135   protected boolean someModelsHaveSymmetry;
136   protected boolean someModelsHaveAromaticBonds;
137   protected boolean someModelsHaveFractionalCoordinates;
138 
139   ////////////////////////////////////////////
140 
141   private boolean isBbcageDefault;
142   public BS bboxModels;
143   private BS bboxAtoms;
144   private final BoxInfo boxInfo;
145 
getBoxInfo()146   public BoxInfo getBoxInfo() {
147     return boxInfo;
148   }
149 
150   public Lst<StateScript> stateScripts;
151   /*
152    * stateScripts are connect commands that must be executed in sequence.
153    *
154    * What I fear is that in deleting models we must delete these connections,
155    * and in deleting atoms, the bitsets may not be retrieved properly.
156    *
157    *
158    */
159   private int thisStateModel;
160 
161   protected Lst<V3[]> vibrationSteps;
162 
163   private BS selectedMolecules;
164 
165   //private final static boolean MIX_BSPT_ORDER = false;
166   boolean showRebondTimes = true;
167 
168   protected BS bsAll;
169 
170   public ShapeManager sm;
171 
172   private static float hbondMinRasmol = 2.5f;
173 
174   public boolean proteinStructureTainted;
175 
176   public Hashtable<String, BS> htPeaks;
177 
178   private Quat[] vOrientations;
179 
180   private final P3 ptTemp, ptTemp1, ptTemp2;
181   private final M3 matTemp, matInv;
182   private final M4 mat4, mat4t;
183   private final V3 vTemp;
184 
185   ////////////////////////////////////////////////////////////////
186 
187   /**
188    *
189    * @param vwr
190    * @param name
191    */
ModelSet(Viewer vwr, String name)192   public ModelSet(Viewer vwr, String name) {
193     this.vwr = vwr;
194     modelSetName = name;
195 
196     selectedMolecules = new BS();
197     stateScripts = new Lst<StateScript>();
198 
199     boxInfo = new BoxInfo();
200     boxInfo.addBoundBoxPoint(P3.new3(-10, -10, -10));
201     boxInfo.addBoundBoxPoint(P3.new3(10, 10, 10));
202 
203     am = new Model[1];
204     modelNumbers = new int[1]; // from adapter -- possibly PDB MODEL record; possibly modelFileNumber
205     modelFileNumbers = new int[1]; // file * 1000000 + modelInFile (1-based)
206     modelNumbersForAtomLabel = new String[1];
207     modelNames = new String[1];
208     frameTitles = new String[1];
209 
210     closest = new Atom[1];
211 
212     ptTemp = new P3();
213     ptTemp1 = new P3();
214     ptTemp2 = new P3();
215     matTemp = new M3();
216     matInv = new M3();
217     mat4 = new M4();
218     mat4t = new M4();
219     vTemp = new V3();
220 
221     setupBC();
222   }
223 
releaseModelSet()224   protected void releaseModelSet() {
225     am = null;
226     closest[0] = null;
227     /*
228      * Probably unnecessary, but here for general accounting.
229      *
230      * I added this when I was trying to track down a memory bug.
231      * I know that you don't have to do this, but I was concerned
232      * that somewhere in this mess was a reference to modelSet.
233      * As it turns out, it was in models[i] (which was not actually
234      * nulled but, rather, transferred to the new model set anyway).
235      * Quite amazing that that worked at all, really. Some models were
236      * referencing the old modelset, some the new. Yeiks!
237      *
238      * Bob Hanson 11/7/07
239      *
240      */
241     am = null;
242     bsSymmetry = null;
243     bsAll = null;
244     unitCells = null;
245     releaseModelSetBC();
246   }
247 
248   //variables that will be reset when a new frame is instantiated
249 
250   private boolean echoShapeActive = false;
251 
getEchoStateActive()252   public boolean getEchoStateActive() {
253     return echoShapeActive;
254   }
255 
setEchoStateActive(boolean TF)256   public void setEchoStateActive(boolean TF) {
257     echoShapeActive = TF;
258   }
259 
260   protected String modelSetTypeName;
261 
getModelSetTypeName()262   public String getModelSetTypeName() {
263     return modelSetTypeName;
264   }
265 
266   /**
267    *
268    * @param modelNumber
269    *        can be a PDB MODEL number or a simple index number, or a fffnnnnnn
270    *        f.n number
271    * @param useModelNumber
272    * @param doSetTrajectory
273    * @return index
274    */
getModelNumberIndex(int modelNumber, boolean useModelNumber, boolean doSetTrajectory)275   public int getModelNumberIndex(int modelNumber, boolean useModelNumber,
276                                  boolean doSetTrajectory) {
277     if (useModelNumber) {
278       for (int i = 0; i < mc; i++)
279         if (modelNumbers[i] == modelNumber || modelNumber < 1000000
280             && modelNumbers[i] == 1000000 + modelNumber)
281           return i;
282       return -1;
283     }
284     if (modelNumber < 1000000)
285       return modelNumber;
286     //new decimal format:   frame 1.2 1.3 1.4
287     for (int i = 0; i < mc; i++)
288       if (modelFileNumbers[i] == modelNumber) {
289         if (doSetTrajectory && isTrajectory(i))
290           setTrajectory(i);
291         return i;
292       }
293     return -1;
294   }
295 
getModelDataBaseName(BS bsAtoms)296   public String getModelDataBaseName(BS bsAtoms) {
297     for (int i = 0; i < mc; i++) {
298       if (bsAtoms.equals(am[i].bsAtoms))
299         return (String) getInfo(i, "dbName");
300     }
301     return null;
302   }
303 
setTrajectory(int modelIndex)304   public void setTrajectory(int modelIndex) {
305     if (modelIndex >= 0 && isTrajectory(modelIndex)
306         && at[am[modelIndex].firstAtomIndex].mi != modelIndex)
307       trajectory.setModel(modelIndex);
308   }
309 
getBitSetTrajectories()310   public BS getBitSetTrajectories() {
311     return (trajectory == null ? null : trajectory.getModelsSelected());
312   }
313 
setTrajectoryBs(BS bsModels)314   public void setTrajectoryBs(BS bsModels) {
315     if (trajectory != null)
316       for (int i = 0; i < mc; i++)
317         if (bsModels.get(i))
318           setTrajectory(i);
319   }
320 
morphTrajectories(int m1, int m2, float f)321   public void morphTrajectories(int m1, int m2, float f) {
322     if (m1 >= 0 && m2 >= 0 && isTrajectory(m1) && isTrajectory(m2))
323       trajectory.morph(m1, m2, f);
324   }
325 
326   public P3[] translations;
327 
getTranslation(int iModel)328   public P3 getTranslation(int iModel) {
329     return (translations == null || iModel >= translations.length ? null
330         : translations[iModel]);
331   }
332 
333   /**
334    * move atoms by vector pt; used for co-centering with FRAME ALIGN {atoms}
335    * TRUE
336    *
337    * @param iModel
338    * @param pt
339    */
translateModel(int iModel, T3 pt)340   public void translateModel(int iModel, T3 pt) {
341     if (pt == null) {
342       P3 t = getTranslation(iModel);
343       if (t == null)
344         return;
345       pt = P3.newP(t);
346       pt.scale(-1);
347       translateModel(iModel, pt);
348       translations[iModel] = null;
349       return;
350     }
351     if (translations == null || translations.length <= iModel)
352       translations = new P3[mc];
353     if (translations[iModel] == null)
354       translations[iModel] = new P3();
355     translations[iModel].add(pt);
356     BS bs = am[iModel].bsAtoms;
357     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
358       at[i].add(pt);
359   }
360 
getFrameOffsets(BS bsAtoms, boolean isFull)361   public P3[] getFrameOffsets(BS bsAtoms, boolean isFull) {
362     if (bsAtoms == null) {
363       if (isFull)
364         for (int i = mc; --i >= 0;) {
365           Model m = am[i];
366           if (!m.isJmolDataFrame && !m.isTrajectory)
367             translateModel(m.modelIndex, null);
368         }
369       return null;
370     }
371     int i0 = bsAtoms.nextSetBit(0);
372     if (i0 < 0)
373       return null;
374     if (isFull) {
375       BS bs = BSUtil.copy(bsAtoms);
376       P3 pt = null;
377       P3 pdiff = new P3();
378       for (int i = 0; i < mc; i++) {
379         Model m = am[i];
380         if (!m.isJmolDataFrame && !m.isTrajectory) {
381           int j = bs.nextSetBit(0);
382           if (m.bsAtoms.get(j)) {
383             if (pt == null) {
384               pt = P3.newP(at[j]);
385             } else {
386               pdiff.sub2(pt, at[j]);
387               translateModel(i, pdiff);
388             }
389           }
390         }
391         bs.andNot(m.bsAtoms);
392       }
393       return null;
394     }
395     P3[] offsets = new P3[mc];
396     for (int i = mc; --i >= 0;)
397       offsets[i] = new P3();
398     int lastModel = 0;
399     int n = 0;
400     P3 offset = offsets[0];
401     boolean asTrajectory = (trajectory != null && trajectory.steps.size() == mc);
402     int m1 = (asTrajectory ? mc : 1);
403     offsets[0].set(0, 0, 0);
404     for (int m = 0; m < m1; m++) {
405       if (asTrajectory)
406         setTrajectory(m);
407       for (int i = 0; i <= ac; i++) {
408         if (i == ac || at[i].mi != lastModel) {
409           if (n > 0) {
410             offset.scale(-1.0f / n);
411             if (lastModel != 0)
412               offset.sub(offsets[0]);
413             n = 0;
414           }
415           if (i == ac)
416             break;
417           lastModel = at[i].mi;
418           offset = offsets[lastModel];
419         }
420         if (!bsAtoms.get(i))
421           continue;
422         offset.add(at[i]);
423         n++;
424       }
425     }
426     return offsets;
427   }
428 
429   /**
430    * general lookup for integer type -- from Eval
431    *
432    * @param tokType
433    * @param specInfo
434    * @return bitset; null only if we mess up with name
435    */
getAtoms(int tokType, Object specInfo)436   public BS getAtoms(int tokType, Object specInfo) {
437     switch (tokType) {
438     default:
439       return BSUtil.andNot(getAtomBitsMaybeDeleted(tokType, specInfo),
440           vwr.slm.bsDeleted);
441     case T.spec_model:
442       int modelNumber = ((Integer) specInfo).intValue();
443       int modelIndex = getModelNumberIndex(modelNumber, true, true);
444       return (modelIndex < 0 && modelNumber > 0 ? new BS() : vwr
445           .getModelUndeletedAtomsBitSet(modelIndex));
446     case T.polyhedra:
447       Object[] data = new Object[] { null, null, null };
448       vwr.shm.getShapePropertyData(JC.SHAPE_POLYHEDRA, "getCenters", data);
449       return (data[1] == null ? new BS() : (BS) data[1]);
450     }
451   }
452 
findNearestAtomIndex(int x, int y, BS bsNot, int min)453   public int findNearestAtomIndex(int x, int y, BS bsNot, int min) {
454     if (ac == 0)
455       return -1;
456     closest[0] = null;
457     if (g3d.isAntialiased()) {
458       x <<= 1;
459       y <<= 1;
460     }
461     findNearest2(x, y, closest, bsNot, min);
462     sm.findNearestShapeAtomIndex(x, y, closest, bsNot);
463     int closestIndex = (closest[0] == null ? -1 : closest[0].i);
464     closest[0] = null;
465     return closestIndex;
466   }
467 
468   /*
469   private Map userProperties;
470 
471   void putUserProperty(String name, Object property) {
472     if (userProperties == null)
473       userProperties = new Hashtable();
474     if (property == null)
475       userProperties.remove(name);
476     else
477       userProperties.put(name, property);
478   }
479   */
480 
481   ///////// atom and shape selecting /////////
482 
calculatePointGroup(BS bsAtoms)483   public String calculatePointGroup(BS bsAtoms) {
484     return (String) calculatePointGroupForFirstModel(bsAtoms, false,
485         false, null, 0, 0, null, null, null);
486   }
487 
488   @SuppressWarnings("unchecked")
getPointGroupInfo(BS bsAtoms)489   public Map<String, Object> getPointGroupInfo(BS bsAtoms) {
490     return (Map<String, Object>) calculatePointGroupForFirstModel(bsAtoms,
491         false, true, null, 0, 0, null, null, null);
492   }
493 
getPointGroupAsString(BS bsAtoms, String type, int index, float scale, P3[] pts, P3 center, String id)494   public String getPointGroupAsString(BS bsAtoms, String type,
495                                       int index, float scale, P3[] pts, P3 center, String id) {
496     return (String) calculatePointGroupForFirstModel(bsAtoms, true,
497         false, type, index, scale, pts, center, id);
498   }
499 
500   private BoxInfo defaultBBox;
501 
calculatePointGroupForFirstModel(BS bsAtoms, boolean doAll, boolean asInfo, String type, int index, float scale, T3[] pts, P3 center, String id)502   private Object calculatePointGroupForFirstModel(BS bsAtoms, boolean doAll,
503                                                   boolean asInfo, String type,
504                                                   int index, float scale,
505                                                   T3[] pts, P3 center, String id) {
506     SymmetryInterface pointGroup = this.pointGroup;
507     SymmetryInterface symmetry = Interface.getSymmetry(vwr, "ms");
508     BS bs = null;
509     boolean haveVibration = false;
510     boolean isPolyhedron = false;
511     boolean localEnvOnly = false;
512     boolean isPoints = (pts != null);
513     int modelIndex = vwr.am.cmi;
514     if (!isPoints) {
515       int iAtom = (bsAtoms == null ? -1 : bsAtoms.nextSetBit(0));
516       if (modelIndex < 0 && iAtom >= 0)
517         modelIndex = at[iAtom].mi;
518       if (modelIndex < 0) {
519         modelIndex = vwr.getVisibleFramesBitSet().nextSetBit(0);
520         bsAtoms = null;
521       }
522       bs = vwr.getModelUndeletedAtomsBitSet(modelIndex);
523       localEnvOnly = (bsAtoms != null && bs.cardinality() != bsAtoms
524           .cardinality());
525       if (bsAtoms != null)
526         bs.and(bsAtoms);
527       iAtom = bs.nextSetBit(0);
528       if (iAtom < 0) {
529         bs = vwr.getModelUndeletedAtomsBitSet(modelIndex);
530         iAtom = bs.nextSetBit(0);
531       }
532       Object obj = vwr.shm
533           .getShapePropertyIndex(JC.SHAPE_VECTORS, "mad", iAtom);
534       haveVibration = (obj != null && ((Integer) obj).intValue() != 0 || vwr.tm.vibrationOn);
535       isPolyhedron = (type != null && type.toUpperCase().indexOf(":POLY") >= 0);
536       if (isPolyhedron) {
537         Object[] data = new Object[] { Integer.valueOf(iAtom), null };
538         vwr.shm.getShapePropertyData(JC.SHAPE_POLYHEDRA, "points", data);
539         pts = (T3[]) data[1];
540         if (pts == null)
541           return null;
542         bs = null;
543         haveVibration = false;
544         pointGroup = null;
545       } else {
546         pts = at;
547       }
548     }
549 
550     int tp;
551     if (type != null && (tp = type.indexOf(":")) >= 0)
552       type = type.substring(0, tp);
553     if (type != null && (tp = type.indexOf(".")) >= 0) {
554       index = PT.parseInt(type.substring(tp + 1));
555       if (index < 0)
556         index = 0;
557       type = type.substring(0, tp);
558     }
559     pointGroup = symmetry.setPointGroup(pointGroup, center, pts, bs,
560         haveVibration, (isPoints ? 0 : vwr.getFloat(T.pointgroupdistancetolerance)),
561         vwr.getFloat(T.pointgrouplineartolerance), localEnvOnly);
562     if (!isPolyhedron && !isPoints)
563       this.pointGroup = pointGroup;
564     if (!doAll && !asInfo)
565       return pointGroup.getPointGroupName();
566     Object ret = pointGroup.getPointGroupInfo(modelIndex, id, asInfo, type,
567         index, scale);
568     return (asInfo ? ret : (mc > 1 ? "frame "
569         + getModelNumberDotted(modelIndex) + "; " : "")
570         + ret);
571   }
572 
getDefaultStructure(BS bsAtoms, BS bsModified)573   public String getDefaultStructure(BS bsAtoms, BS bsModified) {
574     return (haveBioModels ? bioModelset.getAllDefaultStructures(bsAtoms,
575         bsModified) : "");
576   }
577 
deleteModelBonds(int modelIndex)578   public void deleteModelBonds(int modelIndex) {
579     BS bsAtoms = getModelAtomBitSetIncludingDeleted(modelIndex, false);
580     makeConnections(0, Float.MAX_VALUE, Edge.BOND_ORDER_NULL, T.delete, bsAtoms, bsAtoms, null, false, false, 0);
581   }
582 
583   ///// super-overloaded methods ///////
584 
makeConnections(float minDistance, float maxDistance, int order, int connectOperation, BS bsA, BS bsB, BS bsBonds, boolean isBonds, boolean addGroup, float energy)585   public int[] makeConnections(float minDistance, float maxDistance, int order,
586                                int connectOperation, BS bsA, BS bsB,
587                                BS bsBonds, boolean isBonds, boolean addGroup,
588                                float energy) {
589     if (connectOperation == T.auto && order != Edge.BOND_H_REGULAR) {
590       String stateScript = "connect ";
591       if (minDistance != JC.DEFAULT_MIN_CONNECT_DISTANCE)
592         stateScript += minDistance + " ";
593       if (maxDistance != JC.DEFAULT_MAX_CONNECT_DISTANCE)
594         stateScript += maxDistance + " ";
595       addStateScript(stateScript, (isBonds ? bsA : null),
596           (isBonds ? null : bsA), (isBonds ? null : bsB), " auto", false, true);
597     }
598     moleculeCount = 0;
599     return makeConnections2(minDistance, maxDistance, order, connectOperation,
600         bsA, bsB, bsBonds, isBonds, addGroup, energy);
601   }
602 
603   @SuppressWarnings("unchecked")
setPdbConectBonding(int baseAtomIndex, int baseModelIndex, BS bsExclude)604   public void setPdbConectBonding(int baseAtomIndex, int baseModelIndex,
605                                   BS bsExclude) {
606     short mad = vwr.getMadBond();
607     for (int i = baseModelIndex; i < mc; i++) {
608       Lst<int[]> vConnect = (Lst<int[]>) getInfo(i, "PDB_CONECT_bonds");
609       if (vConnect == null)
610         continue;
611       int nConnect = vConnect.size();
612       setInfo(i, "initialBondCount", Integer.valueOf(nConnect));
613       int[] atomInfo = (int[]) getInfo(i, "PDB_CONECT_firstAtom_count_max");
614       int firstAtom = atomInfo[0] + baseAtomIndex;
615       int atomMax = firstAtom + atomInfo[1];
616       int max = atomInfo[2];
617       int[] serialMap = new int[max + 1];
618       int iSerial;
619       for (int iAtom = firstAtom; iAtom < atomMax; iAtom++)
620         if ((iSerial = atomSerials[iAtom]) > 0)
621           serialMap[iSerial] = iAtom + 1;
622       for (int iConnect = 0; iConnect < nConnect; iConnect++) {
623         int[] pair = vConnect.get(iConnect);
624         int sourceSerial = pair[0];
625         int targetSerial = pair[1];
626         short order = (short) pair[2];
627         if (sourceSerial < 0 || targetSerial < 0 || sourceSerial > max
628             || targetSerial > max)
629           continue;
630         int sourceIndex = serialMap[sourceSerial] - 1;
631         int targetIndex = serialMap[targetSerial] - 1;
632         if (sourceIndex < 0 || targetIndex < 0)
633           continue;
634         Atom atomA = at[sourceIndex];
635         Atom atomB = at[targetIndex];
636         if (bsExclude != null) {
637           if (atomA.isHetero())
638             bsExclude.set(sourceIndex);
639           if (atomB.isHetero())
640             bsExclude.set(targetIndex);
641         }
642         // don't connect differing altloc
643         if (atomA.altloc == atomB.altloc || atomA.altloc == '\0'
644             || atomB.altloc == '\0')
645           getOrAddBond(atomA, atomB, order, (order == Edge.BOND_H_REGULAR ? 1
646               : mad), null, 0, false);
647       }
648     }
649   }
650 
deleteAllBonds()651   public void deleteAllBonds() {
652     moleculeCount = 0;
653     for (int i = stateScripts.size(); --i >= 0;) {
654       if (stateScripts.get(i).isConnect()) {
655         stateScripts.removeItemAt(i);
656       }
657     }
658     deleteAllBonds2();
659   }
660 
661   /* ******************************************************
662    *
663    * methods for definining the state
664    *
665    ********************************************************/
666 
includeAllRelatedFrames(BS bsModels)667   private void includeAllRelatedFrames(BS bsModels) {
668     int baseModel = 0;
669     for (int i = 0; i < mc; i++) {
670       boolean isTraj = isTrajectory(i);
671       boolean isBase = (isTraj && bsModels
672           .get(baseModel = am[i].trajectoryBaseIndex));
673       if (bsModels.get(i)) {
674         if (isTraj && !isBase) {
675           bsModels.set(baseModel);
676           includeAllRelatedFrames(bsModels);
677           return;
678         }
679       } else if (isTraj || isJmolDataFrameForModel(i)
680           && bsModels.get(am[i].dataSourceFrame)) {
681         bsModels.set(i);
682       }
683     }
684   }
685 
deleteModels(BS bsModels)686   public BS deleteModels(BS bsModels) {
687     // full models are deleted for any model containing the specified atoms
688     includeAllRelatedFrames(bsModels);
689 
690     int nModelsDeleted = bsModels.cardinality();
691     if (nModelsDeleted == 0)
692       return null;
693 
694     moleculeCount = 0;
695     if (msInfo != null)
696       msInfo.remove("models");
697 
698     // clear references to this frame if it is a dataFrame
699 
700     for (int i = bsModels.nextSetBit(0); i >= 0; i = bsModels.nextSetBit(i + 1))
701       clearDataFrameReference(i);
702 
703     BS bsDeleted;
704     if (nModelsDeleted == mc) {
705       bsDeleted = getModelAtomBitSetIncludingDeleted(-1, true);
706       vwr.zap(true, false, false);
707       return bsDeleted;
708     }
709 
710     // zero out reproducible arrays
711 
712     validateBspf(false);
713 
714     bsDeleted = new BS();
715     // if atoms have been added to a not-last model, and that model is not one being deleted,
716     // then we can't (yet) delete the model AND shift any values.
717     boolean allOrderly = true;
718     boolean isOneOfSeveral = false;
719     BS files = new BS();
720     for (int i = 0; i < mc; i++) {
721       Model m = am[i];
722       allOrderly &= m.isOrderly;//isOrderly(m);
723       if (bsModels.get(i)) { // get a good count now
724         if (m.fileIndex >= 0)
725           files.set(m.fileIndex);
726         bsDeleted.or(getModelAtomBitSetIncludingDeleted(i, false));
727       } else {
728         if (m.fileIndex >= 0 && files.get(m.fileIndex))
729           isOneOfSeveral = true;
730       }
731     }
732     if (!allOrderly || isOneOfSeveral) {
733       vwr.deleteAtoms(bsDeleted, false);
734       return null;
735     }
736     // create a new models array,
737     // and pre-calculate Model.bsAtoms and Model.ac
738     Model[] newModels = new Model[mc - nModelsDeleted];
739     Model[] oldModels = am;
740     for (int i = 0, mpt = 0; i < mc; i++) {
741       if (!bsModels.get(i)) { // get a good count now
742         Model m = am[i];
743         // TODO this does not account for file numbers!
744         m.modelIndex = mpt;
745         newModels[mpt++] = m;
746       }
747     }
748     am = newModels;
749     int oldModelCount = mc;
750     // delete bonds
751     BS bsBonds = getBondsForSelectedAtoms(bsDeleted, true);
752     deleteBonds(bsBonds, true);
753 
754     // main deletion cycle
755 
756     for (int i = 0, mpt = 0; i < oldModelCount; i++) {
757       if (!bsModels.get(i)) {
758         mpt++;
759         continue;
760       }
761       Model old = oldModels[i];
762       int nAtoms = old.act;
763       if (nAtoms == 0)
764         continue;
765       BS bsModelAtoms = old.bsAtoms;
766       int firstAtomIndex = old.firstAtomIndex;
767 
768       // delete from symmetry set
769       BSUtil.deleteBits(bsSymmetry, bsModelAtoms);
770 
771       // delete from stateScripts, model arrays and bitsets,
772       // atom arrays, and atom bitsets
773       deleteModel(mpt, bsModelAtoms, bsBonds);
774 
775       deleteModelAtoms(firstAtomIndex, nAtoms, bsModelAtoms);
776       vwr.deleteModelAtoms(mpt, firstAtomIndex, nAtoms, bsModelAtoms);
777 
778 
779       // adjust all models after this one
780       for (int j = oldModelCount; --j > i;)
781         oldModels[j].fixIndices(mpt, nAtoms, bsModelAtoms);
782 
783       // adjust all shapes
784       vwr.shm.deleteShapeAtoms(new Object[] { newModels, at,
785           new int[] { mpt, firstAtomIndex, nAtoms } }, bsModelAtoms);
786       mc--;
787     }
788 
789     // set final values
790     //final deletions
791     haveBioModels = false;
792     for (int i = mc; --i >= 0;)
793       if (am[i].isBioModel) {
794         haveBioModels = true;
795         bioModelset.set(vwr, this);
796       }
797     validateBspf(false);
798     bsAll = null;
799     resetMolecules();
800     isBbcageDefault = false;
801     calcBoundBoxDimensions(null, 1);
802     return bsDeleted;
803   }
804 
resetMolecules()805   public void resetMolecules() {
806     bsAll = null;
807     molecules = null;
808     moleculeCount = 0;
809     resetChirality();
810   }
811 
resetChirality()812   private void resetChirality() {
813     if (haveChirality) {
814       int modelIndex = -1;
815       for (int i = ac; --i >= 0;) {
816         Atom a = at[i];
817         if (a == null)
818           continue;
819         a.setCIPChirality(0);
820         if (a.mi != modelIndex && a.mi < am.length)
821           am[modelIndex = a.mi].hasChirality = false;
822       }
823     }
824   }
825 
deleteModel(int modelIndex, BS bsModelAtoms, BS bsBonds)826   private void deleteModel(int modelIndex, BS bsModelAtoms, BS bsBonds) {
827     /*
828      *   ModelCollection.modelSetAuxiliaryInfo["group3Lists", "group3Counts, "models"]
829      * ModelCollection.stateScripts ?????
830      */
831     if (modelIndex < 0) {
832       return;
833     }
834 
835     modelNumbers = (int[]) AU.deleteElements(modelNumbers, modelIndex, 1);
836     modelFileNumbers = (int[]) AU.deleteElements(modelFileNumbers, modelIndex,
837         1);
838     modelNumbersForAtomLabel = (String[]) AU.deleteElements(
839         modelNumbersForAtomLabel, modelIndex, 1);
840     modelNames = (String[]) AU.deleteElements(modelNames, modelIndex, 1);
841     frameTitles = (String[]) AU.deleteElements(frameTitles, modelIndex, 1);
842     thisStateModel = -1;
843     String[] group3Lists = (String[]) getInfoM("group3Lists");
844     int[][] group3Counts = (int[][]) getInfoM("group3Counts");
845     int ptm = modelIndex + 1;
846     if (group3Lists != null && group3Lists[ptm] != null) {
847       for (int i = group3Lists[ptm].length() / 6; --i >= 0;)
848         if (group3Counts[ptm][i] > 0) {
849           group3Counts[0][i] -= group3Counts[ptm][i];
850           if (group3Counts[0][i] == 0)
851             group3Lists[0] = group3Lists[0].substring(0, i * 6) + ",["
852                 + group3Lists[0].substring(i * 6 + 2);
853         }
854     }
855     if (group3Lists != null) {
856       msInfo.put("group3Lists", AU.deleteElements(group3Lists, modelIndex, 1));
857       msInfo
858           .put("group3Counts", AU.deleteElements(group3Counts, modelIndex, 1));
859     }
860 
861     //fix cellInfos array
862     if (unitCells != null) {
863       unitCells = (SymmetryInterface[]) AU.deleteElements(unitCells,
864           modelIndex, 1);
865     }
866 
867     // correct stateScripts, particularly CONNECT scripts
868     for (int i = stateScripts.size(); --i >= 0;) {
869       if (!stateScripts.get(i).deleteAtoms(modelIndex, bsBonds, bsModelAtoms)) {
870         stateScripts.removeItemAt(i);
871       }
872     }
873   }
874 
setAtomProperty(BS bs, int tok, int iValue, float fValue, String sValue, float[] values, String[] list)875   public void setAtomProperty(BS bs, int tok, int iValue, float fValue,
876                               String sValue, float[] values, String[] list) {
877     switch (tok) {
878     case T.backbone:
879     case T.cartoon:
880     case T.meshRibbon:
881     case T.ribbon:
882     case T.rocket:
883     case T.strands:
884     case T.trace:
885       if (fValue > Shape.RADIUS_MAX)
886         fValue = Shape.RADIUS_MAX;
887       if (values != null) {
888         // convert to atom indices
889         float[] newValues = new float[ac];
890         try {
891           for (int i = bs.nextSetBit(0), ii = 0; i >= 0; i = bs
892               .nextSetBit(i + 1))
893             newValues[i] = values[ii++];
894         } catch (Exception e) {
895           return;
896         }
897         values = newValues;
898       }
899       //$FALL-THROUGH$
900     case T.halo:
901     case T.star:
902       RadiusData rd = null;
903       int mar = 0;
904       if (values == null) {
905         if (fValue > Atom.RADIUS_MAX)
906           fValue = Atom.RADIUS_GLOBAL;
907         if (fValue < 0)
908           fValue = 0;
909         mar = (int) Math.floor(fValue * 2000);
910       } else {
911         rd = new RadiusData(values, 0, null, null);
912       }
913       sm.setShapeSizeBs(JC.shapeTokenIndex(tok), mar, rd, bs);
914       return;
915     }
916     setAPm(bs, tok, iValue, fValue, sValue, values, list);
917   }
918 
919   @SuppressWarnings("unchecked")
getFileData(int modelIndex)920   public Object getFileData(int modelIndex) {
921     if (modelIndex < 0)
922       return "";
923     Map<String, Object> fileData = (Map<String, Object>) getInfo(modelIndex,
924         "fileData");
925     if (fileData != null)
926       return fileData;
927     if (!getInfoB(modelIndex, "isCIF"))
928       return getPDBHeader(modelIndex);
929     fileData = vwr.getCifData(modelIndex);
930     setInfo(modelIndex, "fileData", fileData);
931     return fileData;
932   }
933 
934   /**
935    * these are hydrogens that are being added due to a load 2D command and are
936    * therefore not to be flagged as NEW
937    *
938    * @param vConnections
939    * @param pts
940    * @return BitSet of new atoms
941    */
addHydrogens(Lst<Atom> vConnections, P3[] pts)942   public BS addHydrogens(Lst<Atom> vConnections, P3[] pts) {
943     int modelIndex = mc - 1;
944     BS bs = new BS();
945     if (isTrajectory(modelIndex) || am[modelIndex].getGroupCount() > 1) {
946       // can't add atoms to a trajectory or a system with multiple groups!
947       return bs;
948     }
949     growAtomArrays(ac + pts.length);
950     RadiusData rd = vwr.rd;
951     short mad = getDefaultMadFromOrder(1);
952     am[modelIndex].resetDSSR(false);
953     for (int i = 0, n = am[modelIndex].act + 1; i < vConnections.size(); i++, n++) {
954       Atom atom1 = vConnections.get(i);
955       // hmm. atom1.group will not be expanded, though...
956       // something like within(group,...) will not select these atoms!
957       Atom atom2 = addAtom(modelIndex, atom1.group, 1, "H" + n, null, n,
958           atom1.getSeqID(), n, pts[i], Float.NaN, null, 0, 0, 100, Float.NaN,
959           null, false, (byte) 0, null, Float.NaN);
960 
961       atom2.setMadAtom(vwr, rd);
962       bs.set(atom2.i);
963       bondAtoms(atom1, atom2, Edge.BOND_COVALENT_SINGLE, mad, null, 0, false,
964           false);
965     }
966     // must reset the shapes to give them new atom counts and arrays
967     sm.loadDefaultShapes(this);
968     return bs;
969   }
970 
971   /**
972    * initial transfer of model data from old to new model set. Note that all new
973    * models are added later, AFTER thfe old ones. This is very important,
974    * because all of the old atom numbers must map onto the same numbers in the
975    * new model set, or the state script will not run properly, among other
976    * problems.
977    *
978    * @param mergeModelSet
979    */
mergeModelArrays(ModelSet mergeModelSet)980   protected void mergeModelArrays(ModelSet mergeModelSet) {
981     at = mergeModelSet.at;
982     bo = mergeModelSet.bo;
983     stateScripts = mergeModelSet.stateScripts;
984     proteinStructureTainted = mergeModelSet.proteinStructureTainted;
985     thisStateModel = -1;
986     bsSymmetry = mergeModelSet.bsSymmetry;
987     modelFileNumbers = mergeModelSet.modelFileNumbers; // file * 1000000 + modelInFile (1-based)
988     modelNumbersForAtomLabel = mergeModelSet.modelNumbersForAtomLabel;
989     modelNames = mergeModelSet.modelNames;
990     modelNumbers = mergeModelSet.modelNumbers;
991     frameTitles = mergeModelSet.frameTitles;
992     haveChirality = mergeModelSet.haveChirality;
993     if (msInfo != null)
994       msInfo.remove("models");
995     mergeAtomArrays(mergeModelSet);
996   }
997 
getUnitCell(int modelIndex)998   public SymmetryInterface getUnitCell(int modelIndex) {
999     if (modelIndex < 0 || modelIndex >= mc)
1000       return null;
1001     if (am[modelIndex].simpleCage != null)
1002       return am[modelIndex].simpleCage;
1003     if (unitCells != null && modelIndex < unitCells.length
1004         && unitCells[modelIndex] != null && unitCells[modelIndex].haveUnitCell())
1005       return unitCells[modelIndex];
1006     if (getInfo(modelIndex, "unitCellParams") != null) {
1007       if (unitCells == null)
1008         unitCells = new SymmetryInterface[mc];
1009       haveUnitCells = true;
1010       return unitCells[modelIndex] = vwr.getSymTemp().setSymmetryInfo(modelIndex,
1011           am[modelIndex].auxiliaryInfo, null);
1012     }
1013     return null;
1014   }
1015 
setModelCage(int modelIndex, SymmetryInterface simpleCage)1016   public void setModelCage(int modelIndex, SymmetryInterface simpleCage) {
1017     if (modelIndex >= 0 && modelIndex < mc) {
1018       am[modelIndex].simpleCage = simpleCage;
1019       haveUnitCells = true;
1020     }
1021   }
1022 
getModelName(int modelIndex)1023   public String getModelName(int modelIndex) {
1024     return mc < 1 ? "" : modelIndex >= 0 ? modelNames[modelIndex]
1025         : modelNumbersForAtomLabel[-1 - modelIndex];
1026   }
1027 
getModelTitle(int modelIndex)1028   public String getModelTitle(int modelIndex) {
1029     return (String) getInfo(modelIndex, "title");
1030   }
1031 
getModelFileName(int modelIndex)1032   public String getModelFileName(int modelIndex) {
1033     return (String) getInfo(modelIndex, "fileName");
1034   }
1035 
getModelFileType(int modelIndex)1036   public String getModelFileType(int modelIndex) {
1037     return (String) getInfo(modelIndex, "fileType");
1038   }
1039 
setFrameTitle(BS bsFrames, Object title)1040   public void setFrameTitle(BS bsFrames, Object title) {
1041     if (title instanceof String) {
1042       for (int i = bsFrames.nextSetBit(0); i >= 0; i = bsFrames
1043           .nextSetBit(i + 1))
1044         frameTitles[i] = (String) title;
1045     } else {
1046       String[] list = (String[]) title;
1047       for (int i = bsFrames.nextSetBit(0), n = 0; i >= 0; i = bsFrames
1048           .nextSetBit(i + 1))
1049         if (n < list.length)
1050           frameTitles[i] = list[n++];
1051     }
1052   }
1053 
getFrameTitle(int modelIndex)1054   public String getFrameTitle(int modelIndex) {
1055     return (modelIndex >= 0 && modelIndex < mc ? frameTitles[modelIndex] : "");
1056   }
1057 
getModelNumberForAtomLabel(int modelIndex)1058   public String getModelNumberForAtomLabel(int modelIndex) {
1059     return modelNumbersForAtomLabel[modelIndex];
1060   }
1061 
1062   /**
1063    * In versions earlier than 12.1.51, groups[] was a field of ModelCollection.
1064    * But this is not necessary, and it was wasting space. This method is only
1065    * called when polymers are recreated.
1066    *
1067    * @return full array of groups in modelSet
1068    */
getGroups()1069   public Group[] getGroups() {
1070     int n = 0;
1071     for (int i = 0; i < mc; i++)
1072       n += am[i].getGroupCount();
1073     Group[] groups = new Group[n];
1074     for (int i = 0, iGroup = 0; i < mc; i++)
1075       for (int j = 0; j < am[i].chainCount; j++)
1076         for (int k = 0; k < am[i].chains[j].groupCount; k++) {
1077           groups[iGroup] = am[i].chains[j].groups[k];
1078           groups[iGroup].groupIndex = iGroup;
1079           iGroup++;
1080         }
1081     return groups;
1082   }
1083 
1084   /**
1085    * deprecated due to multimodel issues, but required by an interface -- do NOT
1086    * remove.
1087    *
1088    * @return just the first unit cell
1089    *
1090    */
getUnitCellParams()1091   public float[] getUnitCellParams() {
1092     SymmetryInterface c = getUnitCell(0);
1093     return (c == null ? null : c.getUnitCellParams());
1094   }
1095 
setCrystallographicDefaults()1096   public boolean setCrystallographicDefaults() {
1097     return !haveBioModels && someModelsHaveSymmetry
1098         && someModelsHaveFractionalCoordinates;
1099   }
1100 
getBoundBoxCenter(int modelIndex)1101   public P3 getBoundBoxCenter(int modelIndex) {
1102     return (isJmolDataFrameForModel(modelIndex) ? new P3()
1103         : (getDefaultBoundBox() == null ? boxInfo : defaultBBox)
1104             .getBoundBoxCenter());
1105   }
1106 
getBoundBoxCornerVector()1107   public V3 getBoundBoxCornerVector() {
1108     return boxInfo.getBoundBoxCornerVector();
1109   }
1110 
getBBoxVertices()1111   public Point3fi[] getBBoxVertices() {
1112     return boxInfo.getBoundBoxVertices();
1113   }
1114 
setBoundBox(T3 pt1, T3 pt2, boolean byCorner, float scale)1115   public void setBoundBox(T3 pt1, T3 pt2, boolean byCorner, float scale) {
1116     isBbcageDefault = false;
1117     bboxModels = null;
1118     bboxAtoms = null;
1119     boxInfo.setBoundBox(pt1, pt2, byCorner, scale);
1120   }
1121 
getBoundBoxCommand(boolean withOptions)1122   public String getBoundBoxCommand(boolean withOptions) {
1123     if (!withOptions && bboxAtoms != null)
1124       return "boundbox " + Escape.eBS(bboxAtoms);
1125     ptTemp.setT(boxInfo.getBoundBoxCenter());
1126     V3 bbVector = boxInfo.getBoundBoxCornerVector();
1127     String s = (withOptions ? "boundbox " + Escape.eP(ptTemp) + " "
1128         + Escape.eP(bbVector) + "\n#or\n" : "");
1129     ptTemp.sub(bbVector);
1130     s += "boundbox corners " + Escape.eP(ptTemp) + " ";
1131     ptTemp.scaleAdd2(2, bbVector, ptTemp);
1132     float v = Math.abs(8 * bbVector.x * bbVector.y * bbVector.z);
1133     s += Escape.eP(ptTemp) + " # volume = " + v;
1134     return s;
1135   }
1136 
findAtomsInRectangle(Rectangle rect)1137   public BS findAtomsInRectangle(Rectangle rect) {
1138     BS bsModels = vwr.getVisibleFramesBitSet();
1139     BS bs = new BS();
1140     for (int i = ac; --i >= 0;) {
1141       Atom atom = at[i];
1142       if (atom == null)
1143         continue;
1144       if (!bsModels.get(atom.mi))
1145         i = am[atom.mi].firstAtomIndex;
1146       else if (atom.checkVisible() && rect.contains(atom.sX, atom.sY))
1147         bs.set(i);
1148     }
1149     return bs;
1150   }
1151 
getDefaultVdwType(int modelIndex)1152   public VDW getDefaultVdwType(int modelIndex) {
1153     return (!am[modelIndex].isBioModel ? VDW.AUTO_BABEL
1154         : am[modelIndex].hydrogenCount == 0 ? VDW.AUTO_JMOL : VDW.AUTO_BABEL); // RASMOL is too small
1155   }
1156 
setRotationRadius(int modelIndex, float angstroms)1157   public boolean setRotationRadius(int modelIndex, float angstroms) {
1158     if (isJmolDataFrameForModel(modelIndex)) {
1159       am[modelIndex].defaultRotationRadius = angstroms;
1160       return false;
1161     }
1162     return true;
1163   }
1164 
calcRotationRadius(int modelIndex, P3 center, boolean useBoundBox)1165   public float calcRotationRadius(int modelIndex, P3 center, boolean useBoundBox) {
1166     if (isJmolDataFrameForModel(modelIndex)) {
1167       float r = am[modelIndex].defaultRotationRadius;
1168       return (r == 0 ? 10 : r);
1169     }
1170     if (useBoundBox && getDefaultBoundBox() != null)
1171       return defaultBBox.getMaxDim() / 2 * 1.2f;
1172     float maxRadius = 0;
1173     for (int i = ac; --i >= 0;) {
1174       Atom atom = at[i];
1175       if (atom == null)
1176         continue;
1177       if (isJmolDataFrameForAtom(atom)) {
1178         modelIndex = atom.mi;
1179         while (i >= 0 && at[i] != null && at[i].mi == modelIndex)
1180           i--;
1181         continue;
1182       }
1183       atom = at[i];
1184       if (atom == null)
1185         continue;
1186       float distAtom = center.distance(atom);
1187       float outerVdw = distAtom + getRadiusVdwJmol(atom);
1188       if (outerVdw > maxRadius)
1189         maxRadius = outerVdw;
1190     }
1191     return (maxRadius == 0 ? 10 : maxRadius);
1192   }
1193 
calcBoundBoxDimensions(BS bs, float scale)1194   public void calcBoundBoxDimensions(BS bs, float scale) {
1195     if (bs != null && bs.nextSetBit(0) < 0)
1196       bs = null;
1197     if (bs == null && isBbcageDefault || ac == 0)
1198       return;
1199     if (getDefaultBoundBox() == null) {
1200       bboxModels = getModelBS(bboxAtoms = BSUtil.copy(bs), false);
1201       if (calcAtomsMinMax(bs, boxInfo) == ac)
1202         isBbcageDefault = true;
1203       if (bs == null) { // from modelLoader or reset
1204         if (unitCells != null)
1205           calcUnitCellMinMax();
1206       }
1207     } else {
1208       P3[] vertices = defaultBBox.getBoundBoxVertices();
1209       boxInfo.reset();
1210       for (int j = 0; j < 8; j++)
1211         boxInfo.addBoundBoxPoint(vertices[j]);
1212     }
1213     boxInfo.setBbcage(scale);
1214   }
1215 
1216   /**
1217    * The default bounding box is created when the LOAD .... FILL BOUNDBOX or
1218    * FILL UNITCELL is use.
1219    *
1220    * @return default bounding box, possibly null
1221    */
getDefaultBoundBox()1222   private BoxInfo getDefaultBoundBox() {
1223     T3[] bbox = (T3[]) getInfoM("boundbox");
1224     if (bbox == null)
1225       defaultBBox = null;
1226     else {
1227       if (defaultBBox == null)
1228         defaultBBox = new BoxInfo();
1229       defaultBBox.setBoundBoxFromOABC(bbox);
1230     }
1231     return defaultBBox;
1232   }
1233 
getBoxInfo(BS bs, float scale)1234   public BoxInfo getBoxInfo(BS bs, float scale) {
1235     if (bs == null)
1236       return boxInfo;
1237     BoxInfo bi = new BoxInfo();
1238     calcAtomsMinMax(bs, bi);
1239     bi.setBbcage(scale);
1240     return bi;
1241   }
1242 
calcAtomsMinMax(BS bs, BoxInfo boxInfo)1243   public int calcAtomsMinMax(BS bs, BoxInfo boxInfo) {
1244     boxInfo.reset();
1245     int nAtoms = 0;
1246     boolean isAll = (bs == null);
1247     int i0 = (isAll ? ac - 1 : bs.nextSetBit(0));
1248     for (int i = i0; i >= 0; i = (isAll ? i - 1 : bs.nextSetBit(i + 1))) {
1249       nAtoms++;
1250       Atom a = at[i];
1251       if (a != null && !isJmolDataFrameForAtom(a))
1252         boxInfo.addBoundBoxPoint(a);
1253     }
1254     return nAtoms;
1255   }
1256 
calcUnitCellMinMax()1257   private void calcUnitCellMinMax() {
1258     P3 pt = new P3();
1259     for (int i = 0; i < mc; i++) {
1260       if (!unitCells[i].getCoordinatesAreFractional())
1261         continue;
1262       P3[] vertices = unitCells[i].getUnitCellVerticesNoOffset();
1263       P3 offset = unitCells[i].getCartesianOffset();
1264       for (int j = 0; j < 8; j++) {
1265         pt.add2(offset, vertices[j]);
1266         boxInfo.addBoundBoxPoint(pt);
1267       }
1268     }
1269   }
1270 
calcRotationRadiusBs(BS bs)1271   public float calcRotationRadiusBs(BS bs) {
1272     // Eval getZoomFactor
1273     P3 center = getAtomSetCenter(bs);
1274     float maxRadius = 0;
1275     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
1276       Atom atom = at[i];
1277       float distAtom = center.distance(atom);
1278       float outerVdw = distAtom + getRadiusVdwJmol(atom);
1279       if (outerVdw > maxRadius)
1280         maxRadius = outerVdw;
1281     }
1282     return (maxRadius == 0 ? 10 : maxRadius);
1283   }
1284 
1285   /**
1286    *
1287    * @param vAtomSets
1288    * @param addCenters
1289    * @return array of two lists of points, centers first if desired
1290    */
1291 
getCenterAndPoints(Lst<Object[]> vAtomSets, boolean addCenters)1292   public P3[][] getCenterAndPoints(Lst<Object[]> vAtomSets, boolean addCenters) {
1293     BS bsAtoms1, bsAtoms2;
1294     int n = (addCenters ? 1 : 0);
1295     for (int ii = vAtomSets.size(); --ii >= 0;) {
1296       Object[] bss = vAtomSets.get(ii);
1297       bsAtoms1 = (BS) bss[0];
1298       if (bss[1] instanceof BS) {
1299         bsAtoms2 = (BS) bss[1];
1300         n += Math.min(bsAtoms1.cardinality(), bsAtoms2.cardinality());
1301       } else {
1302         n += Math.min(bsAtoms1.cardinality(), ((P3[]) bss[1]).length);
1303       }
1304     }
1305     P3[][] points = new P3[2][n];
1306     if (addCenters) {
1307       points[0][0] = new P3();
1308       points[1][0] = new P3();
1309     }
1310     for (int ii = vAtomSets.size(); --ii >= 0;) {
1311       Object[] bss = vAtomSets.get(ii);
1312       bsAtoms1 = (BS) bss[0];
1313       if (bss[1] instanceof BS) {
1314         bsAtoms2 = (BS) bss[1];
1315         for (int i = bsAtoms1.nextSetBit(0), j = bsAtoms2.nextSetBit(0); i >= 0
1316             && j >= 0; i = bsAtoms1.nextSetBit(i + 1), j = bsAtoms2
1317             .nextSetBit(j + 1)) {
1318           points[0][--n] = at[i];
1319           points[1][n] = at[j];
1320           if (addCenters) {
1321             points[0][0].add(at[i]);
1322             points[1][0].add(at[j]);
1323           }
1324         }
1325       } else {
1326         P3[] coords = (P3[]) bss[1];
1327         for (int i = bsAtoms1.nextSetBit(0), j = 0; i >= 0 && j < coords.length; i = bsAtoms1
1328             .nextSetBit(i + 1), j++) {
1329           points[0][--n] = at[i];
1330           points[1][n] = coords[j];
1331           if (addCenters) {
1332             points[0][0].add(at[i]);
1333             points[1][0].add(coords[j]);
1334           }
1335         }
1336       }
1337     }
1338     if (addCenters) {
1339       points[0][0].scale(1f / (points[0].length - 1));
1340       points[1][0].scale(1f / (points[1].length - 1));
1341     }
1342     return points;
1343   }
1344 
getAtomSetCenter(BS bs)1345   public P3 getAtomSetCenter(BS bs) {
1346     P3 ptCenter = new P3();
1347     int nPoints = 0;
1348     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
1349       if (!isJmolDataFrameForAtom(at[i])) {
1350         nPoints++;
1351         ptCenter.add(at[i]);
1352       }
1353     }
1354     if (nPoints > 1)
1355       ptCenter.scale(1.0f / nPoints);
1356     return ptCenter;
1357   }
1358 
getAverageAtomPoint()1359   public P3 getAverageAtomPoint() {
1360 //    if (averageAtomPoint == null)
1361 //      averageAtomPoint = getAtomSetCenter(vwr.getAllAtoms());
1362     return getAtomSetCenter(vwr.bsA());//averageAtomPoint;
1363   }
1364 
setAPm(BS bs, int tok, int iValue, float fValue, String sValue, float[] values, String[] list)1365   protected void setAPm(BS bs, int tok, int iValue, float fValue,
1366                         String sValue, float[] values, String[] list) {
1367     setAPa(bs, tok, iValue, fValue, sValue, values, list);
1368     switch (tok) {
1369     case T.valence:
1370     case T.formalcharge:
1371       if (vwr.getBoolean(T.smartaromatic))
1372         assignAromaticBondsBs(true, null);
1373       break;
1374     }
1375   }
1376 
addStateScript(String script1, BS bsBonds, BS bsAtoms1, BS bsAtoms2, String script2, boolean addFrameNumber, boolean postDefinitions)1377   public StateScript addStateScript(String script1, BS bsBonds, BS bsAtoms1,
1378                                     BS bsAtoms2, String script2,
1379                                     boolean addFrameNumber,
1380                                     boolean postDefinitions) {
1381     int iModel = vwr.am.cmi;
1382     if (addFrameNumber) {
1383       if (thisStateModel != iModel)
1384         script1 = "frame "
1385             + (iModel < 0 ? "all #" + iModel : getModelNumberDotted(iModel))
1386             + ";\n  " + script1;
1387       thisStateModel = iModel;
1388     } else {
1389       thisStateModel = -1;
1390     }
1391     StateScript stateScript = new StateScript(thisStateModel, script1, bsBonds,
1392         bsAtoms1, bsAtoms2, script2, postDefinitions);
1393     if (stateScript.isValid()) {
1394       stateScripts.addLast(stateScript);
1395     }
1396     return stateScript;
1397   }
1398 
freezeModels()1399   void freezeModels() {
1400     haveBioModels = false;
1401     for (int iModel = mc; --iModel >= 0;)
1402       haveBioModels |= am[iModel].freeze();
1403   }
1404 
getStructureList()1405   public Map<STR, float[]> getStructureList() {
1406     return vwr.getStructureList();
1407   }
1408 
getInfoM(String keyName)1409   public Object getInfoM(String keyName) {
1410     // the preferred method now
1411     return (msInfo == null ? null : msInfo.get(keyName));
1412   }
1413 
getMSInfoB(String keyName)1414   public boolean getMSInfoB(String keyName) {
1415     Object val = getInfoM(keyName);
1416     return (val instanceof Boolean && ((Boolean) val).booleanValue());
1417   }
1418 
1419   /**
1420    * could be the base model or one of the subframes
1421    *
1422    * @param modelIndex
1423    * @return is any part of a trajectory
1424    */
isTrajectory(int modelIndex)1425   public boolean isTrajectory(int modelIndex) {
1426     return am[modelIndex].isTrajectory;
1427   }
1428 
isTrajectorySubFrame(int i)1429   public boolean isTrajectorySubFrame(int i) {
1430     return (am[i].trajectoryBaseIndex != i);
1431   }
1432 
isTrajectoryMeasurement(int[] countPlusIndices)1433   public boolean isTrajectoryMeasurement(int[] countPlusIndices) {
1434     return (trajectory != null && trajectory.hasMeasure(countPlusIndices));
1435   }
1436 
1437   /**
1438    * Get the set of models associated with a set of atoms.
1439    * This must allow for appended models.
1440    * @param atomList
1441    * @param allTrajectories
1442    * @return bit set of models
1443    */
getModelBS(BS atomList, boolean allTrajectories)1444   public BS getModelBS(BS atomList, boolean allTrajectories) {
1445     BS bs = new BS();
1446     int modelIndex = 0;
1447     boolean isAll = (atomList == null);
1448     allTrajectories &= (trajectory != null);
1449     int i0 = (isAll ? 0 : atomList.nextSetBit(0));
1450     for (int i = i0; i >= 0 && i < ac; i = (isAll ? i + 1 : atomList
1451         .nextSetBit(i + 1))) {
1452       if (at[i] == null)
1453         continue;
1454       bs.set(modelIndex = at[i].mi);
1455       if (allTrajectories)
1456         trajectory.getModelBS(modelIndex, bs);
1457       Model m = am[modelIndex];
1458       if (m.isOrderly)
1459         i = m.firstAtomIndex + m.act - 1;
1460     }
1461    // System.out.println("MS " + atomList + " " + bs);
1462     return bs;
1463   }
1464 
1465   /**
1466    * only some models can be iterated through. models for which
1467    * trajectoryBaseIndexes[i] != i are trajectories only
1468    *
1469    * @param allowJmolData
1470    * @return bitset of models
1471    */
getIterativeModels(boolean allowJmolData)1472   public BS getIterativeModels(boolean allowJmolData) {
1473     BS bs = new BS();
1474     for (int i = 0; i < mc; i++) {
1475       if (!allowJmolData && isJmolDataFrameForModel(i))
1476         continue;
1477       if (!isTrajectorySubFrame(i))
1478         bs.set(i);
1479     }
1480     return bs;
1481   }
1482 
fillAtomData(AtomData atomData, int mode)1483   public void fillAtomData(AtomData atomData, int mode) {
1484     if ((mode & AtomData.MODE_FILL_MOLECULES) != 0) {
1485       getMolecules();
1486       atomData.bsMolecules = new BS[molecules.length];
1487       atomData.atomMolecule = new int[ac];
1488       BS bs;
1489       for (int i = 0; i < molecules.length; i++) {
1490         bs = atomData.bsMolecules[i] = molecules[i].atomList;
1491         for (int iAtom = bs.nextSetBit(0); iAtom >= 0; iAtom = bs
1492             .nextSetBit(iAtom + 1))
1493           atomData.atomMolecule[iAtom] = i;
1494       }
1495     }
1496     if ((mode & AtomData.MODE_GET_ATTACHED_HYDROGENS) != 0) {
1497       int[] nH = new int[1];
1498       atomData.hAtomRadius = vwr.getVanderwaalsMar(1) / 1000f;
1499       atomData.hAtoms = calculateHydrogens(atomData.bsSelected, nH, null, AtomCollection.CALC_H_JUSTC);
1500       atomData.hydrogenAtomCount = nH[0];
1501       return;
1502     }
1503     if (atomData.modelIndex < 0)
1504       atomData.firstAtomIndex = (atomData.bsSelected == null ? 0 : Math.max(0,
1505           atomData.bsSelected.nextSetBit(0)));
1506     else
1507       atomData.firstAtomIndex = am[atomData.modelIndex].firstAtomIndex;
1508     atomData.lastModelIndex = atomData.firstModelIndex = (ac == 0 ? 0
1509         : at[atomData.firstAtomIndex].mi);
1510     atomData.modelName = getModelNumberDotted(atomData.firstModelIndex);
1511     fillADa(atomData, mode);
1512   }
1513 
getModelNumberDotted(int modelIndex)1514   public String getModelNumberDotted(int modelIndex) {
1515     return (mc < 1 || modelIndex >= mc || modelIndex < 0 ? "" : Escape
1516         .escapeModelFileNumber(modelFileNumbers[modelIndex]));
1517   }
1518 
getModelNumber(int modelIndex)1519   public int getModelNumber(int modelIndex) {
1520     return modelNumbers[modelIndex == Integer.MAX_VALUE ? mc - 1 : modelIndex];
1521   }
1522 
getModelProperty(int modelIndex, String property)1523   public String getModelProperty(int modelIndex, String property) {
1524     Properties props = am[modelIndex].properties;
1525     return props == null ? null : props.getProperty(property);
1526   }
1527 
getModelAuxiliaryInfo(int modelIndex)1528   public Map<String, Object> getModelAuxiliaryInfo(int modelIndex) {
1529     return (modelIndex < 0 ? null : am[modelIndex].auxiliaryInfo);
1530   }
1531 
setInfo(int modelIndex, Object key, Object value)1532   public void setInfo(int modelIndex, Object key, Object value) {
1533     if (modelIndex >= 0 && modelIndex < mc)
1534       am[modelIndex].auxiliaryInfo.put((String) key, value);
1535   }
1536 
getInfo(int modelIndex, String key)1537   public Object getInfo(int modelIndex, String key) {
1538     return (modelIndex < 0 ? null : am[modelIndex].auxiliaryInfo.get(key));
1539   }
1540 
getInfoB(int modelIndex, String keyName)1541   protected boolean getInfoB(int modelIndex, String keyName) {
1542     Map<String, Object> info = am[modelIndex].auxiliaryInfo;
1543     return (info != null && info.containsKey(keyName) && ((Boolean) info
1544         .get(keyName)).booleanValue());
1545   }
1546 
getInfoI(int modelIndex, String keyName)1547   protected int getInfoI(int modelIndex, String keyName) {
1548     Map<String, Object> info = am[modelIndex].auxiliaryInfo;
1549     if (info != null && info.containsKey(keyName)) {
1550       return ((Integer) info.get(keyName)).intValue();
1551     }
1552     return Integer.MIN_VALUE;
1553   }
1554 
getInsertionCountInModel(int modelIndex)1555   public int getInsertionCountInModel(int modelIndex) {
1556     return am[modelIndex].insertionCount;
1557   }
1558 
modelFileNumberFromFloat(float fDotM)1559   public static int modelFileNumberFromFloat(float fDotM) {
1560     //only used in the case of select model = someVariable
1561     //2.1 and 2.10 will be ambiguous and reduce to 2.1
1562 
1563     int file = (int) Math.floor(fDotM);
1564     int model = (int) Math.floor((fDotM - file + 0.00001) * 10000);
1565     while (model != 0 && model % 10 == 0)
1566       model /= 10;
1567     return file * 1000000 + model;
1568   }
1569 
getChainCountInModelWater(int modelIndex, boolean countWater)1570   public int getChainCountInModelWater(int modelIndex, boolean countWater) {
1571     if (modelIndex < 0) {
1572       int chainCount = 0;
1573       for (int i = mc; --i >= 0;)
1574         chainCount += am[i].getChainCount(countWater);
1575       return chainCount;
1576     }
1577     return am[modelIndex].getChainCount(countWater);
1578   }
1579 
getGroupCountInModel(int modelIndex)1580   public int getGroupCountInModel(int modelIndex) {
1581     if (modelIndex < 0) {
1582       int groupCount = 0;
1583       for (int i = mc; --i >= 0;)
1584         groupCount += am[i].getGroupCount();
1585       return groupCount;
1586     }
1587     return am[modelIndex].getGroupCount();
1588   }
1589 
calcSelectedGroupsCount()1590   public void calcSelectedGroupsCount() {
1591     BS bsSelected = vwr.bsA();
1592     for (int i = mc; --i >= 0;)
1593       am[i].calcSelectedGroupsCount(bsSelected);
1594   }
1595 
isJmolDataFrameForModel(int modelIndex)1596   public boolean isJmolDataFrameForModel(int modelIndex) {
1597     return (modelIndex >= 0 && modelIndex < mc && am[modelIndex].isJmolDataFrame);
1598   }
1599 
isJmolDataFrameForAtom(Atom atom)1600   private boolean isJmolDataFrameForAtom(Atom atom) {
1601     return am[atom.mi].isJmolDataFrame;
1602   }
1603 
setJmolDataFrame(String type, int modelIndex, int modelDataIndex)1604   public void setJmolDataFrame(String type, int modelIndex, int modelDataIndex) {
1605     Model model = am[type == null ? am[modelDataIndex].dataSourceFrame
1606         : modelIndex];
1607     if (type == null) {
1608       //leaving a data frame -- just set generic to this one if quaternion
1609       type = am[modelDataIndex].jmolFrameType;
1610     }
1611     if (modelIndex >= 0) {
1612       if (model.dataFrames == null) {
1613         model.dataFrames = new Hashtable<String, Integer>();
1614       }
1615       am[modelDataIndex].dataSourceFrame = modelIndex;
1616       am[modelDataIndex].jmolFrameType = type;
1617       model.dataFrames.put(type, Integer.valueOf(modelDataIndex));
1618     }
1619     if (type.startsWith("quaternion") && type.indexOf("deriv") < 0) { //generic quaternion
1620       type = type.substring(0, type.indexOf(" "));
1621       model.dataFrames.put(type, Integer.valueOf(modelDataIndex));
1622     }
1623   }
1624 
getJmolDataFrameIndex(int modelIndex, String type)1625   public int getJmolDataFrameIndex(int modelIndex, String type) {
1626     if (am[modelIndex].dataFrames == null) {
1627       return -1;
1628     }
1629     Integer index = am[modelIndex].dataFrames.get(type);
1630     return (index == null ? -1 : index.intValue());
1631   }
1632 
clearDataFrameReference(int modelIndex)1633   protected void clearDataFrameReference(int modelIndex) {
1634     for (int i = 0; i < mc; i++) {
1635       Map<String, Integer> df = am[i].dataFrames;
1636       if (df == null) {
1637         continue;
1638       }
1639       Iterator<Integer> e = df.values().iterator();
1640       while (e.hasNext()) {
1641         if ((e.next()).intValue() == modelIndex) {
1642           e.remove();
1643         }
1644       }
1645     }
1646   }
1647 
getJmolFrameType(int modelIndex)1648   public String getJmolFrameType(int modelIndex) {
1649     return (modelIndex >= 0 && modelIndex < mc ? am[modelIndex].jmolFrameType
1650         : "modelSet");
1651   }
1652 
getJmolDataSourceFrame(int modelIndex)1653   public int getJmolDataSourceFrame(int modelIndex) {
1654     return (modelIndex >= 0 && modelIndex < mc ? am[modelIndex].dataSourceFrame
1655         : -1);
1656   }
1657 
saveModelOrientation(int modelIndex, Orientation orientation)1658   public void saveModelOrientation(int modelIndex, Orientation orientation) {
1659     am[modelIndex].orientation = orientation;
1660   }
1661 
getModelOrientation(int modelIndex)1662   public Orientation getModelOrientation(int modelIndex) {
1663     return am[modelIndex].orientation;
1664   }
1665 
1666   /*
1667    final static String[] pdbRecords = { "ATOM  ", "HELIX ", "SHEET ", "TURN  ",
1668    "MODEL ", "SCALE",  "HETATM", "SEQRES",
1669    "DBREF ", };
1670    */
1671 
getPDBHeader(int modelIndex)1672   public String getPDBHeader(int modelIndex) {
1673     return (am[modelIndex].isBioModel ? ((BioModel) am[modelIndex])
1674         .getFullPDBHeader() : getFileHeader(modelIndex));
1675   }
1676 
getFileHeader(int modelIndex)1677   public String getFileHeader(int modelIndex) {
1678     if (modelIndex < 0)
1679       return "";
1680     if (am[modelIndex].isBioModel)
1681       return getPDBHeader(modelIndex);
1682     String info = (String) getInfo(modelIndex, "fileHeader");
1683     if (info == null)
1684       info = modelSetName;
1685     if (info != null)
1686       return info;
1687     return "no header information found";
1688   }
1689 
1690   //////////////  individual models ////////////////
1691 
getAltLocCountInModel(int modelIndex)1692   public int getAltLocCountInModel(int modelIndex) {
1693     return am[modelIndex].altLocCount;
1694   }
1695 
getAltLocIndexInModel(int modelIndex, char alternateLocationID)1696   public int getAltLocIndexInModel(int modelIndex, char alternateLocationID) {
1697     if (alternateLocationID == '\0') {
1698       return 0;
1699     }
1700     String altLocList = getAltLocListInModel(modelIndex);
1701     if (altLocList.length() == 0) {
1702       return 0;
1703     }
1704     return altLocList.indexOf(alternateLocationID) + 1;
1705   }
1706 
getInsertionCodeIndexInModel(int modelIndex, char insertionCode)1707   public int getInsertionCodeIndexInModel(int modelIndex, char insertionCode) {
1708     if (insertionCode == '\0')
1709       return 0;
1710     String codeList = getInsertionListInModel(modelIndex);
1711     if (codeList.length() == 0)
1712       return 0;
1713     return codeList.indexOf(insertionCode) + 1;
1714   }
1715 
getAltLocListInModel(int modelIndex)1716   public String getAltLocListInModel(int modelIndex) {
1717     String str = (String) getInfo(modelIndex, "altLocs");
1718     return (str == null ? "" : str);
1719   }
1720 
getInsertionListInModel(int modelIndex)1721   private String getInsertionListInModel(int modelIndex) {
1722     String str = (String) getInfo(modelIndex, "insertionCodes");
1723     return (str == null ? "" : str);
1724   }
1725 
getModelSymmetryCount(int modelIndex)1726   public int getModelSymmetryCount(int modelIndex) {
1727     return (am[modelIndex].biosymmetryCount > 0 ? am[modelIndex].biosymmetryCount
1728         : unitCells == null || unitCells[modelIndex] == null ? 0
1729             : unitCells[modelIndex].getSpaceGroupOperationCount());
1730   }
1731 
getModelCellRange(int modelIndex)1732   public int[] getModelCellRange(int modelIndex) {
1733     return (unitCells == null ? null : unitCells[modelIndex].getCellRange());
1734   }
1735 
getLastVibrationVector(int modelIndex, int tok)1736   public int getLastVibrationVector(int modelIndex, int tok) {
1737     if (vibrations != null && modelIndex < vwr.ms.mc) {
1738       Vibration v;
1739       int a1 = (modelIndex < 0 || isTrajectory(modelIndex)
1740           || modelIndex >= mc - 1 ? ac : am[modelIndex + 1].firstAtomIndex);
1741       int a0 = (modelIndex <= 0 ? 0 : am[modelIndex].firstAtomIndex);
1742       for (int i = a1; --i >= a0;) {
1743         if ((modelIndex < 0 || at[i].mi == modelIndex)
1744             && ((tok == T.modulation || tok == 0)
1745                 && (v = (Vibration) getModulation(i)) != null || (tok == T.vibration || tok == 0)
1746                 && (v = getVibration(i, false)) != null) && v.isNonzero())
1747           return i;
1748       }
1749     }
1750     return -1;
1751   }
1752 
getModulationList(BS bs, char type, P3 t456)1753   public Lst<Object> getModulationList(BS bs, char type, P3 t456) {
1754     Lst<Object> list = new Lst<Object>();
1755     if (vibrations != null)
1756       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
1757         if (vibrations[i] instanceof JmolModulationSet)
1758           list.addLast(((JmolModulationSet) vibrations[i]).getModulation(type,
1759               t456));
1760         else
1761           list.addLast(Float.valueOf(type == 'O' ? Float.NaN : -1));
1762     return list;
1763   }
1764 
getElementsPresentBitSet(int modelIndex)1765   public BS getElementsPresentBitSet(int modelIndex) {
1766     if (modelIndex >= 0)
1767       return elementsPresent[modelIndex];
1768     BS bs = new BS();
1769     for (int i = 0; i < mc; i++)
1770       bs.or(elementsPresent[i]);
1771     return bs;
1772   }
1773 
1774   ///////// molecules /////////
1775 
getMoleculeIndex(int atomIndex, boolean inModel)1776   public int getMoleculeIndex(int atomIndex, boolean inModel) {
1777     //ColorManager
1778     if (moleculeCount == 0)
1779       getMolecules();
1780     for (int i = 0; i < moleculeCount; i++) {
1781       if (molecules[i].atomList.get(atomIndex))
1782         return (inModel ? molecules[i].indexInModel : i);
1783     }
1784     return 0;
1785   }
1786 
1787   /**
1788    * return cumulative sum of all atoms in molecules containing these atoms
1789    *
1790    * @param bs
1791    * @return bitset of atoms
1792    */
getMoleculeBitSet(BS bs)1793   public BS getMoleculeBitSet(BS bs) {
1794     if (moleculeCount == 0)
1795       getMolecules();
1796     BS bsResult = BSUtil.copy(bs);
1797     BS bsInitial = BSUtil.copy(bs);
1798     int i = 0;
1799     BS bsTemp = new BS();
1800     while ((i = bsInitial.length() - 1) >= 0) {
1801       bsTemp = getMoleculeBitSetForAtom(i);
1802       if (bsTemp == null) {
1803         // atom has been deleted
1804         bsInitial.clear(i);
1805         bsResult.clear(i);
1806         continue;
1807       }
1808       bsInitial.andNot(bsTemp);
1809       bsResult.or(bsTemp);
1810     }
1811     return bsResult;
1812   }
1813 
getMoleculeBitSetForAtom(int atomIndex)1814   public BS getMoleculeBitSetForAtom(int atomIndex) {
1815     if (moleculeCount == 0)
1816       getMolecules();
1817     for (int i = 0; i < moleculeCount; i++)
1818       if (molecules[i].atomList.get(atomIndex))
1819         return molecules[i].atomList;
1820     return null;
1821   }
1822 
getModelDipole(int modelIndex)1823   public V3 getModelDipole(int modelIndex) {
1824     if (modelIndex < 0)
1825       return null;
1826     V3 dipole = (V3) getInfo(modelIndex, "dipole");
1827     if (dipole == null)
1828       dipole = (V3) getInfo(modelIndex, "DIPOLE_VEC");
1829     return dipole;
1830   }
1831 
calculateMolecularDipole(int modelIndex, BS bsAtoms)1832   public V3 calculateMolecularDipole(int modelIndex, BS bsAtoms) throws JmolAsyncException {
1833     if (bsAtoms != null) {
1834       int ia = bsAtoms.nextSetBit(0);
1835       if (ia < 0)
1836         return null;
1837       modelIndex = at[ia].mi;
1838     }
1839     if (modelIndex < 0)
1840       return null;
1841     int nPos = 0;
1842     int nNeg = 0;
1843     float cPos = 0;
1844     float cNeg = 0;
1845     V3 pos = new V3();
1846     V3 neg = new V3();
1847     if (bsAtoms == null)
1848       bsAtoms = getModelAtomBitSetIncludingDeleted(-1, false);
1849     vwr.getOrCalcPartialCharges(am[modelIndex].bsAtoms, null);
1850     for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1)) {
1851       if (at[i].mi != modelIndex || at[i].isDeleted()) {
1852         continue;
1853       }
1854       float c = partialCharges[i];
1855       if (c < 0) {
1856         nNeg++;
1857         cNeg += c;
1858         neg.scaleAdd2(c, at[i], neg);
1859       } else if (c > 0) {
1860         nPos++;
1861         cPos += c;
1862         pos.scaleAdd2(c, at[i], pos);
1863       }
1864     }
1865     if (Math.abs(cPos + cNeg) > 0.015) { // BH Jmol 14.22.2 was 0.01, but PubChem charges are only to 0.01 precision
1866       Logger.info("Dipole calculation requires balanced charges: " + cPos + " "
1867           + cNeg);
1868       return null;
1869     }
1870     if (nNeg == 0 || nPos == 0)
1871       return null;
1872     pos.add(neg);
1873     pos.scale(4.8f); //1e-10f * 1.6e-19f/ 3.336e-30f;
1874     // 1 Debye = 3.336e-30 Coulomb-meter; C_e = 1.6022e-19 C
1875     return pos;
1876   }
1877 
getMoleculeCountInModel(int modelIndex)1878   public int getMoleculeCountInModel(int modelIndex) {
1879     //ColorManager
1880     //not implemented for pop-up menu -- will slow it down.
1881     int n = 0;
1882     if (moleculeCount == 0)
1883       getMolecules();
1884     if (modelIndex < 0)
1885       return moleculeCount;
1886     for (int i = 0; i < mc; i++) {
1887       if (modelIndex == i)
1888         n += am[i].moleculeCount;
1889     }
1890     return n;
1891   }
1892 
calcSelectedMoleculesCount()1893   public void calcSelectedMoleculesCount() {
1894     BS bsSelected = vwr.bsA();
1895     if (moleculeCount == 0)
1896       getMolecules();
1897     selectedMolecules.xor(selectedMolecules);
1898     //selectedMoleculeCount = 0;
1899     BS bsTemp = new BS();
1900     for (int i = 0; i < moleculeCount; i++) {
1901       BSUtil.copy2(bsSelected, bsTemp);
1902       bsTemp.and(molecules[i].atomList);
1903       if (bsTemp.length() > 0) {
1904         selectedMolecules.set(i);
1905         //selectedMoleculeCount++;
1906       }
1907     }
1908   }
1909 
1910   /**
1911    * deletes molecules based on: CENTROID -- molecular centroid is not in unit
1912    * cell CENTROID PACKED -- all molecule atoms are not in unit cell
1913    *
1914    * @param bs
1915    * @param minmax
1916    *        fractional [xmin, ymin, zmin, xmax, ymax, zmax, 1=packed]
1917    */
setCentroid(BS bs, int[] minmax)1918   public void setCentroid(BS bs, int[] minmax) {
1919     BS bsDelete = getNotInCentroid(bs, minmax);
1920     if (bsDelete != null && bsDelete.nextSetBit(0) >= 0)
1921       vwr.deleteAtoms(bsDelete, false);
1922   }
1923 
getNotInCentroid(BS bs, int[] minmax)1924   private BS getNotInCentroid(BS bs, int[] minmax) {
1925     int iAtom0 = bs.nextSetBit(0);
1926     if (iAtom0 < 0)
1927       return null;
1928     SymmetryInterface uc = getUnitCell(at[iAtom0].mi);
1929     return (uc == null ? null : uc.notInCentroid(this, bs, minmax));
1930   }
1931 
getMolecules()1932   public JmolMolecule[] getMolecules() {
1933     if (moleculeCount > 0)
1934       return molecules;
1935     if (molecules == null)
1936       molecules = new JmolMolecule[4];
1937     moleculeCount = 0;
1938     Model m = null;
1939     BS[] bsModelAtoms = new BS[mc];
1940     Lst<BS> biobranches = null;
1941     for (int i = 0; i < mc; i++) {
1942       // TODO: Trajectories?
1943       bsModelAtoms[i] = vwr.getModelUndeletedAtomsBitSet(i);
1944       m = am[i];
1945       m.moleculeCount = 0;
1946       biobranches = (m.isBioModel ? ((BioModel) m)
1947           .getBioBranches(biobranches) : null);
1948     }
1949     // problem, as with 1gzx, is that this does not include non-protein cofactors that are
1950     // covalently bonded. So we indicate a set of "biobranches" in JmolMolecule.getMolecules
1951     molecules = JmolMolecule.getMolecules(at, bsModelAtoms, biobranches, null);
1952     moleculeCount = molecules.length;
1953     for (int i = moleculeCount; --i >= 0;) {
1954       m = am[molecules[i].modelIndex];
1955       m.firstMoleculeIndex = i;
1956       m.moleculeCount++;
1957     }
1958     return molecules;
1959   }
1960 
1961   //////////// iterators //////////
1962 
initializeBspf()1963   protected void initializeBspf() {
1964     if (bspf != null && bspf.isValid)
1965       return;
1966     if (showRebondTimes)
1967       Logger.startTimer("build bspf");
1968     Bspf bspf = new Bspf(3);
1969     if (Logger.debugging)
1970       Logger.debug("sequential bspt order");
1971     BS bsNew = BS.newN(mc);
1972     for (int i = ac; --i >= 0;) {
1973       // important that we go backward here, because we are going to
1974       // use System.arrayCopy to expand the array ONCE only
1975       Atom atom = at[i];
1976       if (atom != null && !atom.isDeleted() && !isTrajectorySubFrame(atom.mi)) {
1977         bspf.addTuple(am[atom.mi].trajectoryBaseIndex, atom);
1978         bsNew.set(atom.mi);
1979       }
1980     }
1981     //      }
1982     if (showRebondTimes) {
1983       Logger.checkTimer("build bspf", false);
1984       bspf.stats();
1985       //        bspf.dump();
1986     }
1987     for (int i = bsNew.nextSetBit(0); i >= 0; i = bsNew.nextSetBit(i + 1))
1988       bspf.validateModel(i, true);
1989     bspf.isValid = true;
1990     this.bspf = bspf;
1991 
1992   }
1993 
initializeBspt(int modelIndex)1994   protected void initializeBspt(int modelIndex) {
1995     initializeBspf();
1996     if (bspf.isInitializedIndex(modelIndex))
1997       return;
1998     bspf.initialize(modelIndex, at,
1999         vwr.getModelUndeletedAtomsBitSet(modelIndex));
2000   }
2001 
setIteratorForPoint(AtomIndexIterator iterator, int modelIndex, T3 pt, float distance)2002   public void setIteratorForPoint(AtomIndexIterator iterator, int modelIndex,
2003                                   T3 pt, float distance) {
2004     if (modelIndex < 0) {
2005       iterator.setCenter(pt, distance);
2006       return;
2007     }
2008     initializeBspt(modelIndex);
2009     iterator.setModel(this, modelIndex, am[modelIndex].firstAtomIndex,
2010         Integer.MAX_VALUE, pt, distance, null);
2011   }
2012 
setIteratorForAtom(AtomIndexIterator iterator, int modelIndex, int atomIndex, float distance, RadiusData rd)2013   public void setIteratorForAtom(AtomIndexIterator iterator, int modelIndex,
2014                                  int atomIndex, float distance, RadiusData rd) {
2015     if (modelIndex < 0)
2016       modelIndex = at[atomIndex].mi;
2017     modelIndex = am[modelIndex].trajectoryBaseIndex;
2018     initializeBspt(modelIndex);
2019     iterator.setModel(this, modelIndex, am[modelIndex].firstAtomIndex,
2020         atomIndex, at[atomIndex], distance, rd);
2021   }
2022 
2023   /**
2024    * @param bsSelected
2025    * @param isGreaterOnly
2026    * @param modelZeroBased
2027    * @param hemisphereOnly
2028    * @param isMultiModel
2029    * @return an iterator
2030    */
getSelectedAtomIterator(BS bsSelected, boolean isGreaterOnly, boolean modelZeroBased, boolean hemisphereOnly, boolean isMultiModel)2031   public AtomIndexIterator getSelectedAtomIterator(BS bsSelected,
2032                                                    boolean isGreaterOnly,
2033                                                    boolean modelZeroBased,
2034                                                    boolean hemisphereOnly,
2035                                                    boolean isMultiModel) {
2036     //EnvelopeCalculation, IsoSolventReader
2037     // This iterator returns only atoms OTHER than the atom specified
2038     // and with the specified restrictions.
2039     // Model zero-based means the index returned is within the model,
2040     // not the full atom set. broken in 12.0.RC6; repaired in 12.0.RC15
2041 
2042     initializeBspf();
2043     AtomIteratorWithinModel iter;
2044     if (isMultiModel) {
2045       BS bsModels = getModelBS(bsSelected, false);
2046       for (int i = bsModels.nextSetBit(0); i >= 0; i = bsModels
2047           .nextSetBit(i + 1))
2048         initializeBspt(i);
2049       iter = new AtomIteratorWithinModelSet(bsModels);
2050     } else {
2051       iter = new AtomIteratorWithinModel();
2052     }
2053     iter.initialize(bspf, bsSelected, isGreaterOnly, modelZeroBased,
2054         hemisphereOnly, vwr.isParallel());
2055     return iter;
2056   }
2057 
2058   ////////// bonds /////////
2059 
2060   @Override
getBondCountInModel(int modelIndex)2061   public int getBondCountInModel(int modelIndex) {
2062     return (modelIndex < 0 ? bondCount : am[modelIndex].getBondCount());
2063   }
2064 
getAtomCountInModel(int modelIndex)2065   public int getAtomCountInModel(int modelIndex) {
2066     return (modelIndex < 0 ? ac : am[modelIndex].act);
2067   }
2068 
2069   /**
2070    * note -- this method returns ALL atoms, including deleted.
2071    *
2072    * @param bsModels
2073    * @return bitset of atoms
2074    */
getModelAtomBitSetIncludingDeletedBs(BS bsModels)2075   public BS getModelAtomBitSetIncludingDeletedBs(BS bsModels) {
2076     BS bs = new BS();
2077     if (bsModels == null && bsAll == null)
2078       bsAll = BSUtil.setAll(ac);
2079     if (bsModels == null)
2080       bs.or(bsAll);
2081     else
2082       for (int i = bsModels.nextSetBit(0); i >= 0; i = bsModels
2083           .nextSetBit(i + 1))
2084         bs.or(getModelAtomBitSetIncludingDeleted(i, false));
2085     return bs;
2086   }
2087 
2088   /**
2089    * Note that this method returns all atoms, included deleted ones. If you
2090    * don't want deleted atoms, then use
2091    * vwr.getModelAtomBitSetUndeleted(modelIndex, TRUE)
2092    *
2093    * @param modelIndex
2094    * @param asCopy
2095    *        MUST BE TRUE IF THE BITSET IS GOING TO BE MODIFIED!
2096    * @return either the actual bitset or a copy
2097    */
getModelAtomBitSetIncludingDeleted(int modelIndex, boolean asCopy)2098   public BS getModelAtomBitSetIncludingDeleted(int modelIndex, boolean asCopy) {
2099     BS bs = (modelIndex < 0 ? bsAll : am[modelIndex].bsAtoms);
2100     if (bs == null)
2101       bs = bsAll = BSUtil.setAll(ac);
2102     return (asCopy ? BSUtil.copy(bs) : bs);
2103   }
2104 
getAtomBitsMaybeDeleted(int tokType, Object specInfo)2105   protected BS getAtomBitsMaybeDeleted(int tokType, Object specInfo) {
2106     BS bs;
2107     switch (tokType) {
2108     default:
2109       return getAtomBitsMDa(tokType, specInfo, bs = new BS());
2110     case T.domains:
2111     case T.validation:
2112     case T.dssr:
2113     case T.rna3d:
2114     case T.basepair:
2115     case T.sequence:
2116       bs = new BS();
2117       return (haveBioModels ? bioModelset.getAtomBitsStr(tokType,
2118           (String) specInfo, bs) : bs);
2119     case T.bonds:
2120     case T.isaromatic:
2121       return getAtomBitsMDb(tokType, specInfo);
2122     case T.boundbox:
2123       BoxInfo boxInfo = getBoxInfo((BS) specInfo, 1);
2124       bs = getAtomsWithin(boxInfo.getBoundBoxCornerVector().length() + 0.0001f,
2125           boxInfo.getBoundBoxCenter(), null, -1);
2126       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
2127         if (!boxInfo.isWithin(at[i]))
2128           bs.clear(i);
2129       return bs;
2130     case T.cell:
2131       // select cell=555 (NO NOT an absolute quantity)
2132       // select cell=1505050
2133       // select cell=1500500500
2134       bs = new BS();
2135       P3 pt = (P3) specInfo;
2136       boolean ignoreOffset = false;//!vwr.getBoolean(T.fractionalrelative);
2137       for (int i = ac; --i >= 0;)
2138         if (at[i] != null && isInLatticeCell(i, pt, ptTemp2, ignoreOffset))
2139           bs.set(i);
2140       return bs;
2141     case T.centroid:
2142       // select centroid=555  -- like cell=555 but for whole molecules
2143       // if it  is one full molecule, then return the EMPTY bitset
2144       bs = BSUtil.newBitSet2(0, ac);
2145       P3 pt1 = (P3) specInfo;
2146       int[] minmax = new int[] { (int) pt1.x - 1, (int) pt1.y - 1, (int) pt1.z - 1,
2147           (int) pt1.x, (int) pt1.y, (int) pt1.z, 0 };
2148       for (int i = mc; --i >= 0;) {
2149         SymmetryInterface uc = getUnitCell(i);
2150         if (uc == null) {
2151           BSUtil.andNot(bs, am[i].bsAtoms);
2152           continue;
2153         }
2154         bs.andNot(uc.notInCentroid(this, am[i].bsAtoms, minmax));
2155       }
2156       return bs;
2157     case T.molecule:
2158       return getMoleculeBitSet((BS) specInfo);
2159     case T.spec_seqcode_range:
2160       return getSelectCodeRange((int[]) specInfo);
2161     case T.specialposition:
2162       bs = BS.newN(ac);
2163       int modelIndex = -1;
2164       int nOps = 0;
2165       for (int i = ac; --i >= 0;) {
2166         Atom atom = at[i];
2167         if (atom == null)
2168           continue;
2169         BS bsSym = atom.atomSymmetry;
2170         if (bsSym != null) {
2171           if (atom.mi != modelIndex) {
2172             modelIndex = atom.mi;
2173             if (getModelCellRange(modelIndex) == null)
2174               continue;
2175             nOps = getModelSymmetryCount(modelIndex);
2176           }
2177           // special positions are characterized by
2178           // multiple operator bits set in the first (overall)
2179           // block of nOpts bits.
2180           // only strictly true with load {nnn mmm 1}
2181 
2182           int n = 0;
2183           for (int j = nOps; --j >= 0;)
2184             if (bsSym.get(j))
2185               if (++n > 1) {
2186                 bs.set(i);
2187                 break;
2188               }
2189         }
2190       }
2191       return bs;
2192     case T.symmetry:
2193       return BSUtil.copy(bsSymmetry == null ? bsSymmetry = BS.newN(ac)
2194           : bsSymmetry);
2195     case T.unitcell:
2196       // select UNITCELL (a relative quantity)
2197       bs = new BS();
2198       SymmetryInterface unitcell = vwr.getCurrentUnitCell();
2199       if (unitcell == null)
2200         return bs;
2201       ptTemp1.set(1, 1, 1);
2202       for (int i = ac; --i >= 0;)
2203         if (at[i] != null && isInLatticeCell(i, ptTemp1, ptTemp2, false))
2204           bs.set(i);
2205       return bs;
2206     }
2207   }
2208 
getSelectCodeRange(int[] info)2209   private BS getSelectCodeRange(int[] info) {
2210     // could be a PDB format file that is all UNK
2211     BS bs = new BS();
2212     int seqcodeA = info[0];
2213     int seqcodeB = info[1];
2214     int chainID = info[2];
2215     boolean caseSensitive = vwr.getBoolean(T.chaincasesensitive);
2216     if (chainID >= 0 && chainID < 300 && !caseSensitive)
2217       chainID = chainToUpper(chainID);
2218     for (int iModel = mc; --iModel >= 0;)
2219       if (am[iModel].isBioModel) {
2220         BioModel m = (BioModel) am[iModel];
2221         int id;
2222         for (int i = m.chainCount; --i >= 0;) {
2223           Chain chain = m.chains[i];
2224           if (chainID == -1 || chainID == (id = chain.chainID)
2225               || !caseSensitive && id > 0 && id < 300
2226               && chainID == chainToUpper(id)) {
2227             Group[] groups = chain.groups;
2228             int n = chain.groupCount;
2229             for (int index = 0; index >= 0;) {
2230               index = selectSeqcodeRange(groups, n, index, seqcodeA, seqcodeB,
2231                   bs);
2232             }
2233           }
2234         }
2235       }
2236     return bs;
2237   }
2238 
selectSeqcodeRange(Group[] groups, int n, int index, int seqcodeA, int seqcodeB, BS bs)2239   private static int selectSeqcodeRange(Group[] groups, int n, int index,
2240                                         int seqcodeA, int seqcodeB, BS bs) {
2241     int seqcode, indexA, indexB, minDiff;
2242     boolean isInexact = false;
2243     for (indexA = index; indexA < n && groups[indexA].seqcode != seqcodeA; indexA++) {
2244     }
2245     if (indexA == n) {
2246       // didn't find A exactly -- go find the nearest that is GREATER than this value
2247       if (index > 0)
2248         return -1;
2249       isInexact = true;
2250       minDiff = Integer.MAX_VALUE;
2251       for (int i = n; --i >= 0;)
2252         if ((seqcode = groups[i].seqcode) > seqcodeA
2253             && (seqcode - seqcodeA) < minDiff) {
2254           indexA = i;
2255           minDiff = seqcode - seqcodeA;
2256         }
2257       if (minDiff == Integer.MAX_VALUE)
2258         return -1;
2259     }
2260     if (seqcodeB == Integer.MAX_VALUE) {
2261       indexB = n - 1;
2262       isInexact = true;
2263     } else {
2264       for (indexB = indexA; indexB < n && groups[indexB].seqcode != seqcodeB; indexB++) {
2265       }
2266       if (indexB == n) {
2267         // didn't find B exactly -- get the nearest that is LESS than this value
2268         if (index > 0)
2269           return -1;
2270         isInexact = true;
2271         minDiff = Integer.MAX_VALUE;
2272         for (int i = indexA; i < n; i++)
2273           if ((seqcode = groups[i].seqcode) < seqcodeB
2274               && (seqcodeB - seqcode) < minDiff) {
2275             indexB = i;
2276             minDiff = seqcodeB - seqcode;
2277           }
2278         if (minDiff == Integer.MAX_VALUE)
2279           return -1;
2280       }
2281     }
2282     for (int i = indexA; i <= indexB; ++i)
2283       groups[i].setAtomBits(bs);
2284     return (isInexact ? -1 : indexB + 1);
2285   }
2286 
isInLatticeCell(int i, P3 cell, P3 ptTemp, boolean isAbsolute)2287   private boolean isInLatticeCell(int i, P3 cell, P3 ptTemp, boolean isAbsolute) {
2288     // this is the one method that allows for an absolute fractional cell business
2289     // but it is always called with isAbsolute FALSE.
2290     // so then it is determining values for select UNITCELL and the like.
2291 
2292     int iModel = at[i].mi;
2293     SymmetryInterface uc = getUnitCell(iModel);
2294     ptTemp.setT(at[i]);
2295     return (uc != null && uc.checkUnitCell(uc, cell, ptTemp, isAbsolute));
2296   }
2297 
2298   /**
2299    * Get atoms within a specific distance of any atom in a specific set of atoms
2300    * either within all models or within just the model(s) of those atoms
2301    *
2302    * @param distance
2303    * @param bs
2304    * @param withinAllModels
2305    * @param rd
2306    * @return the set of atoms
2307    */
getAtomsWithinRadius(float distance, BS bs, boolean withinAllModels, RadiusData rd)2308   public BS getAtomsWithinRadius(float distance, BS bs,
2309                                  boolean withinAllModels, RadiusData rd) {
2310     BS bsResult = new BS();
2311     BS bsCheck = getIterativeModels(false);
2312     bs = BSUtil.andNot(bs, vwr.slm.bsDeleted);
2313     AtomIndexIterator iter = getSelectedAtomIterator(null, false, false, false,
2314         false);
2315     if (withinAllModels) {
2316       boolean fixJavaFloat = !vwr.g.legacyJavaFloat;
2317       P3 ptTemp = new P3();
2318       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
2319         for (int iModel = mc; --iModel >= 0;) {
2320           if (!bsCheck.get(iModel))
2321             continue;
2322           if (distance < 0) {
2323             getAtomsWithin(distance,
2324                 at[i].getFractionalUnitCoordPt(fixJavaFloat, true, ptTemp),
2325                 bsResult, -1);
2326             continue;
2327           }
2328           setIteratorForAtom(iter, iModel, i, distance, rd);
2329           iter.addAtoms(bsResult);
2330         }
2331     } else {
2332       bsResult.or(bs);
2333       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
2334         if (distance < 0) {
2335           getAtomsWithin(distance, at[i], bsResult, at[i].mi);
2336           continue;
2337         }
2338         setIteratorForAtom(iter, -1, i, distance, rd);
2339         iter.addAtoms(bsResult);
2340       }
2341     }
2342     iter.release();
2343     return bsResult;
2344   }
2345 
getAtomsWithin(float distance, T3 coord, BS bsResult, int modelIndex)2346   public BS getAtomsWithin(float distance, T3 coord, BS bsResult, int modelIndex) {
2347 
2348     if (bsResult == null)
2349       bsResult = new BS();
2350 
2351     if (distance < 0) { // check all unitCell distances
2352       distance = -distance;
2353       for (int i = ac; --i >= 0;) {
2354         Atom atom = at[i];
2355         if (atom == null || modelIndex >= 0 && atom.mi != modelIndex)
2356           continue;
2357         if (!bsResult.get(i)
2358             && atom.getFractionalUnitDistance(coord, ptTemp1, ptTemp2) <= distance)
2359           bsResult.set(atom.i);
2360       }
2361       return bsResult;
2362     }
2363 
2364     BS bsCheck = getIterativeModels(true);
2365     AtomIndexIterator iter = getSelectedAtomIterator(null, false, false, false,
2366         false);
2367     for (int iModel = mc; --iModel >= 0;) {
2368       if (!bsCheck.get(iModel) || am[iModel].bsAtoms.isEmpty())
2369         continue;
2370       setIteratorForAtom(iter, -1, am[iModel].firstAtomIndex, -1, null);
2371       iter.setCenter(coord, distance);
2372       iter.addAtoms(bsResult);
2373     }
2374     iter.release();
2375     return bsResult;
2376   }
2377 
2378   // ////////// Bonding methods ////////////
2379 
deleteBonds(BS bsBonds, boolean isFullModel)2380   public void deleteBonds(BS bsBonds, boolean isFullModel) {
2381     if (!isFullModel) {
2382       BS bsA = new BS();
2383       BS bsB = new BS();
2384       for (int i = bsBonds.nextSetBit(0); i >= 0; i = bsBonds.nextSetBit(i + 1)) {
2385         Atom atom1 = bo[i].atom1;
2386         if (am[atom1.mi].isModelKit)
2387           continue;
2388         bsA.clearAll();
2389         bsB.clearAll();
2390         bsA.set(atom1.i);
2391         bsB.set(bo[i].getAtomIndex2());
2392         addStateScript("connect ", null, bsA, bsB, "delete", false, true);
2393       }
2394     }
2395     dBb(bsBonds, isFullModel);
2396   }
2397 
makeConnections2(float minD, float maxD, int order, int connectOperation, BS bsA, BS bsB, BS bsBonds, boolean isBonds, boolean addGroup, float energy)2398   public int[] makeConnections2(float minD, float maxD, int order,
2399                                 int connectOperation, BS bsA, BS bsB,
2400                                 BS bsBonds, boolean isBonds, boolean addGroup,
2401                                 float energy) {
2402     if (bsBonds == null)
2403       bsBonds = new BS();
2404 
2405     //System.out.println("MS makeConn2 " + bsA + " " + bsB  + " " + order + " " + minD + " " + maxD + " " + connectOperation);
2406 
2407 
2408     boolean matchAny = (order == Edge.BOND_ORDER_ANY);
2409     boolean matchNull = (order == Edge.BOND_ORDER_NULL);
2410     boolean isAtrop = (order == Edge.TYPE_ATROPISOMER);
2411     if (matchNull)
2412       order = Edge.BOND_COVALENT_SINGLE; //default for setting
2413     boolean matchHbond = Edge.isOrderH(order);
2414     boolean identifyOnly = false;
2415     boolean idOrModifyOnly = false;
2416     boolean createOnly = false;
2417     boolean autoAromatize = false;
2418     switch (connectOperation) {
2419     case T.delete:
2420       return deleteConnections(minD, maxD, order, bsA, bsB, isBonds, matchNull);
2421     case T.legacyautobonding:
2422     case T.auto:
2423       if (order != Edge.BOND_AROMATIC) {
2424         if (isBonds) {
2425           BS bs = bsA;
2426           bsA = new BS();
2427           bsB = new BS();
2428           for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
2429             bsA.set(bo[i].atom1.i);
2430             bsB.set(bo[i].atom2.i);
2431           }
2432         }
2433         return new int[] {
2434             matchHbond ? autoHbond(bsA, bsB, false) : autoBondBs4(bsA, bsB,
2435                 null, bsBonds, vwr.getMadBond(),
2436                 connectOperation == T.legacyautobonding), 0 };
2437       }
2438       idOrModifyOnly = autoAromatize = true;
2439       break;
2440     case T.identify:
2441       identifyOnly = idOrModifyOnly = true;
2442       break;
2443     case T.modify:
2444       idOrModifyOnly = true;
2445       break;
2446     case T.create:
2447       createOnly = true;
2448       break;
2449     }
2450     boolean anyOrNoId = (!identifyOnly || matchAny);
2451     boolean notAnyAndNoId = (!identifyOnly && !matchAny);
2452 
2453     defaultCovalentMad = vwr.getMadBond();
2454     boolean minDIsFrac = (minD < 0);
2455     boolean maxDIsFrac = (maxD < 0);
2456     boolean isFractional = (minDIsFrac || maxDIsFrac);
2457     boolean checkDistance = (!isBonds
2458         || minD != JC.DEFAULT_MIN_CONNECT_DISTANCE || maxD != JC.DEFAULT_MAX_CONNECT_DISTANCE);
2459     if (checkDistance) {
2460       minD = fixD(minD, minDIsFrac);
2461       maxD = fixD(maxD, maxDIsFrac);
2462     }
2463     short mad = getDefaultMadFromOrder(order);
2464     int nNew = 0;
2465     int nModified = 0;
2466     Bond bondAB = null;
2467     Atom atomA = null;
2468     Atom atomB = null;
2469     char altloc = '\0';
2470     short newOrder = (short) (order | Edge.BOND_NEW);
2471     boolean isAromatic = ((order & Edge.BOND_AROMATIC_MASK) != 0);
2472     try {
2473       for (int i = bsA.nextSetBit(0); i >= 0; i = bsA.nextSetBit(i + 1)) {
2474         if (isBonds) {
2475           bondAB = bo[i];
2476           atomA = bondAB.atom1;
2477           atomB = bondAB.atom2;
2478         } else {
2479           atomA = at[i];
2480           if (atomA.isDeleted())
2481             continue;
2482           altloc = (isModulated(i) ? '\0' : atomA.altloc);
2483         }
2484         for (int j = (isBonds ? 0 : bsB.nextSetBit(0)); j >= 0; j = bsB
2485             .nextSetBit(j + 1)) {
2486           if (isBonds) {
2487             j = 2147483646; // Integer.MAX_VALUE - 1; one pass only
2488           } else {
2489             if (j == i)
2490               continue;
2491             atomB = at[j];
2492             if (atomB == null || atomA.mi != atomB.mi || atomB.isDeleted())
2493               continue;
2494             if (altloc != '\0' && altloc != atomB.altloc
2495                 && atomB.altloc != '\0')
2496               continue;
2497             bondAB = atomA.getBond(atomB);
2498           }
2499           if ((bondAB == null ? idOrModifyOnly : createOnly)
2500               || checkDistance
2501               && !isInRange(atomA, atomB, minD, maxD, minDIsFrac, maxDIsFrac,
2502                   isFractional)
2503               || isAromatic && !allowAromaticBond(bondAB)
2504               )
2505             continue;
2506           if (bondAB == null) {
2507             bsBonds.set(bondAtoms(atomA, atomB, order, mad, bsBonds, energy,
2508                 addGroup, true).index);
2509             nNew++;
2510           } else {
2511             if (notAnyAndNoId) {
2512               bondAB.setOrder(order);
2513               if (isAtrop) {
2514                 haveAtropicBonds = true;
2515                 bondAB.setAtropisomerOptions(bsA, bsB);
2516               }
2517               bsAromatic.clear(bondAB.index);
2518             }
2519             if (anyOrNoId || order == bondAB.order || newOrder == bondAB.order
2520                 || matchHbond && bondAB.isHydrogen()) {
2521               bsBonds.set(bondAB.index);
2522               nModified++;
2523             }
2524           }
2525         }
2526       }
2527     } catch (Exception e) {
2528       // well, we tried -- probably ran over
2529     }
2530     if (autoAromatize)
2531       assignAromaticBondsBs(true, bsBonds);
2532     if (!identifyOnly)
2533       sm.setShapeSizeBs(JC.SHAPE_STICKS, Integer.MIN_VALUE, null, bsBonds);
2534     return new int[] { nNew, nModified };
2535   }
2536 
autoBondBs4(BS bsA, BS bsB, BS bsExclude, BS bsBonds, short mad, boolean preJmol11_9_24)2537   public int autoBondBs4(BS bsA, BS bsB, BS bsExclude, BS bsBonds, short mad,
2538                          boolean preJmol11_9_24) {
2539     // unfortunately, 11.9.24 changed the order with which atoms were processed
2540     // for autobonding. This means that state script prior to that that use
2541     // select BOND will be misread by later version.
2542     // In addition, prior to Jmol 14.2.6, the difference between double (JavaScript) and
2543     // float (Java) was not accounted for. However, this would only affect
2544     // very borderline cases -- where we are just at the edge of the bond tolerance.
2545     // Is it worth it to fix? This is the question.
2546     if (preJmol11_9_24)
2547       return autoBond_Pre_11_9_24(bsA, bsB, bsExclude, bsBonds, mad);
2548     if (ac == 0)
2549       return 0;
2550     if (mad == 0)
2551       mad = 1;
2552     if (maxBondingRadius == PT.FLOAT_MIN_SAFE)
2553       findMaxRadii();
2554     float bondTolerance = vwr.getFloat(T.bondtolerance);
2555     float minBondDistance = vwr.getFloat(T.minbonddistance);
2556     float minBondDistance2 = minBondDistance * minBondDistance;
2557     int nNew = 0;
2558     if (showRebondTimes)// && Logger.debugging)
2559       Logger.startTimer("autobond");
2560     int lastModelIndex = -1;
2561     boolean isAll = (bsA == null);
2562     BS bsCheck;
2563     int i0;
2564     if (isAll) {
2565       i0 = 0;
2566       bsCheck = null;
2567     } else {
2568       if (bsA.equals(bsB)) {
2569         bsCheck = bsA;
2570       } else {
2571         bsCheck = BSUtil.copy(bsA);
2572         bsCheck.or(bsB);
2573       }
2574       i0 = bsCheck.nextSetBit(0);
2575     }
2576     AtomIndexIterator iter = getSelectedAtomIterator(null, false, false, true,
2577         false);
2578     boolean useOccupation = false;
2579     for (int i = i0; i >= 0 && i < ac; i = (isAll ? i + 1 : bsCheck
2580         .nextSetBit(i + 1))) {
2581       boolean isAtomInSetA = (isAll || bsA.get(i));
2582       boolean isAtomInSetB = (isAll || bsB.get(i));
2583       Atom atom = at[i];
2584       if (atom == null || atom.isDeleted())
2585         continue;
2586       int modelIndex = atom.mi;
2587       // no connections allowed in a data frame
2588       if (modelIndex != lastModelIndex) {
2589         lastModelIndex = modelIndex;
2590         if (isJmolDataFrameForModel(modelIndex)) {
2591           // ok here -- all data frames are orderly
2592           i = am[modelIndex].firstAtomIndex + am[modelIndex].act - 1;
2593           continue;
2594         }
2595         useOccupation = getInfoB(modelIndex, "autoBondUsingOccupation"); // JANA reader
2596       }
2597       // Covalent bonds
2598       float myBondingRadius = atom.getBondingRadius();
2599       if (myBondingRadius == 0)
2600         continue;
2601       float myFormalCharge = atom.getFormalCharge();
2602       boolean useCharge = (myFormalCharge != 0);
2603       if (useCharge)
2604         myFormalCharge = Math.signum(myFormalCharge);
2605       boolean isFirstExcluded = (bsExclude != null && bsExclude.get(i));
2606       float searchRadius = myBondingRadius + maxBondingRadius + bondTolerance;
2607       setIteratorForAtom(iter, -1, i, searchRadius, null);
2608       while (iter.hasNext()) {
2609         Atom atomNear = at[iter.next()];
2610         if (atomNear.isDeleted())
2611           continue;
2612         int j = atomNear.i;
2613         boolean isNearInSetA = (isAll || bsA.get(j));
2614         boolean isNearInSetB = (isAll || bsB.get(j));
2615         // BOTH must be excluded in order to ignore bonding
2616         if (!isNearInSetA && !isNearInSetB
2617             || !(isAtomInSetA && isNearInSetB || isAtomInSetB && isNearInSetA)
2618             || isFirstExcluded && bsExclude.get(j) || useOccupation
2619             && occupancies != null
2620             && (occupancies[i] < 50) != (occupancies[j] < 50)
2621             || useCharge && (Math.signum(atomNear.getFormalCharge()) == myFormalCharge)
2622             )
2623           continue;
2624         int order = (isBondable(myBondingRadius, atomNear.getBondingRadius(),
2625             iter.foundDistance2(), minBondDistance2, bondTolerance) ? 1 : 0);
2626         if (order > 0 && autoBondCheck(atom, atomNear, order, mad, bsBonds))
2627           nNew++;
2628       }
2629       iter.release();
2630     }
2631     if (showRebondTimes)
2632       Logger.checkTimer("autoBond", false);
2633     return nNew;
2634   }
2635 
isBondable(float bondingRadiusA, float bondingRadiusB, float distance2, float minBondDistance2, float bondTolerance)2636   public boolean isBondable(float bondingRadiusA, float bondingRadiusB,
2637                             float distance2, float minBondDistance2,
2638                             float bondTolerance) {
2639     if (bondingRadiusA == 0 || bondingRadiusB == 0
2640         || distance2 < minBondDistance2)
2641       return false;
2642     float maxAcceptable = bondingRadiusA + bondingRadiusB + bondTolerance;
2643     float maxAcceptable2 = maxAcceptable * maxAcceptable;
2644     return (distance2 <= maxAcceptable2);
2645   }
2646 
2647   private boolean maxBondWarned;
2648 
autoBondCheck(Atom atomA, Atom atomB, int order, short mad, BS bsBonds)2649   private boolean autoBondCheck(Atom atomA, Atom atomB, int order, short mad,
2650                                 BS bsBonds) {
2651     if (atomA.getCurrentBondCount() > JC.MAXIMUM_AUTO_BOND_COUNT
2652         || atomB.getCurrentBondCount() > JC.MAXIMUM_AUTO_BOND_COUNT) {
2653       if (!maxBondWarned)
2654         Logger.warn("maximum auto bond count reached");
2655       maxBondWarned = true;
2656       return false;
2657     }
2658     int formalChargeA = atomA.getFormalCharge();
2659     if (formalChargeA != 0) {
2660       int formalChargeB = atomB.getFormalCharge();
2661       if ((formalChargeA < 0 && formalChargeB < 0)
2662           || (formalChargeA > 0 && formalChargeB > 0))
2663         return false;
2664     }
2665     // don't connect differing altloc unless there are modulations
2666     if (atomA.altloc != atomB.altloc && atomA.altloc != '\0'
2667         && atomB.altloc != '\0' && getModulation(atomA.i) == null)
2668       return false;
2669     getOrAddBond(atomA, atomB, order, mad, bsBonds, 0, false);
2670     return true;
2671   }
2672 
autoBond_Pre_11_9_24(BS bsA, BS bsB, BS bsExclude, BS bsBonds, short mad)2673   private int autoBond_Pre_11_9_24(BS bsA, BS bsB, BS bsExclude, BS bsBonds,
2674                                    short mad) {
2675     if (ac == 0)
2676       return 0;
2677     if (mad == 0)
2678       mad = 1;
2679     // null values for bitsets means "all"
2680     if (maxBondingRadius == PT.FLOAT_MIN_SAFE)
2681       findMaxRadii();
2682     float bondTolerance = vwr.getFloat(T.bondtolerance);
2683     float minBondDistance = vwr.getFloat(T.minbonddistance);
2684     float minBondDistance2 = minBondDistance * minBondDistance;
2685     int nNew = 0;
2686     initializeBspf();
2687     /*
2688      * miguel 2006 04 02
2689      * note that the way that these loops + iterators are constructed,
2690      * everything assumes that all possible pairs of atoms are going to
2691      * be looked at.
2692      * for example, the hemisphere iterator will only look at atom indexes
2693      * that are >= (or <= ?) the specified atom.
2694      * if we are going to allow arbitrary sets bsA and bsB, then this will
2695      * not work.
2696      * so, for now I will do it the ugly way.
2697      * maybe enhance/improve in the future.
2698      */
2699     int lastModelIndex = -1;
2700     for (int i = ac; --i >= 0;) {
2701       Atom atom = at[i];
2702       if (atom == null)
2703         continue;
2704       boolean isAtomInSetA = (bsA == null || bsA.get(i));
2705       boolean isAtomInSetB = (bsB == null || bsB.get(i));
2706       if (!isAtomInSetA && !isAtomInSetB)
2707         //|| bsExclude != null && bsExclude.get(i))
2708         continue;
2709       if (atom.isDeleted())
2710         continue;
2711       int modelIndex = atom.mi;
2712       //no connections allowed in a data frame
2713       if (modelIndex != lastModelIndex) {
2714         lastModelIndex = modelIndex;
2715         if (isJmolDataFrameForModel(modelIndex)) {
2716           for (; --i >= 0;)
2717             if (at[i] == null || at[i].mi != modelIndex)
2718               break;
2719           i++;
2720           continue;
2721         }
2722       }
2723       // Covalent bonds
2724       float myBondingRadius = atom.getBondingRadius();
2725       if (myBondingRadius == 0)
2726         continue;
2727       float searchRadius = myBondingRadius + maxBondingRadius + bondTolerance;
2728       initializeBspt(modelIndex);
2729       CubeIterator iter = bspf.getCubeIterator(modelIndex);
2730       iter.initialize(atom, searchRadius, true);
2731       while (iter.hasMoreElements()) {
2732         Atom atomNear = (Atom) iter.nextElement();
2733         if (atomNear == atom || atomNear.isDeleted())
2734           continue;
2735         int atomIndexNear = atomNear.i;
2736         boolean isNearInSetA = (bsA == null || bsA.get(atomIndexNear));
2737         boolean isNearInSetB = (bsB == null || bsB.get(atomIndexNear));
2738         if (!isNearInSetA && !isNearInSetB || bsExclude != null
2739             && bsExclude.get(atomIndexNear) && bsExclude.get(i) //this line forces BOTH to be excluded in order to ignore bonding
2740         )
2741           continue;
2742         if (!(isAtomInSetA && isNearInSetB || isAtomInSetB && isNearInSetA))
2743           continue;
2744         int order = (isBondable(myBondingRadius, atomNear.getBondingRadius(),
2745             iter.foundDistance2(), minBondDistance2, bondTolerance) ? 1 : 0);
2746         if (order > 0) {
2747           if (autoBondCheck(atom, atomNear, order, mad, bsBonds))
2748             nNew++;
2749         }
2750       }
2751       iter.release();
2752     }
2753     return nNew;
2754   }
2755 
2756   /**
2757    * a generalized formation of HBONDS, carried out in relation to calculate
2758    * HBONDS {atomsFrom} {atomsTo}. The calculation can create pseudo-H bonds for
2759    * files that do not contain H atoms.
2760    *
2761    * @param bsA
2762    *        "from" set (must contain H if that is desired)
2763    * @param bsB
2764    *        "to" set
2765    * @param onlyIfHaveCalculated
2766    * @return negative number of pseudo-hbonds or number of actual hbonds formed
2767    */
autoHbond(BS bsA, BS bsB, boolean onlyIfHaveCalculated)2768   public int autoHbond(BS bsA, BS bsB, boolean onlyIfHaveCalculated) {
2769     if (onlyIfHaveCalculated) {
2770       BS bsModels = getModelBS(bsA, false);
2771       for (int i = bsModels.nextSetBit(0); i >= 0
2772           && onlyIfHaveCalculated; i = bsModels.nextSetBit(i + 1))
2773         onlyIfHaveCalculated = !am[i].hasRasmolHBonds;
2774       if (onlyIfHaveCalculated)
2775         return 0;
2776     }
2777     boolean haveHAtoms = false;
2778     for (int i = bsA.nextSetBit(0); i >= 0; i = bsA.nextSetBit(i + 1))
2779       if (at[i].getElementNumber() == 1) {
2780         haveHAtoms = true;
2781         break;
2782       }
2783     BS bsHBonds = new BS();
2784     boolean useRasMol = vwr.getBoolean(T.hbondsrasmol);
2785     if (bsB == null || useRasMol && !haveHAtoms) {
2786       Logger.info((bsB == null ? "DSSP/DSSR " : "RasMol")
2787           + " pseudo-hbond calculation");
2788       calcRasmolHydrogenBonds(bsA, bsB, null, false, Integer.MAX_VALUE, false,
2789           bsHBonds);
2790       return -bsHBonds.cardinality();
2791     }
2792     Logger.info(haveHAtoms ? "Standard Hbond calculation"
2793         : "Jmol pseudo-hbond calculation");
2794     BS bsCO = null;
2795     if (!haveHAtoms) {
2796       bsCO = new BS();
2797       for (int i = bsA.nextSetBit(0); i >= 0; i = bsA.nextSetBit(i + 1)) {
2798         int atomID = at[i].atomID;
2799         switch (atomID) {
2800         case JC.ATOMID_TERMINATING_OXT:
2801         case JC.ATOMID_CARBONYL_OXYGEN:
2802         case JC.ATOMID_CARBONYL_OD1:
2803         case JC.ATOMID_CARBONYL_OD2:
2804         case JC.ATOMID_CARBONYL_OE1:
2805         case JC.ATOMID_CARBONYL_OE2:
2806           bsCO.set(i);
2807           break;
2808         }
2809       }
2810     }
2811     float dmax;
2812     float min2;
2813     if (haveHAtoms) {
2814       // no set maximumn for this anymore -- default is 2.5A
2815       dmax = vwr.getFloat(T.hbondhxdistancemaximum);
2816       min2 = 1f;
2817     } else {
2818       dmax = vwr.getFloat(T.hbondnodistancemaximum);
2819       // default 3.25 for pseudo; user can make longer or shorter
2820       min2 = hbondMinRasmol * hbondMinRasmol;
2821     }
2822     float max2 = dmax * dmax;
2823     float minAttachedAngle = (float) (vwr.getFloat(T.hbondsangleminimum)
2824         * Math.PI / 180);
2825     int nNew = 0;
2826     float d2 = 0;
2827     V3 v1 = new V3();
2828     V3 v2 = new V3();
2829     if (showRebondTimes && Logger.debugging)
2830       Logger.startTimer("hbond");
2831     P3 C = null;
2832     P3 D = null;
2833     AtomIndexIterator iter = getSelectedAtomIterator(bsB, false, false, false,
2834         false);
2835     for (int i = bsA.nextSetBit(0); i >= 0; i = bsA.nextSetBit(i + 1)) {
2836       Atom atom = at[i];
2837       int elementNumber = atom.getElementNumber();
2838       boolean isH = (elementNumber == 1);
2839       // If this is an H atom, then skip if we don't have H atoms in set A
2840       // If this is NOT an H atom, then skip if we have hydrogen atoms or this is not N or O
2841       if (isH ? !haveHAtoms
2842           : haveHAtoms || elementNumber != 7 && elementNumber != 8)
2843         continue;
2844 
2845       boolean firstIsCO;
2846       if (isH) {
2847         firstIsCO = false;
2848         Bond[] b = atom.bonds;
2849         if (b == null)
2850           continue;
2851         // must have OH or NH
2852         boolean isOK = false;
2853         for (int j = 0; !isOK && j < b.length; j++) {
2854           Atom a2 = b[j].getOtherAtom(atom);
2855           int element = a2.getElementNumber();
2856           isOK = (element == 7 || element == 8);
2857         }
2858         if (!isOK)
2859           continue;
2860       } else {
2861         // check if the first atom is C=O
2862         firstIsCO = bsCO.get(i);
2863       }
2864       setIteratorForAtom(iter, -1, atom.i, dmax, null);
2865       while (iter.hasNext()) {
2866         Atom atomNear = at[iter.next()];
2867         int elementNumberNear = atomNear.getElementNumber();
2868         if (atomNear == atom
2869             || (isH ? elementNumberNear == 1 : elementNumberNear != 7 && elementNumberNear != 8)
2870             || (d2 = iter.foundDistance2()) < min2 || d2 > max2
2871             || firstIsCO && bsCO.get(atomNear.i)
2872             || atom.isBonded(atomNear)) {
2873           continue;
2874         }
2875         if (minAttachedAngle > 0) {
2876           v1.sub2(atom, atomNear);
2877           if ((D = checkMinAttachedAngle(atom, minAttachedAngle, v1, v2,
2878               haveHAtoms)) == null)
2879             continue;
2880           v1.scale(-1);
2881           if ((C = checkMinAttachedAngle(atomNear, minAttachedAngle, v1, v2,
2882               haveHAtoms)) == null)
2883             continue;
2884         }
2885         float energy = 0;
2886         short bo;
2887         if (isH && !Float.isNaN(C.x) && !Float.isNaN(D.x)) {
2888           bo = Edge.BOND_H_CALC;
2889           // (+) H .......... A (-)
2890           //     |            |
2891           //     |            |
2892           // (-) D            C (+)
2893           //
2894           //    AH args[0], CH args[1], CD args[2], DA args[3]
2895           //
2896           energy = HBond.getEnergy((float) Math.sqrt(d2), C.distance(atom),
2897               C.distance(D), atomNear.distance(D)) / 1000f;
2898         } else {
2899           bo = Edge.BOND_H_REGULAR;
2900         }
2901         bsHBonds.set(addHBond(atom, atomNear, bo, energy));
2902         nNew++;
2903       }
2904     }
2905     iter.release();
2906     sm.setShapeSizeBs(JC.SHAPE_STICKS, Integer.MIN_VALUE, null, bsHBonds);
2907     if (showRebondTimes)
2908       Logger.checkTimer("hbond", false);
2909     return (haveHAtoms ? nNew : -nNew);
2910   }
2911 
checkMinAttachedAngle(Atom atom1, float minAngle, V3 v1, V3 v2, boolean haveHAtoms)2912   private static P3 checkMinAttachedAngle(Atom atom1, float minAngle, V3 v1,
2913                                           V3 v2, boolean haveHAtoms) {
2914     Bond[] bonds = atom1.bonds;
2915     boolean ignore = true;
2916     Atom X = null;
2917     if (bonds != null && bonds.length > 0) {
2918       float dMin = Float.MAX_VALUE;
2919       for (int i = bonds.length; --i >= 0;)
2920         if (bonds[i].isCovalent()) {
2921           ignore = false;
2922           Atom atomA = bonds[i].getOtherAtom(atom1);
2923           if (!haveHAtoms && atomA.getElementNumber() == 1)
2924             continue;
2925           v2.sub2(atom1, atomA);
2926           float d = v2.angle(v1);
2927           if (d < minAngle)
2928             return null;
2929           if (d < dMin) {
2930             X = atomA;
2931             dMin = d;
2932           }
2933         }
2934     }
2935     return (ignore ? P3.new3(Float.NaN, 0, 0) : X);
2936   }
2937 
2938   //////////// state definition ///////////
2939 
setStructureIndexes()2940   public void setStructureIndexes() {
2941     int id;
2942     int idnew = 0;
2943     int lastid = -1;
2944     int imodel = -1;
2945     int lastmodel = -1;
2946     for (int i = 0; i < ac; i++) {
2947       if (at[i] == null)
2948         continue;
2949       if ((imodel = at[i].mi) != lastmodel) {
2950         idnew = 0;
2951         lastmodel = imodel;
2952         lastid = -1;
2953       }
2954       if ((id = at[i].group.getStrucNo()) != lastid && id != 0) {
2955         at[i].group.setStrucNo(++idnew);
2956         lastid = idnew;
2957       }
2958     }
2959   }
2960 
getModelInfoAsString()2961   public String getModelInfoAsString() {
2962     SB sb = new SB().append("<models count=\"");
2963     sb.appendI(mc).append("\" modelSetHasVibrationVectors=\"")
2964         .append(modelSetHasVibrationVectors() + "\">\n<properties>");
2965     if (modelSetProperties != null) {
2966       Enumeration<?> e = modelSetProperties.propertyNames();
2967       while (e.hasMoreElements()) {
2968         String propertyName = (String) e.nextElement();
2969         sb.append("\n <property name=\"").append(propertyName)
2970             .append("\" value=")
2971             .append(PT.esc(modelSetProperties.getProperty(propertyName)))
2972             .append(" />");
2973       }
2974       sb.append("\n</properties>");
2975     }
2976     for (int i = 0; i < mc; ++i) {
2977       sb.append("\n<model index=\"").appendI(i).append("\" n=\"")
2978           .append(getModelNumberDotted(i)).append("\" id=")
2979           .append(PT.esc("" + getInfo(i, "modelID")));
2980       int ib = vwr.getJDXBaseModelIndex(i);
2981       if (ib != i)
2982         sb.append(" baseModelId=").append(
2983             PT.esc((String) getInfo(ib, "jdxModelID")));
2984       sb.append(" name=").append(PT.esc(getModelName(i))).append(" title=")
2985           .append(PT.esc(getModelTitle(i))).append(" hasVibrationVectors=\"")
2986           .appendB(vwr.modelHasVibrationVectors(i)).append("\" />");
2987     }
2988     sb.append("\n</models>");
2989     return sb.toString();
2990   }
2991 
getSymmetryInfoAsString()2992   public String getSymmetryInfoAsString() {
2993     SB sb = new SB().append("Symmetry Information:");
2994     for (int i = 0; i < mc; ++i) {
2995       sb.append("\nmodel #").append(getModelNumberDotted(i)).append("; name=")
2996           .append(getModelName(i)).append("\n");
2997       SymmetryInterface unitCell = getUnitCell(i);
2998       sb.append(unitCell == null ? "no symmetry information" : unitCell
2999           .getSymmetryInfoStr());
3000     }
3001     return sb.toString();
3002   }
3003 
createModels(int n)3004   public void createModels(int n) {
3005     int newModelCount = mc + n;
3006     Model[] newModels = (Model[]) AU.arrayCopyObject(am, newModelCount);
3007     validateBspf(false);
3008     modelNumbers = AU.arrayCopyI(modelNumbers, newModelCount);
3009     modelFileNumbers = AU.arrayCopyI(modelFileNumbers, newModelCount);
3010     modelNumbersForAtomLabel = AU.arrayCopyS(modelNumbersForAtomLabel,
3011         newModelCount);
3012     modelNames = AU.arrayCopyS(modelNames, newModelCount);
3013     frameTitles = AU.arrayCopyS(frameTitles, newModelCount);
3014     int f = modelFileNumbers[mc - 1] / 1000000 + 1;
3015     for (int i = mc, pt = 0; i < newModelCount; i++) {
3016       modelNumbers[i] = i + mc;
3017       modelFileNumbers[i] = f * 1000000 + (++pt);
3018       modelNumbersForAtomLabel[i] = modelNames[i] = f + "." + pt;
3019     }
3020     thisStateModel = -1;
3021     String[] group3Lists = (String[]) getInfoM("group3Lists");
3022     if (group3Lists != null) {
3023       int[][] group3Counts = (int[][]) getInfoM("group3Counts");
3024       group3Lists = AU.arrayCopyS(group3Lists, newModelCount);
3025       group3Counts = AU.arrayCopyII(group3Counts, newModelCount);
3026       msInfo.put("group3Lists", group3Lists);
3027       msInfo.put("group3Counts", group3Counts);
3028     }
3029     unitCells = (SymmetryInterface[]) AU.arrayCopyObject(unitCells,
3030         newModelCount);
3031     for (int i = mc; i < newModelCount; i++) {
3032       newModels[i] = new Model().set(this, i, -1, null, null, null);
3033       newModels[i].loadState = " model create #" + i + ";";
3034     }
3035     am = newModels;
3036     mc = newModelCount;
3037   }
3038 
deleteAtoms(BS bs)3039   public void deleteAtoms(BS bs) {
3040     //averageAtomPoint = null;
3041     if (bs == null)
3042       return;
3043     BS bsBonds = new BS();
3044     boolean doNull = Viewer.nullDeletedAtoms;
3045     for (int i = bs.nextSetBit(0); i >= 0 && i < ac; i = bs.nextSetBit(i + 1)) {
3046       at[i].delete(bsBonds);
3047       if (doNull)
3048         at[i] = null;
3049     }
3050     for (int i = 0; i < mc; i++) {
3051       Model m = am[i];
3052       m.resetDSSR(false);
3053       m.bsAtomsDeleted.or(bs);
3054       m.bsAtomsDeleted.and(m.bsAtoms);
3055       bs = BSUtil.andNot(m.bsAtoms, m.bsAtomsDeleted);
3056       m.firstAtomIndex = bs.nextSetBit(0);
3057       m.act = bs.cardinality();
3058       m.isOrderly = (m.act == m.bsAtoms.length());
3059     }
3060     deleteBonds(bsBonds, false);
3061     validateBspf(false);
3062   }
3063 
clearDB(int atomIndex)3064   public void clearDB(int atomIndex) {
3065     getModelAuxiliaryInfo(at[atomIndex].mi).remove("dbName");
3066   }
3067 
3068 
3069   // atom addition //
3070 
adjustAtomArrays(int[] map, int i0, int ac)3071   public void adjustAtomArrays(int[] map, int i0, int ac) {
3072     // from ModelLoader, after hydrogen atom addition
3073     this.ac = ac;
3074     for (int i = i0; i < ac; i++) {
3075       at[i] = at[map[i]];
3076       at[i].i = i;
3077       Model m = am[at[i].mi];
3078       if (m.firstAtomIndex == map[i])
3079         m.firstAtomIndex = i;
3080       m.bsAtoms.set(i);
3081     }
3082     if (vibrations != null)
3083       for (int i = i0; i < ac; i++)
3084         vibrations[i] = vibrations[map[i]];
3085     if (atomTensorList != null) {
3086       for (int i = i0; i < ac; i++) {
3087         Object[] list = atomTensorList[i] = atomTensorList[map[i]];
3088         if (list != null)
3089           for (int j = list.length; --j >= 0;) {
3090             Tensor t = (Tensor) list[j];
3091             if (t != null) {
3092               t.atomIndex1 = i;
3093             }
3094           }
3095       }
3096     }
3097     if (atomNames != null)
3098       for (int i = i0; i < ac; i++)
3099         atomNames[i] = atomNames[map[i]];
3100     if (atomTypes != null)
3101       for (int i = i0; i < ac; i++)
3102         atomTypes[i] = atomTypes[map[i]];
3103 
3104     if (atomResnos != null)
3105       for (int i = i0; i < ac; i++)
3106         atomResnos[i] = atomResnos[map[i]];
3107     if (atomSerials != null)
3108       for (int i = i0; i < ac; i++)
3109         atomSerials[i] = atomSerials[map[i]];
3110     if (atomSeqIDs != null)
3111       for (int i = i0; i < ac; i++)
3112         atomSeqIDs[i] = atomSeqIDs[map[i]];
3113 
3114     if (bfactor100s != null)
3115       for (int i = i0; i < ac; i++)
3116         bfactor100s[i] = bfactor100s[map[i]];
3117 
3118     if (occupancies != null)
3119       for (int i = i0; i < ac; i++)
3120         occupancies[i] = occupancies[map[i]];
3121     if (partialCharges != null)
3122       for (int i = i0; i < ac; i++)
3123         partialCharges[i] = partialCharges[map[i]];
3124   }
3125 
growAtomArrays(int newLength)3126   protected void growAtomArrays(int newLength) {
3127     at = (Atom[]) AU.arrayCopyObject(at, newLength);
3128     if (vibrations != null)
3129       vibrations = (Vibration[]) AU.arrayCopyObject(vibrations, newLength);
3130     if (occupancies != null)
3131       occupancies = AU.arrayCopyF(occupancies, newLength);
3132     if (bfactor100s != null)
3133       bfactor100s = AU.arrayCopyShort(bfactor100s, newLength);
3134     if (partialCharges != null)
3135       partialCharges = AU.arrayCopyF(partialCharges, newLength);
3136     if (atomTensorList != null)
3137       atomTensorList = (Object[][]) AU.arrayCopyObject(atomTensorList,
3138           newLength);
3139     if (atomNames != null)
3140       atomNames = AU.arrayCopyS(atomNames, newLength);
3141     if (atomTypes != null)
3142       atomTypes = AU.arrayCopyS(atomTypes, newLength);
3143     if (atomResnos != null)
3144       atomResnos = AU.arrayCopyI(atomResnos, newLength);
3145     if (atomSerials != null)
3146       atomSerials = AU.arrayCopyI(atomSerials, newLength);
3147     if (atomSeqIDs != null)
3148       atomSeqIDs = AU.arrayCopyI(atomSeqIDs, newLength);
3149   }
3150 
addAtom(int modelIndex, Group group, int atomicAndIsotopeNumber, String atomName, String atomType, int atomSerial, int atomSeqID, int atomSite, P3 xyz, float radius, V3 vib, int formalCharge, float partialCharge, float occupancy, float bfactor, Lst<Object> tensors, boolean isHetero, byte specialAtomID, BS atomSymmetry, float bondRadius)3151   public Atom addAtom(int modelIndex, Group group, int atomicAndIsotopeNumber,
3152                       String atomName, String atomType, int atomSerial,
3153                       int atomSeqID, int atomSite, P3 xyz, float radius,
3154                       V3 vib, int formalCharge, float partialCharge,
3155                       float occupancy, float bfactor, Lst<Object> tensors,
3156                       boolean isHetero, byte specialAtomID, BS atomSymmetry, float bondRadius) {
3157     Atom atom = new Atom().setAtom(modelIndex, ac, xyz, radius, atomSymmetry,
3158         atomSite, (short) atomicAndIsotopeNumber, formalCharge, isHetero);
3159     am[modelIndex].act++;
3160     am[modelIndex].bsAtoms.set(ac);
3161     if (Elements.isElement(atomicAndIsotopeNumber, 1))
3162       am[modelIndex].hydrogenCount++;
3163     if (ac >= at.length)
3164       growAtomArrays(ac + 100); // only due to added hydrogens
3165 
3166     at[ac] = atom;
3167     setBFactor(ac, bfactor, false);
3168     setOccupancy(ac, occupancy, false);
3169     setPartialCharge(ac, partialCharge, false);
3170     if (tensors != null)
3171       setAtomTensors(ac, tensors);
3172     atom.group = group;
3173     atom.colixAtom = vwr.cm.getColixAtomPalette(atom, PAL.CPK.id);
3174     if (atomName != null) {
3175       if (atomType != null) {
3176         if (atomTypes == null)
3177           atomTypes = new String[at.length];
3178         atomTypes[ac] = atomType;
3179       }
3180       atom.atomID = specialAtomID;
3181       if (specialAtomID == 0) {
3182         if (atomNames == null)
3183           atomNames = new String[at.length];
3184         atomNames[ac] = atomName.intern();
3185       }
3186     }
3187     if (atomSerial != Integer.MIN_VALUE) {
3188       if (atomSerials == null)
3189         atomSerials = new int[at.length];
3190       atomSerials[ac] = atomSerial;
3191     }
3192     if (atomSeqID != 0) {
3193       if (atomSeqIDs == null)
3194         atomSeqIDs = new int[at.length];
3195       atomSeqIDs[ac] = atomSeqID;
3196     }
3197     if (vib != null)
3198       setVibrationVector(ac, vib);
3199     if (!Float.isNaN(bondRadius))
3200       setBondingRadius(ac, bondRadius);
3201     ac++;
3202     return atom;
3203   }
3204 
getInlineData(int modelIndex)3205   public String getInlineData(int modelIndex) {
3206     SB data = null;
3207     if (modelIndex >= 0)
3208       data = am[modelIndex].loadScript;
3209     else
3210       for (modelIndex = mc; --modelIndex >= 0;)
3211         if ((data = am[modelIndex].loadScript).length() > 0)
3212           break;
3213     int pt = data.lastIndexOf("data \"");
3214     if (pt < 0) { // load inline ...
3215       String s = PT.getQuotedStringAt(data.toString(), 0);
3216       return ScriptCompiler.unescapeString(s, 0, s.length());
3217     }
3218     pt = data.indexOf2("\"", pt + 7);
3219     int pt2 = data.lastIndexOf("end \"");
3220     if (pt2 < pt || pt < 0)
3221       return null;
3222     return data.substring2(pt + 2, pt2);
3223   }
3224 
isAtomPDB(int i)3225   public boolean isAtomPDB(int i) {
3226     return i >= 0 && am[at[i].mi].isBioModel;
3227   }
3228 
3229   /**
3230    * Ensure the atom index is >= 0 and that the
3231    * atom's model is the last model.
3232    *
3233    * @param i
3234    * @return true if that is the case
3235    */
isAtomInLastModel(int i)3236   public boolean isAtomInLastModel(int i) {
3237     return i >= 0 && at[i].mi == mc - 1;
3238   }
3239 
haveModelKit()3240   public boolean haveModelKit() {
3241     for (int i = 0; i < mc; i++)
3242       if (am[i].isModelKit)
3243         return true;
3244     return false;
3245   }
3246 
getModelKitStateBitset(BS bs, BS bsDeleted)3247   public BS getModelKitStateBitset(BS bs, BS bsDeleted) {
3248     // task here is to remove bits from bs that are deleted atoms in
3249     // models that are model kits.
3250 
3251     BS bs1 = BSUtil.copy(bsDeleted);
3252     for (int i = 0; i < mc; i++)
3253       if (!am[i].isModelKit)
3254         bs1.andNot(am[i].bsAtoms);
3255     return BSUtil.deleteBits(bs, bs1);
3256   }
3257 
3258   /**
3259    *
3260    * @param iFirst
3261    *        0 from ModelLoader.freeze; -1 from Viewer.assignAtom
3262    * @param baseAtomIndex
3263    * @param mergeSet
3264    */
setAtomNamesAndNumbers(int iFirst, int baseAtomIndex, AtomCollection mergeSet)3265   public void setAtomNamesAndNumbers(int iFirst, int baseAtomIndex,
3266                                      AtomCollection mergeSet) {
3267     // first, validate that all atomSerials are NaN
3268     if (baseAtomIndex < 0)
3269       iFirst = am[at[iFirst].mi].firstAtomIndex;
3270     if (atomSerials == null)
3271       atomSerials = new int[ac];
3272     if (atomNames == null)
3273       atomNames = new String[ac];
3274     // now, we'll assign 1-based atom numbers within each model
3275     boolean isZeroBased = isXYZ && vwr.getBoolean(T.zerobasedxyzrasmol);
3276     int lastModelIndex = Integer.MAX_VALUE;
3277     int atomNo = 1;
3278     for (int i = iFirst; i < ac; ++i) {
3279       Atom atom = at[i];
3280       if (atom == null)
3281         continue;
3282       if (atom.mi != lastModelIndex) {
3283         lastModelIndex = atom.mi;
3284         atomNo = (isZeroBased ? 0 : 1);
3285       }
3286       // 1) do not change numbers assigned by adapter
3287       // 2) do not change the number already assigned when merging
3288       // 3) restart numbering with new atoms, not a continuation of old
3289       int ano = atomSerials[i];
3290 
3291       if (i >= -baseAtomIndex) {
3292         if (ano == 0 || baseAtomIndex < 0)
3293           atomSerials[i] = (i < baseAtomIndex ? mergeSet.atomSerials[i]
3294               : atomNo);
3295         if (atomNames[i] == null || baseAtomIndex < 0)
3296           atomNames[i] = (atom.getElementSymbol() + atomSerials[i]).intern();
3297       } else if (ano > atomNo) {
3298         atomNo = ano;
3299       }
3300       if (!am[lastModelIndex].isModelKit || atom.getElementNumber() > 0
3301           && !atom.isDeleted())
3302         atomNo++;
3303     }
3304   }
3305 
setUnitCellOffset(SymmetryInterface unitCell, T3 pt, int ijk)3306   public void setUnitCellOffset(SymmetryInterface unitCell, T3 pt, int ijk) {
3307     //    for (int i = modelIndex; i < modelCount; i++) {
3308     //      if (i < 0 || modelIndex >= 0 && i != modelIndex
3309     //          && models[i].trajectoryBaseIndex != modelIndex)
3310     //        continue;
3311     //      unitCell = getUnitCell(i);
3312     if (unitCell == null)
3313       return;
3314     if (pt == null)
3315       unitCell.setOffset(ijk);
3316     else
3317       unitCell.setOffsetPt(pt);
3318     //    }
3319   }
3320 
connect(float[][] connections)3321   public void connect(float[][] connections) {
3322     // array of [index1 index2 order diameter energy]
3323     resetMolecules();
3324     BS bsDelete = new BS();
3325     for (int i = 0; i < connections.length; i++) {
3326       float[] f = connections[i];
3327       if (f == null || f.length < 2)
3328         continue;
3329       int index1 = (int) f[0];
3330       boolean addGroup = (index1 < 0);
3331       if (addGroup)
3332         index1 = -1 - index1;
3333       int index2 = (int) f[1];
3334       if (index2 < 0 || index1 >= ac || index2 >= ac)
3335         continue;
3336       int order = (f.length > 2 ? (int) f[2] : Edge.BOND_COVALENT_SINGLE);
3337       if (order < 0)
3338         order &= 0xFFFF; // 12.0.1 was saving struts as negative numbers
3339       short mad = (f.length > 3 ? (short) (1000f * connections[i][3])
3340           : getDefaultMadFromOrder(order));
3341       if (order == 0 || mad == 0 && order != Edge.BOND_STRUT
3342           && !Edge.isOrderH(order)) {
3343         Bond b = at[index1].getBond(at[index2]);
3344         if (b != null)
3345           bsDelete.set(b.index);
3346         continue;
3347       }
3348       float energy = (f.length > 4 ? f[4] : 0);
3349       bondAtoms(at[index1], at[index2], order, mad, null, energy, addGroup,
3350           true);
3351     }
3352     if (bsDelete.nextSetBit(0) >= 0)
3353       deleteBonds(bsDelete, false);
3354   }
3355 
setFrameDelayMs(long millis, BS bsModels)3356   public void setFrameDelayMs(long millis, BS bsModels) {
3357     for (int i = bsModels.nextSetBit(0); i >= 0; i = bsModels.nextSetBit(i + 1))
3358       am[am[i].trajectoryBaseIndex].frameDelay = millis;
3359   }
3360 
getFrameDelayMs(int i)3361   public long getFrameDelayMs(int i) {
3362     return (i < am.length && i >= 0 ? am[am[i].trajectoryBaseIndex].frameDelay
3363         : 0);
3364   }
3365 
getModelIndexFromId(String id)3366   public int getModelIndexFromId(String id) {
3367     boolean haveFile = (id.indexOf("#") >= 0);
3368     boolean isBaseModel = id.toLowerCase().endsWith(".basemodel");
3369     if (isBaseModel)
3370       id = id.substring(0, id.length() - 10);
3371     int errCode = -1;
3372     String fname = null;
3373     for (int i = 0; i < mc; i++) {
3374       String mid = (String) getInfo(i, "modelID");
3375       String mnum = (id.startsWith("~") ? "~" + getModelNumberDotted(i) : null);
3376       if (mnum == null && mid == null && (mid = getModelTitle(i)) == null)
3377         continue;
3378       if (haveFile) {
3379         fname = getModelFileName(i);
3380         if (fname.endsWith("#molfile")) {
3381           mid = fname;
3382         } else {
3383           fname += "#";
3384           mid = fname + mid;
3385         }
3386       }
3387       if (id.equalsIgnoreCase(mid) || id.equalsIgnoreCase(mnum))
3388         return (isBaseModel ? vwr.getJDXBaseModelIndex(i) : i);
3389       if (fname != null && id.startsWith(fname))
3390         errCode = -2;
3391     }
3392     return (fname == null && !haveFile ? -2 : errCode);
3393   }
3394 
3395   /**
3396    * Retrieve the main modelset info Hashtable (or a new non-null Hashtable)
3397    * with an up-to-date "models" key.
3398    *
3399    * @param bsModels
3400    * @return Map
3401    */
getAuxiliaryInfo(BS bsModels)3402   public Map<String, Object> getAuxiliaryInfo(BS bsModels) {
3403     Map<String, Object> info = msInfo;
3404     if (info == null)
3405       info = new Hashtable<String, Object>();
3406     if (bsModels != null || !info.containsKey("models")) {
3407       Lst<Map<String, Object>> minfo = new Lst<Map<String, Object>>();
3408       for (int i = 0; i < mc; ++i)
3409         if (bsModels == null || bsModels.get(i)) {
3410           Map<String, Object> m = getModelAuxiliaryInfo(i);
3411           m.put("modelIndex", Integer.valueOf(i));
3412           minfo.addLast(m);
3413         }
3414       info.put("models", minfo);
3415     }
3416     return info;
3417   }
3418 
getDihedralMap(int[] alist)3419   public int[][] getDihedralMap(int[] alist) {
3420     Lst<int[]> list = new Lst<int[]>();
3421     int n = alist.length;
3422     Atom ai = null, aj = null, ak = null, al = null;
3423     for (int i = n - 1; --i >= 0;)
3424       for (int j = n; --j > i;) {
3425         ai = at[alist[i]];
3426         aj = at[alist[j]];
3427         if (ai.isBonded(aj)) {
3428           for (int k = n; --k >= 0;)
3429             if (k != i && k != j && (ak = at[alist[k]]).isBonded(ai))
3430               for (int l = n; --l >= 0;)
3431                 if (l != i && l != j && l != k
3432                     && (al = at[alist[l]]).isBonded(aj)) {
3433                   int[] a = new int[4];
3434                   a[0] = ak.i;
3435                   a[1] = ai.i;
3436                   a[2] = aj.i;
3437                   a[3] = al.i;
3438                   list.addLast(a);
3439                 }
3440         }
3441       }
3442     n = list.size();
3443     int[][] ilist = AU.newInt2(n);
3444     for (int i = n; --i >= 0;)
3445       ilist[n - i - 1] = list.get(i);
3446     return ilist;
3447   }
3448 
3449   /**
3450    * Sets the modulation for all atoms in bs.
3451    *
3452    * @param bs
3453    * @param isOn
3454    * @param qtOffset
3455    *        multiples of q or just t.
3456    * @param isQ
3457    *        true if multiples of q.
3458    *
3459    *
3460    */
setModulation(BS bs, boolean isOn, P3 qtOffset, boolean isQ)3461   public void setModulation(BS bs, boolean isOn, P3 qtOffset, boolean isQ) {
3462     if (bsModulated == null) {
3463       if (isOn)
3464         bsModulated = new BS();
3465       else if (bs == null)
3466         return;
3467     }
3468     if (bs == null)
3469       bs = getModelAtomBitSetIncludingDeleted(-1, false);
3470     float scale = vwr.getFloat(T.modulation);
3471     boolean haveMods = false;
3472     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
3473       JmolModulationSet ms = getModulation(i);
3474       if (ms == null)
3475         continue;
3476       ms.setModTQ(at[i], isOn, qtOffset, isQ, scale);
3477       if (bsModulated != null)
3478         bsModulated.setBitTo(i, isOn);
3479       haveMods = true;
3480     }
3481     if (!haveMods)
3482       bsModulated = null;
3483   }
3484 
3485   /**
3486    *
3487    * @param type
3488    *        volume, best, x, y, z, unitcell
3489    * @param bsAtoms
3490    * @return quaternion for best rotation or, for volume, string with volume
3491    *         \t{dx dy dz}
3492    */
getBoundBoxOrientation(int type, BS bsAtoms)3493   public Object getBoundBoxOrientation(int type, BS bsAtoms) {
3494     float dx = 0, dy = 0, dz = 0;
3495     Quat q = null, qBest = null;
3496     int j0 = bsAtoms.nextSetBit(0);
3497     float vMin = 0;
3498     if (j0 >= 0) {
3499       int n = (vOrientations == null ? 0 : vOrientations.length);
3500       if (n == 0) {
3501         V3[] av = new V3[15 * 15 * 15];
3502         n = 0;
3503         P4 p4 = new P4();
3504         for (int i = -7; i <= 7; i++)
3505           for (int j = -7; j <= 7; j++)
3506             for (int k = 0; k <= 14; k++, n++)
3507               if ((av[n] = V3.new3(i / 7f, j / 7f, k / 14f)).length() > 1)
3508                 --n;
3509         vOrientations = new Quat[n];
3510         for (int i = n; --i >= 0;) {
3511           float cos = (float) Math.sqrt(1 - av[i].lengthSquared());
3512           if (Float.isNaN(cos))
3513             cos = 0;
3514           p4.set4(av[i].x, av[i].y, av[i].z, cos);
3515           vOrientations[i] = Quat.newP4(p4);
3516         }
3517       }
3518       P3 pt = new P3();
3519       vMin = Float.MAX_VALUE;
3520       BoxInfo bBest = null;
3521       float v;
3522       BoxInfo b = new BoxInfo();
3523       b.setMargin(type == T.volume ? 0 : 0.1f);
3524       for (int i = 0; i < n; i++) {
3525         q = vOrientations[i];
3526         b.reset();
3527         for (int j = j0; j >= 0; j = bsAtoms.nextSetBit(j + 1))
3528           b.addBoundBoxPoint(q.transform2(at[j], pt));
3529         switch (type) {
3530         default:
3531         case T.volume:
3532         case T.best:
3533         case T.unitcell:
3534           v = (b.bbCorner1.x - b.bbCorner0.x) * (b.bbCorner1.y - b.bbCorner0.y)
3535               * (b.bbCorner1.z - b.bbCorner0.z);
3536           break;
3537         case T.x:
3538           v = b.bbCorner1.x - b.bbCorner0.x;
3539           break;
3540         case T.y:
3541           v = b.bbCorner1.y - b.bbCorner0.y;
3542           break;
3543         case T.z:
3544           v = b.bbCorner1.z - b.bbCorner0.z;
3545           break;
3546         }
3547         if (v < vMin) {
3548           qBest = q;
3549           bBest = b;
3550           b = new BoxInfo();
3551           b.setMargin(0.1f);
3552           vMin = v;
3553         }
3554       }
3555       switch (type) {
3556       default:
3557         return qBest;
3558       case T.unitcell:
3559         P3[] pts = bBest.getBoundBoxVertices();
3560         pts = new P3[] {pts[0], pts[BoxInfo.X], pts[BoxInfo.Y], pts[BoxInfo.Z]};
3561         qBest = qBest.inv();
3562         for (int i = 0; i < 4; i++) {
3563           qBest.transform2(pts[i], pts[i]);
3564           if (i > 0)
3565             pts[i].sub(pts[0]);
3566         }
3567         return pts;
3568       case T.volume:
3569       case T.best:
3570         // we want dz < dy < dx
3571         q = Quat.newQ(qBest);
3572         dx = bBest.bbCorner1.x - bBest.bbCorner0.x;
3573         dy = bBest.bbCorner1.y - bBest.bbCorner0.y;
3574         dz = bBest.bbCorner1.z - bBest.bbCorner0.z;
3575         if (dx < dy) {
3576           pt.set(0, 0, 1);
3577           q = Quat.newVA(pt, 90).mulQ(q);
3578           float f = dx;
3579           dx = dy;
3580           dy = f;
3581         }
3582         if (dy < dz) {
3583           if (dz > dx) {
3584             // is dy < dx < dz
3585             pt.set(0, 1, 0);
3586             q = Quat.newVA(pt, 90).mulQ(q);
3587             float f = dx;
3588             dx = dz;
3589             dz = f;
3590           }
3591           // is dy < dz < dx
3592           pt.set(1, 0, 0);
3593           q = Quat.newVA(pt, 90).mulQ(q);
3594           float f = dy;
3595           dy = dz;
3596           dz = f;
3597         }
3598         break;
3599       }
3600     }
3601     return (type == T.volume ? vMin + "\t{" + dx + " " + dy + " " + dz + "}\t" + bsAtoms
3602         : type == T.unitcell ? null : q == null || q.getTheta() == 0 ? new Quat() : q);
3603   }
3604 
getUnitCellForAtom(int index)3605   public SymmetryInterface getUnitCellForAtom(int index) {
3606     if (index < 0 || index > ac)
3607       return null;
3608     if (bsModulated != null) {
3609       JmolModulationSet ms = getModulation(index);
3610       SymmetryInterface uc = (ms == null ? null : ms.getSubSystemUnitCell());
3611       if (uc != null)
3612         return uc; // subsystems
3613     }
3614     return getUnitCell(at[index].mi);
3615   }
3616 
clearCache()3617   public void clearCache() {
3618     for (int i = mc; --i >= 0;)
3619       am[i].resetDSSR(false);
3620   }
3621 
getSymMatrices(int modelIndex)3622   public M4[] getSymMatrices(int modelIndex) {
3623     int n = getModelSymmetryCount(modelIndex);
3624     if (n == 0)
3625       return null;
3626     M4[] ops = new M4[n];
3627     SymmetryInterface unitcell = am[modelIndex].biosymmetry;
3628     if (unitcell == null)
3629       unitcell = getUnitCell(modelIndex);
3630     for (int i = n; --i >= 0;)
3631       ops[i] = unitcell.getSpaceGroupOperation(i);
3632     return ops;
3633   }
3634 
getBsBranches(float[] dihedralList)3635   public BS[] getBsBranches(float[] dihedralList) {
3636     int n = dihedralList.length / 6;
3637     BS[] bsBranches = new BS[n];
3638     Map<String, Boolean> map = new Hashtable<String, Boolean>();
3639     for (int i = 0, pt = 0; i < n; i++, pt += 6) {
3640       float dv = dihedralList[pt + 5] - dihedralList[pt + 4];
3641       if (Math.abs(dv) < 1f)
3642         continue;
3643       int i0 = (int) dihedralList[pt + 1];
3644       int i1 = (int) dihedralList[pt + 2];
3645       String s = "" + i0 + "_" + i1;
3646       if (map.containsKey(s))
3647         continue;
3648       map.put(s, Boolean.TRUE);
3649       BS bs = vwr.getBranchBitSet(i1, i0, true);
3650       Bond[] bonds = at[i0].bonds;
3651       Atom a0 = at[i0];
3652       for (int j = 0; j < bonds.length; j++) {
3653         Bond b = bonds[j];
3654         if (!b.isCovalent())
3655           continue;
3656         int i2 = b.getOtherAtom(a0).i;
3657         if (i2 == i1)
3658           continue;
3659         if (bs.get(i2)) {
3660           bs = null;
3661           break;
3662         }
3663       }
3664       bsBranches[i] = bs;
3665     }
3666     return bsBranches;
3667   }
3668 
recalculatePositionDependentQuantities(BS bs, M4 mat)3669   public void recalculatePositionDependentQuantities(BS bs, M4 mat) {
3670     if ((vwr.shm.getShape(JC.SHAPE_POLYHEDRA) != null))
3671       vwr.shm.getShapePropertyData(JC.SHAPE_POLYHEDRA, "move", new Object[] {
3672           bs, mat });
3673     if (haveStraightness)
3674       calculateStraightnessAll();
3675     recalculateLeadMidpointsAndWingVectors(-1);
3676     BS bsModels = getModelBS(bs, false);
3677     for (int i = bsModels.nextSetBit(0); i >= 0; i = bsModels.nextSetBit(i + 1)) {
3678       sm.notifyAtomPositionsChanged(i, bs, mat);
3679       if (mat != null) {
3680         Model m = am[i];
3681         if (m.isContainedIn(bs)) {
3682           if (m.mat4 == null)
3683             m.mat4 = M4.newM4(null);
3684           m.mat4.mul2(mat, m.mat4);
3685         }
3686       }
3687     }
3688     //averageAtomPoint = null;
3689     /* but we would need to somehow indicate this in the state
3690     if (ellipsoids != null)
3691       for (int i = bs.nextSetBit(0); i >= 0 && i < ellipsoids.length; i = bs.nextSetBit(i + 1))
3692         ellipsoids[i].rotate(mat);
3693     if (vibrationVectors != null)
3694       for (int i = bs.nextSetBit(0); i >= 0 && i < vibrationVectors.length; i = bs.nextSetBit(i + 1))
3695         if (vibrationVectors[i] != null)
3696             mat.transform(vibrationVectors[i]);
3697             */
3698   }
3699 
moveAtoms(M4 m4, M3 mNew, M3 rotation, V3 translation, BS bs, P3 center, boolean isInternal, boolean translationOnly)3700   public void moveAtoms(M4 m4, M3 mNew, M3 rotation, V3 translation, BS bs,
3701                         P3 center, boolean isInternal, boolean translationOnly) {
3702     if (m4 != null) {
3703       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
3704         m4.rotTrans(at[i]);
3705         taintAtom(i, TAINT_COORD);
3706       }
3707       mat4.setM4(m4);
3708       translation = null;
3709     } else if (!translationOnly) {
3710       if (mNew == null) {
3711         matTemp.setM3(rotation);
3712       } else {
3713         // screen frame?
3714         // must do inv(currentRot) * mNew * currentRot
3715         ptTemp.set(0, 0, 0);
3716         matInv.setM3(rotation);
3717         matInv.invert();
3718         matTemp.mul2(mNew, rotation);
3719         matTemp.mul2(matInv, matTemp);
3720       }
3721       if (isInternal) {
3722         // adjust rotation to be around center of this set of atoms
3723         vTemp.setT(center);
3724         mat4.setIdentity();
3725         mat4.setTranslation(vTemp);
3726         mat4t.setToM3(matTemp);
3727         mat4.mul(mat4t);
3728         mat4t.setIdentity();
3729         vTemp.scale(-1);
3730         mat4t.setTranslation(vTemp);
3731         mat4.mul(mat4t);
3732       } else {
3733         mat4.setToM3(matTemp);
3734       }
3735       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
3736         if (isInternal) {
3737           mat4.rotTrans(at[i]);
3738         } else {
3739           ptTemp.add(at[i]);
3740           mat4.rotTrans(at[i]);
3741           ptTemp.sub(at[i]);
3742         }
3743         taintAtom(i, TAINT_COORD);
3744       }
3745       if (!isInternal) {
3746         ptTemp.scale(1f / bs.cardinality());
3747         if (translation == null)
3748           translation = new V3();
3749         translation.add(ptTemp);
3750       }
3751     }
3752     if (translation != null) {
3753       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
3754         at[i].add(translation);
3755         taintAtom(i, TAINT_COORD);
3756       }
3757       if (!translationOnly) {
3758         mat4t.setIdentity();
3759         mat4t.setTranslation(translation);
3760         mat4.mul2(mat4t, mat4);
3761       }
3762     }
3763     recalculatePositionDependentQuantities(bs, mat4);
3764   }
3765 
setDihedrals(float[] dihedralList, BS[] bsBranches, float f)3766   public void setDihedrals(float[] dihedralList, BS[] bsBranches, float f) {
3767     int n = dihedralList.length / 6;
3768     if (f > 1)
3769       f = 1;
3770     for (int j = 0, pt = 0; j < n; j++, pt += 6) {
3771       BS bs = bsBranches[j];
3772       if (bs == null || bs.isEmpty())
3773         continue;
3774       Atom a1 = at[(int) dihedralList[pt + 1]];
3775       V3 v = V3.newVsub(at[(int) dihedralList[pt + 2]], a1);
3776       float angle = (dihedralList[pt + 5] - dihedralList[pt + 4]) * f;
3777       A4 aa = A4.newVA(v, (float) (-angle / TransformManager.degreesPerRadian));
3778       matTemp.setAA(aa);
3779       ptTemp.setT(a1);
3780       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
3781         at[i].sub(ptTemp);
3782         matTemp.rotate(at[i]);
3783         at[i].add(ptTemp);
3784         taintAtom(i, TAINT_COORD);
3785       }
3786     }
3787   }
3788 
setAtomCoordsRelative(T3 offset, BS bs)3789   public void setAtomCoordsRelative(T3 offset, BS bs) {
3790     setAtomsCoordRelative(bs, offset.x, offset.y, offset.z);
3791     mat4.setIdentity();
3792     vTemp.setT(offset);
3793     mat4.setTranslation(vTemp);
3794     recalculatePositionDependentQuantities(bs, mat4);
3795   }
3796 
setAtomCoords(BS bs, int tokType, Object xyzValues)3797   public void setAtomCoords(BS bs, int tokType, Object xyzValues) {
3798     setAtomCoord2(bs, tokType, xyzValues);
3799     switch (tokType) {
3800     case T.vibx:
3801     case T.viby:
3802     case T.vibz:
3803     case T.vibxyz:
3804       break;
3805     default:
3806       recalculatePositionDependentQuantities(bs, null);
3807     }
3808   }
3809 
3810   /**
3811    * Carries out a stereochemical inversion through a point, across a plane, or at a chirality center.
3812    *
3813    * @param pt point to invert around if not null
3814    * @param plane plane to invert across if not null
3815    * @param iAtom atom to switch two groups on if >= 0
3816    * @param bsAtoms atoms to switch for the atom option
3817    */
invertSelected(P3 pt, P4 plane, int iAtom, BS bsAtoms)3818   public void invertSelected(P3 pt, P4 plane, int iAtom, BS bsAtoms) {
3819     resetChirality();
3820     if (pt != null) {
3821       for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1)) {
3822         float x = (pt.x - at[i].x) * 2;
3823         float y = (pt.y - at[i].y) * 2;
3824         float z = (pt.z - at[i].z) * 2;
3825         setAtomCoordRelative(i, x, y, z);
3826       }
3827       return;
3828     }
3829     if (plane != null) {
3830       // ax + by + cz + d = 0
3831       V3 norm = V3.new3(plane.x, plane.y, plane.z);
3832       norm.normalize();
3833       float d = (float) Math.sqrt(plane.x * plane.x + plane.y * plane.y
3834           + plane.z * plane.z);
3835       for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1)) {
3836         float twoD = -Measure.distanceToPlaneD(plane, d, at[i]) * 2;
3837         float x = norm.x * twoD;
3838         float y = norm.y * twoD;
3839         float z = norm.z * twoD;
3840         setAtomCoordRelative(i, x, y, z);
3841       }
3842       return;
3843     }
3844     if (iAtom >= 0) {
3845       Atom thisAtom = at[iAtom];
3846       // stereochemical inversion at iAtom
3847       Bond[] bonds = thisAtom.bonds;
3848       if (bonds == null)
3849         return;
3850       BS bsToMove = new BS();
3851       Lst<P3> vNot = new Lst<P3>();
3852       BS bsModel = vwr.getModelUndeletedAtomsBitSet(thisAtom.mi);
3853       for (int i = 0; i < bonds.length; i++) {
3854         Atom a = bonds[i].getOtherAtom(thisAtom);
3855         if (bsAtoms.get(a.i)) {
3856           bsToMove.or(JmolMolecule.getBranchBitSet(at, a.i, bsModel, null,
3857               iAtom, true, true));
3858         } else {
3859           vNot.addLast(a);
3860         }
3861       }
3862       if (vNot.size() == 0)
3863         return;
3864       pt = Measure.getCenterAndPoints(vNot)[0];
3865       V3 v = V3.newVsub(thisAtom, pt);
3866       Quat q = Quat.newVA(v, 180);
3867       moveAtoms(null, null, q.getMatrix(), null, bsToMove, thisAtom, true, false);
3868     }
3869   }
3870 
getCellWeights(BS bsAtoms)3871   public float[] getCellWeights(BS bsAtoms) {
3872     float[] wts = null;
3873     int i = bsAtoms.nextSetBit(0);
3874     int iModel = -1;
3875     if (i >= 0 && getUnitCell(iModel = at[i].mi) != null) {
3876       P3 pt = new P3();
3877       BS bs = getModelAtomBitSetIncludingDeleted(iModel, true);
3878       bs.and(bsAtoms);
3879       wts = new float[bsAtoms.cardinality()];
3880       for (int p = 0; i >= 0; i = bsAtoms.nextSetBit(i + 1))
3881         wts[p++] = SimpleUnitCell.getCellWeight(at[i].getFractionalUnitCoordPt(
3882             true, false, pt));
3883     }
3884     return wts;
3885   }
3886 
getAtomGroupQuaternions(BS bsAtoms, int nMax, char qtype)3887   public Quat[] getAtomGroupQuaternions(BS bsAtoms, int nMax, char qtype) {
3888     // run through list, getting quaternions. For simple groups,
3889     // go ahead and take first three atoms
3890     // for PDB files, do not include NON-protein groups.
3891     int n = 0;
3892     Lst<Quat> v = new Lst<Quat>();
3893     bsAtoms = BSUtil.copy(bsAtoms);
3894     BS bsDone = new BS();
3895     for (int i = bsAtoms.nextSetBit(0); i >= 0 && n < nMax; i = bsAtoms
3896         .nextSetBit(i + 1)) {
3897       Group g = at[i].group;
3898       g.setAtomBits(bsDone);
3899       bsAtoms.andNot(bsDone);
3900       Quat q = g.getQuaternion(qtype);
3901       if (q == null) {
3902         if (!am[at[i].mi].isBioModel)
3903           q = g.getQuaternionFrame(at); // non-PDB just use first three atoms
3904         if (q == null)
3905           continue;
3906       }
3907       n++;
3908       v.addLast(q);
3909     }
3910     return v.toArray(new Quat[v.size()]);
3911   }
3912 
getConformation(int modelIndex, int conformationIndex, boolean doSet, BS bsSelected)3913   public BS getConformation(int modelIndex, int conformationIndex,
3914                             boolean doSet, BS bsSelected) {
3915     BS bs = new BS();
3916     if (conformationIndex >= 0)
3917       for (int i = mc; --i >= 0;) {
3918         if (i != modelIndex && modelIndex >= 0)
3919           continue;
3920         Model m = am[i];
3921         BS bsAtoms = vwr.getModelUndeletedAtomsBitSet(modelIndex);
3922         if (bsSelected != null)
3923           bsAtoms.and(bsSelected);
3924         if (bsAtoms.nextSetBit(0) < 0)
3925           continue;
3926         if (conformationIndex >= m.altLocCount) {
3927           if (conformationIndex == 0)
3928             bs.or(bsAtoms);
3929           continue;
3930         }
3931         if (am[i].isBioModel
3932             && ((BioModel) am[i]).getConformation(conformationIndex, doSet,
3933                 bsAtoms, bs))
3934           continue;
3935         int nAltLocs = getAltLocCountInModel(i);
3936         String altLocs = getAltLocListInModel(i);
3937         BS bsTemp = new BS();
3938         for (int c = nAltLocs; --c >= 0;)
3939           if (c != conformationIndex)
3940             bsAtoms.andNot(getAtomBitsMDa(T.spec_alternate,
3941                 altLocs.substring(c, c + 1), bsTemp));
3942         if (bsAtoms.nextSetBit(0) >= 0)
3943           bs.or(bsAtoms);
3944       }
3945     return bs;
3946   }
3947 
3948   ///// bio-only methods /////
3949 
getSequenceBits(String specInfo, BS bsAtoms, BS bsResult)3950   public BS getSequenceBits(String specInfo, BS bsAtoms, BS bsResult) {
3951     return (haveBioModels ? bioModelset.getAllSequenceBits(specInfo, bsAtoms, bsResult)
3952         : bsResult);
3953   }
3954 
getBioPolymerCountInModel(int modelIndex)3955   public int getBioPolymerCountInModel(int modelIndex) {
3956     return (haveBioModels ? bioModelset.getBioPolymerCountInModel(modelIndex)
3957         : 0);
3958   }
3959 
getPolymerPointsAndVectors(BS bs, Lst<P3[]> vList, boolean isTraceAlpha, float sheetSmoothing)3960   public void getPolymerPointsAndVectors(BS bs, Lst<P3[]> vList,
3961                                          boolean isTraceAlpha,
3962                                          float sheetSmoothing) {
3963     if (haveBioModels)
3964       bioModelset.getAllPolymerPointsAndVectors(bs, vList, isTraceAlpha,
3965           sheetSmoothing);
3966   }
3967 
recalculateLeadMidpointsAndWingVectors(int modelIndex)3968   public void recalculateLeadMidpointsAndWingVectors(int modelIndex) {
3969     if (haveBioModels)
3970       bioModelset.recalculatePoints(modelIndex);
3971   }
3972 
3973   /**
3974    * These are not actual hydrogen bonds. They are N-O bonds in proteins and
3975    * nucleic acids The method is called by AminoPolymer and NucleicPolymer
3976    * methods, which are indirectly called by ModelCollection.autoHbond
3977    *
3978    * @param bsA
3979    * @param bsB
3980    * @param vHBonds
3981    *        vector of bonds to fill; if null, creates the HBonds
3982    * @param nucleicOnly
3983    * @param nMax
3984    * @param dsspIgnoreHydrogens
3985    * @param bsHBonds
3986    */
3987 
calcRasmolHydrogenBonds(BS bsA, BS bsB, Lst<Bond> vHBonds, boolean nucleicOnly, int nMax, boolean dsspIgnoreHydrogens, BS bsHBonds)3988   public void calcRasmolHydrogenBonds(BS bsA, BS bsB, Lst<Bond> vHBonds,
3989                                       boolean nucleicOnly, int nMax,
3990                                       boolean dsspIgnoreHydrogens, BS bsHBonds) {
3991     if (haveBioModels)
3992       bioModelset.calcAllRasmolHydrogenBonds(bsA, bsB, vHBonds, nucleicOnly,
3993           nMax, dsspIgnoreHydrogens, bsHBonds, 2); // DSSP version 2 here
3994   }
3995 
calculateStraightnessAll()3996   public void calculateStraightnessAll() {
3997     if (haveBioModels && !haveStraightness)
3998       bioModelset.calculateStraightnessAll();
3999   }
4000 
4001   /**
4002    * see comments in org.jmol.modelsetbio.AlphaPolymer.java
4003    *
4004    * Struts are calculated for atoms in bs1 connecting to atoms in bs2. The two
4005    * bitsets may overlap.
4006    *
4007    * @param bs1
4008    * @param bs2
4009    * @return number of struts found
4010    */
calculateStruts(BS bs1, BS bs2)4011   public int calculateStruts(BS bs1, BS bs2) {
4012     return (haveBioModels ? bioModelset.calculateStruts(bs1, bs2) : 0);
4013   }
4014 
getGroupsWithin(int nResidues, BS bs)4015   public BS getGroupsWithin(int nResidues, BS bs) {
4016     return (haveBioModels ? bioModelset.getGroupsWithinAll(nResidues, bs)
4017         : new BS());
4018   }
4019 
getProteinStructureState(BS bsAtoms, int mode)4020   public String getProteinStructureState(BS bsAtoms, int mode) {
4021     return (haveBioModels ? bioModelset.getFullProteinStructureState(bsAtoms,
4022         mode) : "");
4023   }
4024 
calculateStructures(BS bsAtoms, boolean asDSSP, boolean doReport, boolean dsspIgnoreHydrogen, boolean setStructure, int version)4025   public String calculateStructures(BS bsAtoms, boolean asDSSP,
4026                                     boolean doReport,
4027                                     boolean dsspIgnoreHydrogen,
4028                                     boolean setStructure, int version) {
4029     return (haveBioModels ? bioModelset.calculateAllStuctures(bsAtoms, asDSSP,
4030         doReport, dsspIgnoreHydrogen, setStructure, version) : "");
4031   }
4032 
4033   /**
4034    * allows rebuilding of PDB structures; also accessed by ModelManager from
4035    * Eval
4036    *
4037    * @param alreadyDefined
4038    *        set to skip calculation
4039    * @param asDSSP
4040    * @param doReport
4041    * @param dsspIgnoreHydrogen
4042    * @param setStructure
4043    * @param includeAlpha
4044    * @param version TODO
4045    * @return report
4046    *
4047    */
calculateStructuresAllExcept(BS alreadyDefined, boolean asDSSP, boolean doReport, boolean dsspIgnoreHydrogen, boolean setStructure, boolean includeAlpha, int version)4048   public String calculateStructuresAllExcept(BS alreadyDefined, boolean asDSSP,
4049                                              boolean doReport,
4050                                              boolean dsspIgnoreHydrogen,
4051                                              boolean setStructure,
4052                                              boolean includeAlpha, int version) {
4053     freezeModels();
4054     return (haveBioModels ? bioModelset.calculateAllStructuresExcept(
4055         alreadyDefined, asDSSP, doReport, dsspIgnoreHydrogen, setStructure,
4056         includeAlpha, version) : "");
4057   }
4058 
recalculatePolymers(BS bsModelsExcluded)4059   public void recalculatePolymers(BS bsModelsExcluded) {
4060     bioModelset.recalculateAllPolymers(bsModelsExcluded, getGroups());
4061   }
4062 
calculatePolymers(Group[] groups, int groupCount, int baseGroupIndex, BS modelsExcluded)4063   protected void calculatePolymers(Group[] groups, int groupCount,
4064                                    int baseGroupIndex, BS modelsExcluded) {
4065     if (bioModelset != null) // not using haveBioModels because not frozen yet
4066       bioModelset.calculateAllPolymers(groups, groupCount, baseGroupIndex,
4067           modelsExcluded);
4068   }
4069 
calcSelectedMonomersCount()4070   public void calcSelectedMonomersCount() {
4071     if (haveBioModels)
4072       bioModelset.calcSelectedMonomersCount();
4073   }
4074 
setProteinType(BS bs, STR type)4075   public void setProteinType(BS bs, STR type) {
4076     if (haveBioModels)
4077       bioModelset.setAllProteinType(bs, type);
4078   }
4079 
setStructureList(Map<STR, float[]> structureList)4080   public void setStructureList(Map<STR, float[]> structureList) {
4081     if (haveBioModels)
4082       bioModelset.setAllStructureList(structureList);
4083   }
4084 
setConformation(BS bsAtoms)4085   public BS setConformation(BS bsAtoms) {
4086     if (haveBioModels)
4087       bioModelset.setAllConformation(bsAtoms);
4088     return bsAtoms;
4089   }
4090 
4091   @SuppressWarnings("unchecked")
getHeteroList(int modelIndex)4092   public Map<String, String> getHeteroList(int modelIndex) {
4093     Object o = (haveBioModels ? bioModelset.getAllHeteroList(modelIndex) : null);
4094     return (Map<String, String>) (o == null ? getInfoM("hetNames") : o);
4095   }
4096 
getUnitCellPointsWithin(float distance, BS bs, P3 pt, boolean asMap)4097   public Object getUnitCellPointsWithin(float distance, BS bs, P3 pt,
4098                                         boolean asMap) {
4099     Lst<P3> lst = new Lst<P3>();
4100     Map<String, Object> map = null;
4101     Lst<Integer> lstI = null;
4102     if (asMap) {
4103       map = new Hashtable<String, Object>();
4104       lstI = new Lst<Integer>();
4105       map.put("atoms", lstI);
4106       map.put("points", lst);
4107     }
4108     int iAtom = (bs == null ? -1 : bs.nextSetBit(0));
4109     bs = vwr
4110         .getModelUndeletedAtomsBitSet(iAtom < 0 ? vwr.am.cmi : at[iAtom].mi);
4111     if (iAtom < 0)
4112       iAtom = bs.nextSetBit(0);
4113     if (iAtom >= 0) {
4114       SymmetryInterface unitCell = getUnitCellForAtom(iAtom);
4115       if (unitCell != null) {
4116         AtomIndexIterator iter = unitCell.getIterator(vwr, at[iAtom], at, bs,
4117             distance);
4118         if (pt != null)
4119           iter.setCenter(pt, distance);
4120         while (iter.hasNext()) {
4121           iAtom = iter.next();
4122           pt = iter.getPosition();
4123           lst.addLast(pt);
4124           if (asMap) {
4125             lstI.addLast(Integer.valueOf(iAtom));
4126           }
4127         }
4128       }
4129     }
4130     return (asMap ? map : lst);
4131   }
4132 
calculateDssrProperty(String dataType)4133   public void calculateDssrProperty(String dataType) {
4134     if (dataType == null)
4135       return;
4136     if (dssrData == null || dssrData.length < ac)
4137       dssrData = new float[ac];
4138     for (int i = 0; i < ac; i++)
4139       dssrData[i] = Float.NaN;
4140     for (int i = mc; --i >= 0;)
4141       if (am[i].isBioModel)
4142         ((BioModel) am[i]).getAtomicDSSRData(dssrData, dataType);
4143   }
4144 
getAtomicDSSRData(int i)4145   public float getAtomicDSSRData(int i) {
4146     return (dssrData == null || dssrData.length <= i ? Float.NaN : dssrData[i]);
4147   }
4148 
4149   /**
4150    * Determine the Cahn-Ingold-Prelog R/S chirality of an atom
4151    *
4152    * @param atom
4153    * @return [0:none, 1:R, 2:S]
4154    */
getAtomCIPChiralityCode(Atom atom)4155   public int getAtomCIPChiralityCode(Atom atom) {
4156     haveChirality = true;
4157     Model m = am[atom.mi];
4158     if (!m.hasChirality) {
4159       calculateChiralityForAtoms(m.bsAtoms, false);
4160       m.hasChirality = true;
4161     }
4162     return atom.getCIPChiralityCode();
4163   }
4164 
calculateChiralityForAtoms(BS bsAtoms, boolean withReturn)4165   public String calculateChiralityForAtoms(BS bsAtoms, boolean withReturn) {
4166     haveChirality = true;
4167     for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1))
4168       at[i].setCIPChirality(0);
4169     Interface.getSymmetry(vwr, "ms").calculateCIPChiralityForAtoms(vwr, bsAtoms);
4170     if (!withReturn)
4171       return null;
4172     String s = "";
4173     for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1))
4174       s += at[i].getCIPChirality(false);
4175     return s;
4176   }
4177 
4178 }
4179 
4180