/* $Revision$ $Author$ $Date$
*
* Copyright (C) 1997-2007 Christoph Steinbeck <steinbeck@users.sf.net>
*
* 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.Iterator;
import java.util.List;
import javax.vecmath.Point2d;
import javax.vecmath.Vector2d;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.geometry.GeometryTools;
import org.openscience.cdk.graph.ConnectivityChecker;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IAtomContainerSet;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IMolecule;
import org.openscience.cdk.interfaces.IRing;
import org.openscience.cdk.interfaces.IRingSet;
import org.openscience.cdk.ringsearch.RingPartitioner;
import org.openscience.cdk.ringsearch.SSSRFinder;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;
import org.openscience.cdk.tools.manipulator.AtomContainerSetManipulator;
import org.openscience.cdk.tools.manipulator.RingSetManipulator;
/**
* Generates 2D coordinates for a molecule for which only connectivity is known
* or the coordinates have been discarded for some reason. Usage: Create an
* instance of this class, thereby assigning a molecule, call
* generateCoordinates() and get your molecule back:
* <pre>
* StructureDiagramGenerator sdg = new StructureDiagramGenerator();
* sdg.setMolecule(someMolecule);
* sdg.generateCoordinates();
* Molecule layedOutMol = sdg.getMolecule();
* </pre>
*
* <p>The method will fail if the molecule is disconnected. The
* partitionIntoMolecules(AtomContainer) can help here.
*
* @author steinbeck
* @cdk.created 2004-02-02
* @see org.openscience.cdk.graph.ConnectivityChecker#partitionIntoMolecules(IAtomContainer)
* @cdk.keyword Layout
* @cdk.keyword Structure Diagram Generation (SDG)
* @cdk.keyword 2D-coordinates
* @cdk.keyword Coordinate generation, 2D
* @cdk.dictref blue-obelisk:layoutMolecule
* @cdk.module sdg
* @cdk.githash
* @cdk.bug 1536561
* @cdk.bug 1788686
*/
public class StructureDiagramGenerator
{
private ILoggingTool logger =
LoggingToolFactory.createLoggingTool(StructureDiagramGenerator.class);
private static TemplateHandler DEFAULT_TEMPLATE_HANDLER = null;
private IMolecule molecule;
private IRingSet sssr;
private double bondLength = 1.5;
private Vector2d firstBondVector;
private RingPlacer ringPlacer = new RingPlacer();
private AtomPlacer atomPlacer = new AtomPlacer();
private List ringSystems = null;
private final String disconnectedMessage = "Molecule not connected. Use ConnectivityChecker.partitionIntoMolecules() and do the layout for every single component.";
private TemplateHandler templateHandler = null;
private boolean useTemplates = true;
/** Atoms of the molecule that mapped a template */
private IAtomContainerSet mappedSubstructures;
/**
* The empty constructor.
*/
public StructureDiagramGenerator()
{
}
/**
* Creates an instance of this class while assigning a molecule to be layed
* out.
*
* @param molecule The molecule to be layed out.
*/
public StructureDiagramGenerator(IMolecule molecule) {
this();
setMolecule(molecule, false);
templateHandler = new TemplateHandler(molecule.getBuilder());
}
/**
* Assings a molecule to be layed out. Call generateCoordinates() to do the
* actual layout.
*
* @param mol the molecule for which coordinates are to be generated.
* @param clone Should the whole process be performed with a cloned copy?
*/
public void setMolecule(IMolecule mol, boolean clone) {
templateHandler = new TemplateHandler(mol.getBuilder());
IAtom atom = null;
if (clone)
{
try {
this.molecule = (IMolecule) mol.clone();
} catch (CloneNotSupportedException e) {
logger.error("Should clone, but exception occured: ", e.getMessage());
logger.debug(e);
}
} else
{
this.molecule = mol;
}
for (int f = 0; f < molecule.getAtomCount(); f++)
{
atom = molecule.getAtom(f);
atom.setPoint2d(null);
atom.setFlag(CDKConstants.ISPLACED, false);
atom.setFlag(CDKConstants.VISITED, false);
atom.setFlag(CDKConstants.ISINRING, false);
atom.setFlag(CDKConstants.ISALIPHATIC, false);
}
atomPlacer.setMolecule(this.molecule);
ringPlacer.setMolecule(this.molecule);
ringPlacer.setAtomPlacer(this.atomPlacer);
}
/**
* Sets whether to use templates or not. Some complicated ring systems
* like adamantane are only nicely layouted when using templates. This
* option is by default set true.
*
*@param useTemplates set true to use templates, false otherwise
*/
public void setUseTemplates(boolean useTemplates)
{
this.useTemplates = useTemplates;
}
/**
* Returns whether the use of templates is enabled or disabled.
*
* @return true, when the use of templates is enables, false otherwise
*/
public boolean getUseTemplates()
{
return useTemplates;
}
/**
* Sets the templateHandler attribute of the StructureDiagramGenerator object
*
* @param templateHandler The new templateHandler value
*/
public void setTemplateHandler(TemplateHandler templateHandler)
{
this.templateHandler = templateHandler;
}
/**
* Gets the templateHandler attribute of the StructureDiagramGenerator object
*
* @return The templateHandler value
*/
public TemplateHandler getTemplateHandler()
{
if (templateHandler == null)
{
return DEFAULT_TEMPLATE_HANDLER;
}
else
{
return templateHandler;
}
}
/**
* Assings a molecule to be layed out. Call generateCoordinates() to do the
* actual layout.
*
* @param molecule the molecule for which coordinates are to be generated.
*/
public void setMolecule(IMolecule molecule)
{
setMolecule(molecule, true);
}
/**
* Returns the molecule, usually used after a call of generateCoordinates()
*
* @return The molecule with new coordinates (if generateCoordinates() had
* been called)
*/
public IMolecule getMolecule()
{
return molecule;
}
/**
* This method uses generateCoordinates, but it removes the hydrogens first,
* lays out the structuren and then adds them again.
*
* @throws java.lang.Exception if an error occurs
* @see #generateCoordinates
*/
public void generateExperimentalCoordinates() throws java.lang.Exception
{
generateExperimentalCoordinates(new Vector2d(0, 1));
}
/**
* Generates 2D coordinates on the non-hydrogen skeleton, after which
* coordinates for the hydrogens are calculated.
*
* @param firstBondVector the vector of the first bond to lay out
* @throws java.lang.Exception if an error occurs
*/
public void generateExperimentalCoordinates(Vector2d firstBondVector) throws java.lang.Exception {
// first make a shallow copy: Atom/Bond references are kept
IMolecule original = molecule;
IMolecule shallowCopy = molecule.getBuilder().newMolecule(molecule);
// ok, delete H's from
//IAtom[] atoms = shallowCopy.getAtoms();
for (int i = 0; i < shallowCopy.getAtomCount(); i++) {
IAtom curAtom = shallowCopy.getAtom(i);
if (curAtom.getSymbol().equals("H")) {
shallowCopy.removeAtomAndConnectedElectronContainers(curAtom);
curAtom.setPoint2d(null);
}
}
// do layout on the shallow copy
molecule = shallowCopy;
generateCoordinates(firstBondVector);
double bondLength = GeometryTools.getBondLengthAverage(molecule);
// ok, now create the coordinates for the hydrogens
HydrogenPlacer hPlacer = new HydrogenPlacer();
molecule = original;
hPlacer.placeHydrogens2D(molecule, bondLength);
}
/**
* The main method of this StructurDiagramGenerator. Assign a molecule to the
* StructurDiagramGenerator, call the generateCoordinates() method and get
* your molecule back.
*
* @param firstBondVector The vector of the first bond to lay out
* @throws java.lang.Exception if an error occurs
*/
public void generateCoordinates(Vector2d firstBondVector) throws java.lang.Exception
{
int safetyCounter = 0;
/*
* if molecule contains only one Atom, don't fail, simply
* set coordinates to simplest: 0,0. See bug #780545
*/
logger.debug("Entry point of generateCoordinates()");
logger.debug("We have a molecules with " + molecule.getAtomCount() + " atoms.");
if (molecule.getAtomCount() == 1)
{
molecule.getAtom(0).setPoint2d(new Point2d(0, 0));
return;
}
if (!ConnectivityChecker.isConnected(molecule))
{
logger.debug("Molecule is not connected. Throwing exception.");
throw new CDKException(disconnectedMessage);
} else
{
logger.debug("Molecule is connected.");
}
/*
* compute the minimum number of rings as
* given by Frerejacque, Bull. Soc. Chim. Fr., 5, 1008 (1939)
*/
int nrOfEdges = molecule.getBondCount();
//Vector2d ringSystemVector = null;
//Vector2d newRingSystemVector = null;
this.firstBondVector = firstBondVector;
boolean templateMapped = false;
double angle;
/*
* First we check if we can map any templates with predefined coordinates
* Those are stored as CML in <i>org/openscience/cdk/layout/templates</i>.
*/
if (useTemplates && (System.getProperty("java.version").indexOf("1.3.") == -1))
{
logger.debug("Initializing TemplateHandler");
logger.debug("TemplateHander initialized");
logger.debug("Now starting Template Detection in Molecule...");
mappedSubstructures = getTemplateHandler().getMappedSubstructures(molecule);
templateMapped = mappedSubstructures.getAtomContainerCount() > 0;
logger.debug("Template Detection finished");
logger.debug("Number of found templates: " + mappedSubstructures.getAtomContainerCount());
}
int expectedRingCount = nrOfEdges - molecule.getAtomCount() + 1;
if (expectedRingCount > 0)
{
logger.debug("*** Start of handling rings. ***");
/*
* Get the smallest set of smallest rings on this molecule
*/
SSSRFinder sssrf = new SSSRFinder(molecule);
sssr = sssrf.findSSSR();
if (sssr.getAtomContainerCount() < 1)
{
return;
}
/*
* Order the rings because SSSRFinder.findSSSR() returns rings in an
* undeterministic order.
*/
AtomContainerSetManipulator.sort(sssr);
/*
* Mark all the atoms from the ring system as "ISINRING"
*/
markRingAtoms(sssr);
/*
* Give a handle of our molecule to the ringPlacer
*/
ringPlacer.setMolecule(molecule);
ringPlacer.checkAndMarkPlaced(sssr);
/*
* Partition the smallest set of smallest rings into disconnected ring system.
* The RingPartioner returns a Vector containing RingSets. Each of the RingSets contains
* rings that are connected to each other either as bridged ringsystems, fused rings or
* via spiro connections.
*/
ringSystems = RingPartitioner.partitionRings(sssr);
/*
* We got our ring systems now
*/
/*
* Do the layout for the first connected ring system ...
*/
int largest = 0;
int largestSize = ((IRingSet) ringSystems.get(0)).getAtomContainerCount();
logger.debug("We have " + ringSystems.size() + " ring system(s).");
for (int f = 0; f < ringSystems.size(); f++)
{
logger.debug("RingSet " + f + " has size " + ((IRingSet) ringSystems.get(f)).getAtomContainerCount());
if (((IRingSet) ringSystems.get(f)).getAtomContainerCount() > largestSize)
{
largestSize = ((IRingSet) ringSystems.get(f)).getAtomContainerCount();
largest = f;
}
}
logger.debug("Largest RingSystem is at RingSet collection's position " + largest);
logger.debug("Size of Largest RingSystem: " + largestSize);
layoutRingSet(firstBondVector, (IRingSet) ringSystems.get(largest));
logger.debug("First RingSet placed");
/*
* and do the placement of all the directly connected atoms of this ringsystem
*/
ringPlacer.placeRingSubstituents((IRingSet) ringSystems.get(largest), bondLength);
} else
{
logger.debug("*** Start of handling purely aliphatic molecules. ***");
/*
* We are here because there are no rings in the molecule
* so we get the longest chain in the molecule and placed in
* on a horizontal axis
*/
logger.debug("Searching initialLongestChain for this purely aliphatic molecule");
IAtomContainer longestChain = atomPlacer.getInitialLongestChain(molecule);
logger.debug("Found linear chain of length " + longestChain.getAtomCount());
logger.debug("Setting coordinated of first atom to 0,0");
longestChain.getAtom(0).setPoint2d(new Point2d(0, 0));
longestChain.getAtom(0).setFlag(CDKConstants.ISPLACED, true);
/*
* place the first bond such that the whole chain will be horizontally
* alligned on the x axis
*/
angle = Math.toRadians(-30);
logger.debug("Attempting to place the first bond such that the whole chain will be horizontally alligned on the x axis");
if (firstBondVector != null)
atomPlacer.placeLinearChain(longestChain, firstBondVector, bondLength);
else
atomPlacer.placeLinearChain(longestChain, new Vector2d(Math.cos(angle), Math.sin(angle)), bondLength);
logger.debug("Placed longest aliphatic chain");
}
/*
* Now, do the layout of the rest of the molecule
*/
do
{
safetyCounter++;
logger.debug("*** Start of handling the rest of the molecule. ***");
/*
* do layout for all aliphatic parts of the molecule which are
* connected to the parts which have already been laid out.
*/
handleAliphatics();
/*
* do layout for the next ring aliphatic parts of the molecule which are
* connected to the parts which have already been laid out.
*/
layoutNextRingSystem();
} while (!atomPlacer.allPlaced(molecule) && safetyCounter <= molecule.getAtomCount());
fixRest();
new OverlapResolver().resolveOverlap(molecule, sssr);
}
/**
* The main method of this StructurDiagramGenerator. Assign a molecule to the
* StructurDiagramGenerator, call the generateCoordinates() method and get
* your molecule back.
*
* @throws java.lang.Exception if an error occurs
*/
public void generateCoordinates() throws java.lang.Exception
{
generateCoordinates(new Vector2d(0, 1));
}
/**
* Does a layout of all the rings in a given connected RingSet. Uses a TemplateHandler
* to treat templated mapped substructures differently if <code>useTemplates</code> is
* set true.
*
* @param firstBondVector A vector giving the placement for the first bond
* @param rs The connected RingSet for which the layout is to be
* done
* @throws java.lang.Exception if an error occurs
*/
private void layoutRingSet(Vector2d firstBondVector, IRingSet rs) throws Exception
{
IAtomContainer sharedAtoms;
Vector2d ringCenterVector;
int thisRing;
logger.debug("Start of layoutRingSet");
/*
* First we check if we can map any templates with predifined coordinates.
* All mapped substructures are saved in: this.mappedSubstructures
*/
if (useTemplates && mappedSubstructures.getAtomContainerCount() > 0 && System.getProperty("java.version").indexOf("1.3.") == -1) {
/*
* Find mapped substructures
*/
for (Iterator<IAtomContainer> substructureIterator = mappedSubstructures.atomContainers().iterator(); substructureIterator.hasNext(); ) {
IAtomContainer substructure = (IAtomContainer) substructureIterator.next();
boolean substructureMapped = false;
for (Iterator<IAtomContainer> ringSetIterator = rs.atomContainers().iterator(); ringSetIterator.hasNext() && !substructureMapped; ) {
IRing ring = (IRing) ringSetIterator.next();
for (Iterator atomIterator = ring.atoms().iterator(); atomIterator.hasNext() && !substructureMapped; ) {
IAtom atom = (IAtom) atomIterator.next();
if (substructure.contains(atom))
substructureMapped = true;
}
}
/*
* Layout a found mapped substructure
*/
if (substructureMapped) {
boolean mapped = getTemplateHandler().mapTemplateExact(substructure);
if (!mapped)
logger.warn("A supposedly matched substructure failed to match.");
else {
// Mark substructure atoms as CDKConstants.ISPLACED
for (Iterator iterator = substructure.atoms().iterator(); iterator.hasNext(); ) {
IAtom atom = (IAtom) iterator.next();
atom.setFlag(CDKConstants.ISPLACED, true);
}
// Mark rings of substrucure as CDKConstants.ISPLACED
ringPlacer.checkAndMarkPlaced(rs);
}
}
}
}
/*
* Now layout the rest of this ring system
*/
/*
* Get the most complex ring in this RingSet
*/
IRing ring = RingSetManipulator.getMostComplexRing(rs);
int i = 0;
/*
* Place the most complex ring at the origin of the coordinate system
*/
if (!ring.getFlag(CDKConstants.ISPLACED))
{
sharedAtoms = placeFirstBond((IBond) ring.getBond(i), firstBondVector);
/*
* Call the method which lays out the new ring.
*/
ringCenterVector = ringPlacer.getRingCenterOfFirstRing(ring, firstBondVector, bondLength);
ringPlacer.placeRing(ring, sharedAtoms, GeometryTools.get2DCenter(sharedAtoms), ringCenterVector, bondLength);
/*
* Mark the ring as placed
*/
ring.setFlag(CDKConstants.ISPLACED, true);
}
/*
* Place all other rings in this ringsystem.
*/
thisRing = 0;
do
{
if (ring.getFlag(CDKConstants.ISPLACED))
{
ringPlacer.placeConnectedRings(rs, ring, RingPlacer.FUSED, bondLength);
ringPlacer.placeConnectedRings(rs, ring, RingPlacer.BRIDGED, bondLength);
ringPlacer.placeConnectedRings(rs, ring, RingPlacer.SPIRO, bondLength);
}
thisRing++;
if (thisRing == rs.getAtomContainerCount())
{
thisRing = 0;
}
ring = (IRing) rs.getAtomContainer(thisRing);
} while (!allPlaced(rs));
logger.debug("End of layoutRingSet");
}
/**
* Does a layout of all aliphatic parts connected to the parts of the molecule
* that have already been laid out. Starts at the first bond with unplaced
* neighbours and stops when a ring is encountered.
*
* @throws org.openscience.cdk.exception.CDKException if an error occurs
*/
private void handleAliphatics() throws CDKException
{
logger.debug("Start of handleAliphatics");
int safetyCounter = 0;
IAtomContainer unplacedAtoms = null;
IAtomContainer placedAtoms = null;
IAtomContainer longestUnplacedChain = null;
IAtom atom = null;
Vector2d direction = null;
Vector2d startVector = null;
boolean done;
do
{
safetyCounter++;
done = false;
atom = getNextAtomWithAliphaticUnplacedNeigbors();
if (atom != null)
{
unplacedAtoms = getUnplacedAtoms(atom);
placedAtoms = getPlacedAtoms(atom);
longestUnplacedChain = atomPlacer.getLongestUnplacedChain(molecule, atom);
logger.debug("---start of longest unplaced chain---");
try
{
logger.debug("Start at atom no. " + (molecule.getAtomNumber(atom) + 1));
logger.debug(atomPlacer.listNumbers(molecule, longestUnplacedChain));
} catch (Exception exc) {
logger.debug(exc);
}
logger.debug("---end of longest unplaced chain---");
if (longestUnplacedChain.getAtomCount() > 1)
{
if (placedAtoms.getAtomCount() > 1)
{
logger.debug("More than one atoms placed already");
logger.debug("trying to place neighbors of atom " + (molecule.getAtomNumber(atom) + 1));
atomPlacer.distributePartners(atom, placedAtoms, GeometryTools.get2DCenter(placedAtoms), unplacedAtoms, bondLength);
direction = new Vector2d(longestUnplacedChain.getAtom(1).getPoint2d());
startVector = new Vector2d(atom.getPoint2d());
direction.sub(startVector);
logger.debug("Done placing neighbors of atom " + (molecule.getAtomNumber(atom) + 1));
} else
{
logger.debug("Less than or equal one atoms placed already");
logger.debug("Trying to get next bond vector.");
direction = atomPlacer.getNextBondVector(atom, placedAtoms.getAtom(0), GeometryTools.get2DCenter(molecule),true);
}
for (int f = 1; f < longestUnplacedChain.getAtomCount(); f++)
{
longestUnplacedChain.getAtom(f).setFlag(CDKConstants.ISPLACED, false);
}
atomPlacer.placeLinearChain(longestUnplacedChain, direction, bondLength);
} else
{
done = true;
}
} else
{
done = true;
}
} while (!done && safetyCounter <= molecule.getAtomCount());
logger.debug("End of handleAliphatics");
}
/**
* Does the layout for the next RingSystem that is connected to those parts of
* the molecule that have already been laid out. Finds the next ring with an
* unplaced ring atom and lays out this ring. Then lays out the ring substituents
* of this ring. Then moves and rotates the laid out ring to match the position
* of its attachment bond to the rest of the molecule.
*
* @throws java.lang.Exception if an error occurs
*/
private void layoutNextRingSystem() throws Exception
{
logger.debug("Start of layoutNextRingSystem()");
resetUnplacedRings();
IAtomContainer tempAc = atomPlacer.getPlacedAtoms(molecule);
logger.debug("Finding attachment bond to already placed part...");
IBond nextRingAttachmentBond = getNextBondWithUnplacedRingAtom();
if (nextRingAttachmentBond != null)
{
logger.debug("...bond found.");
/*
* Get the chain and the ring atom that are connected to where we are comming from.
* Both are connected by nextRingAttachmentBond.
*/
IAtom ringAttachmentAtom = getRingAtom(nextRingAttachmentBond);
IAtom chainAttachmentAtom = getOtherBondAtom(ringAttachmentAtom, nextRingAttachmentBond);
/*
* Get ring system which ringAttachmentAtom is part of
*/
IRingSet nextRingSystem = getRingSystemOfAtom(ringSystems, ringAttachmentAtom);
/*
* Get all rings of nextRingSytem as one IAtomContainer
*/
IAtomContainer ringSystem = tempAc.getBuilder().newAtomContainer();
for (Iterator containers = RingSetManipulator.getAllAtomContainers(nextRingSystem).iterator(); containers.hasNext(); )
ringSystem.add((IAtomContainer) containers.next());
/*
* Save coordinates of ringAttachmentAtom and chainAttachmentAtom
*/
Point2d oldRingAttachmentAtomPoint = ringAttachmentAtom.getPoint2d();
Point2d oldChainAttachmentAtomPoint = chainAttachmentAtom.getPoint2d();
/*
* Do the layout of the next ring system
*/
layoutRingSet(firstBondVector, nextRingSystem);
/*
* Place all the substituents of next ring system
*/
atomPlacer.markNotPlaced(tempAc);
IAtomContainer placedRingSubstituents = ringPlacer.placeRingSubstituents(nextRingSystem, bondLength);
ringSystem.add(placedRingSubstituents);
atomPlacer.markPlaced(tempAc);
/*
* Move and rotate the laid out ring system to match the geometry of the
* attachment bond
*/
logger.debug("Computing translation/rotation of new ringset to fit old attachment bond orientation...");
// old placed ring atom coordinate
Point2d oldPoint2 = oldRingAttachmentAtomPoint;
// old placed substituent atom coordinate
Point2d oldPoint1 = oldChainAttachmentAtomPoint;
// new placed ring atom coordinate
Point2d newPoint2 = ringAttachmentAtom.getPoint2d();
// new placed substituent atom coordinate
Point2d newPoint1 = chainAttachmentAtom.getPoint2d();
logger.debug("oldPoint1: " + oldPoint1);
logger.debug("oldPoint2: " + oldPoint2);
logger.debug("newPoint1: " + newPoint1);
logger.debug("newPoint2: " + newPoint2);
double oldAngle = GeometryTools.getAngle(oldPoint2.x - oldPoint1.x, oldPoint2.y - oldPoint1.y);
double newAngle = GeometryTools.getAngle(newPoint2.x - newPoint1.x, newPoint2.y - newPoint1.y);
double angleDiff = oldAngle - newAngle;
logger.debug("oldAngle: " + oldAngle + ", newAngle: " + newAngle + "; diff = " + angleDiff);
Vector2d translationVector = new Vector2d(oldPoint1);
translationVector.sub(new Vector2d(newPoint1));
/*
* Move to fit old attachment bond orientation
*/
GeometryTools.translate2D(ringSystem, translationVector);
/*
* Rotate to fit old attachment bond orientation
*/
GeometryTools.rotate(ringSystem, oldPoint1, angleDiff);
logger.debug("...done translating/rotating new ringset to fit old attachment bond orientation.");
}
else
logger.debug("...no bond found");
logger.debug("End of layoutNextRingSystem()");
}
/**
* Returns an AtomContainer with all unplaced atoms connected to a given
* atom
*
* @param atom The Atom whose unplaced bonding partners are to be returned
* @return an AtomContainer with all unplaced atoms connected to a
* given atom
*/
private IAtomContainer getUnplacedAtoms(IAtom atom)
{
IAtomContainer unplacedAtoms = atom.getBuilder().newAtomContainer();
java.util.List bonds = molecule.getConnectedBondsList(atom);
IAtom connectedAtom;
for (int f = 0; f < bonds.size(); f++)
{
connectedAtom = ((IBond)bonds.get(f)).getConnectedAtom(atom);
if (!connectedAtom.getFlag(CDKConstants.ISPLACED))
{
unplacedAtoms.addAtom(connectedAtom);
}
}
return unplacedAtoms;
}
/**
* Returns an AtomContainer with all placed atoms connected to a given
* atom
*
* @param atom The Atom whose placed bonding partners are to be returned
* @return an AtomContainer with all placed atoms connected to a given
* atom
*/
private IAtomContainer getPlacedAtoms(IAtom atom)
{
IAtomContainer placedAtoms = atom.getBuilder().newAtomContainer();
java.util.List bonds = molecule.getConnectedBondsList(atom);
IAtom connectedAtom;
for (int f = 0; f < bonds.size(); f++)
{
connectedAtom = ((IBond)bonds.get(f)).getConnectedAtom(atom);
if (connectedAtom.getFlag(CDKConstants.ISPLACED))
{
placedAtoms.addAtom(connectedAtom);
}
}
return placedAtoms;
}
/**
* Returns the next atom with unplaced aliphatic neighbors
*
* @return the next atom with unplaced aliphatic neighbors
*/
private IAtom getNextAtomWithAliphaticUnplacedNeigbors()
{
IBond bond;
for (int f = 0; f < molecule.getBondCount(); f++)
{
bond = molecule.getBond(f);
if (bond.getAtom(1).getFlag(CDKConstants.ISPLACED) &&
!bond.getAtom(0).getFlag(CDKConstants.ISPLACED))
{
return bond.getAtom(1);
}
if (bond.getAtom(0).getFlag(CDKConstants.ISPLACED) &&
!bond.getAtom(1).getFlag(CDKConstants.ISPLACED))
{
return bond.getAtom(0);
}
}
return null;
}
/**
* Returns the next bond with an unplaced ring atom
*
* @return the next bond with an unplaced ring atom
*/
private IBond getNextBondWithUnplacedRingAtom() {
Iterator bonds = molecule.bonds().iterator();
while (bonds.hasNext()) {
IBond bond = (IBond) bonds.next();
if (bond.getAtom(0).getPoint2d() != null &&
bond.getAtom(1).getPoint2d() != null) {
if (bond.getAtom(1).getFlag(CDKConstants.ISPLACED) &&
!bond.getAtom(0).getFlag(CDKConstants.ISPLACED) &&
bond.getAtom(0).getFlag(CDKConstants.ISINRING)) {
return bond;
}
if (bond.getAtom(0).getFlag(CDKConstants.ISPLACED) &&
!bond.getAtom(1).getFlag(CDKConstants.ISPLACED) &&
bond.getAtom(1).getFlag(CDKConstants.ISINRING)) {
return bond;
}
}
}
return null;
}
/**
* Places the first bond of the first ring such that one atom is at (0,0) and
* the other one at the position given by bondVector
*
* @param bondVector A 2D vector to point to the position of the second bond
* atom
* @param bond the bond to lay out
* @return an IAtomContainer with the atoms of the bond and the bond itself
*/
private IAtomContainer placeFirstBond(IBond bond, Vector2d bondVector)
{
IAtomContainer sharedAtoms = null;
try
{
bondVector.normalize();
logger.debug("placeFirstBondOfFirstRing->bondVector.length():" + bondVector.length());
bondVector.scale(bondLength);
logger.debug("placeFirstBondOfFirstRing->bondVector.length() after scaling:" + bondVector.length());
IAtom atom;
Point2d point = new Point2d(0, 0);
atom = bond.getAtom(0);
logger.debug("Atom 1 of first Bond: " + (molecule.getAtomNumber(atom) + 1));
atom.setPoint2d(point);
atom.setFlag(CDKConstants.ISPLACED, true);
point = new Point2d(0, 0);
atom = bond.getAtom(1);
logger.debug("Atom 2 of first Bond: " + (molecule.getAtomNumber(atom) + 1));
point.add(bondVector);
atom.setPoint2d(point);
atom.setFlag(CDKConstants.ISPLACED, true);
/*
* The new ring is layed out relativ to some shared atoms that have already been
* placed. Usually this is another ring, that has already been draw and to which the new
* ring is somehow connected, or some other system of atoms in an aliphatic chain.
* In this case, it's the first bond that we layout by hand.
*/
sharedAtoms = atom.getBuilder().newAtomContainer();
sharedAtoms.addBond(bond);
sharedAtoms.addAtom(bond.getAtom(0));
sharedAtoms.addAtom(bond.getAtom(1));
} catch (Exception exc) {
logger.debug(exc);
}
return sharedAtoms;
}
/**
* This method will go as soon as the rest works. It just assignes Point2d's
* of position (0,0) so that the molecule can be drawn.
*/
private void fixRest()
{
IAtom atom = null;
for (int f = 0; f < molecule.getAtomCount(); f++)
{
atom = molecule.getAtom(f);
if (atom.getPoint2d() == null)
{
atom.setPoint2d(new Point2d(0, 0));
}
}
}
/**
* This method will go as soon as the rest works. It just assignes Point2d's
* of position (0,0) so that the molecule can be drawn.
* @param molecule the molecule to fix
* @return the fixed molecule
*/
private IMolecule fixMol(IMolecule molecule)
{
IAtom atom = null;
for (int f = 0; f < molecule.getAtomCount(); f++)
{
atom = molecule.getAtom(f);
if (atom.getPoint2d() == null)
{
atom.setPoint2d(new Point2d(0, 0));
}
}
return molecule;
}
/**
* Initializes all rings in RingSet rs as not placed
*
* @param rs The RingSet to be initialized
*/
// private void markNotPlaced(IRingSet rs)
// {
// for (int f = 0; f < rs.size(); f++)
// {
// ((IRing) rs.get(f)).setFlag(CDKConstants.ISPLACED, false);
// }
// }
/**
* Are all rings in the Vector placed?
*
* @param rings The Vector to be checked
* @return true if all rings are placed, false otherwise
*/
private boolean allPlaced(IRingSet rings)
{
for (int f = 0; f < rings.getAtomContainerCount(); f++)
{
if (!((IRing) rings.getAtomContainer(f)).getFlag(CDKConstants.ISPLACED))
{
logger.debug("allPlaced->Ring " + f + " not placed");
return false;
}
}
return true;
}
/**
* Mark all atoms in the molecule as being part of a ring
*
* @param rings an IRingSet with the rings to process
*/
private void markRingAtoms(IRingSet rings)
{
IRing ring = null;
for (int i = 0; i < rings.getAtomContainerCount(); i++)
{
ring = (IRing) rings.getAtomContainer(i);
for (int j = 0; j < ring.getAtomCount(); j++)
{
ring.getAtom(j).setFlag(CDKConstants.ISINRING, true);
}
}
}
/**
* Get the unplaced ring atom in this bond
*
* @param bond the bond to be search for the unplaced ring atom
* @return the unplaced ring atom in this bond
*/
private IAtom getRingAtom(IBond bond)
{
if (bond.getAtom(0).getFlag(CDKConstants.ISINRING) &&
!bond.getAtom(0).getFlag(CDKConstants.ISPLACED))
{
return bond.getAtom(0);
}
if (bond.getAtom(1).getFlag(CDKConstants.ISINRING) &&
!bond.getAtom(1).getFlag(CDKConstants.ISPLACED))
{
return bond.getAtom(1);
}
return null;
}
/**
* Get the ring system of which the given atom is part of
*
* @param ringSystems a List of ring systems to be searched
* @param ringAtom the ring atom to be search in the ring system.
* @return the ring system the given atom is part of
*/
private IRingSet getRingSystemOfAtom(List ringSystems, IAtom ringAtom)
{
IRingSet ringSet = null;
for (int f = 0; f < ringSystems.size(); f++)
{
ringSet = (IRingSet) ringSystems.get(f);
if (ringSet.contains(ringAtom))
{
return ringSet;
}
}
return null;
}
/**
* Set all the atoms in unplaced rings to be unplaced
*/
private void resetUnplacedRings()
{
IRing ring = null;
if (sssr == null)
{
return;
}
int unplacedCounter = 0;
for (int f = 0; f < sssr.getAtomContainerCount(); f++)
{
ring = (IRing) sssr.getAtomContainer(f);
if (!ring.getFlag(CDKConstants.ISPLACED))
{
logger.debug("Ring with " + ring.getAtomCount() + " atoms is not placed.");
unplacedCounter++;
for (int g = 0; g < ring.getAtomCount(); g++)
{
ring.getAtom(g).setFlag(CDKConstants.ISPLACED, false);
}
}
}
logger.debug("There are " + unplacedCounter + " unplaced Rings.");
}
/**
* Set the bond length used for laying out the molecule.
* The defaut value is 1.5.
*
* @param bondLength The new bondLength value
*/
public void setBondLength(double bondLength)
{
this.bondLength = bondLength;
}
/**
* Returns the other atom of the bond.
* Expects bond to have only two atoms.
* Returns null if the given atom is not part of the given bond.
*
* @param atom the atom we already have
* @param bond the bond
* @return the other atom of the bond
*/
public IAtom getOtherBondAtom(IAtom atom, IBond bond) {
if (!bond.contains(atom))
return null;
if (bond.getAtom(0).equals(atom))
return bond.getAtom(1);
else
return bond.getAtom(0);
}
}