1 /* $RCSfile$
2  * $Author: hansonr $
3  * $Date: 2021-09-07 18:56:03 -0500 (Tue, 07 Sep 2021) $
4  * $Revision: 22229 $
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 javajs.util.CU;
29 import javajs.util.Lst;
30 import javajs.util.P3;
31 import javajs.util.PT;
32 import javajs.util.SB;
33 import javajs.util.T3;
34 import javajs.util.V3;
35 
36 import org.jmol.api.JmolDataManager;
37 import org.jmol.api.JmolModulationSet;
38 import org.jmol.api.SymmetryInterface;
39 import org.jmol.atomdata.RadiusData;
40 import org.jmol.atomdata.RadiusData.EnumType;
41 import org.jmol.c.PAL;
42 import org.jmol.c.VDW;
43 import javajs.util.BS;
44 import org.jmol.modelsetbio.BioModel;
45 import org.jmol.script.T;
46 import org.jmol.util.C;
47 import org.jmol.util.Edge;
48 import org.jmol.util.Elements;
49 import org.jmol.util.Node;
50 import org.jmol.util.Point3fi;
51 import org.jmol.util.Tensor;
52 import org.jmol.util.Vibration;
53 import org.jmol.viewer.JC;
54 import org.jmol.viewer.Viewer;
55 
56 
57 
58 
59 public class Atom extends Point3fi implements Node {
60 
61   // ATOM_IN_FRAME simply associates an atom with the current model
62   // but doesn't necessarily mean it is visible
63   // ATOM_VIS_SET and ATOM_VISIBLE are checked once only for each atom per rendering
64 
65   public final static int ATOM_INFRAME     = 1;
66   public final static int ATOM_VISSET      = 2;  // have carried out checkVisible()
67   public final static int ATOM_VISIBLE     = 4;  // set from checkVisible()
68   public final static int ATOM_NOTHIDDEN   = 8;
69   public final static int ATOM_NOFLAGS     = ~63; // all of the above, plus balls and sticks
70   public final static int ATOM_INFRAME_NOTHIDDEN = ATOM_INFRAME | ATOM_NOTHIDDEN;
71   public final static int ATOM_SHAPE_VIS_MASK = ~ATOM_INFRAME_NOTHIDDEN;
72 
73 
74   public static final int RADIUS_MAX = 16;
75   public static final float RADIUS_GLOBAL = 16.1f;
76   public static short MAD_GLOBAL = 32200;
77 
78   public char altloc = '\0';
79   public byte atomID;
80   public int atomSite;
81   public Group group;
82   private float userDefinedVanDerWaalRadius;
83   byte valence;
84   private short atomicAndIsotopeNumber;
85   public BS atomSymmetry;
86 
87   private int formalChargeAndFlags; //  cccc CIP_ _CIP --hv
88 
89   private final static int CHARGE_OFFSET = 24;
90 
91   private final static int FLAG_MASK = 0xF;
92   private final static int VIBRATION_VECTOR_FLAG = 1;
93   private final static int IS_HETERO_FLAG = 2;
94 
95   private final static int CIP_CHIRALITY_OFFSET = 4;
96   private final static int CIP_CHIRALITY_MASK = 0x1F0;
97   private final static int CIP_CHIRALITY_RULE_OFFSET = 9;
98   private final static int CIP_CHIRALITY_RULE_MASK = 0xE00;
99   private final static int CIP_MASK = CIP_CHIRALITY_MASK | CIP_CHIRALITY_RULE_MASK;
100 
101   public short madAtom;
102 
103   public short colixAtom;
104   public byte paletteID = PAL.CPK.id;
105 
106   /**
107    *
108    * MAY BE NULL
109    *
110    */
111   public Bond[] bonds;
112 
113   private int nBondsDisplayed = 0;
114   public int nBackbonesDisplayed = 0;
115 
116   public int clickabilityFlags;
117   public int shapeVisibilityFlags;
118 
119   /**
120    * @j2sIgnoreSuperConstructor
121    * @j2sOverride
122    *
123    * @param modelIndex
124    * @param atomIndex
125    * @param xyz
126    * @param radius
127    * @param atomSymmetry
128    * @param atomSite
129    * @param atomicAndIsotopeNumber
130    * @param formalCharge
131    * @param isHetero
132    * @return this
133    */
134 
setAtom(int modelIndex, int atomIndex, P3 xyz, float radius, BS atomSymmetry, int atomSite, short atomicAndIsotopeNumber, int formalCharge, boolean isHetero)135   public Atom setAtom(int modelIndex, int atomIndex,
136         P3 xyz, float radius,
137         BS atomSymmetry, int atomSite,
138         short atomicAndIsotopeNumber, int formalCharge,
139         boolean isHetero) {
140     this.mi = (short)modelIndex;
141     this.atomSymmetry = atomSymmetry;
142     this.atomSite = atomSite;
143     this.i = atomIndex;
144     this.atomicAndIsotopeNumber = atomicAndIsotopeNumber;
145     if (isHetero)
146       formalChargeAndFlags = IS_HETERO_FLAG;
147     if (formalCharge != 0 && formalCharge != Integer.MIN_VALUE)
148       setFormalCharge(formalCharge);
149     userDefinedVanDerWaalRadius = radius;
150     setT(xyz);
151     return this;
152   }
153 
setShapeVisibility(int flag, boolean isVisible)154   public final void setShapeVisibility(int flag, boolean isVisible) {
155     if(isVisible)
156       shapeVisibilityFlags |= flag;
157     else
158       shapeVisibilityFlags &=~flag;
159   }
160 
isCovalentlyBonded(Atom atomOther)161   public boolean isCovalentlyBonded(Atom atomOther) {
162     if (bonds != null)
163       for (int i = bonds.length; --i >= 0;)
164         if (bonds[i].isCovalent()
165             && bonds[i].getOtherAtom(this) == atomOther)
166           return true;
167     return false;
168   }
169 
isBonded(Atom atomOther)170   public boolean isBonded(Atom atomOther) {
171     if (bonds != null)
172       for (int i = bonds.length; --i >= 0;)
173         if (bonds[i].getOtherAtom(this) == atomOther)
174           return true;
175     return false;
176   }
177 
getBond(Atom atomOther)178   public Bond getBond(Atom atomOther) {
179     if (bonds != null)
180       for (int i = bonds.length; --i >= 0;)
181         if (bonds[i].getOtherAtom(atomOther) != null)
182           return bonds[i];
183     return null;
184   }
185 
186 
addDisplayedBond(int stickVisibilityFlag, boolean isVisible)187   void addDisplayedBond(int stickVisibilityFlag, boolean isVisible) {
188     nBondsDisplayed += (isVisible ? 1 : -1);
189     setShapeVisibility(stickVisibilityFlag, (nBondsDisplayed > 0));
190   }
191 
deleteBond(Bond bond)192   void deleteBond(Bond bond) {
193     // this one is used -- from Bond.deleteAtomReferences
194     if (bonds != null)
195       for (int i = bonds.length; --i >= 0;)
196         if (bonds[i] == bond) {
197           deleteBondAt(i);
198           return;
199         }
200   }
201 
deleteBondAt(int i)202   private void deleteBondAt(int i) {
203     setCIPChirality(0);
204     int newLength = bonds.length - 1;
205     if (newLength == 0) {
206       bonds = null;
207       return;
208     }
209     Bond[] bondsNew = new Bond[newLength];
210     int j = 0;
211     for ( ; j < i; ++j)
212       bondsNew[j] = bonds[j];
213     for ( ; j < newLength; ++j)
214       bondsNew[j] = bonds[j + 1];
215     bonds = bondsNew;
216   }
217 
218   @Override
getBondedAtomIndex(int bondIndex)219   public int getBondedAtomIndex(int bondIndex) {
220     return bonds[bondIndex].getOtherAtom(this).i;
221   }
222 
223   /*
224    * What is a MAR?
225    *  - just a term that Miguel made up
226    *  - an abbreviation for Milli Angstrom Radius
227    * that is:
228    *  - a *radius* of either a bond or an atom
229    *  - in *millis*, or thousandths of an *angstrom*
230    *  - stored as a short
231    *
232    * However! In the case of an atom radius, if the parameter
233    * gets passed in as a negative number, then that number
234    * represents a percentage of the vdw radius of that atom.
235    * This is converted to a normal MAR as soon as possible
236    *
237    * (I know almost everyone hates bytes & shorts, but I like them ...
238    *  gives me some tiny level of type-checking ...
239    *  a rudimentary form of enumerations/user-defined primitive types)
240    */
241 
setMadAtom(Viewer vwr, RadiusData rd)242   public void setMadAtom(Viewer vwr, RadiusData rd) {
243     madAtom = calculateMad(vwr, rd);
244   }
245 
calculateMad(Viewer vwr, RadiusData rd)246   public short calculateMad(Viewer vwr, RadiusData rd) {
247     if (rd == null)
248       return 0;
249     float f = rd.value;
250     if (f == 0)
251       return 0;
252     switch (rd.factorType) {
253     case SCREEN:
254        return (short) f;
255     case FACTOR:
256     case OFFSET:
257       float r = 0;
258       switch (rd.vdwType) {
259       case TEMP:
260         float tmax = vwr.ms.getBfactor100Hi();
261         r = (tmax > 0 ? getBfactor100() / tmax : 0);
262         break;
263       case HYDRO:
264         r = Math.abs(getHydrophobicity());
265         break;
266       case BONDING:
267         r = getBondingRadius();
268         break;
269       case ADPMIN:
270       case ADPMAX:
271         r = getADPMinMax(rd.vdwType == VDW.ADPMAX);
272         break;
273       default:
274         r = getVanderwaalsRadiusFloat(vwr, rd.vdwType);
275       }
276       if (rd.factorType == EnumType.FACTOR)
277         f *= r;
278       else
279         f += r;
280       break;
281     case ABSOLUTE:
282       if (f == RADIUS_GLOBAL)
283         return MAD_GLOBAL;
284       break;
285     }
286     short mad = (short) (f < 0 ? f: f * 2000);
287     if (mad < 0 && f > 0)
288       mad = 0;
289     return mad;
290   }
291 
getADPMinMax(boolean isMax)292   public float getADPMinMax(boolean isMax) {
293     Object[] tensors = getTensors();
294     if (tensors == null)
295       return 0;
296     Tensor t = (Tensor) tensors[0];
297     if (t == null || t.iType != Tensor.TYPE_ADP)
298       return 0;
299     if (group.chain.model.ms.isModulated(i) && t.isUnmodulated)
300       t = (Tensor) tensors[1];
301     return t.getFactoredValue(isMax ? 2 : 1);
302   }
303 
getTensors()304   public Object[] getTensors() {
305     return group.chain.model.ms.getAtomTensorList(i);
306   }
307 
getRasMolRadius()308   public int getRasMolRadius() {
309     return Math.abs(madAtom / 8); //  1000r = 1000d / 2; rr = (1000r / 4);
310   }
311 
312   @Override
getEdges()313   public Edge[] getEdges() {
314     return (bonds == null ? new Edge[0] : bonds);
315   }
316 
317   @Override
getBondCount()318   public int getBondCount() {
319     return (bonds == null ? 0 : bonds.length);
320   }
321 
setTranslucent(boolean isTranslucent, float translucentLevel)322   public void setTranslucent(boolean isTranslucent, float translucentLevel) {
323     colixAtom = C.getColixTranslucent3(colixAtom, isTranslucent, translucentLevel);
324   }
325 
326   @Override
getElementNumber()327   public int getElementNumber() {
328     return Elements.getElementNumber(atomicAndIsotopeNumber);
329   }
330 
331   @Override
getIsotopeNumber()332   public int getIsotopeNumber() {
333     return Elements.getIsotopeNumber(atomicAndIsotopeNumber);
334   }
335 
336   @Override
getAtomicAndIsotopeNumber()337   public int getAtomicAndIsotopeNumber() {
338     return atomicAndIsotopeNumber;
339   }
340 
setAtomicAndIsotopeNumber(int n)341   public void setAtomicAndIsotopeNumber(int n) {
342     if (n < 0 || (n & 127) >= Elements.elementNumberMax || n > Short.MAX_VALUE)
343       n = 0;
344     atomicAndIsotopeNumber = (short) n;
345   }
346 
getElementSymbolIso(boolean withIsotope)347   public String getElementSymbolIso(boolean withIsotope) {
348     return Elements.elementSymbolFromNumber(withIsotope ? atomicAndIsotopeNumber : atomicAndIsotopeNumber & 127);
349   }
350 
getElementSymbol()351   public String getElementSymbol() {
352     return getElementSymbolIso(true);
353   }
354 
isHetero()355   public boolean isHetero() {
356     return (formalChargeAndFlags & IS_HETERO_FLAG) != 0;
357   }
358 
hasVibration()359   public boolean hasVibration() {
360     return (formalChargeAndFlags & VIBRATION_VECTOR_FLAG) != 0;
361   }
362 
363   /**
364    *
365    * @param charge from -3 to 7
366    */
setFormalCharge(int charge)367   public void setFormalCharge(int charge) {
368     formalChargeAndFlags = (formalChargeAndFlags & FLAG_MASK)
369         | ((charge == Integer.MIN_VALUE ? 0 : charge > 7 ? 7 : charge < -3 ? -3 : charge) << CHARGE_OFFSET);
370   }
371 
setVibrationVector()372   void setVibrationVector() {
373     formalChargeAndFlags |= VIBRATION_VECTOR_FLAG;
374   }
375 
376   @Override
getFormalCharge()377   public int getFormalCharge() {
378     return formalChargeAndFlags >> CHARGE_OFFSET;
379   }
380 
381   // a percentage value in the range 0-100
getOccupancy100()382   public int getOccupancy100() {
383     float[] occupancies = group.chain.model.ms.occupancies;
384     return (occupancies == null ? 100 : Math.round(occupancies[i]));
385   }
386 
387   // a percentage value in the range 0-100
isOccupied()388   public boolean isOccupied() {
389     float[] occupancies = group.chain.model.ms.occupancies;
390     return (occupancies == null || occupancies[i] >= 50);
391   }
392 
393   // This is called bfactor100 because it is stored as an integer
394   // 100 times the bfactor(temperature) value
getBfactor100()395   public int getBfactor100() {
396     short[] bfactor100s = group.chain.model.ms.bfactor100s;
397     return (bfactor100s == null ? 0 : bfactor100s[i]);
398   }
399 
getHydrophobicity()400   public float getHydrophobicity() {
401     float[] values = group.chain.model.ms.hydrophobicities;
402     return (values == null ? Elements.getHydrophobicity(group.groupID) : values[i]);
403   }
404 
setRadius(float radius)405   public boolean setRadius(float radius) {
406     return !Float.isNaN(userDefinedVanDerWaalRadius = (radius > 0 ? radius : Float.NaN));
407   }
408 
delete(BS bsBonds)409   public void delete(BS bsBonds) {
410     valence = -1;
411     if (bonds != null)
412       for (int i = bonds.length; --i >= 0; ) {
413         Bond bond = bonds[i];
414         bond.getOtherAtom(this).deleteBond(bond);
415         bsBonds.set(bond.index);
416       }
417     bonds = null;
418   }
419 
420   @Override
isDeleted()421   public boolean isDeleted() {
422     return (valence < 0);
423   }
424 
setValence(int nBonds)425   public void setValence(int nBonds) {
426     if (!isDeleted()) // no resurrection
427       valence = (byte) (nBonds < 0 ? 0 : nBonds <= 0x7F ? nBonds : 0x7F);
428   }
429 
430   /**
431    * return the total bond order for this atom
432    */
433   @Override
getValence()434   public int getValence() {
435     if (isDeleted())
436       return -1;
437     int n = valence;
438     if (n == 0 && bonds != null)
439       for (int i = bonds.length; --i >= 0;)
440         n += bonds[i].getValence();
441     return n;
442   }
443 
444   @Override
getCovalentBondCount()445   public int getCovalentBondCount() {
446     if (bonds == null)
447       return 0;
448     int n = 0;
449     Bond b;
450     for (int i = bonds.length; --i >= 0; )
451       if (((b = bonds[i]).order & Edge.BOND_COVALENT_MASK) != 0
452           && !b.getOtherAtom(this).isDeleted())
453         ++n;
454     return n;
455   }
456 
457   @Override
getCovalentHydrogenCount()458   public int getCovalentHydrogenCount() {
459     if (bonds == null)
460       return 0;
461     int n = 0;
462     for (int i = bonds.length; --i >= 0; ) {
463       if ((bonds[i].order & Edge.BOND_COVALENT_MASK) == 0)
464         continue;
465       Atom a = bonds[i].getOtherAtom(this);
466       if (a.valence >= 0 && a.getElementNumber() == 1)
467         ++n;
468     }
469     return n;
470   }
471 
472   @Override
getImplicitHydrogenCount()473   public int getImplicitHydrogenCount() {
474     return group.chain.model.ms.getMissingHydrogenCount(this, false);
475   }
476 
477   @Override
getTotalHydrogenCount()478   public int getTotalHydrogenCount() {
479     return getCovalentHydrogenCount() + getImplicitHydrogenCount();
480   }
481 
482   @Override
getTotalValence()483   public int getTotalValence() {
484     int v = getValence();
485     if (v < 0)
486       return v;
487     int h = getImplicitHydrogenCount();
488     int sp2 = group.chain.model.ms.aaRet[4]; // 1 or 0
489     return v + h + sp2;
490   }
491 
492   @Override
getCovalentBondCountPlusMissingH()493   public int getCovalentBondCountPlusMissingH() {
494     return getCovalentBondCount() + getImplicitHydrogenCount();
495   }
496 
getTargetValence()497   int getTargetValence() {
498     switch (getElementNumber()) {
499     case 6: //C
500     case 14: //Si
501     case 32: // Ge
502       return 4;
503     case 5:  // B
504     case 7:  // N
505     case 15: // P
506       return 3;
507     case 8: //O
508     case 16: //S
509       return 2;
510     case 1:
511     case 9: // F
512     case 17: // Cl
513     case 35: // Br
514     case 53: // I
515       return 1;
516     }
517     return -1;
518   }
519 
520 
getDimensionValue(int dimension)521   public float getDimensionValue(int dimension) {
522     return (dimension == 0 ? x : (dimension == 1 ? y : z));
523   }
524 
getVanderwaalsRadiusFloat(Viewer vwr, VDW type)525   public float getVanderwaalsRadiusFloat(Viewer vwr, VDW type) {
526     // called by atomPropertyFloat as VDW_AUTO,
527     // AtomCollection.fillAtomData with VDW_AUTO or VDW_NOJMOL
528     // AtomCollection.findMaxRadii with VDW_AUTO
529     // AtomCollection.getAtomPropertyState with VDW_AUTO
530     // AtomCollection.getVdwRadius with passed on type
531     return (Float.isNaN(userDefinedVanDerWaalRadius)
532         ? vwr.getVanderwaalsMarType(atomicAndIsotopeNumber, getVdwType(type)) / 1000f
533         : userDefinedVanDerWaalRadius);
534   }
535 
536   /**
537    *
538    * @param type
539    * @return if VDW_AUTO, will return VDW_AUTO_JMOL, VDW_AUTO_RASMOL, or VDW_AUTO_BABEL
540    *         based on the model type
541    */
542   @SuppressWarnings("incomplete-switch")
getVdwType(VDW type)543   private VDW getVdwType(VDW type) {
544     switch (type) {
545     case AUTO:
546       type = group.chain.model.ms.getDefaultVdwType(mi);
547       break;
548     case NOJMOL:
549       type = group.chain.model.ms.getDefaultVdwType(mi);
550       if (type == VDW.AUTO_JMOL)
551         type = VDW.AUTO_BABEL;
552       break;
553     }
554     return type;
555   }
556 
getBondingRadius()557   public float getBondingRadius() {
558     float[] rr = group.chain.model.ms.bondingRadii;
559     float r = (rr == null  || i >= rr.length ? 0 : rr[i]);
560     return (r == 0 ? Elements.getBondingRadius(atomicAndIsotopeNumber,
561         getFormalCharge()) : r);
562   }
563 
getVolume(Viewer vwr, VDW vType)564   float getVolume(Viewer vwr, VDW vType) {
565     float r1 = (vType == null ? userDefinedVanDerWaalRadius : Float.NaN);
566     if (Float.isNaN(r1))
567       r1 = vwr.getVanderwaalsMarType(getElementNumber(), getVdwType(vType)) / 1000f;
568     double volume = 0;
569     if (bonds != null)
570       for (int j = 0; j < bonds.length; j++) {
571         if (!bonds[j].isCovalent())
572           continue;
573         Atom atom2 = bonds[j].getOtherAtom(this);
574         float r2 = (vType == null ? atom2.userDefinedVanDerWaalRadius : Float.NaN);
575         if (Float.isNaN(r2))
576           r2 = vwr.getVanderwaalsMarType(atom2.getElementNumber(), atom2
577               .getVdwType(vType)) / 1000f;
578         float d = distance(atom2);
579         if (d > r1 + r2)
580           continue;
581         if (d + r1 <= r2)
582           return 0;
583 
584         // calculate hidden spherical cap height and volume
585         // A.Bondi, J. Phys. Chem. 68, 1964, 441-451.
586 
587         double h = r1 - (r1 * r1 + d * d - r2 * r2) / (2.0 * d);
588         volume -= Math.PI / 3 * h * h * (3 * r1 - h);
589       }
590     return (float) (volume + 4 * Math.PI / 3 * r1 * r1 * r1);
591   }
592 
getCurrentBondCount()593   int getCurrentBondCount() {
594     return bonds == null ? 0 : bonds.length;
595   }
596 
getRadius()597   public float getRadius() {
598     return Math.abs(madAtom / 2000f);
599   }
600 
601   @Override
getIndex()602   public int getIndex() {
603     return i;
604   }
605 
606   @Override
getAtomSite()607   public int getAtomSite() {
608     return atomSite;
609   }
610 
611   @Override
getGroupBits(BS bs)612   public void getGroupBits(BS bs) {
613      group.setAtomBits(bs);
614    }
615 
616    @Override
getAtomName()617   public String getAtomName() {
618      return (atomID > 0 ? Group.specialAtomNames[atomID]
619          : group.chain.model.ms.atomNames == null ? "" : group.chain.model.ms.atomNames[i]);
620    }
621 
622    @Override
getAtomType()623   public String getAtomType() {
624     String[] atomTypes = group.chain.model.ms.atomTypes;
625     String type = (atomTypes == null ? null : atomTypes[i]);
626     return (type == null ? getAtomName() : type);
627   }
628 
629    @Override
getAtomNumber()630   public int getAtomNumber() {
631      int[] atomSerials = group.chain.model.ms.atomSerials;
632      // shouldn't ever be null.
633      return (atomSerials == null ? i : atomSerials[i]);
634 //        : group.chain.model.modelSet.isZeroBased ? atomIndex : atomIndex);
635    }
636 
getSeqID()637    public int getSeqID() {
638      int[] ids = group.chain.model.ms.atomSeqIDs;
639      return (ids == null ? 0 : ids[i]);
640    }
isVisible(int flags)641    public boolean isVisible(int flags) {
642      return ((shapeVisibilityFlags & flags) == flags);
643    }
644 
getPartialCharge()645    public float getPartialCharge() {
646      float[] partialCharges = group.chain.model.ms.partialCharges;
647      return partialCharges == null ? 0 : partialCharges[i];
648    }
649 
650    /**
651     * Given a symmetry operation number, the set of cells in the model, and the
652     * number of operations, this method returns either 0 or the cell number (555, 666)
653     * of the translated symmetry operation corresponding to this atom.
654     *
655     * atomSymmetry is a bitset that is created in adapter.smarter.AtomSetCollection
656     *
657     * It is arranged as follows:
658     *
659     * |--overall--|---cell1---|---cell2---|---cell3---|...
660     *
661     * |012..nOps-1|012..nOps-1|012..nOp-1s|012..nOps-1|...
662     *
663     * If a bit is set, it means that the atom was created using that operator
664     * operating on the base file set and translated for that cell.
665     *
666     * If any bit is set in any of the cell blocks, then the same
667     * bit will also be set in the overall block. This allows for
668     * rapid determination of special positions and also of
669     * atom membership in any operation set.
670     *
671     *  Note that it is not necessarily true that an atom is IN the designated
672     *  cell, because one can load {nnn mmm 0}, and then, for example, the {-x,-y,-z}
673     *  operator sends atoms from 555 to 444. Still, those atoms would be marked as
674     *  cell 555 here, because no translation was carried out.
675     *
676     *  That is, the numbers 444 in symop=3444 do not refer to a cell, per se.
677     *  What they refer to is the file-designated operator plus a translation of
678     *  {-1 -1 -1/1}.
679     *
680     * @param symop        = 0, 1, 2, 3, ....
681     * @param cellRange    = {444, 445, 446, 454, 455, 456, .... }
682     * @param nOps         = 2 for x,y,z;-x,-y,-z, for example
683     * @return cell number such as 565
684     */
getSymmetryTranslation(int symop, int[] cellRange, int nOps)685    public int getSymmetryTranslation(int symop, int[] cellRange, int nOps) {
686      int pt = symop;
687      for (int i = 0; i < cellRange.length; i++)
688        if (atomSymmetry.get(pt += nOps))
689          return cellRange[i];
690      return 0;
691    }
692 
693    /**
694     * Looks for a match in the cellRange list for this atom within the specified translation set
695     * select symop=0NNN for this
696     *
697     * @param cellNNN
698     * @param cellRange
699     * @param nOps
700     * @return     matching cell number, if applicable
701     */
getCellTranslation(int cellNNN, int[] cellRange, int nOps)702    public int getCellTranslation(int cellNNN, int[] cellRange, int nOps) {
703      int pt = nOps;
704      for (int i = 0; i < cellRange.length; i++)
705        for (int j = 0; j < nOps;j++, pt++)
706        if (atomSymmetry.get(pt) && cellRange[i] == cellNNN)
707          return cellRange[i];
708      return 0;
709    }
710 
getSymmetryOperatorList(boolean isAll)711    String getSymmetryOperatorList(boolean isAll) {
712     String str = "";
713     ModelSet f = group.chain.model.ms;
714     int nOps = f.getModelSymmetryCount(mi);
715     if (nOps == 0 || atomSymmetry == null)
716       return "";
717     int[] cellRange = f.getModelCellRange(mi);
718     int pt = nOps;
719     int n = (cellRange == null ? 1 : cellRange.length);
720     BS bs = (isAll ? null : new BS());
721     for (int i = 0; i < n; i++)
722       for (int j = 0; j < nOps; j++)
723         if (atomSymmetry.get(pt++))
724           if (isAll) {
725             str += "," + (j + 1) + cellRange[i];
726           } else {
727             bs.set(j + 1);
728           }
729     if (!isAll)
730       for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i + 1))
731         str += "," + i;
732     return (str.length() == 0 ? "" : str.substring(1));
733   }
734 
735   /**
736    * SMILES only
737    */
738   @Override
getModelIndex()739   public int getModelIndex() {
740     return mi;
741   }
742 
743   @Override
getMoleculeNumber(boolean inModel)744   public int getMoleculeNumber(boolean inModel) {
745     return (group.chain.model.ms.getMoleculeIndex(i, inModel) + 1);
746   }
747 
getFractionalCoord(boolean fixJavaFloat, char ch, boolean ignoreOffset, P3 pt)748   private float getFractionalCoord(boolean fixJavaFloat, char ch, boolean ignoreOffset, P3 pt) {
749     pt = getFractionalCoordPt(fixJavaFloat, ignoreOffset, pt);
750     return (ch == 'X' ? pt.x : ch == 'Y' ? pt.y : pt.z);
751   }
752 
753   @Override
getXYZ()754   public P3 getXYZ() {
755     return this;
756   }
757 
getFractionalCoordPt(boolean fixJavaFloat, boolean ignoreOffset, P3 pt)758   public P3 getFractionalCoordPt(boolean fixJavaFloat, boolean ignoreOffset, P3 pt) {
759     // ignoreOffset TRUE uses the original unshifted matrix
760     SymmetryInterface c = getUnitCell();
761     if (c == null)
762       return this;
763     if (pt == null)
764       pt = P3.newP(this);
765     else
766       pt.setT(this);
767     c.toFractional(pt, ignoreOffset);
768     if (fixJavaFloat)
769       PT.fixPtFloats(pt, PT.FRACTIONAL_PRECISION);
770     return pt;
771   }
772 
getUnitCell()773   SymmetryInterface getUnitCell() {
774     return group.chain.model.ms.getUnitCellForAtom(this.i);
775   }
776 
getFractionalUnitCoord(boolean fixJavaFloat, char ch, P3 pt)777   private float getFractionalUnitCoord(boolean fixJavaFloat, char ch, P3 pt) {
778     pt = getFractionalUnitCoordPt(fixJavaFloat, false, pt);
779     return (ch == 'X' ? pt.x : ch == 'Y' ? pt.y : pt.z);
780   }
781 
782   /**
783    * @param fixJavaFloat ALWAYS set true for any new references to this method. False is for legacy only
784    * @param asCartesian
785    * @param pt
786    * @return unit cell coord
787    */
getFractionalUnitCoordPt(boolean fixJavaFloat, boolean asCartesian, P3 pt)788   P3 getFractionalUnitCoordPt(boolean fixJavaFloat, boolean asCartesian, P3 pt) {
789     SymmetryInterface c = getUnitCell();
790     if (c == null)
791       return this;
792     if (pt == null)
793       pt = P3.newP(this);
794     else
795       pt.setT(this);
796     if (group.chain.model.isJmolDataFrame) {
797       c.toFractional(pt, false);
798       if (asCartesian)
799         c.toCartesian(pt, false);
800     } else {
801       c.toUnitCell(pt, null);
802       if (!asCartesian)
803         c.toFractional(pt, false);
804     }
805     if (fixJavaFloat)
806       PT.fixPtFloats(pt, asCartesian ? PT.CARTESIAN_PRECISION : PT.FRACTIONAL_PRECISION);
807     return pt;
808   }
809 
getFractionalUnitDistance(T3 pt, T3 ptTemp1, T3 ptTemp2)810   float getFractionalUnitDistance(T3 pt, T3 ptTemp1, T3 ptTemp2) {
811     SymmetryInterface c = getUnitCell();
812     if (c == null)
813       return distance(pt);
814     ptTemp1.setT(this);
815     ptTemp2.setT(pt);
816     if (group.chain.model.isJmolDataFrame) {
817       c.toFractional(ptTemp1, true);
818       c.toFractional(ptTemp2, true);
819     } else {
820       c.toUnitCell(ptTemp1, null);
821       c.toUnitCell(ptTemp2, null);
822     }
823     return ptTemp1.distance(ptTemp2);
824   }
825 
setFractionalCoord(int tok, float fValue, boolean asAbsolute)826   void setFractionalCoord(int tok, float fValue, boolean asAbsolute) {
827     SymmetryInterface c = getUnitCell();
828     if (c != null)
829       c.toFractional(this, asAbsolute);
830     switch (tok) {
831     case T.fux:
832     case T.fracx:
833       x = fValue;
834       break;
835     case T.fuy:
836     case T.fracy:
837       y = fValue;
838       break;
839     case T.fuz:
840     case T.fracz:
841       z = fValue;
842       break;
843     }
844     if (c != null)
845       c.toCartesian(this, asAbsolute);
846   }
847 
setFractionalCoordTo(P3 ptNew, boolean asAbsolute)848   void setFractionalCoordTo(P3 ptNew, boolean asAbsolute) {
849     setFractionalCoordPt(this, ptNew, asAbsolute);
850   }
851 
setFractionalCoordPt(P3 pt, P3 ptNew, boolean asAbsolute)852   public void setFractionalCoordPt(P3 pt, P3 ptNew, boolean asAbsolute) {
853     pt.setT(ptNew);
854     SymmetryInterface c = getUnitCell();
855     if (c != null)
856       c.toCartesian(pt, asAbsolute && !group.chain.model.isJmolDataFrame);
857   }
858 
isCursorOnTopOf(int xCursor, int yCursor, int minRadius, Atom competitor)859   boolean isCursorOnTopOf(int xCursor, int yCursor,
860                         int minRadius, Atom competitor) {
861     int r = sD / 2;
862     if (r < minRadius)
863       r = minRadius;
864     int r2 = r * r;
865     int dx = sX - xCursor;
866     int dx2 = dx * dx;
867     if (dx2 > r2)
868       return false;
869     int dy = sY - yCursor;
870     int dy2 = dy * dy;
871     int dz2 = r2 - (dx2 + dy2);
872     if (dz2 < 0)
873       return false;
874     if (competitor == null)
875       return true;
876     int z = sZ;
877     int zCompetitor = competitor.sZ;
878     int rCompetitor = competitor.sD / 2;
879     if (z < zCompetitor - rCompetitor)
880       return true;
881     int dxCompetitor = competitor.sX - xCursor;
882     int dx2Competitor = dxCompetitor * dxCompetitor;
883     int dyCompetitor = competitor.sY - yCursor;
884     int dy2Competitor = dyCompetitor * dyCompetitor;
885     int r2Competitor = rCompetitor * rCompetitor;
886     int dz2Competitor = r2Competitor - (dx2Competitor + dy2Competitor);
887     return (z - Math.sqrt(dz2) < zCompetitor - Math.sqrt(dz2Competitor));
888   }
889 
890   /*
891    *  DEVELOPER NOTE (BH):
892    *
893    *  The following methods may not return
894    *  correct values until after modelSet.finalizeGroupBuild()
895    *
896    */
897 
getInfo()898   public String getInfo() {
899     return getIdentity(true);
900   }
901 
getIdentityXYZ(boolean allInfo, P3 pt)902   public String getIdentityXYZ(boolean allInfo, P3 pt) {
903     pt = (group.chain.model.isJmolDataFrame ? getFractionalCoordPt(!group.chain.model.ms.vwr.g.legacyJavaFloat, false, pt) : this);
904     return getIdentity(allInfo)
905         + " " + PT.formatF(pt.x, 0, 3, true, true)
906         + " " + PT.formatF(pt.y, 0, 3, true, true)
907         + " " + PT.formatF(pt.z, 0, 3, true, true)
908         ;
909   }
910 
getIdentity(boolean allInfo)911   String getIdentity(boolean allInfo) {
912     SB info = new SB();
913     String group3 = getGroup3(true);
914     if (group3 != null && group3.length() > 0 && (!group3.equals("UNK") || group.chain.model.isBioModel)) {
915       info.append("[");
916       info.append(group3);
917       info.append("]");
918       String seqcodeString = group.getSeqcodeString();
919       if (seqcodeString != null)
920         info.append(seqcodeString);
921       int chainID = group.chain.chainID;
922       if (chainID != 0 && chainID != 32) {
923         info.append(":");
924         String s = getChainIDStr();
925         if (chainID >= 256)
926           s = PT.esc(s);
927         info.append(s);
928       }
929       if (!allInfo)
930         return info.toString();
931       info.append(".");
932     }
933     info.append(getAtomName());
934     if (info.length() == 0) {
935       // since atomName cannot be null, this is unreachable
936       info.append(getElementSymbolIso(false));
937       info.append(" ");
938       info.appendI(getAtomNumber());
939     }
940     if (altloc != '\0') {
941       info.append("%");
942       info.appendC(altloc);
943     }
944     if (group.chain.model.ms.mc > 1 && !group.chain.model.isJmolDataFrame) {
945       info.append("/");
946       info.append(getModelNumberForLabel());
947     }
948     info.append(" #");
949     info.appendI(getAtomNumber());
950     return info.toString();
951   }
952 
953   @Override
getGroup3(boolean allowNull)954   public String getGroup3(boolean allowNull) {
955     String group3 = group.getGroup3();
956     return (allowNull
957         || group3 != null && group3.length() > 0
958         ? group3 : "UNK");
959   }
960 
961   @Override
getGroup1(char c0)962   public String getGroup1(char c0) {
963     char c = group.getGroup1();
964     return (c != '\0' ? "" + c : c0 != '\0' ? "" + c0 : "");
965   }
966 
967   @Override
getBioSmilesType()968   public char getBioSmilesType() {
969     return  (group.isProtein() ? 'p'
970         : group.isDna() ? 'd'
971             : group.isRna() ? 'r'
972                 : group.isCarbohydrate() ? 'c'
973                     : ' ');
974   }
975 
976   @Override
isPurine()977   public boolean isPurine() {
978     return group.isPurine();
979   }
980 
981   @Override
isPyrimidine()982   public boolean isPyrimidine() {
983     return group.isPyrimidine();
984   }
985 
986   @Override
getResno()987   public int getResno() {
988     return group.getResno();
989   }
990 
isClickable()991   public boolean isClickable() {
992     // certainly if it is not visible, then it can't be clickable
993     return (checkVisible()
994         && clickabilityFlags != 0
995         && ((shapeVisibilityFlags | group.shapeVisibilityFlags) & clickabilityFlags) != 0);
996   }
997 
setClickable(int flag)998   public void setClickable(int flag) {
999     if (flag == 0) {
1000       clickabilityFlags = 0;
1001     } else {
1002       clickabilityFlags |= flag;
1003       if (flag != JC.ALPHA_CARBON_VISIBILITY_FLAG)
1004         shapeVisibilityFlags |= flag;
1005     }
1006   }
1007 
checkVisible()1008   public boolean checkVisible() {
1009     if (isVisible(ATOM_VISSET))
1010       return isVisible(ATOM_VISIBLE);
1011     boolean isVis = isVisible(ATOM_INFRAME_NOTHIDDEN);
1012     if (isVis) {
1013       int flags = shapeVisibilityFlags;
1014       // Is its PDB group visible in any way (cartoon, e.g.)?
1015       //  An atom is considered visible if its PDB group is visible, even
1016       //  if it does not show up itself as part of the structure
1017       //  (this will be a difference in terms of *clickability*).
1018       // except BACKBONE -- in which case we only see the lead atoms
1019       if (group.shapeVisibilityFlags != 0
1020           && (group.shapeVisibilityFlags != JC.VIS_BACKBONE_FLAG || isLeadAtom()))
1021         flags |= group.shapeVisibilityFlags;
1022       // We know that (flags & AIM), so now we must remove that flag
1023       // and check to see if any others are remaining.
1024       // Only then is the atom considered visible.
1025       flags &= ATOM_SHAPE_VIS_MASK;
1026       // problem with display of bond-only when not clickable.
1027       // bit of a kludge here.
1028       if (flags == JC.VIS_BOND_FLAG && clickabilityFlags == 0)
1029         flags = 0;
1030       isVis = (flags != 0);
1031       if (isVis)
1032         shapeVisibilityFlags |= ATOM_VISIBLE;
1033     }
1034     shapeVisibilityFlags |= ATOM_VISSET;
1035     return isVis;
1036 
1037   }
1038 
1039   @Override
isLeadAtom()1040   public boolean isLeadAtom() {
1041     return group.isLeadAtom(i);
1042   }
1043 
1044   @Override
getChainID()1045   public int getChainID() {
1046     return group.chain.chainID;
1047   }
1048 
1049   @Override
getChainIDStr()1050   public String getChainIDStr() {
1051     return group.chain.getIDStr();
1052   }
1053 
getSurfaceDistance100()1054   public int getSurfaceDistance100() {
1055     return group.chain.model.ms.getSurfaceDistance100(i);
1056   }
1057 
getVibrationVector()1058   public Vibration getVibrationVector() {
1059     return group.chain.model.ms.getVibration(i, false);
1060   }
1061 
getModulation()1062   public JmolModulationSet getModulation() {
1063     return group.chain.model.ms.getModulation(i);
1064   }
1065 
getModelNumberForLabel()1066   public String getModelNumberForLabel() {
1067     return group.chain.model.ms.getModelNumberForAtomLabel(mi);
1068   }
1069 
getModelNumber()1070   public int getModelNumber() {
1071     return group.chain.model.ms.getModelNumber(mi) % 1000000;
1072   }
1073 
1074   @Override
getBioStructureTypeName()1075   public String getBioStructureTypeName() {
1076     return group.getProteinStructureType().getBioStructureTypeName(true);
1077   }
1078 
1079   @Override
equals(Object obj)1080   public boolean equals(Object obj) {
1081     return (this == obj);
1082   }
1083 
1084   @Override
hashCode()1085   public int hashCode() {
1086     //this overrides the Point3fi hashcode, which would
1087     //give a different hashcode for an atom depending upon
1088     //its screen location! Bug fix for 11.1.43 Bob Hanson
1089     return i;
1090   }
1091 
findAromaticNeighbor(int notAtomIndex)1092   public Atom findAromaticNeighbor(int notAtomIndex) {
1093     if (bonds == null)
1094       return null;
1095     for (int i = bonds.length; --i >= 0; ) {
1096       Bond bondT = bonds[i];
1097       Atom a = bondT.getOtherAtom(this);
1098       if (bondT.isAromatic() && a.i != notAtomIndex)
1099         return a;
1100     }
1101     return null;
1102   }
1103 
1104   /**
1105    * called by isosurface and int comparator via atomProperty()
1106    * and also by getBitsetProperty()
1107    *
1108    * @param tokWhat
1109    * @return         int value or Integer.MIN_VALUE
1110    */
atomPropertyInt(int tokWhat)1111   public int atomPropertyInt(int tokWhat) {
1112     switch (tokWhat) {
1113     case T.atomno:
1114       return getAtomNumber();
1115     case T.seqid:
1116       return getSeqID();
1117     case T.atomid:
1118       return atomID;
1119     case T.subsystem:
1120       return Math.max(0, altloc - 32);
1121     case T.atomindex:
1122       return i;
1123     case T.bondcount:
1124       return getCovalentBondCount();
1125     case T.chainno:
1126       return group.chain.chainNo;
1127     case T.color:
1128       return group.chain.model.ms.vwr.gdata.getColorArgbOrGray(colixAtom);
1129     case T.element:
1130     case T.elemno:
1131       return getElementNumber();
1132     case T.elemisono:
1133       return atomicAndIsotopeNumber;
1134     case T.file:
1135       return group.chain.model.fileIndex + 1;
1136     case T.formalcharge:
1137       return getFormalCharge();
1138     case T.groupid:
1139       return group.groupID; //-1 if no group
1140     case T.groupindex:
1141       return group.groupIndex;
1142     case T.model:
1143       //integer model number -- could be PDB/sequential adapter number
1144       //or it could be a sequential model in file number when multiple files
1145       return getModelNumber();
1146     case -T.model:
1147       //float is handled differently
1148       return group.chain.model.ms.modelFileNumbers[mi];
1149     case T.modelindex:
1150       return mi;
1151     case T.molecule:
1152       return getMoleculeNumber(true);
1153     case T.monomer:
1154       return group.getMonomerIndex() + 1;
1155     case T.occupancy:
1156       return getOccupancy100();
1157     case T.polymer:
1158       return group.getBioPolymerIndexInModel() + 1;
1159     case T.polymerlength:
1160       return group.getBioPolymerLength();
1161     case T.radius:
1162       // the comparator uses rasmol radius, unfortunately, for integers
1163       return getRasMolRadius();
1164     case T.resno:
1165       return getResno();
1166     case T.site:
1167       return getAtomSite();
1168     case T.structure:
1169       return group.getProteinStructureType().getId();
1170     case T.substructure:
1171       return group.getProteinStructureSubType().getId();
1172     case T.strucno:
1173       return group.getStrucNo();
1174     case T.symop:
1175       return getSymOp();
1176     case T.valence:
1177       return getValence();
1178     }
1179     return 0;
1180   }
1181 
getSymOp()1182   int getSymOp() {
1183     return (atomSymmetry == null ? 0 : atomSymmetry.nextSetBit(0) + 1);
1184   }
1185 
1186   /**
1187    * called by isosurface and int comparator via atomProperty() and also by
1188    * getBitsetProperty()
1189    *
1190    * @param vwr
1191    *
1192    * @param tokWhat
1193    * @param ptTemp
1194    * @return float value or value*100 (asInt=true) or throw an error if not
1195    *         found
1196    *
1197    */
atomPropertyFloat(Viewer vwr, int tokWhat, P3 ptTemp)1198   public float atomPropertyFloat(Viewer vwr, int tokWhat, P3 ptTemp) {
1199     switch (tokWhat) {
1200     case T.adpmax:
1201       return getADPMinMax(true);
1202     case T.adpmin:
1203       return getADPMinMax(false);
1204     case T.atomx:
1205     case T.x:
1206       return x;
1207     case T.atomy:
1208     case T.y:
1209       return y;
1210     case T.atomz:
1211     case T.z:
1212       return z;
1213     case T.dssr:
1214       return group.chain.model.ms.getAtomicDSSRData(i);
1215     case T.backbone:
1216     case T.cartoon:
1217     case T.dots:
1218     case T.ellipsoid:
1219     case T.geosurface:
1220     case T.halo:
1221     case T.meshRibbon:
1222     case T.ribbon:
1223     case T.rocket:
1224     case T.star:
1225     case T.strands:
1226     case T.trace:
1227       return vwr.shm.getAtomShapeValue(tokWhat, group, i);
1228     case T.bondingradius:
1229       return getBondingRadius();
1230     case T.chemicalshift:
1231       return vwr.getNMRCalculation().getChemicalShift(this);
1232     case T.covalentradius:
1233       return Elements.getCovalentRadius(atomicAndIsotopeNumber);
1234     case T.eta:
1235     case T.theta:
1236     case T.straightness:
1237       return group.getGroupParameter(tokWhat);
1238     case T.fux:
1239     case T.fracx:
1240       return getFractionalCoord(!vwr.g.legacyJavaFloat, 'X', false, ptTemp);
1241     case T.fuy:
1242     case T.fracy:
1243       return getFractionalCoord(!vwr.g.legacyJavaFloat, 'Y', false, ptTemp);
1244     case T.fuz:
1245     case T.fracz:
1246       return getFractionalCoord(!vwr.g.legacyJavaFloat, 'Z', false, ptTemp);
1247     case T.hydrophobicity:
1248       return getHydrophobicity();
1249     case T.magneticshielding:
1250       return vwr.getNMRCalculation().getMagneticShielding(this);
1251     case T.mass:
1252       return getMass();
1253     case T.occupancy:
1254       return getOccupancy100() / 100f;
1255     case T.partialcharge:
1256       return getPartialCharge();
1257     case T.phi:
1258     case T.psi:
1259     case T.omega:
1260       if (group.chain.model.isJmolDataFrame
1261           && group.chain.model.jmolFrameType
1262               .startsWith("plot ramachandran")) {
1263         switch (tokWhat) {
1264         case T.phi:
1265           return getFractionalCoord(!vwr.g.legacyJavaFloat, 'X', false, ptTemp);
1266         case T.psi:
1267           return getFractionalCoord(!vwr.g.legacyJavaFloat, 'Y', false, ptTemp);
1268         case T.omega:
1269           if (group.chain.model.isJmolDataFrame
1270               && group.chain.model.jmolFrameType
1271                   .equals("plot ramachandran")) {
1272             float omega = getFractionalCoord(!vwr.g.legacyJavaFloat, 'Z', false, ptTemp) - 180;
1273             return (omega < -180 ? 360 + omega : omega);
1274           }
1275         }
1276       }
1277       return group.getGroupParameter(tokWhat);
1278     case T.radius:
1279     case T.spacefill:
1280       return getRadius();
1281     case T.screenx:
1282       return (vwr.antialiased ? sX / 2 : sX);
1283     case T.screeny:
1284       return vwr.getScreenHeight() - (vwr.antialiased ? sY / 2 : sY);
1285     case T.screenz:
1286       return (vwr.antialiased ? sZ / 2 : sZ);
1287     case T.selected:
1288       return (vwr.slm.isAtomSelected(i) ? 1 : 0);
1289     case T.surfacedistance:
1290       vwr.ms.getSurfaceDistanceMax();
1291       return getSurfaceDistance100() / 100f;
1292     case T.temperature: // 0 - 9999
1293       return getBfactor100() / 100f;
1294     case T.unitx:
1295       return getFractionalUnitCoord(!vwr.g.legacyJavaFloat, 'X', ptTemp);
1296     case T.unity:
1297       return getFractionalUnitCoord(!vwr.g.legacyJavaFloat, 'Y', ptTemp);
1298     case T.unitz:
1299       return getFractionalUnitCoord(!vwr.g.legacyJavaFloat, 'Z', ptTemp);
1300     case T.vanderwaals:
1301       return getVanderwaalsRadiusFloat(vwr, VDW.AUTO);
1302     case T.vectorscale:
1303       V3 v = getVibrationVector();
1304       return (v == null ? 0 : v.length() * vwr.getFloat(T.vectorscale));
1305     case T.vibx:
1306       return getVib('x');
1307     case T.viby:
1308       return getVib('y');
1309     case T.vibz:
1310       return getVib('z');
1311     case T.modx:
1312       return getVib('X');
1313     case T.mody:
1314       return getVib('Y');
1315     case T.modz:
1316       return getVib('Z');
1317     case T.modo:
1318       return getVib('O');
1319     case T.modt1:
1320       return getVib('1');
1321     case T.modt2:
1322       return getVib('2');
1323     case T.modt3:
1324       return getVib('3');
1325     case T.volume:
1326       return getVolume(vwr, VDW.AUTO);
1327     case T.fracxyz:
1328     case T.fuxyz:
1329     case T.unitxyz:
1330     case T.screenxyz:
1331     case T.vibxyz:
1332     case T.modxyz:
1333     case T.xyz:
1334       T3 v3 = atomPropertyTuple(vwr, tokWhat, ptTemp);
1335       return (v3 == null ? -1 : v3.length());
1336     }
1337     return atomPropertyInt(tokWhat);
1338   }
1339 
getVib(char ch)1340   public float getVib(char ch) {
1341     return group.chain.model.ms.getVibCoord(i, ch);
1342   }
1343 
getNominalMass()1344   public int getNominalMass() {
1345     int mass = getIsotopeNumber();
1346     return (mass > 0 ? mass : Elements.getNaturalIsotope(getElementNumber()));
1347   }
1348 
1349   @Override
getMass()1350   public float getMass() {
1351     float mass = getIsotopeNumber();
1352     return (mass > 0 ? mass : Elements.getAtomicMass(getElementNumber()));
1353   }
1354 
atomPropertyString(Viewer vwr, int tokWhat)1355   public String atomPropertyString(Viewer vwr, int tokWhat) {
1356     char ch;
1357     String s;
1358     switch (tokWhat) {
1359     case T.altloc:
1360       ch = altloc;
1361       return (ch == '\0' ? "" : "" + ch);
1362     case T.atomname:
1363       return getAtomName();
1364     case T.atomtype:
1365       return getAtomType();
1366     case T.chain:
1367       return getChainIDStr();
1368     case T.chirality:
1369       return getCIPChirality(true);
1370     case T.ciprule:
1371       return getCIPChiralityRule();
1372     case T.sequence:
1373       return getGroup1('?');
1374     case T.seqcode:
1375       s = group.getSeqcodeString();
1376       return (s == null ? "" : s);
1377     case T.group1:
1378       return getGroup1('\0');
1379     case T.group:
1380       return getGroup3(false);
1381     case T.element:
1382       return getElementSymbolIso(true);
1383     case T.identify:
1384       return getIdentity(true);
1385     case T.insertion:
1386       ch = group.getInsertionCode();
1387       return (ch == '\0' ? "" : "" + ch);
1388     case T.label:
1389     case T.format:
1390       s = (String) vwr.shm.getShapePropertyIndex(JC.SHAPE_LABELS, "label", i);
1391       if (s == null)
1392         s = "";
1393       return s;
1394     case T.structure:
1395       return group.getProteinStructureType().getBioStructureTypeName(false);
1396     case T.substructure:
1397       return group.getProteinStructureSubType().getBioStructureTypeName(false);
1398     case T.strucid:
1399       return group.getStructureId();
1400     case T.shape:
1401       return vwr.getHybridizationAndAxes(i, null, null, "d");
1402     case T.symbol:
1403       return getElementSymbolIso(false);
1404     case T.symmetry:
1405       return getSymmetryOperatorList(true);
1406     }
1407     return "";
1408   }
1409 
1410   /**
1411    * Determine R/S chirality at this position; non-H atoms only; cached in formalChargeAndFlags
1412    * @param doCalculate
1413    *
1414    * @return one of "", "R", "S", "E", "Z", "r", "s", "?"
1415    */
1416   @Override
getCIPChirality(boolean doCalculate)1417   public String getCIPChirality(boolean doCalculate) {
1418     int flags = (formalChargeAndFlags & CIP_CHIRALITY_MASK) >> CIP_CHIRALITY_OFFSET;
1419     if (flags == 0 && atomicAndIsotopeNumber > 1 && doCalculate) {
1420       flags = group.chain.model.ms.getAtomCIPChiralityCode(this);
1421       formalChargeAndFlags |= ((flags == 0 ? JC.CIP_CHIRALITY_NONE : flags) << CIP_CHIRALITY_OFFSET);
1422     }
1423     return JC.getCIPChiralityName(flags);
1424   }
1425 
getCIPChiralityRule()1426   public String getCIPChiralityRule() {
1427     String rs = getCIPChirality(true);
1428     int flags = (rs.length() == 0 ? -1 : (formalChargeAndFlags & CIP_CHIRALITY_RULE_MASK) >> CIP_CHIRALITY_RULE_OFFSET);
1429     return JC.getCIPRuleName(flags + 1);
1430   }
1431 
1432   /**
1433    *
1434    * @param c [0:unknown; 3:none; 1: R; 2: S; 5: Z; 6: E; 9: M, 10: P, +r,s
1435    */
1436   @Override
setCIPChirality(int c)1437   public void setCIPChirality(int c) {
1438     formalChargeAndFlags = (formalChargeAndFlags & ~CIP_MASK)
1439         | (c << CIP_CHIRALITY_OFFSET);
1440   }
1441 
1442   @Override
getCIPChiralityCode()1443   public int getCIPChiralityCode() {
1444     return (formalChargeAndFlags & CIP_CHIRALITY_MASK) >> CIP_CHIRALITY_OFFSET;
1445   }
1446 
1447   @Override
getInsertionCode()1448   public char getInsertionCode() {
1449     return group.getInsertionCode();
1450   }
1451 
atomPropertyTuple(Viewer vwr, int tok, P3 ptTemp)1452   public T3 atomPropertyTuple(Viewer vwr, int tok, P3 ptTemp) {
1453     switch (tok) {
1454     case T.coord:
1455       return P3.newP(this);
1456     case T.fracxyz:
1457       return getFractionalCoordPt(!vwr.g.legacyJavaFloat, false, ptTemp); // was !group.chain.model.isJmolDataFrame
1458     case T.fuxyz:
1459       return getFractionalCoordPt(!vwr.g.legacyJavaFloat, false, ptTemp);
1460     case T.unitxyz:
1461       return (group.chain.model.isJmolDataFrame ? getFractionalCoordPt(!vwr.g.legacyJavaFloat, false, ptTemp)
1462           : getFractionalUnitCoordPt(!vwr.g.legacyJavaFloat, false, ptTemp));
1463     case T.screenxyz:
1464       return P3.new3(vwr.antialiased ? sX / 2 : sX, vwr.getScreenHeight() - (vwr.antialiased ? sY / 2 : sY), vwr.antialiased ? sZ / 2 : sZ);
1465     case T.vibxyz:
1466       return getVibrationVector();
1467     case T.modxyz:
1468       JmolModulationSet ms = getModulation();
1469       return (ms == null ? null : ms.getV3());
1470     case T.xyz:
1471       return this;
1472     case T.color:
1473       return CU.colorPtFromInt(
1474           group.chain.model.ms.vwr.gdata.getColorArgbOrGray(colixAtom),
1475           ptTemp);
1476     }
1477     return null;
1478   }
1479 
1480   @Override
getOffsetResidueAtom(String name, int offset)1481   public int getOffsetResidueAtom(String name, int offset) {
1482     // used by DSSP and SMILES
1483     return group.getAtomIndex(name, offset);
1484   }
1485 
1486   @Override
isCrossLinked(Node node)1487   public boolean isCrossLinked(Node node) {
1488     return group.isCrossLinked(((Atom) node).group);
1489   }
1490 
1491   /**
1492    * Used by SMILES to get vector of cross-links
1493    */
1494   @Override
getCrossLinkVector(Lst<Integer> vReturn, boolean crosslinkCovalent, boolean crosslinkHBond)1495   public boolean getCrossLinkVector(Lst<Integer> vReturn, boolean crosslinkCovalent, boolean crosslinkHBond) {
1496     return group.getCrossLinkVector(vReturn, crosslinkCovalent, crosslinkHBond);
1497   }
1498 
1499   @Override
toString()1500   public String toString() {
1501     return getInfo();
1502   }
1503 
1504   @Override
findAtomsLike(String atomExpression)1505   public BS findAtomsLike(String atomExpression) {
1506     // for SMARTS searching
1507     return group.chain.model.ms.vwr.getAtomBitSet(atomExpression);
1508   }
1509 
getUnitID(int flags)1510   public String getUnitID(int flags) {
1511     Model m = group.chain.model;
1512     return (m.isBioModel ? ((BioModel) m).getUnitID(this, flags) : "");
1513   }
1514 
1515   @Override
getFloatProperty(String property)1516   public float getFloatProperty(String property) {
1517     Object data = group.chain.model.ms.vwr.getDataObj(property, null,
1518         JmolDataManager.DATA_TYPE_AF);
1519     float f = Float.NaN;
1520     if (data != null) {
1521       try {
1522         f = ((float[]) data)[i];
1523       } catch (Exception e) {
1524       }
1525     }
1526     return f;
1527   }
1528 
1529   @Override
modelIsRawPDB()1530   public boolean modelIsRawPDB() {
1531     Model m = group.chain.model;
1532     return (m.isBioModel && !m.isPdbWithMultipleBonds && m.hydrogenCount == 0);
1533   }
1534 
1535 }
1536