1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2007-10-14 12:33:20 -0500 (Sun, 14 Oct 2007) $
4  * $Revision: 8408 $
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.Arrays;
29 import java.util.Comparator;
30 import java.util.Hashtable;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 
34 import org.jmol.api.Interface;
35 import org.jmol.api.JmolDataManager;
36 import org.jmol.api.JmolEnvCalc;
37 import org.jmol.api.JmolModulationSet;
38 import org.jmol.api.SymmetryInterface;
39 import org.jmol.atomdata.AtomData;
40 import org.jmol.atomdata.RadiusData;
41 import org.jmol.atomdata.RadiusData.EnumType;
42 import org.jmol.bspt.Bspf;
43 import org.jmol.c.PAL;
44 import org.jmol.c.VDW;
45 import org.jmol.modelsetbio.BioModelSet;
46 import org.jmol.script.T;
47 import org.jmol.util.BSUtil;
48 import org.jmol.util.Edge;
49 import org.jmol.util.Elements;
50 import org.jmol.util.GData;
51 import org.jmol.util.Logger;
52 import org.jmol.util.Parser;
53 import org.jmol.util.Tensor;
54 import org.jmol.util.Vibration;
55 import org.jmol.viewer.JC;
56 import org.jmol.viewer.Viewer;
57 
58 import javajs.util.A4;
59 import javajs.util.AU;
60 import javajs.util.BS;
61 import javajs.util.Lst;
62 import javajs.util.M3;
63 import javajs.util.Measure;
64 import javajs.util.P3;
65 import javajs.util.P4;
66 import javajs.util.PT;
67 import javajs.util.Quat;
68 import javajs.util.T3;
69 import javajs.util.V3;
70 
71 abstract public class AtomCollection {
72 
73   private final static float almost180 = (float) Math.PI * 0.95f;
74   private final static float sqrt3_2 = (float) (Math.sqrt(3) / 2);
75   private final static V3 vRef = V3.new3(3.14159f, 2.71828f, 1.41421f);
76 
77   public Viewer vwr;
78   protected GData g3d;
79   /**
80    * If any model in the collection is a BioModel, then
81    * it is also indicated here as a "bioModelset", meaning
82    *
83    */
84   public BioModelSet bioModelset;
85 
86   public Atom[] at;
87 
getAtom(int iatom)88   public Atom getAtom(int iatom) {
89     return (iatom >= 0 && iatom < at.length ? at[iatom] : null);
90   }
91 
92   public int ac;
93 
94   public Trajectory trajectory;
95 
96   protected SymmetryInterface pointGroup;
97 
98   ////////////////////
99 
100   private LabelToken labeler;
101 
102   // the maximum BondingRadius seen in this set of atoms
103   // used in autobonding
104   protected float maxBondingRadius = PT.FLOAT_MIN_SAFE;
105   private float maxVanderwaalsRadius = PT.FLOAT_MIN_SAFE;
106 
107   private boolean hasBfactorRange;
108   private int bfactor100Lo;
109   private int bfactor100Hi;
110 
111   private boolean haveBSVisible, haveBSClickable;
112 
113   private BS bsSurface;
114   private int nSurfaceAtoms;
115   private int surfaceDistanceMax;
116 
117 //  protected P3 averageAtomPoint;
118 
119   protected boolean haveChirality;
120 
121   /**
122    *  Binary Space Partitioning Forest
123    */
124   protected Bspf bspf = null;
125 
126   protected boolean preserveState = true;
127   public boolean canSkipLoad = true;
128 
129   public boolean haveStraightness;
130 
131   private BS bsHidden;
132   public BS bsVisible, bsClickable, bsModulated;
133 
134   ////////////////////////////////////////////////////////////////
135   // these may or may not be allocated
136   // depending upon the AtomSetCollection characteristics
137   //
138   // used by Atom:
139   //
140 
141   public Object[][] atomTensorList; // specifically now for {*}.adpmin {*}.adpmax
142   public Map<String, Lst<Object>> atomTensors;
143 
144   protected int[] surfaceDistance100s;
145 
146   ///// SETTABLE atomic properties ///////////////
147   //
148   // The following references must be made if a new property is added to this list:
149   //
150   // 1) Add a new static value to the following TAINT_XXXX list.
151   // 2) Add its T.map name to the list in setupAC().
152   // 3) Follow all references to both atomType (for a string), formalCharge (for an int), or partialCharge (for a float)
153   //    These will include a reference in setAPa() and setAtomData() here and
154   //    to getAtomicPropertyStateBuffer() in viewer.StateCreator
155   // 4) add the settable attribute to the static int XXXX definition in script.T
156   // 5) check that {*}.xxxx = ....; write state test.spt, and script test.spt all work properly
157 
158   public BS[] tainted;
159   public static String[] userSettableValues;
160 
setupAC()161   protected void setupAC() {
162     bsHidden = new BS();
163     bsVisible = new BS();
164     bsClickable = new BS();
165     // this allows the Google Closure compiler to skip all the TAINTED defs in Clazz.defineStatics
166     if (userSettableValues == null)
167       userSettableValues = ("atomName atomType coord element formalCharge hydrophobicity " +
168           "ionic occupancy partialCharge temperature valence vanderWaals vibrationVector " +
169           "atomNo seqID resNo chain").split(" ");
170   }
171 
172   final public static int TAINT_ATOMNAME = 0;
173   final public static int TAINT_ATOMTYPE = 1;
174   final public static int TAINT_COORD = 2;
175   final public static int TAINT_ELEMENT = 3;
176   final public static int TAINT_FORMALCHARGE = 4;
177   final public static int TAINT_HYDROPHOBICITY = 5;
178   final public static int TAINT_BONDINGRADIUS = 6;
179   final public static int TAINT_OCCUPANCY = 7;
180   final public static int TAINT_PARTIALCHARGE = 8;
181   final public static int TAINT_TEMPERATURE = 9;
182   final public static int TAINT_VALENCE = 10;
183   final public static int TAINT_VANDERWAALS = 11;
184   final public static int TAINT_VIBRATION = 12;
185   final public static int TAINT_ATOMNO = 13;
186   final public static int TAINT_SEQID = 14;
187   final public static int TAINT_RESNO = 15;
188   final public static int TAINT_CHAIN = 16;
189   final public static int TAINT_MAX = 17; // 1 more than last number, above
190 
191 
192   String[] atomNames;
193   String[] atomTypes;
194   // String[] chainIDs; not necessary, as there is a place for this already in atom.group.chain
195   int[] atomSerials;
196   int[] atomResnos;
197   int[] atomSeqIDs;
198   float[] dssrData;
199   public Vibration[] vibrations;
200   public float[] occupancies;
201   short[] bfactor100s;
202   float[] partialCharges;
203   float[] bondingRadii;
204   float[] hydrophobicities;
205   public BS bsPartialCharges;
206 
207 
releaseModelSetAC()208   protected void releaseModelSetAC() {
209     at = null;
210     vwr = null;
211     g3d = null;
212     bspf = null;
213     surfaceDistance100s = null;
214     bsSurface = null;
215     tainted = null;
216 
217     atomNames = null;
218     atomTypes = null;
219     atomResnos = null;
220     dssrData = null;
221     atomSerials = null;
222     atomSeqIDs = null;
223     vibrations = null;
224     occupancies = null;
225     bfactor100s = null;
226     resetPartialCharges();
227     bondingRadii = null;
228     atomTensors = null;
229   }
230 
mergeAtomArrays(AtomCollection mergeModelSet)231   protected void mergeAtomArrays(AtomCollection mergeModelSet) {
232     tainted = mergeModelSet.tainted;
233     atomNames = mergeModelSet.atomNames;
234     atomTypes = mergeModelSet.atomTypes;
235     atomResnos = mergeModelSet.atomResnos;
236     dssrData = mergeModelSet.dssrData;
237     atomSerials = mergeModelSet.atomSerials;
238     atomSeqIDs = mergeModelSet.atomSeqIDs;
239     vibrations = mergeModelSet.vibrations;
240     occupancies = mergeModelSet.occupancies;
241     bfactor100s = mergeModelSet.bfactor100s;
242     bondingRadii = mergeModelSet.bondingRadii;
243     partialCharges = mergeModelSet.partialCharges;
244     bsPartialCharges = mergeModelSet.bsPartialCharges;
245     atomTensors = mergeModelSet.atomTensors;
246     atomTensorList = mergeModelSet.atomTensorList;
247     bsModulated = mergeModelSet.bsModulated;
248     haveStraightness = false;
249     surfaceDistance100s = null;
250   }
251 
getAtomPointVector(BS bs)252   public Lst<P3> getAtomPointVector(BS bs) {
253     Lst<P3> v = new  Lst<P3>();
254     int n = ac;
255     if (bs != null) {
256       for (int i = bs.nextSetBit(0); i >= 0 && i < n; i = bs.nextSetBit(i+1)) {
257         v.addLast(at[i]);
258       }
259     }
260     return v;
261   }
262 
modelSetHasVibrationVectors()263   public boolean modelSetHasVibrationVectors(){
264     return (vibrations != null);
265   }
266 
getAtomTypes()267   public String[] getAtomTypes() {
268     return atomTypes;
269   }
270 
getPartialCharges()271   public float[] getPartialCharges() {
272     return partialCharges;
273   }
274 
getBondingRadii()275   public float[] getBondingRadii() {
276     return bondingRadii;
277   }
278 
getBFactors()279   public short[] getBFactors() {
280     return bfactor100s;
281   }
282 
getHydrophobicity()283   public float[] getHydrophobicity() {
284     return hydrophobicities;
285   }
286 
287 
setBsHidden(BS bs)288   public void setBsHidden(BS bs) { //from selection manager
289     bsHidden = bs;
290   }
291 
isAtomHidden(int iAtom)292   public boolean isAtomHidden(int iAtom) {
293     return bsHidden.get(iAtom);
294   }
295 
296   //////////// atoms //////////////
297 
getLabeler()298   public LabelToken getLabeler() {
299     // prevents JavaScript from requiring LabelToken upon core load
300     // will be abbreviated to JM., so don't use getOption here.
301     return (labeler == null ? labeler = (LabelToken) Interface.getInterface("org.jmol.modelset.LabelToken", vwr, "ms") : labeler);
302   }
303 
getAtomInfo(int i, String format, P3 ptTemp)304   public String getAtomInfo(int i, String format, P3 ptTemp) {
305     return (format == null ? at[i].getInfo()
306         : getLabeler().formatLabel(vwr, at[i], format, ptTemp));
307   }
308 
getElementName(int i)309   public String getElementName(int i) {
310       return Elements.elementNameFromNumber(at[i]
311           .getAtomicAndIsotopeNumber());
312   }
313 
getQuaternion(int i, char qtype)314   public Quat getQuaternion(int i, char qtype) {
315     return (i < 0 ? null : at[i].group.getQuaternion(qtype));
316   }
317 
getFirstAtomIndexFromAtomNumber(int atomNumber, BS bsVisibleFrames)318   public int getFirstAtomIndexFromAtomNumber(int atomNumber, BS bsVisibleFrames) {
319     //definitely want FIRST (model) not last here
320     for (int i = 0; i < ac; i++) {
321       Atom atom = at[i];
322       if (atom != null && atom.getAtomNumber() == atomNumber && bsVisibleFrames.get(atom.mi))
323         return i;
324     }
325     return -1;
326   }
327 
setFormalCharges(BS bs, int formalCharge)328   public void setFormalCharges(BS bs, int formalCharge) {
329     if (bs != null) {
330       resetPartialCharges();
331       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
332         at[i].setFormalCharge(formalCharge);
333         taintAtom(i, TAINT_FORMALCHARGE);
334       }
335     }
336   }
337 
getAtomicCharges()338   public float[] getAtomicCharges() {
339     float[] charges = new float[ac];
340     for (int i = ac; --i >= 0; )
341       charges[i] = (at[i] == null ? 0 : at[i].getElementNumber());
342     return charges;
343   }
344 
getRadiusVdwJmol(Atom atom)345   protected float getRadiusVdwJmol(Atom atom) {
346     return Elements.getVanderwaalsMar(atom.getElementNumber(),
347         VDW.JMOL) / 1000f;
348   }
349 
getMaxVanderwaalsRadius()350   public float getMaxVanderwaalsRadius() {
351     //Dots
352     if (maxVanderwaalsRadius == PT.FLOAT_MIN_SAFE)
353       findMaxRadii();
354     return maxVanderwaalsRadius;
355   }
356 
findMaxRadii()357   protected void findMaxRadii() {
358     float r;
359     for (int i = ac; --i >= 0;) {
360       Atom atom = at[i];
361       if (atom == null)
362         continue;
363       if ((r = atom.getBondingRadius()) > maxBondingRadius)
364         maxBondingRadius = r;
365       if ((r = atom.getVanderwaalsRadiusFloat(vwr, VDW.AUTO)) > maxVanderwaalsRadius)
366         maxVanderwaalsRadius = r;
367     }
368   }
369 
clearBfactorRange()370   public void clearBfactorRange() {
371     hasBfactorRange = false;
372   }
373 
calcBfactorRange(BS bs)374   private void calcBfactorRange(BS bs) {
375     if (hasBfactorRange)
376       return;
377     bfactor100Lo = Integer.MAX_VALUE;
378     bfactor100Hi = Integer.MIN_VALUE;
379     if (bs == null) {
380       for (int i = 0; i < ac; i++)
381         setBf(i);
382     } else {
383       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1))
384         setBf(i);
385     }
386     hasBfactorRange = true;
387   }
388 
setBf(int i)389   private void setBf(int i) {
390     if (at[i] == null)
391       return;
392     int bf = at[i].getBfactor100();
393     if (bf < bfactor100Lo)
394       bfactor100Lo = bf;
395     else if (bf > bfactor100Hi)
396       bfactor100Hi = bf;
397   }
398 
getBfactor100Lo()399   public int getBfactor100Lo() {
400     //ColorManager
401     if (!hasBfactorRange) {
402       if (vwr.g.rangeSelected) {
403         calcBfactorRange(vwr.bsA());
404       } else {
405         calcBfactorRange(null);
406       }
407     }
408     return bfactor100Lo;
409   }
410 
getBfactor100Hi()411   public int getBfactor100Hi() {
412     //ColorManager
413     getBfactor100Lo();
414     return bfactor100Hi;
415   }
416 
getSurfaceDistanceMax()417   public int getSurfaceDistanceMax() {
418     //ColorManager, Eval
419     if (surfaceDistance100s == null)
420       calcSurfaceDistances();
421     return surfaceDistanceMax;
422   }
423 
calculateVolume(BS bs, VDW vType)424   public float calculateVolume(BS bs, VDW vType) {
425     // Eval
426     float volume = 0;
427     if (bs != null)
428       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
429         volume += at[i].getVolume(vwr, vType);
430     return volume;
431   }
432 
getSurfaceDistance100(int atomIndex)433   int getSurfaceDistance100(int atomIndex) {
434     //atom
435     if (nSurfaceAtoms == 0)
436       return -1;
437     if (surfaceDistance100s == null)
438       calcSurfaceDistances();
439     return surfaceDistance100s[atomIndex];
440   }
441 
calcSurfaceDistances()442   private void calcSurfaceDistances() {
443     calculateSurface(null, -1);
444   }
445 
calculateSurface(BS bsSelected, float envelopeRadius)446   public P3[] calculateSurface(BS bsSelected, float envelopeRadius) {
447     if (envelopeRadius < 0)
448       envelopeRadius = JC.ENC_CALC_MAX_DIST;
449 
450     JmolEnvCalc ec = ((JmolEnvCalc) Interface.getOption("geodesic.EnvelopeCalculation", vwr, "ms"))
451     .set(vwr, ac, null);
452     ec.calculate(new RadiusData(null, envelopeRadius, EnumType.ABSOLUTE, null),
453         Float.MAX_VALUE,
454         bsSelected, BSUtil.copyInvert(bsSelected, ac),
455         false, false, false, true);
456     P3[] points = ec.getPoints();
457     surfaceDistanceMax = 0;
458     bsSurface = ec.getBsSurfaceClone();
459     surfaceDistance100s = new int[ac];
460     nSurfaceAtoms = BSUtil.cardinalityOf(bsSurface);
461     if (nSurfaceAtoms == 0 || points == null || points.length == 0)
462       return points;
463     float radiusAdjust = (envelopeRadius == Float.MAX_VALUE ? 0 : envelopeRadius);
464     for (int i = 0; i < ac; i++) {
465       //surfaceDistance100s[i] = Integer.MIN_VALUE;
466       if (bsSurface.get(i) || at[i] == null) {
467         surfaceDistance100s[i] = 0;
468       } else {
469         float dMin = Float.MAX_VALUE;
470         Atom atom = at[i];
471         for (int j = points.length; --j >= 0;) {
472           dMin = Math.min(Math.abs(points[j].distance(atom) - radiusAdjust), dMin);
473         }
474         int d = surfaceDistance100s[i] = (int) Math.floor(dMin * 100);
475         surfaceDistanceMax = Math.max(surfaceDistanceMax, d);
476       }
477     }
478     return points;
479   }
480 
481   @SuppressWarnings("unchecked")
setAtomCoord2(BS bs, int tokType, Object xyzValues)482   protected void setAtomCoord2(BS bs, int tokType, Object xyzValues) {
483     P3 xyz = null;
484     P3[] values = null;
485     Lst<P3> v = null;
486     int type = 0;
487     int nValues = 1;
488     if (xyzValues instanceof P3) {
489       xyz = (P3) xyzValues;
490     } else if (xyzValues instanceof Lst<?>) {
491       v = (Lst<P3>) xyzValues;
492       if ((nValues = v.size()) == 0)
493         return;
494       type = 1;
495     } else if (AU.isAP(xyzValues)) {
496       values = (P3[]) xyzValues;
497       if ((nValues = values.length) == 0)
498         return;
499       type = 2;
500     } else {
501       return;
502     }
503     int n = 0;
504     if (bs != null)
505       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
506         switch (type) {
507         case 1:
508           if (n >= nValues)
509             return;
510           xyz = v.get(n++);
511           break;
512         case 2:
513           if (n >= nValues)
514             return;
515           xyz = values[n++];
516           break;
517         }
518         if (xyz != null)
519           switch (tokType) {
520           case T.xyz:
521             setAtomCoord(i, xyz.x, xyz.y, xyz.z);
522             break;
523           case T.fracxyz:
524             at[i].setFractionalCoordTo(xyz, true);
525             taintAtom(i, TAINT_COORD);
526             break;
527           case T.fuxyz:
528             at[i].setFractionalCoordTo(xyz, false);
529             taintAtom(i, TAINT_COORD);
530             break;
531           case T.vibxyz:
532             setAtomVibrationVector(i, xyz);
533             break;
534           }
535       }
536   }
537 
setAtomVibrationVector(int atomIndex, T3 vib)538   private void setAtomVibrationVector(int atomIndex, T3 vib) {
539     setVibrationVector(atomIndex, vib);
540     taintAtom(atomIndex, TAINT_VIBRATION);
541   }
542 
setAtomCoord(int atomIndex, float x, float y, float z)543   public void setAtomCoord(int atomIndex, float x, float y, float z) {
544     if (atomIndex < 0 || atomIndex >= ac)
545       return;
546     Atom a = at[atomIndex];
547     a.set(x, y, z);
548     fixTrajectory(a);
549     taintAtom(atomIndex, TAINT_COORD);
550   }
551 
fixTrajectory(Atom a)552   private void fixTrajectory(Atom a) {
553     if (((ModelSet) this).isTrajectory(a.mi))
554       trajectory.fixAtom(a);
555   }
556 
setAtomCoordRelative(int atomIndex, float x, float y, float z)557   public void setAtomCoordRelative(int atomIndex, float x, float y, float z) {
558     if (atomIndex < 0 || atomIndex >= ac)
559       return;
560     Atom a = at[atomIndex];
561     a.add3(x, y, z);
562     fixTrajectory(a);
563     taintAtom(atomIndex, TAINT_COORD);
564   }
565 
setAtomsCoordRelative(BS bs, float x, float y, float z)566   protected void setAtomsCoordRelative(BS bs, float x, float y,
567                                       float z) {
568     if (bs != null)
569       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1))
570         setAtomCoordRelative(i, x, y, z);
571   }
572 
setAPa(BS bs, int tok, int iValue, float fValue, String sValue, float[] values, String[] list)573   protected void setAPa(BS bs, int tok, int iValue, float fValue,
574                               String sValue, float[] values, String[] list) {
575     int n = 0;
576 
577     if (values != null && values.length == 0 || bs == null)
578       return;
579     boolean isAll = (values != null && values.length == ac
580         || list != null && list.length == ac);
581     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
582       if (isAll)
583         n = i;
584       if (values != null) {
585         if (n >= values.length)
586           return;
587         fValue = values[n++];
588         if (Float.isNaN(fValue))
589           continue;
590         iValue = (int) fValue;
591       } else if (list != null) {
592         if (n >= list.length)
593           return;
594         sValue = list[n++];
595       }
596       Atom atom = at[i];
597       float f;
598       switch (tok) {
599       case T.atomname:
600         setAtomName(i, sValue, true);
601         break;
602       case T.atomtype:
603         setAtomType(i, sValue);
604         break;
605       case T.chain:
606         setChainID(i, sValue);
607         break;
608       case T.atomno:
609         setAtomNumber(i, iValue, true);
610         break;
611       case T.seqid:
612         setAtomSeqID(i, iValue);
613         break;
614       case T.atomx:
615       case T.x:
616         setAtomCoord(i, fValue, atom.y, atom.z);
617         break;
618       case T.atomy:
619       case T.y:
620         setAtomCoord(i, atom.x, fValue, atom.z);
621         break;
622       case T.atomz:
623       case T.z:
624         setAtomCoord(i, atom.x, atom.y, fValue);
625         break;
626       case T.vibx:
627       case T.viby:
628       case T.vibz:
629         setVibrationVector2(i, tok, fValue);
630         break;
631       case T.fracx:
632       case T.fracy:
633       case T.fracz:
634         atom.setFractionalCoord(tok, fValue, true);
635         taintAtom(i, TAINT_COORD);
636         break;
637       case T.fux:
638       case T.fuy:
639       case T.fuz:
640         atom.setFractionalCoord(tok, fValue, false);
641         taintAtom(i, TAINT_COORD);
642         break;
643       case T.elemno:
644       case T.element:
645         setElement(atom, iValue, true);
646         break;
647       case T.formalcharge:
648         resetPartialCharges();
649         atom.setFormalCharge(iValue);
650         taintAtom(i, TAINT_FORMALCHARGE);
651         break;
652       case T.hydrophobicity:
653         setHydrophobicity(i, fValue);
654         break;
655       case T.occupancy:
656         // a legacy thing
657         f = (fValue < 2 && fValue >= 0.01f ? 100 * fValue : fValue);
658         setOccupancy(i, f, true);
659         break;
660       case T.partialcharge:
661         setPartialCharge(i, fValue, true);
662         break;
663       case T.bondingradius:
664         setBondingRadius(i, fValue);
665         break;
666       case T.temperature:
667         setBFactor(i, fValue, true);
668         break;
669       case T.resno:
670         setAtomResno(i,  iValue);
671         break;
672       case T.label:
673       case T.format:
674         vwr.shm.setAtomLabel(sValue, i);
675         break;
676       case T.radius:
677       case T.spacefill:
678         f = fValue;
679         if (f < 0)
680           f = 0;
681         else if (f > Atom.RADIUS_MAX)
682           f = Atom.RADIUS_GLOBAL;
683         atom.madAtom = ((short) (f * 2000));
684         break;
685       case T.selected:
686         vwr.slm.setSelectedAtom(atom.i, (fValue != 0));
687         break;
688       case T.valence:
689         atom.setValence(iValue);
690         taintAtom(i, TAINT_VALENCE);
691         break;
692       case T.vanderwaals:
693         if (atom.setRadius(fValue))
694           taintAtom(i, TAINT_VANDERWAALS);
695         else
696           untaint(i, TAINT_VANDERWAALS);
697         break;
698       default:
699         Logger.error("unsettable atom property: " + T.nameOf(tok));
700         return;
701       }
702     }
703     switch (tok) {
704     case T.selected:
705       vwr.slm.setSelectedAtom(-1, false);
706       break;
707     case T.radius:
708     case T.spacefill:
709       vwr.setShapeSize(JC.SHAPE_BALLS, Integer.MAX_VALUE, bs);
710     }
711   }
712 
713   /**
714    * also handles modulation info
715    *
716    * @param atomIndex
717    * @param c
718    * @return value or NaN
719    */
getVibCoord(int atomIndex, char c)720   public float getVibCoord(int atomIndex, char c) {
721     JmolModulationSet ms = null;
722     Vibration v = null;
723     switch (c) {
724     case 'x':
725     case 'y':
726     case 'z':
727       v = getVibration(atomIndex, false);
728       break;
729     default:
730       ms = getModulation(atomIndex);
731       if (ms != null) {
732         v = ms.getVibration(false);
733         if (v == null)
734           v = (Vibration) ms;
735       }
736     }
737     if (v == null && ms == null)
738       return Float.NaN;
739     switch (c) {
740     case 'x':
741     case 'X':
742       return v.x;
743     case 'y':
744     case 'Y':
745       return v.y;
746     case 'z':
747     case 'Z':
748       return v.z;
749     case 'O':
750       return ((Float) ms.getModulation('O', null)).floatValue();
751     case '1':
752     case '2':
753     case '3':
754       T3 t = (T3) ms.getModulation('T', null);
755       float x = (c == '1' ? t.x : c == '2' ? t.y : t.z);
756       return (float) (x - Math.floor(x));
757     default:
758       return Float.NaN;
759     }
760   }
761 
getVibration(int atomIndex, boolean forceNew)762   public Vibration getVibration(int atomIndex, boolean forceNew) {
763     Vibration v = (vibrations == null  ? null : (Vibration) vibrations[atomIndex]);
764     return (v instanceof JmolModulationSet ? ((JmolModulationSet) v).getVibration(forceNew)
765         : v == null && forceNew ? new Vibration() : v);
766   }
767 
getModulation(int iAtom)768   public JmolModulationSet getModulation(int iAtom) {
769     Vibration v = (vibrations == null  ? null : (Vibration) vibrations[iAtom]);
770     return (JmolModulationSet) (v != null && v.modDim > 0 ? v : null);
771   }
772 
setVibrationVector(int atomIndex, T3 vib)773   protected void setVibrationVector(int atomIndex, T3 vib) {
774     if (Float.isNaN(vib.x) || Float.isNaN(vib.y) || Float.isNaN(vib.z))
775       return;
776     if (vibrations == null || vibrations.length < atomIndex)
777       vibrations = new Vibration[at.length];
778     if (vib instanceof Vibration) {
779       vibrations[atomIndex] = (Vibration) vib;
780     } else {
781       if (vibrations[atomIndex] == null)
782         vibrations[atomIndex] = new Vibration();
783       vibrations[atomIndex].setXYZ(vib);
784     }
785     at[atomIndex].setVibrationVector();
786   }
787 
setVibrationVector2(int atomIndex, int tok, float fValue)788   private void setVibrationVector2(int atomIndex, int tok, float fValue) {
789     Vibration v = getVibration(atomIndex, true);
790     if (v == null)
791       return;
792     switch (tok) {
793     case T.vibx:
794       v.x = fValue;
795       break;
796     case T.viby:
797       v.y = fValue;
798       break;
799     case T.vibz:
800       v.z = fValue;
801       break;
802     }
803     setAtomVibrationVector(atomIndex, v);
804   }
805 
setAtomName(int atomIndex, String name, boolean doTaint)806   public void setAtomName(int atomIndex, String name, boolean doTaint) {
807     if (doTaint && name.equals(at[atomIndex].getAtomName()))
808       return;
809     // can't set the name of a bioatom
810     byte id = (((ModelSet) this).am[at[atomIndex].mi].isBioModel ? vwr.getJBR()
811         .lookupSpecialAtomID(name) : 0);
812     at[atomIndex].atomID = id;
813     if (id <= 0) {
814       if (atomNames == null)
815         atomNames = new String[at.length];
816       atomNames[atomIndex] = name;
817     }
818     if (doTaint)
819       taintAtom(atomIndex, TAINT_ATOMNAME);
820   }
821 
setAtomType(int atomIndex, String type)822   private void setAtomType(int atomIndex, String type) {
823     if (type.equals(at[atomIndex].getAtomType()))
824       return;
825     if (atomTypes == null)
826       atomTypes = new String[at.length];
827     atomTypes[atomIndex] = type;
828     return;
829   }
830 
setChainID(int atomIndex, String id)831   private void setChainID(int atomIndex, String id) {
832     if (id.equals(at[atomIndex].getChainIDStr()))
833       return;
834     int intid = at[atomIndex].getChainID();
835     BS bs = getChainBits(intid);
836     Chain c = at[atomIndex].group.chain;
837     c.chainID = vwr.getChainID(id, true);
838     for (int i = bs.nextSetBit(0); i >= 0; i  = bs.nextSetBit(i + 1))
839       taintAtom(i, TAINT_CHAIN);
840   }
841 
setAtomNumber(int atomIndex, int atomno, boolean doTaint)842   public void setAtomNumber(int atomIndex, int atomno, boolean doTaint) {
843     if (doTaint && atomno == at[atomIndex].getAtomNumber())
844       return;
845     if (atomSerials == null)
846       atomSerials = new int[at.length];
847     atomSerials[atomIndex] = atomno;
848     if (doTaint)
849       taintAtom(atomIndex, TAINT_ATOMNO);
850   }
851 
setElement(Atom atom, int atomicNumber, boolean doTaint)852   public void setElement(Atom atom, int atomicNumber, boolean doTaint) {
853     if (doTaint && atom.getElementNumber() == atomicNumber)
854       return;
855     atom.setAtomicAndIsotopeNumber(atomicNumber);
856     atom.paletteID = PAL.CPK.id;
857     atom.colixAtom = vwr.cm.getColixAtomPalette(atom,
858         PAL.CPK.id);
859     resetPartialCharges();
860     if (doTaint)
861       taintAtom(atom.i, TAINT_ELEMENT);
862   }
863 
resetPartialCharges()864   private void resetPartialCharges() {
865     partialCharges = null;
866     bsPartialCharges = null;
867   }
868 
setAtomResno(int atomIndex, int resno)869   private void setAtomResno(int atomIndex, int resno) {
870     if (resno == at[atomIndex].getResno())
871       return;
872     at[atomIndex].group.setResno(resno);
873     if (atomResnos == null)
874       atomResnos = new int[at.length];
875     atomResnos[atomIndex] = resno;
876     taintAtom(atomIndex, TAINT_RESNO);
877   }
878 
setAtomSeqID(int atomIndex, int seqID)879   private void setAtomSeqID(int atomIndex, int seqID) {
880     if (seqID == at[atomIndex].getSeqID())
881       return;
882     if (atomSeqIDs == null)
883       atomSeqIDs = new int[at.length];
884     atomSeqIDs[atomIndex] = seqID;
885     taintAtom(atomIndex, TAINT_SEQID);
886   }
887 
setOccupancy(int atomIndex, float occupancy, boolean doTaint)888   protected void setOccupancy(int atomIndex, float occupancy, boolean doTaint) {
889     if (doTaint && occupancy == at[atomIndex].getOccupancy100())
890       return;
891     if (occupancies == null) {
892       if (occupancy == 100)
893         return; // 100 is the default;
894       occupancies = new float[at.length];
895       for (int i = at.length; --i >= 0;)
896         occupancies[i] = 100;
897     }
898     occupancies[atomIndex] = occupancy;
899     if (doTaint)
900       taintAtom(atomIndex, TAINT_OCCUPANCY);
901   }
902 
setPartialCharge(int atomIndex, float partialCharge, boolean doTaint)903   protected void setPartialCharge(int atomIndex, float partialCharge, boolean doTaint) {
904     if (Float.isNaN(partialCharge))
905       return;
906     if (partialCharges == null) {
907       bsPartialCharges = new BS();
908       if (partialCharge == 0)
909         return; // no need to store a 0.
910       partialCharges = new float[at.length];
911     }
912     bsPartialCharges.set(atomIndex);
913     partialCharges[atomIndex] = partialCharge;
914     if (doTaint)
915       taintAtom(atomIndex, TAINT_PARTIALCHARGE);
916   }
917 
setBondingRadius(int atomIndex, float radius)918   protected void setBondingRadius(int atomIndex, float radius) {
919     if (Float.isNaN(radius) || radius == at[atomIndex].getBondingRadius())
920       return;
921     if (bondingRadii == null) {
922       bondingRadii = new float[at.length];
923     } else if (bondingRadii.length < at.length) {
924       bondingRadii = (float[]) AU.ensureLength(bondingRadii, at.length);
925     }
926     bondingRadii[atomIndex] = radius;
927     taintAtom(atomIndex, TAINT_BONDINGRADIUS);
928   }
929 
setBFactor(int atomIndex, float bfactor, boolean doTaint)930   protected void setBFactor(int atomIndex, float bfactor, boolean doTaint) {
931     if (Float.isNaN(bfactor) || doTaint && bfactor == at[atomIndex].getBfactor100())
932       return;
933     if (bfactor100s == null) {
934       if (bfactor == 0) // there's no need to store a 0.
935         return;
936       bfactor100s = new short[at.length];
937     }
938     bfactor100s[atomIndex] = (short) ((bfactor < -327.68f ? -327.68f
939         : bfactor > 327.67 ? 327.67 : bfactor) * 100 + (bfactor < 0 ? -0.5 : 0.5));
940     if (doTaint)
941       taintAtom(atomIndex, TAINT_TEMPERATURE);
942   }
943 
setHydrophobicity(int atomIndex, float value)944   private void setHydrophobicity(int atomIndex, float value) {
945     if (Float.isNaN(value) || value == at[atomIndex].getHydrophobicity())
946       return;
947     if (hydrophobicities == null) {
948       hydrophobicities = new float[at.length];
949       for (int i = 0; i < at.length; i++)
950         hydrophobicities[i] = Elements.getHydrophobicity(at[i].group.groupID);
951     }
952     hydrophobicities[atomIndex] = value;
953     taintAtom(atomIndex, TAINT_HYDROPHOBICITY);
954   }
955 
956   // loading data
957 
setAtomData(int type, String name, String dataString, boolean isDefault)958   public void setAtomData(int type, String name, String dataString,
959                           boolean isDefault) {
960     float[] fData = null;
961     BS bs = null;
962     switch(type) {
963     case TAINT_COORD:
964       loadCoordinates(dataString, false, !isDefault);
965       return;
966     case TAINT_VIBRATION:
967       loadCoordinates(dataString, true, true);
968       return;
969     case TAINT_MAX:
970       fData = new float[ac];
971       bs = BS.newN(ac);
972       break;
973     }
974     int[] lines = Parser.markLines(dataString, ';');
975     int n = 0;
976     try {
977       int nData = PT.parseInt(dataString.substring(0, lines[0] - 1));
978       for (int i = 1; i <= nData; i++) {
979         String[] tokens = PT.getTokens(PT.parseTrimmed(dataString.substring(
980             lines[i], lines[i + 1] - 1)));
981         int atomIndex = PT.parseInt(tokens[0]) - 1;
982         if (atomIndex < 0 || atomIndex >= ac)
983           continue;
984         Atom atom = at[atomIndex];
985         n++;
986         int pt = tokens.length - 1;
987         float x = PT.parseFloat(tokens[pt]);
988         switch (type) {
989         case TAINT_MAX:
990           fData[atomIndex] = x;
991           bs.set(atomIndex);
992           continue;
993         case TAINT_ATOMNAME:
994           setAtomName(atomIndex, tokens[pt], true);
995           break;
996         case TAINT_ATOMNO:
997           setAtomNumber(atomIndex, (int) x, true);
998           break;
999         case TAINT_RESNO:
1000           setAtomResno(atomIndex, (int) x);
1001           break;
1002         case TAINT_SEQID:
1003           setAtomSeqID(atomIndex, (int) x);
1004           break;
1005         case TAINT_ATOMTYPE:
1006           setAtomType(atomIndex, tokens[pt]);
1007           break;
1008         case TAINT_CHAIN:
1009           setChainID(atomIndex, tokens[pt]);
1010           break;
1011         case TAINT_ELEMENT:
1012           atom.setAtomicAndIsotopeNumber((int)x);
1013           atom.paletteID = PAL.CPK.id;
1014           atom.colixAtom = vwr.cm.getColixAtomPalette(atom, PAL.CPK.id);
1015           break;
1016         case TAINT_FORMALCHARGE:
1017           atom.setFormalCharge((int)x);
1018           break;
1019         case TAINT_HYDROPHOBICITY:
1020           setHydrophobicity(atomIndex, x);
1021           break;
1022         case TAINT_BONDINGRADIUS:
1023           setBondingRadius(atomIndex, x);
1024           break;
1025         case TAINT_PARTIALCHARGE:
1026           setPartialCharge(atomIndex, x, true);
1027           break;
1028         case TAINT_TEMPERATURE:
1029           setBFactor(atomIndex, x, true);
1030           break;
1031         case TAINT_VALENCE:
1032           atom.setValence((int)x);
1033           break;
1034         case TAINT_VANDERWAALS:
1035           atom.setRadius(x);
1036           break;
1037         }
1038         taintAtom(atomIndex, type);
1039       }
1040       if (type == TAINT_MAX && n > 0)
1041         vwr.setData(name, new Object[] {name, fData, bs, Integer.valueOf(JmolDataManager.DATA_TYPE_AF)}, 0, 0, 0, 0, 0);
1042 
1043     } catch (Exception e) {
1044       Logger.error("AtomCollection.loadData error: " + e);
1045     }
1046   }
1047 
loadCoordinates(String data, boolean isVibrationVectors, boolean doTaint)1048   private void loadCoordinates(String data, boolean isVibrationVectors, boolean doTaint) {
1049     int[] lines = Parser.markLines(data, ';');
1050     V3 v = (isVibrationVectors ? new V3() : null);
1051     try {
1052       int nData = PT.parseInt(data.substring(0, lines[0] - 1));
1053       for (int i = 1; i <= nData; i++) {
1054         String[] tokens = PT.getTokens(PT.parseTrimmed(data.substring(
1055             lines[i], lines[i + 1])));
1056         int atomIndex = PT.parseInt(tokens[0]) - 1;
1057         // JavaScript will not be exact, and we will be checking for the exact value
1058         // Modulation setting modScale
1059         float x = (tokens[3].equalsIgnoreCase("1.4E-45") ? 1.4e-45f : PT.parseFloat(tokens[3]));
1060         float y = (tokens[4].equalsIgnoreCase("1.4E-45") ? 1.4e-45f : PT.parseFloat(tokens[4]));
1061         float z = PT.parseFloat(tokens[5]);
1062         if (isVibrationVectors) {
1063           v.set(x, y, z);
1064           setAtomVibrationVector(atomIndex, v);
1065         } else {
1066           setAtomCoord(atomIndex, x, y, z);
1067           if (!doTaint)
1068             untaint(atomIndex, TAINT_COORD);
1069         }
1070       }
1071     } catch (Exception e) {
1072       Logger.error("Frame.loadCoordinate error: " + e);
1073     }
1074   }
1075 
validateBspf(boolean isValid)1076   public void validateBspf(boolean isValid) {
1077     if (bspf != null)
1078       bspf.isValid = isValid;
1079     //averageAtomPoint = null;
1080   }
1081 
validateBspfForModel(int modelIndex, boolean isValid)1082   void validateBspfForModel(int modelIndex, boolean isValid) {
1083     if (bspf != null)
1084       bspf.validateModel(modelIndex, isValid);
1085   }
1086 
1087   // state tainting
1088 
setPreserveState(boolean TF)1089   public void setPreserveState(boolean TF) {
1090     preserveState = TF;
1091   }
1092   ////  atom coordinate and property changing  //////////
1093 
getUserSettableType(String dataType)1094   public static int getUserSettableType(String dataType) {
1095     boolean isExplicit = (dataType.indexOf("property_") == 0);
1096     String check = (isExplicit ? dataType.substring(9) : dataType);
1097     for (int i = 0; i < TAINT_MAX; i++)
1098       if (userSettableValues[i].equalsIgnoreCase(check))
1099         return i;
1100     return (isExplicit ? TAINT_MAX : -1);
1101   }
1102 
getTaintedAtoms(int type)1103   public BS getTaintedAtoms(int type) {
1104     return tainted == null ? null : tainted[type];
1105   }
1106 
taintAtoms(BS bsAtoms, int type)1107   public void taintAtoms(BS bsAtoms, int type) {
1108     canSkipLoad = false;
1109     if (!preserveState)
1110       return;
1111     for (int i = bsAtoms.nextSetBit(0); i >= 0; i = bsAtoms.nextSetBit(i + 1))
1112       taintAtom(i, type);
1113   }
1114 
taintAtom(int atomIndex, int type)1115   public void taintAtom(int atomIndex, int type) {
1116     if (preserveState) {
1117       if (tainted == null)
1118         tainted = new BS[TAINT_MAX];
1119       if (tainted[type] == null)
1120         tainted[type] = BS.newN(ac);
1121       tainted[type].set(atomIndex);
1122     }
1123     if (type == TAINT_COORD)
1124       taintModelCoord(atomIndex);
1125   }
1126 
taintModelCoord(int atomIndex)1127   private void taintModelCoord(int atomIndex) {
1128     Model m = ((ModelSet) this).am[at[atomIndex].mi];
1129     validateBspfForModel(m.trajectoryBaseIndex, false);
1130     if (m.isBioModel)
1131       m.resetDSSR(true);
1132     pointGroup = null;
1133   }
1134 
untaint(int atomIndex, int type)1135   private void untaint(int atomIndex, int type) {
1136     if (!preserveState)
1137       return;
1138     if (tainted == null || tainted[type] == null)
1139       return;
1140     tainted[type].clear(atomIndex);
1141   }
1142 
setTaintedAtoms(BS bs, int type)1143   public void setTaintedAtoms(BS bs, int type) {
1144     if (preserveState) {
1145       if (bs == null) {
1146         if (tainted == null)
1147           return;
1148         tainted[type] = null;
1149         return;
1150       }
1151       if (tainted == null)
1152         tainted = new BS[TAINT_MAX];
1153       if (tainted[type] == null)
1154         tainted[type] = BS.newN(ac);
1155       BSUtil.copy2(bs, tainted[type]);
1156     }
1157     if (type == TAINT_COORD) {
1158       int i = bs.nextSetBit(0);
1159       if (i >= 0)
1160         taintModelCoord(i);
1161     }
1162 
1163   }
1164 
unTaintAtoms(BS bs, int type)1165   public void unTaintAtoms(BS bs, int type) {
1166     if (tainted == null || tainted[type] == null)
1167       return;
1168     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1))
1169       tainted[type].clear(i);
1170     if (tainted[type].nextSetBit(0) < 0)
1171       tainted[type] = null;
1172   }
1173 
1174   ///////////////////////////////////////////
1175 
1176   /*
1177    * generalized; not just balls
1178    *
1179    * This algorithm assumes that atoms are circles at the z-depth
1180    * of their center point. Therefore, it probably has some flaws
1181    * around the edges when dealing with intersecting spheres that
1182    * are at approximately the same z-depth.
1183    * But it is much easier to deal with than trying to actually
1184    * calculate which atom was clicked
1185    *
1186    * A more general algorithm of recording which object drew
1187    * which pixel would be very expensive and not worth the trouble
1188    */
findNearest2(int x, int y, Atom[] closest, BS bsNot, int min)1189   protected void findNearest2(int x, int y, Atom[] closest, BS bsNot, int min) {
1190     Atom champion = null;
1191     Atom contender;
1192     for (int i = ac; --i >= 0;) {
1193       if (bsNot != null && bsNot.get(i) || (contender = at[i]) == null)
1194         continue;
1195       if (contender.isClickable()
1196           && isCursorOnTopOf(contender, x, y, min,
1197               champion))
1198         champion = contender;
1199     }
1200     closest[0] = champion;
1201   }
1202 
1203   /**
1204    * used by Frame and AminoMonomer and NucleicMonomer -- does NOT check for clickability
1205    * @param contender
1206    * @param x
1207    * @param y
1208    * @param radius
1209    * @param champion
1210    * @return true if user is pointing to this atom
1211    */
isCursorOnTopOf(Atom contender, int x, int y, int radius, Atom champion)1212   boolean isCursorOnTopOf(Atom contender, int x, int y, int radius,
1213                           Atom champion) {
1214     return contender.sZ > 1 && !g3d.isClippedZ(contender.sZ)
1215         && g3d.isInDisplayRange(contender.sX, contender.sY)
1216         && contender.isCursorOnTopOf(x, y, radius, champion);
1217   }
1218 
fillADa(AtomData atomData, int mode)1219   protected void fillADa(AtomData atomData, int mode) {
1220     atomData.xyz = atomData.atoms = at;
1221     atomData.ac = ac;
1222     atomData.atomicNumber = new int[ac];
1223     boolean includeRadii = ((mode & AtomData.MODE_FILL_RADII) != 0);
1224     if (includeRadii)
1225       atomData.atomRadius = new float[ac];
1226     boolean isMultiModel = ((mode & AtomData.MODE_FILL_MULTIMODEL) != 0);
1227     for (int i = 0; i < ac; i++) {
1228       Atom atom = at[i];
1229       if (atom == null || atom.isDeleted() || !isMultiModel && atomData.modelIndex >= 0
1230           && atom.mi != atomData.firstModelIndex) {
1231         if (atomData.bsIgnored == null)
1232           atomData.bsIgnored = new BS();
1233         atomData.bsIgnored.set(i);
1234         continue;
1235       }
1236       atomData.atomicNumber[i] = atom.getElementNumber();
1237       atomData.lastModelIndex = atom.mi;
1238       if (includeRadii)
1239         atomData.atomRadius[i] = getWorkingRadius(atom, atomData);
1240     }
1241   }
1242 
1243   ////// hybridization ///////////
1244 
1245   @SuppressWarnings("incomplete-switch")
getWorkingRadius(Atom atom, AtomData atomData)1246   private float getWorkingRadius(Atom atom, AtomData atomData) {
1247     float r = 0;
1248     RadiusData rd = atomData.radiusData;
1249     switch (rd.factorType) {
1250     case ABSOLUTE:
1251       r = rd.value;
1252       break;
1253     case FACTOR:
1254     case OFFSET:
1255       switch (rd.vdwType) {
1256       case BONDING:
1257         r = atom.getBondingRadius();
1258         break;
1259       case ADPMAX:
1260         r = atom.getADPMinMax(true);
1261         break;
1262       case ADPMIN:
1263         r = atom.getADPMinMax(false);
1264         break;
1265       default:
1266         r = atom.getVanderwaalsRadiusFloat(vwr,
1267             atomData.radiusData.vdwType);
1268       }
1269       if (rd.factorType == EnumType.FACTOR)
1270         r *= rd.value;
1271       else
1272         r += rd.value;
1273     }
1274     return r + rd.valueExtended;
1275   }
1276 
1277   public final static int CALC_H_DOALL = 0x100;
1278   public final static int CALC_H_JUSTC = 0x200;
1279   public final static int CALC_H_HAVEH = 0x400;
1280   public final static int CALC_H_QUICK = Viewer.MIN_QUICK;
1281   public static final int CALC_H_IGNORE_H = 0x800;
1282   public static final int CALC_H_ALLOW_H = 0x1000;
1283 
1284 
1285   /**
1286    * get a list of potential H atom positions based on
1287    * elemental valence and formal charge
1288    *
1289    * @param bs
1290    * @param nTotal
1291    * @param vConnect
1292    * @param flags [CALC_H_DOALL | CALC_H_JUSTC | CALC_H_HAVEH | CALC_H_QUICK
1293    * @return     array of arrays of points added to specific atoms
1294    */
calculateHydrogens(BS bs, int[] nTotal, Lst<Atom> vConnect, int flags)1295   public P3[][] calculateHydrogens(BS bs, int[] nTotal,
1296                                             Lst<Atom> vConnect, int flags) {
1297     boolean doAll = ((flags & CALC_H_DOALL) == CALC_H_DOALL);
1298     boolean justCarbon = ((flags & CALC_H_JUSTC) == CALC_H_JUSTC);
1299     boolean isQuick = ((flags & CALC_H_QUICK) == CALC_H_QUICK);
1300     boolean ignoreH = ((flags & CALC_H_IGNORE_H) == CALC_H_IGNORE_H);
1301     boolean allowH = ((flags & CALC_H_ALLOW_H) == CALC_H_ALLOW_H);
1302     V3 z = new V3();
1303     V3 x = new V3();
1304     P3[][] hAtoms = new P3[ac][];
1305     BS bsDeleted = vwr.slm.bsDeleted;
1306     P3 pt;
1307     int nH = 0;
1308 
1309     // just not doing aldehydes here -- all A-X-B bent == sp3 for now
1310     if (bs != null)
1311       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
1312         if (bsDeleted != null && bsDeleted.get(i))
1313           continue;
1314         Atom atom = at[i];
1315         int atomicNumber = atom.getElementNumber();
1316         if (justCarbon && atomicNumber != 6)
1317           continue;
1318         float dHX = (atomicNumber <= 6 ? 1.1f // B, C
1319             : atomicNumber <= 10 ? 1.0f       // N, O
1320             : 1.3f);                          // S
1321         switch (atomicNumber) {
1322         case 7:
1323         case 8:
1324           dHX = 1.0f;
1325           break;
1326         case 6:
1327         }
1328         int n = (doAll || ignoreH || allowH ? atom.getCovalentHydrogenCount() : 0);
1329         if (doAll && n > 0 || ignoreH && n == 0)
1330           continue;
1331         int nMissing = getMissingHydrogenCount(atom, false);
1332         if (doAll && nMissing == 0)
1333           continue;
1334         if (!ignoreH)
1335           n = nMissing;
1336 
1337         // TODO - NPE while working with model kit after deleting and adding and dragging atoms.
1338 
1339         int targetValence = aaRet[0];
1340         int hybridization = aaRet[2];
1341         int nBonds = aaRet[3] - (ignoreH ? n : 0);
1342         if (nBonds == 0 && atom.isHetero())
1343           continue; // no adding to water
1344         hAtoms[i] = new P3[n];
1345         int hPt = 0;
1346         if (nBonds == 0) {
1347           switch (n) {
1348           case 4:
1349             // methane
1350             z.set(0.635f, 0.635f, 0.635f);
1351             pt = P3.newP(z);
1352             pt.add(atom);
1353             hAtoms[i][hPt++] = pt;
1354             if (vConnect != null)
1355               vConnect.addLast(atom);
1356             //$FALL-THROUGH$
1357           case 3:
1358             // nitrogen
1359             z.set(-0.635f, -0.635f, 0.635f);
1360             pt = P3.newP(z);
1361             pt.add(atom);
1362             hAtoms[i][hPt++] = pt;
1363             if (vConnect != null)
1364               vConnect.addLast(atom);
1365             //$FALL-THROUGH$
1366           case 2:
1367             // oxygen
1368             z.set(-0.635f, 0.635f, -0.635f);
1369             pt = P3.newP(z);
1370             pt.add(atom);
1371             hAtoms[i][hPt++] = pt;
1372             if (vConnect != null)
1373               vConnect.addLast(atom);
1374             //$FALL-THROUGH$
1375           case 1:
1376             z.set(0.635f, -0.635f, -0.635f);
1377             pt = P3.newP(z);
1378             pt.add(atom);
1379             hAtoms[i][hPt++] = pt;
1380             if (vConnect != null)
1381               vConnect.addLast(atom);
1382           }
1383         } else {
1384           switch (n) {
1385           default:
1386             break;
1387           case 3: // three bonds needed RC
1388             getHybridizationAndAxes(i, atomicNumber, z, x, "sp3b", false, true, isQuick);
1389             pt = new P3();
1390             pt.scaleAdd2(dHX, z, atom);
1391             hAtoms[i][hPt++] = pt;
1392             if (vConnect != null)
1393               vConnect.addLast(atom);
1394             getHybridizationAndAxes(i, atomicNumber, z, x, "sp3c", false, true, isQuick);
1395             pt = new P3();
1396             pt.scaleAdd2(dHX, z, atom);
1397             hAtoms[i][hPt++] = pt;
1398             if (vConnect != null)
1399               vConnect.addLast(atom);
1400             getHybridizationAndAxes(i, atomicNumber, z, x, "sp3d", false, true, isQuick);
1401             pt = new P3();
1402             pt.scaleAdd2(dHX, z, atom);
1403             hAtoms[i][hPt++] = pt;
1404             if (vConnect != null)
1405               vConnect.addLast(atom);
1406             break;
1407           case 2:
1408             // 2 bonds needed R2C or R-N or R2C=C or O
1409             //                    or RC=C or C=C
1410             boolean isEne = (hybridization == 2 || atomicNumber == 5 || nBonds == 1
1411                 && targetValence == 4
1412                 || atomicNumber == 7 && isAdjacentSp2(atom));
1413             getHybridizationAndAxes(i, atomicNumber, z, x, (isEne ? "sp2b"
1414                 : targetValence == 3 ? "sp3c" : "lpa"), false, true, isQuick);
1415             pt = P3.newP(z);
1416             pt.scaleAdd2(dHX, z, atom);
1417             hAtoms[i][hPt++] = pt;
1418             if (vConnect != null)
1419               vConnect.addLast(atom);
1420             getHybridizationAndAxes(i, atomicNumber, z, x, (isEne ? "sp2c"
1421                 : targetValence == 3 ? "sp3d" : "lpb"), false, true, isQuick);
1422             pt = P3.newP(z);
1423             pt.scaleAdd2(dHX, z, atom);
1424             hAtoms[i][hPt++] = pt;
1425             if (vConnect != null)
1426               vConnect.addLast(atom);
1427             break;
1428           case 1:
1429             // one bond needed R2B, R3C, R-N-R, R-O R=C-R R=N R-3-C
1430             // nbonds ......... 2 .. 3 .. 2 ... 1 ... 2 .. 1 .. 1
1431             // nval ........... 2 .. 3 .. 2 ... 1 ... 3 .. 2 .. 3
1432             // targetValence .. 3 .. 4 .. 3 ... 2 ... 4 .. 3 .. 4
1433             // tV - nbonds   .. 1    1    1     1     2    2    3
1434             // ................ sp2c sp3d sp3d  sp3b  sp2c sp2b sp
1435             switch (targetValence - nBonds) {
1436             case 1:
1437               // sp3 or Boron sp2 or N sp2
1438               if (atomicNumber == 8 && atom == atom.group.getCarbonylOxygenAtom()) {
1439                 hAtoms[i] = null;
1440                 continue;
1441               }
1442               if (getHybridizationAndAxes(i, atomicNumber, z, x,
1443                   (hybridization == 2 || atomicNumber == 5
1444                   || atomicNumber == 6 &&  aaRet[1] == 1
1445                   || atomicNumber == 7
1446                   && (atom.group.getNitrogenAtom() == atom & atom.getFormalCharge() == 0 || isAdjacentSp2(atom))
1447                   ? "sp2c"
1448                   : "sp3d"), true, false, isQuick) != null) {
1449                 pt = P3.newP(z);
1450                 pt.scaleAdd2(dHX, z, atom);
1451                 hAtoms[i][hPt++] = pt;
1452                 if (vConnect != null)
1453                   vConnect.addLast(atom);
1454               } else {
1455                 hAtoms[i] = new P3[0];
1456               }
1457               break;
1458             case 2:
1459               // sp2
1460               getHybridizationAndAxes(i, atomicNumber, z, x, (targetValence == 4 ? "sp2c"
1461                   : "sp2b"), false, false, isQuick);
1462               pt = P3.newP(z);
1463               pt.scaleAdd2(dHX, z, atom);
1464               hAtoms[i][hPt++] = pt;
1465               if (vConnect != null)
1466                 vConnect.addLast(atom);
1467               break;
1468             case 3:
1469               // sp
1470               getHybridizationAndAxes(i, atomicNumber, z, x, "spb", false, true, isQuick);
1471               pt = P3.newP(z);
1472               pt.scaleAdd2(dHX, z, atom);
1473               hAtoms[i][hPt++] = pt;
1474               if (vConnect != null)
1475                 vConnect.addLast(atom);
1476               break;
1477             }
1478           }
1479         }
1480         nH += hPt;
1481       }
1482     nTotal[0] = nH;
1483     return hAtoms;
1484   }
1485 
isAdjacentSp2(Atom atom)1486   private boolean isAdjacentSp2(Atom atom) {
1487     Bond[] bonds = atom.bonds;
1488     for (int i = 0; i < bonds.length; i++) {
1489       Bond[] b2 = bonds[i].getOtherAtom(atom).bonds;
1490       for (int j = 0; j < b2.length; j++)
1491         switch (b2[j].order) {
1492         case Edge.BOND_AROMATIC:
1493         case Edge.BOND_AROMATIC_DOUBLE:
1494         case Edge.BOND_COVALENT_DOUBLE:
1495         case Edge.BOND_COVALENT_TRIPLE:
1496           return true;
1497         }
1498     }
1499     return false;
1500   }
1501 
1502   int[] aaRet;
1503 
getMissingHydrogenCount(Atom atom, boolean allowNegative)1504   public int getMissingHydrogenCount(Atom atom, boolean allowNegative) {
1505     int targetCount = atom.getTargetValence();
1506     if (targetCount < 0)
1507       return 0;
1508     int charge = atom.getFormalCharge();
1509     int valence = atom.getValence();
1510     Model model = ((ModelSet) this).am[atom.mi];
1511     String s = (model.isBioModel && !model.isPdbWithMultipleBonds ? atom.group.getGroup3() : null);
1512     if (aaRet == null)
1513       aaRet = new int[5];
1514     aaRet[0] = targetCount;
1515     aaRet[1] = charge;
1516     aaRet[2] = 0; // hybridization
1517     aaRet[3] = atom.getCovalentBondCount();
1518     aaRet[4] = (s == null ? 0 : valence);
1519     if (s != null && charge == 0) {
1520       if (bioModelset.getAminoAcidValenceAndCharge(s, atom.getAtomName(),
1521           aaRet)) {
1522         targetCount = aaRet[0];
1523         charge = aaRet[1];
1524       }
1525     }
1526     if (charge != 0) {
1527       targetCount += (targetCount == 4 ? -Math.abs(charge) : charge);
1528       aaRet[0] = targetCount;
1529     }
1530     int n = targetCount - valence;
1531     return (n < 0 && !allowNegative ? 0 : n);
1532   }
1533 
fixFormalCharges(BS bs)1534   public int fixFormalCharges(BS bs) {
1535     int n = 0;
1536     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1)) {
1537       Atom a = at[i];
1538       int nH = getMissingHydrogenCount(a, true);
1539       if (nH != 0) {
1540 
1541           int c0 = a.getFormalCharge();
1542         int c = c0 - nH;
1543         a.setFormalCharge(c);
1544         taintAtom(i, TAINT_FORMALCHARGE);
1545         if (Logger.debugging)
1546           Logger.debug("atom " + a + " formal charge " + c0 + " -> " + c);
1547         n++;
1548       }
1549     }
1550     return n;
1551   }
1552 
getHybridizationAndAxes(int atomIndex, int atomicNumber, V3 z, V3 x, String lcaoTypeRaw, boolean hybridizationCompatible, boolean doAlignZ, boolean isQuick)1553   public String getHybridizationAndAxes(int atomIndex, int atomicNumber, V3 z, V3 x,
1554                                         String lcaoTypeRaw,
1555                                         boolean hybridizationCompatible,
1556                                         boolean doAlignZ, boolean isQuick) {
1557 
1558     String lcaoType = (lcaoTypeRaw.length() > 0 && lcaoTypeRaw.charAt(0) == '-' ? lcaoTypeRaw
1559         .substring(1)
1560         : lcaoTypeRaw);
1561 
1562     if (lcaoTypeRaw.indexOf("d") >= 0 && !lcaoTypeRaw.endsWith("sp3d"))
1563       return getHybridizationAndAxesD(atomIndex, z, x, lcaoType);
1564 
1565     Atom atom = at[atomIndex];
1566     if (atomicNumber == 0)
1567       atomicNumber = atom.getElementNumber();
1568     Atom[] attached = getAttached(atom, 4, hybridizationCompatible, isQuick);
1569     int nAttached = attached.length;
1570     int pt = lcaoType.charAt(lcaoType.length() - 1) - 'a';
1571     if (pt < 0 || pt > 6)
1572       pt = 0;
1573     z.set(0, 0, 0);
1574     x.set(0, 0, 0);
1575     V3[] v = new V3[4];
1576     for (int i = 0; i < nAttached; i++) {
1577       Atom a = attached[i];
1578       if (a == null) {
1579         nAttached = i;
1580         break;
1581       }
1582       v[i] = V3.newVsub(atom, a);
1583       v[i].normalize();
1584       z.add(v[i]);
1585     }
1586     if (nAttached > 0)
1587       x.setT(v[0]);
1588     boolean isPlanar = false;
1589     V3 vTemp = new V3();
1590     if (nAttached >= 3) {
1591       if (x.angle(v[1]) < almost180)
1592         vTemp.cross(x, v[1]);
1593       else
1594         vTemp.cross(x, v[2]);
1595       vTemp.normalize();
1596       V3 vTemp2 = new V3();
1597       if (v[1].angle(v[2]) < almost180)
1598         vTemp2.cross(v[1], v[2]);
1599       else
1600         vTemp2.cross(x, v[2]);
1601       vTemp2.normalize();
1602       isPlanar = (Math.abs(vTemp2.dot(vTemp)) >= 0.95f);
1603     }
1604 
1605     boolean isSp3 = (lcaoType.indexOf("sp3") == 0);
1606     boolean isSp2 = (!isSp3 && lcaoType.indexOf("sp2") == 0);
1607     boolean isSp = (!isSp3 && !isSp2 && lcaoType.indexOf("sp") == 0);
1608     boolean isP = (lcaoType.indexOf("p") == 0);
1609     boolean isLp = (lcaoType.indexOf("lp") == 0);
1610 
1611     String hybridization = null;
1612     if (hybridizationCompatible) {
1613       if (nAttached == 0)
1614         return null;
1615       if (isSp3) {
1616         if (pt > 3 || nAttached > 4)
1617           return null;
1618       } else if (isSp2) {
1619         if (pt > 2 || nAttached > 3)
1620           return null;
1621       } else if (isSp) {
1622         if (pt > 1 || nAttached > 2)
1623           return null;
1624       }
1625       switch (nAttached) {
1626       case 1:
1627         if (atomicNumber == 1 && !isSp3)
1628           return null;
1629         if (isSp3) {
1630           hybridization = "sp3";
1631           break;
1632         }
1633         switch (attached[0].getCovalentBondCount()) {
1634         case 1:
1635           if (attached[0].getValence() != 2) {
1636             // C-t-C
1637             hybridization = "sp";
1638             break;
1639           }
1640           // C=C, no other atoms
1641           //$FALL-THROUGH$
1642         case 2:
1643           hybridization = (isSp ? "sp" : "sp2");
1644           break;
1645         case 3:
1646           // special case, for example R2C=O oxygen
1647           if (!isSp2 && !isP)
1648             return null;
1649           hybridization = "sp2";
1650           break;
1651         }
1652         break;
1653       case 2:
1654         if (z.length() < 0.1f) {
1655           // linear A--X--B
1656           if (lcaoType.indexOf("2") >= 0 || lcaoType.indexOf("3") >= 0)
1657             return null;
1658           hybridization = "sp";
1659           break;
1660         }
1661         // bent A--X--B
1662         hybridization = (isSp3 ? "sp3" : "sp2");
1663         if (lcaoType.indexOf("sp") == 0) { // align z as sp2 orbital
1664           break;
1665         }
1666         if (isLp) { // align z as lone pair
1667           hybridization = "lp"; // any is OK
1668           break;
1669         }
1670         hybridization = lcaoType;
1671         break;
1672       default:
1673         // 3 or 4 bonds
1674         if (isPlanar && !isQuick) {
1675           hybridization = "sp2";
1676         } else {
1677           if (isPlanar)
1678             z.setT(vTemp);
1679           if (isLp && nAttached == 3) {
1680             hybridization = "lp";
1681             break;
1682           }
1683           hybridization = "sp3";
1684         }
1685       }
1686       if (hybridization == null)
1687         return null;
1688       if (lcaoType.indexOf("p") == 0) {
1689         if (hybridization == "sp3")
1690           return null;
1691       } else if (lcaoType.indexOf(hybridization) < 0) {
1692         return null;
1693       }
1694     }
1695 
1696     if (pt < nAttached && !lcaoType.startsWith("p")
1697         && !lcaoType.startsWith("l")) {
1698       z.sub2(attached[pt], atom);
1699       z.normalize();
1700       return hybridization;
1701     }
1702 
1703     switch (nAttached) {
1704     case 0:
1705       if (lcaoType.equals("sp3c") || lcaoType.equals("sp2d")
1706           || lcaoType.equals("lpa")) {
1707         z.set(-0.5f, -0.7f, 1);
1708         x.set(1, 0, 0);
1709       } else if (lcaoType.equals("sp3b") || lcaoType.equals("lpb")) {
1710         z.set(0.5f, -0.7f, -1f);
1711         x.set(1, 0, 0);
1712       } else if (lcaoType.equals("sp3a")) {
1713         z.set(0, 1, 0);
1714         x.set(1, 0, 0);
1715       } else {
1716         z.set(0, 0, 1);
1717         x.set(1, 0, 0);
1718       }
1719       break;
1720     case 1:
1721       // X-C
1722       vTemp.setT(vRef);
1723       x.cross(vTemp, z);
1724       if (isSp3) {
1725         // align z as sp3 orbital
1726         // with reference to atoms connected to connecting atom.
1727         // vRef is a pseudo-random vector
1728         // z is along the bond
1729         for (int i = attached[0].getBondCount(); --i >= 0;) {
1730           if (attached[0].bonds[i].isCovalent()
1731               && attached[0].getBondedAtomIndex(i) != atom.i) {
1732             x.sub2(attached[0], attached[0].bonds[i].getOtherAtom(attached[0]));
1733             x.cross(z, x);
1734             if (x.length() == 0)
1735               continue;
1736             x.cross(x, z);
1737             break;
1738           }
1739         }
1740         x.normalize();
1741         if (Float.isNaN(x.x)) {
1742           x.setT(vRef);
1743           x.cross(x, z);
1744         }
1745         // x is perp to bond
1746         vTemp.cross(z, x);
1747         vTemp.normalize();
1748         // y1 is perp to bond and x
1749         z.normalize();
1750         x.scaleAdd2(2.828f, x, z); // 2*sqrt(2)
1751         if (pt != 3) {
1752           x.normalize();
1753           // PI*2/3
1754           new M3().setAA(A4.new4(z.x, z.y, z.z,
1755               (pt == 2 ? 1 : -1) * 2.09439507f)).rotate(x);
1756         }
1757         z.setT(x);
1758         x.cross(vTemp, z);
1759         break;
1760       }
1761       // not "sp3" -- sp2 or lone pair
1762       vTemp.cross(x, z); //x and vTemp are now perpendicular to z
1763       switch (attached[0].getCovalentBondCount()) {
1764       case 1:
1765         if (attached[0].getValence() != 2) {
1766           // C-t-C
1767           break;
1768         }
1769         // C=C, no other atoms
1770         //$FALL-THROUGH$
1771       case 2:
1772         // R-C=C* or C=C=C*
1773         // get third atom
1774         boolean isCumulated = false;
1775         Atom a0 = attached[0];
1776         x.setT(z);
1777         vTemp.setT(vRef);
1778         while (a0 != null && a0.getCovalentBondCount() == 2) {
1779           Bond[] bonds = a0.bonds;
1780           Atom a = null;
1781           isCumulated = !isCumulated;
1782           for (int i = 0; i < bonds.length; i++)
1783             if (bonds[i].isCovalent()) {
1784               a = bonds[i].getOtherAtom(a0);
1785               if (a != atom) {
1786                 vTemp.sub2(a, a0);
1787                 break;
1788               }
1789             }
1790           vTemp.cross(vTemp, x);
1791           if (vTemp.length() > 0.1f || a.getCovalentBondCount() != 2)
1792             break;
1793           atom = a0;
1794           a0 = a;
1795         }
1796         if (isSp) {
1797         } else if (vTemp.length() > 0.1f) {
1798           z.cross(vTemp, x);
1799           // C=C or RC=C
1800           z.normalize();
1801           if (pt == 1)
1802             z.scale(-1);
1803           z.scale(sqrt3_2);
1804           z.scaleAdd2(0.5f, x, z);
1805           if (isP) {
1806             vTemp.cross(z, x);
1807             z.setT(vTemp);
1808             vTemp.setT(x);
1809           }
1810           x.cross(vTemp, z);
1811         } else {
1812           z.setT(x);
1813           x.cross(vRef, x);
1814         }
1815         break;
1816       case 3:
1817         // special case, for example R2C=O oxygen
1818         getHybridizationAndAxes(attached[0].i, 0, x, vTemp, "pz", false,
1819             doAlignZ, isQuick);
1820         vTemp.setT(x);
1821         if (isSp2) { // align z as sp2 orbital
1822           x.cross(x, z);
1823           if (pt == 1)
1824             x.scale(-1);
1825           x.scale(sqrt3_2);
1826           z.scaleAdd2(0.5f, z, x);
1827         } else if (isSp) {
1828 
1829         } else {
1830           vTemp.setT(z);
1831           z.setT(x);
1832         }
1833         x.cross(vTemp, z);
1834         break;
1835       }
1836       break;
1837     case 2:
1838       // two attached atoms -- check for linearity
1839       if (z.length() < 0.1f) {
1840         // linear A--X--B
1841         if (!lcaoType.equals("pz")) {
1842           Atom a = attached[0];
1843           boolean ok = (a.getCovalentBondCount() == 3);
1844           if (!ok)
1845             ok = ((a = attached[1]).getCovalentBondCount() == 3);
1846           if (ok) {
1847             // special case, for example R2C=C=CR2 central carbon
1848             getHybridizationAndAxes(a.i, 0, x, z, "pz", false, doAlignZ, isQuick);
1849             if (lcaoType.equals("px"))
1850               x.scale(-1);
1851             z.setT(v[0]);
1852             break;
1853           }
1854           // O-C*-O
1855           vTemp.setT(vRef);
1856           z.cross(vTemp, x);
1857           vTemp.cross(z, x);
1858         }
1859         z.setT(x);
1860         x.cross(vTemp, z);
1861         break;
1862       }
1863       // bent A--X--B
1864       vTemp.cross(z, x);
1865       if (isSp2) { // align z as sp2 orbital
1866         x.cross(z, vTemp);
1867         break;
1868       }
1869       if (isSp3 || isLp) { // align z as lone pair
1870         vTemp.normalize();
1871         z.normalize();
1872         if (!lcaoType.equals("lp")) {
1873           if (pt == 0 || pt == 2)
1874             z.scaleAdd2(-1.2f, vTemp, z);
1875           else
1876             z.scaleAdd2(1.2f, vTemp, z);
1877         }
1878         x.cross(z, vTemp);
1879         break;
1880       }
1881       // align z as p orbital
1882       x.cross(z, vTemp);
1883       z.setT(vTemp);
1884       if (z.z < 0) {
1885         z.scale(-1);
1886         x.scale(-1);
1887       }
1888       break;
1889     default:
1890       // 3 bonds, sp3 or sp2 and lp/p
1891       if (isSp3)
1892         break;
1893       if (!isPlanar) {
1894         // not aligned -- really sp3
1895         x.cross(z, x);
1896         break;
1897       }
1898       // align z as p orbital
1899       z.setT(vTemp);
1900       if (z.z < 0 && doAlignZ) {
1901         z.scale(-1);
1902         x.scale(-1);
1903       }
1904     }
1905 
1906     x.normalize();
1907     z.normalize();
1908 
1909     return hybridization;
1910   }
1911 
1912   /**
1913    * dsp3 (trigonal bipyramidal, see-saw, T-shaped)
1914    * or d2sp3 (square planar, square pyramidal, octahedral)
1915    *
1916    * @param atomIndex
1917    * @param z
1918    * @param x
1919    * @param lcaoType
1920    * @return valid hybridization or null
1921    */
getHybridizationAndAxesD(int atomIndex, V3 z, V3 x, String lcaoType)1922   private String getHybridizationAndAxesD(int atomIndex, V3 z, V3 x,
1923                                          String lcaoType) {
1924     // note -- d2sp3, not sp3d2; dsp3, not sp3d
1925     if (lcaoType.startsWith("sp3d2"))
1926       lcaoType = "d2sp3"
1927           + (lcaoType.length() == 5 ? "a" : lcaoType.substring(5));
1928     if (lcaoType.startsWith("sp3d"))
1929       lcaoType = "dsp3"
1930           + (lcaoType.length() == 4 ? "a" : lcaoType.substring(4));
1931     if (lcaoType.equals("d2sp3") || lcaoType.equals("dsp3"))
1932       lcaoType += "a";
1933     boolean isTrigonal = lcaoType.startsWith("dsp3");
1934     int pt = lcaoType.charAt(lcaoType.length() - 1) - 'a';
1935     if (z != null && (!isTrigonal && (pt > 5 || !lcaoType.startsWith("d2sp3"))
1936         || isTrigonal && pt > 4))
1937       return null;
1938 
1939     // pt: a 0   b 1   c 2   d 3   e 4   f 5
1940 
1941     Atom atom = at[atomIndex];
1942     Atom[] attached = getAttached(atom, 6, true, false);
1943     if (attached == null)
1944       return (z == null ? null : "?");
1945     int nAttached = attached.length;
1946     if (nAttached < 3 && z != null)
1947       return null;
1948     boolean isLP = (pt >= nAttached);
1949 
1950     // determine geometry
1951 
1952     int nAngles = nAttached * (nAttached - 1) / 2;
1953     int[][] angles = AU.newInt2(nAngles);
1954 
1955     // all attached angles must be around 180, 120, or 90 degrees
1956 
1957     int[] ntypes = new int[3];
1958     int[][] typePtrs = new int[3][nAngles];
1959 
1960     int n = 0;
1961     int _90 = 0;
1962     int _120 = 1;
1963     int _180 = 2;
1964     int n120_atom0 = 0;
1965     for (int i = 0; i < nAttached - 1; i++)
1966       for (int j = i + 1; j < nAttached; j++) {
1967         float angle = Measure
1968             .computeAngleABC(attached[i], atom, attached[j], true);
1969         // cutoffs determined empirically and meant to be generous
1970         int itype = (angle < 105 ? _90 : angle >= 150 ? _180 : _120);
1971         typePtrs[itype][ntypes[itype]] = n;
1972         ntypes[itype]++;
1973         angles[n++] = new int[] { i, j };
1974         if (i == 0 && itype == _120)
1975           n120_atom0++;
1976       }
1977     // categorization is done simply by listing
1978     // the number of 90, 120, and 180 angles.
1979     n = ntypes[_90] * 100 + ntypes[_120] * 10 + ntypes[_180];
1980     if (z == null) {
1981       // just return geometry
1982       switch (n) {
1983       default:
1984         return "";
1985       case 0:
1986         return "";// just ignore atoms with only one bond? (atom.getElementNumber() == 1 ? "s" : "");
1987       case 1:
1988         return "linear";
1989       case 100:
1990       case 10:
1991         return "bent";
1992       case 111:
1993       case 201:
1994         return "T-shaped";// -- AX3E or AX3E2 or AX3E3
1995       case 30:
1996       case 120:
1997       case 210:
1998       case 300:
1999         if (Math.abs(Measure.computeTorsion(attached[0], atom, attached[1], attached[2], true)) > 162)
2000             return "trigonal planar";// -- AX3
2001         return "trigonal pyramidal";// -- AX3E
2002       case 330:
2003         // may just have a rather distorted tetrahedron, as in "$phosphorus pentoxide"
2004         // in that case, each atom will have 1 or 3 120o angles, not 0 or 2, as in trigonal pyramid
2005         return (n120_atom0 % 2 == 1 ? "tetrahedral" : "uncapped trigonal pyramid");// -- AX4 or AX4E
2006       case 60:
2007       case 150:
2008       case 240:
2009         return "tetrahedral";// -- AX4
2010       case 402:
2011         return "square planar";// -- AX4E2
2012       case 411:
2013       case 501:
2014         return "see-saw";// -- AX4E
2015       case 631:
2016         return "trigonal bipyramidal";// -- AX5
2017       case 802:
2018         return "uncapped square pyramid";// -- AX5E
2019       case 1203:
2020         return "octahedral";// -- AX6
2021       }
2022     }
2023 
2024     switch (n) {
2025     default:
2026       return null;
2027       // 111 is also possible, but quite odd
2028     case 201:
2029       // 201 T-shaped -- could be either
2030       break;
2031     case 210:
2032     case 330:
2033     case 411:
2034     case 631:
2035       // 210 no name (90-90-120)
2036       // 411 see-saw
2037       // 330 trigonal pyramid
2038       // 631 trigonal bipyramidal
2039      if (!isTrigonal)
2040        return null;
2041      break;
2042     case 300:
2043     case 402:
2044     case 501:
2045     case 802:
2046     case 1203:
2047      // 300 no name (90-90-90)
2048      // 402 square planar
2049      // 501 no name (see-saw like, but with 90o angle)
2050      // 802 square pyramidal
2051      // 1203 octahedral
2052       if (isTrigonal)
2053         return null;
2054      break;
2055     }
2056     // if subType a-f is pointing to an attached atom, use it
2057     // otherwise, we need to find the position
2058     if (isLP) {
2059       int[] a;
2060       BS bs;
2061       if (isTrigonal) {
2062         switch (ntypes[_120]) {
2063         case 0:
2064           // T-shaped
2065           z.sub2(attached[angles[typePtrs[_90][0]][0]], atom);
2066           x.sub2(attached[angles[typePtrs[_90][0]][1]], atom);
2067           z.cross(z, x);
2068           z.normalize();
2069           if (pt == 4)
2070             z.scale(-1);
2071           bs = findNotAttached(nAttached, angles, typePtrs[_180], ntypes[_180]);
2072           int i = bs.nextSetBit(0);
2073           x.sub2(attached[i], atom);
2074           x.normalize();
2075           x.scale(0.5f);
2076           z.scaleAdd2(sqrt3_2, z, x);
2077           pt = -1;
2078           break;
2079         case 1:
2080           // see-saw
2081           if (pt == 4) {
2082             a = angles[typePtrs[_120][0]];
2083             z.add2(attached[a[0]], attached[a[1]]);
2084             z.scaleAdd2(-2, atom, z);
2085             pt = -1;
2086           } else {
2087             bs = findNotAttached(nAttached, angles, typePtrs[_120], ntypes[_120]);
2088             pt = bs.nextSetBit(0);
2089           }
2090           break;
2091         default:
2092           // unobserved nor-apical trigonal bipyramid
2093           // or highly distorted trigonal pyramid (PH3)
2094           bs = findNotAttached(nAttached, angles, typePtrs[_120], ntypes[_120]);
2095           pt = bs.nextSetBit(0);
2096         }
2097       } else {
2098         boolean isPlanar = false;
2099         if (nAttached == 4) {
2100           switch (ntypes[_180]) {
2101           case 1:
2102             // unobserved cis-nor-octahedron
2103             bs = findNotAttached(nAttached, angles, typePtrs[_180],
2104                 ntypes[_180]);
2105             int i = bs.nextSetBit(0);
2106             if (pt == 4)
2107               pt = i;
2108             else
2109               pt = bs.nextSetBit(i + 1);
2110             break;
2111           default:
2112             // square planar
2113             isPlanar = true;
2114           }
2115         } else {
2116           // square pyramidal
2117           bs = findNotAttached(nAttached, angles, typePtrs[_180], ntypes[_180]);
2118           int i = bs.nextSetBit(0);
2119           for (int j = nAttached; j < pt && i >= 0; j++)
2120             i = bs.nextSetBit(i + 1);
2121           if (i == -1)
2122             isPlanar = true;
2123           else
2124             pt = i;
2125         }
2126         if (isPlanar) {
2127           // square planar or T-shaped
2128           z.sub2(attached[angles[typePtrs[_90][0]][0]], atom);
2129           x.sub2(attached[angles[typePtrs[_90][0]][1]], atom);
2130           z.cross(z, x);
2131           if (pt == 4)
2132             z.scale(-1);
2133           pt = -1;
2134         }
2135       }
2136     }
2137     if (pt >= 0)
2138       z.sub2(attached[pt], atom);
2139     if (isLP)
2140       z.scale(-1);
2141     z.normalize();
2142     return (isTrigonal ? "dsp3" : "d2sp3");
2143   }
2144 
getAttached(Atom atom, int nMax, boolean doSort, boolean isQuick)2145   private Atom[] getAttached(Atom atom, int nMax, boolean doSort, boolean isQuick) {
2146     int nAttached = atom.getCovalentBondCount();
2147     if (nAttached > nMax)
2148       return null;
2149     Atom[] attached = new Atom[nAttached];
2150     if (nAttached > 0) {
2151       Bond[] bonds = atom.bonds;
2152       int n = 0;
2153       for (int i = 0; i < bonds.length; i++)
2154         if (bonds[i].isCovalent()) {
2155           Atom a = bonds[i].getOtherAtom(atom);
2156           if (!isQuick || a.getAtomicAndIsotopeNumber() != 1)
2157             attached[n++] = a;
2158         }
2159       if (doSort && !isQuick)
2160         Arrays.sort(attached, new AtomSorter());
2161     }
2162     return attached;
2163   }
2164 
findNotAttached(int nAttached, int[][] angles, int[] ptrs, int nPtrs)2165   private BS findNotAttached(int nAttached, int[][] angles, int[] ptrs, int nPtrs) {
2166     BS bs = BS.newN(nAttached);
2167     bs.setBits(0, nAttached);
2168     for (int i = 0; i < nAttached; i++)
2169       for (int j = 0; j < nPtrs; j++) {
2170         int[] a = angles[ptrs[j]];
2171         if (a[0] == i || a[1] == i)
2172           bs.clear(i);
2173       }
2174     return bs;
2175   }
2176 
2177   protected class AtomSorter implements Comparator<Atom>{
2178     @Override
compare(Atom a1, Atom a2)2179     public int compare(Atom a1, Atom a2) {
2180       return (a1.i > a2.i ? 1 : a1.i < a2.i ? -1 : 0);
2181     }
2182   }
2183 
2184   /*
2185    * ******************************************************
2186    *
2187    * These next methods are used by Eval to select for specific atom sets. They
2188    * all return a BitSet
2189    *
2190    * ******************************************************
2191    */
2192 
2193   /**
2194    * general unqualified lookup of atom set type
2195    *
2196    * @param tokType
2197    * @param specInfo
2198    * @param bs
2199    *        - to be filled
2200    * @return BitSet; or null if we mess up the type
2201    */
getAtomBitsMDa(int tokType, Object specInfo, BS bs)2202   public BS getAtomBitsMDa(int tokType, Object specInfo, BS bs) {
2203     int iSpec = (specInfo instanceof Integer ? ((Integer) specInfo).intValue()
2204         : 0);
2205 
2206     switch (tokType) {
2207     case T.atomname:
2208     case T.atomtype:
2209       // within(atomname
2210       // within(atomtype
2211       boolean isType = (tokType == T.atomtype);
2212       String names = "," + specInfo + ",";
2213       for (int i = ac; --i >= 0;) {
2214         if (at[i] == null)
2215           continue;
2216         String s = (isType ? at[i].getAtomType() : at[i].getAtomName());
2217         if (names.indexOf("," + s + ",") >= 0)
2218           bs.set(i);
2219       }
2220       return bs;
2221     case T.atomno:
2222       for (int i = ac; --i >= 0;) {
2223         if (at[i] != null && at[i].getAtomNumber() == iSpec)
2224           bs.set(i);
2225       }
2226       return bs;
2227     case T.bonded:
2228       for (int i = ac; --i >= 0;) {
2229         if (at[i] != null && at[i].getCovalentBondCount() > 0)
2230           bs.set(i);
2231       }
2232       return bs;
2233     case T.carbohydrate:
2234     case T.dna:
2235     case T.helix: // WITHIN -- not ends
2236     case T.nucleic:
2237     case T.protein:
2238     case T.purine:
2239     case T.pyrimidine:
2240     case T.rna:
2241     case T.sheet: // WITHIN -- not ends
2242       return (((ModelSet)this).haveBioModels ? ((ModelSet)this).bioModelset.getAtomBitsBS(tokType, null, bs) : bs);
2243     case T.hydrogen:
2244       iSpec = 1;
2245       //$FALL-THROUGH$
2246     case T.elemno:
2247       for (int i = ac; --i >= 0;) {
2248         if (at[i] != null && at[i].getElementNumber() == iSpec)
2249           bs.set(i);
2250       }
2251       return bs;
2252     case T.hetero:
2253       for (int i = ac; --i >= 0;) {
2254         if (at[i] != null && at[i].isHetero())
2255           bs.set(i);
2256       }
2257       return bs;
2258     case T.identifier:
2259       return getIdentifierOrNull((String) specInfo);
2260     case T.leadatom:
2261       for (int i = ac; --i >= 0;) {
2262         if (at[i] != null && at[i].isLeadAtom())
2263           bs.set(i);
2264       }
2265       return bs;
2266     case T.polymer:
2267     case T.structure:
2268       return (((ModelSet)this).haveBioModels ? ((ModelSet)this).bioModelset.getAtomBitsBS(tokType, (BS) specInfo, bs)
2269           : bs);
2270     case T.resno:
2271       // could be PDB type file with UNK
2272       for (int i = ac; --i >= 0;) {
2273         if (at[i] != null && at[i].getResno() == iSpec)
2274           bs.set(i);
2275       }
2276       return bs;
2277     case T.solvent:
2278       // fast search for water
2279 
2280       // this is faster by a factor of 2  Jmol 14.1.12  -BH
2281 
2282       //"@water _g>=" + GROUPID_WATER + " & _g<" + GROUPID_SOLVENT_MIN
2283       //+ ", oxygen & connected(2) & connected(2, hydrogen),
2284       //(hydrogen) & connected(oxygen & connected(2) & connected(2, hydrogen))",
2285 
2286       int[] hs = new int[2];
2287       Atom a;
2288       for (int i = ac; --i >= 0;) {
2289         if (at[i] == null)
2290           continue;
2291         int g = at[i].group.groupID;
2292         if (g >= JC.GROUPID_WATER && g < JC.GROUPID_SOLVENT_MIN) {
2293           bs.set(i);
2294         } else if ((a = at[i]).getElementNumber() == 8
2295             && a.getCovalentBondCount() == 2) {
2296           Bond[] bonds = a.bonds;
2297           int n = 0;
2298           Atom b;
2299           for (int j = bonds.length; --j >= 0 && n < 3;)
2300             if (bonds[j].isCovalent()
2301                 && (b = bonds[j].getOtherAtom(a)).getElementNumber() == 1)
2302               hs[n++ % 2] = b.i;
2303           if (n == 2) {
2304             bs.set(hs[1]);
2305             bs.set(hs[0]);
2306             bs.set(i);
2307           }
2308         }
2309       }
2310       return bs;
2311     case T.spec_alternate:
2312       // could be PDB type file with UNK or a modulated CIF file subsystem
2313       String spec = (String) specInfo;
2314       for (int i = ac; --i >= 0;)
2315         if (at[i] != null && isAltLoc(at[i].altloc, spec))
2316           bs.set(i);
2317       return bs;
2318     case T.spec_atom:
2319       String atomSpec = ((String) specInfo).toUpperCase();
2320       if (atomSpec.indexOf("\\?") >= 0)
2321         atomSpec = PT.rep(atomSpec, "\\?", "\1");
2322       // / here xx*yy is NOT changed to "xx??????????yy"
2323       boolean allowStar = atomSpec.startsWith("?*");
2324       if (allowStar)
2325         atomSpec = atomSpec.substring(1);
2326       for (int i = ac; --i >= 0;)
2327         if (at[i] != null && isAtomNameMatch(at[i], atomSpec, allowStar, allowStar))
2328           bs.set(i);
2329       return bs;
2330     case T.spec_chain:
2331       return BSUtil.copy(getChainBits(iSpec));
2332     case T.spec_name_pattern:
2333       return getSpecName((String) specInfo);
2334     case T.spec_resid:
2335       // could be PDB type file with UNK
2336       for (int i = ac; --i >= 0;)
2337         if (at[i] != null && at[i].group.groupID == iSpec)
2338           bs.set(i);
2339       return bs;
2340     case T.spec_seqcode:
2341       return BSUtil.copy(getSeqcodeBits(iSpec, true));
2342     case T.inscode:
2343       for (int i = ac; --i >= 0;)
2344         if (at[i] != null && at[i].group.getInsCode() == iSpec)
2345           bs.set(i);
2346       return bs;
2347     case T.symop:
2348       for (int i = ac; --i >= 0;)
2349         if (at[i] != null && at[i].getSymOp() == iSpec)
2350           bs.set(i);
2351       return bs;
2352     }
2353 
2354     BS bsTemp;
2355     BS bsInfo = (BS) specInfo;
2356     int i0 = bsInfo.nextSetBit(0);
2357     if (i0 < 0)
2358       return bs;
2359     switch (tokType) {
2360     case T.model:
2361       // within(model
2362       bsTemp = BSUtil.copy(bsInfo);
2363       for (int i = i0; i >= 0; i = bsTemp.nextSetBit(i + 1)) {
2364         bs.or(((ModelSet) this).am[at[i].mi].bsAtoms);
2365         bsTemp.andNot(bs);
2366       }
2367       return bs;
2368     case T.chain:
2369       // within(chain
2370       bsTemp = BSUtil.copy(bsInfo);
2371       for (int i = i0; i >= 0; i = bsTemp.nextSetBit(i + 1)) {
2372         at[i].group.chain.setAtomBits(bs);
2373         bsTemp.andNot(bs);
2374       }
2375       return bs;
2376     case T.element:
2377       // within(element
2378       bsTemp = new BS();
2379       for (int i = i0; i >= 0; i = bsInfo.nextSetBit(i + 1))
2380         bsTemp.set(at[i].getElementNumber());
2381       for (int i = ac; --i >= 0;)
2382         if (at[i] != null && bsTemp.get(at[i].getElementNumber()))
2383           bs.set(i);
2384       return bs;
2385     case T.group:
2386       // within(group
2387       bsTemp = BSUtil.copy(bsInfo);
2388       for (int i = i0; i >= 0; i = bsTemp.nextSetBit(i + 1)) {
2389         at[i].group.setAtomBits(bs);
2390         bsTemp.andNot(bs);
2391       }
2392       return bs;
2393     case T.site:
2394       // within(site
2395       bsTemp = new BS();
2396       for (int i = i0; i >= 0; i = bsInfo.nextSetBit(i + 1))
2397         bsTemp.set(at[i].atomSite);
2398       for (int i = ac; --i >= 0;)
2399         if (at[i] != null && bsTemp.get(at[i].atomSite))
2400           bs.set(i);
2401       return bs;
2402     }
2403     Logger.error("MISSING getAtomBits entry for " + T.nameOf(tokType));
2404     return bs;
2405   }
2406 
getChainBits(int chainID)2407   public BS getChainBits(int chainID) {
2408     boolean caseSensitive = vwr.getBoolean(T.chaincasesensitive);
2409     if (caseSensitive) {
2410       // check for script compiler run before setting chain ids
2411       if (chainID >= 97 && chainID <= 122)
2412         chainID += 159;
2413     } else {
2414       if (chainID >= 0 && chainID < 300)
2415       chainID = chainToUpper(chainID);
2416     }
2417     BS bs = new BS();
2418     BS bsDone = BS.newN(ac);
2419     int id;
2420     for (int i = bsDone.nextClearBit(0); i < ac; i = bsDone.nextClearBit(i + 1)) {
2421       if (at[i] == null)
2422         continue;
2423       Chain chain = at[i].group.chain;
2424       if (chainID == (id = chain.chainID) || !caseSensitive
2425           && id >= 0 && id < 300 && chainID == chainToUpper(id)) {
2426         chain.setAtomBits(bs);
2427         bsDone.or(bs);
2428       } else {
2429         chain.setAtomBits(bsDone);
2430       }
2431     }
2432     return bs;
2433   }
2434 
chainToUpper(int chainID)2435   public int chainToUpper(int chainID) {
2436     return (chainID >= 97 && chainID <= 122 ? chainID - 32
2437         : chainID >= 256 && chainID < 300 ? chainID - 191
2438           // must be single-character lower-case 256=="a"
2439           // to lower case is to 65, so subtract (256-65)
2440         : chainID);
2441   }
2442 
isAltLoc(char altloc, String strPattern)2443   private boolean isAltLoc(char altloc, String strPattern) {
2444     if (strPattern == null)
2445       return (altloc == '\0');
2446     if (strPattern.length() != 1)
2447       return false;
2448     char ch = strPattern.charAt(0);
2449     return (ch == '*' || ch == '?' && altloc != '\0' || altloc == ch);
2450   }
2451 
getSeqcodeBits(int seqcode, boolean returnEmpty)2452   public BS getSeqcodeBits(int seqcode, boolean returnEmpty) {
2453     BS bs = new BS();
2454     int seqNum = Group.getSeqNumberFor(seqcode);
2455     boolean haveSeqNumber = (seqNum != Integer.MAX_VALUE);
2456     boolean isEmpty = true;
2457     char insCode = Group.getInsertionCodeChar(seqcode);
2458     switch (insCode) {
2459     case '?':
2460       for (int i = ac; --i >= 0;) {
2461         if (at[i] == null)
2462           continue;
2463         int atomSeqcode = at[i].group.seqcode;
2464         if ((!haveSeqNumber
2465             || seqNum == Group.getSeqNumberFor(atomSeqcode))
2466             && Group.getInsertionCodeFor(atomSeqcode) != 0) {
2467           bs.set(i);
2468           isEmpty = false;
2469         }
2470       }
2471       break;
2472     default:
2473       for (int i = ac; --i >= 0;) {
2474         if (at[i] == null)
2475           continue;
2476         int atomSeqcode = at[i].group.seqcode;
2477         if (seqcode == atomSeqcode ||
2478             !haveSeqNumber && seqcode == Group.getInsertionCodeFor(atomSeqcode)
2479             || insCode == '*' && seqNum == Group.getSeqNumberFor(atomSeqcode)) {
2480           bs.set(i);
2481           isEmpty = false;
2482         }
2483       }
2484     }
2485     return (!isEmpty || returnEmpty ? bs : null);
2486   }
2487 
2488 
2489   /**
2490    * overhauled by RMH Nov 1, 2006.
2491    *
2492    * @param identifier
2493    * @return null or bs
2494    */
getIdentifierOrNull(String identifier)2495   private BS getIdentifierOrNull(String identifier) {
2496     //a primitive lookup scheme when [ ] are not used
2497     //nam
2498     //na?
2499     //nam45
2500     //nam45C
2501     //nam45^
2502     //nam45^A
2503     //nam45^AC -- note, no colon here -- if present, handled separately
2504     //nam4? does NOT match anything for PDB files, but might for others
2505     //atom specifiers:
2506     //H?
2507     //H32
2508     //H3?
2509 
2510     //in the case of a ?, we take the whole thing
2511     // * can be used here, but not with ?
2512     // Jmol 14.1.14: allows initial ?* to be same as *
2513     // and therefore can be used in atom expression
2514 
2515     //first check with * option OFF
2516     BS bs = getSpecNameOrNull(identifier, false);
2517 
2518     if (identifier.indexOf("\\?") >= 0)
2519       identifier = PT.rep(identifier, "\\?","\1");
2520     return (bs != null || identifier.indexOf("?") > 0 ? bs
2521         : identifier.indexOf("*") > 0 ? getSpecNameOrNull(identifier, true)
2522         : ((ModelSet)this).haveBioModels ? ((ModelSet)this).bioModelset.getIdentifierOrNull(identifier)
2523         : null);
2524   }
2525 
getSpecName(String name)2526   private BS getSpecName(String name) {
2527     // * can be used here with ?
2528     BS bs = getSpecNameOrNull(name, false);
2529     if (bs != null)
2530       return bs;
2531     if (name.indexOf("*") > 0)
2532       bs = getSpecNameOrNull(name, true);
2533     return (bs == null ? new BS() : bs);
2534   }
2535 
getSpecNameOrNull(String name, boolean checkStar)2536   public BS getSpecNameOrNull(String name, boolean checkStar) {
2537     /// here xx*yy is changed to "xx??????????yy" when coming from getSpecName
2538     /// but not necessarily when coming from getIdentifierOrNull
2539     BS bs = null;
2540     name = name.toUpperCase();
2541     if (name.indexOf("\\?") >= 0)
2542       name = PT.rep(name, "\\?","\1");
2543     boolean allowInitialStar = name.startsWith("?*");
2544       if (allowInitialStar)
2545         name = name.substring(1);
2546 
2547     for (int i = ac; --i >= 0;) {
2548       if (at[i] == null)
2549         continue;
2550       String g3 = at[i].getGroup3(true);
2551       if (g3 != null && g3.length() > 0) {
2552         if (PT.isMatch(g3, name, checkStar, true)) {
2553           if (bs == null)
2554             bs = BS.newN(i + 1);
2555           bs.set(i);
2556           while (--i >= 0 && at[i].getGroup3(true).equals(g3))
2557             bs.set(i);
2558           i++;
2559         }
2560       } else if (isAtomNameMatch(at[i], name, checkStar, allowInitialStar)) {
2561         if (bs == null)
2562           bs = BS.newN(i + 1);
2563         bs.set(i);
2564       }
2565     }
2566     return bs;
2567   }
2568 
isAtomNameMatch(Atom atom, String strPattern, boolean checkStar, boolean allowInitialStar)2569   private boolean isAtomNameMatch(Atom atom, String strPattern, boolean checkStar, boolean allowInitialStar) {
2570     // called from getAtomBitsMa (spec_atom) and getSpecNameOrNull
2571     /// here xx*yy is changed to "xx??????????yy" when coming from getSpecName
2572     /// but not necessarily when coming from getIdentifierOrNull
2573     /// and NOT when coming from getAtomBits with Token.spec_atom
2574     /// because it is presumed that some names can include "*"
2575     return PT.isMatch(atom.getAtomName().toUpperCase(), strPattern,
2576         checkStar, allowInitialStar);
2577   }
2578 
getAtomIndices(BS bs)2579   public int[] getAtomIndices(BS bs) {
2580     int n = 0;
2581     int[] indices = new int[ac];
2582     for (int j = bs.nextSetBit(0); j >= 0 && j < ac; j = bs.nextSetBit(j + 1))
2583       indices[j] = ++n;
2584     return indices;
2585   }
2586 
getAtomsNearPlane(float distance, P4 plane)2587   public BS getAtomsNearPlane(float distance, P4 plane) {
2588     BS bsResult = new BS();
2589     for (int i = ac; --i >= 0;) {
2590       Atom atom = at[i];
2591       if (atom == null)
2592         continue;
2593       float d = Measure.distanceToPlane(plane, atom);
2594       if (distance > 0 && d >= -0.1 && d <= distance || distance < 0
2595           && d <= 0.1 && d >= distance || distance == 0 && Math.abs(d) < 0.01)
2596         bsResult.set(atom.i);
2597     }
2598     return bsResult;
2599   }
2600 
clearVisibleSets()2601   public void clearVisibleSets() {
2602     haveBSVisible = false;
2603     haveBSClickable = false;
2604   }
2605 
getAtomsInFrame(BS bsAtoms)2606   public void getAtomsInFrame(BS bsAtoms) {
2607     clearVisibleSets();
2608     bsAtoms.clearAll();
2609     for (int i = ac; --i >= 0;)
2610       if (at[i] != null && at[i].isVisible(Atom.ATOM_INFRAME))
2611         bsAtoms.set(i);
2612   }
2613 
getVisibleSet(boolean forceNew)2614   public BS getVisibleSet(boolean forceNew) {
2615     if (forceNew) {
2616       vwr.setModelVisibility();
2617       vwr.shm.finalizeAtoms(null, true);
2618     } else if (haveBSVisible) {
2619       return bsVisible;
2620     }
2621     bsVisible.clearAll();
2622     for (int i = ac; --i >= 0;)
2623       if (at[i] != null && at[i].checkVisible())
2624         bsVisible.set(i);
2625     if (vwr.shm.bsSlabbedInternal != null)
2626       bsVisible.andNot(vwr.shm.bsSlabbedInternal);
2627     haveBSVisible = true;
2628     return bsVisible;
2629   }
2630 
getClickableSet(boolean forceNew)2631   public BS getClickableSet(boolean forceNew) {
2632     if (forceNew)
2633       vwr.setModelVisibility();
2634     else if (haveBSClickable)
2635       return bsClickable;
2636     bsClickable.clearAll();
2637     for (int i = ac; --i >= 0;)
2638       if (at[i] != null && at[i].isClickable())
2639         bsClickable.set(i);
2640     haveBSClickable = true;
2641     return bsClickable;
2642   }
2643 
isModulated(int i)2644   public boolean isModulated(int i) {
2645     return bsModulated != null && bsModulated.get(i);
2646   }
2647 
deleteModelAtoms(int firstAtomIndex, int nAtoms, BS bsAtoms)2648   protected void deleteModelAtoms(int firstAtomIndex, int nAtoms, BS bsAtoms) {
2649     // all atoms in the model are being deleted here
2650     at = (Atom[]) AU.deleteElements(at, firstAtomIndex, nAtoms);
2651     ac = at.length;
2652     for (int j = firstAtomIndex; j < ac; j++) {
2653       at[j].i = j;
2654       at[j].mi--;
2655     }
2656     // fix modulation and tensors
2657     if (bsModulated != null)
2658       BSUtil.deleteBits(bsModulated, bsAtoms);
2659 
2660     deleteAtomTensors(bsAtoms);
2661     atomNames = (String[]) AU.deleteElements(atomNames, firstAtomIndex,
2662         nAtoms);
2663     atomTypes = (String[]) AU.deleteElements(atomTypes, firstAtomIndex,
2664         nAtoms);
2665     atomResnos = (int[]) AU.deleteElements(atomResnos, firstAtomIndex,
2666         nAtoms);
2667     atomSerials = (int[]) AU.deleteElements(atomSerials, firstAtomIndex,
2668         nAtoms);
2669     atomSeqIDs = (int[]) AU.deleteElements(atomSeqIDs, firstAtomIndex,
2670         nAtoms);
2671     dssrData = (float[]) AU.deleteElements(dssrData, firstAtomIndex,
2672         nAtoms);
2673     bfactor100s = (short[]) AU.deleteElements(bfactor100s,
2674         firstAtomIndex, nAtoms);
2675     hasBfactorRange = false;
2676     occupancies = (float[]) AU.deleteElements(occupancies,
2677         firstAtomIndex, nAtoms);
2678     resetPartialCharges();
2679     atomTensorList = (Object[][]) AU.deleteElements(atomTensorList,
2680         firstAtomIndex, nAtoms);
2681     vibrations = (Vibration[]) AU.deleteElements(vibrations,
2682         firstAtomIndex, nAtoms);
2683     nSurfaceAtoms = 0;
2684     bsSurface = null;
2685     surfaceDistance100s = null;
2686     if (tainted != null)
2687       for (int i = 0; i < TAINT_MAX; i++)
2688         BSUtil.deleteBits(tainted[i], bsAtoms);
2689     // what about data?
2690   }
2691 
getAtomIdentityInfo(int i, Map<String, Object> info, P3 ptTemp)2692   public void getAtomIdentityInfo(int i, Map<String, Object> info, P3 ptTemp) {
2693     info.put("_ipt", Integer.valueOf(i));
2694     info.put("atomIndex", Integer.valueOf(i));
2695     info.put("atomno", Integer.valueOf(at[i].getAtomNumber()));
2696     info.put("info", getAtomInfo(i, null, ptTemp));
2697     info.put("sym", at[i].getElementSymbol());
2698   }
2699 
getAtomTensorList(int i)2700   public Object[] getAtomTensorList(int i) {
2701     return (i < 0 || atomTensorList == null || i >= atomTensorList.length ? null
2702         : atomTensorList[i]);
2703   }
2704 
2705   // clean out deleted model atom tensors (ellipsoids)
deleteAtomTensors(BS bsAtoms)2706   private void deleteAtomTensors(BS bsAtoms) {
2707     if (atomTensors == null)
2708       return;
2709     Lst<String> toDelete = new Lst<String>();
2710     for (String key: atomTensors.keySet()) {
2711       Lst<Object> list = atomTensors.get(key);
2712       for (int i = list.size(); --i >= 0;) {
2713         Tensor t = (Tensor) list.get(i);
2714         if (bsAtoms.get(t.atomIndex1) || t.atomIndex2 >= 0 && bsAtoms.get(t.atomIndex2))
2715           list.removeItemAt(i);
2716       }
2717       if (list.size() == 0)
2718         toDelete.addLast(key);
2719     }
2720     for (int i = toDelete.size(); --i >= 0;)
2721       atomTensors.remove(toDelete.get(i));
2722   }
2723 
2724 
2725   private int atomCapacity;
setCapacity(int nAtoms)2726   void setCapacity(int nAtoms) {
2727     atomCapacity += nAtoms;
2728   }
2729 
setAtomTensors(int atomIndex, Lst<Object> list)2730   public void setAtomTensors(int atomIndex, Lst<Object> list) {
2731     if (list == null || list.size() == 0)
2732       return;
2733     if (atomTensors == null)
2734       atomTensors = new Hashtable<String, Lst<Object>>();
2735     if (atomTensorList == null)
2736       atomTensorList = new Object[at.length][];
2737     atomTensorList = (Object[][]) AU.ensureLength(atomTensorList, at.length);
2738     atomTensorList[atomIndex] = getTensorList(list);
2739     for (int i = list.size(); --i >= 0;) {
2740       Tensor t = (Tensor) list.get(i);
2741       t.atomIndex1 = atomIndex;
2742       t.atomIndex2 = -1;
2743       t.modelIndex = at[atomIndex].mi;
2744       addTensor(t, t.type);
2745       if (t.altType != null)
2746         addTensor(t, t.altType);
2747     }
2748   }
2749 
addTensor(Tensor t, String type)2750   public void addTensor(Tensor t, String type) {
2751     type = type.toLowerCase();
2752     Lst<Object> tensors = atomTensors.get(type);
2753     if (tensors == null) {
2754       atomTensors.put(type, tensors = new Lst<Object>());
2755       tensors.ensureCapacity(atomCapacity);
2756     }
2757     tensors.addLast(t);
2758   }
2759 
getTensorList(Lst<Object> list)2760   private static Object[] getTensorList(Lst<Object> list) {
2761     int pt = -1;
2762     boolean haveTLS = false;
2763     int n = list.size();
2764     for (int i = n; --i >= 0;) {
2765       Tensor t = (Tensor) list.get(i);
2766       if (t.forThermalEllipsoid)
2767         pt = i;
2768       else if (t.iType == Tensor.TYPE_TLS_U)
2769         haveTLS = true;
2770     }
2771     Object[] a = new Object[(pt >= 0 || !haveTLS ? 0 : 1) + n];
2772     if (pt >= 0) {
2773       a[0] = list.get(pt);
2774       if (list.size() == 1)
2775         return a;
2776     }
2777     // back-fills list for TLS:
2778     // 0 = temp, 1 = TLS-R, 2 = TLS-U
2779     if (haveTLS) {
2780       pt = 0;
2781       for (int i = n; --i >= 0;) {
2782         Tensor t = (Tensor) list.get(i);
2783         if (t.forThermalEllipsoid)
2784           continue;
2785         a[++pt] = t;
2786       }
2787     } else {
2788       for (int i = 0; i < n; i++)
2789         a[i] = list.get(i);
2790     }
2791     return a;
2792  }
2793 
getAtomTensor(int i, String type)2794   public Tensor getAtomTensor(int i, String type) {
2795     Object[] tensors = getAtomTensorList(i);
2796     if (tensors != null && type != null) {
2797       type = type.toLowerCase();
2798       for (int j = 0; j < tensors.length; j++) {
2799         Tensor t = (Tensor) tensors[j];
2800         if (t != null && (type.equals(t.type) || type.equals(t.altType)))
2801           return t;
2802       }
2803     }
2804     return null;
2805   }
2806 
getAllAtomTensors(String type)2807   public Lst<Object> getAllAtomTensors(String type) {
2808     if (atomTensors == null)
2809       return null;
2810     if (type != null)
2811       return atomTensors.get(type.toLowerCase());
2812     Lst<Object> list = new Lst<Object>();
2813     for (Entry<String, Lst<Object>> e : atomTensors.entrySet())
2814       list.addAll(e.getValue());
2815     return list;
2816   }
2817 
2818   /**
2819    * Scales vibrations and associated vectors such that the
2820    * maximum length is the given value
2821    *
2822    * @param max
2823    */
scaleVectorsToMax(float max)2824   public void scaleVectorsToMax(float max) {
2825     if (vibrations == null)
2826       return;
2827     float m = 0;
2828     BS bsVib = BS.newN(ac);
2829     for (int i = vibrations.length; --i >= 0;) {
2830       Vibration v = getVibration(i, false);
2831       if (v != null && (v.modDim == Vibration.TYPE_VIBRATION
2832           || v.modDim == Vibration.TYPE_SPIN)) {
2833         m = Math.max(m, v.length());
2834         bsVib.set(i);
2835       }
2836     }
2837     if (m == max || m == 0)
2838       return;
2839     m = max / m;
2840     boolean ok = false;
2841     for (int i = bsVib.nextSetBit(0); i >= 0; i = bsVib.nextSetBit(i + 1)) {
2842       Vibration v = getVibration(i, false);
2843       JmolModulationSet mod = getModulation(i);
2844       if (mod == null) {
2845         if (m == 0)
2846           return; // get out of here!
2847         v.scale(m);
2848       } else {
2849         mod.scaleVibration(m);
2850       }
2851       if (!ok) {
2852         taintAtom(i, TAINT_VIBRATION);
2853         ok = true;
2854       }
2855     }
2856     tainted[TAINT_VIBRATION].or(bsVib);
2857     //{*}.vxyz = {*}.vxyz.all.mul(3.0/{*}.vxyz.all.max)
2858   }
2859 
getAtomsFromAtomNumberInFrame(int atomNumber)2860   public BS getAtomsFromAtomNumberInFrame(int atomNumber) {
2861     BS bs = vwr.getFrameAtoms();
2862     for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
2863       if (at[i].getAtomNumber() != atomNumber)
2864         bs.clear(i);
2865     return bs;
2866   }
2867 
2868 
generateCrystalClass(int atomIndex, P3 pt)2869   public Lst<P3> generateCrystalClass(int atomIndex, P3 pt) {
2870     SymmetryInterface sym = (atomIndex < 0 || atomIndex >= ac ? null
2871         : at[atomIndex].getUnitCell());
2872     boolean isRandom = (pt != null && Float.isNaN(pt.x));
2873     return (sym == null ? new Lst<P3>() : sym
2874         .generateCrystalClass(isRandom ? null : pt != null ? pt : at[atomIndex]));
2875 
2876   }
2877 
2878 
2879 }
2880 
2881