/*
* This file is part of MoleculeViewer.
*
* MoleculeViewer 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 3 of the License, or
* (at your option) any later version.
*
* MoleculeViewer 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 MoleculeViewer. If not, see <http://www.gnu.org/licenses/>.
*/
package astex;
/* Copyright Astex Technology Ltd. 1999 */
/* Copyright David Hall, Boston University, 2011 */
/*
* 07-07-04 mjh
* add ability to handle CONECT record bonds
* don't connect atoms if they both had bonds
* specified by CONECT records (use Atom.ConectRecords);
* 21-08-02 mjh
* implement linked cell algorithm for connectivity
* determination. much faster especially for big
* structures.
* 08-08-02 mjh
* improve performance of loading big .mol2 files where
* there are explicit bonds. the caching was only working
* about half the time, new method addBondFromIds() will
* retrieve the atoms on the basis of id, but cache the
* first atom index as this gives good search performance.
* 19-06-00 mjh
* reintroduce generation of all ring impropers, due to
* concerns about how effectively the current scheme
* maintains planarity
* 17-02-00 mjh
* add method markRingBonds() that will mark ring bonds by
* constructing the minimum spanning tree.
* 15-12-99 mjh
* make 4 membered rings only generate one improper. saves us
* 3 impropers over the simple minded way. also only generate
* bondCount - 3 impropers for any ring. This enforces planarity
* 14-12-99 mjh
* add definitions of angles and impropers to molecule structure.
* simplifies generation of dictionaries and force field parameters.
* 14-11-99 mjh
* improve performance of connect() by caching bonding radii
* also use atoms array directly to avoid calls to getAtom()
* 07-11-99 mjh
* make changes to support pdb files. getRing() or getRingCount() will
* now call findRings if it hasn't already been called.
* findRings() will only check atoms that have more than 1 bond
* 05-11-99 mjh
* make addBond set the bond order to SingleBond and add method that
* allows specification of the bond order.
* 01-11-99 mjh
* extend functionality for addBond so that bonds are added to the
* atoms as they are created.
* 28-10-99 mjh
* created
*/
import astex.generic.*;
import java.util.*;
public class Molecule extends Generic implements Selectable {
/** Dynamic array of atoms. */
private List<Atom> atoms = new ArrayList<Atom>(20);
/** Dynamic array of bonds. */
private List<Bond> bonds = new ArrayList<Bond>(20);
/** Dynamic array of rings. */
private List<Ring> rings = new ArrayList<Ring>(1);
/** Dynamic array of chains. */
private List<Chain> chains = new ArrayList<Chain>(1);
/** The molecule name as a string. */
private String moleculeName = null;
/** The filename that the molecule came from. */
private String filename = null;
/** The type of molecule. */
private String type = null;
/** The center of the molecule. */
private Point3d center = null;
/** The radius of the molecule. */
private double radius = 0.0;
/** The symmetry object for this molecule. */
private Symmetry symmetry = null;
/** Flags for various properties. */
private int flags;
/* Bit masks for various properties. */
/** Have rings been assigned. */
private static final int RingsAssigned = 0x1;
/** Total number of residues. */
private static int residueCount = 0;
/* Constants for various overall display styles. */
/** Normal display style. */
public static final int Normal = 1;
/** Backbone trace. */
public static final int Trace = 2;
/** Backbone trace and atoms. */
public static final int TraceAlways = 3;
/** The display style for the molecule. */
private int displayStyle = Normal;
/** Is the molecule displayed or not. */
private boolean displayed = true;
/** What type of molecule is this. */
private int moleculeType = NormalMolecule;
/** Type for a normal molecule. */
public static final int NormalMolecule = 1;
public static final int FeatureMolecule = 2;
public static final int SkeletonMolecule = 3;
public static final int SymmetryMolecule = 4;
/** Number of central atoms for .istr files. */
private int centralAtomCount = 0;
/** Set the number of central atoms. */
public void setCentralAtomCount(int cac){
centralAtomCount = cac;
}
/** Get the number of central atoms. */
public int getCentralAtomCount(){
return centralAtomCount;
}
/** Return what type of molecule this is. */
public int getMoleculeType(){
return moleculeType;
}
/** Set what type of molecule this is. */
public void setMoleculeType(int t){
moleculeType = t;
}
/** Constructor which prepares a standard molecule. */
public Molecule(){
set(DisplayHydrogens, Boolean.TRUE);
set(DisplayBondDetails, Boolean.TRUE);
}
/** Generate a string representation. */
@Override
public String toString(){
StringBuilder ret = new StringBuilder(getName());
int atomCount = getAtomCount();
int bondCount = getBondCount();
int chainCount = getChainCount();
if(chainCount > 1){
ret.append(" ").append(chainCount).append(" chains");
}
ret.append(" ").append(atomCount).append(" atoms");
ret.append(" ").append(bondCount).append(" bonds");
return ret.toString();
}
/** Set Display style. */
public void setDisplayStyle(int style){
displayStyle = style;
}
/** Get the display style. */
public int getDisplayStyle(){
return displayStyle;
}
/** Get the display style. */
public boolean getDisplayStyle(int style){
return (displayStyle & style) > 0;
}
/** Set if the molecule is on or off. */
public void setDisplayed(int newState){
if(newState == 0){
displayed = false;
}else if(newState == 1){
displayed = true;
}else if(newState == 2){
displayed = !displayed;
}else{
System.out.println("setDisplayed: invalid state " + newState);
}
}
/** Is the molecule displayed? */
public boolean getDisplayed(){
return displayed;
}
/** Return the number of atoms in the molecule. */
public int getAtomCount(){
return atoms.size();
}
/** Return the specified atom. */
public Atom getAtom(int index){
return atoms.get(index);
}
/** The atom we will start searching at when looking for id's. */
private int startAtom = 0;
/** Return the atom with the specified id. */
//the idea behind the use of startAtom is that
//this function is often used to look up the two
//atoms in a bond, so... they are likely near each other
//hopefully the first leaves startAtom a couple before the second
public Atom getAtomWithId(int id){
for(ListIterator<Atom> it = atoms.listIterator(startAtom); it.hasNext();){
Atom atom = it.next();
if(atom.getId() == id){
startAtom =it.previousIndex(); //set to be the atom found
return atom;
}
}
// we didn't find it so search backwards from startAtom
for(ListIterator<Atom> it = atoms.listIterator(startAtom); it.hasNext();){
Atom atom = it.previous();
if(atom.getId() == id){
startAtom = it.nextIndex();
return atom;
}
}
// it really wasn't there...?
return null;
}
/** Assign atom numbers. */
public void assignAtomNumbers(){
int a = 0;
for(Atom atom: atoms){
atom.setId(a++);
}
}
/** Is this a symmetry molecule. */
public boolean isSymmetryMolecule(){
if(moleculeName != null &&
moleculeName.startsWith("Symmetry")){
return true;
}
return false;
}
/** Return the number of bonds in the molecule. */
public int getBondCount(){
return bonds.size();
}
/** Return the specified bond. */
public Bond getBond(int index){
return bonds.get(index);
}
/** Make sure that rings have been assigned for the molecule. */
private void ensureRingsAssigned(){
if((flags & RingsAssigned) == 0){
findRings();
flags |= RingsAssigned;
}
}
/** Return the number of rings. */
public int getRingCount(){
ensureRingsAssigned();
return rings.size();
}
/** Return the number of chains. */
public int getChainCount(){
return chains.size();
}
/** Return the specified chain. */
public Chain getChain(int index){
return chains.get(index);
}
/** The current chain to which atoms are added. */
private Chain currentChain;
/** Get the current chain. */
private Chain getCurrentChain(){
if(currentChain == null){
addChain();
}
return currentChain;
}
/** Get the total number of residues. */
public int getResidueCount(){
int totalResidues = 0;
for(Chain chain : chains){
totalResidues += chain.getResidueCount();
}
return totalResidues;
}
/** Add a chain to the molecule. */
public Chain addChain(){
currentChain = new Chain();
currentChain.setParent(this);
chains.add(currentChain);
return currentChain;
}
/** Add a residue to the current chain. */
public Residue addResidue(){
Chain chain = getCurrentChain();
Residue residue = chain.addResidue();
residue.setSequentialNumber(residueCount++);
return residue;
}
/** Add an atom to the molecule and return the reference to it. */
public Atom addAtom(){
Atom newAtom = Atom.create();
atoms.add(newAtom);
Residue currentResidue = currentChain.getCurrentResidue();
currentResidue.addAtom(newAtom);
newAtom.setParent(currentResidue);
return newAtom;
}
/** Add a new ring to the molecule. */
private Ring addRing(){
Ring newRing = new Ring();
rings.add(newRing);
return newRing;
}
/** Add a bond to the molecule and return a reference to it. */
public Bond addBond(Atom firstAtom, Atom secondAtom, Bond.BondOrder bondOrder){
return addBond(firstAtom, secondAtom, bondOrder, true);
}
/** Add a bond to the molecule and return a reference to it. */
public Bond addBond(Atom firstAtom, Atom secondAtom, Bond.BondOrder bondOrder, boolean explicit){
// handle insertion codes for multiple positions of
// atoms in pdb structures.
int firstInsertion = firstAtom.getInsertionCode();
int secondInsertion = secondAtom.getInsertionCode();
Bond newBond = null;
if((!explicit) && (firstAtom.hasExplicitBond() && secondAtom.hasExplicitBond())){
return null;
}
if(((firstInsertion == ' ' || secondInsertion == ' ') ||
firstInsertion == secondInsertion)&& !(firstAtom.isSolvent() || secondAtom.isSolvent())){
newBond = new Bond(firstAtom, secondAtom);
newBond.setBondOrder(bondOrder);
if(explicit){
newBond.setExplicitBond(true);
}
bonds.add(newBond);
// and add the bond to the atoms
firstAtom.addBond(newBond);
secondAtom.addBond(newBond);
}
return newBond;
}
/** Add a bond to the molecule and return a reference to it. */
public Bond addBond(int firstAtomIndex, int secondAtomIndex,
Bond.BondOrder bondOrder){
Atom firstAtom = getAtom(firstAtomIndex);
Atom secondAtom = getAtom(secondAtomIndex);
return addBond(firstAtom, secondAtom, bondOrder, true);
}
/** Add a bond to the molecule and return a reference to it. */
public Bond addBondFromIds(int firstAtomIndex, int secondAtomIndex,
Bond.BondOrder bondOrder){
// attempt to improve caching
Atom a1 = getAtomWithId(firstAtomIndex);
int searchAtom = startAtom;
Atom a2 = getAtomWithId(secondAtomIndex);
startAtom = searchAtom;
Bond newBond = addBond(a1, a2, bondOrder, false);
return newBond;
}
/** Remove an atom from the molecule. */
public void removeAtom(Atom a){
Residue residue = a.getResidue();
residue.removeAtom(a);
atoms.remove(a);
}
/** Remove a bond from the molecule. */
public void removeBond(Bond b){
bonds.remove(b);
}
/** Does this atom need treating specially for bonding. */
private boolean isSpecialAtom(Atom atom){
int element = atom.getElement();
return (element == PeriodicTable.SULPHUR ||
element == PeriodicTable.PHOSPHORUS);
}
/* Linked cell connectivity calculation. */
/** Is debugging on. */
public boolean debug = false;
/** The atom ids in a cell. */
private int cell1[] = null;
/** The atom ids in a cell. */
private int cell2[] = null;
/** The number of atoms in cell1. */
private int nc1 = 0;
/** The number of atoms in cell2. */
private int nc2 = 0;
/** Cache of the bonding radii used for a molecule. */
private double bondingRadii[] = null;
/** The list of cell positions for atoms. */
private int list[] = null;
/** The head pointers for each cell. */
private int head[] = null;
/** Generate a cell index for the given grid. */
private int cellIndex(int ix, int iy, int iz,
int nx, int ny){
return ix + iy * nx + iz * nx * ny;
}
/**
* The cell offsets for the half space that generates
* icell2 > icell1.
*/
private int offsets[][] = {
{-1,-1,-1},
{ 0,-1,-1},
{ 1,-1,-1},
{-1, 0,-1},
{ 0, 0,-1},
{ 1, 0,-1},
{-1, 1,-1},
{ 0, 1,-1},
{ 1, 1,-1},
{-1,-1, 0},
{ 0,-1, 0},
{ 1,-1, 0},
{-1, 0, 0},
};
/** Connect the atoms using a neighbour grid. */
public void connect2(){
double xmin = 1.e10, xmax = -1.e10;
double ymin = 1.e10, ymax = -1.e10;
double zmin = 1.e10, zmax = -1.e10;
int atomCount = getAtomCount();
bondingRadii = new double[atomCount];
for(int a1 = 0; a1 < atomCount; a1++){
Atom atom = getAtom(a1);
bondingRadii[a1] = atom.getBondingRadius();
}
// Find the bounding box of the molecule
for(Atom atom: atoms){
if(atom.x < xmin) xmin = atom.x;
if(atom.y < ymin) ymin = atom.y;
if(atom.z < zmin) zmin = atom.z;
if(atom.x > xmax) xmax = atom.x;
if(atom.y > ymax) ymax = atom.y;
if(atom.z > zmax) zmax = atom.z;
}
// Increase bounding box for comfort
xmin -= 0.1; ymin -= 0.1; zmin -= 0.1;
xmax += 0.1; ymax += 0.1; zmax += 0.1;
if(debug){
System.out.println("xmin " + xmin);
System.out.println("ymin " + ymin);
System.out.println("zmin " + zmin);
System.out.println("xmax " + xmax);
System.out.println("ymax " + ymax);
System.out.println("zmax " + zmax);
}
// use fixed spacing and calculate the grid dimensions
// alogorithm is reasonably invariant to grid size
// even for really big structures.
double spacing = 5.0;
int nx = 1 + (int)((xmax - xmin) / spacing);
int ny = 1 + (int)((ymax - ymin) / spacing);
int nz = 1 + (int)((zmax - zmin) / spacing);
int ncell = nx * ny * nz;
// allocate space for working...
// do it here so that mol files don't
// allocate this memory (as they don't need it)
cell1 = new int[1024];
cell2 = new int[1024];
if(debug){
System.out.println("nx " + nx + " ny " + ny + " nz " + nz);
}
head = new int[ncell];
list = new int[atomCount];
// initialise cell head pointers
// -1 shows that the cell is empty
for(int i = 0; i < ncell; i++){
head[i] = -1;
}
// put the atoms into the linked cell structure
for(int a = 0; a < atomCount; a++){
Atom atom = getAtom(a);
int ix = (int)((atom.x - xmin)/spacing);
int iy = (int)((atom.y - ymin)/spacing);
int iz = (int)((atom.z - zmin)/spacing);
int icell = cellIndex(ix, iy, iz, nx, ny);
if(icell < 0 || icell >= ncell){
System.out.println("invalid cell " + icell);
}
list[a] = head[icell];
head[icell] = a;
}
int icell = 0;
int offsetlen = offsets.length;
int occupiedCells = 0;
for(int iz = 0; iz < nz; iz++){
for(int iy = 0; iy < ny; iy++){
for(int ix = 0; ix < nx; ix++){
if(head[icell] != -1){
occupiedCells++;
connectCell(icell);
for(int ioff = 0; ioff < offsetlen; ioff++){
int ix2 = ix - offsets[ioff][0];
int iy2 = iy - offsets[ioff][1];
int iz2 = iz - offsets[ioff][2];
if(ix2 >= 0 && ix2 < nx &&
iy2 >= 0 && iy2 < ny &&
iz2 >= 0 && iz2 < nz){
int icell2 = cellIndex(ix2, iy2, iz2,
nx, ny);
connectTwoCells(icell2);
}
}
}
icell++;
}
}
}
if(debug){
double percentOccupied =
100.0 * (double)(occupiedCells)/(double)(ncell);
double atomsPerCell = (double)(atomCount)/(double)(occupiedCells);
System.out.println("total cells in grid " + ncell);
System.out.println("occupied cells in grid " + occupiedCells);
System.out.println("% cells occupied " + percentOccupied);
System.out.println("average atoms per cell " + atomsPerCell);
}
// try and release the memory we allocated
// no guarantee that this will garbage collect it
// but at least it will be available when gc() runs
bondingRadii = null;
list = null;
head = null;
cell1 = null;
cell2 = null;
System.gc();
}
/** Get the contents of the cell. */
private int getCellContents(int icell, int c[]){
int nc = 0;
int j = head[icell];
if(j == -1){
return 0;
}
while(j >= 0){
c[nc++] = j;
j = list[j];
}
return nc;
}
/**
* Connect the contents of two cells.
* Assumes that the target cell has been correctly
* connected and the cell contents are in cell1
*/
private void connectTwoCells(int icell2){
// only need to get the contents of cell2
nc2 = getCellContents(icell2, cell2);
// don't forget to look up the atom ids
// in the cell array.
for(int i = 0; i < nc1; i++){
int i1 = cell1[i];
Atom a1 = atoms.get(i1);
double r1 = bondingRadii[i1];
for(int j = 0; j < nc2; j++){
int i2 = cell2[j];
Atom a2 = atoms.get(i2);
double r2 = bondingRadii[i2];
double d2 = r1 + r2;
d2 *= d2;
if(a1.distanceSquared(a2) < d2){
addBond(a1, a2, Bond.BondOrder.SingleBond, false);
}
}
}
}
/** Connect the contents of one cell up. */
private void connectCell(int icell){
nc1 = getCellContents(icell, cell1);
// don't forget to look up the atom ids
// in the cell array.
for(int i = 0; i < nc1; i++){
int i1 = cell1[i];
Atom a1 = atoms.get(i1);
double r1 = bondingRadii[i1];
for(int j = i + 1; j < nc1; j++){
int i2 = cell1[j];
Atom a2 = atoms.get(i2);
double r2 = bondingRadii[i2];
double d2 = r1 + r2;
d2 *= d2;
if(a1.distanceSquared(a2) < d2){
addBond(a1, a2, Bond.BondOrder.SingleBond, false);
}
}
}
}
/** Connect the atoms in a molecule using standard bonding radii. */
public void connect(){
int atomCount = getAtomCount();
// store all of the bonding radii so we don't have to
// keep looking them up.
double bondingRadii[] = new double[atomCount];
for(int a1 = 0; a1 < atomCount; a1++){
Atom atom = getAtom(a1);
bondingRadii[a1] = atom.getBondingRadius();
}
// now check each atom.
for(int a1 = 0; a1 < atomCount; a1++){
Atom firstAtom = atoms.get(a1);
double firstRadius = bondingRadii[a1];
int startAtom = 0;
int endAtom = atomCount;
boolean specialAtom = isSpecialAtom(firstAtom);
if(specialAtom){
// its a special atom so we need to check all others
startAtom = 0;
endAtom = atomCount;
}else{
// its a regular atom so we only need to check in the next 50
startAtom = a1 + 1;
endAtom = a1 + 50;
if(endAtom > atomCount){
endAtom = atomCount;
}
}
for(int a2 = startAtom; a2 < endAtom; a2++){
if(a2 != a1){
Atom secondAtom = atoms.get(a2);
double secondRadius = bondingRadii[a2];
double dSquare = firstRadius + secondRadius;
dSquare *= dSquare;
if(firstAtom.distanceSquared(secondAtom) < dSquare){
if(specialAtom){
Bond bond = firstAtom.getBond(secondAtom);
if(bond == null){
addBond(firstAtom, secondAtom, Bond.BondOrder.SingleBond, false);
}
}else{
addBond(firstAtom, secondAtom, Bond.BondOrder.SingleBond, false);
}
}
}
}
}
}
/** Find rings in the structure. */
private void findRings(){
// assign ids to the atoms so that we can compare
// ordering for creating rings.
int atomCount = getAtomCount();
int oldIds[] = new int[atomCount];
for(int i = 0; i < atomCount; i++){
Atom atom = getAtom(i);
oldIds[i] = atom.getId();
atom.setId(i + 1);
}
try {
for(int ringSize = 3; ringSize <= 6; ringSize++){
findRings(ringSize);
}
}catch(Exception e){
e.printStackTrace();
}finally{
for(int i = 0; i < atomCount; i++){
Atom atom = getAtom(i);
atom.setId(oldIds[i]);
}
oldIds = null;
}
}
/** Find rings of a particular size. */
private void findRings(int ringSize){
if(ringSize == 3){
find3Rings();
}else{
// allocate space for keeping track of the
// path we take through the molecule.
Atom atomPath[] = new Atom[ringSize];
Bond bondPath[] = new Bond[ringSize];
for(int a = 0; a < getAtomCount(); a++){
Atom atom = getAtom(a);
// no point looking if we only have one bond
if(atom.getBondCount() > 1){
propagateRingSearch(0, ringSize,
atom, atomPath, bondPath);
}
}
}
}
/** Find 3 membered rings by direct search. */
private void find3Rings(){
for(int a = 0; a < getAtomCount(); a++){
Atom atom = getAtom(a);
int bondCount = atom.getBondCount();
for(int i = 0; i < bondCount; i++){
Bond bondi = atom.getBond(i);
Atom atomi = bondi.getOtherAtom(atom);
for(int j = 0; j < bondCount; j++){
if( i == j) continue;
Bond bondj = atom.getBond(j);
Atom atomj = bondj.getOtherAtom(atom);
Bond bondij = atomi.getBond(atomj);
if((bondij != null)&& (atom.getId() < atomi.getId() &&
atomi.getId() < atomj.getId())){
// its a three membered ring.
Ring newRing = addRing();
newRing.addAtom(atom);
newRing.addAtom(atomi);
newRing.addAtom(atomj);
newRing.addBond(bondi);
newRing.addBond(bondij);
newRing.addBond(bondj);
}
}
}
}
}
/** Recursive method for propagating the search for rings. */
private void propagateRingSearch(int currentDepth, int maximumDepth,
Atom currentAtom,
Atom atomPath[], Bond bondPath[]){
if(currentDepth == maximumDepth && currentAtom == atomPath[0]){
possiblyCreateRing(atomPath, bondPath, maximumDepth);
}else if(currentDepth < maximumDepth){
atomPath[currentDepth] = currentAtom;
for(int b = 0; b < currentAtom.getBondCount(); b++){
Bond bond = currentAtom.getBond(b);
Atom otherAtom = bond.getOtherAtom(currentAtom);
if(currentDepth == 0 ||
otherAtom != atomPath[currentDepth - 1]){
bondPath[currentDepth] = bond;
propagateRingSearch(currentDepth + 1, maximumDepth,
otherAtom, atomPath, bondPath);
}
}
}
}
/**
* Create a ring if the atoms are in the right order.
*
* The ring is only created if
* 1. the first atom has the lowest index.
* 2. the second atom has a lower index than the last atom.
*/
private void possiblyCreateRing(Atom atomPath[], Bond bondPath[],
int ringSize){
int firstAtomId = atomPath[0].getId();
for(int i = 1; i < ringSize; i++){
Atom atom = atomPath[i];
if(firstAtomId >= atom.getId()){
return;
}
}
if(atomPath[1].getId() >= atomPath[ringSize - 1].getId()){
return;
}
// ok, we have passed all the test add the ring
Ring newRing = addRing();
for(int i = 0; i < ringSize; i++){
newRing.addAtom(atomPath[i]);
newRing.addBond(bondPath[i]);
}
}
/** Return the best ring containing this bond. */
Ring getBestRingContainingBond(Bond bond){
ensureRingsAssigned();
for(int i = 1; i >= 0; i--){
for(Ring ring: rings){
if(ring.contains(bond) && ring.getAtomCount() == (6 - i) &&
ring.isAromatic()){
return ring;
}
}
}
for(Ring ring: rings){
if(ring.contains(bond)){
return ring;
}
}
return null;
}
/** Set the molecule name */
public void setName(String name){
moleculeName = name;
}
/** Get the molecule name. */
public String getName(){
if(moleculeName == null)
return "Unnamed molecule";
return moleculeName;
}
/** Set the filename. */
public void setFilename(String s){
filename = s;
}
/** Get the filename. */
public String getFilename(){
return filename;
}
/** Set the type. */
public void setType(String s){
type = s;
}
/** Get the type. */
public String getType(){
return type;
}
/** Find the center of the molecule. */
public Point3d getCenter(){
if(center == null){
center = new Point3d();
int atomCount = getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = getAtom(a);
center.add(atom);
}
if(atomCount > 0){
center.divide(atomCount);
}
}
return center.clone();
}
/** Get the radius of the molecule. */
public double getRadius(){
Point3d moleculeCenter = getCenter();
radius = 0.0;
int atomCount = getAtomCount();
for(int a = 0; a < atomCount; a++){
Atom atom = getAtom(a);
double dSq = moleculeCenter.distanceSquared(atom);
if(dSq > radius){
radius = dSq;
}
}
radius = Math.sqrt(radius);
return radius;
}
/** Make sure that the symmetry object is allocated. */
private void ensureSymmetryAllocated(){
if(symmetry == null){
symmetry = new Symmetry();
}
}
/** Set the unit cell. */
public void setUnitCell(double newCell[]){
ensureSymmetryAllocated();
symmetry.setUnitCell(newCell);
}
/** Get the symmetry object. */
public Symmetry getSymmetry(){
return symmetry;
}
/** Set the symmetry entry for this molecule. */
public void setSymmetry(Symmetry s){
symmetry = s;
}
/** Set the space group name. */
public void setSpaceGroupName(String name){
if(name == null){
symmetry = null;
}else{
ensureSymmetryAllocated();
symmetry.setSpaceGroupName(name);
}
}
/* Implementation of Selectable. */
public String selectStatement(){
return "molexact '" + getName() + "'";
}
/** Apply a selection recursively. */
public int select(int state){
int selectCount = 0;
for(Chain chain : chains){
selectCount += chain.select(state);
}
return selectCount;
}
public static final String Displayed = "displayed";
public static final String DisplayHydrogens = "hydrogens";
public static final String DisplayBondDetails = "bondDetails";
@Override
public Object set(Object key, Object property){
String name = (String)key;
if(name.equals(Displayed)){
setDisplayed(((Boolean)property).booleanValue() ? 1 : 0);
}
super.set(key, property);
// should return old value
return null;
}
@Override
public Object get(Object key, Object def){
if(key.equals(Displayed)){
return Boolean.valueOf(getDisplayed());
}
return super.get(key, def);
}
}