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

com.actelion.research.chem.coords.CoordinateInventor Maven / Gradle / Ivy

There is a newer version: 2024.11.2
Show newest version
/*
 * Copyright (c) 1997 - 2016
 * Actelion Pharmaceuticals Ltd.
 * Gewerbestrasse 16
 * CH-4123 Allschwil, Switzerland
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the the copyright holder nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Thomas Sander
 */

package com.actelion.research.chem.coords;

import com.actelion.research.chem.*;

import java.util.*;

public class CoordinateInventor {
	public static final int MODE_SKIP_DEFAULT_TEMPLATES = 1;
	public static final int MODE_REMOVE_HYDROGEN = 2;
	public static final int MODE_KEEP_MARKED_ATOM_COORDS = 4;
	public static final int MODE_PREFER_MARKED_ATOM_COORDS = 8;
	private static final int MODE_CONSIDER_MARKED_ATOMS = MODE_KEEP_MARKED_ATOM_COORDS | MODE_PREFER_MARKED_ATOM_COORDS;
	public static final int MODE_DEFAULT = MODE_REMOVE_HYDROGEN;

	private static final byte FLIP_AS_LAST_RESORT = 1;
	private static final byte FLIP_POSSIBLE = 2;
	private static final byte FLIP_PREFERRED = 3;
	private static final int  PREFERRED_FLIPS = 32;
	private static final int  POSSIBLE_FLIPS = 64;
	private static final int  LAST_RESORT_FLIPS = 128;
	private static final int  TOTAL_FLIPS = PREFERRED_FLIPS + POSSIBLE_FLIPS + LAST_RESORT_FLIPS;

	private static volatile List sDefaultTemplateList;

	private StereoMolecule mMol;
	private long[]		mFFP;
	private Random		mRandom;
	private boolean[]	mAtomHandled;
	private boolean[]	mBondHandled;
	private boolean[]	mAtomIsPartOfCustomTemplate;
	private boolean     mAbsoluteOrientationTemplateFound;  // we just use the first matching template, which is set to define absolute orientation
	private int[]		mUnPairedCharge;
	private int			mMode;
	private List mFragmentList;
	private List mCustomTemplateList;

	private static synchronized void buildDefaultTemplateList() {
		if (sDefaultTemplateList == null)
			sDefaultTemplateList = new InventorDefaultTemplateList();
	}


	/**
	 * Creates an CoordinateInventor, which removes unneeded hydrogen atoms
	 * and creates new atom coordinates for all(!) atoms.
	 * This constructor creates a CoordinateInventor that uses templates
	 * of the InventorDefaultTemplateList to create 3D-projection derived coordinates for
	 * polycyclic structures from these templates (adamantanes, cubane, etc.).
	 */
	public CoordinateInventor () {
		this(MODE_DEFAULT);
		}


	/**
	 * Creates an CoordinateInventor, which removes unneeded hydrogens, if mode flags include
	 * MODE_REMOVE_HYDROGEN. If mode includes MODE_KEEP_MARKED_ATOM_COORDS, then marked atoms
	 * keep their coordinates. If mode includes MODE_PREFER_MARKED_ATOM_COORDS, then coordinates
	 * of marked atoms are changed only, if perfect coordinates are not possible without.
	 * Unless mode includes MODE_SKIP_DEFAULT_TEMPLATES, the CoordinateInventor uses templates
	 * of the InventorDefaultTemplateList to create 3D-projection derived coordinates for
	 * polycyclic structures from these templates (adamantanes, cubane, etc.).
	 * @param mode
	 */
	public CoordinateInventor (int mode) {
		mMode = mode;
		if ((mode & MODE_SKIP_DEFAULT_TEMPLATES) == 0 && sDefaultTemplateList == null)
			buildDefaultTemplateList();
		}


	public void setRandomSeed(long seed) {
		mRandom = new Random(seed);
		}


	/**
	 * A custom template list contains substructures with predefined atom coordinates.
	 * When such a list is provided, and if a molecules contains one of the list's substructures,
	 * then the matching atoms will receive the relative atom coordinates of the provided template,
	 * unless the substructure shares two or more atoms with another earlier found template or
	 * if mode is MODE_????_MARKED_ATOM_COORDS and the substructure match contains two or more
	 * non-marked (and therefore untouchable) atoms. If a template substructure contains an E- or Z-
	 * double bound, then the query feature 'match EZ-parity' should be set.
	 * @param templateList
	 */
	public void setCustomTemplateList(List templateList) {
		mCustomTemplateList = templateList;
		for (InventorTemplate template:templateList)
			template.normalizeCoordinates();
		}

	/**
	 * Creates new atom 2D-coordinates for a molecule or a part of a molecule.
	 * Typically, the molecule has defined TH- and EZ-parities (even if unknown or none), which were not
	 * calculated, but taken from a SMILES or from an IDCode. In these cases setParitiesValid() should have
	 * been called to indicate that a parity calculation is not needed and even would destroy given parities.
	 * New coordinates will correctly reflect E/Z double bond parities, unless the double bond is in a small ring.
	 * If atom parities are available, this call is typically followed by calling mol.setStereoBondsFromParity();
	 * Unneeded explicit hydrogens are removed, if mode includes MODE_REMOVE_HYDROGEN.
	 * The relative orientation of all marked atoms is retained, if mode includes MODE_KEEP_MARKED_ATOM_COORDS.
	 * The relative orientation of all marked atoms is changed as last resort only, if mode includes MODE_PREFER_MARKED_ATOM_COORDS.
	 * If setTemplateList() was called, then any substructures matching any of the templates will be shown
	 * with the relative atom orientation provided with the template. If many molecule's coordinates are invented
	 * with templates, then you should also provide the molecules' fragment fingerprint to speed up template search
	 * using invent(mol, ffp).
	 * @param mol the molecule that gets new 2D coordinates in place
	 */
	public void invent(StereoMolecule mol) {
		invent(mol, null);
		}


	/**
	 * Creates new atom 2D-coordinates for a molecule or a part of a molecule.
	 * Typically, the molecule has defined TH- and EZ-parities (even if unknown or none), which were not
	 * calculated, but taken from a SMILES or from an IDCode. In these cases setParitiesValid() should have
	 * been called to indicate that a parity calculation is not needed and even would destroy given parities.
	 * New coordinates will correctly reflect E/Z double bond parities, unless the double bond is in a small ring.
	 * If atom parities are available, this call is typically followed by calling mol.setStereoBondsFromParity();
	 * Unneeded explicit hydrogens are removed, if mode includes MODE_REMOVE_HYDROGEN.
	 * The relative orientation of all marked atoms is retained, if mode includes MODE_KEEP_MARKED_ATOM_COORDS.
	 * The relative orientation of all marked atoms is changed as last resort only, if mode includes MODE_PREFER_MARKED_ATOM_COORDS.
	 * If setTemplateList() was called, then any substructures matching any of the templates will be shown
	 * with the relative atom orientation provided with the template. If many molecule's coordinates are invented
	 * with templates, then you should also provide the molecules' fragment fingerprint to speed up template search.
	 * @param mol the molecule that gets new 2D coordinates in place
	 * @parem ffp null or fragment fingerprint of the molecule, which is used (if available) for faster template location
	 */
	public void invent(StereoMolecule mol, long[] ffp) {
		boolean paritiesPresent = (mol.getHelperArrayStatus() & Molecule.cHelperParities) != 0;
		int parityState = mol.getHelperArrayStatus() & Molecule.cHelperBitsStereo;

		if (mRandom == null)
			mRandom = new Random();

		if ((mMode & MODE_REMOVE_HYDROGEN) != 0)
			mol.removeExplicitHydrogens(false, false);

		mMol = mol;
		mMol.ensureHelperArrays(Molecule.cHelperRings);

		mFFP = ffp;

		mFragmentList = new ArrayList() {
			@Override
			public boolean add(InventorFragment f) {
				for (InventorFragment ff:this)
					if (ff.equals(f))
						return false;
				return super.add(f);
				}
			};
		mAtomHandled = new boolean[mMol.getAllAtoms()];
		mBondHandled = new boolean[mMol.getAllBonds()];

		mUnPairedCharge = new int[mMol.getAllAtoms()];
		for (int atom=0; atom templateList, int priority) {
		boolean useFFP = (mFFP != null && templateList.size() != 0 && templateList.get(0).getFFP() != null);

		SSSearcher searcher = null;
		SSSearcherWithIndex searcherWithIndex = null;
		if (useFFP) {
			searcherWithIndex = new SSSearcherWithIndex();
			searcherWithIndex.setMolecule(mMol, mFFP);
			}
		else {
			searcher = new SSSearcher();
			searcher.setMolecule(mMol);
			}

		boolean[] atomIsPartOfTemplate = new boolean[mMol.getAtoms()];

		for (InventorTemplate template: templateList) {
			ArrayList matchList = null;
			StereoMolecule templateMol = template.getFragment();
			if (useFFP) {
				searcherWithIndex.setFragment(templateMol, template.getFFP());
				if (searcherWithIndex.findFragmentInMolecule(SSSearcher.cCountModeOverlapping, SSSearcher.cDefaultMatchMode) != 0)
					matchList = searcherWithIndex.getGraphMatcher().getMatchList();
				}
			else {
				searcher.setFragment(templateMol);
				if (searcher.findFragmentInMolecule(SSSearcher.cCountModeOverlapping, SSSearcher.cDefaultMatchMode) != 0)
					matchList = searcher.getMatchList();
				}

			if (matchList != null) {
				for (int[] match:matchList) {
					int templateAtomCount = 0;
					for (int atom:match)
						if (atomIsPartOfTemplate[atom])
							templateAtomCount++;
					if (templateAtomCount <= 1) {
						// we just use the first matching template that is supposed to keep its absolute orientation
						boolean definesAbsoluteOrientation = template.keepAbsoluteOrientation();
						if (mAbsoluteOrientationTemplateFound)
							definesAbsoluteOrientation = false;
						else
							mAbsoluteOrientationTemplateFound = true;

						InventorFragment fragment = new InventorFragment(mMol, match.length, definesAbsoluteOrientation);

						for (int i=0; i 4) {
				InventorFragment f = new InventorFragment(mMol, 1+mMol.getAllConnAtoms(atom), false);

				f.mAtomX[mMol.getAllConnAtoms(atom)] = 0.0;
				f.mAtomY[mMol.getAllConnAtoms(atom)] = 0.0;
				f.mPriority[mMol.getAllConnAtoms(atom)] = 32;
				f.mGlobalAtom[mMol.getAllConnAtoms(atom)] = atom;
				mAtomHandled[atom] = true;

				for (int i=0; i 2) {
					InventorFragment f = new InventorFragment(mMol, members, false);
					int count = 0;
					for (int i=0; i 2)
								break;

							mAtomHandled[alleneAtom[last+1]] = true;
							mBondHandled[mMol.getConnBond(alleneAtom[last], nextIndex)] = true;
							last++;
							} while (mMol.getAtomPi(alleneAtom[last]) == 2
								  && mMol.getAllConnAtoms(alleneAtom[last]) == 2);

						int members = mMol.getAllConnAtoms(alleneAtom[0])
									+ mMol.getAllConnAtoms(alleneAtom[last])
									+ last - 1;
						InventorFragment f = new InventorFragment(mMol, members, false);
						for (int j=0; j<=last; j++) {
							f.mAtomX[j] = j;
							f.mAtomY[j] = 0.0;
							f.mPriority[j] = 64;
							f.mGlobalAtom[j] = alleneAtom[j];
							}

						int current = last+1;
						boolean found = false;
						for (int j=0; j connAtom)
								secondAtomCounts[i] = true;
							f.mAtomX[count] = (i==0) ? -1.0 : 1.0;
							f.mAtomY[count] = (neighbours==0) ? -Math.sin(Math.PI/3) : Math.sin(Math.PI/3);
							f.mAtom[count++] = connAtom;
							mAtomHandled[connAtom] = true;
							mBondHandled[mMol.getConnBond(bondAtom[i], j)] = true;
							neighbours++;
							}
						}
					}

				if ((bondParity == Molecule.cBondParityE) ^ (secondAtomCounts[0] ^ secondAtomCounts[1]))
					for (int i=1; i longestChain.getChainLength())
						longestChain = theChain;
					}
				}

			if (longestChain == null)
				break;

			InventorFragment f = new InventorFragment(mMol, longestChain.getChainLength(), false);
			for (int i=0; i= FIRST_RING_SIZE)
//			printRingStats(f, ringAtom, ringBond);

		int maxBit = (1 << f.size());
		int bondEConstraint = 0;
		int bondZConstraint = 0;
		if (f.size() > 7) {
			for (int i=0; i>>= 1;
				bondZConstraint >>>= 1;
				}
			}

		int ringIndex = f.size()-FIRST_RING_SIZE;
		if (f.size() >= FIRST_RING_SIZE && f.size() <= LAST_RING_SIZE && cBondZList[ringIndex] != null) {
			for (int zList=0; zList>>= 1;
								}

							// We choose the starting turn direction such that we end up
							// with more right turns and, thus, close the ring in clockwise direction.
							boolean wasRightTurn = (rightTurns > f.size() / 2); // create a ring that closes with right turns

							for (int i=1; i>>= 1;
								}
							return;
							}
						if ((bondZList & 1) != 0)
							bondZList |= maxBit;
						bondZList >>>= 1;
						}
					}
				}
			}

			// if not successful so far
		createRegularRingFragment(f, bondEConstraint, bondZConstraint);
		}


	private int getLargeRingBondParity(int[] ringAtom, int[] ringBond, int index) {
		int higherIndex = (index == ringAtom.length-1) ? 0 : index+1;    // second ringAtom index
		int lowerIndex = (index == 0) ? ringAtom.length-1 : index-1;
		int highestIndex = (higherIndex == ringAtom.length-1) ? 0 : higherIndex+1;
		if (mMol.getBondOrder(ringBond[index]) == 2) {
			int bondParity = mMol.getBondParity(ringBond[index]);
			if (bondParity == Molecule.cBondParityEor1
			 || bondParity == Molecule.cBondParityZor2) {
				// translate bond configuration from lowest atom index to ring members
				if (isLowestIndexNeighbour(ringAtom[lowerIndex], ringAtom[index], ringAtom[higherIndex])
				  ^ isLowestIndexNeighbour(ringAtom[highestIndex], ringAtom[higherIndex], ringAtom[index]))
						bondParity = (bondParity == Molecule.cBondParityEor1) ?
								Molecule.cBondParityZor2 : Molecule.cBondParityEor1;
				return bondParity;
				}
			}

		// If the bond is also a member of a small ring
		if (mMol.isSmallRingBond(ringBond[index])) {
			int sharedRing1 = mMol.getRingSet().getSharedRing(ringBond[lowerIndex], ringBond[index]);
			int sharedRing2 = mMol.getRingSet().getSharedRing(ringBond[higherIndex], ringBond[index]);

			// If more than one bond is shared by the large and the small ring,
			// then the first and the last shared bond are E and the ones between Z.
			if (sharedRing1 != -1 || sharedRing2 != -1)
				return sharedRing1 == sharedRing2 ? Molecule.cBondParityZor2 : Molecule.cBondParityEor1;

			// If only this one bond is shared, then we have a Z-configuration
			return Molecule.cBondParityZor2;
			}

		return Molecule.cBondParityNone;
		}


	/**
	 * @param atom
	 * @param rootAtom
	 * @param excludeAtom
	 * @return whether atom has the lowest atom index of rootAtom's neighbours not considering excludeAtom
	 */
	private boolean isLowestIndexNeighbour(int atom, int rootAtom, int excludeAtom) {
		for (int i=0; i>>= 1;
			bondZConstraint >>>= 1;
			}
		System.out.println("ringSize:"+f.size()
				+" E-list:"+Integer.toBinaryString(bondEConstraint)
				+" Z-list:"+Integer.toBinaryString(bondZConstraint));
		}*/


	private InventorChain getSmallestRingFromBond(int bond) {
			// find smallest ring of given bond
		int atom1 = mMol.getBondAtom(0, bond);
		int atom2 = mMol.getBondAtom(1, bond);
		int graphAtom[] = new int[mMol.getAllAtoms()];
		int graphBond[] = new int[mMol.getAllAtoms()];
		int graphLevel[] = new int[mMol.getAllAtoms()];
		int graphParent[] = new int[mMol.getAllAtoms()];
		graphAtom[0] = atom1;
		graphAtom[1] = atom2;
		graphBond[1] = bond;
		graphLevel[atom1] = 1;
		graphLevel[atom2] = 2;
		graphParent[0] = -1;
		graphParent[1] = 0;
		int current = 1;
		int highest = 1;
		while (current <= highest) {
			for (int i=0; i 1) && candidate == atom1) {
					InventorChain theRing = new InventorChain(graphLevel[graphAtom[current]]);
					graphBond[0] = mMol.getConnBond(graphAtom[current], i);
					int index = current;
					for (int j=0; j 0) {
						int handlePreferred = (commonAtoms == 1
											&& getConnAtoms(f1, commonAtom) == 1
											&& getConnAtoms(f2, commonAtom) == 1) ? 0 : 1;

						int joinPriority;
						if (maxF1Priority > maxF2Priority)
							joinPriority = (handlePreferred << 24)
										 + (maxF1Priority << 16)
										 + (maxF2Priority << 8)
										 + commonAtoms;
						else
							joinPriority = (handlePreferred << 24)
										 + (maxF2Priority << 16)
										 + (maxF1Priority << 8)
										 + commonAtoms;

						if (maxJoinPriority < joinPriority) {
							maxJoinPriority = joinPriority;
							maxCommonAtoms = commonAtoms;

							// retain coordinates of fragment with highest priority atom
							maxF1Priority = 0;
							maxF2Priority = 0;
							for (int k=0; k maxF2Priority) {
								maxFragment1 = f1;
								maxFragment2 = f2;
								}
							else {
								maxFragment1 = f2;
								maxFragment2 = f1;
								}
							}
						}
					}
				}

			if (maxJoinPriority == 0)
				break;

			if (maxCommonAtoms == maxFragment1.size())
				mFragmentList.remove(maxFragment1);
			else if (maxCommonAtoms == maxFragment2.size())
				mFragmentList.remove(maxFragment2);
			else
				joinFragments(maxFragment1, maxFragment2, maxCommonAtoms);
			}
		}


	private void joinFragments(InventorFragment f1, InventorFragment f2, int commonAtoms) {
		int[] commonAtom = new int[commonAtoms];
		int count = 0;
		for (int i = 0; i Math.abs(getAngleDif(meanNeighbourAngleF1.mAngle, meanNeighbourAngleF2Flip.mAngle))) {
			f2.rotate(meanX1, meanY1, meanAngleDif.mAngle);
			}
		else {
			f2.flip(meanX1, meanY1, 0.0);
			f2.rotate(meanX1, meanY1, meanAngleDifFlip.mAngle);
			}

		return getMergedFragment(f1, f2, commonAtoms);
		}


	private InventorFragment getMergedFragment(InventorFragment f1, InventorFragment f2, int commonAtoms) {
			// merges all atoms of two fragments into a new one retaining original coordinates
		InventorFragment f = new InventorFragment(mMol, f1.mGlobalAtom.length + f2.mGlobalAtom.length - commonAtoms, f1.mKeepMarkedAtoms | f2.mKeepMarkedAtoms);
		int count = 0;
		for (int i = 0; i0; i--) {	// bubble sort
			for (int j=0; j connAngle[j+1]) {
					double tempAngle = connAngle[j];
					connAngle[j] = connAngle[j+1];
					connAngle[j+1] = tempAngle;
					int tempAtom = connAtom[j];
					connAtom[j] = connAtom[j+1];
					connAtom[j+1] = tempAtom;
					int tempBond = connBond[j];
					connBond[j] = connBond[j+1];
					connBond[j+1] = tempBond;
					}
				}
			}

		connAngle[connAngles] = connAngle[0] + 2 * Math.PI;
		connAtom[connAngles] = connAtom[0];
		connBond[connAngles] = connBond[0];

		double maxAngleDif = -100.0;
		int maxIndex = 0;
		for (int i=0; i 2
			 && mMol.isRingBond(connBond[i])
			 && mMol.isRingBond(connBond[i+1])) {
				int ringSize = getSmallestRingSize(connAtom[i], atom, connAtom[i+1]);
				if (ringSize != 0)
					angleDif -= 100.0 - ringSize;
				}

			if (maxAngleDif < angleDif) {
				maxAngleDif = angleDif;
				maxIndex = i;
				}
			}

		return (connAngle[maxIndex] + connAngle[maxIndex+1]) / 2;
		}


	private double getAngleDif(double angle1, double angle2) {
		double angleDif = angle1 - angle2;
		while (angleDif < -Math.PI)
			angleDif += 2 * Math.PI;
		while (angleDif > Math.PI)
			angleDif -= 2 * Math.PI;
		return angleDif;
		}


	protected static InventorAngle getMeanAngle(InventorAngle[] angle, int noOfAngles) {
		// adds noOfAngles vectors of length=1 with angles angle[i]
		// and returns angle of sum-vector
		// length of sum-vector is criteria for deviation
		double sinSum = 0;
		double cosSum = 0;
		for (int i=0; i 0) ? Math.PI/2 : -Math.PI/2;
		else {
			meanAngle = Math.atan(sinSum/cosSum);
			if (cosSum < 0)
				meanAngle += Math.PI;
			}

		double length = Math.sqrt(sinSum * sinSum + cosSum * cosSum) / noOfAngles;

		return new InventorAngle(meanAngle, length);
		}


	private int getConnAtoms(InventorFragment f, int atom) {
		int connAtoms = 0;
		for (int i=0; i 1)
					 && (mMol.getConnAtoms(mMol.getBondAtom(1, bond)) > 1)
					 && (mMol.getBondParity(bond) == Molecule.cBondParityEor1
					  || mMol.getBondParity(bond) == Molecule.cBondParityZor2)) {
						int[] minConnAtom = new int[2];
						int[] bondAtom = new int[2];
						for (int j=0; j<2; j++) {
							minConnAtom[j] = mMol.getMaxAtoms();
							bondAtom[j] = mMol.getBondAtom(j, bond);
							for (int k=0; k connAtom)
									minConnAtom[j] = connAtom;
								}
							}
	
						double dbAngle = InventorAngle.getAngle(f.mAtomX[f.mGlobalToLocalAtom[bondAtom[0]]],
																f.mAtomY[f.mGlobalToLocalAtom[bondAtom[0]]],
																f.mAtomX[f.mGlobalToLocalAtom[bondAtom[1]]],
																f.mAtomY[f.mGlobalToLocalAtom[bondAtom[1]]]);
						double angle1  = InventorAngle.getAngle(f.mAtomX[f.mGlobalToLocalAtom[minConnAtom[0]]],
																f.mAtomY[f.mGlobalToLocalAtom[minConnAtom[0]]],
																f.mAtomX[f.mGlobalToLocalAtom[bondAtom[0]]],
																f.mAtomY[f.mGlobalToLocalAtom[bondAtom[0]]]);
						double angle2  = InventorAngle.getAngle(f.mAtomX[f.mGlobalToLocalAtom[bondAtom[1]]],
																f.mAtomY[f.mGlobalToLocalAtom[bondAtom[1]]],
																f.mAtomX[f.mGlobalToLocalAtom[minConnAtom[1]]],
																f.mAtomY[f.mGlobalToLocalAtom[minConnAtom[1]]]);
	
						if (((getAngleDif(dbAngle, angle1) < 0)
						   ^ (getAngleDif(dbAngle, angle2) < 0))
						  ^ (mMol.getBondParity(bond) == Molecule.cBondParityZor2)) {
							f.flipOneSide(bond);
							}
						}
					}
				}
			}
		}


	private void optimizeFragments() {
		int[] atomSymRank = calculateAtomSymmetries();

		byte[] bondFlipPriority = new byte[mMol.getAllBonds()];
		locateFlipBonds(bondFlipPriority, atomSymRank);
		for (int bond=0; bond collisionList = f.getCollisionList();
			double minCollisionPanalty = f.getCollisionPanalty();
			InventorFragment minCollisionFragment = new InventorFragment(f);

			int lastBond = -1;
			for (int flip=0; flip= FLIP_POSSIBLE)
							availableBond[availableBonds++] = bondSequence[i];
					}
				else {
					for (int i=1; i= FLIP_AS_LAST_RESORT)
							availableBond[availableBonds++] = bondSequence[i];
					}

				if (availableBonds != 0) {
					int theBond = availableBond[0];
					if (availableBonds > 1) {
						do {	// don't rotate 2 times around same bond
							theBond = availableBond[mRandom.nextInt(availableBonds)];
							} while (theBond == lastBond);
						}

					if (theBond != lastBond) {
						lastBond = theBond;
		
						f.flipOneSide(theBond);

							// getCollisionList() is necessary to update collision panalty
						collisionList = f.getCollisionList();
						if (minCollisionPanalty > f.getCollisionPanalty()) {
							minCollisionPanalty = f.getCollisionPanalty();
							minCollisionFragment = new InventorFragment(f);
							}
						}
					}
				}

			mFragmentList.set(fragmentNo, minCollisionFragment);
				// finished optimization by rotating around single bonds

				// starting optimization by moving individual atoms
/*
double avbl = mMol.getAverageBondLength();
for (int i=0; i currentRank && theRank < nextAvailableRank)
						nextAvailableRank = theRank;
					}
				currentRank = nextAvailableRank;
				} while (nextAvailableRank != 9999);
			}
		}


	private int[] getShortestConnection(int atom1, int atom2) {
		int graphAtom[] = new int[mMol.getAllAtoms()];
		int graphBond[] = new int[mMol.getAllAtoms()];
		int graphLevel[] = new int[mMol.getAllAtoms()];
		int graphParent[] = new int[mMol.getAllAtoms()];
		graphAtom[0] = atom2;
		graphLevel[atom2] = 1;
		graphParent[0] = -1;
		int current = 0;
		int highest = 0;
		while (current <= highest) {
			for (int i=0; i 2) {
					boolean symmetricEndFound = true;
					int connSymRank = -1;
					for (int j=0; jj; k--)
					connRank[k] = connRank[k-1];
				connRank[j] = rank;
				}

			int neighbours = mMol.getAllConnAtoms(atom);
			baseValue[atom].init(atom);
			baseValue[atom].add(atomBits, symRank[atom]);
			baseValue[atom].add((maxConnAtoms - neighbours)*(atomBits + 1), 0);
			for (int i=0; i associationList = createMetalBondAssociations();
		while (associationList != null) {
			FragmentAssociation association = getMaxPriorityAssociation(associationList);
			joinAssociatedFragments(association, 1.2);
			associationList = createMetalBondAssociations();
			}
		}

	private void joinChargedFragments() {
		FragmentAssociation association = createChargeAssociation();
		while (association != null) {
			joinAssociatedFragments(association, 1.5);
			association = createChargeAssociation();
			}
		}

	private void joinRemainingFragments() {
		FragmentAssociation association = createDisconnectedAssociation();
		while (association != null) {
			joinAssociatedFragments(association, 1.8);
			association = createDisconnectedAssociation();
			}
		}

	private void joinAssociatedFragments(FragmentAssociation association, double minDistance) {
		association.arrange(minDistance, (mMode & MODE_CONSIDER_MARKED_ATOMS) != 0);
		InventorFragment mergedFragment = getMergedFragment(association.getFragment(0), association.getFragment(1), 0);
		updateFragmentList(association.getFragment(0), association.getFragment(1), mergedFragment);
		}

	private FragmentAssociation getMaxPriorityAssociation(ArrayList associationList) {
		int maxPriority = 0;
		FragmentAssociation maxFA = null;
		for (FragmentAssociation association:associationList) {
			if (maxPriority < association.getPriority()) {
				maxPriority = association.getPriority();
				maxFA = association;
				}
			}
		return maxFA;
		}

	private ArrayList createMetalBondAssociations() {
		ArrayList associationList = null;
		FragmentAssociation[][] fa = null;
		for (int bond=0; bond f2) {	// f1 must be the smaller one (and potentially the core fragment with fixed coords)
						int temp = f1;
						f1 = f2;
						f2 = temp;
						temp = atomIndex1;
						atomIndex1 = atomIndex2;
						atomIndex2 = temp;
						}
					if (fa == null)
						fa = new FragmentAssociation[mFragmentList.size()][];
					if (fa[f2] == null)
						fa[f2] = new FragmentAssociation[f2];
					if (fa[f2][f1] != null)
						fa[f2][f1].add(atomIndex1, atomIndex2);
					else {
						fa[f2][f1] = new FragmentAssociation(mFragmentList.get(f1), mFragmentList.get(f2), atomIndex1, atomIndex2);
						if (associationList == null)
							associationList = new ArrayList();
						associationList.add(fa[f2][f1]);
						}
					}
				}
			}

		return associationList;
		}

	private FragmentAssociation createChargeAssociation() {
		ArrayList negChargeList = new ArrayList();
		ArrayList posChargeList = new ArrayList();
		ArrayList chargeList = new ArrayList();

		for (InventorFragment f:mFragmentList) {
			int fragmentCharge = 0;
			chargeList.clear();
			for (int i=0; i() {
					//@Override Annotation incompatible with 1.5
					public int compare(InventorCharge o1, InventorCharge o2) {
						int c1 = Math.abs(o1.charge);
						int c2 = Math.abs(o2.charge);
						return c1 < c2 ? -1 : c1 == c2 ? 0 : 1;
						}
					});
				for (InventorCharge ic:chargeList) {
					if (fragmentCharge * ic.charge > 0) {	// charges have same sign
						int charge = (Math.abs(fragmentCharge) >= Math.abs(ic.charge)) ? ic.charge : fragmentCharge;
						fragmentCharge -= charge;
						(charge < 0 ? negChargeList : posChargeList).add(new InventorCharge(f, ic.atom, charge));
						if (fragmentCharge == 0)
							break;
						}
					}
				}
			}

		if (negChargeList.size() == 0 || posChargeList.size() == 0)
			return null;

		// with positive charges we have the large fragments first
		Collections.sort(posChargeList, new Comparator() {
			//@Override Annotation incompatible with 1.5
			public int compare(InventorCharge o1, InventorCharge o2) {
				int c1 = o1.fragment.size();
				int c2 = o1.fragment.size();
				return c1 < c2 ? 1 : c1 == c2 ? 0 : -1;
			}
		});

		// with negative charges we have the small o1.fragments first
		Collections.sort(negChargeList, new Comparator() {
			//@Override Annotation incompatible with 1.5
			public int compare(InventorCharge o1, InventorCharge o2) {
				int c1 = o1.fragment.size();
				int c2 = o1.fragment.size();
				return c1 < c2 ? -1 : c1 == c2 ? 0 : 1;
			}
		});

		// we combine preferably small with large
		for (InventorCharge pc:posChargeList) {
			for (InventorCharge nc : negChargeList) {
				if (pc.charge == -nc.charge) {
					mUnPairedCharge[pc.fragment.getGlobalAtom(pc.atom)] -= pc.charge;
					mUnPairedCharge[nc.fragment.getGlobalAtom(nc.atom)] -= nc.charge;
					return new FragmentAssociation(pc.fragment, nc.fragment, pc.atom, nc.atom);
					}
				}
			}

		for (InventorCharge pc:posChargeList) {
			for (InventorCharge nc : negChargeList) {
				if (pc.charge > -nc.charge) {
					mUnPairedCharge[pc.fragment.getGlobalAtom(pc.atom)] += nc.charge;
					mUnPairedCharge[nc.fragment.getGlobalAtom(nc.atom)] -= nc.charge;
					return new FragmentAssociation(pc.fragment, nc.fragment, pc.atom, nc.atom);
					}
				}
			}

		for (InventorCharge pc:posChargeList) {
			for (InventorCharge nc : negChargeList) {
				if (pc.charge < -nc.charge) {
					mUnPairedCharge[pc.fragment.getGlobalAtom(pc.atom)] -= pc.charge;
					mUnPairedCharge[nc.fragment.getGlobalAtom(nc.atom)] += pc.charge;
					return new FragmentAssociation(pc.fragment, nc.fragment, pc.atom, nc.atom);
					}
				}
			}

		return null;
		}

	private FragmentAssociation createDisconnectedAssociation() {
		if (mFragmentList.size() < 2)
			return null;

		return new FragmentAssociation(mFragmentList.get(0), mFragmentList.get(1));
		}

	/* ingenious solution to whether we have a cyclic relationship between fragments and metal bond associations
	private boolean isCyclic(FragmentAssociation[][] fa) {
		for (int i=1; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy