From: <ha...@us...> - 2010-05-05 06:30:22
|
Revision: 13017 http://jmol.svn.sourceforge.net/jmol/?rev=13017&view=rev Author: hansonr Date: 2010-05-05 06:30:14 +0000 (Wed, 05 May 2010) Log Message: ----------- version=12.0.RC10_dev # new feature: smiles/smarts/substructure with tetrahedral chirality # select substructure("C[C@@]([C]=O)([CH])[CH2]") Modified Paths: -------------- trunk/Jmol/src/org/jmol/api/SmilesMatcherInterface.java trunk/Jmol/src/org/jmol/modelset/Atom.java trunk/Jmol/src/org/jmol/script/ScriptCompilationTokenParser.java trunk/Jmol/src/org/jmol/script/ScriptEvaluator.java trunk/Jmol/src/org/jmol/script/ScriptMathProcessor.java trunk/Jmol/src/org/jmol/script/Token.java trunk/Jmol/src/org/jmol/smiles/SmilesAtom.java trunk/Jmol/src/org/jmol/smiles/SmilesMatcher.java trunk/Jmol/src/org/jmol/smiles/SmilesParser.java trunk/Jmol/src/org/jmol/smiles/SmilesSearch.java trunk/Jmol/src/org/jmol/viewer/Jmol.properties Added Paths: ----------- trunk/Jmol/src/org/jmol/smiles/SmartsParser.java Modified: trunk/Jmol/src/org/jmol/api/SmilesMatcherInterface.java =================================================================== --- trunk/Jmol/src/org/jmol/api/SmilesMatcherInterface.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/api/SmilesMatcherInterface.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -6,13 +6,13 @@ public interface SmilesMatcherInterface { - public abstract BitSet getSubstructureSet(String smiles, Atom[] atoms, int atomCount, boolean isAll) + public abstract BitSet getSubstructureSet(String smiles, Atom[] atoms, int atomCount, boolean asSmarts, boolean isAll) throws Exception; public abstract BitSet[] getSubstructureSetArray(String smiles, Atom[] atoms, int atomCount, - BitSet bsSelected, BitSet bsRequired, BitSet bsNot, boolean isAll) throws Exception; + BitSet bsSelected, BitSet bsRequired, BitSet bsNot, boolean asSmarts, boolean isAll) throws Exception; - public abstract int find(String smiles1, String smiles2, boolean isAll); + public abstract int find(String pattern,/* ...in... */ String smiles, boolean asSmarts, boolean isAll); } Modified: trunk/Jmol/src/org/jmol/modelset/Atom.java =================================================================== --- trunk/Jmol/src/org/jmol/modelset/Atom.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/modelset/Atom.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -264,7 +264,7 @@ return n; } - int getCovalentHydrogenCount() { + public int getCovalentHydrogenCount() { if (bonds == null) return 0; int n = 0; Modified: trunk/Jmol/src/org/jmol/script/ScriptCompilationTokenParser.java =================================================================== --- trunk/Jmol/src/org/jmol/script/ScriptCompilationTokenParser.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/script/ScriptCompilationTokenParser.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -373,6 +373,8 @@ case Token.connected: return clauseConnected(); case Token.substructure: + case Token.smarts: + case Token.smiles: return clauseSubstructure(); case Token.within: return clauseWithin(); @@ -545,6 +547,7 @@ case Token.polymer: case Token.sequence: case Token.site: + //case Token.smarts: NOT... //case Token.smiles: NOT, because we want this only for x = within("smiles"...) not select within(smiles...) case Token.structure: case Token.string: Modified: trunk/Jmol/src/org/jmol/script/ScriptEvaluator.java =================================================================== --- trunk/Jmol/src/org/jmol/script/ScriptEvaluator.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/script/ScriptEvaluator.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -3073,6 +3073,8 @@ } break; case Token.within: + case Token.smiles: + case Token.smarts: case Token.substructure: case Token.connected: case Token.comma: Modified: trunk/Jmol/src/org/jmol/script/ScriptMathProcessor.java =================================================================== --- trunk/Jmol/src/org/jmol/script/ScriptMathProcessor.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/script/ScriptMathProcessor.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -705,7 +705,9 @@ case Token.connected: return evaluateConnected(args); case Token.substructure: - return evaluateSubstructure(args); + case Token.smiles: + case Token.smarts: + return evaluateSubstructure(args, tok); case Token.symop: return evaluateSymop(args, op.tok == Token.propselector); } @@ -1093,16 +1095,17 @@ String sFind = ScriptVariable.sValue(args[0]); String flags = (args.length > 1 ? ScriptVariable.sValue(args[1]) : ""); boolean isSmiles = sFind.equalsIgnoreCase("smiles"); - if (isSmiles || x1.tok == Token.bitset) { + boolean isSmarts = sFind.equalsIgnoreCase("smarts"); + if (isSmiles || isSmarts || x1.tok == Token.bitset) { if (x1.tok == Token.string) return addX(isSyntaxCheck ? 0 : viewer.getSmilesMatcher().find(flags, ScriptVariable.sValue(x1), - (args.length == 3 && ScriptVariable.bValue(args[2])))); + isSmarts, (args.length == 3 && ScriptVariable.bValue(args[2])))); if (x1.tok == Token.bitset) { boolean isAll = (args.length > 1 && ScriptVariable .bValue(args[args.length - 1])); - Object ret = getSmilesMatches((isSmiles ? flags : sFind), - (BitSet) x1.value, null, null, isAll); + Object ret = getSmilesMatches((isSmiles || isSmarts ? flags : sFind), + (BitSet) x1.value, null, null, !isSmiles, isAll); if (!isAll) return addX((BitSet) ret); return addX((String[]) ret); @@ -1890,6 +1893,7 @@ ((BitSet) args[2].value).nextSetBit(0), ((BitSet) args[1].value) .nextSetBit(0))); case Token.smiles: + case Token.smarts: BitSet bsSelected = null; BitSet bsRequired = null; BitSet bsNot = null; @@ -1923,7 +1927,7 @@ eval.error(ScriptEvaluator.ERROR_invalidArgument); if (isSyntaxCheck) return addX(new Vector()); - return addX((String[]) getSmilesMatches(ScriptVariable.sValue(args[1]), bsSelected, bsRequired, bsNot, true)); + return addX((String[]) getSmilesMatches(ScriptVariable.sValue(args[1]), bsSelected, bsRequired, bsNot, tok == Token.smarts, true)); } if (withinSpec instanceof String) { if (tok == Token.nada) { @@ -2035,7 +2039,7 @@ } private Object getSmilesMatches(String smiles, BitSet bsSelected, - BitSet bsRequired, BitSet bsNot, boolean isAll) throws ScriptException { + BitSet bsRequired, BitSet bsNot, boolean asSmarts, boolean isAll) throws ScriptException { if (isSyntaxCheck) { if (isAll) return new String[] {""}; @@ -2044,7 +2048,7 @@ try { BitSet[] b = viewer.getSmilesMatcher().getSubstructureSetArray( smiles, viewer.getModelSet().atoms, viewer.getAtomCount(), - bsSelected, bsRequired, bsNot, isAll); + bsSelected, bsRequired, bsNot, asSmarts, isAll); if (!isAll) return (b.length > 0 ? b[0] : new BitSet()); String[] matches = new String[b.length]; @@ -2155,15 +2159,15 @@ return addX(viewer.getAtomsConnected(min, max, order, atoms1)); } - private boolean evaluateSubstructure(ScriptVariable[] args) + private boolean evaluateSubstructure(ScriptVariable[] args, int tok) throws ScriptException { if (args.length != 1) return false; BitSet bs = new BitSet(); - String smiles = (isSyntaxCheck ? "" : ScriptVariable.sValue(args[0])); - if (smiles.length() > 0) + String pattern = (isSyntaxCheck ? "" : ScriptVariable.sValue(args[0])); + if (pattern.length() > 0) try { - bs = viewer.getSmilesMatcher().getSubstructureSet(smiles, viewer.getModelSet().atoms, viewer.getAtomCount(), true); + bs = viewer.getSmilesMatcher().getSubstructureSet(pattern, viewer.getModelSet().atoms, viewer.getAtomCount(), tok == Token.smarts, true); } catch (Exception e) { eval.evalError(e.getMessage(), null); } Modified: trunk/Jmol/src/org/jmol/script/Token.java =================================================================== --- trunk/Jmol/src/org/jmol/script/Token.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/script/Token.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -655,6 +655,8 @@ final static int forcmd = 8 | 1 << 9 | mathfunc | flowCommand; final static int ifcmd = 9 | 1 << 9 | mathfunc | flowCommand; final static int abs = 10 | 1 << 9 | mathfunc; + final static int smarts = 11 | 1 << 9 | mathfunc; + final static int smiles = 12 | 1 << 9 | mathfunc; // ___.xxx(a) @@ -1132,8 +1134,7 @@ final static int sigma = misc | 322;// new final static int sign = misc | 323;// new final static int silent = misc | 324;// new - final static int smiles = misc | 325;// new Jmol 12.0.RC5 - final static int solid = misc | 326; + final static int solid = misc | 327; final static int spacegroup = misc | 328; final static int sphere = misc | 330;// new final static int squared = misc | 332;// new @@ -1745,7 +1746,9 @@ "sin", new Token(sin), "site", new Token(site), "size", new Token(size), + "smarts", new Token(smarts), "smiles", new Token(smiles), + "substructure", null, // 12.0 substructure-->smiles (should be smarts, but for legacy reasons, need this to be smiles "solid", new Token(solid), "sort", new Token(sort), "specialPosition", new Token(specialposition), @@ -1755,7 +1758,6 @@ "straightness", new Token(straightness), "structureId", new Token(strucid), "sub", new Token(sub), - "substructure", new Token(substructure), "sum", new Token(sum), // sum "sum2", new Token(sum2), // sum of squares "surface", new Token(surface), Added: trunk/Jmol/src/org/jmol/smiles/SmartsParser.java =================================================================== --- trunk/Jmol/src/org/jmol/smiles/SmartsParser.java (rev 0) +++ trunk/Jmol/src/org/jmol/smiles/SmartsParser.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -0,0 +1,66 @@ +/* $RCSfile$ + * $Author: hansonr $ + * $Date: 2010-05-04 07:53:26 -0500 (Tue, 04 May 2010) $ + * $Revision: 13011 $ + * + * Copyright (C) 2005 The Jmol Development Team + * + * Contact: jmo...@li... + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package org.jmol.smiles; + +/** + * Parses a SMARTS String to create a <code>SmilesMolecule</code>. + * The SMILES specification has been found at the + * <a href="http://www.daylight.com/smiles/">SMILES Home Page</a>. + * An other explanation can be found in the + * <a href="http://www.daylight.com/dayhtml/doc/theory/theory.smarts.html">Daylight Smarts Theory Manual</a>. <br> + * + * Currently this parser supports only parts of the SMARTS specification. <br> + * + * An example on how to use it: + * <pre><code> + * try { + * SmartsParser sp = new SmartsParser(); + * SmilesMolecule sm = sp.parseSmarts("CC(C)C(=O)O"); + * // Use the resulting molecule + * } catch (InvalidSmilesException e) { + * // Exception management + * } + * </code></pre> + * + * @see <a href="http://www.daylight.com/smiles/">SMILES Home Page</a> + */ +public class SmartsParser extends SmilesParser { + + public static SmilesSearch getMolecule(String smarts) throws InvalidSmilesException { + return (new SmartsParser()).parse(smarts); + } + + /** + * Parses a SMARTS String + * + * @param smarts SMILES String + * @return Molecule corresponding to <code>smiles</code> + * @throws InvalidSmilesException + */ + SmilesSearch parse(String smarts) throws InvalidSmilesException { + isSmarts = true; + return super.parse(smarts); + } +} Property changes on: trunk/Jmol/src/org/jmol/smiles/SmartsParser.java ___________________________________________________________________ Added: svn:mime-type + text/plain Modified: trunk/Jmol/src/org/jmol/smiles/SmilesAtom.java =================================================================== --- trunk/Jmol/src/org/jmol/smiles/SmilesAtom.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/smiles/SmilesAtom.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -37,7 +37,7 @@ private int charge; private int hydrogenCount = Integer.MIN_VALUE; private int matchingAtom = -1; - private String chiralClass; + private int chiralClass = Integer.MIN_VALUE; private int chiralOrder = Integer.MIN_VALUE; private boolean isAromatic; private SmilesBond[] bonds = new SmilesBond[INITIAL_BONDS]; @@ -45,33 +45,7 @@ private final static int INITIAL_BONDS = 4; - /** - * Constant used for default chirality. - */ - public final static String DEFAULT_CHIRALITY = ""; - /** - * Constant used for Allene chirality. - */ - public final static String CHIRALITY_ALLENE = "AL"; - /** - * Constant used for Octahedral chirality. - */ - public final static String CHIRALITY_OCTAHEDRAL = "OH"; - /** - * Constant used for Square Planar chirality. - */ - public final static String CHIRALITY_SQUARE_PLANAR = "SP"; - /** - * Constant used for Tetrahedral chirality. - */ - public final static String CHIRALITY_TETRAHEDRAL = "TH"; - /** - * Constant used for Trigonal Bipyramidal chirality. - */ - public final static String CHIRALITY_TRIGONAL_BIPYRAMIDAL = "TB"; - - /** * Constructs a <code>SmilesAtom</code>. * * @param index Atom number in the molecule. @@ -90,32 +64,35 @@ int count = 0; if (hydrogenCount == Integer.MIN_VALUE) { // not a complete set... + // B, C, N, O, P, S, F, Cl, Br, and I + // B (3), C (4), N (3,5), O (2), P (3,5), S (2,4,6), and 1 for the halogens + switch (atomicNumber) { case -1: break; - case 1: // H - case 9: // F - case 17: // Cl - case 35: // Br - case 53: // At - case 85: // I - count = 1; + case 5: // B + count = 3; break; + case 6: // C + count = (isAromatic ? 3 : 4); + break; case 8: // O - case 16: // S - case 34: // Se - case 52: // Te count = 2; break; - case 5: // B case 7: // N - case 13: // Al + count = 3; + break; + case 16: // S + count = 2; + break; case 15: // P - case 33: // As count = 3; break; - case 6: // C - count = (isAromatic ? 3 : 4); + case 9: // F + case 17: // Cl + case 35: // Br + case 85: // I + count = 1; break; } @@ -138,11 +115,13 @@ } } } else { - count = hydrogenCount; + count = Math.abs(hydrogenCount); + hydrogenCount = Integer.MIN_VALUE; } + // Adding hydrogens - //System.out.println(" adding " + count + " H atoms to atom " + number + " " + symbol); + //System.out.println(" adding " + count + " H atoms to atom " + index); for (int i = 0; i < count; i++) { SmilesAtom hydrogen = molecule.createAtom(); hydrogen.setAtomicNumber((short) 1); @@ -171,17 +150,19 @@ * Sets the symbol of the atm. * * @param symbol Atom symbol. + * @return false if invalid symbol */ - public void setSymbol(String symbol) { + public boolean setSymbol(String symbol) { if (symbol.equals("*")) { atomicNumber = 0; - return; + return true; } isAromatic = symbol.equals(symbol.toLowerCase()); // BH added if (isAromatic) symbol = symbol.substring(0, 1).toUpperCase() + (symbol.length() == 1 ? "" : symbol.substring(1)); atomicNumber = JmolConstants.elementNumberFromSymbol(symbol); + return (atomicNumber != 0); } /** @@ -253,13 +234,24 @@ this.matchingAtom = atom; } + final static int CHIRALITY_DEFAULT = 0; + final static int CHIRALITY_ALLENE = 2; + final static int CHIRALITY_TETRAHEDRAL = 4; + final static int CHIRALITY_TRIGONAL_BIPYRAMIDAL = 5; + final static int CHIRALITY_OCTAHEDRAL = 6; + final static int CHIRALITY_SQUARE_PLANAR = 8; + + static int getChiralityClass(String xx) { + return (" ;AL;OH;SP;TH;TP".indexOf(xx) + 1)/ 3; + } + /** * Returns the chiral class of the atom. * (see <code>CHIRALITY_...</code> constants) * * @return Chiral class. */ - public String getChiralClass() { + public int getChiralClass() { return chiralClass; } @@ -269,8 +261,8 @@ * * @param chiralClass Chiral class. */ - public void setChiralClass(String chiralClass) { - this.chiralClass = (chiralClass != null) ? chiralClass.intern() : null; + public void setChiralClass(int chiralClass) { + this.chiralClass = chiralClass; } /** @@ -306,7 +298,7 @@ * @param count Number of hydrogen atoms. */ public void setHydrogenCount(int count) { - this.hydrogenCount = count; + hydrogenCount = count; } /** @@ -345,4 +337,10 @@ bonds[bondsCount] = bond; bondsCount++; } + + public int getMatchingBondedAtom(int i) { + SmilesBond b = bonds[i]; + return (b.getAtom1() == this ? b.getAtom2() : b.getAtom1()).matchingAtom; + } + } Modified: trunk/Jmol/src/org/jmol/smiles/SmilesMatcher.java =================================================================== --- trunk/Jmol/src/org/jmol/smiles/SmilesMatcher.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/smiles/SmilesMatcher.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -81,18 +81,19 @@ /** * - * searches for matches of smiles1 within smiles2 + * searches for matches of pattern within smiles * - * @param smiles1 - * @param smiles2 + * @param pattern + * @param smiles + * @param asSmarts * @param isAll - * @return number of occurances of smiles1 within smiles2 + * @return number of occurances of pattern within smiles */ - public int find(String smiles1, String smiles2, boolean isAll) { + public int find(String pattern, String smiles, boolean asSmarts, boolean isAll) { BitSet[] list = null; try { - // create a topological model set from smiles2 - SmilesSearch search = (new SmilesParser()).parseSmiles(smiles2); + // create a topological model set from smiles + SmilesSearch search = (new SmilesParser()).parse(smiles); search.isAll = isAll; int atomCount = search.patternAtomCount; Atom[] atoms = new Atom[atomCount]; @@ -139,8 +140,8 @@ atom1.bonds[bondCounts[i]++] = atom2.bonds[bondCounts[i2]++] = b; } } - list = getSubstructureSetArray(smiles1, atoms, atomCount, null, null, - null, isAll); + list = getSubstructureSetArray(pattern, atoms, atomCount, null, null, + null, asSmarts, isAll); return list.length; } catch (Exception e) { return -1; @@ -154,13 +155,14 @@ * @param smiles SMILES pattern. * @param atoms * @param atomCount + * @param asSmarts * @param isAll * @return BitSet indicating which atoms match the pattern. * @throws Exception Raised if <code>smiles</code> is not a valid SMILES pattern. */ - public BitSet getSubstructureSet(String smiles, Atom[] atoms, int atomCount, boolean isAll) throws Exception { - SmilesSearch search = SmilesParser.getMolecule(smiles); + public BitSet getSubstructureSet(String smiles, Atom[] atoms, int atomCount, boolean asSmarts, boolean isAll) throws Exception { + SmilesSearch search = (asSmarts ? SmartsParser.getMolecule(smiles) : SmilesParser.getMolecule(smiles)); search.jmolAtoms = atoms; search.jmolAtomCount = atomCount; search.isAll = isAll; @@ -176,15 +178,16 @@ * @param bsSelected * @param bsRequired * @param bsNot + * @param asSmarts * @param isAll * @return BitSet Array indicating which atoms match the pattern. * @throws Exception Raised if <code>smiles</code> is not a valid SMILES pattern. */ public BitSet[] getSubstructureSetArray(String smiles, Atom[] atoms, int atomCount, BitSet bsSelected, - BitSet bsRequired, BitSet bsNot, boolean isAll) + BitSet bsRequired, BitSet bsNot, boolean asSmarts, boolean isAll) throws Exception { - SmilesSearch search = SmilesParser.getMolecule(smiles); + SmilesSearch search = (asSmarts ? SmartsParser.getMolecule(smiles) : SmilesParser.getMolecule(smiles)); search.bsSelected = bsSelected; search.bsRequired = (bsRequired != null && bsRequired.cardinality() > 0 ? bsRequired : null); search.bsNot = bsNot; Modified: trunk/Jmol/src/org/jmol/smiles/SmilesParser.java =================================================================== --- trunk/Jmol/src/org/jmol/smiles/SmilesParser.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/smiles/SmilesParser.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -48,72 +48,107 @@ */ public class SmilesParser { - public static SmilesSearch getMolecule(String smiles) throws InvalidSmilesException { - return (new SmilesParser()).parseSmiles(smiles); + protected boolean isSmarts; + protected SmilesBond[] ringBonds; + + + public static SmilesSearch getMolecule(String pattern) throws InvalidSmilesException { + return (new SmilesParser()).parse(pattern); } - private SmilesBond[] ringBonds; - /** * Parses a SMILES String * - * @param smiles SMILES String - * @return Molecule corresponding to <code>smiles</code> + * @param pattern SMILES String + * @return Molecule corresponding to <code>pattern</code> * @throws InvalidSmilesException */ - public SmilesSearch parseSmiles(String smiles) throws InvalidSmilesException { - if (smiles == null) { + SmilesSearch parse(String pattern) throws InvalidSmilesException { + if (pattern == null) throw new InvalidSmilesException("SMILES expressions must not be null"); - } // First pass SmilesSearch molecule = new SmilesSearch(); - parseSmiles(molecule, smiles, null); - - // Implicit hydrogen creation - for (int i = 0; i< molecule.patternAtomCount; i++) { - SmilesAtom atom = molecule.getAtom(i); - atom.createMissingHydrogen(molecule); - } + molecule.isSmarts = isSmarts; + parseSmiles(molecule, pattern, null); + if (!isSmarts) + for (int i = molecule.patternAtomCount; --i >= 0; ) + molecule.getAtom(i).createMissingHydrogen(molecule); + + fixChirality(molecule); // Check for rings - if (ringBonds != null) { - for (int i = 0; i < ringBonds.length; i++) { - if (ringBonds[i] != null) { + if (ringBonds != null) + for (int i = 0; i < ringBonds.length; i++) + if (ringBonds[i] != null) throw new InvalidSmilesException("Open ring"); + + return molecule; + } + + private void fixChirality(SmilesSearch molecule) throws InvalidSmilesException { + for (int i = molecule.patternAtomCount; --i >= 0; ) { + SmilesAtom sAtom = molecule.getAtom(i); + int chiralClass = sAtom.getChiralClass(); + int nBonds = sAtom.getHydrogenCount(); + if (nBonds < 0) + nBonds = 0; + nBonds += sAtom.getBondsCount(); + switch (chiralClass) { + case SmilesAtom.CHIRALITY_DEFAULT: + switch (nBonds) { + case 2: + case 4: + case 5: + case 6: + chiralClass = nBonds; + break; } + break; + case SmilesAtom.CHIRALITY_SQUARE_PLANAR: + if (nBonds != 4) + chiralClass = SmilesAtom.CHIRALITY_DEFAULT; + break; + case SmilesAtom.CHIRALITY_ALLENE: + case SmilesAtom.CHIRALITY_OCTAHEDRAL: + case SmilesAtom.CHIRALITY_TETRAHEDRAL: + case SmilesAtom.CHIRALITY_TRIGONAL_BIPYRAMIDAL: + if (nBonds != chiralClass) + chiralClass = SmilesAtom.CHIRALITY_DEFAULT; + break; } + if (chiralClass == SmilesAtom.CHIRALITY_DEFAULT) + throw new InvalidSmilesException("Incorrect number of bonds for chirality descriptor"); + sAtom.setChiralClass(chiralClass); } - - return molecule; } /** * Parses a part of a SMILES String * * @param molecule Resulting molecule - * @param smiles SMILES String + * @param pattern SMILES String * @param currentAtom Current atom * @throws InvalidSmilesException */ - private void parseSmiles( + protected void parseSmiles( SmilesSearch molecule, - String smiles, + String pattern, SmilesAtom currentAtom) throws InvalidSmilesException { - if ((smiles == null) || (smiles.length() == 0)) { + if ((pattern == null) || (pattern.length() == 0)) { return; } // Branching int index = 0; - char firstChar = smiles.charAt(index); - if (firstChar == '(') { + char ch = pattern.charAt(0); + if (ch == '(') { index++; int currentIndex = index; int parenthesisCount = 1; - while ((currentIndex < smiles.length()) && + while ((currentIndex < pattern.length()) && (parenthesisCount > 0)) { - switch (smiles.charAt(currentIndex)) { + switch (pattern.charAt(currentIndex)) { case '(': parenthesisCount++; break; @@ -123,20 +158,18 @@ } currentIndex++; } - if (parenthesisCount != 0) { + if (parenthesisCount != 0) throw new InvalidSmilesException("Unbalanced parenthesis"); - } - String subSmiles = smiles.substring(index, currentIndex - 1); + String subSmiles = pattern.substring(index, currentIndex - 1); parseSmiles(molecule, subSmiles, currentAtom); index = currentIndex; - if (index >= smiles.length()) { - throw new InvalidSmilesException("Pattern must not end with ')'"); - } + if (index >= pattern.length()) + throw new InvalidSmilesException("Pattern must not end with ')'"); } // Bonds - firstChar = smiles.charAt(index); - int bondType = SmilesBond.getBondTypeFromCode(firstChar); + ch = pattern.charAt(index); + int bondType = SmilesBond.getBondTypeFromCode(ch); if (bondType != SmilesBond.TYPE_UNKNOWN) { if (currentAtom == null) { throw new InvalidSmilesException("Bond without a previous atom"); @@ -145,64 +178,64 @@ } // Atom - firstChar = smiles.charAt(index); - if ((firstChar >= '0') && (firstChar <= '9')) { + ch = pattern.charAt(index); + if ((ch >= '0') && (ch <= '9')) { // Ring - String subSmiles = smiles.substring(index, index + 1); + String subSmiles = pattern.substring(index, index + 1); parseRing(molecule, subSmiles, currentAtom, bondType); index++; - } else if (firstChar == '%') { + } else if (ch == '%') { // Ring index++; - if ((smiles.charAt(index) < 0) || (smiles.charAt(index) > 9)) { + if ((pattern.charAt(index) < 0) || (pattern.charAt(index) > 9)) { throw new InvalidSmilesException("Ring number must follow the % sign"); } int currentIndex = index; - while ((currentIndex < smiles.length()) && - (smiles.charAt(currentIndex) >= '0') && - (smiles.charAt(currentIndex) <= '9')) { + while ((currentIndex < pattern.length()) && + (pattern.charAt(currentIndex) >= '0') && + (pattern.charAt(currentIndex) <= '9')) { currentIndex++; } - String subSmiles = smiles.substring(index, currentIndex); + String subSmiles = pattern.substring(index, currentIndex); parseRing(molecule, subSmiles, currentAtom, bondType); index = currentIndex; - } else if (firstChar == '[') { + } else if (ch == '[') { // Atom definition index++; int currentIndex = index; - while ((currentIndex < smiles.length()) && - (smiles.charAt(currentIndex) != ']')) { + while ((currentIndex < pattern.length()) && + (pattern.charAt(currentIndex) != ']')) { currentIndex++; } - if (currentIndex >= smiles.length()) { + if (currentIndex >= pattern.length()) { throw new InvalidSmilesException("Unmatched ["); } - String subSmiles = smiles.substring(index, currentIndex); + String subSmiles = pattern.substring(index, currentIndex); currentAtom = parseAtom(molecule, subSmiles, currentAtom, bondType, true); index = currentIndex + 1; - } else if (((firstChar >= 'a') && (firstChar <= 'z')) || - ((firstChar >= 'A') && (firstChar <= 'Z')) || - (firstChar == '*')) { + } else if (((ch >= 'a') && (ch <= 'z')) || + ((ch >= 'A') && (ch <= 'Z')) || + (ch == '*')) { // Atom definition int size = 1; - if (index + 1 < smiles.length()) { - char secondChar = smiles.charAt(index + 1); - if ((firstChar >= 'A') && (firstChar <= 'Z') && + if (index + 1 < pattern.length()) { + char secondChar = pattern.charAt(index + 1); + if ((ch >= 'A') && (ch <= 'Z') && (secondChar >= 'a') && (secondChar <= 'z')) { size = 2; } } - String subSmiles = smiles.substring(index, index + size); + String subSmiles = pattern.substring(index, index + size); currentAtom = parseAtom(molecule, subSmiles, currentAtom, bondType, false); index += size; } // Next part of the SMILES String if (index == 0) { - throw new InvalidSmilesException("Unexpected character: " + smiles.charAt(0)); + throw new InvalidSmilesException("Unexpected character: " + pattern.charAt(0)); } - if (index < smiles.length()) { - String subSmiles = smiles.substring(index); + if (index < pattern.length()) { + String subSmiles = pattern.substring(index); parseSmiles(molecule, subSmiles, currentAtom); } } @@ -210,224 +243,214 @@ /** * Parses an atom definition * - * @param molecule Resulting molecule - * @param smiles SMILES String - * @param currentAtom Current atom - * @param bondType Bond type - * @param complete Indicates if is a complete definition (between []) + * @param molecule + * Resulting molecule + * @param pattern + * SMILES String + * @param currentAtom + * Current atom + * @param bondType + * Bond type + * @param complete + * Indicates if is a complete definition (between []) * @return New atom * @throws InvalidSmilesException */ - private SmilesAtom parseAtom( - SmilesSearch molecule, - String smiles, - SmilesAtom currentAtom, - int bondType, - boolean complete) throws InvalidSmilesException { - if ((smiles == null) || (smiles.length() == 0)) { + protected SmilesAtom parseAtom(SmilesSearch molecule, String pattern, + SmilesAtom currentAtom, int bondType, + boolean complete) + throws InvalidSmilesException { + if ((pattern == null) || (pattern.length() == 0)) { throw new InvalidSmilesException("Empty atom definition"); } + SmilesAtom newAtom = molecule.createAtom(); - // Atomic mass - int index = 0; - char firstChar = smiles.charAt(index); - int atomicMass = Integer.MIN_VALUE; - if ((firstChar >= '0') && (firstChar <= '9')) { - int currentIndex = index; - while ((currentIndex < smiles.length()) && - (smiles.charAt(currentIndex) >= '0') && - (smiles.charAt(currentIndex) <= '9')) { - currentIndex++; - } - String sub = smiles.substring(index, currentIndex); - try { - atomicMass = Integer.parseInt(sub); - } catch (NumberFormatException e) { - throw new InvalidSmilesException("Non numeric atomic mass"); - } - index = currentIndex; - } + pattern = checkCharge(pattern, newAtom); + pattern = checkChirality(pattern, newAtom); - // Symbol - if (index >= smiles.length()) { - throw new InvalidSmilesException("Missing atom symbol"); - } - firstChar = smiles.charAt(index); - if (((firstChar < 'a') || (firstChar > 'z')) && - ((firstChar < 'A') || (firstChar > 'Z')) && - (firstChar != '*')) { - throw new InvalidSmilesException("Unexpected atom symbol"); - } - int size = 1; - if (index + 1 < smiles.length()) { - char secondChar = smiles.charAt(index + 1); - if ((firstChar >= 'A') && (firstChar <= 'Z') && - (secondChar >= 'a') && (secondChar <= 'z')) { - size = 2; + int len = pattern.length(); + int index = 0; + int pt = index; + char ch = pattern.charAt(0); + + // isotope + if (Character.isDigit(ch)) { + while (pt < len && Character.isDigit(pattern.charAt(pt))) + pt++; + try { + newAtom.setAtomicMass(Integer.parseInt(pattern.substring(index, pt))); + } catch (NumberFormatException e) { + throw new InvalidSmilesException("Non numeric atomic mass"); } + index = pt; } - String atomSymbol = smiles.substring(index, index + size); + // Symbol + if (index >= len) + throw new InvalidSmilesException("Missing atom symbol"); + ch = pattern.charAt(index); + if (ch != '*' && !Character.isLetter(ch)) + throw new InvalidSmilesException("Unexpected atom symbol"); + char nextChar = (index + 1 < len ? pattern.charAt(index + 1) : 'Z'); + int size = (Character.isLetter(ch) + && Character.isLowerCase(nextChar) ? 2 : 1); + if (size == 2 && nextChar == 'h' && index + 2 < len + && Character.isDigit(pattern.charAt(index + 2))) + size = 1; + if (!newAtom.setSymbol(Character.toUpperCase(ch) + + pattern.substring(index + 1, index + size))) + throw new InvalidSmilesException("Invalid atom symbol"); index += size; - // Chirality - String chiralClass = null; - int chiralOrder = Integer.MIN_VALUE; - if (index < smiles.length()) { - firstChar = smiles.charAt(index); - if (firstChar == '@') { - index++; - if (index < smiles.length()) { - firstChar = smiles.charAt(index); - if (firstChar == '@') { - index++; - chiralClass = SmilesAtom.DEFAULT_CHIRALITY; - chiralOrder = 2; - } else if ((firstChar >= 'A') && (firstChar <= 'Z') && (firstChar != 'H')) { - if (index + 1 < smiles.length()) { - char secondChar = smiles.charAt(index); - if ((secondChar >= 'A') && (secondChar <= 'Z')) { - chiralClass = smiles.substring(index, index + 2); - index += 2; - int currentIndex = index; - while ((currentIndex < smiles.length()) && - (smiles.charAt(currentIndex) >= '0') && - (smiles.charAt(currentIndex) <= '9')) { - currentIndex++; - } - if (currentIndex > index) { - String sub = smiles.substring(index, currentIndex); - try { - chiralOrder = Integer.parseInt(sub); - } catch (NumberFormatException e) { - throw new InvalidSmilesException("Non numeric chiral order"); - } - } else { - chiralOrder = 1; - } - index = currentIndex; - } - } - } else { - chiralClass = SmilesAtom.DEFAULT_CHIRALITY; - chiralOrder = 1; - } - } else { - chiralClass = SmilesAtom.DEFAULT_CHIRALITY; - chiralOrder = 1; + checkHydrogenCount(complete, pattern, index, newAtom); + + // Final check + + if (bondType == SmilesBond.TYPE_UNKNOWN) + bondType = SmilesBond.TYPE_SINGLE; + if ((currentAtom != null) && (bondType != SmilesBond.TYPE_NONE)) + molecule.createBond(currentAtom, newAtom, bondType); + return newAtom; + } + + private void checkHydrogenCount(boolean complete, String pattern, int index, + SmilesAtom newAtom) + throws InvalidSmilesException { + // Hydrogen count + int hydrogenCount = Integer.MIN_VALUE; + int len = pattern.length(); + if (index >= len) + return; + char ch = pattern.charAt(index); + if (ch == 'H' || isSmarts && ch == 'h') { + index++; + int pt = index; + while (pt < len && Character.isDigit(pattern.charAt(pt))) + pt++; + if (pt > index) { + try { + hydrogenCount = Integer.parseInt(pattern.substring(index, pt)); + } catch (NumberFormatException e) { + throw new InvalidSmilesException("Non numeric hydrogen count"); } + } else { + hydrogenCount = 1; } + index = pt; } + if (index < len) + throw new InvalidSmilesException( + "Unexpected characters after atom definition: " + + pattern.substring(index)); + if (ch == 'h') // minimum count + hydrogenCount = -hydrogenCount; + if (hydrogenCount == Integer.MIN_VALUE && complete) + hydrogenCount = Integer.MAX_VALUE; + newAtom.setHydrogenCount(hydrogenCount); + } - // Hydrogen count - int hydrogenCount = Integer.MIN_VALUE; - if (index < smiles.length()) { - firstChar = smiles.charAt(index); - if (firstChar == 'H') { + private String checkChirality(String pattern, SmilesAtom newAtom) + throws InvalidSmilesException { + int chiralClass = 0; + int chiralOrder = Integer.MIN_VALUE; + int pt0 = pattern.indexOf('@'); + int len = pattern.length(); + int ch; + int index = pt0; + if (index < 0) + return pattern; + chiralClass = SmilesAtom.CHIRALITY_DEFAULT; + chiralOrder = 1; + if (++index < len) { + switch (ch = pattern.charAt(index)) { + case '@': + chiralOrder = 2; index++; - int currentIndex = index; - while ((currentIndex < smiles.length()) && - (smiles.charAt(currentIndex) >= '0') && - (smiles.charAt(currentIndex) <= '9')) { - currentIndex++; - } - if (currentIndex > index) { - String sub = smiles.substring(index, currentIndex); + break; + case 'H': + break; + case 'A': + case 'O': + case 'S': + case 'T': + chiralClass = (index + 1 < len ? SmilesAtom.getChiralityClass(pattern + .substring(index, index + 2)) : -1); + index += 2; + break; + default: + chiralOrder = (Character.isDigit(ch) ? 1 : -1); + } + int pt = index; + if (chiralOrder == 1) { + while (pt < len && Character.isDigit(pattern.charAt(pt))) + pt++; + if (pt > index) { try { - hydrogenCount = Integer.parseInt(sub); + chiralOrder = Integer.parseInt(pattern.substring(index, pt)); } catch (NumberFormatException e) { - throw new InvalidSmilesException("Non numeric hydrogen count"); + chiralOrder = -1; } - } else { - hydrogenCount = 1; + index = pt; } - index = currentIndex; } + if (chiralOrder < 1 || chiralClass < 0) + throw new InvalidSmilesException("Invalid chirality descriptor"); } - if ((hydrogenCount == Integer.MIN_VALUE) && (complete)) { - hydrogenCount = 0; - } + newAtom.setChiralClass(chiralClass); + newAtom.setChiralOrder(chiralOrder); + return pattern.substring(0, pt0) + pattern.substring(index); + } + private String checkCharge(String pattern, SmilesAtom newAtom) throws InvalidSmilesException { // Charge - int charge = 0; - if (index < smiles.length()) { - firstChar = smiles.charAt(index); - if ((firstChar == '+') || (firstChar == '-')) { - int count = 1; - index++; - if (index < smiles.length()) { - char nextChar = smiles.charAt(index); - if ((nextChar >= '0') && (nextChar <= '9')) { - int currentIndex = index; - while ((currentIndex < smiles.length()) && - (smiles.charAt(currentIndex) >= '0') && - (smiles.charAt(currentIndex) <= '9')) { - currentIndex++; - } - String sub = smiles.substring(index, currentIndex); - try { - count = Integer.parseInt(sub); - } catch (NumberFormatException e) { - throw new InvalidSmilesException("Non numeric charge"); - } - index = currentIndex; - } else { - int currentIndex = index; - while ((currentIndex < smiles.length()) && - (smiles.charAt(currentIndex) == firstChar)) { - currentIndex++; - count++; - } - index = currentIndex; - } + int pt = pattern.indexOf("-") + pattern.indexOf("+") + 1; + if (pt < 0) + return pattern; + int len = pattern.length(); + char ch = pattern.charAt(pt); + int count = 1; + int index = pt + 1; + int currentIndex = index; + if (index < len) { + char nextChar = pattern.charAt(index); + if (Character.isDigit(nextChar)) { + while (currentIndex < len && Character.isDigit(pattern.charAt(currentIndex))) + currentIndex++; + try { + count = Integer.parseInt(pattern.substring(index, currentIndex)); + } catch (NumberFormatException e) { + throw new InvalidSmilesException("Non numeric charge"); } - if (firstChar == '+') { - charge = count; - } else { - charge = -count; + } else { + while (currentIndex < len + && pattern.charAt(currentIndex) == ch) { + currentIndex++; + count++; } } + index = currentIndex; } - - // Final check - if (index < smiles.length()) { - throw new InvalidSmilesException("Unexpected characters after atom definition: " + smiles.substring(index)); - } - - // Create atom - if (bondType == SmilesBond.TYPE_UNKNOWN) { - bondType = SmilesBond.TYPE_SINGLE; - } - SmilesAtom newAtom = molecule.createAtom(); - newAtom.setSymbol(atomSymbol); - newAtom.setAtomicMass(atomicMass); - newAtom.setCharge(charge); - newAtom.setChiralClass(chiralClass); - newAtom.setChiralOrder(chiralOrder); - newAtom.setHydrogenCount(hydrogenCount); - if ((currentAtom != null) && (bondType != SmilesBond.TYPE_NONE)) { - molecule.createBond(currentAtom, newAtom, bondType); - } - return newAtom; + newAtom.setCharge(ch == '+' ? count : -count); + return pattern.substring(0, pt) + pattern.substring(index, currentIndex); } /** * Parses a ring definition * * @param molecule Resulting molecule - * @param smiles SMILES String + * @param pattern SMILES String * @param currentAtom Current atom * @param bondType Bond type * @throws InvalidSmilesException */ - private void parseRing( + protected void parseRing( SmilesSearch molecule, - String smiles, + String pattern, SmilesAtom currentAtom, int bondType) throws InvalidSmilesException { // Extracting ring number int ringNum = 0; try { - ringNum = Integer.parseInt(smiles); + ringNum = Integer.parseInt(pattern); } catch (NumberFormatException e) { throw new InvalidSmilesException("Non numeric ring identifier"); } Modified: trunk/Jmol/src/org/jmol/smiles/SmilesSearch.java =================================================================== --- trunk/Jmol/src/org/jmol/smiles/SmilesSearch.java 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/smiles/SmilesSearch.java 2010-05-05 06:30:14 UTC (rev 13017) @@ -27,10 +27,13 @@ import java.util.BitSet; import java.util.Vector; +import javax.vecmath.Vector3f; + import org.jmol.modelset.Atom; import org.jmol.modelset.Bond; import org.jmol.util.Escape; import org.jmol.util.Logger; +import org.jmol.util.Measure; import org.jmol.viewer.JmolConstants; /** @@ -47,6 +50,7 @@ BitSet bsRequired; BitSet bsNot; boolean isAll; + boolean isSmarts; private final static int INITIAL_ATOMS = 16; private SmilesAtom[] atoms = new SmilesAtom[INITIAL_ATOMS]; @@ -154,6 +158,9 @@ */ private final boolean checkMatch(SmilesAtom patternAtom, int atomNum, int i) { + if (bsNot != null && bsNot.get(i) || bsSelected != null && !bsSelected.get(i)) + return true; + for (int j = 0; j < atomNum; j++) { SmilesAtom previousAtom = atoms[j]; if (previousAtom.getMatchingAtom() == i) { @@ -167,6 +174,7 @@ if (targetAtomicNumber != 0 && targetAtomicNumber != (atom.getElementNumber())) return true; + // Check isotope int targetMass = patternAtom.getAtomicMass(); if (targetMass > 0 && targetMass != atom.getIsotopeNumber()) { // smiles indicates [13C] or [12C] @@ -177,6 +185,13 @@ // Check charge if (patternAtom.getCharge() != atom.getFormalCharge()) return true; + // Check hcount + int npH = patternAtom.getHydrogenCount(); + if (npH != Integer.MIN_VALUE && npH != Integer.MAX_VALUE) { + int nH = atom.getCovalentHydrogenCount(); + if (npH < 0 && nH < -npH || npH >= 0 && nH != npH) + return true; + } // Check bonds @@ -210,11 +225,13 @@ continue; SmilesAtom atom1 = patternBond.getAtom1(); int matchingAtom = atom1.getMatchingAtom(); + // at least for SMILES... // we don't care what the bond is designated as for an aromatic atom. // That may seem strange, but it's true for aromatic carbon, as we // already know it is double- or aromatic-bonded. // for N, we assume it is attached to at least one aromatic atom, // and that is enough for us. + // this does not actually work for SMARTS boolean matchAnyBond = (isAromatic && atom1.isAromatic()); for (int k = 0; k < bonds.length; k++) { if (bonds[k].getAtomIndex1() != matchingAtom @@ -251,13 +268,24 @@ } if (!bondFound) return true; + break; } } // add this atom to the growing list patternAtom.setMatchingAtom(i); - if (++atomNum < patternAtomCount) { + atomNum++; + if (Logger.debugging) { + StringBuffer s = new StringBuffer(); + for (int k = 0; k < atomNum; k++) { + s.append("-").append(atoms[k].getMatchingAtom()); + } + s.append(" ").append(atomNum).append("/").append(getPatternAtomCount()); + Logger.debug(s.toString()); + } + + if (atomNum < patternAtomCount) { // next position... patternAtom = atoms[atomNum]; // for all the pattern bonds for this atom... @@ -275,51 +303,89 @@ // this is the iterative step if (bonds != null) for (int j = 0; j < bonds.length; j++) - if (!checkMatch(patternAtom, atomNum, atom - .getBondedAtomIndex(j))) + if (!checkMatch(patternAtom, atomNum, atom.getBondedAtomIndex(j))) return false; break; // once through } } } else { - boolean isOK = true; + if (!checkChirality()) + return true; + BitSet bs = new BitSet(); + for (int k = 0; k < patternAtomCount; k++) + bs.set(atoms[k].getMatchingAtom()); + if (bsRequired != null && !bsRequired.intersects(bs)) + return true; if (asVector) { - bsReturn = new BitSet(); - } - for (int k = 0; k < patternAtomCount; k++) { - SmilesAtom matching = atoms[k]; - bsReturn.set(matching.getMatchingAtom()); - } - if (asVector) { - if (bsNot != null && bsNot.intersects(bsReturn)) - isOK = false; - else if (bsRequired != null && !bsRequired.intersects(bsReturn)) - isOK = false; - else if (bsSelected != null) - for (int j = bsReturn.nextSetBit(0); j >= 0 && isOK; j = bsReturn - .nextSetBit(j + 1)) - isOK = bsSelected.get(j); + boolean isOK = true; for (int j = vReturn.size(); --j >= 0 && isOK;) - isOK = !(((BitSet) vReturn.get(j)).equals(bsReturn)); + isOK = !(((BitSet) vReturn.get(j)).equals(bs)); if (isOK) - vReturn.add(bsReturn); + vReturn.add(bs); + else + return true; + bsReturn = bs; + } else { + bsReturn.or(bs); } if (Logger.debugging) { StringBuffer s = new StringBuffer(); - for (int k = 0; k < atomNum; k++) { + for (int k = 0; k < atomNum; k++) s.append("-").append(atoms[k].getMatchingAtom()); - } - s.append(" ").append(atomNum).append("/") - .append(getPatternAtomCount()); + s.append(" ").append(atomNum).append("/").append(getPatternAtomCount()); Logger.debug(s.toString()); - if (isOK) - Logger.debug("match: " + Escape.escape(bsReturn)); + Logger.debug("match: " + Escape.escape(bsReturn)); } if (!isAll || bsReturn.cardinality() == jmolAtomCount) return false; } patternAtom.setMatchingAtom(-1); return true; + } + + private boolean checkChirality() { + for (int k = 0; k < patternAtomCount; k++) { + SmilesAtom sAtom = atoms[k]; + int nH = sAtom.getHydrogenCount(); + int chiralClass = sAtom.getChiralClass(); + switch (chiralClass) { + case Integer.MIN_VALUE: + break; + case SmilesAtom.CHIRALITY_TETRAHEDRAL: + Atom atom1 = null; + switch (nH) { + case 0: + atom1 = jmolAtoms[sAtom.getMatchingBondedAtom(0)]; + break; + case 1: + Atom atom = jmolAtoms[sAtom.getMatchingAtom()]; + Bond[] b = atom.getBonds(); + for (int i = 0; i < b.length; i++) + if ((atom = jmolAtoms[atom.getBondedAtomIndex(i)]).getElementNumber() == 1) + break; + break; + default: + continue; + } + Atom atom2 = jmolAtoms[sAtom.getMatchingBondedAtom(1 - nH)]; + Atom atom3 = jmolAtoms[sAtom.getMatchingBondedAtom(2 - nH)]; + Atom atom4 = jmolAtoms[sAtom.getMatchingBondedAtom(3 - nH)]; + if (getChirality(atom2, atom3, atom4, atom1) != sAtom.getChiralOrder()) + return false; + break; + default: + Logger.error("chirality class " + chiralClass + " not supported"); + } + } + return true; + } + + + Vector3f vTemp = new Vector3f(); + Vector3f vA = new Vector3f(); + Vector3f vB = new Vector3f(); + private int getChirality(Atom a, Atom b, Atom c, Atom pt) { + return (Measure.getDirectedNormalThroughPoints(a, b, c, pt, vTemp, vA, vB) < 0 ? 1 : 2); } } Modified: trunk/Jmol/src/org/jmol/viewer/Jmol.properties =================================================================== --- trunk/Jmol/src/org/jmol/viewer/Jmol.properties 2010-05-04 16:37:27 UTC (rev 13016) +++ trunk/Jmol/src/org/jmol/viewer/Jmol.properties 2010-05-05 06:30:14 UTC (rev 13017) @@ -3,8 +3,17 @@ version=12.0.RC10_dev +# new feature: smiles/smarts/substructure with tetrahedral chirality +# select substructure("C[C@@]([C]=O)([CH])[CH2]") + +# note: I realize that smarts and smiles is a can of worms. +# new feature: {atomSet}.find(["SMARTS",]"smartsString"[,isAll]) +# new feature: {atomSet}.find("SMILES","smilesString"[,isAll]) +# new feature: "smilesString".find("SMARTS","smartsString" [, isAll]) +# new feature: select smarts("smartsString") +# new feature: select smiles("smilesString") alias for substructure(..) + # new feature: JME data saved in variable "jmeString" for later generation of 2D data -# new feature: {atomSet}.find("smilesString" [, isAll (FALSE)] ) # new feature: "smilesString".find("SMILES","smilesString" [, isAll (FALSE)] ) # code: Smiles refactoring and efficiencies # bug fix: SMILES parser not properly checking explicit double bond check This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |