1 //
2 //  Copyright (C) 2001-2021 Greg Landrum and other RDKit contributors
3 //
4 //   @@ All Rights Reserved @@
5 //  This file is part of the RDKit.
6 //  The contents are covered by the terms of the BSD license
7 //  which is included in the file license.txt, found at the root
8 //  of the RDKit source tree.
9 //
10 #include "Bond.h"
11 #include "Atom.h"
12 #include "ROMol.h"
13 #include <RDGeneral/Invariant.h>
14 
15 namespace RDKit {
16 
Bond()17 Bond::Bond() : RDProps() { initBond(); };
18 
Bond(BondType bT)19 Bond::Bond(BondType bT) : RDProps() {
20   initBond();
21   d_bondType = bT;
22 };
23 
Bond(const Bond & other)24 Bond::Bond(const Bond &other) : RDProps(other) {
25   // NOTE: we do *not* copy ownership!
26   dp_mol = nullptr;
27   d_bondType = other.d_bondType;
28   d_beginAtomIdx = other.d_beginAtomIdx;
29   d_endAtomIdx = other.d_endAtomIdx;
30   d_dirTag = other.d_dirTag;
31   d_stereo = other.d_stereo;
32   if (other.dp_stereoAtoms) {
33     dp_stereoAtoms = new INT_VECT(*other.dp_stereoAtoms);
34   } else {
35     dp_stereoAtoms = nullptr;
36   }
37   df_isAromatic = other.df_isAromatic;
38   df_isConjugated = other.df_isConjugated;
39   d_index = other.d_index;
40 }
41 
~Bond()42 Bond::~Bond() { delete dp_stereoAtoms; }
43 
operator =(const Bond & other)44 Bond &Bond::operator=(const Bond &other) {
45   if (this == &other) {
46     return *this;
47   }
48   dp_mol = other.dp_mol;
49   d_bondType = other.d_bondType;
50   d_beginAtomIdx = other.d_beginAtomIdx;
51   d_endAtomIdx = other.d_endAtomIdx;
52   d_dirTag = other.d_dirTag;
53   delete dp_stereoAtoms;
54   if (other.dp_stereoAtoms) {
55     dp_stereoAtoms = new INT_VECT(*other.dp_stereoAtoms);
56   } else {
57     dp_stereoAtoms = nullptr;
58   }
59   df_isAromatic = other.df_isAromatic;
60   df_isConjugated = other.df_isConjugated;
61   d_index = other.d_index;
62   d_props = other.d_props;
63 
64   return *this;
65 }
66 
copy() const67 Bond *Bond::copy() const {
68   auto *res = new Bond(*this);
69   return res;
70 }
71 
setOwningMol(ROMol * other)72 void Bond::setOwningMol(ROMol *other) {
73   // FIX: doesn't update topology
74   dp_mol = other;
75 }
76 
getOtherAtomIdx(const unsigned int thisIdx) const77 unsigned int Bond::getOtherAtomIdx(const unsigned int thisIdx) const {
78   PRECONDITION(d_beginAtomIdx == thisIdx || d_endAtomIdx == thisIdx,
79                "bad index");
80   if (d_beginAtomIdx == thisIdx) {
81     return d_endAtomIdx;
82   } else if (d_endAtomIdx == thisIdx) {
83     return d_beginAtomIdx;
84   }
85   // we cannot actually get down here
86   return 0;
87 }
88 
setBeginAtomIdx(unsigned int what)89 void Bond::setBeginAtomIdx(unsigned int what) {
90   if (dp_mol) {
91     URANGE_CHECK(what, getOwningMol().getNumAtoms());
92   }
93   d_beginAtomIdx = what;
94 };
95 
setEndAtomIdx(unsigned int what)96 void Bond::setEndAtomIdx(unsigned int what) {
97   if (dp_mol) {
98     URANGE_CHECK(what, getOwningMol().getNumAtoms());
99   }
100   d_endAtomIdx = what;
101 };
102 
setBeginAtom(Atom * at)103 void Bond::setBeginAtom(Atom *at) {
104   PRECONDITION(dp_mol != nullptr, "no owning molecule for bond");
105   setBeginAtomIdx(at->getIdx());
106 }
setEndAtom(Atom * at)107 void Bond::setEndAtom(Atom *at) {
108   PRECONDITION(dp_mol != nullptr, "no owning molecule for bond");
109   setEndAtomIdx(at->getIdx());
110 }
111 
getBeginAtom() const112 Atom *Bond::getBeginAtom() const {
113   PRECONDITION(dp_mol != nullptr, "no owning molecule for bond");
114   return dp_mol->getAtomWithIdx(d_beginAtomIdx);
115 };
getEndAtom() const116 Atom *Bond::getEndAtom() const {
117   PRECONDITION(dp_mol != nullptr, "no owning molecule for bond");
118   return dp_mol->getAtomWithIdx(d_endAtomIdx);
119 };
getOtherAtom(Atom const * what) const120 Atom *Bond::getOtherAtom(Atom const *what) const {
121   PRECONDITION(dp_mol != nullptr, "no owning molecule for bond");
122 
123   return dp_mol->getAtomWithIdx(getOtherAtomIdx(what->getIdx()));
124 };
125 
getBondTypeAsDouble() const126 double Bond::getBondTypeAsDouble() const {
127   double res;
128   switch (getBondType()) {
129     case UNSPECIFIED:
130     case IONIC:
131     case ZERO:
132       res = 0;
133       break;
134     case SINGLE:
135       res = 1;
136       break;
137     case DOUBLE:
138       res = 2;
139       break;
140     case TRIPLE:
141       res = 3;
142       break;
143     case QUADRUPLE:
144       res = 4;
145       break;
146     case QUINTUPLE:
147       res = 5;
148       break;
149     case HEXTUPLE:
150       res = 6;
151       break;
152     case ONEANDAHALF:
153       res = 1.5;
154       break;
155     case TWOANDAHALF:
156       res = 2.5;
157       break;
158     case THREEANDAHALF:
159       res = 3.5;
160       break;
161     case FOURANDAHALF:
162       res = 4.5;
163       break;
164     case FIVEANDAHALF:
165       res = 5.5;
166       break;
167     case AROMATIC:
168       res = 1.5;
169       break;
170     case DATIVEONE:
171       res = 1.0;
172       break;  // FIX: this should probably be different
173     case DATIVE:
174       res = 1.0;
175       break;  // FIX: again probably wrong
176     case HYDROGEN:
177       res = 0.0;
178       break;
179     default:
180       UNDER_CONSTRUCTION("Bad bond type");
181   }
182   return res;
183 }
184 
getValenceContrib(const Atom * atom) const185 double Bond::getValenceContrib(const Atom *atom) const {
186   double res;
187   if ((getBondType() == DATIVE || getBondType() == DATIVEONE) &&
188       atom->getIdx() != getEndAtomIdx()) {
189     res = 0.0;
190   } else {
191     res = getBondTypeAsDouble();
192   }
193 
194   return res;
195 }
196 
setQuery(QUERYBOND_QUERY * what)197 void Bond::setQuery(QUERYBOND_QUERY *what) {
198   //  Bonds don't have queries at the moment because I have not
199   //  yet figured out what a good base query should be.
200   //  It would be nice to be able to do substructure searches
201   //  using molecules alone, so it'd be nice if we got this
202   //  issue resolved ASAP.
203   RDUNUSED_PARAM(what);
204   PRECONDITION(0, "plain bonds have no Query");
205 }
206 
getQuery() const207 Bond::QUERYBOND_QUERY *Bond::getQuery() const {
208   PRECONDITION(0, "plain bonds have no Query");
209   return nullptr;
210 };
211 
Match(Bond const * what) const212 bool Bond::Match(Bond const *what) const {
213   bool res;
214   if (getBondType() == Bond::UNSPECIFIED ||
215       what->getBondType() == Bond::UNSPECIFIED) {
216     res = true;
217   } else {
218     res = getBondType() == what->getBondType();
219   }
220   return res;
221 };
222 
expandQuery(Bond::QUERYBOND_QUERY * what,Queries::CompositeQueryType how,bool maintainOrder)223 void Bond::expandQuery(Bond::QUERYBOND_QUERY *what,
224                        Queries::CompositeQueryType how, bool maintainOrder) {
225   RDUNUSED_PARAM(what);
226   RDUNUSED_PARAM(how);
227   RDUNUSED_PARAM(maintainOrder);
228   PRECONDITION(0, "plain bonds have no query");
229 };
230 
initBond()231 void Bond::initBond() {
232   d_bondType = UNSPECIFIED;
233   d_dirTag = NONE;
234   d_stereo = STEREONONE;
235   dp_mol = nullptr;
236   d_beginAtomIdx = 0;
237   d_endAtomIdx = 0;
238   df_isAromatic = 0;
239   d_index = 0;
240   df_isConjugated = 0;
241   dp_stereoAtoms = nullptr;
242 };
243 
setStereoAtoms(unsigned int bgnIdx,unsigned int endIdx)244 void Bond::setStereoAtoms(unsigned int bgnIdx, unsigned int endIdx) {
245   PRECONDITION(
246       getOwningMol().getBondBetweenAtoms(getBeginAtomIdx(), bgnIdx) != nullptr,
247       "bgnIdx not connected to begin atom of bond");
248   PRECONDITION(
249       getOwningMol().getBondBetweenAtoms(getEndAtomIdx(), endIdx) != nullptr,
250       "endIdx not connected to end atom of bond");
251 
252   INT_VECT &atoms = getStereoAtoms();
253   atoms.clear();
254   atoms.push_back(bgnIdx);
255   atoms.push_back(endIdx);
256 };
257 
getTwiceBondType(const Bond & b)258 uint8_t getTwiceBondType(const Bond &b) {
259   switch (b.getBondType()) {
260     case Bond::UNSPECIFIED:
261     case Bond::IONIC:
262     case Bond::ZERO:
263       return 0;
264       break;
265     case Bond::SINGLE:
266       return 2;
267       break;
268     case Bond::DOUBLE:
269       return 4;
270       break;
271     case Bond::TRIPLE:
272       return 6;
273       break;
274     case Bond::QUADRUPLE:
275       return 8;
276       break;
277     case Bond::QUINTUPLE:
278       return 10;
279       break;
280     case Bond::HEXTUPLE:
281       return 12;
282       break;
283     case Bond::ONEANDAHALF:
284       return 3;
285       break;
286     case Bond::TWOANDAHALF:
287       return 5;
288       break;
289     case Bond::THREEANDAHALF:
290       return 7;
291       break;
292     case Bond::FOURANDAHALF:
293       return 9;
294       break;
295     case Bond::FIVEANDAHALF:
296       return 11;
297       break;
298     case Bond::AROMATIC:
299       return 3;
300       break;
301     case Bond::DATIVEONE:
302       return 2;
303       break;  // FIX: this should probably be different
304     case Bond::DATIVE:
305       return 2;
306       break;  // FIX: again probably wrong
307     case Bond::HYDROGEN:
308       return 0;
309       break;
310     default:
311       UNDER_CONSTRUCTION("Bad bond type");
312   }
313 }
314 };  // namespace RDKit
315 
operator <<(std::ostream & target,const RDKit::Bond & bond)316 std::ostream &operator<<(std::ostream &target, const RDKit::Bond &bond) {
317   target << bond.getIdx() << " ";
318   target << bond.getBeginAtomIdx() << "->" << bond.getEndAtomIdx();
319   target << " order: " << bond.getBondType();
320   if (bond.getBondDir()) {
321     target << " dir: " << bond.getBondDir();
322   }
323   if (bond.getStereo()) {
324     target << " stereo: " << bond.getStereo();
325     if (bond.getStereoAtoms().size() == 2) {
326       const auto &ats = bond.getStereoAtoms();
327       target << " stereoAts: (" << ats[0] << " " << ats[1] << ")";
328     }
329   }
330   target << " conj?: " << bond.getIsConjugated();
331   target << " aromatic?: " << bond.getIsAromatic();
332 
333   return target;
334 }
335