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