RDKit
Open-source cheminformatics and machine learning.
DrawingToCairo.h
Go to the documentation of this file.
1 // $Id$
2 //
3 // Copyright (C) 2012 Greg Landrum
4 //
5 // @@ All Rights Reserved @@
6 // This file is part of the RDKit.
7 // The contents are covered by the terms of the BSD license
8 // which is included in the file license.txt, found at the root
9 // of the RDKit source tree.
10 //
11 
12 /*
13  To use include this in your CPPFLAGS:
14  $(shell pkg-config --cflags cairo)
15  and this in your LDFLAGS:
16  $(shell pkg-config --libs cairo)
17 */
18 
19 #ifndef _RD_DRAWING_TO_CAIRO_H_
20 #define _RD_DRAWING_TO_CAIRO_H_
21 
23 #include <cairo.h>
24 
25 namespace RDKit {
26 namespace Drawing {
27 namespace {
28 void setColorCairo(int atNum, cairo_t *cr) {
29  PRECONDITION(cr, "no context");
30  switch (atNum) {
31  case 7:
32  cairo_set_source_rgb(cr, 0.0, 0.0, 1.0);
33  break;
34  case 8:
35  cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
36  break;
37  case 9:
38  cairo_set_source_rgb(cr, 0.2, 0.8, 0.8);
39  break;
40  case 15:
41  cairo_set_source_rgb(cr, 1.0, 0.5, 0.0);
42  break;
43  case 16:
44  cairo_set_source_rgb(cr, 0.8, 0.8, 0.0);
45  break;
46  case 17:
47  cairo_set_source_rgb(cr, 0.0, 0.8, 0.0);
48  break;
49  case 35:
50  cairo_set_source_rgb(cr, 0.5, 0.3, 0.1);
51  break;
52  case 0:
53  cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
54  break;
55  default:
56  cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
57  }
58 }
59 void drawLineCairo(std::vector<int>::const_iterator &pos, cairo_t *cr) {
60  PRECONDITION(cr, "no context");
61  int width = *pos++;
62  cairo_set_line_width(cr, width * 10);
63  int dashed = *pos++;
64  double dashes[2];
65  switch (dashed) {
66  case 0:
67  cairo_set_dash(cr, 0, 0, 0);
68  break;
69  case 2:
70  dashes[0] = 5.0;
71  dashes[1] = 20.0;
72  cairo_set_dash(cr, dashes, sizeof(dashes) / sizeof(dashes[0]), 0);
73  break;
74  default:
75  dashes[0] = 20.0;
76  dashes[1] = 20.0;
77  cairo_set_dash(cr, dashes, sizeof(dashes) / sizeof(dashes[0]), 0);
78  }
79  int an1 = *pos++;
80  int an2 = *pos++;
81  int xp1 = *pos++;
82  int yp1 = *pos++;
83  int xp2 = *pos++;
84  int yp2 = *pos++;
85  if (an1 == an2) {
86  setColorCairo(an1, cr);
87  cairo_move_to(cr, xp1, yp1);
88  cairo_line_to(cr, xp2, yp2);
89  cairo_stroke(cr);
90  } else {
91  int mx = xp1 + (xp2 - xp1) / 2;
92  int my = yp1 + (yp2 - yp1) / 2;
93 
94  setColorCairo(an1, cr);
95  cairo_move_to(cr, xp1, yp1);
96  cairo_line_to(cr, mx, my);
97  cairo_stroke(cr);
98  setColorCairo(an2, cr);
99  cairo_move_to(cr, mx, my);
100  cairo_line_to(cr, xp2, yp2);
101  cairo_stroke(cr);
102  }
103 }
104 void drawAtomCairo(std::vector<int>::const_iterator &pos, cairo_t *cr) {
105  PRECONDITION(cr, "no context");
106  int fontSz = 50;
107  int atNum = *pos++;
108  double xp = static_cast<double>(*pos++);
109  double yp = static_cast<double>(*pos++);
110  int slen = *pos++;
111  std::string label = "";
112  for (unsigned int i = 0; i < slen; ++i) {
113  label += (char)*pos++;
114  }
116  static_cast<RDKit::Drawing::OrientType>(*pos++);
117  if (label.length()) {
118  cairo_text_extents_t extents;
119  cairo_text_extents(cr, label.c_str(), &extents);
120  double twidth = extents.width, theight = extents.height;
121 
122  switch (orient) {
123  case RDKit::Drawing::W:
124  xp -= twidth;
125  yp += theight / 2;
126  break;
127  case RDKit::Drawing::E:
128  yp += theight / 2;
129  break;
130  case RDKit::Drawing::S:
131  xp -= twidth / 2;
132  yp += theight;
133  break;
134  case RDKit::Drawing::N:
135  xp -= twidth / 2;
136  yp -= theight / 2;
137  break;
138  default:
139  xp -= twidth / 2;
140  yp += theight / 2;
141  }
142  cairo_set_source_rgb(cr, 1.0, 1., 1.);
143  cairo_rectangle(cr, xp - 10, yp - theight - 10, twidth + 20, theight + 20);
144  cairo_fill(cr);
145 
146  cairo_move_to(cr, xp, yp);
147  setColorCairo(atNum, cr);
148  cairo_show_text(cr, label.c_str());
149  cairo_stroke(cr);
150  }
151 }
152 } // end of anonymous namespace
153 
154 void DrawingToCairo(const std::vector<int> &drawing, cairo_t *cr, int width,
155  int height, int fontSize = 12,
156  int maxDotsPerAngstrom = 60) {
157  PRECONDITION(cr, "no context");
158  PRECONDITION(width > 0 && height > 0, "bad dimensions");
159 
160  cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
161  cairo_rectangle(cr, 0, 0, width, height);
162  cairo_fill(cr);
163 
164  cairo_select_font_face(cr, "sans", CAIRO_FONT_SLANT_NORMAL,
165  CAIRO_FONT_WEIGHT_NORMAL);
166 
167  std::vector<int>::const_iterator pos = drawing.begin();
168  int resolution = 0;
169  if (*pos != Drawing::RESOLUTION) {
170  std::cerr << "no RESOLUTION token found" << std::endl;
171  return;
172  }
173  resolution = *(pos + 1);
174  PRECONDITION(resolution > 0, "bad resolution");
175  pos += 2;
176  if (*pos != Drawing::BOUNDS) {
177  std::cerr << "no bounds token found" << std::endl;
178  return;
179  }
180  pos += 3;
181  int dwidth, dheight;
182  dwidth = *pos++;
183  dheight = *pos++;
184  PRECONDITION(dwidth > 0 && dheight > 0, "bad dimensions");
185 
186  // size of the image in angstroms:
187  double xSize, ySize;
188  xSize = static_cast<double>(dwidth) / resolution;
189  ySize = static_cast<double>(dheight) / resolution;
190  double scale = 1.0;
191 
192  if (dwidth > width || dheight > height) {
193  if (static_cast<double>(dwidth) / width >
194  static_cast<double>(dheight) / height) {
195  scale = static_cast<double>(width) / dwidth;
196  } else {
197  scale = static_cast<double>(height) / dheight;
198  }
199  } else {
200  if (width / xSize > height / ySize) {
201  if (width / xSize > maxDotsPerAngstrom) {
202  scale = maxDotsPerAngstrom * xSize / width;
203  }
204  } else {
205  if (height / ySize > maxDotsPerAngstrom) {
206  scale = maxDotsPerAngstrom * ySize / height;
207  }
208  }
209  }
210 
211  scale *= 0.80;
212  cairo_translate(cr, .5 * (width - dwidth * scale),
213  .5 * (height - dheight * scale));
214  cairo_scale(cr, scale, scale);
215 
216  // scaling factors here are empirically determined
217  double dFontSize = 1.5 * maxDotsPerAngstrom * fontSize / 14;
218  cairo_set_font_size(cr, dFontSize);
219 
220  while (pos != drawing.end()) {
221  int token = *pos++;
222  switch (token) {
223  case Drawing::LINE:
224  drawLineCairo(pos, cr);
225  break;
226  case Drawing::ATOM:
227  drawAtomCairo(pos, cr);
228  break;
229  default:
230  std::cerr << "unrecognized token: " << token << std::endl;
231  }
232  }
233 }
234 } // end of namespace Drawing
235 } // end of namespace RDKit
236 #endif
void DrawingToCairo(const std::vector< int > &drawing, cairo_t *cr, int width, int height, int fontSize=12, int maxDotsPerAngstrom=60)
Includes a bunch of functionality for handling Atom and Bond queries.
Definition: Atom.h:29
#define PRECONDITION(expr, mess)
Definition: Invariant.h:107