org.openscience.cdk.geometry.GeometryUtil Maven / Gradle / Ivy
/* Copyright (C) 1997-2014 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.geometry;
import org.openscience.cdk.CDKConstants;
import org.openscience.cdk.config.Isotopes;
import org.openscience.cdk.exception.CDKException;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IChemModel;
import org.openscience.cdk.interfaces.IReaction;
import org.openscience.cdk.interfaces.IRingSet;
import org.openscience.cdk.sgroup.Sgroup;
import org.openscience.cdk.sgroup.SgroupBracket;
import org.openscience.cdk.sgroup.SgroupKey;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;
import org.openscience.cdk.tools.manipulator.ChemModelManipulator;
import org.openscience.cdk.tools.manipulator.ReactionManipulator;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector2d;
import javax.vecmath.Vector3d;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* A set of static utility classes for geometric calculations and operations. This class is
* extensively used, for example, by JChemPaint to edit molecule. All methods in this class change
* the coordinates of the atoms. Use GeometryTools if you use an external set of coordinates (e. g.
* renderingCoordinates from RendererModel)
*
* @author seb
* @author Stefan Kuhn
* @author Egon Willighagen
* @author Ludovic Petain
* @author Christian Hoppe
* @author Niels Out
* @author John May
* @cdk.githash
*/
public final class GeometryUtil {
private static ILoggingTool logger = LoggingToolFactory.createLoggingTool(GeometryUtil.class);
/**
* Provides the coverage of coordinates for this molecule.
*
* @see GeometryUtil#get2DCoordinateCoverage(org.openscience.cdk.interfaces.IAtomContainer)
* @see GeometryUtil#get3DCoordinateCoverage(org.openscience.cdk.interfaces.IAtomContainer)
*/
public static enum CoordinateCoverage {
/**
* All atoms have coordinates.
*/
FULL,
/**
* At least one atom has coordinates but not all.
*/
PARTIAL,
/**
* No atoms have coordinates.
*/
NONE
}
/**
* Static utility class can not be instantiated.
*/
private GeometryUtil() {}
/**
* Adds an automatically calculated offset to the coordinates of all atoms such that all
* coordinates are positive and the smallest x or y coordinate is exactly zero. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param atomCon AtomContainer for which all the atoms are translated to positive coordinates
*/
public static void translateAllPositive(IAtomContainer atomCon) {
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
for (IAtom atom : atomCon.atoms()) {
if (atom.getPoint2d() != null) {
if (atom.getPoint2d().x < minX) {
minX = atom.getPoint2d().x;
}
if (atom.getPoint2d().y < minY) {
minY = atom.getPoint2d().y;
}
}
}
logger.debug("Translating: minx=" + minX + ", minY=" + minY);
translate2D(atomCon, minX * -1, minY * -1);
}
/**
* Translates the given molecule by the given Vector. See comment for center(IAtomContainer
* atomCon, Dimension areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param atomCon The molecule to be translated
* @param transX translation in x direction
* @param transY translation in y direction
*/
public static void translate2D(IAtomContainer atomCon, double transX, double transY) {
translate2D(atomCon, new Vector2d(transX, transY));
}
/**
* Scales a molecule such that it fills a given percentage of a given dimension. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param atomCon The molecule to be scaled {width, height}
* @param areaDim The dimension to be filled {width, height}
* @param fillFactor The percentage of the dimension to be filled
*/
public static void scaleMolecule(IAtomContainer atomCon, double[] areaDim, double fillFactor) {
double[] molDim = get2DDimension(atomCon);
double widthFactor = (double) areaDim[0] / (double) molDim[0];
double heightFactor = (double) areaDim[1] / (double) molDim[1];
double scaleFactor = Math.min(widthFactor, heightFactor) * fillFactor;
scaleMolecule(atomCon, scaleFactor);
}
/**
* Multiplies all the coordinates of the atoms of the given molecule with the scalefactor. See
* comment for center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates)
* for details on coordinate sets
*
* @param atomCon The molecule to be scaled
* @param scaleFactor Description of the Parameter
*/
public static void scaleMolecule(IAtomContainer atomCon, double scaleFactor) {
for (int i = 0; i < atomCon.getAtomCount(); i++) {
if (atomCon.getAtom(i).getPoint2d() != null) {
atomCon.getAtom(i).getPoint2d().scale(scaleFactor);
}
}
// scale Sgroup brackets
if (atomCon.getProperty(CDKConstants.CTAB_SGROUPS) != null) {
List sgroups = atomCon.getProperty(CDKConstants.CTAB_SGROUPS);
for (Sgroup sgroup : sgroups) {
List brackets = sgroup.getValue(SgroupKey.CtabBracket);
if (brackets != null) {
for (SgroupBracket bracket : brackets) {
bracket.getFirstPoint().scale(scaleFactor);
bracket.getSecondPoint().scale(scaleFactor);
}
}
}
}
}
/**
* Centers the molecule in the given area. See comment for center(IAtomContainer atomCon,
* Dimension areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param atomCon molecule to be centered
* @param areaDim dimension in which the molecule is to be centered, array containing
* {width, height}
*/
public static void center(IAtomContainer atomCon, double[] areaDim) {
double[] molDim = get2DDimension(atomCon);
double transX = (areaDim[0] - molDim[0]) / 2;
double transY = (areaDim[1] - molDim[1]) / 2;
translateAllPositive(atomCon);
translate2D(atomCon, new Vector2d(transX, transY));
}
/**
* Translates a molecule from the origin to a new point denoted by a vector. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param atomCon molecule to be translated
* @param vector dimension that represents the translation vector
*/
public static void translate2D(IAtomContainer atomCon, Vector2d vector) {
for (IAtom atom : atomCon.atoms()) {
if (atom.getPoint2d() != null) {
atom.getPoint2d().add(vector);
} else {
logger.warn("Could not translate atom in 2D space");
}
}
// translate Sgroup brackets
if (atomCon.getProperty(CDKConstants.CTAB_SGROUPS) != null) {
List sgroups = atomCon.getProperty(CDKConstants.CTAB_SGROUPS);
for (Sgroup sgroup : sgroups) {
List brackets = sgroup.getValue(SgroupKey.CtabBracket);
if (brackets != null) {
for (SgroupBracket bracket : brackets) {
bracket.getFirstPoint().add(vector);
bracket.getSecondPoint().add(vector);
}
}
}
}
}
/**
* Rotates a molecule around a given center by a given angle.
*
* @param atomCon The molecule to be rotated
* @param center A point giving the rotation center
* @param angle The angle by which to rotate the molecule, in radians
*/
public static void rotate(IAtomContainer atomCon, Point2d center, double angle) {
Point2d point;
double costheta = Math.cos(angle);
double sintheta = Math.sin(angle);
IAtom atom;
for (int i = 0; i < atomCon.getAtomCount(); i++) {
atom = atomCon.getAtom(i);
point = atom.getPoint2d();
double relativex = point.x - center.x;
double relativey = point.y - center.y;
point.x = relativex * costheta - relativey * sintheta + center.x;
point.y = relativex * sintheta + relativey * costheta + center.y;
}
}
/**
* Rotates a 3D point about a specified line segment by a specified angle.
*
* The code is based on code available here.
* Positive angles are anticlockwise looking down the axis towards the origin. Assume right hand
* coordinate system.
*
* @param atom The atom to rotate
* @param p1 The first point of the line segment
* @param p2 The second point of the line segment
* @param angle The angle to rotate by (in degrees)
*/
public static void rotate(IAtom atom, Point3d p1, Point3d p2, double angle) {
double costheta, sintheta;
Point3d r = new Point3d();
r.x = p2.x - p1.x;
r.y = p2.y - p1.y;
r.z = p2.z - p1.z;
normalize(r);
angle = angle * Math.PI / 180.0;
costheta = Math.cos(angle);
sintheta = Math.sin(angle);
Point3d p = atom.getPoint3d();
p.x -= p1.x;
p.y -= p1.y;
p.z -= p1.z;
Point3d q = new Point3d(0, 0, 0);
q.x += (costheta + (1 - costheta) * r.x * r.x) * p.x;
q.x += ((1 - costheta) * r.x * r.y - r.z * sintheta) * p.y;
q.x += ((1 - costheta) * r.x * r.z + r.y * sintheta) * p.z;
q.y += ((1 - costheta) * r.x * r.y + r.z * sintheta) * p.x;
q.y += (costheta + (1 - costheta) * r.y * r.y) * p.y;
q.y += ((1 - costheta) * r.y * r.z - r.x * sintheta) * p.z;
q.z += ((1 - costheta) * r.x * r.z - r.y * sintheta) * p.x;
q.z += ((1 - costheta) * r.y * r.z + r.x * sintheta) * p.y;
q.z += (costheta + (1 - costheta) * r.z * r.z) * p.z;
q.x += p1.x;
q.y += p1.y;
q.z += p1.z;
atom.setPoint3d(q);
}
/**
* Normalizes a point.
*
* @param point The point to normalize
*/
public static void normalize(Point3d point) {
double sum = Math.sqrt(point.x * point.x + point.y * point.y + point.z * point.z);
point.x = point.x / sum;
point.y = point.y / sum;
point.z = point.z / sum;
}
/**
* Returns the dimension of a molecule (width/height).
*
* @param atomCon of which the dimension should be returned
* @return array containing {width, height}
*/
public static double[] get2DDimension(IAtomContainer atomCon) {
double[] minmax = getMinMax(atomCon);
double maxX = minmax[2];
double maxY = minmax[3];
double minX = minmax[0];
double minY = minmax[1];
return new double[]{maxX - minX, maxY - minY};
}
/**
* Returns the minimum and maximum X and Y coordinates of the atoms in the
* AtomContainer. The output is returned as:
* minmax[0] = minX;
* minmax[1] = minY;
* minmax[2] = maxX;
* minmax[3] = maxY;
*
* See comment for center(IAtomContainer atomCon, Dimension areaDim, HashMap
* renderingCoordinates) for details on coordinate sets
*
* @param container Description of the Parameter
* @return An four int array as defined above.
*/
public static double[] getMinMax(IAtomContainer container) {
double maxX = -Double.MAX_VALUE;
double maxY = -Double.MAX_VALUE;
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
for (int i = 0; i < container.getAtomCount(); i++) {
IAtom atom = container.getAtom(i);
if (atom.getPoint2d() != null) {
if (atom.getPoint2d().x > maxX) {
maxX = atom.getPoint2d().x;
}
if (atom.getPoint2d().x < minX) {
minX = atom.getPoint2d().x;
}
if (atom.getPoint2d().y > maxY) {
maxY = atom.getPoint2d().y;
}
if (atom.getPoint2d().y < minY) {
minY = atom.getPoint2d().y;
}
}
}
double[] minmax = new double[4];
minmax[0] = minX;
minmax[1] = minY;
minmax[2] = maxX;
minmax[3] = maxY;
return minmax;
}
/**
* Translates a molecule from the origin to a new point denoted by a vector. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param atomCon molecule to be translated
* @param p Description of the Parameter
*/
public static void translate2DCentreOfMassTo(IAtomContainer atomCon, Point2d p) {
Point2d com = get2DCentreOfMass(atomCon);
Vector2d translation = new Vector2d(p.x - com.x, p.y - com.y);
for (IAtom atom : atomCon.atoms()) {
if (atom.getPoint2d() != null) {
atom.getPoint2d().add(translation);
}
}
}
/**
* Calculates the center of the given atoms and returns it as a Point2d. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param atoms The vector of the given atoms
* @return The center of the given atoms as Point2d
*/
public static Point2d get2DCenter(Iterable atoms) {
double xsum = 0;
double ysum = 0;
int length = 0;
for (IAtom atom : atoms) {
if (atom.getPoint2d() != null) {
xsum += atom.getPoint2d().x;
ysum += atom.getPoint2d().y;
length++;
}
}
return new Point2d(xsum / (double) length, ysum / (double) length);
}
/**
* Calculates the center of the given atoms and returns it as a Point2d. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param atoms The Iterator of the given atoms
* @return The center of the given atoms as Point2d
*/
public static Point2d get2DCenter(Iterator atoms) {
IAtom atom;
double xsum = 0;
double ysum = 0;
int length = 0;
while (atoms.hasNext()) {
atom = atoms.next();
if (atom.getPoint2d() != null) {
xsum += atom.getPoint2d().x;
ysum += atom.getPoint2d().y;
}
++length;
}
return new Point2d(xsum / (double) length, ysum / (double) length);
}
/**
* Returns the geometric center of all the rings in this ringset. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param ringSet Description of the Parameter
* @return the geometric center of the rings in this ringset
*/
public static Point2d get2DCenter(IRingSet ringSet) {
double centerX = 0;
double centerY = 0;
for (int i = 0; i < ringSet.getAtomContainerCount(); i++) {
Point2d centerPoint = get2DCenter(ringSet.getAtomContainer(i));
centerX += centerPoint.x;
centerY += centerPoint.y;
}
return new Point2d(centerX / ((double) ringSet.getAtomContainerCount()), centerY
/ ((double) ringSet.getAtomContainerCount()));
}
/**
* Calculates the center of mass for the Atom
s in the AtomContainer for the 2D
* coordinates. See comment for center(IAtomContainer atomCon, Dimension areaDim, HashMap
* renderingCoordinates) for details on coordinate sets
*
* @param ac AtomContainer for which the center of mass is calculated
* @return Null, if any of the atomcontainer {@link org.openscience.cdk.interfaces.IAtom}'s
* masses are null
* @cdk.keyword center of mass
*/
public static Point2d get2DCentreOfMass(IAtomContainer ac) {
double xsum = 0.0;
double ysum = 0.0;
double totalmass = 0.0;
for (IAtom a : ac.atoms()) {
Double mass = a.getExactMass();
if (mass == null) return null;
totalmass += mass;
xsum += mass * a.getPoint2d().x;
ysum += mass * a.getPoint2d().y;
}
return new Point2d(xsum / totalmass, ysum / totalmass);
}
/**
* Returns the geometric center of all the atoms in the atomContainer. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param container Description of the Parameter
* @return the geometric center of the atoms in this atomContainer
*/
public static Point2d get2DCenter(IAtomContainer container) {
double centerX = 0;
double centerY = 0;
double counter = 0;
for (IAtom atom : container.atoms()) {
if (atom.getPoint2d() != null) {
centerX += atom.getPoint2d().x;
centerY += atom.getPoint2d().y;
counter++;
}
}
return new Point2d(centerX / (counter), centerY / (counter));
}
/**
* Translates the geometric 2DCenter of the given AtomContainer container to the specified
* Point2d p.
*
* @param container AtomContainer which should be translated.
* @param p New Location of the geometric 2D Center.
* @see #get2DCenter
* @see #translate2DCentreOfMassTo
*/
public static void translate2DCenterTo(IAtomContainer container, Point2d p) {
Point2d com = get2DCenter(container);
Vector2d translation = new Vector2d(p.x - com.x, p.y - com.y);
for (IAtom atom : container.atoms()) {
if (atom.getPoint2d() != null) {
atom.getPoint2d().add(translation);
}
}
}
/**
* Calculates the center of mass for the Atom
s in the AtomContainer.
*
* @param ac AtomContainer for which the center of mass is calculated
* @return The center of mass of the molecule, or NULL
if the molecule
* does not have 3D coordinates or if any of the atoms do not have a valid atomic mass
* @cdk.keyword center of mass
* @cdk.dictref blue-obelisk:calculate3DCenterOfMass
*/
public static Point3d get3DCentreOfMass(IAtomContainer ac) {
double xsum = 0.0;
double ysum = 0.0;
double zsum = 0.0;
double totalmass = 0.0;
Isotopes isotopes;
try {
isotopes = Isotopes.getInstance();
} catch (IOException e) {
throw new RuntimeException("Could not initialize Isotopes");
}
for (IAtom a : ac.atoms()) {
Double mass = a.getExactMass();
// some sanity checking
if (a.getPoint3d() == null) return null;
if (mass == null)
mass = isotopes.getNaturalMass(a);
totalmass += mass;
xsum += mass * a.getPoint3d().x;
ysum += mass * a.getPoint3d().y;
zsum += mass * a.getPoint3d().z;
}
return new Point3d(xsum / totalmass, ysum / totalmass, zsum / totalmass);
}
/**
* Returns the geometric center of all the atoms in this atomContainer. See comment for
* center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for details
* on coordinate sets
*
* @param ac Description of the Parameter
* @return the geometric center of the atoms in this atomContainer
*/
public static Point3d get3DCenter(IAtomContainer ac) {
double centerX = 0;
double centerY = 0;
double centerZ = 0;
double counter = 0;
for (IAtom atom : ac.atoms()) {
if (atom.getPoint3d() != null) {
centerX += atom.getPoint3d().x;
centerY += atom.getPoint3d().y;
centerZ += atom.getPoint3d().z;
counter++;
}
}
return new Point3d(centerX / (counter), centerY / (counter), centerZ / (counter));
}
/**
* Gets the angle attribute of the GeometryTools class.
*
* @param xDiff Description of the Parameter
* @param yDiff Description of the Parameter
* @return The angle value
*/
public static double getAngle(double xDiff, double yDiff) {
double angle = 0;
// logger.debug("getAngle->xDiff: " + xDiff);
// logger.debug("getAngle->yDiff: " + yDiff);
if (xDiff >= 0 && yDiff >= 0) {
angle = Math.atan(yDiff / xDiff);
} else if (xDiff < 0 && yDiff >= 0) {
angle = Math.PI + Math.atan(yDiff / xDiff);
} else if (xDiff < 0 && yDiff < 0) {
angle = Math.PI + Math.atan(yDiff / xDiff);
} else if (xDiff >= 0 && yDiff < 0) {
angle = 2 * Math.PI + Math.atan(yDiff / xDiff);
}
return angle;
}
/**
* Gets the coordinates of two points (that represent a bond) and calculates for each the
* coordinates of two new points that have the given distance vertical to the bond.
*
* @param coords The coordinates of the two given points of the bond like this [point1x,
* point1y, point2x, point2y]
* @param dist The vertical distance between the given points and those to be calculated
* @return The coordinates of the calculated four points
*/
public static int[] distanceCalculator(int[] coords, double dist) {
double angle;
if ((coords[2] - coords[0]) == 0) {
angle = Math.PI / 2;
} else {
angle = Math.atan(((double) coords[3] - (double) coords[1]) / ((double) coords[2] - (double) coords[0]));
}
int begin1X = (int) (Math.cos(angle + Math.PI / 2) * dist + coords[0]);
int begin1Y = (int) (Math.sin(angle + Math.PI / 2) * dist + coords[1]);
int begin2X = (int) (Math.cos(angle - Math.PI / 2) * dist + coords[0]);
int begin2Y = (int) (Math.sin(angle - Math.PI / 2) * dist + coords[1]);
int end1X = (int) (Math.cos(angle - Math.PI / 2) * dist + coords[2]);
int end1Y = (int) (Math.sin(angle - Math.PI / 2) * dist + coords[3]);
int end2X = (int) (Math.cos(angle + Math.PI / 2) * dist + coords[2]);
int end2Y = (int) (Math.sin(angle + Math.PI / 2) * dist + coords[3]);
return new int[]{begin1X, begin1Y, begin2X, begin2Y, end1X, end1Y, end2X, end2Y};
}
public static double[] distanceCalculator(double[] coords, double dist) {
double angle;
if ((coords[2] - coords[0]) == 0) {
angle = Math.PI / 2;
} else {
angle = Math.atan((coords[3] - coords[1]) / (coords[2] - coords[0]));
}
double begin1X = (Math.cos(angle + Math.PI / 2) * dist + coords[0]);
double begin1Y = (Math.sin(angle + Math.PI / 2) * dist + coords[1]);
double begin2X = (Math.cos(angle - Math.PI / 2) * dist + coords[0]);
double begin2Y = (Math.sin(angle - Math.PI / 2) * dist + coords[1]);
double end1X = (Math.cos(angle - Math.PI / 2) * dist + coords[2]);
double end1Y = (Math.sin(angle - Math.PI / 2) * dist + coords[3]);
double end2X = (Math.cos(angle + Math.PI / 2) * dist + coords[2]);
double end2Y = (Math.sin(angle + Math.PI / 2) * dist + coords[3]);
return new double[]{begin1X, begin1Y, begin2X, begin2Y, end1X, end1Y, end2X, end2Y};
}
/**
* Writes the coordinates of the atoms participating the given bond into an array. See comment
* for center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for
* details on coordinate sets
*
* @param bond The given bond
* @return The array with the coordinates
*/
public static int[] getBondCoordinates(IBond bond) {
if (bond.getBegin().getPoint2d() == null || bond.getEnd().getPoint2d() == null) {
logger.error("getBondCoordinates() called on Bond without 2D coordinates!");
return new int[0];
}
int beginX = (int) bond.getBegin().getPoint2d().x;
int endX = (int) bond.getEnd().getPoint2d().x;
int beginY = (int) bond.getBegin().getPoint2d().y;
int endY = (int) bond.getEnd().getPoint2d().y;
return new int[]{beginX, beginY, endX, endY};
}
/**
* Returns the atom of the given molecule that is closest to the given coordinates. See comment
* for center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for
* details on coordinate sets
*
* @param xPosition The x coordinate
* @param yPosition The y coordinate
* @param atomCon The molecule that is searched for the closest atom
* @return The atom that is closest to the given coordinates
*/
public static IAtom getClosestAtom(int xPosition, int yPosition, IAtomContainer atomCon) {
IAtom closestAtom = null;
IAtom currentAtom;
double smallestMouseDistance = -1;
double mouseDistance;
double atomX;
double atomY;
for (int i = 0; i < atomCon.getAtomCount(); i++) {
currentAtom = atomCon.getAtom(i);
atomX = currentAtom.getPoint2d().x;
atomY = currentAtom.getPoint2d().y;
mouseDistance = Math.sqrt(Math.pow(atomX - xPosition, 2) + Math.pow(atomY - yPosition, 2));
if (mouseDistance < smallestMouseDistance || smallestMouseDistance == -1) {
smallestMouseDistance = mouseDistance;
closestAtom = currentAtom;
}
}
return closestAtom;
}
/**
* Returns the atom of the given molecule that is closest to the given atom (excluding itself).
*
* @param atomCon The molecule that is searched for the closest atom
* @param atom The atom to search around
* @return The atom that is closest to the given coordinates
*/
public static IAtom getClosestAtom(IAtomContainer atomCon, IAtom atom) {
IAtom closestAtom = null;
double min = Double.MAX_VALUE;
Point2d atomPosition = atom.getPoint2d();
for (int i = 0; i < atomCon.getAtomCount(); i++) {
IAtom currentAtom = atomCon.getAtom(i);
if (!currentAtom.equals(atom)) {
double d = atomPosition.distance(currentAtom.getPoint2d());
if (d < min) {
min = d;
closestAtom = currentAtom;
}
}
}
return closestAtom;
}
/**
* Returns the atom of the given molecule that is closest to the given coordinates and is not
* the atom. See comment for center(IAtomContainer atomCon, Dimension areaDim, HashMap
* renderingCoordinates) for details on coordinate sets
*
* @param xPosition The x coordinate
* @param yPosition The y coordinate
* @param atomCon The molecule that is searched for the closest atom
* @param toignore This molecule will not be returned.
* @return The atom that is closest to the given coordinates
*/
public static IAtom getClosestAtom(double xPosition, double yPosition, IAtomContainer atomCon, IAtom toignore) {
IAtom closestAtom = null;
IAtom currentAtom;
// we compare squared distances, allowing us to do one sqrt()
// calculation less
double smallestSquaredMouseDistance = -1;
double mouseSquaredDistance;
double atomX;
double atomY;
for (int i = 0; i < atomCon.getAtomCount(); i++) {
currentAtom = atomCon.getAtom(i);
if (!currentAtom.equals(toignore)) {
atomX = currentAtom.getPoint2d().x;
atomY = currentAtom.getPoint2d().y;
mouseSquaredDistance = Math.pow(atomX - xPosition, 2) + Math.pow(atomY - yPosition, 2);
if (mouseSquaredDistance < smallestSquaredMouseDistance || smallestSquaredMouseDistance == -1) {
smallestSquaredMouseDistance = mouseSquaredDistance;
closestAtom = currentAtom;
}
}
}
return closestAtom;
}
/**
* Returns the atom of the given molecule that is closest to the given coordinates. See comment
* for center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for
* details on coordinate sets
*
* @param xPosition The x coordinate
* @param yPosition The y coordinate
* @param atomCon The molecule that is searched for the closest atom
* @return The atom that is closest to the given coordinates
*/
public static IAtom getClosestAtom(double xPosition, double yPosition, IAtomContainer atomCon) {
IAtom closestAtom = null;
IAtom currentAtom;
double smallestMouseDistance = -1;
double mouseDistance;
double atomX;
double atomY;
for (int i = 0; i < atomCon.getAtomCount(); i++) {
currentAtom = atomCon.getAtom(i);
atomX = currentAtom.getPoint2d().x;
atomY = currentAtom.getPoint2d().y;
mouseDistance = Math.sqrt(Math.pow(atomX - xPosition, 2) + Math.pow(atomY - yPosition, 2));
if (mouseDistance < smallestMouseDistance || smallestMouseDistance == -1) {
smallestMouseDistance = mouseDistance;
closestAtom = currentAtom;
}
}
return closestAtom;
}
/**
* Returns the bond of the given molecule that is closest to the given coordinates. See comment
* for center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for
* details on coordinate sets
*
* @param xPosition The x coordinate
* @param yPosition The y coordinate
* @param atomCon The molecule that is searched for the closest bond
* @return The bond that is closest to the given coordinates
*/
public static IBond getClosestBond(int xPosition, int yPosition, IAtomContainer atomCon) {
Point2d bondCenter;
IBond closestBond = null;
double smallestMouseDistance = -1;
double mouseDistance;
for (IBond currentBond : atomCon.bonds()) {
bondCenter = get2DCenter(currentBond.atoms());
mouseDistance = Math.sqrt(Math.pow(bondCenter.x - xPosition, 2) + Math.pow(bondCenter.y - yPosition, 2));
if (mouseDistance < smallestMouseDistance || smallestMouseDistance == -1) {
smallestMouseDistance = mouseDistance;
closestBond = currentBond;
}
}
return closestBond;
}
/**
* Returns the bond of the given molecule that is closest to the given coordinates. See comment
* for center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates) for
* details on coordinate sets
*
* @param xPosition The x coordinate
* @param yPosition The y coordinate
* @param atomCon The molecule that is searched for the closest bond
* @return The bond that is closest to the given coordinates
*/
public static IBond getClosestBond(double xPosition, double yPosition, IAtomContainer atomCon) {
Point2d bondCenter;
IBond closestBond = null;
double smallestMouseDistance = -1;
double mouseDistance;
for (IBond currentBond : atomCon.bonds()) {
bondCenter = get2DCenter(currentBond.atoms());
mouseDistance = Math.sqrt(Math.pow(bondCenter.x - xPosition, 2) + Math.pow(bondCenter.y - yPosition, 2));
if (mouseDistance < smallestMouseDistance || smallestMouseDistance == -1) {
smallestMouseDistance = mouseDistance;
closestBond = currentBond;
}
}
return closestBond;
}
/**
* Sorts a Vector of atoms such that the 2D distances of the atom locations from a given point
* are smallest for the first atoms in the vector. See comment for center(IAtomContainer
* atomCon, Dimension areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param point The point from which the distances to the atoms are measured
* @param atoms The atoms for which the distances to point are measured
*/
public static void sortBy2DDistance(IAtom[] atoms, Point2d point) {
double distance1;
double distance2;
IAtom atom1;
IAtom atom2;
boolean doneSomething;
do {
doneSomething = false;
for (int f = 0; f < atoms.length - 1; f++) {
atom1 = atoms[f];
atom2 = atoms[f + 1];
distance1 = point.distance(atom1.getPoint2d());
distance2 = point.distance(atom2.getPoint2d());
if (distance2 < distance1) {
atoms[f] = atom2;
atoms[f + 1] = atom1;
doneSomething = true;
}
}
} while (doneSomething);
}
/**
* Determines the scale factor for displaying a structure loaded from disk in a frame. An
* average of all bond length values is produced and a scale factor is determined which would
* scale the given molecule such that its See comment for center(IAtomContainer atomCon,
* Dimension areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param container The AtomContainer for which the ScaleFactor is to be calculated
* @param bondLength The target bond length
* @return The ScaleFactor with which the AtomContainer must be scaled to have the target bond
* length
*/
public static double getScaleFactor(IAtomContainer container, double bondLength) {
double currentAverageBondLength = getBondLengthMedian(container);
if (currentAverageBondLength == 0 || Double.isNaN(currentAverageBondLength)) return 1;
return bondLength / currentAverageBondLength;
}
/**
* An average of all 2D bond length values is produced. Bonds which have Atom's with no
* coordinates are disregarded. See comment for center(IAtomContainer atomCon, Dimension
* areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param container The AtomContainer for which the average bond length is to be calculated
* @return the average bond length
*/
public static double getBondLengthAverage(IAtomContainer container) {
double bondLengthSum = 0;
Iterator bonds = container.bonds().iterator();
int bondCounter = 0;
while (bonds.hasNext()) {
IBond bond = bonds.next();
IAtom atom1 = bond.getBegin();
IAtom atom2 = bond.getEnd();
if (atom1.getPoint2d() != null && atom2.getPoint2d() != null) {
bondCounter++;
bondLengthSum += getLength2D(bond);
}
}
return bondLengthSum / bondCounter;
}
/**
* Returns the geometric length of this bond in 2D space. See comment for center(IAtomContainer
* atomCon, Dimension areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param bond Description of the Parameter
* @return The geometric length of this bond
*/
public static double getLength2D(IBond bond) {
if (bond.getBegin() == null || bond.getEnd() == null) {
return 0.0;
}
Point2d point1 = bond.getBegin().getPoint2d();
Point2d point2 = bond.getEnd().getPoint2d();
if (point1 == null || point2 == null) {
return 0.0;
}
return point1.distance(point2);
}
/**
* Determines if all this {@link org.openscience.cdk.interfaces.IAtomContainer}'s atoms contain
* 2D coordinates. If any atom is null or has unset 2D coordinates this method will return
* false.
*
* @param container the atom container to examine
* @return indication that all 2D coordinates are available
* @see org.openscience.cdk.interfaces.IAtom#getPoint2d()
*/
public static boolean has2DCoordinates(IAtomContainer container) {
if (container == null || container.getAtomCount() == 0) return Boolean.FALSE;
for (IAtom atom : container.atoms()) {
if (atom == null || atom.getPoint2d() == null) return Boolean.FALSE;
}
return Boolean.TRUE;
}
/**
* Determine if all parts of a reaction have coodinates
*
* @param reaction a reaction
* @return the reaction has coordinates
*/
public static boolean has2DCoordinates(IReaction reaction) {
for (IAtomContainer mol : reaction.getReactants().atomContainers())
if (!has2DCoordinates(mol))
return false;
for (IAtomContainer mol : reaction.getProducts().atomContainers())
if (!has2DCoordinates(mol))
return false;
for (IAtomContainer mol : reaction.getAgents().atomContainers())
if (!has2DCoordinates(mol))
return false;
return true;
}
/**
* Determines the coverage of this {@link org.openscience.cdk.interfaces.IAtomContainer}'s 2D
* coordinates. If all atoms are non-null and have 2D coordinates this method will return {@link
* CoordinateCoverage#FULL}. If one or more atoms does have 2D coordinates and any others atoms
* are null or are missing 2D coordinates this method will return {@link
* CoordinateCoverage#PARTIAL}. If all atoms are null or are all missing 2D coordinates this
* method will return {@link CoordinateCoverage#NONE}. If the provided container is null {@link
* CoordinateCoverage#NONE} is also returned.
*
* @param container the container to inspect
* @return {@link CoordinateCoverage#FULL}, {@link CoordinateCoverage#PARTIAL} or {@link
* CoordinateCoverage#NONE} depending on the number of 3D coordinates present
* @see CoordinateCoverage
* @see #has2DCoordinates(org.openscience.cdk.interfaces.IAtomContainer)
* @see #get3DCoordinateCoverage(org.openscience.cdk.interfaces.IAtomContainer)
* @see org.openscience.cdk.interfaces.IAtom#getPoint2d()
*/
public static CoordinateCoverage get2DCoordinateCoverage(IAtomContainer container) {
if (container == null || container.getAtomCount() == 0) return CoordinateCoverage.NONE;
int count = 0;
for (IAtom atom : container.atoms()) {
count += atom != null && atom.getPoint2d() != null ? 1 : 0;
}
return count == 0 ? CoordinateCoverage.NONE : count == container.getAtomCount() ? CoordinateCoverage.FULL
: CoordinateCoverage.PARTIAL;
}
/**
* Determines if this AtomContainer contains 2D coordinates for some or all molecules. See
* comment for center(IAtomContainer atomCon, Dimension areaDim, HashMap renderingCoordinates)
* for details on coordinate sets
*
* @param container the molecule to be considered
* @return 0 no 2d, 1=some, 2= for each atom
* @see #get2DCoordinateCoverage(org.openscience.cdk.interfaces.IAtomContainer)
* @deprecated use {@link #get2DCoordinateCoverage(org.openscience.cdk.interfaces.IAtomContainer)}
* for determining partial coordinates
*/
@Deprecated
public static int has2DCoordinatesNew(IAtomContainer container) {
if (container == null) return 0;
boolean no2d = false;
boolean with2d = false;
for (IAtom atom : container.atoms()) {
if (atom.getPoint2d() == null) {
no2d = true;
} else {
with2d = true;
}
}
if (!no2d && with2d) {
return 2;
} else if (no2d && with2d) {
return 1;
} else {
return 0;
}
}
/**
* Determines if this Atom contains 2D coordinates. See comment for center(IAtomContainer
* atomCon, Dimension areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param atom Description of the Parameter
* @return boolean indication that 2D coordinates are available
*/
public static boolean has2DCoordinates(IAtom atom) {
return (atom.getPoint2d() != null);
}
/**
* Determines if this Bond contains 2D coordinates. See comment for center(IAtomContainer
* atomCon, Dimension areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param bond Description of the Parameter
* @return boolean indication that 2D coordinates are available
*/
public static boolean has2DCoordinates(IBond bond) {
for (IAtom iAtom : bond.atoms()) {
if (iAtom.getPoint2d() == null) {
return false;
}
}
return true;
}
/**
* Determines if all this {@link org.openscience.cdk.interfaces.IAtomContainer}'s atoms contain
* 3D coordinates. If any atom is null or has unset 3D coordinates this method will return
* false. If the provided container is null false is returned.
*
* @param container the atom container to examine
* @return indication that all 3D coordinates are available
* @see org.openscience.cdk.interfaces.IAtom#getPoint3d()
*/
public static boolean has3DCoordinates(IAtomContainer container) {
if (container == null || container.getAtomCount() == 0) return Boolean.FALSE;
for (IAtom atom : container.atoms()) {
if (atom == null || atom.getPoint3d() == null) return Boolean.FALSE;
}
return Boolean.TRUE;
}
/**
* Determines the coverage of this {@link org.openscience.cdk.interfaces.IAtomContainer}'s 3D
* coordinates. If all atoms are non-null and have 3D coordinates this method will return {@link
* CoordinateCoverage#FULL}. If one or more atoms does have 3D coordinates and any others atoms
* are null or are missing 3D coordinates this method will return {@link
* CoordinateCoverage#PARTIAL}. If all atoms are null or are all missing 3D coordinates this
* method will return {@link CoordinateCoverage#NONE}. If the provided container is null {@link
* CoordinateCoverage#NONE} is also returned.
*
* @param container the container to inspect
* @return {@link CoordinateCoverage#FULL}, {@link CoordinateCoverage#PARTIAL} or {@link
* CoordinateCoverage#NONE} depending on the number of 3D coordinates present
* @see CoordinateCoverage
* @see #has3DCoordinates(org.openscience.cdk.interfaces.IAtomContainer)
* @see #get2DCoordinateCoverage(org.openscience.cdk.interfaces.IAtomContainer)
* @see org.openscience.cdk.interfaces.IAtom#getPoint3d()
*/
public static CoordinateCoverage get3DCoordinateCoverage(IAtomContainer container) {
if (container == null || container.getAtomCount() == 0) return CoordinateCoverage.NONE;
int count = 0;
for (IAtom atom : container.atoms()) {
count += atom != null && atom.getPoint3d() != null ? 1 : 0;
}
return count == 0 ? CoordinateCoverage.NONE : count == container.getAtomCount() ? CoordinateCoverage.FULL
: CoordinateCoverage.PARTIAL;
}
/**
* Determines the normalized vector orthogonal on the vector p1->p2.
*
* @param point1 Description of the Parameter
* @param point2 Description of the Parameter
* @return Description of the Return Value
*/
public static Vector2d calculatePerpendicularUnitVector(Point2d point1, Point2d point2) {
Vector2d vector = new Vector2d();
vector.sub(point2, point1);
vector.normalize();
// Return the perpendicular vector
return new Vector2d(-1.0 * vector.y, vector.x);
}
/**
* Calculates the normalization factor in order to get an average bond length of 1.5. It takes
* only into account Bond's with two atoms. See comment for center(IAtomContainer atomCon,
* Dimension areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param container Description of the Parameter
* @return The normalizationFactor value
*/
public static double getNormalizationFactor(IAtomContainer container) {
double bondlength = 0.0;
double ratio;
/*
* Desired bond length for storing structures in MDL mol files This
* should probably be set externally (from system wide settings)
*/
double desiredBondLength = 1.5;
// loop over all bonds and determine the mean bond distance
int counter = 0;
for (IBond bond : container.bonds()) {
// only consider two atom bonds into account
if (bond.getAtomCount() == 2) {
counter++;
IAtom atom1 = bond.getBegin();
IAtom atom2 = bond.getEnd();
bondlength += Math.sqrt(Math.pow(atom1.getPoint2d().x - atom2.getPoint2d().x, 2)
+ Math.pow(atom1.getPoint2d().y - atom2.getPoint2d().y, 2));
}
}
bondlength = bondlength / counter;
ratio = desiredBondLength / bondlength;
return ratio;
}
/**
* Determines the best alignment for the label of an atom in 2D space. It returns 1 if left
* aligned, and -1 if right aligned. See comment for center(IAtomContainer atomCon, Dimension
* areaDim, HashMap renderingCoordinates) for details on coordinate sets
*
* @param container Description of the Parameter
* @param atom Description of the Parameter
* @return The bestAlignmentForLabel value
*/
public static int getBestAlignmentForLabel(IAtomContainer container, IAtom atom) {
double overallDiffX = 0;
for (IAtom connectedAtom : container.getConnectedAtomsList(atom)) {
overallDiffX += connectedAtom.getPoint2d().x - atom.getPoint2d().x;
}
if (overallDiffX <= 0) {
return 1;
} else {
return -1;
}
}
/**
* Determines the best alignment for the label of an atom in 2D space. It returns 1 if right
* (=default) aligned, and -1 if left aligned. returns 2 if top aligned, and -2 if H is aligned
* below the atom See comment for center(IAtomContainer atomCon, Dimension areaDim, HashMap
* renderingCoordinates) for details on coordinate sets
*
* @param container Description of the Parameter
* @param atom Description of the Parameter
* @return The bestAlignmentForLabel value
*/
public static int getBestAlignmentForLabelXY(IAtomContainer container, IAtom atom) {
double overallDiffX = 0;
double overallDiffY = 0;
for (IAtom connectedAtom : container.getConnectedAtomsList(atom)) {
overallDiffX += connectedAtom.getPoint2d().x - atom.getPoint2d().x;
overallDiffY += connectedAtom.getPoint2d().y - atom.getPoint2d().y;
}
if (Math.abs(overallDiffY) > Math.abs(overallDiffX)) {
if (overallDiffY < 0)
return 2;
else
return -2;
} else {
if (overallDiffX <= 0)
return 1;
else
return -1;
}
}
/**
* Returns the atoms which are closes to an atom in an AtomContainer by distance in 3d.
*
* @param container The AtomContainer to examine
* @param startAtom the atom to start from
* @param max the number of neighbours to return
* @return the average bond length
* @throws org.openscience.cdk.exception.CDKException Description of the Exception
*/
public static List findClosestInSpace(IAtomContainer container, IAtom startAtom, int max)
throws CDKException {
Point3d originalPoint = startAtom.getPoint3d();
if (originalPoint == null) {
throw new CDKException("No point3d, but findClosestInSpace is working on point3ds");
}
Map atomsByDistance = new TreeMap();
for (IAtom atom : container.atoms()) {
if (!atom.equals(startAtom)) {
if (atom.getPoint3d() == null) {
throw new CDKException("No point3d, but findClosestInSpace is working on point3ds");
}
double distance = atom.getPoint3d().distance(originalPoint);
atomsByDistance.put(distance, atom);
}
}
// FIXME: should there not be some sort here??
Set keySet = atomsByDistance.keySet();
Iterator keyIter = keySet.iterator();
List returnValue = new ArrayList();
int i = 0;
while (keyIter.hasNext() && i < max) {
returnValue.add(atomsByDistance.get(keyIter.next()));
i++;
}
return (returnValue);
}
/**
* Returns a Map with the AtomNumbers, the first number corresponds to the first (or the largest
* AtomContainer) atomcontainer. It is recommend to sort the atomContainer due to their number
* of atoms before calling this function.
*
* The molecules needs to be aligned before! (coordinates are needed)
*
* @param firstAtomContainer the (largest) first aligned AtomContainer which is the reference
* @param secondAtomContainer the second aligned AtomContainer
* @param searchRadius the radius of space search from each atom
* @return a Map of the mapped atoms
* @throws org.openscience.cdk.exception.CDKException Description of the Exception
*/
public static Map mapAtomsOfAlignedStructures(IAtomContainer firstAtomContainer,
IAtomContainer secondAtomContainer, double searchRadius, Map mappedAtoms)
throws CDKException {
getLargestAtomContainer(firstAtomContainer, secondAtomContainer);
double[][] distanceMatrix = new double[firstAtomContainer.getAtomCount()][secondAtomContainer.getAtomCount()];
for (int i = 0; i < firstAtomContainer.getAtomCount(); i++) {
Point3d firstAtomPoint = firstAtomContainer.getAtom(i).getPoint3d();
for (int j = 0; j < secondAtomContainer.getAtomCount(); j++) {
distanceMatrix[i][j] = firstAtomPoint.distance(secondAtomContainer.getAtom(j).getPoint3d());
}
}
double minimumDistance;
for (int i = 0; i < firstAtomContainer.getAtomCount(); i++) {
minimumDistance = searchRadius;
for (int j = 0; j < secondAtomContainer.getAtomCount(); j++) {
if (distanceMatrix[i][j] < searchRadius && distanceMatrix[i][j] < minimumDistance) {
//check atom properties
if (checkAtomMapping(firstAtomContainer, secondAtomContainer, i, j)) {
minimumDistance = distanceMatrix[i][j];
mappedAtoms.put(firstAtomContainer.indexOf(firstAtomContainer.getAtom(i)),
secondAtomContainer.indexOf(secondAtomContainer.getAtom(j)));
}
}
}
}
return mappedAtoms;
}
// FIXME: huh!?!?!
private static void getLargestAtomContainer(IAtomContainer firstAC, IAtomContainer secondAC) {
if (firstAC.getAtomCount() < secondAC.getAtomCount()) {
IAtomContainer tmp;
try {
tmp = firstAC.clone();
firstAC = secondAC.clone();
secondAC = tmp.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private static boolean checkAtomMapping(IAtomContainer firstAC, IAtomContainer secondAC, int posFirstAtom,
int posSecondAtom) {
IAtom firstAtom = firstAC.getAtom(posFirstAtom);
IAtom secondAtom = secondAC.getAtom(posSecondAtom);
// XXX: floating point comparision!
return firstAtom.getSymbol().equals(secondAtom.getSymbol())
&& firstAC.getConnectedAtomsList(firstAtom).size() == secondAC.getConnectedAtomsList(secondAtom).size()
&& firstAtom.getBondOrderSum().equals(secondAtom.getBondOrderSum())
&& firstAtom.getMaxBondOrder() == secondAtom.getMaxBondOrder();
}
private static IAtomContainer setVisitedFlagsToFalse(IAtomContainer atomContainer) {
for (int i = 0; i < atomContainer.getAtomCount(); i++) {
atomContainer.getAtom(i).setFlag(CDKConstants.VISITED, false);
}
return atomContainer;
}
/**
* Return the RMSD of bonds length between the 2 aligned molecules.
*
* @param firstAtomContainer the (largest) first aligned AtomContainer which is the reference
* @param secondAtomContainer the second aligned AtomContainer
* @param mappedAtoms Map: a Map of the mapped atoms
* @param Coords3d boolean: true if moecules has 3D coords, false if molecules has 2D
* coords
* @return double: all the RMSD of bonds length
*/
public static double getBondLengthRMSD(IAtomContainer firstAtomContainer, IAtomContainer secondAtomContainer,
Map mappedAtoms, boolean Coords3d) {
//logger.debug("**** GT getBondLengthRMSD ****");
Iterator firstAtoms = mappedAtoms.keySet().iterator();
IAtom centerAtomFirstMolecule;
IAtom centerAtomSecondMolecule;
List connectedAtoms;
double sum = 0;
double n = 0;
double distance1 = 0;
double distance2 = 0;
setVisitedFlagsToFalse(firstAtomContainer);
setVisitedFlagsToFalse(secondAtomContainer);
while (firstAtoms.hasNext()) {
centerAtomFirstMolecule = firstAtomContainer.getAtom(firstAtoms.next());
centerAtomFirstMolecule.setFlag(CDKConstants.VISITED, true);
centerAtomSecondMolecule = secondAtomContainer.getAtom(mappedAtoms.get(firstAtomContainer
.indexOf(centerAtomFirstMolecule)));
connectedAtoms = firstAtomContainer.getConnectedAtomsList(centerAtomFirstMolecule);
for (int i = 0; i < connectedAtoms.size(); i++) {
IAtom conAtom = connectedAtoms.get(i);
//this step is built to know if the program has already calculate a bond length (so as not to have duplicate values)
if (!conAtom.getFlag(CDKConstants.VISITED)) {
if (Coords3d) {
distance1 = centerAtomFirstMolecule.getPoint3d().distance(conAtom.getPoint3d());
distance2 = centerAtomSecondMolecule.getPoint3d().distance(
secondAtomContainer.getAtom(mappedAtoms.get(firstAtomContainer.indexOf(conAtom)))
.getPoint3d());
sum = sum + Math.pow((distance1 - distance2), 2);
n++;
} else {
distance1 = centerAtomFirstMolecule.getPoint2d().distance(conAtom.getPoint2d());
distance2 = centerAtomSecondMolecule.getPoint2d().distance(
secondAtomContainer.getAtom(
(mappedAtoms.get(firstAtomContainer.indexOf(conAtom)))).getPoint2d());
sum = sum + Math.pow((distance1 - distance2), 2);
n++;
}
}
}
}
setVisitedFlagsToFalse(firstAtomContainer);
setVisitedFlagsToFalse(secondAtomContainer);
return Math.sqrt(sum / n);
}
/**
* Return the variation of each angle value between the 2 aligned molecules.
*
* @param firstAtomContainer the (largest) first aligned AtomContainer which is the reference
* @param secondAtomContainer the second aligned AtomContainer
* @param mappedAtoms Map: a Map of the mapped atoms
* @return double: the value of the RMSD
*/
public static double getAngleRMSD(IAtomContainer firstAtomContainer, IAtomContainer secondAtomContainer,
Map mappedAtoms) {
//logger.debug("**** GT getAngleRMSD ****");
Iterator firstAtoms = mappedAtoms.keySet().iterator();
//logger.debug("mappedAtoms:"+mappedAtoms.toString());
IAtom firstAtomfirstAC;
IAtom centerAtomfirstAC;
IAtom firstAtomsecondAC;
IAtom secondAtomsecondAC;
IAtom centerAtomsecondAC;
double angleFirstMolecule;
double angleSecondMolecule;
double sum = 0;
double n = 0;
while (firstAtoms.hasNext()) {
int firstAtomNumber = firstAtoms.next();
centerAtomfirstAC = firstAtomContainer.getAtom(firstAtomNumber);
List connectedAtoms = firstAtomContainer.getConnectedAtomsList(centerAtomfirstAC);
if (connectedAtoms.size() > 1) {
//logger.debug("If "+centerAtomfirstAC.getSymbol()+" is the center atom :");
for (int i = 0; i < connectedAtoms.size() - 1; i++) {
firstAtomfirstAC = connectedAtoms.get(i);
for (int j = i + 1; j < connectedAtoms.size(); j++) {
angleFirstMolecule = getAngle(centerAtomfirstAC, firstAtomfirstAC, connectedAtoms.get(j));
centerAtomsecondAC = secondAtomContainer.getAtom(mappedAtoms.get(firstAtomContainer
.indexOf(centerAtomfirstAC)));
firstAtomsecondAC = secondAtomContainer.getAtom(mappedAtoms.get(firstAtomContainer
.indexOf(firstAtomfirstAC)));
secondAtomsecondAC = secondAtomContainer.getAtom(mappedAtoms.get(firstAtomContainer
.indexOf(connectedAtoms.get(j))));
angleSecondMolecule = getAngle(centerAtomsecondAC, firstAtomsecondAC, secondAtomsecondAC);
sum = sum + Math.pow(angleFirstMolecule - angleSecondMolecule, 2);
n++;
//logger.debug("Error for the "+firstAtomfirstAC.getSymbol().toLowerCase()+"-"+centerAtomfirstAC.getSymbol()+"-"+connectedAtoms[j].getSymbol().toLowerCase()+" Angle :"+deltaAngle+" degrees");
}
}
}//if
}
return Math.sqrt(sum / n);
}
private static double getAngle(IAtom atom1, IAtom atom2, IAtom atom3) {
Vector3d centerAtom = new Vector3d();
centerAtom.x = atom1.getPoint3d().x;
centerAtom.y = atom1.getPoint3d().y;
centerAtom.z = atom1.getPoint3d().z;
Vector3d firstAtom = new Vector3d();
Vector3d secondAtom = new Vector3d();
firstAtom.x = atom2.getPoint3d().x;
firstAtom.y = atom2.getPoint3d().y;
firstAtom.z = atom2.getPoint3d().z;
secondAtom.x = atom3.getPoint3d().x;
secondAtom.y = atom3.getPoint3d().y;
secondAtom.z = atom3.getPoint3d().z;
firstAtom.sub(centerAtom);
secondAtom.sub(centerAtom);
return firstAtom.angle(secondAtom);
}
/**
* Return the RMSD between the 2 aligned molecules.
*
* @param firstAtomContainer the (largest) first aligned AtomContainer which is the reference
* @param secondAtomContainer the second aligned AtomContainer
* @param mappedAtoms Map: a Map of the mapped atoms
* @param Coords3d boolean: true if molecules has 3D coords, false if molecules has
* 2D coords
* @return double: the value of the RMSD
* @throws org.openscience.cdk.exception.CDKException if there is an error in getting mapped
* atoms
*/
public static double getAllAtomRMSD(IAtomContainer firstAtomContainer, IAtomContainer secondAtomContainer,
Map mappedAtoms, boolean Coords3d) throws CDKException {
//logger.debug("**** GT getAllAtomRMSD ****");
double sum = 0;
double RMSD;
Iterator firstAtoms = mappedAtoms.keySet().iterator();
int firstAtomNumber;
int secondAtomNumber;
int n = 0;
while (firstAtoms.hasNext()) {
firstAtomNumber = firstAtoms.next();
try {
secondAtomNumber = mappedAtoms.get(firstAtomNumber);
IAtom firstAtom = firstAtomContainer.getAtom(firstAtomNumber);
if (Coords3d) {
sum = sum
+ Math.pow(
firstAtom.getPoint3d().distance(
secondAtomContainer.getAtom(secondAtomNumber).getPoint3d()), 2);
n++;
} else {
sum = sum
+ Math.pow(
firstAtom.getPoint2d().distance(
secondAtomContainer.getAtom(secondAtomNumber).getPoint2d()), 2);
n++;
}
} catch (Exception ex) {
throw new CDKException(ex.getMessage(), ex);
}
}
RMSD = Math.sqrt(sum / n);
return RMSD;
}
/**
* Return the RMSD of the heavy atoms between the 2 aligned molecules.
*
* @param firstAtomContainer the (largest) first aligned AtomContainer which is the reference
* @param secondAtomContainer the second aligned AtomContainer
* @param mappedAtoms Map: a Map of the mapped atoms
* @param hetAtomOnly boolean: true if only hetero atoms should be considered
* @param Coords3d boolean: true if molecules has 3D coords, false if molecules has
* 2D coords
* @return double: the value of the RMSD
*/
public static double getHeavyAtomRMSD(IAtomContainer firstAtomContainer, IAtomContainer secondAtomContainer,
Map mappedAtoms, boolean hetAtomOnly, boolean Coords3d) {
//logger.debug("**** GT getAllAtomRMSD ****");
double sum = 0;
double RMSD;
Iterator firstAtoms = mappedAtoms.keySet().iterator();
int firstAtomNumber;
int secondAtomNumber;
int n = 0;
while (firstAtoms.hasNext()) {
firstAtomNumber = firstAtoms.next();
secondAtomNumber = mappedAtoms.get(firstAtomNumber);
IAtom firstAtom = firstAtomContainer.getAtom(firstAtomNumber);
if (hetAtomOnly) {
if (!firstAtom.getSymbol().equals("H") && !firstAtom.getSymbol().equals("C")) {
if (Coords3d) {
sum = sum
+ Math.pow(
firstAtom.getPoint3d().distance(
secondAtomContainer.getAtom(secondAtomNumber).getPoint3d()), 2);
n++;
} else {
sum = sum
+ Math.pow(
firstAtom.getPoint2d().distance(
secondAtomContainer.getAtom(secondAtomNumber).getPoint2d()), 2);
n++;
}
}
} else {
if (!firstAtom.getSymbol().equals("H")) {
if (Coords3d) {
sum = sum
+ Math.pow(
firstAtom.getPoint3d().distance(
secondAtomContainer.getAtom(secondAtomNumber).getPoint3d()), 2);
n++;
} else {
sum = sum
+ Math.pow(
firstAtom.getPoint2d().distance(
secondAtomContainer.getAtom(secondAtomNumber).getPoint2d()), 2);
n++;
}
}
}
}
RMSD = Math.sqrt(sum / n);
return RMSD;
}
/**
* An average of all 3D bond length values is produced, using point3ds in atoms. Atom's with no
* coordinates are disregarded.
*
* @param container The AtomContainer for which the average bond length is to be calculated
* @return the average bond length
*/
public static double getBondLengthAverage3D(IAtomContainer container) {
double bondLengthSum = 0;
int bondCounter = 0;
for (IBond bond : container.bonds()) {
IAtom atom1 = bond.getBegin();
IAtom atom2 = bond.getEnd();
if (atom1.getPoint3d() != null && atom2.getPoint3d() != null) {
bondCounter++;
bondLengthSum += atom1.getPoint3d().distance(atom2.getPoint3d());
}
}
return bondLengthSum / bondCounter;
}
/**
* Shift the container horizontally to the right to make its bounds not overlap with the other
* bounds. To avoid dependence on Java AWT, rectangles are described by arrays of double. Each
* rectangle is specified by {minX, minY, maxX, maxY}.
*
* @param container the {@link IAtomContainer} to shift to the
* right
* @param bounds the bounds of the {@link IAtomContainer} to shift
* @param last the bounds that is used as reference
* @param gap the gap between the two rectangles
* @return the rectangle of the {@link IAtomContainer} after the shift
*/
public static double[] shiftContainer(IAtomContainer container, double[] bounds, double[] last, double gap) {
assert bounds.length == 4;
assert last.length == 4;
final double boundsMinX = bounds[0];
final double boundsMinY = bounds[1];
final double boundsMaxX = bounds[2];
final double boundsMaxY = bounds[3];
final double lastMaxX = last[2];
// determine if the containers are overlapping
if (lastMaxX + gap >= boundsMinX) {
double xShift = lastMaxX + gap - boundsMinX;
Vector2d shift = new Vector2d(xShift, 0.0);
GeometryUtil.translate2D(container, shift);
return new double[]{boundsMinX + xShift, boundsMinY, boundsMaxX + xShift, boundsMaxY};
} else {
// the containers are not overlapping
return bounds;
}
}
/*
* Returns the average 2D bond length values of all products and reactants
* of the given reaction. The method uses {@link
* #getBondLengthAverage(IAtomContainer)} internally.
* @param reaction The IReaction for which the average 2D bond length is
* calculated
* @return the average 2D bond length
* @see #getBondLengthAverage(IAtomContainer)
*/
public static double getBondLengthAverage(IReaction reaction) {
double bondlenghtsum = 0.0;
int containercount = 0;
List containers = ReactionManipulator.getAllAtomContainers(reaction);
for (IAtomContainer container : containers) {
containercount++;
bondlenghtsum += getBondLengthAverage(container);
}
return bondlenghtsum / containercount;
}
/**
* Calculate the median bond length of an atom container.
*
* @param container structure representation
* @return median bond length
* @throws java.lang.IllegalArgumentException unset coordinates or no bonds
*/
public static double getBondLengthMedian(final IAtomContainer container) {
if (container.getBondCount() == 0) throw new IllegalArgumentException("Container has no bonds.");
int nBonds = 0;
double[] lengths = new double[container.getBondCount()];
for (int i = 0; i < container.getBondCount(); i++) {
final IBond bond = container.getBond(i);
final IAtom atom1 = bond.getBegin();
final IAtom atom2 = bond.getEnd();
Point2d p1 = atom1.getPoint2d();
Point2d p2 = atom2.getPoint2d();
if (p1 == null || p2 == null)
throw new IllegalArgumentException("An atom has no 2D coordinates.");
if (p1.x != p2.x || p1.y != p2.y)
lengths[nBonds++] = p1.distance(p2);
}
Arrays.sort(lengths, 0, nBonds);
return lengths[nBonds / 2];
}
/**
* Determines if this model contains 3D coordinates for all atoms.
*
* @param chemModel the ChemModel to consider
* @return Boolean indication that 3D coordinates are available for all atoms.
*/
public static boolean has3DCoordinates(IChemModel chemModel) {
List acs = ChemModelManipulator.getAllAtomContainers(chemModel);
for (IAtomContainer ac : acs) {
if (!has3DCoordinates(ac)) {
return false;
}
}
return true;
}
/**
* Shift the containers in a reaction vertically upwards to not overlap with the reference
* rectangle. The shift is such that the given gap is realized, but only if the reactions are
* actually overlapping. To avoid dependence on Java AWT, rectangles are described by
* arrays of double. Each rectangle is specified by {minX, minY, maxX, maxY}.
*
* @param reaction the reaction to shift
* @param bounds the bounds of the reaction to shift
* @param last the bounds of the last reaction
* @return the rectangle of the shifted reaction
*/
public static double[] shiftReactionVertical(IReaction reaction, double[] bounds, double[] last, double gap) {
assert bounds.length == 4;
assert last.length == 4;
final double boundsMinX = bounds[0];
final double boundsMinY = bounds[1];
final double boundsMaxX = bounds[2];
final double boundsMaxY = bounds[3];
final double lastMinY = last[1];
final double lastMaxY = last[3];
final double boundsHeight = boundsMaxY - boundsMinY;
final double lastHeight = lastMaxY - lastMinY;
// determine if the reactions are overlapping
if (lastMaxY + gap >= boundsMinY) {
double yShift = boundsHeight + lastHeight + gap;
Vector2d shift = new Vector2d(0, yShift);
List containers = ReactionManipulator.getAllAtomContainers(reaction);
for (IAtomContainer container : containers) {
translate2D(container, shift);
}
return new double[]{boundsMinX, boundsMinY + yShift, boundsMaxX, boundsMaxY + yShift};
} else {
// the reactions were not overlapping
return bounds;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy