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