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