Files
rdkit/Code/GraphMol/MolDrawing/DrawingToCairo.h
2015-11-14 14:58:11 +01:00

237 lines
6.2 KiB
C++

// $Id$
//
// Copyright (C) 2012 Greg Landrum
//
// @@ 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.
//
/*
To use include this in your CPPFLAGS:
$(shell pkg-config --cflags cairo)
and this in your LDFLAGS:
$(shell pkg-config --libs cairo)
*/
#ifndef _RD_DRAWING_TO_CAIRO_H_
#define _RD_DRAWING_TO_CAIRO_H_
#include <GraphMol/MolDrawing/MolDrawing.h>
#include <cairo.h>
namespace RDKit {
namespace Drawing {
namespace {
void setColorCairo(int atNum, cairo_t *cr) {
PRECONDITION(cr, "no context");
switch (atNum) {
case 7:
cairo_set_source_rgb(cr, 0.0, 0.0, 1.0);
break;
case 8:
cairo_set_source_rgb(cr, 1.0, 0.0, 0.0);
break;
case 9:
cairo_set_source_rgb(cr, 0.2, 0.8, 0.8);
break;
case 15:
cairo_set_source_rgb(cr, 1.0, 0.5, 0.0);
break;
case 16:
cairo_set_source_rgb(cr, 0.8, 0.8, 0.0);
break;
case 17:
cairo_set_source_rgb(cr, 0.0, 0.8, 0.0);
break;
case 35:
cairo_set_source_rgb(cr, 0.5, 0.3, 0.1);
break;
case 0:
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
break;
default:
cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
}
}
void drawLineCairo(std::vector<int>::const_iterator &pos, cairo_t *cr) {
PRECONDITION(cr, "no context");
int width = *pos++;
cairo_set_line_width(cr, width * 10);
int dashed = *pos++;
double dashes[2];
switch (dashed) {
case 0:
cairo_set_dash(cr, 0, 0, 0);
break;
case 2:
dashes[0] = 5.0;
dashes[1] = 20.0;
cairo_set_dash(cr, dashes, sizeof(dashes) / sizeof(dashes[0]), 0);
break;
default:
dashes[0] = 20.0;
dashes[1] = 20.0;
cairo_set_dash(cr, dashes, sizeof(dashes) / sizeof(dashes[0]), 0);
}
int an1 = *pos++;
int an2 = *pos++;
int xp1 = *pos++;
int yp1 = *pos++;
int xp2 = *pos++;
int yp2 = *pos++;
if (an1 == an2) {
setColorCairo(an1, cr);
cairo_move_to(cr, xp1, yp1);
cairo_line_to(cr, xp2, yp2);
cairo_stroke(cr);
} else {
int mx = xp1 + (xp2 - xp1) / 2;
int my = yp1 + (yp2 - yp1) / 2;
setColorCairo(an1, cr);
cairo_move_to(cr, xp1, yp1);
cairo_line_to(cr, mx, my);
cairo_stroke(cr);
setColorCairo(an2, cr);
cairo_move_to(cr, mx, my);
cairo_line_to(cr, xp2, yp2);
cairo_stroke(cr);
}
}
void drawAtomCairo(std::vector<int>::const_iterator &pos, cairo_t *cr) {
PRECONDITION(cr, "no context");
int fontSz = 50;
int atNum = *pos++;
double xp = static_cast<double>(*pos++);
double yp = static_cast<double>(*pos++);
int slen = *pos++;
std::string label = "";
for (unsigned int i = 0; i < slen; ++i) {
label += (char)*pos++;
}
RDKit::Drawing::OrientType orient =
static_cast<RDKit::Drawing::OrientType>(*pos++);
if (label.length()) {
cairo_text_extents_t extents;
cairo_text_extents(cr, label.c_str(), &extents);
double twidth = extents.width, theight = extents.height;
switch (orient) {
case RDKit::Drawing::W:
xp -= twidth;
yp += theight / 2;
break;
case RDKit::Drawing::E:
yp += theight / 2;
break;
case RDKit::Drawing::S:
xp -= twidth / 2;
yp += theight;
break;
case RDKit::Drawing::N:
xp -= twidth / 2;
yp -= theight / 2;
break;
default:
xp -= twidth / 2;
yp += theight / 2;
}
cairo_set_source_rgb(cr, 1.0, 1., 1.);
cairo_rectangle(cr, xp - 10, yp - theight - 10, twidth + 20, theight + 20);
cairo_fill(cr);
cairo_move_to(cr, xp, yp);
setColorCairo(atNum, cr);
cairo_show_text(cr, label.c_str());
cairo_stroke(cr);
}
}
} // end of anonymous namespace
void DrawingToCairo(const std::vector<int> &drawing, cairo_t *cr, int width,
int height, int fontSize = 12,
int maxDotsPerAngstrom = 60) {
PRECONDITION(cr, "no context");
PRECONDITION(width > 0 && height > 0, "bad dimensions");
cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
cairo_rectangle(cr, 0, 0, width, height);
cairo_fill(cr);
cairo_select_font_face(cr, "sans", CAIRO_FONT_SLANT_NORMAL,
CAIRO_FONT_WEIGHT_NORMAL);
std::vector<int>::const_iterator pos = drawing.begin();
int resolution = 0;
if (*pos != Drawing::RESOLUTION) {
std::cerr << "no RESOLUTION token found" << std::endl;
return;
}
resolution = *(pos + 1);
PRECONDITION(resolution > 0, "bad resolution");
pos += 2;
if (*pos != Drawing::BOUNDS) {
std::cerr << "no bounds token found" << std::endl;
return;
}
pos += 3;
int dwidth, dheight;
dwidth = *pos++;
dheight = *pos++;
PRECONDITION(dwidth > 0 && dheight > 0, "bad dimensions");
// size of the image in angstroms:
double xSize, ySize;
xSize = static_cast<double>(dwidth) / resolution;
ySize = static_cast<double>(dheight) / resolution;
double scale = 1.0;
if (dwidth > width || dheight > height) {
if (static_cast<double>(dwidth) / width >
static_cast<double>(dheight) / height) {
scale = static_cast<double>(width) / dwidth;
} else {
scale = static_cast<double>(height) / dheight;
}
} else {
if (width / xSize > height / ySize) {
if (width / xSize > maxDotsPerAngstrom) {
scale = maxDotsPerAngstrom * xSize / width;
}
} else {
if (height / ySize > maxDotsPerAngstrom) {
scale = maxDotsPerAngstrom * ySize / height;
}
}
}
scale *= 0.80;
cairo_translate(cr, .5 * (width - dwidth * scale),
.5 * (height - dheight * scale));
cairo_scale(cr, scale, scale);
// scaling factors here are empirically determined
double dFontSize = 1.5 * maxDotsPerAngstrom * fontSize / 14;
cairo_set_font_size(cr, dFontSize);
while (pos != drawing.end()) {
int token = *pos++;
switch (token) {
case Drawing::LINE:
drawLineCairo(pos, cr);
break;
case Drawing::ATOM:
drawAtomCairo(pos, cr);
break;
default:
std::cerr << "unrecognized token: " << token << std::endl;
}
}
}
} // end of namespace Drawing
} // end of namespace RDKit
#endif