Revision: 18465 http://sourceforge.net/p/jmol/code/18465 Author: hansonr Date: 2013-07-18 18:32:28 +0000 (Thu, 18 Jul 2013) Log Message: -----------
Modified Paths: -------------- branches/v13_0/Jmol/src/org/jmol/adapter/readers/cifpdb/PdbReader.java branches/v13_0/Jmol/src/org/jmol/shapebio/BioShapeRenderer.java branches/v13_0/Jmol/src/org/jmol/shapespecial/Ellipsoids.java branches/v13_0/Jmol/src/org/jmol/symmetry/SymmetryOperation.java branches/v13_0/Jmol/src/org/jmol/viewer/Jmol.properties Modified: branches/v13_0/Jmol/src/org/jmol/adapter/readers/cifpdb/PdbReader.java =================================================================== --- branches/v13_0/Jmol/src/org/jmol/adapter/readers/cifpdb/PdbReader.java 2013-07-18 18:06:27 UTC (rev 18464) +++ branches/v13_0/Jmol/src/org/jmol/adapter/readers/cifpdb/PdbReader.java 2013-07-18 18:32:28 UTC (rev 18465) @@ -46,28 +46,23 @@ /** * PDB file reader. - * + * *<p> - * <a href='http://www.rcsb.org'> - * http://www.rcsb.org - * </a> - * + * <a href='http://www.rcsb.org'> http://www.rcsb.org </a> + * * @author Miguel, Egon, and Bob (hans...@stolaf.edu) * - * pqr and gromacs pdb_wide_format added by Bob - * see http://repo.or.cz/w/gromacs.git/blob/HEAD:/src/gmxlib/pdbio.c line 244 - * see http://repo.or.cz/w/gromacs.git/blob/HEAD:/src/gmxlib/pdbio.c line 323 + * pqr and gromacs pdb_wide_format added by Bob see + * http://repo.or.cz/w/gromacs.git/blob/HEAD:/src/gmxlib/pdbio.c line + * 244 see http://repo.or.cz/w/gromacs.git/blob/HEAD:/src/gmxlib/pdbio.c + * line 323 * - * symmetry added by Bob Hanson: + * symmetry added by Bob Hanson: * - * setFractionalCoordinates() - * setSpaceGroupName() - * setUnitCell() - * initializeCartesianToFractional(); - * setUnitCellItem() - * setAtomCoord() - * applySymmetryAndSetTrajectory() - * + * setFractionalCoordinates() setSpaceGroupName() setUnitCell() + * initializeCartesianToFractional(); setUnitCellItem() setAtomCoord() + * applySymmetryAndSetTrajectory() + * */ public class PdbReader extends AtomSetCollectionReader { @@ -78,36 +73,36 @@ private boolean applySymmetry; private boolean getTlsGroups; - - private boolean isMultiModel; // MODEL ... + + private boolean isMultiModel; // MODEL ... private boolean haveMappedSerials; private boolean isConnectStateBug; private boolean isLegacyModelType; private boolean gromacsWideFormat; protected boolean isPQR; - + private final Map<String, Map<String, Boolean>> htFormul = new Hashtable<String, Map<String, Boolean>>(); private Map<String, String> htHetero; private Map<String, Map<String, Object>> htSites; private Map<String, Boolean> htElementsInCurrentGroup; private Map<String, Map<String, String>> htMolIds; - + private List<Map<String, String>> vCompnds; private List<Matrix4f> vBiomts; private List<Map<String, Object>> vBiomolecules; private List<Map<String, Object>> vTlsModels; private StringBuffer sbTlsErrors; - private int[] chainAtomCounts; - + private int[] chainAtomCounts; + private StringBuffer sbIgnored, sbSelected, sbConect, sb; private int atomCount; private int maxSerial; private int nUNK; private int nRes; - + private Map<String, String> currentCompnd; private String currentGroup3; private String currentKey; @@ -126,52 +121,47 @@ private int lastTargetSerial = Integer.MIN_VALUE; private int tlsGroupID; - final private static String lineOptions = - "ATOM " + //0 - "HETATM " + //1 - "MODEL " + //2 - "CONECT " + //3 - "HELIX " + //4,5,6 - "SHEET " + - "TURN " + - "HET " + //7 - "HETNAM " + //8 - "ANISOU " + //9 - "SITE " + //10 - "CRYST1 " + //11 - "SCALE1 " + //12,13,14 - "SCALE2 " + - "SCALE3 " + - "EXPDTA " + //15 - "FORMUL " + //16 - "REMARK " + //17 - "HEADER " + //18 - "COMPND " + //19 - "SOURCE " + //20 - "TITLE "; //21 + final private static String lineOptions = "ATOM " + //0 + "HETATM " + //1 + "MODEL " + //2 + "CONECT " + //3 + "HELIX " + //4,5,6 + "SHEET " + "TURN " + "HET " + //7 + "HETNAM " + //8 + "ANISOU " + //9 + "SITE " + //10 + "CRYST1 " + //11 + "SCALE1 " + //12,13,14 + "SCALE2 " + "SCALE3 " + "EXPDTA " + //15 + "FORMUL " + //16 + "REMARK " + //17 + "HEADER " + //18 + "COMPND " + //19 + "SOURCE " + //20 + "TITLE "; //21 + @SuppressWarnings("unchecked") + @Override + protected void initializeReader() throws Exception { + setIsPDB(); + pdbHeader = (getHeader ? new StringBuffer() : null); + applySymmetry = !checkFilter("NOSYMMETRY"); + getTlsGroups = checkFilter("TLS"); + if (htParams.containsKey("vTlsModels")) { + // from load files "tls.out" "xxxx.pdb" + vTlsModels = (List<Map<String, Object>>) htParams.remove("vTlsModels"); + } + if (checkFilter("CONF ")) { + configurationPtr = parseInt(filter, filter.indexOf("CONF ") + 5); + sbIgnored = new StringBuffer(); + sbSelected = new StringBuffer(); + } + isLegacyModelType = (stateScriptVersionInt < 120000); + isConnectStateBug = (stateScriptVersionInt >= 120151 + && stateScriptVersionInt <= 120220 || stateScriptVersionInt >= 120300 + && stateScriptVersionInt <= 120320); + } - @SuppressWarnings("unchecked") -@Override - protected void initializeReader() throws Exception { - setIsPDB(); - pdbHeader = (getHeader ? new StringBuffer() : null); - applySymmetry = !checkFilter("NOSYMMETRY"); - getTlsGroups = checkFilter("TLS"); - if (htParams.containsKey("vTlsModels")) { - // from load files "tls.out" "xxxx.pdb" - vTlsModels = (List<Map<String, Object>>) htParams.remove("vTlsModels"); - } - if (checkFilter("CONF ")) { - configurationPtr = parseInt(filter, filter.indexOf("CONF ") + 5); - sbIgnored = new StringBuffer(); - sbSelected = new StringBuffer(); - } - isLegacyModelType = (stateScriptVersionInt < 120000); - isConnectStateBug = (stateScriptVersionInt >= 120151 && stateScriptVersionInt <= 120220 - || stateScriptVersionInt >= 120300 && stateScriptVersionInt <= 120320); - } - @Override protected boolean checkLine() throws Exception { int ptOption = ((lineLength = line.length()) < 6 ? -1 : lineOptions @@ -305,20 +295,24 @@ atomSetCollection.setAtomSetAuxiliaryInfo("biomolecules", vBiomolecules); setBiomoleculeAtomCounts(); if (vBiomts != null && applySymmetry) { - atomSetCollection.applySymmetry(vBiomts, notionalUnitCell, applySymmetryToBonds, filter); + atomSetCollection.applySymmetry(vBiomts, notionalUnitCell, + applySymmetryToBonds, filter); vTlsModels = null; // for now, no TLS groups for biomolecules } } if (vTlsModels != null) { - SymmetryInterface symmetry = (SymmetryInterface) Interface.getOptionInterface("symmetry.Symmetry"); + SymmetryInterface symmetry = (SymmetryInterface) Interface + .getOptionInterface("symmetry.Symmetry"); int n = atomSetCollection.getAtomSetCount(); if (n == vTlsModels.size()) { for (int i = n; --i >= 0;) setTlsGroups(i, i, symmetry); } else { - Logger.info(n + " models but " + vTlsModels.size() + " TLS descriptions"); + Logger.info(n + " models but " + vTlsModels.size() + + " TLS descriptions"); if (vTlsModels.size() == 1) { - Logger.info(" -- assuming all models have the same TLS description -- check REMARK 3 for details."); + Logger + .info(" -- assuming all models have the same TLS description -- check REMARK 3 for details."); for (int i = n; --i >= 0;) setTlsGroups(0, i, symmetry); } @@ -326,13 +320,15 @@ checkForResidualBFactors(symmetry); } if (sbTlsErrors != null) { - atomSetCollection.setAtomSetCollectionAuxiliaryInfo("tlsErrors", sbTlsErrors.toString()); + atomSetCollection.setAtomSetCollectionAuxiliaryInfo("tlsErrors", + sbTlsErrors.toString()); appendLoadNote(sbTlsErrors.toString()); } - + super.finalizeReader(); if (vCompnds != null) - atomSetCollection.setAtomSetCollectionAuxiliaryInfo("compoundSource", vCompnds); + atomSetCollection.setAtomSetCollectionAuxiliaryInfo("compoundSource", + vCompnds); if (htSites != null) { // && atomSetCollection.getAtomSetCount() == 1) addSites(htSites); } @@ -344,43 +340,44 @@ Logger.info(sbIgnored.toString()); } } - + private void checkForResidualBFactors(SymmetryInterface symmetry) { Atom[] atoms = atomSetCollection.getAtoms(); boolean isResidual = false; - for (int i = atomSetCollection.getAtomCount(); --i >= 0;) { + for (int i = atomSetCollection.getAtomCount(); --i >= 0;) { float[] anisou = tlsU.get(atoms[i]); if (anisou == null) continue; - float resid = anisou[7] - (anisou[0] + anisou[1] + anisou[2])/3f; + float resid = anisou[7] - (anisou[0] + anisou[1] + anisou[2]) / 3f; if (resid < 0 || Float.isNaN(resid)) { isResidual = true; // can't be total break; } - } - - Logger.info("TLS analysis suggests Bfactors are " + (isResidual ? "" : "NOT") + " residuals"); + } - for (Map.Entry<Atom, float[]> entry : tlsU.entrySet()) { - float[] anisou = entry.getValue(); - float resid = anisou[7]; - if (resid == 0) - continue; - if (!isResidual) - resid -= (anisou[0] + anisou[1] + anisou[2])/3f; - anisou[0] += resid; - anisou[1] += resid; - anisou[2] += resid; - entry.getKey().ellipsoid[1] = symmetry.getEllipsoid(anisou); - - // check for equal: - - System.out.println("TLS-U: " + Escape.escape(anisou)); - anisou = (entry.getKey().anisoBorU); - if (anisou != null) - System.out.println("ANISOU: " + Escape.escape(anisou)); - } - tlsU = null; + Logger.info("TLS analysis suggests Bfactors are " + + (isResidual ? "" : "NOT") + " residuals"); + + for (Map.Entry<Atom, float[]> entry : tlsU.entrySet()) { + float[] anisou = entry.getValue(); + float resid = anisou[7]; + if (resid == 0) + continue; + if (!isResidual) + resid -= (anisou[0] + anisou[1] + anisou[2]) / 3f; + anisou[0] += resid; + anisou[1] += resid; + anisou[2] += resid; + entry.getKey().ellipsoid[1] = symmetry.getEllipsoid(anisou); + + // check for equal: + + System.out.println("TLS-U: " + Escape.escape(anisou)); + anisou = (entry.getKey().anisoBorU); + if (anisou != null) + System.out.println("ANISOU: " + Escape.escape(anisou)); + } + tlsU = null; } private void header() { @@ -390,11 +387,13 @@ String pdbID = (lineLength >= 66 ? line.substring(62, 66).trim() : ""); if (pdbID.length() == 4) { atomSetCollection.setCollectionName(pdbID); - atomSetCollection.setAtomSetCollectionAuxiliaryInfo("havePDBHeaderName", Boolean.TRUE); + atomSetCollection.setAtomSetCollectionAuxiliaryInfo("havePDBHeaderName", + Boolean.TRUE); } if (lineLength > 50) line = line.substring(0, 50); - atomSetCollection.setAtomSetCollectionAuxiliaryInfo("CLASSIFICATION", line.substring(7).trim()); + atomSetCollection.setAtomSetCollectionAuxiliaryInfo("CLASSIFICATION", line + .substring(7).trim()); } private void title() { @@ -402,7 +401,7 @@ return; appendLoadNote(line.substring(10).trim()); } - + private void compnd(boolean isSource) { if (!isSource) { if (compnd == null) @@ -418,8 +417,8 @@ if (vCompnds == null) { if (isSource) return; - vCompnds = new ArrayList<Map<String,String>>(); - htMolIds = new Hashtable<String, Map<String,String>>(); + vCompnds = new ArrayList<Map<String, String>>(); + htMolIds = new Hashtable<String, Map<String, String>>(); currentCompnd = new Hashtable<String, String>(); currentCompnd.put("select", "(*)"); currentKey = "MOLECULE"; @@ -482,43 +481,42 @@ } } -/* - REMARK 350 BIOMOLECULE: 1 - REMARK 350 APPLY THE FOLLOWING TO CHAINS: 1, 2, 3, 4, 5, 6, - REMARK 350 A, B, C - REMARK 350 BIOMT1 1 1.000000 0.000000 0.000000 0.00000 - REMARK 350 BIOMT2 1 0.000000 1.000000 0.000000 0.00000 - REMARK 350 BIOMT3 1 0.000000 0.000000 1.000000 0.00000 - REMARK 350 BIOMT1 2 0.309017 -0.809017 0.500000 0.00000 - REMARK 350 BIOMT2 2 0.809017 0.500000 0.309017 0.00000 - REMARK 350 BIOMT3 2 -0.500000 0.309017 0.809017 0.00000 - - - or, as fount in http://www.ebi.ac.uk/msd-srv/pqs/pqs-doc/macmol/1k28.mmol - -REMARK 350 AN OLIGOMER OF TYPE :HEXAMERIC : CAN BE ASSEMBLED BY -REMARK 350 APPLYING THE FOLLOWING TO CHAINS: -REMARK 350 A, D -REMARK 350 BIOMT1 1 1.000000 0.000000 0.000000 0.00000 -REMARK 350 BIOMT2 1 0.000000 1.000000 0.000000 0.00000 -REMARK 350 BIOMT3 1 0.000000 0.000000 1.000000 0.00000 -REMARK 350 IN ADDITION APPLY THE FOLLOWING TO CHAINS: -REMARK 350 A, D -REMARK 350 BIOMT1 2 0.000000 -1.000000 0.000000 0.00000 -REMARK 350 BIOMT2 2 1.000000 -1.000000 0.000000 0.00000 -REMARK 350 BIOMT3 2 0.000000 0.000000 1.000000 0.00000 -REMARK 350 IN ADDITION APPLY THE FOLLOWING TO CHAINS: -REMARK 350 A, D -REMARK 350 BIOMT1 3 -1.000000 1.000000 0.000000 0.00000 -REMARK 350 BIOMT2 3 -1.000000 0.000000 0.000000 0.00000 -REMARK 350 BIOMT3 3 0.000000 0.000000 1.000000 0.00000 + /* + REMARK 350 BIOMOLECULE: 1 + REMARK 350 APPLY THE FOLLOWING TO CHAINS: 1, 2, 3, 4, 5, 6, + REMARK 350 A, B, C + REMARK 350 BIOMT1 1 1.000000 0.000000 0.000000 0.00000 + REMARK 350 BIOMT2 1 0.000000 1.000000 0.000000 0.00000 + REMARK 350 BIOMT3 1 0.000000 0.000000 1.000000 0.00000 + REMARK 350 BIOMT1 2 0.309017 -0.809017 0.500000 0.00000 + REMARK 350 BIOMT2 2 0.809017 0.500000 0.309017 0.00000 + REMARK 350 BIOMT3 2 -0.500000 0.309017 0.809017 0.00000 + + + or, as fount in http://www.ebi.ac.uk/msd-srv/pqs/pqs-doc/macmol/1k28.mmol + + REMARK 350 AN OLIGOMER OF TYPE :HEXAMERIC : CAN BE ASSEMBLED BY + REMARK 350 APPLYING THE FOLLOWING TO CHAINS: + REMARK 350 A, D + REMARK 350 BIOMT1 1 1.000000 0.000000 0.000000 0.00000 + REMARK 350 BIOMT2 1 0.000000 1.000000 0.000000 0.00000 + REMARK 350 BIOMT3 1 0.000000 0.000000 1.000000 0.00000 + REMARK 350 IN ADDITION APPLY THE FOLLOWING TO CHAINS: + REMARK 350 A, D + REMARK 350 BIOMT1 2 0.000000 -1.000000 0.000000 0.00000 + REMARK 350 BIOMT2 2 1.000000 -1.000000 0.000000 0.00000 + REMARK 350 BIOMT3 2 0.000000 0.000000 1.000000 0.00000 + REMARK 350 IN ADDITION APPLY THE FOLLOWING TO CHAINS: + REMARK 350 A, D + REMARK 350 BIOMT1 3 -1.000000 1.000000 0.000000 0.00000 + REMARK 350 BIOMT2 3 -1.000000 0.000000 0.000000 0.00000 + REMARK 350 BIOMT3 3 0.000000 0.000000 1.000000 0.00000 -*/ - - + */ + private void remark350() throws Exception { List<Matrix4f> biomts = null; - vBiomolecules = new ArrayList<Map<String,Object>>(); + vBiomolecules = new ArrayList<Map<String, Object>>(); chainAtomCounts = new int[255]; String title = ""; String chainlist = ""; @@ -562,7 +560,8 @@ } chainlist = ":" + line.substring(41).trim().replace(' ', ':'); needLine = false; - while (readLine() != null && line.indexOf("BIOMT") < 0 && line.indexOf("350") == 7) + while (readLine() != null && line.indexOf("BIOMT") < 0 + && line.indexOf("350") == 7) chainlist += ":" + line.substring(11).trim().replace(' ', ':'); if (checkFilter("BIOMOLECULE " + iMolecule + ";")) { setFilter(filter.replace(':', '_') + chainlist); @@ -611,30 +610,30 @@ } /* -REMARK 290 -REMARK 290 CRYSTALLOGRAPHIC SYMMETRY -REMARK 290 SYMMETRY OPERATORS FOR SPACE GROUP: P 1 21 1 -REMARK 290 -REMARK 290 SYMOP SYMMETRY -REMARK 290 NNNMMM OPERATOR -REMARK 290 1555 X,Y,Z -REMARK 290 2555 -X,Y+1/2,-Z -REMARK 290 -REMARK 290 WHERE NNN -> OPERATOR NUMBER -REMARK 290 MMM -> TRANSLATION VECTOR -REMARK 290 -REMARK 290 CRYSTALLOGRAPHIC SYMMETRY TRANSFORMATIONS -REMARK 290 THE FOLLOWING TRANSFORMATIONS OPERATE ON THE ATOM/HETATM -REMARK 290 RECORDS IN THIS ENTRY TO PRODUCE CRYSTALLOGRAPHICALLY -REMARK 290 RELATED MOLECULES. -REMARK 290 SMTRY1 1 1.000000 0.000000 0.000000 0.00000 -REMARK 290 SMTRY2 1 0.000000 1.000000 0.000000 0.00000 -REMARK 290 SMTRY3 1 0.000000 0.000000 1.000000 0.00000 -REMARK 290 SMTRY1 2 -1.000000 0.000000 0.000000 0.00000 -REMARK 290 SMTRY2 2 0.000000 1.000000 0.000000 9.32505 -REMARK 290 SMTRY3 2 0.000000 0.000000 -1.000000 0.00000 -REMARK 290 -REMARK 290 REMARK: NULL + REMARK 290 + REMARK 290 CRYSTALLOGRAPHIC SYMMETRY + REMARK 290 SYMMETRY OPERATORS FOR SPACE GROUP: P 1 21 1 + REMARK 290 + REMARK 290 SYMOP SYMMETRY + REMARK 290 NNNMMM OPERATOR + REMARK 290 1555 X,Y,Z + REMARK 290 2555 -X,Y+1/2,-Z + REMARK 290 + REMARK 290 WHERE NNN -> OPERATOR NUMBER + REMARK 290 MMM -> TRANSLATION VECTOR + REMARK 290 + REMARK 290 CRYSTALLOGRAPHIC SYMMETRY TRANSFORMATIONS + REMARK 290 THE FOLLOWING TRANSFORMATIONS OPERATE ON THE ATOM/HETATM + REMARK 290 RECORDS IN THIS ENTRY TO PRODUCE CRYSTALLOGRAPHICALLY + REMARK 290 RELATED MOLECULES. + REMARK 290 SMTRY1 1 1.000000 0.000000 0.000000 0.00000 + REMARK 290 SMTRY2 1 0.000000 1.000000 0.000000 0.00000 + REMARK 290 SMTRY3 1 0.000000 0.000000 1.000000 0.00000 + REMARK 290 SMTRY1 2 -1.000000 0.000000 0.000000 0.00000 + REMARK 290 SMTRY2 2 0.000000 1.000000 0.000000 9.32505 + REMARK 290 SMTRY3 2 0.000000 0.000000 -1.000000 0.00000 + REMARK 290 + REMARK 290 REMARK: NULL */ private void remark290() throws Exception { @@ -648,14 +647,14 @@ } } } - } - + } + // Gromacs pdb_wide_format: //%-6s%5u %-4.4s %3.3s %c%4d%c %10.5f%10.5f%10.5f%8.4f%8.4f %2s\n") //0 1 2 3 4 5 6 7 //01234567890123456789012345678901234567890123456789012345678901234567890123456789 //aaaaaauuuuu ssss sss cnnnnc xxxxxxxxxxyyyyyyyyyyzzzzzzzzzzccccccccrrrrrrrr - + private void atom(int serial) { Atom atom = new Atom(); atom.atomName = line.substring(12, 16).trim(); @@ -732,15 +731,14 @@ } } } - - @Override protected boolean filterAtom(Atom atom, int iAtom) { if (!super.filterAtom(atom, iAtom)) return false; if (configurationPtr > 0) { - if (atom.sequenceNumber != lastGroup || atom.insertionCode != lastInsertion) { + if (atom.sequenceNumber != lastGroup + || atom.insertionCode != lastInsertion) { conformationIndex = configurationPtr - 1; lastGroup = atom.sequenceNumber; lastInsertion = atom.insertionCode; @@ -749,12 +747,10 @@ // ignore atoms that have no designation if (atom.alternateLocationID != '\0') { // count down until we get the desired index into the list - String msg = " atom [" + atom.group3 + "]" - + atom.sequenceNumber - + (atom.insertionCode == '\0' ? "" : "^" + atom.insertionCode) - + (atom.chainID == '\0' ? "" : ":" + atom.chainID) - + "." + atom.atomName - + "%" + atom.alternateLocationID + "\n"; + String msg = " atom [" + atom.group3 + "]" + atom.sequenceNumber + + (atom.insertionCode == '\0' ? "" : "^" + atom.insertionCode) + + (atom.chainID == '\0' ? "" : ":" + atom.chainID) + "." + + atom.atomName + "%" + atom.alternateLocationID + "\n"; if (conformationIndex >= 0 && atom.alternateLocationID != lastAltLoc) { lastAltLoc = atom.alternateLocationID; conformationIndex--; @@ -787,52 +783,51 @@ } return; } - + float floatOccupancy; - + if (gromacsWideFormat) { floatOccupancy = parseFloat(line.substring(60, 68)); atom.bfactor = fixRadius(parseFloat(line.substring(68, 76))); } else { /**************************************************************** - * read the occupancy from cols 55-60 (1-based) - * --should be in the range 0.00 - 1.00 + * read the occupancy from cols 55-60 (1-based) --should be in the range + * 0.00 - 1.00 ****************************************************************/ - + floatOccupancy = parseFloat(line, 54, 60); /**************************************************************** * read the bfactor from cols 61-66 (1-based) ****************************************************************/ - atom.bfactor = parseFloat(line, 60, 66); + atom.bfactor = parseFloat(line, 60, 66); } - + atom.occupancy = (Float.isNaN(floatOccupancy) ? 100 : (int) (floatOccupancy * 100)); - + } /** - * The problem here stems from the fact that developers have not fully - * understood the PDB specifications -- and that those have changed. - * The actual rules are as follows (using 1-based numbering: + * The problem here stems from the fact that developers have not fully + * understood the PDB specifications -- and that those have changed. The + * actual rules are as follows (using 1-based numbering: * - * 1) Chemical symbols may be in columns 77 and 78 for total disambiguity. - * 2) Only valid chemical symbols should be in columns 13 and 14 - * These are the first two characters of a four-character field. - * 3) Four-character atom names for hydrogen necessarily start in - * column 13, so when that is the case, if the four-letter - * name starts with "H" then it is hydrogen regardless of what - * letter comes next. For example, "HG3 " is mercury (and should - * be in a HETATM record, not an ATOM record, anyway), but "HG33" - * is hydrogen, presumably. - * - * This leave open the ambiguity of a four-letter H name in a - * heteroatom set where the symbol is really H, not Hg or Ha, or Ho or Hf, etc. - * + * 1) Chemical symbols may be in columns 77 and 78 for total disambiguity. 2) + * Only valid chemical symbols should be in columns 13 and 14 These are the + * first two characters of a four-character field. 3) Four-character atom + * names for hydrogen necessarily start in column 13, so when that is the + * case, if the four-letter name starts with "H" then it is hydrogen + * regardless of what letter comes next. For example, "HG3 " is mercury (and + * should be in a HETATM record, not an ATOM record, anyway), but "HG33" is + * hydrogen, presumably. * + * This leave open the ambiguity of a four-letter H name in a heteroatom set + * where the symbol is really H, not Hg or Ha, or Ho or Hf, etc. + * + * * @param isHetero - * @return an atom symbol + * @return an atom symbol */ protected String deduceElementSymbol(boolean isHetero) { if (lineLength >= 78) { @@ -847,36 +842,38 @@ char ch13 = line.charAt(13); // PDB atom symbols are supposed to be in these two characters // But they could be right-aligned or left-aligned - if ((htElementsInCurrentGroup == null || - htElementsInCurrentGroup.get(line.substring(12, 14)) != null) && - Atom.isValidElementSymbolNoCaseSecondChar(ch12, ch13)) + if ((htElementsInCurrentGroup == null || htElementsInCurrentGroup.get(line + .substring(12, 14)) != null) + && Atom.isValidElementSymbolNoCaseSecondChar(ch12, ch13)) return (isHetero || ch12 != 'H' ? "" + ch12 + ch13 : "H"); // not a known two-letter code if (ch12 == 'H') // added check for PQR files "HD22" for example return "H"; // check for " NZ" for example - if ((htElementsInCurrentGroup == null || - htElementsInCurrentGroup.get("" + ch13) != null) && - Atom.isValidElementSymbol(ch13)) + if ((htElementsInCurrentGroup == null || htElementsInCurrentGroup.get("" + + ch13) != null) + && Atom.isValidElementSymbol(ch13)) return "" + ch13; // check for misplaced "O " for example - if (ch12 != ' ' && (htElementsInCurrentGroup == null || - htElementsInCurrentGroup.get("" + ch12) != null) && - Atom.isValidElementSymbol(ch12)) + if (ch12 != ' ' + && (htElementsInCurrentGroup == null || htElementsInCurrentGroup.get("" + + ch12) != null) && Atom.isValidElementSymbol(ch12)) return "" + ch12; // could be GLX or ASX; // probably a bad file. But we will make ONE MORE ATTEMPT // and read columns 14/15 instead of 12/13. What the heck! char ch14 = line.charAt(14); - if (ch12 == ' ' && ch13 != 'X' && (htElementsInCurrentGroup == null || - htElementsInCurrentGroup.get(line.substring(13, 15)) != null) && - Atom.isValidElementSymbolNoCaseSecondChar(ch13, ch14)) - return "" + ch13 + ch14; + if (ch12 == ' ' + && ch13 != 'X' + && (htElementsInCurrentGroup == null || htElementsInCurrentGroup + .get(line.substring(13, 15)) != null) + && Atom.isValidElementSymbolNoCaseSecondChar(ch13, ch14)) + return "" + ch13 + ch14; return "Xx"; } - + private boolean haveDoubleBonds; - + private void conect() { // adapted for improper non-crossreferenced files such as 1W7R if (sbConect == null) { @@ -951,21 +948,21 @@ SHEET 3 A 6 ARG A 22 ILE A 26 1 N VAL A 23 O GLU A 47 -TYPE OF HELIX CLASS NUMBER (COLUMNS 39 - 40) --------------------------------------------------------------- -Right-handed alpha (default) 1 -Right-handed omega 2 -Right-handed pi 3 -Right-handed gamma 4 -Right-handed 310 5 -Left-handed alpha 6 -Left-handed omega 7 -Left-handed gamma 8 -27 ribbon/helix 9 -Polyproline 10 + TYPE OF HELIX CLASS NUMBER (COLUMNS 39 - 40) + -------------------------------------------------------------- + Right-handed alpha (default) 1 + Right-handed omega 2 + Right-handed pi 3 + Right-handed gamma 4 + Right-handed 310 5 + Left-handed alpha 6 + Left-handed omega 7 + Left-handed gamma 8 + 27 ribbon/helix 9 + Polyproline 10 */ - + private void structure() { EnumStructure structureType = EnumStructure.NONE; EnumStructure substructureType = EnumStructure.NONE; @@ -981,7 +978,8 @@ endChainIDIndex = 31; endIndex = 33; if (line.length() >= 40) - substructureType = Structure.getHelixType(parseInt(line.substring(38, 40))); + substructureType = Structure.getHelixType(parseInt(line.substring(38, + 40))); } else if (line.startsWith("SHEET ")) { structureType = EnumStructure.SHEET; startChainIDIndex = 21; @@ -1035,18 +1033,16 @@ return 0; } } - + private void model(int modelNumber) { /**************************************************************** - * mth 2004 02 28 - * note that the pdb spec says: - * COLUMNS DATA TYPE FIELD DEFINITION - * ---------------------------------------------------------------------- - * 1 - 6 Record name "MODEL " - * 11 - 14 Integer serial Model serial number. - * - * but I received a file with the serial - * number right after the word MODEL :-( + * mth 2004 02 28 note that the pdb spec says: COLUMNS DATA TYPE FIELD + * DEFINITION + * ---------------------------------------------------------------------- 1 + * - 6 Record name "MODEL " 11 - 14 Integer serial Model serial number. + * + * but I received a file with the serial number right after the word MODEL + * :-( ****************************************************************/ checkNotPDB(); haveMappedSerials = false; @@ -1063,7 +1059,8 @@ // be no center of symmetry, no rotation-inversions, // no atom-centered rotation axes, and no mirror or glide planes. atomSetCollection.setCheckSpecial(!isPDB); - atomSetCollection.setAtomSetAuxiliaryInfo("isPDB", isPDB ? Boolean.TRUE : Boolean.FALSE); + atomSetCollection.setAtomSetAuxiliaryInfo("isPDB", isPDB ? Boolean.TRUE + : Boolean.FALSE); nUNK = nRes = 0; currentGroup3 = null; } @@ -1072,21 +1069,21 @@ float a = getFloat(6, 9); if (a == 1) a = Float.NaN; // 1 for a means no unit cell - setUnitCell(a, getFloat(15, 9), getFloat(24, 9), getFloat(33, - 7), getFloat(40, 7), getFloat(47, 7)); + setUnitCell(a, getFloat(15, 9), getFloat(24, 9), getFloat(33, 7), getFloat( + 40, 7), getFloat(47, 7)); setSpaceGroupName(parseTrimmed(line, 55, 66)); } private float getFloat(int ich, int cch) throws Exception { - return parseFloat(line, ich, ich+cch); + return parseFloat(line, ich, ich + cch); } private void scale(int n) throws Exception { int pt = n * 4 + 2; - setUnitCellItem(pt++,getFloat(10, 10)); - setUnitCellItem(pt++,getFloat(20, 10)); - setUnitCellItem(pt++,getFloat(30, 10)); - setUnitCellItem(pt++,getFloat(45, 10)); + setUnitCellItem(pt++, getFloat(10, 10)); + setUnitCellItem(pt++, getFloat(20, 10)); + setUnitCellItem(pt++, getFloat(30, 10)); + setUnitCellItem(pt++, getFloat(45, 10)); } private void expdta() { @@ -1100,14 +1097,15 @@ int ichLeftParen = formula.indexOf('('); if (ichLeftParen >= 0) { int ichRightParen = formula.indexOf(')'); - if (ichRightParen < 0 || ichLeftParen >= ichRightParen || - ichLeftParen + 1 == ichRightParen ) // pick up () case in 1SOM.pdb + if (ichRightParen < 0 || ichLeftParen >= ichRightParen + || ichLeftParen + 1 == ichRightParen) // pick up () case in 1SOM.pdb return; // invalid formula; formula = parseTrimmed(formula, ichLeftParen + 1, ichRightParen); } Map<String, Boolean> htElementsInGroup = htFormul.get(groupName); if (htElementsInGroup == null) - htFormul.put(groupName, htElementsInGroup = new Hashtable<String, Boolean>()); + htFormul.put(groupName, + htElementsInGroup = new Hashtable<String, Boolean>()); // now, look for atom names in the formula next[0] = 0; String elementWithCount; @@ -1122,7 +1120,7 @@ htElementsInGroup.put("" + chFirst, Boolean.TRUE); } } - + private void het() { if (line.length() < 30) { return; @@ -1137,7 +1135,7 @@ String hetName = parseTrimmed(line, 30, 70); htHetero.put(groupName, hetName); } - + private void hetnam() { if (htHetero == null) { htHetero = new Hashtable<String, String>(); @@ -1145,7 +1143,8 @@ String groupName = parseToken(line, 11, 14); String hetName = parseTrimmed(line, 15, 70); if (groupName == null) { - Logger.error("ERROR: HETNAM record does not contain a group name: " + line); + Logger.error("ERROR: HETNAM record does not contain a group name: " + + line); return; } String htName = htHetero.get(groupName); @@ -1155,57 +1154,57 @@ htHetero.put(groupName, hetName); //Logger.debug("hetero: "+groupName+" "+hetName); } - + /* - The ANISOU records present the anisotropic temperature factors. + The ANISOU records present the anisotropic temperature factors. -Record Format + Record Format -COLUMNS DATA TYPE FIELD DEFINITION ----------------------------------------------------------------------- - 1 - 6 Record name "ANISOU" + COLUMNS DATA TYPE FIELD DEFINITION + ---------------------------------------------------------------------- + 1 - 6 Record name "ANISOU" - 7 - 11 Integer serial Atom serial number. + 7 - 11 Integer serial Atom serial number. -13 - 16 Atom name Atom name. + 13 - 16 Atom name Atom name. -17 Character altLoc Alternate location indicator. + 17 Character altLoc Alternate location indicator. -18 - 20 Residue name resName Residue name. + 18 - 20 Residue name resName Residue name. -22 Character chainID Chain identifier. + 22 Character chainID Chain identifier. -23 - 26 Integer resSeq Residue sequence number. + 23 - 26 Integer resSeq Residue sequence number. -27 AChar iCode Insertion code. + 27 AChar iCode Insertion code. -29 - 35 Integer u[0][0] U(1,1) + 29 - 35 Integer u[0][0] U(1,1) -36 - 42 Integer u[1][1] U(2,2) + 36 - 42 Integer u[1][1] U(2,2) -43 - 49 Integer u[2][2] U(3,3) + 43 - 49 Integer u[2][2] U(3,3) -50 - 56 Integer u[0][1] U(1,2) + 50 - 56 Integer u[0][1] U(1,2) -57 - 63 Integer u[0][2] U(1,3) + 57 - 63 Integer u[0][2] U(1,3) -64 - 70 Integer u[1][2] U(2,3) + 64 - 70 Integer u[1][2] U(2,3) -73 - 76 LString(4) segID Segment identifier, left-justified. + 73 - 76 LString(4) segID Segment identifier, left-justified. -77 - 78 LString(2) element Element symbol, right-justified. + 77 - 78 LString(2) element Element symbol, right-justified. -79 - 80 LString(2) charge Charge on the atom. + 79 - 80 LString(2) charge Charge on the atom. -Details + Details -* Columns 7 - 27 and 73 - 80 are identical to the corresponding ATOM/HETATM record. + * Columns 7 - 27 and 73 - 80 are identical to the corresponding ATOM/HETATM record. -* The anisotropic temperature factors (columns 29 - 70) are scaled by a factor of 10**4 (Angstroms**2) and are presented as integers. + * The anisotropic temperature factors (columns 29 - 70) are scaled by a factor of 10**4 (Angstroms**2) and are presented as integers. -* The anisotropic temperature factors are stored in the same coordinate frame as the atomic coordinate records. + * The anisotropic temperature factors are stored in the same coordinate frame as the atomic coordinate records. */ - + private void anisou() { float[] data = new float[8]; data[6] = 1; //U not B @@ -1229,8 +1228,8 @@ data[pt] = parseFloat(line, i, i + 7); for (int i = 0; i < 6; i++) { if (Float.isNaN(data[i])) { - Logger.error("Bad ANISOU record: " + line); - return; + Logger.error("Bad ANISOU record: " + line); + return; } data[i] /= 10000f; } @@ -1240,35 +1239,36 @@ // Ortep Type 8: D = 2pi^2, C = 2, a*b* // Ortep Type 10: D = 2pi^2, C = 2, Cartesian } + /* * http://www.wwpdb.org/documentation/format23/sect7.html * - Record Format + Record Format -COLUMNS DATA TYPE FIELD DEFINITION ------------------------------------------------------------------------- - 1 - 6 Record name "SITE " - 8 - 10 Integer seqNum Sequence number. -12 - 14 LString(3) siteID Site name. -16 - 17 Integer numRes Number of residues comprising + COLUMNS DATA TYPE FIELD DEFINITION + ------------------------------------------------------------------------ + 1 - 6 Record name "SITE " + 8 - 10 Integer seqNum Sequence number. + 12 - 14 LString(3) siteID Site name. + 16 - 17 Integer numRes Number of residues comprising site. -19 - 21 Residue name resName1 Residue name for first residue + 19 - 21 Residue name resName1 Residue name for first residue comprising site. -23 Character chainID1 Chain identifier for first residue + 23 Character chainID1 Chain identifier for first residue comprising site. -24 - 27 Integer seq1 Residue sequence number for first + 24 - 27 Integer seq1 Residue sequence number for first residue comprising site. -28 AChar iCode1 Insertion code for first residue + 28 AChar iCode1 Insertion code for first residue comprising site. -30 - 32 Residue name resName2 Residue name for second residue -... -41 - 43 Residue name resName3 Residue name for third residue -... -52 - 54 Residue name resName4 Residue name for fourth residue - + 30 - 32 Residue name resName2 Residue name for second residue + ... + 41 - 43 Residue name resName3 Residue name for third residue + ... + 52 - 54 Residue name resName4 Residue name for fourth residue + */ - + private void site() { if (htSites == null) { htSites = new Hashtable<String, Map<String, Object>>(); @@ -1284,7 +1284,7 @@ htSite.put("groups", ""); htSites.put(siteID, htSite); } - String groups = (String)htSite.get("groups"); + String groups = (String) htSite.get("groups"); for (int i = 0; i < 4; i++) { int pt = 18 + i * 11; String resName = parseTrimmed(line, pt, pt + 3); @@ -1399,10 +1399,10 @@ range.put("residues", new int[] { res1, res2 }); ranges.add(range); } else { - tlsAddError(" TLS group residues are not in order (range ignored)"); + tlsAddError(" TLS group residues are not in order (range ignored)"); } } else { - tlsAddError(" TLS group chains are different (range ignored)"); + tlsAddError(" TLS group chains are different (range ignored)"); } } else if (tokens[0].equalsIgnoreCase("SELECTION")) { /* @@ -1444,7 +1444,8 @@ parseFloat(line.substring(n - 18, n - 9)), parseFloat(line .substring(n - 9, n))); } - if (Float.isNaN(origin.x) || Float.isNaN(origin.y) || Float.isNaN(origin.z)) { + if (Float.isNaN(origin.x) || Float.isNaN(origin.y) + || Float.isNaN(origin.z)) { origin.set(Float.NaN, Float.NaN, Float.NaN); tlsAddError("invalid origin: " + line); } @@ -1456,9 +1457,8 @@ * REMARK 3 T13: -0.0070 T23: 0.0011 */ char tensorType = tokens[0].charAt(0); - String s = (readLine().substring(10) - + readLine().substring(10) + readLine().substring(10)).replace( - tensorType, ' ').replace(':', ' '); + String s = (readLine().substring(10) + readLine().substring(10) + readLine() + .substring(10)).replace(tensorType, ' ').replace(':', ' '); //System.out.println("Tensor data = " + s); tokens = getTokens(s); float[][] tensor = new float[3][3]; @@ -1512,7 +1512,7 @@ * * @param iGroup * @param iModel - * @param symmetry + * @param symmetry */ @SuppressWarnings("unchecked") private void setTlsGroups(int iGroup, int iModel, SymmetryInterface symmetry) { @@ -1546,18 +1546,18 @@ } Logger.info("TLS ID=" + tlsGroupID + " model atom index range " + index1 + "-" + index2); - boolean isSameChain = (chain0 == chain1); // will be true + boolean isSameChain = (chain0 == chain1); // will be true // could demand a contiguous section here for each range. for (int iAtom = index0; iAtom < indexMax; iAtom++) { Atom atom = atoms[iAtom]; - if (isSameChain ? atom.sequenceNumber >= res0 && atom.sequenceNumber <= res1 - : atom.chainID > chain0 && atom.chainID < chain1 - || atom.chainID == chain0 && atom.sequenceNumber >= res0 - || atom.chainID == chain1 && atom.sequenceNumber <= res1 - ) { - data[iAtom - index0] = tlsGroupID; - setTlsEllipsoid(atom, group, symmetry); - } + if (isSameChain ? atom.sequenceNumber >= res0 + && atom.sequenceNumber <= res1 : atom.chainID > chain0 + && atom.chainID < chain1 || atom.chainID == chain0 + && atom.sequenceNumber >= res0 || atom.chainID == chain1 + && atom.sequenceNumber <= res1) { + data[iAtom - index0] = tlsGroupID; + setTlsEllipsoid(atom, group, symmetry); + } } } } @@ -1571,20 +1571,23 @@ } private int findAtomForRange(int atom1, int atom2, char chain, int resno, - boolean isLast) { + boolean isLast) { int iAtom = findAtom(atom1, atom2, chain, resno, true); - return (isLast && iAtom >= 0 ? findAtom(iAtom, atom2, chain, resno, false) : iAtom); + return (isLast && iAtom >= 0 ? findAtom(iAtom, atom2, chain, resno, false) + : iAtom); } - private int findAtom(int atom1, int atom2, char chain, int resno, boolean isTrue) { + private int findAtom(int atom1, int atom2, char chain, int resno, + boolean isTrue) { Atom[] atoms = atomSetCollection.getAtoms(); for (int i = atom1; i < atom2; i++) { - Atom atom = atoms[i]; - if ((atom.chainID == chain && atom.sequenceNumber == resno) == isTrue) - return i; + Atom atom = atoms[i]; + if ((atom.chainID == chain && atom.sequenceNumber == resno) == isTrue) + return i; } if (isTrue) { - Logger.warn("PdbReader findAtom chain=" + chain + " resno=" + resno + " not found"); + Logger.warn("PdbReader findAtom chain=" + chain + " resno=" + resno + + " not found"); tlsAddError("atom not found: chain=" + chain + " resno=" + resno); } return (isTrue ? -1 : atom2); @@ -1594,13 +1597,14 @@ private static final float RAD_PER_DEG = (float) (Math.PI / 180); private static final float _8PI2_ = (float) (8 * Math.PI * Math.PI); - private Map<Atom, float[]>tlsU; - - private void setTlsEllipsoid(Atom atom, Map<String, Object> group, SymmetryInterface symmetry) { + private Map<Atom, float[]> tlsU; + + private void setTlsEllipsoid(Atom atom, Map<String, Object> group, + SymmetryInterface symmetry) { Point3f origin = (Point3f) group.get("origin"); if (Float.isNaN(origin.x)) return; - + float[][] T = (float[][]) group.get("tT"); float[][] L = (float[][]) group.get("tL"); float[][] S = (float[][]) group.get("tS"); @@ -1656,16 +1660,16 @@ anisou[7] = bresidual; if (Float.isNaN(bresidual)) System.out.println("hmm"); - + if (tlsU == null) tlsU = new Hashtable<Atom, float[]>(); - tlsU.put(atom, anisou); + tlsU.put(atom, anisou); // symmetry is set to [1 1 1 90 90 90] -- Cartesians, not actual unit cell atom.ellipsoid = new Quadric[] { null, null, symmetry.getEllipsoid(dataT) }; //if (atom.atomIndex == 0) - //System.out.println("pdbreader ellip 0 = " + atom.ellipsoid[1]); + //System.out.println("pdbreader ellip 0 = " + atom.ellipsoid[1]); } private void tlsAddError(String error) { @@ -1675,7 +1679,7 @@ tlsGroupID).append('\t').append(error).append('\n'); } - protected static float fixRadius(float r) { + protected static float fixRadius(float r) { return (r < 0.9f ? 1 : r); // based on parameters in http://pdb2pqr.svn.sourceforge.net/viewvc/pdb2pqr/trunk/pdb2pqr/dat/ // AMBER forcefield, H atoms may be given 0 (on O) or 0.6 (on N) for radius @@ -1686,4 +1690,3 @@ } } - Modified: branches/v13_0/Jmol/src/org/jmol/shapebio/BioShapeRenderer.java =================================================================== --- branches/v13_0/Jmol/src/org/jmol/shapebio/BioShapeRenderer.java 2013-07-18 18:06:27 UTC (rev 18464) +++ branches/v13_0/Jmol/src/org/jmol/shapebio/BioShapeRenderer.java 2013-07-18 18:32:28 UTC (rev 18465) @@ -334,9 +334,12 @@ //e.printStackTrace(); } } - g3d.fillHermite(isNucleic ? 4 : 7, diameterBeg, diameterMid, - diameterEnd, controlPointScreens[iPrev], controlPointScreens[i], - controlPointScreens[iNext], controlPointScreens[iNext2]); + if (diameterBeg == 0 && diameterEnd == 0) + g3d.drawLine(controlPointScreens[i], controlPointScreens[iNext]); + else + g3d.fillHermite(isNucleic ? 4 : 7, diameterBeg, diameterMid, diameterEnd, + controlPointScreens[iPrev], controlPointScreens[i], + controlPointScreens[iNext], controlPointScreens[iNext2]); } //// cardinal hermite box or flat ribbon or twin strand (cartoons, meshRibbon, ribbon) Modified: branches/v13_0/Jmol/src/org/jmol/shapespecial/Ellipsoids.java =================================================================== --- branches/v13_0/Jmol/src/org/jmol/shapespecial/Ellipsoids.java 2013-07-18 18:06:27 UTC (rev 18464) +++ branches/v13_0/Jmol/src/org/jmol/shapespecial/Ellipsoids.java 2013-07-18 18:32:28 UTC (rev 18465) @@ -242,6 +242,7 @@ if (ellipsoid.axes == null || ellipsoid.lengths == null) return; Matrix3f mat = new Matrix3f(); + mat.setIdentity(); Matrix3f mTemp = new Matrix3f(); Vector3f v1 = new Vector3f(); ellipsoid.coef = new double[10]; Modified: branches/v13_0/Jmol/src/org/jmol/symmetry/SymmetryOperation.java =================================================================== --- branches/v13_0/Jmol/src/org/jmol/symmetry/SymmetryOperation.java 2013-07-18 18:06:27 UTC (rev 18464) +++ branches/v13_0/Jmol/src/org/jmol/symmetry/SymmetryOperation.java 2013-07-18 18:32:28 UTC (rev 18465) @@ -1040,7 +1040,7 @@ // returns triangles and lines for (int i = v.size(); --i >= 0;) { Point3f[] pts = (Point3f[]) v.get(i); - draw1.append(drawid).append("planep").append(i).append( + draw1.append(drawid).append("planep").append(i).append(" ").append( Escape.escape(pts[0])).append(Escape.escape(pts[1])); if (pts.length == 3) draw1.append(Escape.escape(pts[2])); Modified: branches/v13_0/Jmol/src/org/jmol/viewer/Jmol.properties =================================================================== --- branches/v13_0/Jmol/src/org/jmol/viewer/Jmol.properties 2013-07-18 18:06:27 UTC (rev 18464) +++ branches/v13_0/Jmol/src/org/jmol/viewer/Jmol.properties 2013-07-18 18:32:28 UTC (rev 18465) @@ -9,8 +9,12 @@ # Don't use ___ in your text, as that is the key for stripping out # the information saved in the JAR version of this file. -___version=13.0.18 +___version=13.0.19 +bug fix: cartoons with too small nonzero size will not render anything + +version=13.0.18 + bug fix NOT: pdbAddHydrogens may miss O3' or O5' H atoms at end of DNA strands bug fix NOT: pdbAddHydrogens may miss H in first group of a chain bug fix: "ligand" should include all _g=0 (nonPDB atoms) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ See everything from the browser to the database with AppDynamics Get end-to-end visibility with application monitoring from AppDynamics Isolate bottlenecks and diagnose root cause in seconds. Start your free trial of AppDynamics Pro today! http://pubads.g.doubleclick.net/gampad/clk?id=48808831&iu=/4140/ostg.clktrk _______________________________________________ Jmol-commits mailing list Jmol-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jmol-commits