DepictUtils.h

Go to the documentation of this file.
00001 //
00002 //  Copyright (C) 2003-2010 Greg Landrum and Rational Discovery LLC
00003 //
00004 //   @@ All Rights Reserved @@
00005 //  This file is part of the RDKit.
00006 //  The contents are covered by the terms of the BSD license
00007 //  which is included in the file license.txt, found at the root
00008 //  of the RDKit source tree.
00009 //
00010 #ifndef _RD_DEPICT_UTILS_H_
00011 #define _RD_DEPICT_UTILS_H_
00012 
00013 // REVIEW: remove extra headers here
00014 #include <RDGeneral/types.h>
00015 #include <GraphMol/RDKitBase.h>
00016 #include <GraphMol/RWMol.h>
00017 #include <GraphMol/ROMol.h>
00018 #include <Geometry/Transform2D.h>
00019 #include <Geometry/point.h>
00020 #include <queue>
00021 
00022 namespace RDDepict {
00023   extern double BOND_LEN;
00024   extern double COLLISION_THRES;
00025   extern double BOND_THRES;
00026   extern double ANGLE_OPEN;
00027   extern unsigned int MAX_COLL_ITERS;
00028   extern double HETEROATOM_COLL_SCALE;
00029   extern unsigned int NUM_BONDS_FLIPS;
00030 
00031   typedef std::vector<const RDGeom::Point2D *> VECT_C_POINT;
00032   
00033   typedef std::pair<int, int> PAIR_I_I;
00034   typedef std::vector<PAIR_I_I> VECT_PII;
00035   struct gtIIPair {
00036     bool operator() ( const PAIR_I_I &pd1, const PAIR_I_I &pd2) const {
00037       return pd1.first > pd2.first;
00038     }
00039   };
00040 
00041   typedef std::priority_queue<PAIR_I_I, VECT_PII, gtIIPair> PR_QUEUE;
00042 
00043   typedef std::pair<double, PAIR_I_I> PAIR_D_I_I;
00044   typedef std::list<PAIR_D_I_I> LIST_PAIR_DII;
00045 
00046 
00047   //! Some utility functions used in generating 2D coordinates
00048   
00049   //! Embed a ring as a convex polygon in 2D
00050   /*!
00051     The process here is very straightforward:
00052 
00053     We take the center of the ring to lie at the origin, so put the first 
00054     point at the origin and then sweep
00055     anti-clockwise by an angle A = 360/n for the next point.
00056 
00057     The length of the arm (l) we want to sweep is easy to compute given the 
00058     bond length (b) we want to use for each bond in the ring (for now
00059     we will assume that this bond legnth is the same for all bonds in the ring:
00060 
00061     l = b/sqrt(2*(1 - cos(A)) 
00062 
00063     the above formula derives from the triangle formula, where side 'c' is given
00064     in terms of sides 'a' and 'b' as:
00065 
00066     c = a^2 + b^2 - 2.a.b.cos(A) 
00067 
00068     where A is the angle between a and b
00069    */
00070   RDGeom::INT_POINT2D_MAP embedRing(const RDKit::INT_VECT &ring);
00071 
00072   void transformPoints(RDGeom::INT_POINT2D_MAP &nringCor, const RDGeom::Transform2D &trans);
00073 
00074   //! Find a point that bisects the angle at rcr
00075   /*!
00076     The new point lies between nb1 and nb2. The line (rcr, newPt) bisects the angle
00077     'ang' at rcr
00078   */
00079   RDGeom::Point2D computeBisectPoint(const RDGeom::Point2D &rcr, 
00080                                      double ang, const RDGeom::Point2D &nb1,
00081                                      const RDGeom::Point2D &nb2);
00082 
00083   //! Reflect a set of point through a the line joining two point
00084   /*!
00085     ARGUMENTS:
00086     \param coordMap       a map of <int, point2D> going from atom id to current
00087                           coordinates of the points that need to be reflected:
00088                           The coordinates are overwritten
00089     \param loc1           the first point of the line that is to be used as a mirror
00090     \param loc2           the second point of the line to be used as a mirror
00091    */
00092   void reflectPoints(RDGeom::INT_POINT2D_MAP &coordMap, const RDGeom::Point2D &loc1,
00093                    const RDGeom::Point2D &loc2);
00094 
00095   RDGeom::Point2D reflectPoint(const RDGeom::Point2D &point, const RDGeom::Point2D &loc1,
00096                                const RDGeom::Point2D &loc2);
00097 
00098   //! Set the neighbors yet to added to aid such that the atoms with the most subs fall on opposite sides
00099   /*!
00100     Ok this needs some explanation
00101     - Let A, B, C, D be the substituent on the central atom X (given
00102       by atom index aid)
00103     - also let A be the atom that is already embedded
00104     - Now we want the order in which the remaining neighbors B,C,D are
00105       added to X such that the atoms with atom with largest number of
00106       substituent fall on opposite sides of X so as to minimize atom
00107       clashes later in the depiction
00108 
00109     E.g. let say we have the following situation
00110 <pre>    
00111             B
00112          |  |  
00113          A--X--C
00114          |  |
00115           --D--
00116             |
00117 </pre>    
00118     In this case the the number substituent of A, B, C, D are 3, 1, 1,
00119     4 respectively so want to A and D to go opposite sides and so that
00120     we draw
00121 <pre>    
00122             B
00123          |  |  |
00124          A--X--D--
00125          |  |  |
00126             C
00127 </pre>    
00128     And the correct ordering of the neighbors is B,D,C
00129   */
00130   RDKit::INT_VECT setNbrOrder(unsigned int aid, const RDKit::INT_VECT &nbrs, const RDKit::ROMol &mol);
00131 
00132   //! \brief From a given set of rings find the ring the largest common elements with other rings
00133   /*
00134     Bit of a weird function - this is typically called once we have embedded some of the 
00135     rings in a fused system and we are looking for the ring that must be embedded (or merged)
00136     next. The heuristic used here is to pick the rings with the maximum number of atoms
00137     in common with the rings that are already embedded.
00138     
00139     \param doneRings    a vertor of ring IDs that have been embedded already
00140     \param fusedRings   list of all the rings in the the fused system
00141     \param nextId       this is where the ID for the next ring is written
00142     
00143     \return list of atom ids that are common
00144   */
00145   RDKit::INT_VECT findNextRingToEmbed(const RDKit::INT_VECT &doneRings, 
00146                                       const RDKit::VECT_INT_VECT &fusedRings, 
00147                                       int &nextId);
00148 
00149   typedef std::pair<int,int> INT_PAIR;
00150   typedef std::vector<INT_PAIR> INT_PAIR_VECT;
00151   typedef INT_PAIR_VECT::const_iterator INT_PAIR_VECT_CI;
00152   
00153   typedef std::pair<double, INT_PAIR> DOUBLE_INT_PAIR;
00154   
00155   //! Sort a list of atoms by their  CIP rank 
00156   /*!
00157     \param mol        molecule of interest
00158     \param commAtms   atoms that need to be ranked
00159     \param ascending  sort to an ascending order or a descending order
00160   */
00161   template<class T> T rankAtomsByRank(const RDKit::ROMol &mol, const T &commAtms,
00162                                       bool ascending=true);
00163   
00164    
00165   //! computes a subangle for an atom of given hybridization and degree
00166   /*!
00167     \param degree the degree of the atom (number of neighbors)
00168     \param htype  the atom's hybridization
00169 
00170     \return the subangle (in radians)
00171   */
00172   inline double computeSubAngle(unsigned int degree, RDKit::Atom::HybridizationType htype) {
00173     double angle = M_PI;
00174     switch (htype) {
00175     case RDKit::Atom::SP3 :
00176       if (degree == 4) {
00177         angle = M_PI/2;
00178       } else {
00179         angle = 2*M_PI/3;
00180       }
00181       break;
00182     case RDKit::Atom::SP2 :
00183       angle = 2*M_PI/3;
00184       break;
00185     default:
00186       angle = 2.*RDKit::PI/degree;
00187     }
00188     return angle;
00189   }
00190   
00191   //! computes the rotation direction between two vectors
00192   /*!
00193 
00194     Let:
00195 
00196        v1 = loc1 - center
00197 
00198        v2 = loc2 - center
00199 
00200     If remaining angle(v1, v2) is < 180 and corss(v1, v2) > 0.0 then the rotation dir is +1.0
00201 
00202     else if remAngle(v1, v2) is > 180 and cross(v1, v2) < 0.0 then rotation dir is -1.0
00203 
00204     else if remAngle(v1, v2) is < 180 and cross(v1, v2) < 0.0 then rotation dir is -1.0
00205 
00206     finally if remAngle(v1, v2) is > 180 and cross(v1, v2) < 0.0 then rotation dir is +1.0
00207     
00208     \param center     the common point
00209     \param loc1       endpoint 1   
00210     \param loc2       endpoint 2
00211     \param remAngle   the remaining angle about center in radians
00212 
00213     \return the rotation direction (1 or -1)
00214   */
00215   inline int rotationDir(const RDGeom::Point2D &center, const RDGeom::Point2D &loc1, 
00216                           const RDGeom::Point2D &loc2, double remAngle) {
00217      RDGeom::Point2D pt1 = loc1 - center;
00218      RDGeom::Point2D pt2 = loc2 - center;
00219      double cross = pt1.x*pt2.y - pt1.y*pt2.x;
00220      double diffAngle = RDKit::PI - remAngle;
00221      cross  *= diffAngle;
00222      if (cross >= 0.0) {
00223        return -1;
00224      } else {
00225        return 1;
00226      }
00227    }
00228 
00229   //! computes and return the normal of a vector between two points
00230   /*!
00231     \param center     the common point
00232     \param other      the endpoint
00233 
00234     \return the normal
00235   */
00236   inline RDGeom::Point2D computeNormal(const RDGeom::Point2D &center, 
00237                                        const RDGeom::Point2D &other) {
00238     RDGeom::Point2D res = other - center;
00239     res.normalize();
00240     double tmp = res.x;
00241     res.x = -res.y;
00242     res.y = tmp;
00243     return res;
00244   }
00245 
00246   //! computes the rotation angle between two vectors
00247   /*!
00248     \param center     the common point
00249     \param loc1       endpoint 1   
00250     \param loc2       endpoint 2
00251 
00252     \return the angle (in radians)
00253   */
00254   inline double computeAngle(const RDGeom::Point2D &center, 
00255                              const RDGeom::Point2D &loc1, 
00256                              const RDGeom::Point2D &loc2) {
00257     RDGeom::Point2D v1 = loc1 - center;
00258     RDGeom::Point2D v2 = loc2 - center;
00259     return v1.angleTo(v2);
00260   }
00261 
00262   //! \brief pick the ring to embed first in a fused system
00263   /*!
00264     \param mol        the molecule of interest
00265     \param fusedRings the collection of the molecule's fused rings
00266 
00267     \return the index of the ring with the least number of substitutions
00268   */
00269   int pickFirstRingToEmbed(const RDKit::ROMol &mol, const RDKit::VECT_INT_VECT &fusedRings);
00270 
00271   //! \brief find the rotatable bonds on the shortest path between two atoms
00272   //!   we will ignore ring atoms, and double bonds which are marked cis/trans
00273   /*!
00274     <b>Note</b> that rotatable in this context doesn't connect to the
00275       standard chemical definition of a rotatable bond; we're just talking
00276       about bonds than can be flipped in order to clean up the depiction.
00277   
00278     \param mol   the molecule of interest
00279     \param aid1  index of the first atom
00280     \param aid2  index of the second atom
00281 
00282     \return a set of the indices of the rotatable bonds
00283   */
00284   RDKit::INT_VECT getRotatableBonds(const RDKit::ROMol &mol, unsigned int aid1,
00285                                     unsigned int aid2);
00286 
00287   //! \brief find all the rotatable bonds in a molecule
00288   //!   we will ignore ring atoms, and double bonds which are marked cis/trans
00289   /*!
00290     <b>Note</b> that rotatable in this context doesn't connect to the
00291       standard chemical definition of a rotatable bond; we're just talking
00292       about bonds than can be flipped in order to clean up the depiction.
00293   
00294     \param mol   the molecule of interest
00295     
00296     \return a set of the indices of the rotatable bonds
00297   */
00298   RDKit::INT_VECT getAllRotatbleBonds(const RDKit::ROMol &mol);
00299 
00300   //! Get the ids of the atoms and bonds that are connected to aid
00301   void getNbrAtomAndBondIds(unsigned int aid, const RDKit::ROMol *mol,
00302                             RDKit::INT_VECT &aids, RDKit::INT_VECT &bids);
00303 
00304   //! Find pairs of bonds that can be permuted at a non-ring degree 4 atom
00305   /*!
00306     This function will return only those pairs that cannot be 
00307     permuted by flipping a rotatble bond
00308     
00309            D
00310            |
00311            b3
00312            |
00313       A-b1-B-b2-C
00314            |
00315            b4
00316            |
00317            E
00318      For example in teh above situation on the pairs (b1, b3) and (b1, b4) will be returned
00319      All other permutations can be achieved via a rotatable bond flip.
00320 
00321      ARGUMENTS:
00322      \param center - location of the central atom
00323      \param nbrBids - a vector (of length 4) containing the ids of the bonds to the neighbors
00324      \param nbrLocs - locations of the neighbors
00325   */
00326   INT_PAIR_VECT findBondsPairsToPermuteDeg4(const RDGeom::Point2D &center, const RDKit::INT_VECT &nbrBids, 
00327                                             const VECT_C_POINT &nbrLocs);
00328 }
00329 
00330 #endif