org.xmlcml.cml.tools.RingNucleus Maven / Gradle / Ivy
/**
* Copyright 2011 Peter Murray-Rust et. al.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.xmlcml.cml.tools;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import org.xmlcml.cml.base.AbstractTool;
import org.xmlcml.cml.element.CMLAtom;
import org.xmlcml.cml.element.CMLAtomSet;
import org.xmlcml.cml.element.CMLBond;
import org.xmlcml.cml.element.CMLBondSet;
import org.xmlcml.cml.element.CMLMolecule;
import org.xmlcml.euclid.Angle;
import org.xmlcml.euclid.Real2;
import org.xmlcml.euclid.Transform2;
import org.xmlcml.euclid.Vector2;
import org.xmlcml.molutil.ChemicalElement.AS;
/**
* tool to support a ring. not fully developed
*
* @author pmr
*
*/
public class RingNucleus extends AbstractTool implements Comparable {
final static Logger LOG = Logger.getLogger(RingNucleus.class);
private CMLAtomSet atomSet;
private CMLBondSet bondSet;
private List ringList = null;
private List junctionList = null;
private Map bridgeAtomMap = null;
private MoleculeLayout moleculeDraw;
private List connectedNucleusList;
private List sproutList;
private List remoteSproutList;
private List coordinateList;
// private RingNucleus() {
// init();
// //
// }
/**
* @return the ringList
*/
public List getRingList() {
return ringList;
}
/**
* @return the sproutList
*/
public List getSproutList() {
return sproutList;
}
/** constructor.
* copies reference to sets
*
* @param atomSet
* @param bondSet
*/
public RingNucleus(CMLAtomSet atomSet, CMLBondSet bondSet) {
this.atomSet = atomSet;
this.bondSet = bondSet;
init();
}
private void init() {
bridgeAtomMap = new HashMap();
// sproutList = new ArrayList();
}
/**
* @return the atomSet
*/
public CMLAtomSet getAtomSet() {
return atomSet;
}
/**
* @return the bondSet
*/
public CMLBondSet getBondSet() {
return bondSet;
}
/** get list of rings.
* not necessarily SSSR
* @return list of rings
*/
public List getRings() {
if (ringList == null) {
ringList = new ArrayList();
Set usedAtoms = new HashSet();
CMLAtom root = atomSet.getAtom(0);
CMLAtom parentAtom = null;
Map atomToParentMap = new HashMap();
Set linkBondSet = new HashSet();
expand(root, parentAtom, usedAtoms, atomToParentMap, linkBondSet);
for (CMLBond bond : linkBondSet) {
Ring ring = getRing(bond, atomToParentMap);
ringList.add(ring);
}
Collections.sort(ringList);
}
return ringList;
}
private void expand(CMLAtom atom, CMLAtom parentAtom,
Set usedAtoms, Map atomToParentMap,
Set linkBondSet) {
usedAtoms.add(atom);
atomToParentMap.put(atom, parentAtom);
List ligandAtomList = atom.getLigandAtoms();
CMLMolecule molecule = atom.getMolecule();
for (int i = 0; i < ligandAtomList.size(); i++) {
CMLAtom ligandAtom = ligandAtomList.get(i);
if (!atomSet.contains(ligandAtom)) {
// skip atoms outside set
} else if (ligandAtom.equals(parentAtom)) {
// skip existing bond
} else if (usedAtoms.contains(ligandAtom)) {
CMLBond linkBond = molecule.getBond(atom, ligandAtom);
linkBondSet.add(linkBond);
// already treated
} else {
expand(ligandAtom, atom, usedAtoms, atomToParentMap, linkBondSet);
}
}
}
private Ring getRing(CMLBond bond, Map atomToParentMap) {
CMLAtomSet atomSet0 = getAncestors(bond.getAtom(0), atomToParentMap);
CMLAtomSet atomSet1 = getAncestors(bond.getAtom(1), atomToParentMap);
CMLBondSet bondSet0 = getAncestors1(bond.getAtom(0), atomToParentMap, atomSet1);
CMLBondSet bondSet1 = getAncestors1(bond.getAtom(1), atomToParentMap, atomSet0);
CMLBondSet mergedBondSet = bondSet0.symmetricDifference(bondSet1);
mergedBondSet.addBond(bond);
Ring ring = new Ring(mergedBondSet.getAtomSet(), mergedBondSet);
return ring;
}
private CMLAtomSet getAncestors(CMLAtom atom, Map atomToParentMap) {
CMLAtomSet newAtomSet = new CMLAtomSet();
while (true) {
atom = (CMLAtom) atomToParentMap.get(atom);
if (atom == null) {
break;
}
newAtomSet.addAtom(atom);
if (atomSet != null && atomSet.contains(atom)) {
break;
}
}
return newAtomSet;
}
private CMLBondSet getAncestors1(CMLAtom atom, Map atomToParentMap, CMLAtomSet atomSet) {
CMLMolecule molecule =atom.getMolecule();
CMLBondSet newBondSet = new CMLBondSet();
while (true) {
CMLAtom atom1 = (CMLAtom) atomToParentMap.get(atom);
if (atom1 == null) {
break;
}
CMLBond bond = molecule.getBond(atom, atom1);
if (newBondSet.contains(bond)) {
break;
}
newBondSet.addBond(bond);
atom = atom1;
}
return newBondSet;
}
/** get set of smallest rings.
* crude
* @param update replace current list
* @return list of rings
*/
public List getSetOfSmallestRings(boolean update) {
getRings();
List newList = new ArrayList();
for (Ring ring : ringList) {
newList.add(ring);
}
if (newList.size() > 1) {
boolean change = true;
while (change) {
for (int i = 0; i < newList.size(); i++) {
Ring ring = newList.get(i);
change = reduceRingSizes(ring, newList);
}
}
}
if (update) {
ringList = newList;
}
Collections.sort(newList);
return newList;
}
/**
* @param ring
*/
private boolean reduceRingSizes(Ring ring, List newList) {
boolean change = false;
for (int i = 0; i < newList.size(); i++) {
Ring target = newList.get(i);
if (target == ring) {
continue;
}
CMLBondSet newBondSet = target.getBondSet().symmetricDifference(ring.getBondSet());
if (newBondSet.size() < target.size()) {
// LOG.debug(""+newBondSet.size()+" < "+target.size());
Ring newRing = new Ring(newBondSet);
newList.set(i, newRing);
change = true;
}
}
return change;
}
/** get list of junctions
*
* @return list
*/
public List getJunctions() {
if (junctionList == null) {
junctionList = new ArrayList();
// do this anyway
getSetOfSmallestRings(true);
for (int i = 0; i < ringList.size()-1; i++) {
Ring ringi = ringList.get(i);
for (int j = i+1; j < ringList.size(); j++) {
Ring ringj = ringList.get(j);
Junction junction = Junction.createJunction(ringi, ringj);
if (junction != null) {
junctionList.add(junction);
add(junction.getBridgeAtomList().get(0));
add(junction.getBridgeAtomList().get(1));
}
}
}
}
return junctionList;
}
private void add(BridgeAtom bridgeAtom) {
CMLAtom atom = bridgeAtom.getAtom();
BridgeAtom existingBridgeAtom = bridgeAtomMap.get(atom);
if (existingBridgeAtom == null) {
existingBridgeAtom = bridgeAtom;
bridgeAtomMap.put(atom, existingBridgeAtom);
}
// add rings to existing rings
if (existingBridgeAtom != bridgeAtom) {
for (Ring ring : bridgeAtom.getRingSet()) {
existingBridgeAtom.addRing(ring);
}
existingBridgeAtom.addJunction(bridgeAtom.getJunctionList().get(0));
}
}
/** layout starting with this nucleus.
*
* @param sprout
*/
public void layout(Sprout sprout, int cycles) {
if (cycles-- <= 0) {
throw new RuntimeException("layout recurses too deeply");
}
Real2 oldSproutVector = null;
Real2 oldRingXY2 = null;
Real2 oldFirstXY2 = null;
if (sprout != null) {
oldRingXY2 = sprout.getRingAtom().getXY2();
oldFirstXY2 = sprout.getFirstAtom().getXY2();
if (oldFirstXY2 == null) {
System.err.println("null sprout vector");
return;
} else {
oldSproutVector = oldRingXY2.subtract(oldFirstXY2);
}
}
this.add2DCoordinates();
if (sprout != null && oldSproutVector != null) {
Real2 sproutVector = sprout.getSproutVector();
if (sproutVector != null) {
Angle a = new Vector2(sproutVector).getAngleMadeWith(new Vector2(oldSproutVector));
Transform2 transform2 = new Transform2(a.plus(new Angle(Math.PI)));
atomSet.transform(transform2);
Real2 delta2 = oldRingXY2.subtract(sprout.getRingAtom().getXY2());
atomSet.translate2D(delta2);
}
}
getSproutList(((MoleculeDisplay)moleculeDraw.getAbstractDisplay()).isOmitHydrogens());
for (Sprout otherSprout : sproutList) {
if (otherSprout != sprout) {
Chain chain = moleculeDraw.getSproutMap().get(otherSprout);
try {
chain.layout(otherSprout, cycles);
} catch (RuntimeException e) {
// cut down on stack print
if (e.getMessage().equals("layout recurses too deeply")) {
e = new RuntimeException("layout recurses too deeply");
}
throw e;
}
}
}
}
/** add 2D coordinates
*/
public void add2DCoordinates() {
getSetOfSmallestRings(true);
this.getJunctions();
if (ringList.size() == 0) {
LOG.error("ring nucleus has no rings");
} else {
// get largest ring
Collections.sort(ringList);
Collections.reverse(ringList);
List oldRingList = new ArrayList();
for (Ring ring : ringList) {
oldRingList.add(ring);
}
List newRingList = new ArrayList();
// add first ring
Ring currentRing = ringList.get(0);
currentRing.calculate2DCoordinates(moleculeDraw);
currentRing.updateCoordinates();
oldRingList.remove(currentRing);
newRingList.add(currentRing);
Junction junction = null;
// findSprouts(true);
while (true) {
junction = findNextJunctionUpdateListsAdd2DCoordinates(
oldRingList, newRingList);
if (junction == null) {
break;
}
}
if (oldRingList.size() > 0) {
throw new RuntimeException("Undrawn rings ");
}
}
}
/**
* @param remainingRingList
* @param growingRingList
* @param change
* @return junction or null
* @throws RuntimeException
*/
private Junction findNextJunctionUpdateListsAdd2DCoordinates(List remainingRingList, List growingRingList) throws RuntimeException {
int commonAtoms = 0;
Junction junction0 = null;
Ring ringToBeAdded0 = null;
Ring existingRing0 = null;
for (Ring existingRing : growingRingList) {
for (Ring nextRemainingRing : remainingRingList) {
List junctionList = nextRemainingRing.getJunctionList(existingRing);
if (junctionList == null) {
continue;
} else if (junctionList.size() > 1) {
throw new RuntimeException("Cannot layout ring pair with multiple junctions");
}
Junction junction = junctionList.get(0);
if (junction.getCommonAtomList().size() > 2) {
LOG.debug("complex junction");
}
// record largest junction
if (commonAtoms < atomSet.size()) {
junction0 = junction;
ringToBeAdded0 = nextRemainingRing;
existingRing0 = existingRing;
commonAtoms = atomSet.size();
}
}
}
// did we find any junctions?
if (junction0 != null) {
// for (CMLAtom atom : existingRing0.getCyclicAtomList()) {
// LOG.debug("JUNCT: "+atom.getElementType()+"/"+atom.getXY2());
// }
try {
calculate2DCoordinates(junction0, ringToBeAdded0, existingRing0, moleculeDraw);
ringToBeAdded0.updateCoordinates();
growingRingList.add(ringToBeAdded0);
remainingRingList.remove(ringToBeAdded0);
} catch (RuntimeException e) {
throw e;
}
}
return junction0;
}
private void calculate2DCoordinates(
Junction junction, Ring ringToBeAdded, Ring existingRing, MoleculeLayout moleculeDraw)
throws RuntimeException {
ringToBeAdded.calculate2DCoordinates(moleculeDraw);
List junctionCommonAtomList = junction.getCommonAtomList();
int commonAtomCount = junctionCommonAtomList.size();
CMLAtom tailAtom = junctionCommonAtomList.get(0);
CMLAtom leadAtom = junctionCommonAtomList.get(commonAtomCount-1);
// distance between existing bridge atoms
Real2 oldVector = leadAtom.getXY2().subtract(tailAtom.getXY2());
Real2 tailXY2 = ringToBeAdded.getAtomCoordMap().get(tailAtom);
Real2 leadXY2 = ringToBeAdded.getAtomCoordMap().get(leadAtom);
// and distance in initial new ring
Real2 newVector = leadXY2.subtract(tailXY2);
// get scale and scale new ring to be of correct size
double scale = oldVector.getLength() / newVector.getLength();
ringToBeAdded.multiplyCoordMap(scale);
tailXY2 = ringToBeAdded.getAtomCoordMap().get(tailAtom);
leadXY2 = ringToBeAdded.getAtomCoordMap().get(leadAtom);
// new vector should be of same length as old one
newVector = leadXY2.subtract(tailXY2);
// rotate new ring so old and new coordinates of bridge atoms
// are aligned.
Angle angle = new Vector2(newVector).getAngleMadeWith(new Vector2(oldVector));
Transform2 transform = new Transform2(angle);
ringToBeAdded.transformBy(transform);
// now find if the new ring is oriented "away" from old ring
Sprout tailSprout = getSprout(ringToBeAdded, tailAtom);
Sprout leadSprout = getSprout(ringToBeAdded, leadAtom);
if (tailSprout == null || leadSprout == null) {
LOG.error("Null sprout");
// throw new RuntimeException("Null sprout - cannot draw");
} else {
//resultant sprout from old ring
Real2 oldRingMedianSprout = tailSprout.getSproutVector().plus(leadSprout.getSproutVector());
CMLAtom nextLeadAtom = getNextNewRingAtom(ringToBeAdded, existingRing, leadAtom);
CMLAtom nextTailAtom = getNextNewRingAtom(ringToBeAdded, existingRing, tailAtom);
Real2 nextTailXY2 = ringToBeAdded.getAtomCoordMap().get(nextTailAtom);
Real2 nextLeadXY2 = ringToBeAdded.getAtomCoordMap().get(nextLeadAtom);
Real2 nextLeadVector = leadXY2.subtract(nextLeadXY2);
Real2 nextTailVector = tailXY2.subtract(nextTailXY2);
//resultant sprout from new ring
Real2 newRingMedianSprout = nextLeadVector.plus(nextTailVector);
angle = new Vector2(oldRingMedianSprout).getAngleMadeWith(
new Vector2(newRingMedianSprout));
double angleRad = Math.abs(angle.getRadian());
// if point in same direction flip the ring
if (angleRad < Math.PI/2) {
Transform2 tt = Transform2.flipAboutVector(oldVector);
ringToBeAdded.transformBy(tt);
}
// and translate to merge the rings
Real2 delta = leadAtom.getXY2().subtract(leadXY2);
ringToBeAdded.translateCoordMap(delta);
}
}
private CMLAtom getNextNewRingAtom(
Ring ringToBeAdded, Ring existingRing, CMLAtom ringAtom) {
List ligands = ringAtom.getLigandAtoms();
CMLAtom nextAtom = null;
for (CMLAtom ligand : ligands) {
if (ringToBeAdded.getAtomSet().contains(ligand) &&
!existingRing.getAtomSet().contains(ligand)) {
nextAtom = ligand;
break;
}
}
return nextAtom;
}
/**
* @param ringToBeAdded
* @param ringAtom
* @return sprout
*/
private Sprout getSprout(Ring ringToBeAdded, CMLAtom ringAtom) {
Sprout sprout = null;
CMLBond sproutBond = null;
List ligands = ringAtom.getLigandAtoms();
List ligandBonds = ringAtom.getLigandBonds();
for (int i = 0; i < ligands.size(); i++) {
CMLAtom ligand = ligands.get(i);
if (ligand.getXY2() == null &&
ringToBeAdded.getAtomSet().contains(ligand)) {
sproutBond = ligandBonds.get(i);
}
}
if (sproutBond != null) {
sprout = new Sprout(ringAtom, sproutBond, this);
}
return sprout;
}
/**
* @param omitHydrogens
*/
public void findSprouts(boolean omitHydrogens) {
sproutList = new ArrayList();
for (CMLAtom atom : this.atomSet.getAtoms()) {
List ligandBondList = atom.getLigandBonds();
for (CMLBond ligandBond : ligandBondList) {
if (!bondSet.contains(ligandBond)) {
CMLAtom otherAtom = ligandBond.getOtherAtom(atom);
if (!(omitHydrogens && AS.H.equals(otherAtom.getElementType()))) {
Sprout sprout = new Sprout(atom, ligandBond, this);
sproutList.add(sprout);
}
}
}
}
}
/** debug.
*/
public void debug() {
this.getBondSet().debug();
}
/**
* @return the connectedNucleusList
*/
public List getConnectedNucleusList() {
if (connectedNucleusList == null) {
getRemoteSproutList();
}
return connectedNucleusList;
}
/**
* @return the junctionList
*/
public List getJunctionList() {
return junctionList;
}
/**
* @param omitHydrogens
* @return the sproutList
*/
public List getSproutList(boolean omitHydrogens) {
if (sproutList == null) {
findSprouts(omitHydrogens);
}
return sproutList;
}
/**
* @return sprouts
*/
public List getRemoteSproutList() {
if (remoteSproutList == null) {
if (sproutList == null) {
throw new RuntimeException("calculate sprouts first");
}
remoteSproutList = new ArrayList();
for (Sprout sprout : sproutList) {
Chain chain = sprout.getChain();
if (chain != null) {
for (Sprout remoteSprout : chain.getSproutList()) {
if (!sproutList.contains(remoteSprout)) {
remoteSproutList.add(remoteSprout);
}
}
}
}
}
return remoteSproutList;
}
/**
* @return ring count
*/
public int size() {
return this.getRings().size();
}
/**
* first sorts on ring count, then compares ordered rings
* @param ringNucleus
* @return -1 0 1
*/
public int compareTo(RingNucleus ringNucleus) {
int result = 0;
if (this.size() != ringNucleus.size()) {
result = (this.size() < ringNucleus.size()) ? -1 : 1;
} else {
List otherRings = ringNucleus.getRings();
for (int i = 0; i < ringList.size(); i++) {
result = ringList.get(i).compareTo(otherRings.get(i));
if (result != 0) {
break;
}
}
}
return result;
}
/**
* @return the moleculeDraw
*/
public MoleculeLayout getMoleculeDraw() {
return moleculeDraw;
}
/**
* @param moleculeDraw the moleculeDraw to set
*/
public void setMoleculeDraw(MoleculeLayout moleculeDraw) {
this.moleculeDraw = moleculeDraw;
}
/**
* @return the coordinateList
*/
public List getCoordinateList() {
return coordinateList;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy