All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openscience.cdk.layout.AtomPlacer Maven / Gradle / Ivy

/* Copyright (C) 2003-2007  The Chemistry Development Kit (CDK) project
 *
 *  Contact: [email protected]
 *
 *  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 com.google.common.collect.FluentIterable;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.geometry.BondTools;
import org.openscience.cdk.geometry.GeometryUtil;
import org.openscience.cdk.graph.GraphUtil;
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.IRing;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator;

import javax.vecmath.Point2d;
import javax.vecmath.Tuple2d;
import javax.vecmath.Vector2d;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

/**
 *  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 {

    private static final double ANGLE_120 = Math.toRadians(120);
    public final static boolean debug = true;
    public static final String PRIORITY = "Weight";
    private static ILoggingTool logger = LoggingToolFactory.createLoggingTool(AtomPlacer.class);

    /**
     *  The molecule to be laid out. To be assigned from outside
     */
    IAtomContainer molecule = null;

    /**
     *  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);
        // if the placing on the centre atom we get NaNs just give a arbitary direciton the
        // rest works it's self out
        if (Math.abs(occupiedDirection.length()) < 0.001)
            occupiedDirection = new Vector2d(0, 1);
        logger.debug("distributePartners->occupiedDirection.lenght(): " + occupiedDirection.length());
        List atomsToDraw = new ArrayList();

        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.add(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 = GeometryUtil.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.add(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 (doAngleSnap(atom, placedNeighbours)) {

            int numTerminal = 0;
            for (IAtom unplaced : unplacedNeighbours.atoms())
                if (molecule.getConnectedBondsCount(unplaced) == 1)
                    numTerminal++;

            if (numTerminal == unplacedNeighbours.getAtomCount()) {
                final Vector2d a = newVector(placedNeighbours.getAtom(0).getPoint2d(), atom.getPoint2d());
                final Vector2d b = newVector(placedNeighbours.getAtom(1).getPoint2d(), atom.getPoint2d());
                final double d1 = GeometryUtil.getAngle(a.x, a.y);
                final double d2 = GeometryUtil.getAngle(b.x, b.y);
                double sweep = a.angle(b);
                if (sweep < Math.PI) {
                    sweep = 2 * Math.PI - sweep;
                }
                startAngle = d2;
                if (d1 > d2 && d1 - d2 < Math.PI || d2 - d1 >= Math.PI) {
                    startAngle = d1;
                }
                sweep /= (1 + unplacedNeighbours.getAtomCount());
                populatePolygonCorners(FluentIterable.from(unplacedNeighbours.atoms()).toList(),
                                       atom.getPoint2d(), startAngle, sweep, bondLength);

                markPlaced(unplacedNeighbours);
                return;
            } else {
                atom.removeProperty(MacroCycleLayout.MACROCYCLE_ATOM_HINT);
            }
        }

        /*
         * 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);
        GeometryUtil.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 = GeometryUtil.getAngle(sortedAtoms[0].getPoint2d().x - atom.getPoint2d().x,
                sortedAtoms[0].getPoint2d().y - atom.getPoint2d().y);
        double angle2 = GeometryUtil.getAngle(sortedAtoms[1].getPoint2d().x - atom.getPoint2d().x,
                sortedAtoms[1].getPoint2d().y - atom.getPoint2d().y);
        double angle3 = GeometryUtil.getAngle(distanceMeasure.x - atom.getPoint2d().x,
                distanceMeasure.y - atom.getPoint2d().y);
        if (debug) {
            try {
                logger.debug("distributePartners->sortedAtoms[0]: ", (molecule.indexOf(sortedAtoms[0]) + 1));
                logger.debug("distributePartners->sortedAtoms[1]: ", (molecule.indexOf(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.indexOf(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.add(unplacedNeighbours.getAtom(f));
        }
        radius = bondLength;
        startAngle = GeometryUtil.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);
    }

    private boolean doAngleSnap(IAtom atom, IAtomContainer placedNeighbours) {
        if (placedNeighbours.getAtomCount() != 2)
            return false;
        IBond b1 = molecule.getBond(atom, placedNeighbours.getAtom(0));
        if (!b1.isInRing())
            return false;
        IBond b2 = molecule.getBond(atom, placedNeighbours.getAtom(1));
        if (!b2.isInRing())
            return false;

        Point2d p1 = atom.getPoint2d();
        Point2d p2 = placedNeighbours.getAtom(0).getPoint2d();
        Point2d p3 = placedNeighbours.getAtom(1).getPoint2d();

        Vector2d v1 = newVector(p2, p1);
        Vector2d v2 = newVector(p3, p1);

        return Math.abs(v2.angle(v1) - ANGLE_120) < 0.01;
    }

    /**
     * Places the atoms in a linear chain.
     *
     * 

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) { IAtomContainer withh = atomContainer.getBuilder().newInstance(IAtomContainer.class, 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).getImplicitHydrogenCount(); 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 // [email protected] // 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; IBond prevBond = null, currBond = null; for (int f = 0; f < atomContainer.getAtomCount() - 1; f++) { atom = atomContainer.getAtom(f); nextAtom = atomContainer.getAtom(f + 1); currBond = atomContainer.getBond(atom, nextAtom); 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 (prevBond != null && isColinear(atom, molecule.getConnectedBondsList(atom))) { int atomicNumber = atom.getAtomicNumber(); int charge = atom.getFormalCharge(); // double length of the last bond to determing next placement Point2d p = new Point2d(prevBond.getOther(atom).getPoint2d()); p.interpolate(atom.getPoint2d(), 2); nextAtom.setPoint2d(p); } if (GeometryUtil.has2DCoordinates(atomContainer)) { 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, GeometryUtil.get2DCenter(molecule), trans); } else { bondVector = getNextBondVector(nextAtom, atom, GeometryUtil.get2DCenter(molecule), true); } prevBond = currBond; } // BUGFIX part 2 - restore hydrogen counts for (int i = 0, n = atomContainer.getAtomCount(); i < n; i++) { atomContainer.getAtom(i).setImplicitHydrogenCount(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); } final Point2d a = previousAtom.getPoint2d(); final Point2d b = atom.getPoint2d(); if (isColinear(atom, molecule.getConnectedBondsList(atom))) { return new Vector2d(b.x-a.x, b.y-a.y); } double angle = GeometryUtil.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 (isColinear(atom, molecule.getConnectedBondsList(atom))) 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 * furthermost away from the rest of the molecules. The "weight" mentioned here is * calculated by a modified morgan number algorithm. * * @param atoms All the atoms to draw * @param thetaBeg A start angle (in radians), giving the angle of the most clockwise * atom which has already been placed * @param thetaStep An angle (in radians) to be added for each atom from * atomsToDraw * @param center 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(final List atoms, final Point2d center, final double thetaBeg, final double thetaStep, final double radius) { final int numAtoms = atoms.size(); double theta = thetaBeg; logger.debug("populatePolygonCorners(numAtoms=", numAtoms, ", center=", center, ", thetaBeg=", Math.toDegrees(thetaBeg), ", r=", radius); for (IAtom atom : atoms) { theta += thetaStep; double x = Math.cos(theta) * radius; double y = Math.sin(theta) * radius; double newX = x + center.x; double newY = y + center.y; atom.setPoint2d(new Point2d(newX, newY)); atom.setFlag(CDKConstants.ISPLACED, true); logger.debug("populatePolygonCorners - angle=", Math.toDegrees(theta), ", newX=", newX, ", newY=", newY); } } /** * 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) { 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 */ static public IAtomContainer getInitialLongestChain(IAtomContainer 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().newInstance(IAtomContainer.class); 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 */ static public IAtomContainer getLongestUnplacedChain(IAtomContainer 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().newInstance(IAtomContainer.class); pathes[f].addAtom(startAtom); } List startSphere = new ArrayList(); startSphere.add(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 */ static public void breadthFirstSearch(IAtomContainer ac, List sphere, IAtomContainer[] pathes) throws CDKException { IAtom atom = null; IAtom nextAtom = null; int atomNr; int nextAtomNr; //IAtomContainer path = null; List newSphere = new ArrayList(); logger.debug("Start of breadthFirstSearch"); for (int f = 0; f < sphere.size(); f++) { atom = sphere.get(f); if (!atom.getFlag(CDKConstants.ISINRING)) { atomNr = ac.indexOf(atom); logger.debug("BreadthFirstSearch around atom " + (atomNr + 1)); List bonds = ac.getConnectedBondsList(atom); for (int g = 0; g < bonds.size(); g++) { IBond curBond = (IBond) bonds.get(g); nextAtom = curBond.getOther(atom); if (!nextAtom.getFlag(CDKConstants.VISITED) && !nextAtom.getFlag(CDKConstants.ISPLACED)) { nextAtomNr = ac.indexOf(nextAtom); logger.debug("BreadthFirstSearch is meeting new atom " + (nextAtomNr + 1)); pathes[nextAtomNr] = ac.getBuilder().newInstance(IAtomContainer.class, pathes[atomNr]); logger.debug("Making copy of path " + (atomNr + 1) + " to form new path " + (nextAtomNr + 1)); pathes[nextAtomNr].addAtom(nextAtom); logger.debug("Adding atom " + (nextAtomNr + 1) + " to path " + (nextAtomNr + 1)); pathes[nextAtomNr].addBond(curBond); if (ac.getConnectedBondsCount(nextAtom) > 1) { newSphere.add(nextAtom); } } } } } if (newSphere.size() > 0) { for (int f = 0; f < newSphere.size(); f++) { newSphere.get(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 */ static public String listNumbers(IAtomContainer mol, IAtomContainer ac) throws CDKException { String s = "Numbers: "; for (int f = 0; f < ac.getAtomCount(); f++) { s += (mol.indexOf(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 */ static public String listNumbers(IAtomContainer mol, List ac) { String s = "Numbers: "; for (int f = 0; f < ac.size(); f++) { s += (mol.indexOf((IAtom) ac.get(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 */ static 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 */ static 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 */ static 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 */ static public IAtomContainer getPlacedAtoms(IAtomContainer ac) { IAtomContainer ret = ac.getBuilder().newInstance(IAtomContainer.class); for (int f = 0; f < ac.getAtomCount(); f++) { if (ac.getAtom(f).getFlag(CDKConstants.ISPLACED)) { ret.addAtom(ac.getAtom(f)); } } return ret; } /** * Copy placed atoms/bonds from one container to another. * * @param dest destination container * @param src source container */ static void copyPlaced(IRing dest, IAtomContainer src) { for (IBond bond : src.bonds()) { IAtom beg = bond.getBegin(); IAtom end = bond.getEnd(); if (beg.getFlag(CDKConstants.ISPLACED)) { dest.addAtom(beg); if (end.getFlag(CDKConstants.ISPLACED)) { dest.addAtom(end); dest.addBond(bond); } } else if (end.getFlag(CDKConstants.ISPLACED)) { dest.addAtom(end); } } } /** * 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 sum of degrees */ static 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)); Integer implH = ac.getAtom(f).getImplicitHydrogenCount(); if (implH != null) degreeSum += implH; } //System.out.println(path + ": " + degreeSum); return degreeSum; } /** * Calculates priority for atoms in a molecule. * * @param mol connected molecule * @see #PRIORITY */ static void prioritise(IAtomContainer mol) { prioritise(mol, GraphUtil.toAdjList(mol)); } /** * Calculates priority for atoms in a molecule. * * @param mol connected molecule * @param adjList fast adjacency lookup * @see #PRIORITY */ static void prioritise(IAtomContainer mol, int[][] adjList) { int[] weights = getPriority(mol, adjList); for (int i = 0; i < mol.getAtomCount(); i++) { mol.getAtom(i).setProperty(PRIORITY, weights[i]); } } /** * Prioritise atoms of a molecule base on how 'buried' they are. The priority * is cacheted with a morgan-like relaxation O(n^2 lg n). Priorities are assign * from 1..|V| (usually less than |V| due to symmetry) where the lowest numbers * have priority. * * @param mol molecule * @param adjList fast adjacency lookup * @return the priority */ static int[] getPriority(IAtomContainer mol, int[][] adjList) { final int n = mol.getAtomCount(); final Integer[] order = new Integer[n]; final int[] rank = new int[n]; final int[] prev = new int[n]; // init priorities, Helson 99 favours cyclic (init=2) for (int f = 0; f < n; f++) { rank[f] = 1; prev[f] = 1; order[f] = f; } final Comparator comparator = new Comparator() { @Override public int compare(Integer a, Integer b) { // highest (most buried) first return Integer.compare(rank[a], rank[b]); } }; int nDistinct = 1; for (int rep = 0; rep < n; rep++) { for (int i = 0; i < n; i++) { rank[i] = 3 * prev[i]; for (int w : adjList[i]) rank[i] += prev[w]; } // assign new ranks Arrays.sort(order, comparator); int clsNum = 1; prev[order[0]] = clsNum; for (int i = 1; i < n; i++) { if (rank[order[i]] != rank[order[i-1]]) clsNum++; prev[order[i]] = clsNum; } // no refinement over previous if (clsNum == nDistinct) break; nDistinct = clsNum; } // we want values 1 ≤ x < |V| for (int i = 0; i < n; i++) prev[i] = 1 + nDistinct - prev[i]; return prev; } /** * -C#N * -[N+]#[C-] * -C=[N+]=N * -N=[N+]=N */ boolean isColinear(IAtom atom, List bonds) { if (bonds.size() != 2) return false; int numSgl = atom.getImplicitHydrogenCount() == null ? 0 : atom.getImplicitHydrogenCount(); int numDbl = 0; int numTpl = 0; for (IBond bond : bonds) { switch (bond.getOrder()) { case SINGLE: numSgl++; break; case DOUBLE: numDbl++; break; case TRIPLE: numTpl++; break; case QUADRUPLE: return true; default: return false; } } switch (atom.getAtomicNumber()) { case 6: case 7: case 14: case 32: if (numTpl == 1 && numSgl == 1) return true; if (numDbl == 2 && numSgl == 0) return true; break; } return false; } @Deprecated static public boolean shouldBeLinear(IAtom atom, IAtomContainer molecule) { int sum = 0; 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; } static Vector2d newVector(Tuple2d to, Tuple2d from) { return new Vector2d(to.x-from.x, to.y-from.y); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy