/* $RCSfile$
* $Author$
* $Date$
* $Revision$
*
* Copyright (C) 2003-2007 The Chemistry Development Kit (CDK) project
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program 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.
* All we ask is that proper credit is given for our work, which includes
* - but is not limited to - adding the above copyright notice to the beginning
* of your source code files, and to any copyright notice that you may distribute
* with programs based on this work.
*
* This program 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 program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
package org.openscience.cdk.layout;
import java.util.Comparator;
import java.util.List;
import java.util.Vector;
import javax.vecmath.Point2d;
import javax.vecmath.Vector2d;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.geometry.BondTools;
import org.openscience.cdk.geometry.GeometryTools;
import org.openscience.cdk.graph.PathTools;
import org.openscience.cdk.graph.matrix.ConnectionMatrix;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IMolecule;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;
/**
* Methods for generating coordinates for atoms in various situations. They can
* be used for Automated Structure Diagram Generation or in the interactive
* buildup of molecules by the user.
*
*@author steinbeck
*@cdk.created 2003-08-29
*@cdk.module sdg
* @cdk.githash
*/
public class AtomPlacer
{
public final static boolean debug = true;
private static ILoggingTool logger =
LoggingToolFactory.createLoggingTool(AtomPlacer.class);
/**
* The molecule to be laid out. To be assigned from outside
*/
IAtomContainer molecule = null;
final Comparator ATOM_ORDER =
new Comparator()
{
public int compare(Object o1, Object o2)
{
IAtom a1 = (IAtom) o1;
IAtom a2 = (IAtom) o2;
int i1 = ((Integer) a1.getProperty("Weight")).intValue();
int i2 = ((Integer) a2.getProperty("Weight")).intValue();
if (i1 < i2)
{
return -1;
}
if (i1 == i2)
{
return 0;
}
return 1;
}
};
/**
* Constructor for the AtomPlacer object
*/
public AtomPlacer()
{
}
/**
* Return the molecule the AtomPlacer currently works with
*
*@return the molecule the AtomPlacer currently works with
*/
public IAtomContainer getMolecule()
{
return this.molecule;
}
/**
* Sets the molecule the AtomPlacer currently works with
*
*@param molecule the molecule the AtomPlacer currently works with
*/
public void setMolecule(IAtomContainer molecule)
{
this.molecule = molecule;
}
/**
* Distribute the bonded atoms (neighbours) of an atom such that they fill the
* remaining space around an atom in a geometrically nice way.
* IMPORTANT: This method is not supposed to handle the
* case of one or no place neighbor. In the case of
* one placed neigbor, the chain placement methods
* should be used.
*
*@param atom The atom whose partners are to be placed
*@param placedNeighbours The atoms which are already placed
*@param unplacedNeighbours The partners to be placed
*@param bondLength The standared bond length for the newly placed
* Atoms
*@param sharedAtomsCenter The 2D centre of the placed Atoms
*/
public void distributePartners(IAtom atom, IAtomContainer placedNeighbours, Point2d sharedAtomsCenter,
IAtomContainer unplacedNeighbours, double bondLength)
{
double occupiedAngle = 0;
//double smallestDistance = Double.MAX_VALUE;
//IAtom[] nearestAtoms = new IAtom[2];
IAtom[] sortedAtoms = null;
double startAngle = 0.0;
double addAngle = 0.0;
double radius = 0.0;
double remainingAngle = 0.0;
/*
* calculate the direction away from the already placed partners of atom
*/
//Point2d sharedAtomsCenter = sharedAtoms.get2DCenter();
Vector2d sharedAtomsCenterVector = new Vector2d(sharedAtomsCenter);
Vector2d newDirection = new Vector2d(atom.getPoint2d());
Vector2d occupiedDirection = new Vector2d(sharedAtomsCenter);
occupiedDirection.sub(newDirection);
logger.debug("distributePartners->occupiedDirection.lenght(): " + occupiedDirection.length());
Vector atomsToDraw = new Vector();
logger.debug("Number of shared atoms: ", placedNeighbours.getAtomCount());
/*
* IMPORTANT: This method is not supposed to handle the
* case of one or no place neighbor. In the case of
* one placed neigbor, the chain placement methods
* should be used.
*/
if (placedNeighbours.getAtomCount() == 1)
{
logger.debug("Only one neighbour...");
for (int f = 0; f < unplacedNeighbours.getAtomCount(); f++)
{
atomsToDraw.addElement(unplacedNeighbours.getAtom(f));
}
addAngle = Math.PI * 2 / (unplacedNeighbours.getAtomCount() + placedNeighbours.getAtomCount());
/*
* IMPORTANT: At this point we need a calculation of the
* start angle.
* Not done yet.
*/
IAtom placedAtom = placedNeighbours.getAtom(0);
// double xDiff = atom.getX2d() - placedAtom.getX2d();
// double yDiff = atom.getY2d() - placedAtom.getY2d();
double xDiff = placedAtom.getPoint2d().x - atom.getPoint2d().x;
double yDiff = placedAtom.getPoint2d().y - atom.getPoint2d().y;
logger.debug("distributePartners->xdiff: " + Math.toDegrees(xDiff));
logger.debug("distributePartners->ydiff: " + Math.toDegrees(yDiff));
startAngle = GeometryTools.getAngle(xDiff, yDiff);
//- (Math.PI / 2.0);
logger.debug("distributePartners->angle: " + Math.toDegrees(startAngle));
populatePolygonCorners(atomsToDraw, new Point2d(atom.getPoint2d()), startAngle, addAngle, bondLength);
return;
} else if (placedNeighbours.getAtomCount() == 0)
{
logger.debug("First atom...");
for (int f = 0; f < unplacedNeighbours.getAtomCount(); f++)
{
atomsToDraw.addElement(unplacedNeighbours.getAtom(f));
}
addAngle = Math.PI * 2.0 / unplacedNeighbours.getAtomCount();
/*
* IMPORTANT: At this point we need a calculation of the
* start angle. Not done yet.
*/
startAngle = 0.0;
populatePolygonCorners(atomsToDraw, new Point2d(atom.getPoint2d()), startAngle, addAngle, bondLength);
return;
}
/*
* if the least hindered side of the atom is clearly defined (bondLength / 10 is an arbitrary value that seemed reasonable)
*/
//newDirection.sub(sharedAtomsCenterVector);
sharedAtomsCenterVector.sub(newDirection);
newDirection = sharedAtomsCenterVector;
newDirection.normalize();
newDirection.scale(bondLength);
newDirection.negate();
logger.debug("distributePartners->newDirection.lenght(): " + newDirection.length());
Point2d distanceMeasure = new Point2d(atom.getPoint2d());
distanceMeasure.add(newDirection);
/*
* get the two sharedAtom partners with the smallest distance to the new center
*/
sortedAtoms = AtomContainerManipulator.getAtomArray(placedNeighbours);
GeometryTools.sortBy2DDistance(sortedAtoms, distanceMeasure);
Vector2d closestPoint1 = new Vector2d(sortedAtoms[0].getPoint2d());
Vector2d closestPoint2 = new Vector2d(sortedAtoms[1].getPoint2d());
closestPoint1.sub(new Vector2d(atom.getPoint2d()));
closestPoint2.sub(new Vector2d(atom.getPoint2d()));
occupiedAngle = closestPoint1.angle(occupiedDirection);
occupiedAngle += closestPoint2.angle(occupiedDirection);
double angle1 = GeometryTools.getAngle(sortedAtoms[0].getPoint2d().x - atom.getPoint2d().x, sortedAtoms[0].getPoint2d().y - atom.getPoint2d().y);
double angle2 = GeometryTools.getAngle(sortedAtoms[1].getPoint2d().x - atom.getPoint2d().x, sortedAtoms[1].getPoint2d().y - atom.getPoint2d().y);
double angle3 = GeometryTools.getAngle(distanceMeasure.x - atom.getPoint2d().x, distanceMeasure.y - atom.getPoint2d().y);
if (debug)
{
try
{
logger.debug("distributePartners->sortedAtoms[0]: ", (molecule.getAtomNumber(sortedAtoms[0]) + 1));
logger.debug("distributePartners->sortedAtoms[1]: ", (molecule.getAtomNumber(sortedAtoms[1]) + 1));
logger.debug("distributePartners->angle1: ", Math.toDegrees(angle1));
logger.debug("distributePartners->angle2: ", Math.toDegrees(angle2));
} catch (Exception exc) {
logger.debug(exc);
}
}
IAtom startAtom = null;
if (angle1 > angle3)
{
if (angle1 - angle3 < Math.PI)
{
startAtom = sortedAtoms[1];
} else
{
// 12 o'clock is between the two vectors
startAtom = sortedAtoms[0];
}
} else
{
if (angle3 - angle1 < Math.PI)
{
startAtom = sortedAtoms[0];
} else
{
// 12 o'clock is between the two vectors
startAtom = sortedAtoms[1];
}
}
remainingAngle = (2 * Math.PI) - occupiedAngle;
addAngle = remainingAngle / (unplacedNeighbours.getAtomCount() + 1);
if (debug)
{
try
{
logger.debug("distributePartners->startAtom: " + (molecule.getAtomNumber(startAtom) + 1));
logger.debug("distributePartners->remainingAngle: " + Math.toDegrees(remainingAngle));
logger.debug("distributePartners->addAngle: " + Math.toDegrees(addAngle));
logger.debug("distributePartners-> partners.getAtomCount(): " + unplacedNeighbours.getAtomCount());
} catch (Exception exc)
{
logger.debug(exc);
}
}
for (int f = 0; f < unplacedNeighbours.getAtomCount(); f++)
{
atomsToDraw.addElement(unplacedNeighbours.getAtom(f));
}
radius = bondLength;
startAngle = GeometryTools.getAngle(startAtom.getPoint2d().x - atom.getPoint2d().x, startAtom.getPoint2d().y - atom.getPoint2d().y);
logger.debug("Before check: distributePartners->startAngle: " + startAngle);
// if (startAngle < (Math.PI + 0.001) && startAngle > (Math.PI
// -0.001))
// {
// startAngle = Math.PI/placedNeighbours.getAtomCount();
// }
logger.debug("After check: distributePartners->startAngle: " + startAngle);
populatePolygonCorners(atomsToDraw, new Point2d(atom.getPoint2d()), startAngle, addAngle, radius);
}
/**
* Places the atoms in a linear chain.
*
* <p>Expects the first atom to be placed and
* places the next atom according to initialBondVector. The rest of the chain
* is placed such that it is as linear as possible (in the overall result, the
* angles in the chain are set to 120 Deg.)
*
* @param atomContainer The IAtomContainer containing the chain atom to be placed
* @param initialBondVector The Vector indicating the direction of the first bond
* @param bondLength The factor used to scale the initialBondVector
*/
public void placeLinearChain(IAtomContainer atomContainer, Vector2d initialBondVector, double bondLength)
{
IMolecule withh = atomContainer.getBuilder().newMolecule(atomContainer);
// BUGFIX - withh does not have cloned cloned atoms, so changes are
// reflected in our atom container. If we're using implicit hydrogens
// the correct counts need saving and restoring
int[] numh = new int[atomContainer.getAtomCount()];
for (int i = 0, n = atomContainer.getAtomCount(); i < n; i ++) {
Integer tmp = atomContainer.getAtom(i).getHydrogenCount();
if (tmp == CDKConstants.UNSET) numh[i]= 0;
else numh[i] = tmp;
}
// SDG should lay out what it gets and not fiddle with molecules
// during layout so this was
// removed during debugging. Before you put this in again, contact
// er@doktor-steinbeck.de
// if(GeometryTools.has2DCoordinatesNew(atomContainer)==2){
// try{
// new HydrogenAdder().addExplicitHydrogensToSatisfyValency(withh);
// }catch(Exception ex){
// logger.warn("Exception in hydrogen adding. This could mean that cleanup does not respect E/Z: ", ex.getMessage());
// logger.debug(ex);
// }
// new HydrogenPlacer().placeHydrogens2D(withh, bondLength);
// }
logger.debug("Placing linear chain of length " + atomContainer.getAtomCount());
Vector2d bondVector = initialBondVector;
IAtom atom = null;
Point2d atomPoint = null;
IAtom nextAtom = null;
for (int f = 0; f < atomContainer.getAtomCount() - 1; f++)
{
atom = atomContainer.getAtom(f);
nextAtom = atomContainer.getAtom(f + 1);
atomPoint = new Point2d(atom.getPoint2d());
bondVector.normalize();
bondVector.scale(bondLength);
atomPoint.add(bondVector);
nextAtom.setPoint2d(atomPoint);
nextAtom.setFlag(CDKConstants.ISPLACED, true);
boolean trans=false;
if(GeometryTools.has2DCoordinatesNew(atomContainer)==2){
try{
if(f>2 && BondTools.isValidDoubleBondConfiguration(withh,withh.getBond(withh.getAtom(f-2),withh.getAtom(f-1)))){
trans=BondTools.isCisTrans(withh.getAtom(f-3),withh.getAtom(f-2),withh.getAtom(f-1),withh.getAtom(f-0),withh);
}
}catch(Exception ex){
logger.debug("Excpetion in detecting E/Z. This could mean that cleanup does not respect E/Z");
}
bondVector = getNextBondVector(nextAtom, atom, GeometryTools.get2DCenter(molecule),trans);
}else{
bondVector = getNextBondVector(nextAtom, atom, GeometryTools.get2DCenter(molecule),true);
}
}
// BUGFIX part 2 - restore hydrogen counts
for (int i = 0, n = atomContainer.getAtomCount(); i < n; i ++) {
atomContainer.getAtom(i).setHydrogenCount(numh[i]);
}
}
/**
* Returns the next bond vector needed for drawing an extended linear chain of
* atoms. It assumes an angle of 120 deg for a nice chain layout and
* calculates the two possible placments for the next atom. It returns the
* vector pointing farmost away from a given start atom.
*
*@param atom An atom for which the vector to the next atom to
* draw is calculated
*@param previousAtom The preceding atom for angle calculation
*@param distanceMeasure A point from which the next atom is to be farmost
* away
*@param trans if true E (trans) configurations are built, false makes Z (cis) configurations
*@return A vector pointing to the location of the next atom
* to draw
*/
public Vector2d getNextBondVector(IAtom atom, IAtom previousAtom, Point2d distanceMeasure, boolean trans)
{
if (logger.isDebugEnabled())
{
logger.debug("Entering AtomPlacer.getNextBondVector()");
logger.debug("Arguments are atom: " + atom + ", previousAtom: " + previousAtom + ", distanceMeasure: " + distanceMeasure);
}
double angle = GeometryTools.getAngle(previousAtom.getPoint2d().x - atom.getPoint2d().x, previousAtom.getPoint2d().y - atom.getPoint2d().y);
double addAngle = Math.toRadians(120);
if(!trans)
addAngle=Math.toRadians(60);
if (shouldBeLinear(atom, molecule)) addAngle = Math.toRadians(180);
angle += addAngle;
Vector2d vec1 = new Vector2d(Math.cos(angle), Math.sin(angle));
Point2d point1 = new Point2d(atom.getPoint2d());
point1.add(vec1);
double distance1 = point1.distance(distanceMeasure);
angle += addAngle;
Vector2d vec2 = new Vector2d(Math.cos(angle), Math.sin(angle));
Point2d point2 = new Point2d(atom.getPoint2d());
point2.add(vec2);
double distance2 = point2.distance(distanceMeasure);
if (distance2 > distance1)
{
logger.debug("Exiting AtomPlacer.getNextBondVector()");
return vec2;
}
logger.debug("Exiting AtomPlacer.getNextBondVector()");
return vec1;
}
/**
* Populates the corners of a polygon with atoms. Used to place atoms in a
* geometrically regular way around a ring center or another atom. If this is
* used to place the bonding partner of an atom (and not to draw a ring) we
* want to place the atoms such that those with highest "weight" are placed
* farmost away from the rest of the molecules. The "weight" mentioned here is
* calculated by a modified morgan number algorithm.
*
*@param atomsToDraw All the atoms to draw
*@param startAngle A start angle, giving the angle of the most clockwise
* atom which has already been placed
*@param addAngle An angle to be added to startAngle for each atom from
* atomsToDraw
*@param rotationCenter The center of a ring, or an atom for which the
* partners are to be placed
*@param radius The radius of the polygon to be populated: bond
* length or ring radius
*/
public void populatePolygonCorners(List<IAtom> atomsToDraw, Point2d rotationCenter, double startAngle, double addAngle, double radius)
{
IAtom connectAtom = null;
double angle = startAngle;
double newX;
double newY;
double x;
double y;
logger.debug("populatePolygonCorners->startAngle: ", Math.toDegrees(angle));
Vector points = new Vector();
//IAtom atom = null;
logger.debug(" centerX:", rotationCenter.x);
logger.debug(" centerY:", rotationCenter.y);
logger.debug(" radius :", radius);
for (int i = 0; i < atomsToDraw.size(); i++)
{
angle = angle + addAngle;
if (angle >= 2.0 * Math.PI)
{
angle -= 2.0 * Math.PI;
}
logger.debug("populatePolygonCorners->angle: ", Math.toDegrees(angle));
x = Math.cos(angle) * radius;
y = Math.sin(angle) * radius;
newX = x + rotationCenter.x;
newY = y + rotationCenter.y;
logger.debug(" newX:", newX);
logger.debug(" newY:", newY);
points.addElement(new Point2d(newX, newY));
if (logger.isDebugEnabled())
try
{
logger.debug("populatePolygonCorners->connectAtom: " + (molecule.getAtomNumber(connectAtom) + 1) + " placed at " + connectAtom.getPoint2d());
} catch (Exception exc)
{
//nothing to catch here. This is just for logging
}
}
for (int i = 0; i < atomsToDraw.size(); i++)
{
connectAtom = (IAtom) atomsToDraw.get(i);
connectAtom.setPoint2d((Point2d) points.elementAt(i));
connectAtom.setFlag(CDKConstants.ISPLACED, true);
}
}
/**
* Partition the bonding partners of a given atom into placed (coordinates
* assinged) and not placed.
*
*@param atom The atom whose bonding partners are to be
* partitioned
*@param unplacedPartners A vector for the unplaced bonding partners to go in
*@param placedPartners A vector for the placed bonding partners to go in
*/
public void partitionPartners(IAtom atom, IAtomContainer unplacedPartners, IAtomContainer placedPartners)
{
java.util.List atoms = molecule.getConnectedAtomsList(atom);
for (int i = 0; i < atoms.size(); i++)
{
IAtom curatom = (IAtom)atoms.get(i);
if (curatom.getFlag(CDKConstants.ISPLACED))
{
placedPartners.addAtom(curatom);
} else
{
unplacedPartners.addAtom(curatom);
}
}
}
/**
* Search an aliphatic molecule for the longest chain. This is the method to
* be used if there are no rings in the molecule and you want to layout the
* longest chain in the molecule as a starting point of the structure diagram
* generation.
*
*@param molecule The molecule
* to be search for the longest unplaced chain
*@return An
* AtomContainer holding the longest chain.
*@exception org.openscience.cdk.exception.NoSuchAtomException Description of
* the Exception
*/
public IAtomContainer getInitialLongestChain(IMolecule molecule) throws CDKException
{
logger.debug("Start of getInitialLongestChain()");
double[][] conMat = ConnectionMatrix.getMatrix(molecule);
logger.debug("Computing all-pairs-shortest-pathes");
int[][] apsp = PathTools.computeFloydAPSP(conMat);
int maxPathLength = 0;
int bestStartAtom = -1;
int bestEndAtom = -1;
IAtom atom = null;
IAtom startAtom = null;
//IAtom endAtom = null;
for (int f = 0; f < apsp.length; f++)
{
atom = molecule.getAtom(f);
if (molecule.getConnectedBondsCount(atom) == 1)
{
for (int g = 0; g < apsp.length; g++)
{
if (apsp[f][g] > maxPathLength)
{
maxPathLength = apsp[f][g];
bestStartAtom = f;
bestEndAtom = g;
}
}
}
}
logger.debug("Longest chaing in molecule is of length " + maxPathLength + " between atoms " + (bestStartAtom+1) + " and " + (bestEndAtom+1) );
startAtom = molecule.getAtom(bestStartAtom);
//endAtom = molecule.getAtomAt(bestEndAtom);
IAtomContainer path = molecule.getBuilder().newAtomContainer();
path.addAtom(startAtom);
path = getLongestUnplacedChain(molecule, startAtom);
//PathTools.depthFirstTargetSearch(molecule, startAtom, endAtom, path);
logger.debug("End of getInitialLongestChain()");
return path;
}
/**
* Search a molecule for the longest unplaced, aliphatic chain in it. If an
* aliphatic chain encounters an unplaced ring atom, the ring atom is also
* appended to allow for it to be laid out. This gives us a vector for
* attaching the unplaced ring later.
*
*@param molecule The molecule to be
* search for the longest unplaced chain
*@param startAtom A start atom from
* which the chain search starts
*@return An AtomContainer
* holding the longest unplaced chain.
*@exception org.openscience.cdk.exception.CDKException Description of the
* Exception
*/
public IAtomContainer getLongestUnplacedChain(IMolecule molecule, IAtom startAtom) throws CDKException
{
logger.debug("Start of getLongestUnplacedChain.");
//ConnectivityChecker cc = new ConnectivityChecker();
int longest = 0;
int longestPathLength = 0;
int maxDegreeSum = 0;
int degreeSum = 0;
IAtomContainer[] pathes = new IAtomContainer[molecule.getAtomCount()];
for (int f = 0; f < molecule.getAtomCount(); f++)
{
molecule.getAtom(f).setFlag(CDKConstants.VISITED, false);
pathes[f] = molecule.getBuilder().newAtomContainer();
pathes[f].addAtom(startAtom);
}
Vector startSphere = new Vector();
startSphere.addElement(startAtom);
breadthFirstSearch(molecule, startSphere, pathes);
for (int f = 0; f < molecule.getAtomCount(); f++)
{
if (pathes[f].getAtomCount() >= longestPathLength)
{
degreeSum = getDegreeSum(pathes[f], molecule);
if (degreeSum > maxDegreeSum)
{
maxDegreeSum = degreeSum;
longest = f;
longestPathLength = pathes[f].getAtomCount();
}
}
}
logger.debug("End of getLongestUnplacedChain.");
return pathes[longest];
}
/**
* Performs a breadthFirstSearch in an AtomContainer starting with a
* particular sphere, which usually consists of one start atom, and searches
* for the longest aliphatic chain which is yet unplaced. If the search
* encounters an unplaced ring atom, it is also appended to the chain so that
* this last bond of the chain can also be laid out. This gives us the
* orientation for the attachment of the ring system.
*
*@param ac The AtomContainer to
* be searched
*@param sphere A sphere of atoms to
* start the search with
*@param pathes A vector of N pathes
* (N = no of heavy atoms).
*@exception org.openscience.cdk.exception.CDKException Description of the
* Exception
*/
public void breadthFirstSearch(IAtomContainer ac, Vector sphere, IAtomContainer[] pathes) throws CDKException
{
IAtom atom = null;
IAtom nextAtom = null;
int atomNr;
int nextAtomNr;
//IAtomContainer path = null;
Vector newSphere = new Vector();
logger.debug("Start of breadthFirstSearch");
for (int f = 0; f < sphere.size(); f++)
{
atom = (IAtom) sphere.elementAt(f);
if (!atom.getFlag(CDKConstants.ISINRING))
{
atomNr = ac.getAtomNumber(atom);
logger.debug("BreadthFirstSearch around atom " + (atomNr + 1));
java.util.List bonds = ac.getConnectedBondsList(atom);
for (int g = 0; g < bonds.size(); g++)
{
IBond curBond = (IBond)bonds.get(g);
nextAtom = curBond.getConnectedAtom(atom);
if (!nextAtom.getFlag(CDKConstants.VISITED) &&
!nextAtom.getFlag(CDKConstants.ISPLACED))
{
nextAtomNr = ac.getAtomNumber(nextAtom);
logger.debug("BreadthFirstSearch is meeting new atom " + (nextAtomNr + 1));
pathes[nextAtomNr] = ac.getBuilder().newAtomContainer(pathes[atomNr]);
logger.debug("Making copy of path " + (atomNr + 1) + " to form new path " + (nextAtomNr + 1));
logger.debug("Old path " + (atomNr + 1) + " looks like: " + listNumbers(molecule, pathes[atomNr]));
logger.debug("Copied path " + (nextAtomNr + 1) + " looks like: " + listNumbers(molecule, pathes[nextAtomNr]));
pathes[nextAtomNr].addAtom(nextAtom);
logger.debug("Adding atom " + (nextAtomNr + 1) + " to path " + (nextAtomNr + 1));
pathes[nextAtomNr].addBond(curBond);
logger.debug("New path " + (nextAtomNr + 1) + " looks like: " + listNumbers(molecule, pathes[nextAtomNr]));
if (ac.getConnectedBondsCount(nextAtom) > 1)
{
newSphere.addElement(nextAtom);
}
}
}
}
}
if (newSphere.size() > 0)
{
for (int f = 0; f < newSphere.size(); f++)
{
((IAtom) newSphere.elementAt(f)).setFlag(CDKConstants.VISITED, true);
}
breadthFirstSearch(ac, newSphere, pathes);
}
logger.debug("End of breadthFirstSearch");
}
/**
* Returns a string with the numbers of all placed atoms in an AtomContainer
*
*@param ac The AtomContainer for which the placed atoms are to be listed
*@return A string with the numbers of all placed atoms in an AtomContainer
*/
public String listPlaced(IAtomContainer ac)
{
String s = "Placed: ";
for (int f = 0; f < ac.getAtomCount(); f++)
{
if (ac.getAtom(f).getFlag(CDKConstants.ISPLACED))
{
s += (f + 1) + "+ ";
} else
{
s += (f + 1) + "- ";
}
}
return s;
}
/**
* Returns a string with the numbers of all atoms in an AtomContainer relative
* to a given molecule. I.e. the number the is listesd is the position of each
* atom in the molecule.
*
*@param ac The AtomContainer for
* which the placed atoms are to be listed
*@param mol Description of the
* Parameter
*@return A string with the
* numbers of all placed atoms in an AtomContainer
*@exception org.openscience.cdk.exception.CDKException Description of the
* Exception
*/
public String listNumbers(IAtomContainer mol, IAtomContainer ac) throws CDKException
{
String s = "Numbers: ";
for (int f = 0; f < ac.getAtomCount(); f++)
{
s += (mol.getAtomNumber(ac.getAtom(f)) + 1) + " ";
}
return s;
}
/**
* Returns a string with the numbers of all atoms in a Vector relative to a
* given molecule. I.e. the number the is listesd is the position of each atom
* in the molecule.
*
*@param ac The Vector for which the placed atoms are to
* be listed
*@param mol Description of the Parameter
*@return A string with the numbers of all placed
* atoms in an AtomContainer
*@exception java.lang.Exception Description of the Exception
*/
public String listNumbers(IAtomContainer mol, Vector ac) throws java.lang.Exception
{
String s = "Numbers: ";
for (int f = 0; f < ac.size(); f++)
{
s += (mol.getAtomNumber((IAtom) ac.elementAt(f)) + 1) + " ";
}
return s;
}
/**
* True is all the atoms in the given AtomContainer have been placed
*
*@param ac The AtomContainer to be searched
*@return True is all the atoms in the given AtomContainer have been placed
*/
public boolean allPlaced(IAtomContainer ac)
{
for (int f = 0; f < ac.getAtomCount(); f++)
{
if (!ac.getAtom(f).getFlag(CDKConstants.ISPLACED))
{
return false;
}
}
return true;
}
/**
* Marks all the atoms in the given AtomContainer as not placed
*
*@param ac The AtomContainer whose atoms are to be marked
*/
public void markNotPlaced(IAtomContainer ac)
{
for (int f = 0; f < ac.getAtomCount(); f++)
{
ac.getAtom(f).setFlag(CDKConstants.ISPLACED, false);
}
}
/**
* Marks all the atoms in the given AtomContainer as placed
*
*@param ac The AtomContainer whose atoms are to be marked
*/
public void markPlaced(IAtomContainer ac)
{
for (int f = 0; f < ac.getAtomCount(); f++)
{
ac.getAtom(f).setFlag(CDKConstants.ISPLACED, true);
}
}
/**
* Get all the placed atoms in an AtomContainer
*
*@param ac The AtomContainer to be searched for placed atoms
*@return An AtomContainer containing all the placed atoms
*/
public IAtomContainer getPlacedAtoms(IAtomContainer ac)
{
IAtomContainer ret = ac.getBuilder().newAtomContainer();
for (int f = 0; f < ac.getAtomCount(); f++)
{
if (ac.getAtom(f).getFlag(CDKConstants.ISPLACED))
{
ret.addAtom(ac.getAtom(f));
}
}
return ret;
}
/**
* Sums up the degrees of atoms in an atomcontainer
*
*@param ac The atomcontainer to be processed
*@param superAC The superAtomContainer from which the former has been derived
*
*@return
*/
int getDegreeSum(IAtomContainer ac, IAtomContainer superAC)
{
int degreeSum = 0;
//String path = "DegreeSum for Path: ";
for (int f = 0; f < ac.getAtomCount(); f++)
{
//path += ac.getAtom(f).getSymbol();
degreeSum += superAC.getConnectedBondsCount(ac.getAtom(f));
}
//System.out.println(path + ": " + degreeSum);
return degreeSum;
}
/**
* Calculates weights for unplaced atoms.
*
*@param ac The atomcontainer for which weights are to be calculated
*/
void calculateWeights(IAtomContainer ac)
{
int[] weights = getWeightNumbers(ac);
for (int f = 0; f < ac.getAtomCount(); f++)
{
ac.getAtom(f).setProperty("Weight", new Integer(weights[f]));
}
}
/**
* Makes an array containing morgan-number-like number for an atomContainer.
*
*@param atomContainer The atomContainer to analyse.
*@return The morgan numbers value.
*/
int[] getWeightNumbers(IAtomContainer atomContainer)
{
int[] morganMatrix;
int[] tempMorganMatrix;
int N = atomContainer.getAtomCount();
morganMatrix = new int[N];
tempMorganMatrix = new int[N];
java.util.List atoms = null;
for (int f = 0; f < N; f++)
{
morganMatrix[f] = atomContainer.getConnectedBondsCount(f);
tempMorganMatrix[f] = atomContainer.getConnectedBondsCount(f);
}
for (int e = 0; e < N; e++)
{
for (int f = 0; f < N; f++)
{
morganMatrix[f] = 0;
atoms = atomContainer.getConnectedAtomsList(atomContainer.getAtom(f));
for (int g = 0; g < atoms.size(); g++)
{
IAtom atom = (IAtom)atoms.get(g);
if (!atom.getFlag(CDKConstants.ISPLACED))
{
morganMatrix[f] += tempMorganMatrix[atomContainer.getAtomNumber(atom)];
}
}
}
System.arraycopy(morganMatrix, 0, tempMorganMatrix, 0, N);
}
return tempMorganMatrix;
}
public boolean shouldBeLinear(IAtom atom, IAtomContainer molecule)
{
int sum = 0;
java.util.List bonds = molecule.getConnectedBondsList(atom);
for (int g = 0; g < bonds.size(); g++)
{
IBond bond = (IBond)bonds.get(g);
if (bond.getOrder() == IBond.Order.TRIPLE) sum += 10;
else if (bond.getOrder() == IBond.Order.SINGLE) sum += 1;
// else if (bond.getOrder() == IBond.Order.DOUBLE) sum += 5;
}
if (sum >= 10) return true;
return false;
}
}