1
/* $Revision: 9065 $ $Author: egonw $ $Date: 2007-10-14 22:04:57 +0200 (Sun, 14 Oct 2007) $
3
* Copyright (C) 1997-2007 Christoph Steinbeck <steinbeck@users.sf.net>
5
* Contact: cdk-devel@lists.sourceforge.net
7
* This program is free software; you can redistribute it and/or
8
* modify it under the terms of the GNU Lesser General Public License
9
* as published by the Free Software Foundation; either version 2.1
10
* of the License, or (at your option) any later version.
11
* All we ask is that proper credit is given for our work, which includes
12
* - but is not limited to - adding the above copyright notice to the beginning
13
* of your source code files, and to any copyright notice that you may distribute
14
* with programs based on this work.
16
* This program is distributed in the hope that it will be useful,
17
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
* GNU Lesser General Public License for more details.
21
* You should have received a copy of the GNU Lesser General Public License
22
* along with this program; if not, write to the Free Software
23
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
26
package org.openscience.cdk.layout;
28
import java.util.Iterator;
29
import java.util.List;
31
import javax.vecmath.Point2d;
32
import javax.vecmath.Vector2d;
34
import org.openscience.cdk.CDKConstants;
35
import org.openscience.cdk.exception.CDKException;
36
import org.openscience.cdk.geometry.GeometryToolsInternalCoordinates;
37
import org.openscience.cdk.graph.ConnectivityChecker;
38
import org.openscience.cdk.interfaces.IAtom;
39
import org.openscience.cdk.interfaces.IAtomContainer;
40
import org.openscience.cdk.interfaces.IAtomContainerSet;
41
import org.openscience.cdk.interfaces.IBond;
42
import org.openscience.cdk.interfaces.IMolecule;
43
import org.openscience.cdk.interfaces.IRing;
44
import org.openscience.cdk.interfaces.IRingSet;
45
import org.openscience.cdk.ringsearch.RingPartitioner;
46
import org.openscience.cdk.ringsearch.SSSRFinder;
47
import org.openscience.cdk.tools.LoggingTool;
48
import org.openscience.cdk.tools.manipulator.AtomContainerSetManipulator;
49
import org.openscience.cdk.tools.manipulator.RingSetManipulator;
52
* Generates 2D coordinates for a molecule for which only connectivity is known
53
* or the coordinates have been discarded for some reason. Usage: Create an
54
* instance of this class, thereby assigning a molecule, call
55
* generateCoordinates() and get your molecule back:
57
* StructureDiagramGenerator sdg = new StructureDiagramGenerator();
58
* sdg.setMolecule(someMolecule);
59
* sdg.generateCoordinates();
60
* Molecule layedOutMol = sdg.getMolecule();
63
* <p>The method will fail if the molecule is disconnected. The
64
* partitionIntoMolecules(AtomContainer) can help here.
67
* @cdk.created 2004-02-02
68
* @see org.openscience.cdk.graph.ConnectivityChecker#partitionIntoMolecules(IAtomContainer)
70
* @cdk.keyword Structure Diagram Generation (SDG)
71
* @cdk.keyword 2D coordinates
72
* @cdk.keyword Coordinate generation, 2D
73
* @cdk.dictref blue-obelisk:layoutMolecule
78
public class StructureDiagramGenerator
81
private LoggingTool logger = new LoggingTool(StructureDiagramGenerator.class);
83
private static TemplateHandler DEFAULT_TEMPLATE_HANDLER = null;
85
private IMolecule molecule;
86
private IRingSet sssr;
87
private double bondLength = 1.5;
88
private Vector2d firstBondVector;
89
private RingPlacer ringPlacer = new RingPlacer();
90
private AtomPlacer atomPlacer = new AtomPlacer();
91
private List ringSystems = null;
92
private final String disconnectedMessage = "Molecule not connected. Use ConnectivityChecker.partitionIntoMolecules() and do the layout for every single component.";
93
private TemplateHandler templateHandler = null;
94
private boolean useTemplates = true;
96
/** Atoms of the molecule that mapped a template */
97
private IAtomContainerSet mappedSubstructures;
101
* The empty constructor.
103
public StructureDiagramGenerator()
109
* Creates an instance of this class while assigning a molecule to be layed
112
* @param molecule The molecule to be layed out.
114
public StructureDiagramGenerator(IMolecule molecule) {
116
setMolecule(molecule, false);
117
templateHandler = new TemplateHandler(molecule.getBuilder());
123
* Assings a molecule to be layed out. Call generateCoordinates() to do the
126
* @param mol the molecule for which coordinates are to be generated.
127
* @param clone Should the whole process be performed with a cloned copy?
129
public void setMolecule(IMolecule mol, boolean clone) {
130
templateHandler = new TemplateHandler(mol.getBuilder());
135
this.molecule = (IMolecule) mol.clone();
136
} catch (CloneNotSupportedException e) {
137
logger.error("Should clone, but exception occured: ", e.getMessage());
144
for (int f = 0; f < molecule.getAtomCount(); f++)
146
atom = molecule.getAtom(f);
147
atom.setPoint2d(null);
148
atom.setFlag(CDKConstants.ISPLACED, false);
149
atom.setFlag(CDKConstants.VISITED, false);
150
atom.setFlag(CDKConstants.ISINRING, false);
151
atom.setFlag(CDKConstants.ISALIPHATIC, false);
153
atomPlacer.setMolecule(this.molecule);
154
ringPlacer.setMolecule(this.molecule);
155
ringPlacer.setAtomPlacer(this.atomPlacer);
160
* Sets whether to use templates or not. Some complicated ring systems
161
* like adamantane are only nicely layouted when using templates. This
162
* option is by default set true.
164
*@param useTemplates set true to use templates, false otherwise
166
public void setUseTemplates(boolean useTemplates)
168
this.useTemplates = useTemplates;
173
* Returns whether the use of templates is enabled or disabled.
175
* @return true, when the use of templates is enables, false otherwise
177
public boolean getUseTemplates()
184
* Sets the templateHandler attribute of the StructureDiagramGenerator object
186
* @param templateHandler The new templateHandler value
188
public void setTemplateHandler(TemplateHandler templateHandler)
190
this.templateHandler = templateHandler;
195
* Gets the templateHandler attribute of the StructureDiagramGenerator object
197
* @return The templateHandler value
199
public TemplateHandler getTemplateHandler()
201
if (templateHandler == null)
203
return DEFAULT_TEMPLATE_HANDLER;
207
return templateHandler;
213
* Assings a molecule to be layed out. Call generateCoordinates() to do the
216
* @param molecule the molecule for which coordinates are to be generated.
218
public void setMolecule(IMolecule molecule)
220
setMolecule(molecule, true);
225
* Returns the molecule, usually used after a call of generateCoordinates()
227
* @return The molecule with new coordinates (if generateCoordinates() had
230
public IMolecule getMolecule()
237
* This method uses generateCoordinates, but it removes the hydrogens first,
238
* lays out the structuren and then adds them again.
240
* @throws java.lang.Exception if an error occurs
241
* @see #generateCoordinates
243
public void generateExperimentalCoordinates() throws java.lang.Exception
245
generateExperimentalCoordinates(new Vector2d(0, 1));
250
* Generates 2D coordinates on the non-hydrogen skeleton, after which
251
* coordinates for the hydrogens are calculated.
253
* @param firstBondVector the vector of the first bond to lay out
254
* @throws java.lang.Exception if an error occurs
256
public void generateExperimentalCoordinates(Vector2d firstBondVector) throws java.lang.Exception {
257
// first make a shallow copy: Atom/Bond references are kept
258
IMolecule original = molecule;
259
IMolecule shallowCopy = molecule.getBuilder().newMolecule(molecule);
260
// ok, delete H's from
261
//IAtom[] atoms = shallowCopy.getAtoms();
262
for (int i = 0; i < shallowCopy.getAtomCount(); i++) {
263
IAtom curAtom = shallowCopy.getAtom(i);
264
if (curAtom.getSymbol().equals("H")) {
265
shallowCopy.removeAtomAndConnectedElectronContainers(curAtom);
266
curAtom.setPoint2d(null);
269
// do layout on the shallow copy
270
molecule = shallowCopy;
271
generateCoordinates(firstBondVector);
272
double bondLength = GeometryToolsInternalCoordinates.getBondLengthAverage(molecule);
273
// ok, now create the coordinates for the hydrogens
274
HydrogenPlacer hPlacer = new HydrogenPlacer();
276
hPlacer.placeHydrogens2D(molecule, bondLength);
281
* The main method of this StructurDiagramGenerator. Assign a molecule to the
282
* StructurDiagramGenerator, call the generateCoordinates() method and get
283
* your molecule back.
285
* @param firstBondVector The vector of the first bond to lay out
286
* @throws java.lang.Exception if an error occurs
288
public void generateCoordinates(Vector2d firstBondVector) throws java.lang.Exception
290
int safetyCounter = 0;
292
* if molecule contains only one Atom, don't fail, simply
293
* set coordinates to simplest: 0,0. See bug #780545
295
logger.debug("Entry point of generateCoordinates()");
296
logger.debug("We have a molecules with " + molecule.getAtomCount() + " atoms.");
297
if (molecule.getAtomCount() == 1)
299
molecule.getAtom(0).setPoint2d(new Point2d(0, 0));
302
if (!ConnectivityChecker.isConnected(molecule))
304
logger.debug("Molecule is not connected. Throwing exception.");
305
throw new CDKException(disconnectedMessage);
308
logger.debug("Molecule is connected.");
312
* compute the minimum number of rings as
313
* given by Frerejacque, Bull. Soc. Chim. Fr., 5, 1008 (1939)
315
int nrOfEdges = molecule.getBondCount();
316
//Vector2d ringSystemVector = null;
317
//Vector2d newRingSystemVector = null;
318
this.firstBondVector = firstBondVector;
319
boolean templateMapped = false;
323
* First we check if we can map any templates with predefined coordinates
324
* Those are stored as MDL molfiles in data/templates
326
if (useTemplates && (System.getProperty("java.version").indexOf("1.3.") == -1))
328
logger.debug("Initializing TemplateHandler");
329
logger.debug("TemplateHander initialized");
330
logger.debug("Now starting Template Detection in Molecule...");
331
mappedSubstructures = getTemplateHandler().getMappedSubstructures(molecule);
332
templateMapped = mappedSubstructures.getAtomContainerCount() > 0;
333
logger.debug("Template Detection finished");
334
logger.debug("Number of found templates: " + mappedSubstructures.getAtomContainerCount());
337
int expectedRingCount = nrOfEdges - molecule.getAtomCount() + 1;
338
if (expectedRingCount > 0)
340
logger.debug("*** Start of handling rings. ***");
342
* Get the smallest set of smallest rings on this molecule
344
SSSRFinder sssrf = new SSSRFinder(molecule);
346
sssr = sssrf.findSSSR();
347
if (sssr.getAtomContainerCount() < 1)
353
* Order the rings because SSSRFinder.findSSSR() returns rings in an
354
* undeterministic order.
356
AtomContainerSetManipulator.sort(sssr);
359
* Mark all the atoms from the ring system as "ISINRING"
363
* Give a handle of our molecule to the ringPlacer
365
ringPlacer.setMolecule(molecule);
366
ringPlacer.checkAndMarkPlaced(sssr);
368
* Partition the smallest set of smallest rings into disconnected ring system.
369
* The RingPartioner returns a Vector containing RingSets. Each of the RingSets contains
370
* rings that are connected to each other either as bridged ringsystems, fused rings or
371
* via spiro connections.
373
ringSystems = RingPartitioner.partitionRings(sssr);
376
* We got our ring systems now
380
* Do the layout for the first connected ring system ...
383
int largestSize = ((IRingSet) ringSystems.get(0)).getAtomContainerCount();
384
logger.debug("We have " + ringSystems.size() + " ring system(s).");
385
for (int f = 0; f < ringSystems.size(); f++)
387
logger.debug("RingSet " + f + " has size " + ((IRingSet) ringSystems.get(f)).getAtomContainerCount());
388
if (((IRingSet) ringSystems.get(f)).getAtomContainerCount() > largestSize)
390
largestSize = ((IRingSet) ringSystems.get(f)).getAtomContainerCount();
394
logger.debug("Largest RingSystem is at RingSet collection's position " + largest);
395
logger.debug("Size of Largest RingSystem: " + largestSize);
397
layoutRingSet(firstBondVector, (IRingSet) ringSystems.get(largest));
398
logger.debug("First RingSet placed");
400
* and do the placement of all the directly connected atoms of this ringsystem
402
ringPlacer.placeRingSubstituents((IRingSet) ringSystems.get(largest), bondLength);
407
logger.debug("*** Start of handling purely aliphatic molecules. ***");
409
* We are here because there are no rings in the molecule
410
* so we get the longest chain in the molecule and placed in
411
* on a horizontal axis
413
logger.debug("Searching initialLongestChain for this purely aliphatic molecule");
414
IAtomContainer longestChain = atomPlacer.getInitialLongestChain(molecule);
415
logger.debug("Found linear chain of length " + longestChain.getAtomCount());
416
logger.debug("Setting coordinated of first atom to 0,0");
417
longestChain.getAtom(0).setPoint2d(new Point2d(0, 0));
418
longestChain.getAtom(0).setFlag(CDKConstants.ISPLACED, true);
421
* place the first bond such that the whole chain will be horizontally
422
* alligned on the x axis
424
angle = Math.toRadians(-30);
425
logger.debug("Attempting to place the first bond such that the whole chain will be horizontally alligned on the x axis");
426
if (firstBondVector != null)
427
atomPlacer.placeLinearChain(longestChain, firstBondVector, bondLength);
429
atomPlacer.placeLinearChain(longestChain, new Vector2d(Math.cos(angle), Math.sin(angle)), bondLength);
430
logger.debug("Placed longest aliphatic chain");
434
* Now, do the layout of the rest of the molecule
439
logger.debug("*** Start of handling the rest of the molecule. ***");
441
* do layout for all aliphatic parts of the molecule which are
442
* connected to the parts which have already been laid out.
446
* do layout for the next ring aliphatic parts of the molecule which are
447
* connected to the parts which have already been laid out.
449
layoutNextRingSystem();
450
} while (!atomPlacer.allPlaced(molecule) && safetyCounter <= molecule.getAtomCount());
453
new OverlapResolver().resolveOverlap(molecule, sssr);
458
* The main method of this StructurDiagramGenerator. Assign a molecule to the
459
* StructurDiagramGenerator, call the generateCoordinates() method and get
460
* your molecule back.
462
* @throws java.lang.Exception if an error occurs
464
public void generateCoordinates() throws java.lang.Exception
466
generateCoordinates(new Vector2d(0, 1));
471
* Does a layout of all the rings in a given connected RingSet. Uses a TemplateHandler
472
* to treat templated mapped substructures differently if <code>useTemplates</code> is
475
* @param firstBondVector A vector giving the placement for the first bond
476
* @param rs The connected RingSet for which the layout is to be
478
* @throws java.lang.Exception if an error occurs
480
private void layoutRingSet(Vector2d firstBondVector, IRingSet rs) throws Exception
482
IAtomContainer sharedAtoms;
483
Vector2d ringCenterVector;
485
logger.debug("Start of layoutRingSet");
488
* First we check if we can map any templates with predifined coordinates.
489
* All mapped substructures are saved in: this.mappedSubstructures
491
if (useTemplates && mappedSubstructures.getAtomContainerCount() > 0 && System.getProperty("java.version").indexOf("1.3.") == -1) {
493
* Find mapped substructures
495
for (Iterator substructureIterator = mappedSubstructures.atomContainers(); substructureIterator.hasNext(); ) {
496
IAtomContainer substructure = (IAtomContainer) substructureIterator.next();
497
boolean substructureMapped = false;
498
for (Iterator ringSetIterator = rs.atomContainers(); ringSetIterator.hasNext() && !substructureMapped; ) {
499
IRing ring = (IRing) ringSetIterator.next();
500
for (Iterator atomIterator = ring.atoms(); atomIterator.hasNext() && !substructureMapped; ) {
501
IAtom atom = (IAtom) atomIterator.next();
502
if (substructure.contains(atom))
503
substructureMapped = true;
507
* Layout a found mapped substructure
509
if (substructureMapped) {
510
boolean mapped = getTemplateHandler().mapTemplateExact(substructure);
512
logger.warn("A supposedly matched substructure failed to match.");
514
// Mark substructure atoms as CDKConstants.ISPLACED
515
for (Iterator iterator = substructure.atoms(); iterator.hasNext(); ) {
516
IAtom atom = (IAtom) iterator.next();
517
atom.setFlag(CDKConstants.ISPLACED, true);
519
// Mark rings of substrucure as CDKConstants.ISPLACED
520
ringPlacer.checkAndMarkPlaced(rs);
527
* Now layout the rest of this ring system
531
* Get the most complex ring in this RingSet
533
IRing ring = RingSetManipulator.getMostComplexRing(rs);
537
* Place the most complex ring at the origin of the coordinate system
539
if (!ring.getFlag(CDKConstants.ISPLACED))
541
sharedAtoms = placeFirstBond((IBond) ring.getBond(i), firstBondVector);
543
* Call the method which lays out the new ring.
545
ringCenterVector = ringPlacer.getRingCenterOfFirstRing(ring, firstBondVector, bondLength);
546
ringPlacer.placeRing(ring, sharedAtoms, GeometryToolsInternalCoordinates.get2DCenter(sharedAtoms), ringCenterVector, bondLength);
548
* Mark the ring as placed
550
ring.setFlag(CDKConstants.ISPLACED, true);
553
* Place all other rings in this ringsystem.
558
if (ring.getFlag(CDKConstants.ISPLACED))
560
ringPlacer.placeConnectedRings(rs, ring, RingPlacer.FUSED, bondLength);
561
ringPlacer.placeConnectedRings(rs, ring, RingPlacer.BRIDGED, bondLength);
562
ringPlacer.placeConnectedRings(rs, ring, RingPlacer.SPIRO, bondLength);
565
if (thisRing == rs.getAtomContainerCount())
569
ring = (IRing) rs.getAtomContainer(thisRing);
570
} while (!allPlaced(rs));
571
logger.debug("End of layoutRingSet");
577
* Does a layout of all aliphatic parts connected to the parts of the molecule
578
* that have already been laid out. Starts at the first bond with unplaced
579
* neighbours and stops when a ring is encountered.
581
* @throws org.openscience.cdk.exception.CDKException if an error occurs
583
private void handleAliphatics() throws org.openscience.cdk.exception.CDKException
585
logger.debug("Start of handleAliphatics");
587
int safetyCounter = 0;
588
IAtomContainer unplacedAtoms = null;
589
IAtomContainer placedAtoms = null;
590
IAtomContainer longestUnplacedChain = null;
593
Vector2d direction = null;
594
Vector2d startVector = null;
600
atom = getNextAtomWithAliphaticUnplacedNeigbors();
603
unplacedAtoms = getUnplacedAtoms(atom);
604
placedAtoms = getPlacedAtoms(atom);
606
longestUnplacedChain = atomPlacer.getLongestUnplacedChain(molecule, atom);
608
logger.debug("---start of longest unplaced chain---");
611
logger.debug("Start at atom no. " + (molecule.getAtomNumber(atom) + 1));
612
logger.debug(atomPlacer.listNumbers(molecule, longestUnplacedChain));
613
} catch (Exception exc) {
616
logger.debug("---end of longest unplaced chain---");
618
if (longestUnplacedChain.getAtomCount() > 1)
621
if (placedAtoms.getAtomCount() > 1)
623
logger.debug("More than one atoms placed already");
624
logger.debug("trying to place neighbors of atom " + (molecule.getAtomNumber(atom) + 1));
625
atomPlacer.distributePartners(atom, placedAtoms, GeometryToolsInternalCoordinates.get2DCenter(placedAtoms), unplacedAtoms, bondLength);
626
direction = new Vector2d(longestUnplacedChain.getAtom(1).getPoint2d());
627
startVector = new Vector2d(atom.getPoint2d());
628
direction.sub(startVector);
629
logger.debug("Done placing neighbors of atom " + (molecule.getAtomNumber(atom) + 1));
632
logger.debug("Less than or equal one atoms placed already");
633
logger.debug("Trying to get next bond vector.");
634
direction = atomPlacer.getNextBondVector(atom, placedAtoms.getAtom(0), GeometryToolsInternalCoordinates.get2DCenter(molecule),true);
638
for (int f = 1; f < longestUnplacedChain.getAtomCount(); f++)
640
longestUnplacedChain.getAtom(f).setFlag(CDKConstants.ISPLACED, false);
642
atomPlacer.placeLinearChain(longestUnplacedChain, direction, bondLength);
652
} while (!done && safetyCounter <= molecule.getAtomCount());
654
logger.debug("End of handleAliphatics");
659
* Does the layout for the next RingSystem that is connected to those parts of
660
* the molecule that have already been laid out. Finds the next ring with an
661
* unplaced ring atom and lays out this ring. Then lays out the ring substituents
662
* of this ring. Then moves and rotates the laid out ring to match the position
663
* of its attachment bond to the rest of the molecule.
665
* @throws java.lang.Exception if an error occurs
667
private void layoutNextRingSystem() throws Exception
669
logger.debug("Start of layoutNextRingSystem()");
671
resetUnplacedRings();
672
IAtomContainer tempAc = atomPlacer.getPlacedAtoms(molecule);
673
logger.debug("Finding attachment bond to already placed part...");
674
IBond nextRingAttachmentBond = getNextBondWithUnplacedRingAtom();
675
if (nextRingAttachmentBond != null)
677
logger.debug("...bond found.");
680
* Get the chain and the ring atom that are connected to where we are comming from.
681
* Both are connected by nextRingAttachmentBond.
683
IAtom ringAttachmentAtom = getRingAtom(nextRingAttachmentBond);
684
IAtom chainAttachmentAtom = getOtherBondAtom(ringAttachmentAtom, nextRingAttachmentBond);
687
* Get ring system which ringAttachmentAtom is part of
689
IRingSet nextRingSystem = getRingSystemOfAtom(ringSystems, ringAttachmentAtom);
692
* Get all rings of nextRingSytem as one IAtomContainer
694
IAtomContainer ringSystem = tempAc.getBuilder().newAtomContainer();
695
for (Iterator containers = RingSetManipulator.getAllAtomContainers(nextRingSystem).iterator(); containers.hasNext(); )
696
ringSystem.add((IAtomContainer) containers.next());
699
* Save coordinates of ringAttachmentAtom and chainAttachmentAtom
701
Point2d oldRingAttachmentAtomPoint = ringAttachmentAtom.getPoint2d();
702
Point2d oldChainAttachmentAtomPoint = chainAttachmentAtom.getPoint2d();
705
* Do the layout of the next ring system
707
layoutRingSet(firstBondVector, nextRingSystem);
710
* Place all the substituents of next ring system
712
atomPlacer.markNotPlaced(tempAc);
713
IAtomContainer placedRingSubstituents = ringPlacer.placeRingSubstituents(nextRingSystem, bondLength);
714
ringSystem.add(placedRingSubstituents);
715
atomPlacer.markPlaced(tempAc);
718
* Move and rotate the laid out ring system to match the geometry of the
721
logger.debug("Computing translation/rotation of new ringset to fit old attachment bond orientation...");
723
// old placed ring atom coordinate
724
Point2d oldPoint2 = oldRingAttachmentAtomPoint;
725
// old placed substituent atom coordinate
726
Point2d oldPoint1 = oldChainAttachmentAtomPoint;
728
// new placed ring atom coordinate
729
Point2d newPoint2 = ringAttachmentAtom.getPoint2d();
730
// new placed substituent atom coordinate
731
Point2d newPoint1 = chainAttachmentAtom.getPoint2d();
733
logger.debug("oldPoint1: " + oldPoint1);
734
logger.debug("oldPoint2: " + oldPoint2);
735
logger.debug("newPoint1: " + newPoint1);
736
logger.debug("newPoint2: " + newPoint2);
738
double oldAngle = GeometryToolsInternalCoordinates.getAngle(oldPoint2.x - oldPoint1.x, oldPoint2.y - oldPoint1.y);
739
double newAngle = GeometryToolsInternalCoordinates.getAngle(newPoint2.x - newPoint1.x, newPoint2.y - newPoint1.y);
740
double angleDiff = oldAngle - newAngle;
742
logger.debug("oldAngle: " + oldAngle + ", newAngle: " + newAngle + "; diff = " + angleDiff);
744
Vector2d translationVector = new Vector2d(oldPoint1);
745
translationVector.sub(new Vector2d(newPoint1));
748
* Move to fit old attachment bond orientation
750
GeometryToolsInternalCoordinates.translate2D(ringSystem, translationVector);
753
* Rotate to fit old attachment bond orientation
755
GeometryToolsInternalCoordinates.rotate(ringSystem, oldPoint1, angleDiff);
757
logger.debug("...done translating/rotating new ringset to fit old attachment bond orientation.");
760
logger.debug("...no bond found");
762
logger.debug("End of layoutNextRingSystem()");
767
* Returns an AtomContainer with all unplaced atoms connected to a given
770
* @param atom The Atom whose unplaced bonding partners are to be returned
771
* @return an AtomContainer with all unplaced atoms connected to a
774
private IAtomContainer getUnplacedAtoms(IAtom atom)
776
IAtomContainer unplacedAtoms = atom.getBuilder().newAtomContainer();
777
java.util.List bonds = molecule.getConnectedBondsList(atom);
779
for (int f = 0; f < bonds.size(); f++)
781
connectedAtom = ((IBond)bonds.get(f)).getConnectedAtom(atom);
782
if (!connectedAtom.getFlag(CDKConstants.ISPLACED))
784
unplacedAtoms.addAtom(connectedAtom);
787
return unplacedAtoms;
792
* Returns an AtomContainer with all placed atoms connected to a given
795
* @param atom The Atom whose placed bonding partners are to be returned
796
* @return an AtomContainer with all placed atoms connected to a given
799
private IAtomContainer getPlacedAtoms(IAtom atom)
801
IAtomContainer placedAtoms = atom.getBuilder().newAtomContainer();
802
java.util.List bonds = molecule.getConnectedBondsList(atom);
804
for (int f = 0; f < bonds.size(); f++)
806
connectedAtom = ((IBond)bonds.get(f)).getConnectedAtom(atom);
807
if (connectedAtom.getFlag(CDKConstants.ISPLACED))
809
placedAtoms.addAtom(connectedAtom);
817
* Returns the next atom with unplaced aliphatic neighbors
819
* @return the next atom with unplaced aliphatic neighbors
821
private IAtom getNextAtomWithAliphaticUnplacedNeigbors()
824
for (int f = 0; f < molecule.getBondCount(); f++)
826
bond = molecule.getBond(f);
828
if (bond.getAtom(1).getFlag(CDKConstants.ISPLACED) &&
829
!bond.getAtom(0).getFlag(CDKConstants.ISPLACED))
831
return bond.getAtom(1);
834
if (bond.getAtom(0).getFlag(CDKConstants.ISPLACED) &&
835
!bond.getAtom(1).getFlag(CDKConstants.ISPLACED))
837
return bond.getAtom(0);
845
* Returns the next bond with an unplaced ring atom
847
* @return the next bond with an unplaced ring atom
849
private IBond getNextBondWithUnplacedRingAtom() {
850
Iterator bonds = molecule.bonds();
851
while (bonds.hasNext()) {
852
IBond bond = (IBond) bonds.next();
854
if (bond.getAtom(0).getPoint2d() != null &&
855
bond.getAtom(1).getPoint2d() != null) {
856
if (bond.getAtom(1).getFlag(CDKConstants.ISPLACED) &&
857
!bond.getAtom(0).getFlag(CDKConstants.ISPLACED) &&
858
bond.getAtom(0).getFlag(CDKConstants.ISINRING)) {
862
if (bond.getAtom(0).getFlag(CDKConstants.ISPLACED) &&
863
!bond.getAtom(1).getFlag(CDKConstants.ISPLACED) &&
864
bond.getAtom(1).getFlag(CDKConstants.ISINRING)) {
874
* Places the first bond of the first ring such that one atom is at (0,0) and
875
* the other one at the position given by bondVector
877
* @param bondVector A 2D vector to point to the position of the second bond
879
* @param bond the bond to lay out
880
* @return an IAtomContainer with the atoms of the bond and the bond itself
882
private IAtomContainer placeFirstBond(IBond bond, Vector2d bondVector)
884
IAtomContainer sharedAtoms = null;
887
bondVector.normalize();
888
logger.debug("placeFirstBondOfFirstRing->bondVector.length():" + bondVector.length());
889
bondVector.scale(bondLength);
890
logger.debug("placeFirstBondOfFirstRing->bondVector.length() after scaling:" + bondVector.length());
892
Point2d point = new Point2d(0, 0);
893
atom = bond.getAtom(0);
894
logger.debug("Atom 1 of first Bond: " + (molecule.getAtomNumber(atom) + 1));
895
atom.setPoint2d(point);
896
atom.setFlag(CDKConstants.ISPLACED, true);
897
point = new Point2d(0, 0);
898
atom = bond.getAtom(1);
899
logger.debug("Atom 2 of first Bond: " + (molecule.getAtomNumber(atom) + 1));
900
point.add(bondVector);
901
atom.setPoint2d(point);
902
atom.setFlag(CDKConstants.ISPLACED, true);
904
* The new ring is layed out relativ to some shared atoms that have already been
905
* placed. Usually this is another ring, that has already been draw and to which the new
906
* ring is somehow connected, or some other system of atoms in an aliphatic chain.
907
* In this case, it's the first bond that we layout by hand.
909
sharedAtoms = atom.getBuilder().newAtomContainer();
910
sharedAtoms.addBond(bond);
911
sharedAtoms.addAtom(bond.getAtom(0));
912
sharedAtoms.addAtom(bond.getAtom(1));
913
} catch (Exception exc) {
921
* This method will go as soon as the rest works. It just assignes Point2d's
922
* of position (0,0) so that the molecule can be drawn.
924
private void fixRest()
927
for (int f = 0; f < molecule.getAtomCount(); f++)
929
atom = molecule.getAtom(f);
930
if (atom.getPoint2d() == null)
932
atom.setPoint2d(new Point2d(0, 0));
938
* This method will go as soon as the rest works. It just assignes Point2d's
939
* of position (0,0) so that the molecule can be drawn.
940
* @param molecule the molecule to fix
941
* @return the fixed molecule
943
private IMolecule fixMol(IMolecule molecule)
946
for (int f = 0; f < molecule.getAtomCount(); f++)
948
atom = molecule.getAtom(f);
949
if (atom.getPoint2d() == null)
951
atom.setPoint2d(new Point2d(0, 0));
958
* Initializes all rings in RingSet rs as not placed
960
* @param rs The RingSet to be initialized
962
// private void markNotPlaced(IRingSet rs)
964
// for (int f = 0; f < rs.size(); f++)
966
// ((IRing) rs.get(f)).setFlag(CDKConstants.ISPLACED, false);
972
* Are all rings in the Vector placed?
974
* @param rings The Vector to be checked
975
* @return true if all rings are placed, false otherwise
977
private boolean allPlaced(IRingSet rings)
979
for (int f = 0; f < rings.getAtomContainerCount(); f++)
981
if (!((IRing) rings.getAtomContainer(f)).getFlag(CDKConstants.ISPLACED))
983
logger.debug("allPlaced->Ring " + f + " not placed");
992
* Mark all atoms in the molecule as being part of a ring
994
* @param rings an IRingSet with the rings to process
996
private void markRingAtoms(IRingSet rings)
999
for (int i = 0; i < rings.getAtomContainerCount(); i++)
1001
ring = (IRing) rings.getAtomContainer(i);
1002
for (int j = 0; j < ring.getAtomCount(); j++)
1004
ring.getAtom(j).setFlag(CDKConstants.ISINRING, true);
1011
* Get the unplaced ring atom in this bond
1013
* @param bond the bond to be search for the unplaced ring atom
1014
* @return the unplaced ring atom in this bond
1016
private IAtom getRingAtom(IBond bond)
1018
if (bond.getAtom(0).getFlag(CDKConstants.ISINRING) &&
1019
!bond.getAtom(0).getFlag(CDKConstants.ISPLACED))
1021
return bond.getAtom(0);
1023
if (bond.getAtom(1).getFlag(CDKConstants.ISINRING) &&
1024
!bond.getAtom(1).getFlag(CDKConstants.ISPLACED))
1026
return bond.getAtom(1);
1033
* Get the ring system of which the given atom is part of
1035
* @param ringSystems a List of ring systems to be searched
1036
* @param ringAtom the ring atom to be search in the ring system.
1037
* @return the ring system the given atom is part of
1039
private IRingSet getRingSystemOfAtom(List ringSystems, IAtom ringAtom)
1041
IRingSet ringSet = null;
1042
for (int f = 0; f < ringSystems.size(); f++)
1044
ringSet = (IRingSet) ringSystems.get(f);
1045
if (ringSet.contains(ringAtom))
1055
* Set all the atoms in unplaced rings to be unplaced
1057
private void resetUnplacedRings()
1064
int unplacedCounter = 0;
1065
for (int f = 0; f < sssr.getAtomContainerCount(); f++)
1067
ring = (IRing) sssr.getAtomContainer(f);
1068
if (!ring.getFlag(CDKConstants.ISPLACED))
1070
logger.debug("Ring with " + ring.getAtomCount() + " atoms is not placed.");
1072
for (int g = 0; g < ring.getAtomCount(); g++)
1074
ring.getAtom(g).setFlag(CDKConstants.ISPLACED, false);
1078
logger.debug("There are " + unplacedCounter + " unplaced Rings.");
1083
* Set the bond length used for laying out the molecule.
1084
* The defaut value is 1.5.
1086
* @param bondLength The new bondLength value
1088
public void setBondLength(double bondLength)
1090
this.bondLength = bondLength;
1094
* Returns the other atom of the bond.
1095
* Expects bond to have only two atoms.
1096
* Returns null if the given atom is not part of the given bond.
1098
* @param atom the atom we already have
1099
* @param bond the bond
1100
* @return the other atom of the bond
1102
public IAtom getOtherBondAtom(IAtom atom, IBond bond) {
1103
if (!bond.contains(atom))
1105
if (bond.getAtom(0).equals(atom))
1106
return bond.getAtom(1);
1108
return bond.getAtom(0);