File: DrawMol.h

package info (click to toggle)
rdkit 202503.1-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 220,160 kB
  • sloc: cpp: 399,240; python: 77,453; ansic: 25,517; java: 8,173; javascript: 4,005; sql: 2,389; yacc: 1,565; lex: 1,263; cs: 1,081; makefile: 580; xml: 229; fortran: 183; sh: 105
file content (367 lines) | stat: -rwxr-xr-x 17,813 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
//
//  Copyright (C) 2021-2022 David Cosgrove and other RDKit contributors
//
//   @@ All Rights Reserved @@
//  This file is part of the RDKit.
//  The contents are covered by the terms of the BSD license
//  which is included in the file license.txt, found at the root
//  of the RDKit source tree.
//
// Original author: David Cosgrove (CozChemIx Limited)
//
// This class is a helper class used by MolDraw2D to draw an ROMol.
// It is not part of the public API and is not intended to be used except
// by MolDraw2D.

#ifndef RDKIT_DRAWMOL_H
#define RDKIT_DRAWMOL_H

#include <map>
#include <string>
#include <vector>

#include <Geometry/point.h>
#include <GraphMol/MolDraw2D/AtomSymbol.h>
#include <GraphMol/MolDraw2D/DrawAnnotation.h>
#include <GraphMol/MolDraw2D/DrawShape.h>
#include <GraphMol/MolDraw2D/MolDraw2DHelpers.h>

namespace RDKit {

class Atom;
class Bond;
class ROMol;
class RWMol;
class DrawText;

namespace MolDraw2D_detail {

class DrawMol {
 public:
  virtual ~DrawMol() = default;

  // Make the object, scaled to a given pixel size.
  /*!
    \param mol             : the molecule to draw
    \param legend          : the legend (to be drawn under the molecule)
    \param width           : width (in pixels) of the rendering
    set this to -1 to have the canvas size set automatically
    \param height          : height (in pixels) of the rendering
    set this to -1 to have the canvas size set automatically
    \param drawOptions    : a MolDrawOptions object from the owning MolDraw2D
    \param textDrawer     : a DrawText object from the owning MolDraw2D
    \param highlightAtoms : (optional) vector of atom ids to highlight
    \param highlightBonds : (optional) vector of bond ids to highlight
    \param highlightAtomMap   : (optional) map from atomId -> DrawColour
    providing the highlight colors. If not provided the default highlight colour
    from \c drawOptions() will be used.
    \param highlightBondMap   : (optional) map from bondId -> DrawColour
    providing the highlight colors. If not provided the default highlight colour
    from \c drawOptions() will be used.
    \param highlightRadii : (optional) map from atomId -> radius (in molecule
    coordinates) for the radii of atomic highlights. If not provided the default
    value from \c drawOptions() will be used.
    \param confId          : (optional) conformer ID to be used for atomic
    coordinates
  */
  DrawMol(const ROMol &mol, const std::string &legend, int width, int height,
          const MolDrawOptions &drawOptions, DrawText &textDrawer,
          const std::vector<int> *highlightAtoms = nullptr,
          const std::vector<int> *highlightBonds = nullptr,
          const std::map<int, DrawColour> *highlightAtomMap = nullptr,
          const std::map<int, DrawColour> *highlightBondMap = nullptr,
          const std::vector<std::pair<DrawColour, DrawColour>> *bondColours =
              nullptr,
          const std::map<int, double> *highlight_radii = nullptr,
          bool includeAnnotations = true, int confId = -1,
          bool isReactionMol = false);
  /*!
   Make a DrawMol when there's no molecule to draw, but we still want
   a DrawMol in the MolDraw2D for scale, conversion of atom coords to
   draw coords etc.  And so DrawMol starts sounding like a poor name for
   the class.
   \param width        : width (in pixels) of the rendering
   \param height       : height (in pixels) of the rendering
   \param drawOptions  : a MolDrawOptions object from the owning MolDraw2D
   \param textDrawer   : a DrawText object from the owning MolDraw2D
   \param xmin         : minimum value expected in X
   \param xmax         : miaximum value expected in X
   \param ymin         : minimum value expected in Y
   \param ymax         : maximum value expected in Y
   \param scale        : scale to use
   \param fontscale    : font scale to use
   */
  DrawMol(int width, int height, const MolDrawOptions &drawOptions,
          DrawText &textDrawer, double xmin, double xmax, double ymin,
          double ymax, double scale, double fontscale);
  DrawMol(const DrawMol &) = delete;
  DrawMol(DrawMol &&) = delete;
  DrawMol &operator=(const DrawMol &) = delete;
  DrawMol &operator=(DrawMol &&) = delete;

  // this must be called before a drawing can be done.
  void createDrawObjects();
  // common bits used by createDrawObjects and setScale.
  void finishCreateDrawObjects();
  void initDrawMolecule(const ROMol &mol);
  void extractAll(double scale);
  void extractAtomCoords();
  void extractAtomSymbols();
  void extractBonds();
  virtual void extractHighlights(double scale);
  void extractRegions();
  void extractAttachments();
  void extractMolNotes();
  void extractStereoGroups();
  void extractAtomNotes();
  void extractBondNotes();
  void extractRadicals();
  void extractSGroupData();
  void extractVariableBonds();
  void extractBrackets();
  void extractLinkNodes();
  // extractCloseContacts is to show where 2 atoms are drawn too close
  // together and so needs the final drawing coords.  It is therefore called
  // at the end of changeToDrawCoords() and any necessary DrawShapePolyLines
  // added to postShapes_ in drawCoords.
  void extractCloseContacts();
  void resolveAtomSymbolClashes();
  void calculateScale();
  void findExtremes();
  void changeToDrawCoords();
  void draw(MolDraw2D &drawer) const;
  void drawRadicals(MolDraw2D &drawer) const;
  void resetEverything();
  // reduce width_ and height_ to just accomodate the Xrange_ and YRange_
  // at the current scale.  Recentres everything.  So the DrawMol takes up
  // no more screen real estate than it needs.
  void shrinkToFit(bool withPadding = true);

  // adds LaTeX-like annotation for super- and sub-script.
  std::pair<std::string, OrientType> getAtomSymbolAndOrientation(
      const Atom &atom) const;
  std::string getAtomSymbol(const Atom &atom, OrientType orientation) const;
  OrientType getAtomOrientation(const Atom &atom) const;

  // if there's a legend, partition the height_ to accommodate it
  void partitionForLegend();
  // extractLegend is different from all the other extract... functions
  // in that it needs to be called once the final scale has been found
  // by calculateScale.
  void extractLegend();

  void calcMeanBondLength();
  void makeStandardBond(Bond *bond, double doubleBondOffset);
  void makeQueryBond(Bond *bond, double doubleBondOffset);
  void makeDoubleBondLines(Bond *bond, double doubleBondOffset,
                           const std::pair<DrawColour, DrawColour> &cols);
  void makeTripleBondLines(Bond *bond, double doubleBondOffset,
                           const std::pair<DrawColour, DrawColour> &cols);
  void makeWedgedBond(Bond *bond,
                      const std::pair<DrawColour, DrawColour> &cols);
  void makeWavyBond(Bond *bond, double offset,
                    const std::pair<DrawColour, DrawColour> &cols);
  void makeDativeBond(Bond *bond, double offset,
                      const std::pair<DrawColour, DrawColour> &cols);
  void makeZeroBond(Bond *bond, const std::pair<DrawColour, DrawColour> &cols,
                    const DashPattern &dashPattern);
  void adjustBondEndsForLabels(int begAtIdx, int endAtIdx, Point2D &begCds,
                               Point2D &endCds) const;
  void newBondLine(const Point2D &pt1, const Point2D &pt2,
                   const DrawColour &col1, const DrawColour &col2, int atom1Idx,
                   int atom2Idx, int bondIdx, const DashPattern &dashPattern);
  std::pair<DrawColour, DrawColour> getBondColours(Bond *bond);
  void makeContinuousHighlights(double scale);
  void makeAtomCircleHighlights();
  void makeAtomEllipseHighlights(double lineWidth);
  // These are the lines for continuous highlights, that are
  // now filled trapezoids rather than fat simple lines.
  void makeBondHighlightLines(double lineWidth, double scale);
  void calcAnnotationPosition(const Atom *atom, DrawAnnotation &annot) const;
  void calcAnnotationPosition(const Bond *bond, DrawAnnotation &annot) const;
  double getNoteStartAngle(const Atom *atom) const;
  // see if the note will clash with anything else drawn on the molecule.
  // Returns 0 if no clash, 1-4 if there is a clash, denoting what clashed.
  int doesNoteClash(const DrawAnnotation &annot) const;
  int doesRectClash(const StringRect &rect, double padding) const;
  OrientType calcRadicalRect(const Atom *atom, StringRect &rad_rect) const;
  void getDrawTransformers(Point2D &trans, Point2D &scale,
                           Point2D &toCentre) const;
  // Given some coords in molecule space (angstrom, probably) return the
  // screen coords.
  Point2D getDrawCoords(const Point2D &atCds, const Point2D &trans,
                        const Point2D &scaleFactor,
                        const Point2D &toCentre) const;
  Point2D getDrawCoords(const Point2D &atCds) const;
  Point2D getDrawCoords(int atnum) const;
  // and the other way.
  Point2D getAtomCoords(const Point2D &screenCds) const;
  Point2D getAtomCoords(int atnum) const;
  double getScale() const { return scale_; }
  double getFontScale() const { return fontScale_; }
  // More often than not, newScale and newFontScale will be the same,
  // but not if minFontScale or maxFontScale have become involved.
  // The newFontScale will be used without checking the min and max.
  void setScale(double newScale, double newFontScale,
                bool ignoreFontLimits = true);
  // Set all the transformation details from the incoming DrawMol to this
  // one, so they can be overlaid properly.  Doesn't change the offsets.
  void setTransformation(const DrawMol &sourceMol);

  // For drawing into a grid, for example.  Must be set before
  // changeToDrawCoords is called for it to have effect.
  void setOffsets(double xOffset, double yOffset);
  // So we can add metadata later.  Most likely used after changeToDrawCoords
  // has been called.
  void tagAtomsWithCoords();
  // Apply the transformations to everything. trans and toCentre are added,
  // scale is multiplied.
  void transformAll(const Point2D *trans = nullptr, Point2D *scale = nullptr,
                    const Point2D *toCentre = nullptr);
  // Apply the transformations to the given point and return a new one.
  Point2D transformPoint(const Point2D &pt, const Point2D *trans = nullptr,
                         Point2D *scale = nullptr,
                         const Point2D *toCentre = nullptr) const;
  void calcDoubleBondLines(double offset, const Bond &bond, Point2D &l1s,
                           Point2D &l1f, Point2D &l2s, Point2D &l2f) const;
  void bondInsideRing(const Bond &bond, double offset, Point2D &l2s,
                      Point2D &l2f) const;
  void bondNonRing(const Bond &bond, double offset, Point2D &l2s,
                   Point2D &l2f) const;
  // deal with terminal double bond between at1 and at2, either to atoms of
  // degree 2 or 3.
  void doubleBondTerminal(Atom *at1, Atom *at2, double offset, Point2D &l1s,
                          Point2D &l1f, Point2D &l2s, Point2D &l2f) const;
  // assuming at[1-3] are atoms where at1 is bonded to at2 and at2 is bonded
  // to at3, find the position of the at2 end of a double bond between at2
  // and at3.  If trunc, it'll be along the vector that bisects the two bonds
  // on the inside, otherwise it's perpendicular to the bond from at1 to at2.
  Point2D doubleBondEnd(unsigned int at1, unsigned int at2, unsigned int at3,
                        double offset, bool trunc) const;
  void calcTripleBondLines(double offset, const Bond &bond, Point2D &l1s,
                           Point2D &l1f, Point2D &l2s, Point2D &l2f);
  // find the vectors of any atoms singly bonded to atom that aren't
  // otherAtom.
  void findOtherBondVecs(const Atom *atom, const Atom *otherAtom,
                         std::vector<Point2D> &otherBondVecs) const;
  void adjustBondsOnSolidWedgeEnds();
  void smoothBondJoins();
  // If doing a continuous highlight, add to points the 2 or 3 points that
  // will be for the end1 end of the highlight.  The final highlight will
  // be a 4-6 sided polygon formed by calling this twice, with the ends in
  // opposite order the second time.
  void makeHighlightEnd(const Atom *end1, const Atom *end2, double lineWidth,
                        const std::vector<Atom *> &end1HighNbrs,
                        std::vector<Point2D> &points);
  DrawColour getColour(int atom_idx) const;

  const MolDrawOptions &drawOptions_;
  DrawText &textDrawer_;
  // For drawing reactions, padding needs to be 0 irrespective
  // of what drawOptions_ does elsewhere, so it is copied from drawOptions_
  // on construction, and is then immune to changes in the outer program.
  double marginPadding_;
  std::vector<int> highlightAtoms_;
  std::vector<int> highlightBonds_;
  std::map<int, DrawColour> highlightAtomMap_;
  std::map<int, DrawColour> highlightBondMap_;
  std::vector<std::pair<DrawColour, DrawColour>> bondColours_;
  std::map<int, double> highlightRadii_;
  bool includeAnnotations_;
  bool isReactionMol_;
  std::string legend_;

  std::unique_ptr<RWMol> drawMol_;
  int confId_;
  // atCds_ are as extracted from the molecule, except that the y is
  // inverted and drawOptions_.rotate is applied.
  std::vector<Point2D> atCds_;
  std::vector<std::unique_ptr<DrawShape>> bonds_;
  std::vector<std::unique_ptr<DrawShape>> preShapes_;
  std::vector<std::unique_ptr<DrawShape>> postShapes_;
  std::vector<int> atomicNums_;
  std::vector<std::pair<std::string, OrientType>> atomSyms_;
  std::vector<std::unique_ptr<AtomSymbol>> atomLabels_;
  std::vector<std::unique_ptr<DrawShape>> highlights_;
  std::vector<std::unique_ptr<DrawAnnotation>> annotations_;
  std::vector<std::unique_ptr<DrawAnnotation>> legends_;
  std::vector<std::tuple<StringRect, OrientType, int>> radicals_;
  std::vector<int> singleBondLines_;

  // The total width and height of the canvas.  Either or both may be
  // -1 initially, in which case they will be calculated so the molecule
  // can be drawn within it at a fixed scale (the so-called flexiCanvas).
  int width_, height_;
  // The width and height of the drawing area within the canvas.  This is
  // width_ and height_ less the drawOptions._padding round the outside.
  // Will always be <= width_, height_.
  int drawWidth_, drawHeight_;
  // to allow for min and max font sizes, the font scale needs to be
  // independent of the main scale.
  double scale_, fontScale_;
  double xMin_, yMin_, xMax_, yMax_, xRange_, yRange_;
  // offsets are for drawing molecules in grids, for example.
  double xOffset_ = 0.0, yOffset_ = 0.0;
  double meanBondLength_ = 0.0;
  // if there's a legend, we reserve a bit for it.  molHeight_ is the
  // bit for drawing the molecule, legendHeight_ the bit under that
  // for the legend.  In pixels.
  int molHeight_, legendHeight_ = 0;
  bool drawingInitialised_ = false;
  // when drawing the atoms and bonds in an SVG, they are given a class
  // via MolDraw2D's activeAtmIdx[12]_ and activeBndIdx.  We don't always want
  // them to start from 0 for atom/bond 0.
  int activeAtmIdxOffset_ = 0, activeBndIdxOffset_ = 0;
  bool flexiCanvasX_ = false;
  bool flexiCanvasY_ = false;
};

void centerMolForDrawing(RWMol &mol, int confId = 1);
bool isLinearAtom(const Atom &atom, const std::vector<Point2D> &atCds);
std::string getAtomListText(const Atom &atom);
DrawColour getColourByAtomicNum(int atomicNum,
                                const MolDrawOptions &drawOptions);
DrawColour getHighlightBondColour(
    const Bond *bond, const MolDrawOptions &drawOptions,
    const std::vector<int> &highlightBonds,
    const std::map<int, DrawColour> &highlightBondMap,
    const std::vector<int> &highlightAtoms,
    const std::map<int, DrawColour> &highlightAtomMap);
double getHighlightBondWidth(
    const MolDrawOptions &drawOptions, int bond_idx,
    const std::map<int, int> *highlight_linewidth_multipliers);

Point2D calcPerpendicular(const Point2D &cds1, const Point2D &cds2);
Point2D calcInnerPerpendicular(const Point2D &cds1, const Point2D &cds2,
                               const Point2D &cds3);
// return a point that is moveEnd moved so as not to clash with any of the
// rects of a label.  moveEnd to end2 are the coords of 2 ends of a bond.
void adjustBondEndForString(
    const Point2D &end2, double padding,
    const std::vector<std::shared_ptr<StringRect>> &rects, Point2D &moveEnd);
void findRadicalExtremes(
    const std::vector<std::tuple<StringRect, OrientType, int>> &radicals,
    double &xmin, double &xmax, double &ymin, double &ymax);
void findRectExtremes(const StringRect &rect, const TextAlignType &align,
                      double &xmin, double &xmax, double &ymin, double &ymax);
void getBondHighlightsForAtoms(const ROMol &mol,
                               const std::vector<int> &highlight_atoms,
                               std::vector<int> &highlight_bonds);
// returns true if the vector at2->at1 points in roughly the opposite
// direction to at3->at4.  Basically, if the dot product is negative.
bool areBondsTrans(const Point2D &at1, const Point2D &at2, const Point2D &at3,
                   const Point2D &at4);
// returns true if the vector at2->at1 points is roughly linear with
// direction of at3->at4.  Basically, if the dot product is 1.0 within the
// given tolerance.
bool areBondsParallel(const Point2D &at1, const Point2D &at2,
                      const Point2D &at3, const Point2D &at4,
                      double tol = 1.0e-4);

// find the nborNum'th neighbour of firstAtom that isn't secondAtom
const Atom *otherNeighbor(const Atom *firstAtom, const Atom *secondAtom,
                          int nborNum, const ROMol &mol);
}  // namespace MolDraw2D_detail
}  // namespace RDKit

#endif  // RDKIT_DRAWMOL_H