Revision: 21499
          http://sourceforge.net/p/jmol/code/21499
Author:   hansonr
Date:     2017-04-08 20:20:20 +0000 (Sat, 08 Apr 2017)
Log Message:
-----------
Jmol.___JmolVersion="14.13.1"

new feature: set labelfor {atomset} "value"
 -- allows setting of label with out changing current selection
 -- uses same syntax as LABEL command after {atomset}
 -- for example:
 
   set labelfor @atoms @myLabel
   set labelfor {atomno <= 3} @{["a","b","c"]}
   set labelfor {_C && chirality != ""} "%[atomname] %[chirality]"

bug fix: {*}.chirality with triple bonds fails

Modified Paths:
--------------
    trunk/Jmol/src/org/jmol/api/SmilesMatcherInterface.java
    trunk/Jmol/src/org/jmol/api/SymmetryInterface.java
    trunk/Jmol/src/org/jmol/modelset/Atom.java
    trunk/Jmol/src/org/jmol/modelset/AtomCollection.java
    trunk/Jmol/src/org/jmol/modelset/BondCollection.java
    trunk/Jmol/src/org/jmol/modelset/ModelSet.java
    trunk/Jmol/src/org/jmol/script/ScriptEval.java
    trunk/Jmol/src/org/jmol/script/ScriptExpr.java
    trunk/Jmol/src/org/jmol/smiles/SmilesAromatic.java
    trunk/Jmol/src/org/jmol/smiles/SmilesMatcher.java
    trunk/Jmol/src/org/jmol/smiles/SmilesSearch.java
    trunk/Jmol/src/org/jmol/smiles/SmilesStereo.java
    trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java
    trunk/Jmol/src/org/jmol/symmetry/Symmetry.java
    trunk/Jmol/src/org/jmol/viewer/Jmol.properties

Modified: trunk/Jmol/src/org/jmol/api/SmilesMatcherInterface.java
===================================================================
--- trunk/Jmol/src/org/jmol/api/SmilesMatcherInterface.java     2017-04-07 
23:40:05 UTC (rev 21498)
+++ trunk/Jmol/src/org/jmol/api/SmilesMatcherInterface.java     2017-04-08 
20:20:20 UTC (rev 21499)
@@ -56,6 +56,4 @@
 
   public int[][] getMapForJME(String jme, Atom[] at, BS bsAtoms);
 
-  public int getChirality(Node atom4, Node atom3, Node atom2, Node atom1);
-
 }

Modified: trunk/Jmol/src/org/jmol/api/SymmetryInterface.java
===================================================================
--- trunk/Jmol/src/org/jmol/api/SymmetryInterface.java  2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/api/SymmetryInterface.java  2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -218,6 +218,6 @@
 
   void toFractionalM(M4 m);
 
-  public String getChirality(Viewer vwr, Atom atom);
+  public int getChirality(Viewer vwr, Atom atom);
 
 }

Modified: trunk/Jmol/src/org/jmol/modelset/Atom.java
===================================================================
--- trunk/Jmol/src/org/jmol/modelset/Atom.java  2017-04-07 23:40:05 UTC (rev 
21498)
+++ trunk/Jmol/src/org/jmol/modelset/Atom.java  2017-04-08 20:20:20 UTC (rev 
21499)
@@ -25,13 +25,11 @@
 
 package org.jmol.modelset;
 
-import javax.annotation.processing.RoundEnvironment;
-
 import javajs.util.CU;
 import javajs.util.Lst;
+import javajs.util.P3;
 import javajs.util.PT;
 import javajs.util.SB;
-import javajs.util.P3;
 import javajs.util.T3;
 import javajs.util.V3;
 
@@ -46,11 +44,11 @@
 import org.jmol.modelsetbio.BioModel;
 import org.jmol.script.T;
 import org.jmol.util.C;
+import org.jmol.util.Edge;
 import org.jmol.util.Elements;
 import org.jmol.util.Node;
 import org.jmol.util.Point3fi;
 import org.jmol.util.Tensor;
-import org.jmol.util.Edge;
 import org.jmol.util.Vibration;
 import org.jmol.viewer.JC;
 import org.jmol.viewer.Viewer;
@@ -72,9 +70,6 @@
   public final static int ATOM_INFRAME_NOTHIDDEN = ATOM_INFRAME | 
ATOM_NOTHIDDEN;
   public final static int ATOM_SHAPE_VIS_MASK = ~ATOM_INFRAME_NOTHIDDEN;
 
-  private final static byte VIBRATION_VECTOR_FLAG = 1;
-  private final static byte IS_HETERO_FLAG = 2;
-  private final static byte FLAG_MASK = 3;
   
   public static final int RADIUS_MAX = 16;
   public static final float RADIUS_GLOBAL = 16.1f;
@@ -86,11 +81,20 @@
   public Group group;
   private float userDefinedVanDerWaalRadius;
   byte valence;
-  
   private short atomicAndIsotopeNumber;
   public BS atomSymmetry;
-  private byte formalChargeAndFlags;
 
+  private byte formalChargeAndFlags; // cccc RSvh
+  
+  private final static int CHARGE_OFFSET = 4;
+  private final static int VIBRATION_VECTOR_FLAG = 1;
+  private final static int IS_HETERO_FLAG = 2;
+  private final static int CHIRALITY_OFFSET = 2;
+  private final static int CHIRALITY_R_FLAG = 1;
+  private final static int CHIRALITY_S_FLAG = 2; // 3 is "no chirality"
+  private final static int CHIRALITY_MASK = 12;
+  private final static int FLAG_MASK = 0xF;
+
   public short madAtom;
 
   public short colixAtom;
@@ -193,6 +197,7 @@
   }
 
   private void deleteBondAt(int i) {
+    setChirality(0);
     int newLength = bonds.length - 1;
     if (newLength == 0) {
       bonds = null;
@@ -352,9 +357,13 @@
     return (formalChargeAndFlags & VIBRATION_VECTOR_FLAG) != 0;
   }
 
+  /**
+   * 
+   * @param charge from -3 to 7  
+   */
   public void setFormalCharge(int charge) {
     formalChargeAndFlags = (byte)((formalChargeAndFlags & FLAG_MASK) 
-        | ((charge == Integer.MIN_VALUE ? 0 : charge > 7 ? 7 : charge < -3 ? 
-3 : charge) << 2));
+        | ((charge == Integer.MIN_VALUE ? 0 : charge > 7 ? 7 : charge < -3 ? 
-3 : charge) << CHARGE_OFFSET));
   }
   
   void setVibrationVector() {
@@ -363,7 +372,7 @@
   
   @Override
   public int getFormalCharge() {
-    return formalChargeAndFlags >> 2;
+    return formalChargeAndFlags >> CHARGE_OFFSET;
   }
 
   // a percentage value in the range 0-100
@@ -1384,12 +1393,27 @@
   }
 
   /**
-   * Determine R/S chirality at this position
+   * Determine R/S chirality at this position; non-H atoms only; cached in 
formalChargeAndFlags
+   * 
    * @return "" or "R" or "S"
    */
   private String getChirality() {
-    return group.chain.model.ms.getChirality(this);
+    int flags = (formalChargeAndFlags & CHIRALITY_MASK) >> CHIRALITY_OFFSET;
+    if (flags == 0 && atomicAndIsotopeNumber > 1) {
+      flags = group.chain.model.ms.getChirality(this);
+      formalChargeAndFlags |= ((flags == 0 ? 3 : flags) << CHIRALITY_OFFSET);
+    }
+    return (flags == CHIRALITY_R_FLAG ? "R" : flags == CHIRALITY_S_FLAG ? "S" 
: "");
   }
+  
+  /**
+   * 
+   * @param c [0:unknown; 1: R; 2: S; 3: none]
+   */
+  void setChirality(int c) {
+    formalChargeAndFlags = (byte)((formalChargeAndFlags & ~CHIRALITY_MASK) 
+        | (c << CHIRALITY_OFFSET));
+  }
 
   @Override
   public char getInsertionCode() {

Modified: trunk/Jmol/src/org/jmol/modelset/AtomCollection.java
===================================================================
--- trunk/Jmol/src/org/jmol/modelset/AtomCollection.java        2017-04-07 
23:40:05 UTC (rev 21498)
+++ trunk/Jmol/src/org/jmol/modelset/AtomCollection.java        2017-04-08 
20:20:20 UTC (rev 21499)
@@ -113,6 +113,8 @@
 
   protected P3 averageAtomPoint;
 
+  protected boolean haveChirality;
+
   /**
    *  Binary Space Partitioning Forest
    */

Modified: trunk/Jmol/src/org/jmol/modelset/BondCollection.java
===================================================================
--- trunk/Jmol/src/org/jmol/modelset/BondCollection.java        2017-04-07 
23:40:05 UTC (rev 21498)
+++ trunk/Jmol/src/org/jmol/modelset/BondCollection.java        2017-04-08 
20:20:20 UTC (rev 21499)
@@ -83,6 +83,9 @@
   public void resetMolecules() {
     molecules = null;
     moleculeCount = 0;
+    if (haveChirality)
+      for (int i = ac; --i >= 0;)
+        at[i].setChirality(0);
   }
 
   public BondIterator getBondIteratorForType(int bondType, BS bsAtoms) {

Modified: trunk/Jmol/src/org/jmol/modelset/ModelSet.java
===================================================================
--- trunk/Jmol/src/org/jmol/modelset/ModelSet.java      2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/modelset/ModelSet.java      2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -940,6 +940,7 @@
     modelNames = mergeModelSet.modelNames;
     modelNumbers = mergeModelSet.modelNumbers;
     frameTitles = mergeModelSet.frameTitles;
+    haveChirality = mergeModelSet.haveChirality;
     if (msInfo != null)
       msInfo.remove("models");
     mergeAtomArrays(mergeModelSet);
@@ -4068,7 +4069,14 @@
     return (dssrData == null || dssrData.length <= i ? Float.NaN : 
dssrData[i]);
   }
 
-  public String getChirality(Atom atom) {
+  /**
+   * Determine the Cahn-Ingold-Prelog R/S chirality of an atom
+   * 
+   * @param atom
+   * @return [0:none, 1:R, 2:S]
+   */
+  public int getChirality(Atom atom) {
+    haveChirality = true;
     return Interface.getSymmetry(vwr, "ms").getChirality(vwr, atom);
   }
 

Modified: trunk/Jmol/src/org/jmol/script/ScriptEval.java
===================================================================
--- trunk/Jmol/src/org/jmol/script/ScriptEval.java      2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/script/ScriptEval.java      2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -2686,7 +2686,7 @@
       setSize(iShape, (tok == T.halo ? -1000f : 1f));
       return;
     case T.label:
-      cmdLabel(1);
+      cmdLabel(1, null);
       return;
     case T.vector:
       cmdVector();
@@ -4134,7 +4134,7 @@
     vwr.invertSelected(pt, plane, iAtom, bs);
   }
 
-  private void cmdLabel(int index) throws ScriptException {
+  private void cmdLabel(int index, BS bs) throws ScriptException {
     if (chk)
       return;
     sm.loadShape(JC.SHAPE_LABELS);
@@ -4156,7 +4156,7 @@
     default:
       strLabel = paramAsStr(index);
     }
-    sm.setLabel(strLabel, vwr.bsA());
+    sm.setLabel(strLabel, bs == null ? vwr.bsA() : bs);
   }
 
   public void cmdLoad() throws ScriptException {
@@ -6718,7 +6718,7 @@
       cmdHistory(2);
       return;
     case T.label:
-      cmdLabel(2);
+      cmdLabel(2, null);
       return;
     case T.unitcell:
       cmdUnitcell(2);
@@ -7100,7 +7100,7 @@
       if (lckey.indexOf("label") == 0
           && PT
               .isOneOf(lckey.substring(5),
-                  
";front;group;atom;offset;offsetexact;offsetabsolute;pointer;alignment;toggle;scalereference;"))
 {
+                  
";front;group;atom;offset;offsetexact;offsetabsolute;pointer;alignment;toggle;scalereference;for;"))
 {
         if (cmdSetLabel(lckey.substring(5)))
           return;
       }
@@ -7342,6 +7342,11 @@
     Object propertyValue = null;
     setShapeProperty(JC.SHAPE_LABELS, "setDefaults", vwr.slm.noneSelected);
     while (true) {
+      if (str.equals("for")) {
+        BS bs = atomExpressionAt(2);
+        cmdLabel(iToken + 1, bs);
+        return true;
+      }
       if (str.equals("scalereference")) {
         float scaleAngstromsPerPixel = floatParameter(2);
         if (scaleAngstromsPerPixel >= 5) // actually a zoom value

Modified: trunk/Jmol/src/org/jmol/script/ScriptExpr.java
===================================================================
--- trunk/Jmol/src/org/jmol/script/ScriptExpr.java      2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/script/ScriptExpr.java      2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -2502,7 +2502,7 @@
           fixed[j] = T.tv(T.decimal, getFloatEncodedInt("" + v), v);
         } else if (v instanceof String) {
           if (!forceString && !isExpression) {
-            if ((tok != T.set || j > 1 && st[1].tok != T.echo)
+            if ((tok != T.set || j > 1 && st[1].tok != T.echo && 
!"labelfor".equalsIgnoreCase(st[1].value.toString()))
                 && T.tokAttr(tok, T.mathExpressionCommand)) {
               v = getParameter((String) v, T.variable, true);
             }

Modified: trunk/Jmol/src/org/jmol/smiles/SmilesAromatic.java
===================================================================
--- trunk/Jmol/src/org/jmol/smiles/SmilesAromatic.java  2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/smiles/SmilesAromatic.java  2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -27,6 +27,8 @@
 import java.util.Hashtable;
 
 import javajs.util.Lst;
+import javajs.util.Measure;
+import javajs.util.P3;
 import javajs.util.V3;
 
 import org.jmol.java.BS;
@@ -250,9 +252,8 @@
     if (cutoff <= 0)
       cutoff = 0.01f;
 
-    V3 vTemp = new V3();
-    V3 vA = new V3();
-    V3 vB = new V3();
+    V3 vNorm = null;
+    V3 vTemp = null;
     V3 vMean = null;
     int nPoints = bs.cardinality();
     V3[] vNorms = new V3[nPoints * 2];
@@ -285,18 +286,21 @@
         }
       }
 
-      if (vMean == null)
+      if (vMean == null) {
         vMean = new V3();
+        vNorm = new V3();
+        vTemp = new V3();
+      }
 
       // check the normal for r1 - i - r2 plane
       // check the normal for r1 - iSub - r2 plane
 
       for (int k = 0, j = i; k < 2; k++) {
-        SmilesSearch.getNormalThroughPoints(atoms[r1], atoms[j], atoms[r2],
-            vTemp, vA, vB);
-        if (!addNormal(vTemp, vMean, maxDev))
+        Measure.getNormalThroughPoints((P3) atoms[r1], (P3) atoms[j], (P3) 
atoms[r2],
+            vNorm, vTemp);
+        if (!addNormal(vNorm, vMean, maxDev))
           return false;
-        vNorms[nNorms++] = V3.newV(vTemp);
+        vNorms[nNorms++] = V3.newV(vNorm);
         if ((j = iSub) < 0)
           break;
       }

Modified: trunk/Jmol/src/org/jmol/smiles/SmilesMatcher.java
===================================================================
--- trunk/Jmol/src/org/jmol/smiles/SmilesMatcher.java   2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/smiles/SmilesMatcher.java   2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -581,9 +581,4 @@
     return null;
   }
 
-  @Override
-  public int getChirality(Node atom4, Node atom3, Node atom2, Node atom1) {
-    return SmilesStereo.getHandedness(atom4, atom3, atom2, atom1, new VTemp());
-  }
-
 }

Modified: trunk/Jmol/src/org/jmol/smiles/SmilesSearch.java
===================================================================
--- trunk/Jmol/src/org/jmol/smiles/SmilesSearch.java    2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/smiles/SmilesSearch.java    2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -32,7 +32,6 @@
 import javajs.util.P3;
 import javajs.util.SB;
 import javajs.util.T3;
-import javajs.util.V3;
 
 import org.jmol.java.BS;
 import org.jmol.util.BSUtil;
@@ -1864,31 +1863,7 @@
       }
     }
   }
-
-  /**
-   * calculates a normal to a plane for three points and returns a signed
-   * distance
-   * 
-   * @param pointA
-   * @param pointB
-   * @param pointC
-   * @param vNorm
-   * @param vAB
-   * @param vAC
-   * @return a signed distance
-   */
-  static float getNormalThroughPoints(Node pointA, Node pointB, Node pointC,
-                                      V3 vNorm, V3 vAB, V3 vAC) {
-    vAB.sub2((P3) pointB, (P3) pointA);
-    vAC.sub2((P3) pointC, (P3) pointA);
-    vNorm.cross(vAB, vAC);
-    vNorm.normalize();
-    // ax + by + cz + d = 0
-    // so if a point is in the plane, then N dot X = -d
-    vAB.setT((P3) pointA);
-    return -vAB.dot(vNorm);
-  }
-
+  
   Node findImplicitHydrogen(Node atom) {
 //    if (haveTopo) {
 //      SmilesAtom sAtom = (SmilesAtom) atom;

Modified: trunk/Jmol/src/org/jmol/smiles/SmilesStereo.java
===================================================================
--- trunk/Jmol/src/org/jmol/smiles/SmilesStereo.java    2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/smiles/SmilesStereo.java    2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -829,9 +829,9 @@
     case 4: // tetrahedral, square planar
       if (atom3 == null || atom4 == null)
         return "";
-      float d = SmilesSearch.getNormalThroughPoints(atom1, atom2, atom3,
-          v.vTemp, v.vA, v.vB);
-      if (Math.abs(distanceToPlane(v.vTemp, d, (P3) atom4)) < 0.2f) {
+      float d = Measure.getNormalThroughPoints((P3) atom1, (P3) atom2, (P3) 
atom3,
+          v.vTemp, v.vA);
+      if (Math.abs(Measure.distanceToPlaneV(v.vTemp, d, (P3) atom4)) < 0.2f) {
         chiralClass = SQUARE_PLANAR;
         if (checkStereochemistryAll(false, atom0, chiralClass, 1, atom1, atom2,
             atom3, atom4, atom5, atom6, v))
@@ -863,7 +863,7 @@
     case TETRAHEDRAL:
       return (isNot == (getHandedness(atom2, atom3, atom4, atom1, v) != 
order));
     case SQUARE_PLANAR:
-      getPlaneNormals(atom1, atom2, atom3, atom4, v);
+      getPlaneNormals((P3) atom1, (P3) atom2, (P3) atom3, (P3) atom4, v);
       // vNorm1 vNorm2 vNorm3 are right-hand normals for the given
       // triangles
       // 1-2-3, 2-3-4, 3-4-1
@@ -914,7 +914,7 @@
           || !isDiaxial(atom0, atom0, atom2, atom4, v, -0.95f)
           || !isDiaxial(atom0, atom0, atom3, atom5, v, -0.95f))
         return false;
-      getPlaneNormals(atom2, atom3, atom4, atom5, v);
+      getPlaneNormals((P3) atom2, (P3) atom3, (P3) atom4, (P3) atom5, v);
       // check for proper order 2-3-4-5
       //                          n1n2n3
       if (v.vNorm2.dot(v.vNorm3) < 0 || v.vNorm3.dot(v.vNorm4) < 0)
@@ -948,8 +948,7 @@
    * @return 1 for "@", 2 for "@@"
    */
   static int getHandedness(Node a, Node b, Node c, Node pt, VTemp v) {
-    float d = SmilesSearch.getNormalThroughPoints(a, b, c, v.vTemp, v.vA,
-        v.vB);    
+    float d = Measure.getNormalThroughPoints((P3) a, (P3) b, (P3) c, v.vTemp, 
v.vA);    
 //    int atat = (distanceToPlane(v.vTemp, d, (P3) pt) > 0 ? 1 : 2);
 //    System.out.println("$ draw p1 " + P3.newP((P3)a) +" color red '"+a+" 
[2]'");
 //    System.out.println("$ draw p2 " + P3.newP((P3)b) +"  color green '"+b+" 
[3]'");
@@ -956,26 +955,19 @@
 //    System.out.println("$ draw p3 " + P3.newP((P3)c) +"  color blue '"+c+" 
[4]'");
 //    System.out.println("$ draw p " + P3.newP((P3)a) +" " + P3.newP((P3)b) +" 
" + P3.newP((P3)c) +"" );
 //    System.out.println("$ draw v vector {" + P3.newP((P3)pt) +"}  " + 
v.vTemp+" '"+ (atat==2 ? "@@" : "@")+ pt + " [1]' color " + (atat == 2 ? 
"white" : "yellow"));
-    return (distanceToPlane(v.vTemp, d, (P3) pt) > 0 ? 1 : 2);
+    return (Measure.distanceToPlaneV(v.vTemp, d, (P3) pt) > 0 ? 1 : 2);
   }
 
-  private static void getPlaneNormals(Node atom1, Node atom2, Node atom3,
-                                      Node atom4, VTemp v) {
-    SmilesSearch.getNormalThroughPoints(atom1, atom2, atom3, v.vNorm2,
-        v.vTemp1, v.vTemp2);
-    SmilesSearch.getNormalThroughPoints(atom2, atom3, atom4, v.vNorm3,
-        v.vTemp1, v.vTemp2);
-    SmilesSearch.getNormalThroughPoints(atom3, atom4, atom1, v.vNorm4,
-        v.vTemp1, v.vTemp2);
+  private static void getPlaneNormals(P3 atom1, P3 atom2, P3 atom3,
+                                      P3 atom4, VTemp v) {
+    Measure.getNormalThroughPoints(atom1, atom2, atom3, v.vNorm2,
+        v.vTemp1);
+    Measure.getNormalThroughPoints(atom2, atom3, atom4, v.vNorm3,
+        v.vTemp1);
+    Measure.getNormalThroughPoints(atom3, atom4, atom1, v.vNorm4,
+        v.vTemp1);
   }
 
-  static float distanceToPlane(V3 norm, float w, P3 pt) {
-    return (norm == null ? Float.NaN : (norm.x * pt.x + norm.y * pt.y + norm.z
-        * pt.z + w)
-        / (float) Math
-            .sqrt(norm.x * norm.x + norm.y * norm.y + norm.z * norm.z));
-  }
-
   static int checkChirality(String pattern, int index, SmilesAtom newAtom)
       throws InvalidSmilesException {
     int stereoClass = 0;

Modified: trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java
===================================================================
--- trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java  2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/symmetry/CIPChirality.java  2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -2,9 +2,12 @@
 
 import java.util.Arrays;
 
+import javajs.util.Measure;
+import javajs.util.P3;
+import javajs.util.V3;
+
 import org.jmol.java.BS;
 
-import org.jmol.api.SmilesMatcherInterface;
 import org.jmol.util.BSUtil;
 import org.jmol.util.Edge;
 import org.jmol.util.Logger;
@@ -12,82 +15,45 @@
 import org.jmol.viewer.Viewer;
 
 /**
- * A relatively simple implementation of Cahn-Ingold-Prelog rules for 
assigning R/S chirality
+ * A relatively simple implementation of Cahn-Ingold-Prelog rules for assigning
+ * R/S chirality. Based on private knowledge of rules.
  * 
- *     [1] getChirality(Node)
- *              new CIPAtom(Node)
- *              set()
- *              return getRorS();
- *               
- *     [2] CIPAtom.getRorS()
- *              sortSubstituents()
- *              return f(SmilesMatcher().getChirality())
- *              
- *              
- *     [3] sortSubstituents()
- *             if (necessary)
-   *              breakTie(a,b)
- *              
- *     [4] breakTie(a,b)
- *              a.set(), b.set()
- *              for each substituent...
- *                compareAB(a.atoms[i], b.atoms[i])
- *              if (all are tied) ...
- *                sortSubstituents(a.atoms)
- *                sortSubstituents(b.atoms)
- *                for each substituent...
- *                   breakTie(a.atoms[i], b.atoms[i])
- *               
  * 
  * Introduced in Jmol 14.12.0
  * 
- * @author Bob Hanson hans...@stolaf.edu 
+ * @author Bob Hanson hans...@stolaf.edu
  */
 public class CIPChirality {
 
-  // Checked using:
-  
-  //  function checkchiral(m) {
-  //    if (m)  load @m
-  //    background label yellow
-  //    color labels black
-  //    select _C
-  //    label %[atomname]
-  //    refresh
-  //    var b = {_C}
-  //    for (var a in b) {
-  //      var c = a.chirality;
-  //      print _smilesString + " " + a + c
-  //      if (c) {
-  //         select a
-  //         c = a.atomname + " " + c
-  //         label @c
-  //      }
-  //    }
-  //    select *
-  //  }
+  // Pseudocode:
   //
-  //  checkchiral("$(R)-glycidol")
-  //  delay 1
-  //  checkchiral("$glucose")
-  //  delay 1
-  //  checkchiral("$(2S,3R)-2,3-oxiranediol")
-  //  delay 1
-  //  checkchiral("$(S)-2-butanol")
-  //  delay 1
-  //  checkchiral("$(R)-2-butanol")
-  //  delay 1
-  //  checkchiral("$(2S,3R)-2,3-butanediol")
-  //  delay 1
-  //  checkchiral("$(2S,3S)-2,3-butanediol")
-  //  delay 1
-  //  checkchiral("$(2R,3R)-2,3-butanediol")
-  //  delay 1
-  //  checkchiral("$(2R,3S)-2,3-butanediol")
-  //  delay 1
-  //  checkchiral("$1,4-dimethylcyclohexane") // no chirality
-  //  delay 1
-  //  checkchiral("$cholesterol") // (3S,8S,9S,10R,13R,14S,17R) and sidechain R
+  // [1]   if !(sortSubstituents()) return NO_CHIRALITY 
+  //       return getChirality(firstAtom = true)
+  // 
+  // 
+  // [3] sortSubstituents()
+  //       for all pairs of substituents i,j...
+  //          compare(i,j)
+  //          if (TIED and firstAtom) 
+  //             breakTie(i,j) or return NO_CHIRALITY
+  // 
+  // [4] breakTie(a,b) 
+  //       for each substituent i of a and b...
+  //          compareIgnoreDummy(a.sub_i, b.sub_i) 
+  //          if (a winner) return A_WINS or B_WINS 
+  //       a.sortSubstituents(firstAtom = false)
+  //       b.sortSubstituents(firstAtom = false) 
+  //       for each substituent...
+  //          breakTie(a.sub_i, b.sub_i) or return TIED 
+  //       return TIED
+  // 
+  // [5] return checkWinding()
+  //        return 
+
+  static final int NO_CHIRALITY = 0;
+  static final int TIED = NO_CHIRALITY;
+  static final int A_WINS = 1;
+  static final int B_WINS = -1;
   
   Viewer vwr;
 
@@ -100,146 +66,124 @@
     return this;
   }
 
-  public String getChirality(Node atom) {
-    if (atom.getCovalentBondCount() != 4)
-      return "";
-    CIPAtom a = new CIPAtom(atom, null, false);
-    if (!a.set())
-      return "";
-    String rs = getRorS(a);
-    if (Logger.debugging)
-      Logger.info(atom + " " + rs);
-    return rs;
-  }
+  /**
+   * Determine the Cahn-Ingold-Prelog R/S chirality of an atom
+   * 
+   * @param atom
+   * @return [0:none, 1:R, 2:S]
+   */
+  public int getChirality(Node atom) {
+    int rs = NO_CHIRALITY;
+    CIPAtom a;
+    if (atom.getCovalentBondCount() != 4
+        || !(a = new CIPAtom(atom, null, false)).set())
+      return rs;
 
-  private String getRorS(CIPAtom a) {
     try {
-      CIPAtom[] atoms = sortSubstituents(a.atoms, false);
-      if (atoms == null)
-        return "";
-      SmilesMatcherInterface sm = vwr.getSmilesMatcher();
-      switch (sm.getChirality(atoms[3].atom, atoms[2].atom,
-          atoms[1].atom, atoms[0].atom)) {
-      case 1:
-        return "R";
-      case 2:
-        return "S";
-      default:
-        return "";
-      }
+      if (a.sortSubstituents())
+        rs = getHandedness(a);
     } catch (Throwable e) {
-      e.printStackTrace();
-      return "";
+      System.out.println(e + " in CIPChirality");
+      if (!vwr.isJS)
+        e.printStackTrace();
     }
+    if (Logger.debugging)
+      Logger.info(atom + " " + (rs == 1 ? "R" : rs == 2 ? "S" : ""));
+    return rs;
   }
 
   /**
-   * Sort the substituents of an atom in preparation for comparing the set 
with other atoms
+   * determine the winding of the circuit p1--p2--p3 relative to point p4
    * 
-   * @param atoms may be 3 or 4 atoms here
-   * @param allowTie 
-   * 
-   * @return true if we have four distinct atoms in the end
+   * @param a 
+   * @return 1 for "R", 2 for "S"
    */
-  private static CIPAtom[] sortSubstituents(CIPAtom[] atoms, boolean allowTie) 
{
-    int n = atoms.length;
-    for (int i = 0; i < n; i++)
-      atoms[i].isAbove = 0;
-    for (int i = 0; i < n; i++) {
-      CIPAtom a = atoms[i];
-      for (int j = i + 1; j < n; j++) {
-        CIPAtom b = atoms[j];
-        int score = (int) Math.signum(a.compareTo(b));
-        if (Logger.debugging)
-          Logger.info("comparing " + a + " and " + b + " = " + score);
-        switch (score) {
-        case 1:
-          a.isAbove++;
-          break;
-        case -1:
-          b.isAbove++;
-          break;
-        case 0:
-          switch (breakTie(a, b)) {
-          case 1:
-            a.isAbove++;
-            break;
-          case -1:
-            b.isAbove++;
-            break;
-          case 0:
-            if (allowTie)
-              a.isAbove++;
-            else
-              return null;
-          }
-        }
-      }
-    }
-    CIPAtom[] ret = new CIPAtom[n];
-    for (int i = 0; i < n; i++)
-      ret[atoms[i].isAbove] = atoms[i];
-    if (Logger.debugging)
-      for (int i = 0; i < n; i++)
-        Logger.info("" + ret[i]);
-    return ret;
+  static int getHandedness(CIPAtom a) {
+    P3 p1 = (P3) a.atoms[0].atom; // highest priority
+    P3 p2 = (P3) a.atoms[1].atom;
+    P3 p3 = (P3) a.atoms[2].atom;
+    P3 p4 = (P3) a.atoms[3].atom; // lowest priority
+    V3 vNorm = new V3();
+    float d = Measure.getNormalThroughPoints(p1, p2, p3, vNorm, new V3());
+    return (Measure.distanceToPlaneV(vNorm, d, p4) > 0 ? 1 : 2);
   }
 
   /**
-   * Break a tie at any level in the iteration between to atoms that
-   * otherwise are the same by sorting heir
+   * Break a tie at any level in the iteration between to atoms that otherwise
+   * are the same by sorting their substituents.
    * 
    * @param a
    * @param b
-   * @return -1
+   * @return 1 to indicate a is the winner, -1 to indicate b is the winner; 0
+   *         for a tie
    */
-  private static int breakTie(CIPAtom a, CIPAtom b) {
-    if (!a.set() || !b.set() || a.isTerminal || a.atom == b.atom)
-      return 0;
+  static int breakTie(CIPAtom a, CIPAtom b) {
+
+    // If one of them is a dummy atom (from an alkene, alkyne, or ring) and 
the other isn't, 
+    // then the dummy is the loser.
+    if (a.isDummy != b.isDummy)
+      return (a.isDummy ? -1 : 1);
+
+    // If both are dummies, it's a tie.
+    if (a.isDummy && b.isDummy)
+      return TIED;
+
+    // return NO_CHIRALITY/TIED if:
+    //  a) one or the other can't be set (because it has too many connections)
+    //  b) or one or the other is terminal (has no substituents) 
+    //  c) or they are the same atom
+    
+    if (!a.set() || !b.set() 
+        || a.isTerminal || b.isTerminal 
+        || a.atom == b.atom)
+      return NO_CHIRALITY;
     if (Logger.debugging)
       Logger.info("tie for " + a + " and " + b);
-    // check to see if any of the three connections to a and b are different.
+
+    // Phase I -- shallow check using compareIgnoreDummy
+    //
+    // Check to see if any of the three connections to a and b are different.
+    // Note that we do not consider dummy atom different from a regular atom 
here
+    // because we are just looking for atom differences, not substituent 
differences.
+    //
+    // Say we have {O (O) C} and {O O H}
+    //
+    // The rules require that we first only look at just the atoms, so OOC 
beats OOH.
+
+    int score = compareAB(a, b, true);
+    if (score != TIED)
+      return score;
+
+    // Phase I -- deep check using breakTie
+    //
+    // OK, so all three are nominally the same.
+    // Now seriously deep-sort each list based on substituents
+    // and then check them one by one to see if the tie can be broken.
+
+    a.sortSubstituents();
+    b.sortSubstituents();
+    return compareAB(a, b, false);
+  }
+
+  private static int compareAB(CIPAtom a, CIPAtom b, boolean ignoreDummy) {
     for (int i = 0; i < a.nAtoms; i++) {
       CIPAtom ai = a.atoms[i];
       CIPAtom bi = b.atoms[i];
-      int score = (int) Math.signum(compareAB(ai, bi));
-      switch (score) {
-      case -1:
-      case 1:
+      int score = (ignoreDummy ? compareIgnoreDummy(ai, bi) : breakTie(ai, 
bi));
+      if (score != TIED)
         return score;
-      case 0:
-        break;
-      }
     }
-    // all are the same -- check to break tie next level
-    // now isDummy counts
-    
-    a.atoms = sortSubstituents(a.atoms, true);
-    b.atoms = sortSubstituents(b.atoms, true);
-    
-    for (int i = 0; i < a.nAtoms; i++) {
-      CIPAtom ai = a.atoms[i];
-      CIPAtom bi = b.atoms[i];
-      int score = (ai.isDummy == bi.isDummy ? breakTie(ai, bi) : ai.isDummy ? 
-1 : 1);
-      switch (score) {
-      case -1:
-      case 1:
-        return score;
-      case 0:
-      }
-    }
-    // all are the same and no tie breakers
-    return 0;
+    return TIED;
   }
 
   private class CIPAtom implements Comparable<CIPAtom> {
 
     /**
-     * Use of Node allows us to implement this in SMILES or Jmol. 
+     * Use of Node allows us to implement this in SMILES or Jmol.
      * 
      */
     Node atom;
-    
+
     /**
      * One of the two key characteristics for assigning R and S
      */
@@ -253,24 +197,24 @@
     CIPAtom parent;
 
     /**
-     * Dummy atoms have massNo and elemNo but no substituents.
-     * They are slightly lower in priority than standard atoms.
+     * Dummy atoms have massNo and elemNo but no substituents. They are 
slightly
+     * lower in priority than standard atoms.
      * 
      */
     boolean isDummy = true;
-    
+
     /**
      * Terminal (single-valence) atoms need not be followed further.
      * 
      */
     boolean isTerminal;
-    
+
     /**
      * We only set an atom once.
      * 
      */
     boolean isSet;
-    
+
     /**
      * For the main four atoms, isAbove will increment each time they "win" in 
a
      * priority contest, thus leading to our desired ordering.
@@ -277,21 +221,21 @@
      * 
      */
     int isAbove;
-    
+
     /**
-     * It is important to keep track of the path to this atom in order
-     * to prevent infinite cycling. This is taken care of by bsPath.
-     * The last atom in the path when cyclic is a dummy atom. 
+     * It is important to keep track of the path to this atom in order to
+     * prevent infinite cycling. This is taken care of by bsPath. The last atom
+     * in the path when cyclic is a dummy atom.
      * 
      */
     BS bsPath;
-    
+
     /**
      * The substituents -- 4 for the base carbon; 3 or fewer for other atoms.
      * 
      */
     CIPAtom[] atoms;
-    
+
     /**
      * Number of substituent atoms.
      */
@@ -299,18 +243,20 @@
 
     @Override
     public String toString() {
-      return atom.toString() + (isDummy ? " *" : "" + " " + (isAbove + 1));
+      return (atom == null ? "<null>" : atom.toString())
+          + (isDummy ? " *" : "" + " " + (isAbove + 1));
     }
 
     /**
      * 
-     * @param atom or null to indicate a null placeholder
+     * @param atom
+     *        or null to indicate a null placeholder
      * @param parent
      * @param isDummy
      */
-    public CIPAtom(Node atom, CIPAtom parent, boolean isDummy) {
+    CIPAtom(Node atom, CIPAtom parent, boolean isDummy) {
       if (atom == null)
-        return; 
+        return;
       this.atom = atom;
       this.parent = parent;
       this.isTerminal = atom.getCovalentBondCount() == 1;
@@ -334,13 +280,9 @@
      * 
      */
     boolean set() {
-      if (isTerminal)
+      if (isTerminal || isSet)
         return true;
-      if (isSet)
-        return true;
       isSet = true;
-      if (isDummy)
-        return false;
       atoms = new CIPAtom[parent == null ? 4 : 3];
       int nBonds = atom.getBondCount();
       Edge[] bonds = atom.getEdges();
@@ -355,19 +297,19 @@
         int order = bond.getCovalentOrder();
         switch (order) {
         case 3:
-          if (!addAtom(pt++, other, true)) {
+          if (!addAtom(pt++, other, false)) {
             isTerminal = true;
             return false;
           }
           //$FALL-THROUGH$
         case 2:
-          if (!addAtom(pt++, other, true)) {
+          if (!addAtom(pt++, other, order != 2)) {
             isTerminal = true;
             return false;
           }
           //$FALL-THROUGH$
         case 1:
-          if (!addAtom(pt++, other, false)) {
+          if (!addAtom(pt++, other, order != 1)) {
             isTerminal = true;
             return false;
           }
@@ -381,6 +323,9 @@
       nAtoms = pt;
       for (; pt < atoms.length; pt++)
         atoms[pt] = new CIPAtom(null, null, true);
+
+      // Do an initial atom-only shallow sort using a.compareTo(b)
+
       Arrays.sort(atoms);
       return !isTerminal;
     }
@@ -401,29 +346,82 @@
     }
 
     /**
-     * used in Array.sort and sortFourAtoms; includes isDummy check
+     * Deep-Sort the substituents of an atom. 
+     * Don't allow ties if this is the first atom (parent == null).
+     * 
+     * @return true if this is not the first atom or if all four substituents 
are unique
      */
+    boolean sortSubstituents() {
+      boolean allowTie = (parent != null);
+      int n = atoms.length;
+      for (int i = 0; i < n; i++)
+        atoms[i].isAbove = 0;
+      for (int i = 0; i < n; i++) {
+        CIPAtom a = atoms[i];
+        for (int j = i + 1; j < n; j++) {
+          CIPAtom b = atoms[j];
+          int score = a.compareTo(b);
+          if (Logger.debugging)
+            Logger.info("comparing " + n + " " + i + " " + j + " " + a + " and 
" + b + " = " + score);
+          switch (score) {
+          case A_WINS:
+            a.isAbove++;
+            break;
+          case B_WINS:
+            b.isAbove++;
+            break;
+          case TIED:
+            switch (breakTie(a, b)) {
+            case TIED:
+              if (!allowTie)
+                return false;
+              //$FALL-THROUGH$
+            case A_WINS:
+              a.isAbove++;
+              break;
+            case B_WINS:
+              b.isAbove++;
+              break;
+            }
+            break;
+          }
+        }
+      }
+      CIPAtom[] ret = new CIPAtom[n];
+      for (int i = 0; i < n; i++)
+        ret[atoms[i].isAbove] = atoms[i];
+      if (Logger.debugging)
+        for (int i = 0; i < n; i++)
+          Logger.info("" + ret[i]);
+      atoms = ret;
+      return true;
+    }
+
+    /**
+     * Used in Array.sort and sortSubstituents; includes check for dummy.
+     */
     @Override
     public int compareTo(CIPAtom a) {
       return a.atom == atom ? 0 : a.atom == null ? -1 : atom == null ? 1
           : a.elemNo != elemNo ? (a.elemNo < elemNo ? -1 : 1)
-          : a.massNo != massNo ? (a.massNo < massNo ? -1 : 1)
-          : a.isDummy != isDummy ? (a.isDummy ? -1 : 1) : 0;
+              : a.massNo != massNo ? (a.massNo < massNo ? -1 : 1)
+                  : a.isDummy != isDummy ? (a.isDummy ? -1 : 1) : 0;
     }
 
   }
 
   /**
-   * Used only in breakTie; do not check dummy
+   * Used only in breakTie; do not check dummy, because this is only a shallow
+   * sort.
    * 
    * @param a
    * @param b
    * @return 1 if b is higher; -1 if a is higher; otherwise 0
    */
-  static public int compareAB(CIPAtom a, CIPAtom b) {
+  static int compareIgnoreDummy(CIPAtom a, CIPAtom b) {
     return b.atom == a.atom ? 0 : b.atom == null ? -1 : a.atom == null ? 1
         : b.elemNo != a.elemNo ? (b.elemNo < a.elemNo ? -1 : 1)
-        : b.massNo != a.massNo ? (b.massNo < a.massNo ? -1 : 1) : 0;
+            : b.massNo != a.massNo ? (b.massNo < a.massNo ? -1 : 1) : 0;
   }
 
 }

Modified: trunk/Jmol/src/org/jmol/symmetry/Symmetry.java
===================================================================
--- trunk/Jmol/src/org/jmol/symmetry/Symmetry.java      2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/symmetry/Symmetry.java      2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -765,8 +765,14 @@
     return lst;
   }
 
+  /**
+   * Determine the Cahn-Ingold-Prelog R/S chirality of an atom
+   * 
+   * @param atom
+   * @return [0:none, 1:R, 2:S]
+   */
   @Override
-  public String getChirality(Viewer vwr, Atom atom) {
+  public int getChirality(Viewer vwr, Atom atom) {
     CIPChirality cip = getCIPChirality(vwr);
     return cip.getChirality(atom);
   }

Modified: trunk/Jmol/src/org/jmol/viewer/Jmol.properties
===================================================================
--- trunk/Jmol/src/org/jmol/viewer/Jmol.properties      2017-04-07 23:40:05 UTC 
(rev 21498)
+++ trunk/Jmol/src/org/jmol/viewer/Jmol.properties      2017-04-08 20:20:20 UTC 
(rev 21499)
@@ -49,8 +49,21 @@
 # 10. Run jmol/tools build-release.xml
 #
 
-Jmol.___JmolVersion="14.12.1"
+Jmol.___JmolVersion="14.13.1"
 
+new feature: set labelfor {atomset} "value"
+ -- allows setting of label with out changing current selection
+ -- uses same syntax as LABEL command after {atomset}
+ -- for example:
+ 
+   set labelfor @atoms @myLabel
+   set labelfor {atomno <= 3} @{["a","b","c"]}
+   set labelfor {_C && chirality != ""} "%[atomname] %[chirality]"
+
+bug fix: {*}.chirality with triple bonds fails
+
+JmolVersion="14.12.1"
+
 bug fix: NBO update
 bug fix: JavaScript bug - missing  Math.signum(f) - causes {atom}.chirality to 
not work in JSmol
 

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Jmol-commits mailing list
Jmol-commits@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jmol-commits

Reply via email to