/* $RCSfile$
* $Author$
* $Date$
* $Revision$
*
* Copyright (C) 2003-2007 The Chemistry Development Kit (CDK) project
*
* Contact: cdk-devel@lists.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
* All we ask is that proper credit is given for our work, which includes
* - but is not limited to - adding the above copyright notice to the beginning
* of your source code files, and to any copyright notice that you may distribute
* with programs based on this work.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
package org.openscience.cdk.layout;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.Vector;
import javax.vecmath.Point2d;
import javax.vecmath.Vector2d;
import org.openscience.cdk.interfaces.IAtom;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.interfaces.IBond;
import org.openscience.cdk.interfaces.IChemObject;
import org.openscience.cdk.interfaces.IRingSet;
import org.openscience.cdk.tools.ILoggingTool;
import org.openscience.cdk.tools.LoggingToolFactory;
/**
* Helper class for Structure Diagram Generation. Resolves atom or bond
* overlaps after the actual SDG was done
*
* @author steinbeck
* @cdk.created 2003-09-4
* @cdk.keyword layout
* @cdk.keyword 2D-coordinates
* @cdk.module sdg
* @cdk.githash
*/
public class OverlapResolver
{
private static ILoggingTool logger =
LoggingToolFactory.createLoggingTool(OverlapResolver.class);
double bondLength = 1.5;
int maxSteps = 10000;
public OverlapResolver()
{
}
/**
* Main method to be called to resolve overlap situations.
*
* @param ac The atomcontainer in which the atom or bond overlap exists
* @param sssr A ring set for this atom container if one exists, otherwhise null
*/
public double resolveOverlap(IAtomContainer ac, IRingSet sssr)
{
Vector overlappingAtoms = new Vector();
Vector overlappingBonds = new Vector();
logger.debug("Start of resolveOverlap");
double overlapScore = getOverlapScore(ac, overlappingAtoms, overlappingBonds);
if (overlapScore > 0)
{
overlapScore = displace(ac, overlappingAtoms, overlappingBonds);
}
logger.debug("overlapScore = " + overlapScore);
logger.debug("End of resolveOverlap");
return overlapScore;
}
/**
* Makes a small displacement to some atoms or rings in the given
* atomcontainer.
*
*@param ac The AtomContainer to work on
*@param overlappingAtoms Description of the Parameter
*@param overlappingBonds Description of the Parameter
*/
public double displace(IAtomContainer ac, Vector overlappingAtoms, Vector overlappingBonds)
{
OverlapPair op = null;
IAtom a1 = null, a2 = null;
Vector2d v1 = null, v2 = null;
int steps = 0;
int p = 0;
double overlapScore = 0;
double choice = 0;
logger.debug("We are here because of an overlap situation.");
//logger.debug("Overlap score: " + overlapScore);
do{
/* we take a random overlapping
* pair of atoms
*/
p = (int)(Math.random() * overlappingAtoms.size());
logger.debug("Taking overlap pair no. " + p);
op = (OverlapPair)overlappingAtoms.elementAt(p);
/* Now we have an overlapping pair of atoms
* We calculate the 2D vector formed by the
* positions of both and translate one of the atoms by
* one tenth of a bond length
*/
a1 = (IAtom)op.chemObject1;
a2 = (IAtom)op.chemObject2;
v1 = new Vector2d(a1.getPoint2d());
v2 = new Vector2d(a2.getPoint2d());
v2.sub(v1);
v2.normalize();
if(Double.isNaN(v2.x))
v2.x=0.01;
if(Double.isNaN(v2.y))
v2.y=0.01;
v2.scale(bondLength / 20.0);
logger.debug("Calculation translation vector " + v2);
choice = Math.random();
if (choice > 0.5)
{
a2.getPoint2d().add(v2);
logger.debug("Random variable: " + choice + ", displacing first atom");
}
else
{
a1.getPoint2d().sub(v2);
logger.debug("Random variable: " + choice + ", displacing second atom");
}
overlapScore = getOverlapScore(ac, overlappingAtoms
, overlappingBonds);
steps ++;
}while(overlapScore > 0 && !(steps > maxSteps));
if (steps < 100)
{
logger.debug("Overlap situation resolved");
logger.debug("Overlap score: " + overlapScore);
logger.debug(steps + " steps needed to clear situation");
}
else
{
logger.debug("Could not resolve overlap situation");
logger.debug("Number of " + steps + " steps taken exceeds limit of " + maxSteps);
logger.debug("Overlap score: " + overlapScore);
}
return overlapScore;
}
/**
* Calculates a score based on the overlap of atoms and intersection of bonds.
* The overlap is calculated by summing up the distances between all pairs of
* atoms, if they are less than half the standard bondlength apart.
*
*@param ac The Atomcontainer to work on
*@param overlappingAtoms Description of the Parameter
*@param overlappingBonds Description of the Parameter
*@return The overlapScore value
*/
public double getOverlapScore(IAtomContainer ac, Vector overlappingAtoms, Vector overlappingBonds)
{
double overlapScore = 0;
overlapScore = getAtomOverlapScore(ac, overlappingAtoms);
//overlapScore += getBondOverlapScore(ac, overlappingBonds);
return overlapScore;
}
/**
* Calculates a score based on the overlap of atoms.
* The overlap is calculated by summing up the distances between all pairs of
* atoms, if they are less than half the standard bondlength apart.
*
*@param ac The Atomcontainer to work on
*@param overlappingAtoms Description of the Parameter
*@return The overlapScore value
*/
public double getAtomOverlapScore(IAtomContainer ac, Vector overlappingAtoms)
{
overlappingAtoms.removeAllElements();
IAtom atom1 = null;
IAtom atom2 = null;
Point2d p1 = null;
Point2d p2 = null;
double distance = 0;
double overlapScore = 0;
double overlapCutoff = bondLength / 10;
logger.debug("Bond length is set to " + bondLength);
logger.debug("Now cyling through all pairs of atoms");
for (int f = 0; f < ac.getAtomCount(); f++)
{
atom1 = ac.getAtom(f);
p1 = atom1.getPoint2d();
for (int g = f + 1; g < ac.getAtomCount(); g++)
{
atom2 = ac.getAtom(g);
p2 = atom2.getPoint2d();
distance = p1.distance(p2);
if (distance < overlapCutoff)
{
logger.debug("Detected atom clash with distance: " + distance + ", which is smaller than overlapCutoff " + overlapCutoff);
overlapScore += overlapCutoff;
overlappingAtoms.addElement(new OverlapPair(atom1, atom2));
}
}
}
return overlapScore;
}
/**
* Calculates a score based on the intersection of bonds.
*
*@param ac The Atomcontainer to work on
*@param overlappingBonds Description of the Parameter
*@return The overlapScore value
*/
public double getBondOverlapScore(IAtomContainer ac, Vector overlappingBonds)
{
overlappingBonds.removeAllElements();
double overlapScore = 0;
IBond bond1 = null;
IBond bond2 = null;
double overlapCutoff = bondLength / 2;
for (int f = 0; f < ac.getBondCount(); f++)
{
bond1 = ac.getBond(f);
for (int g = f; g < ac.getBondCount(); g++)
{
bond2 = ac.getBond(g);
/* bonds must not be connected */
if (!bond1.isConnectedTo(bond2))
{
if (areIntersected(bond1, bond2))
{
overlapScore += overlapCutoff;
overlappingBonds.addElement(new OverlapPair(bond1, bond2));
}
}
}
}
return overlapScore;
}
/**
* Checks if two bonds cross each other.
*
*@param bond1 Description of the Parameter
*@param bond2 Description of the Parameter
*@return Description of the Return Value
*/
public boolean areIntersected(IBond bond1, IBond bond2)
{
double x1 = 0, x2 = 0, x3 = 0, x4 = 0;
double y1 = 0, y2 = 0, y3 = 0, y4 = 0;
//Point2D.Double p1 = null, p2 = null, p3 = null, p4 = null;
x1 = bond1.getAtom(0).getPoint2d().x;
x2 = bond1.getAtom(1).getPoint2d().x;
x3 = bond2.getAtom(0).getPoint2d().x;
x4 = bond2.getAtom(1).getPoint2d().x;
y1 = bond1.getAtom(0).getPoint2d().y;
y2 = bond1.getAtom(1).getPoint2d().y;
y3 = bond2.getAtom(0).getPoint2d().y;
y4 = bond2.getAtom(1).getPoint2d().y;
Line2D.Double line1 = new Line2D.Double(new Point2D.Double(x1, y1), new Point2D.Double(x2, y2));
Line2D.Double line2 = new Line2D.Double(new Point2D.Double(x3, y3), new Point2D.Double(x4, y4));
if (line1.intersectsLine(line2))
{
logger.debug("Two intersecting bonds detected.");
return true;
}
return false;
}
/**
* A little helper class to store pairs of overlapping atoms.
*
*@author steinbeck
*@cdk.created October 1, 2003
*/
public class OverlapPair
{
IChemObject chemObject1 = null;
IChemObject chemObject2 = null;
/**
* Constructor for the OverlapPair object.
*
* @param co1 Description of the Parameter
* @param co2 Description of the Parameter
*/
public OverlapPair(IChemObject co1, IChemObject co2)
{
chemObject1 = co1;
chemObject2 = co2;
}
}
}