// // Copyright (C) 2015 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. // // derived from Dave Cosgrove's MolDraw2D // #include "MolDraw2DSVG.h" #include #include #include namespace RDKit { namespace { std::string DrawColourToSVG(const DrawColour &col) { const char *convert = "0123456789ABCDEF"; std::string res(7, ' '); res[0] = '#'; unsigned int v; unsigned int i = 1; v = int(255 * col.get<0>()); if (v > 255) throw ValueErrorException( "elements of the color should be between 0 and 1"); res[i++] = convert[v / 16]; res[i++] = convert[v % 16]; v = int(255 * col.get<1>()); if (v > 255) throw ValueErrorException( "elements of the color should be between 0 and 1"); res[i++] = convert[v / 16]; res[i++] = convert[v % 16]; v = int(255 * col.get<2>()); if (v > 255) throw ValueErrorException( "elements of the color should be between 0 and 1"); res[i++] = convert[v / 16]; res[i++] = convert[v % 16]; return res; } } void MolDraw2DSVG::initDrawing() { d_os << "\n"; d_os << "\n"; // d_os<<""; } // **************************************************************************** void MolDraw2DSVG::finishDrawing() { // d_os << ""; d_os << "\n"; } // **************************************************************************** void MolDraw2DSVG::setColour(const DrawColour &col) { MolDraw2D::setColour(col); } void MolDraw2DSVG::drawWavyLine(const Point2D &cds1, const Point2D &cds2, const DrawColour &col1, const DrawColour &col2, unsigned int nSegments, double vertOffset) { PRECONDITION(nSegments > 1, "too few segments"); if (nSegments % 2) ++nSegments; // we're going to assume an even number of segments setColour(col1); Point2D perp = calcPerpendicular(cds1, cds2); Point2D delta = (cds2 - cds1); perp *= vertOffset; delta /= nSegments; Point2D c1 = getDrawCoords(cds1); std::string col = DrawColourToSVG(colour()); unsigned int width = lineWidth(); d_os << "\n"; } // **************************************************************************** void MolDraw2DSVG::drawLine(const Point2D &cds1, const Point2D &cds2) { Point2D c1 = getDrawCoords(cds1); Point2D c2 = getDrawCoords(cds2); std::string col = DrawColourToSVG(colour()); unsigned int width = lineWidth(); std::string dashString = ""; const DashPattern &dashes = dash(); if (dashes.size()) { std::stringstream dss; dss << ";stroke-dasharray:"; std::copy(dashes.begin(), dashes.end() - 1, std::ostream_iterator(dss, ",")); dss << dashes.back(); dashString = dss.str(); } d_os << "\n"; } // **************************************************************************** // draw the char, with the bottom left hand corner at cds void MolDraw2DSVG::drawChar(char c, const Point2D &cds) { unsigned int fontSz = scale() * fontSize(); std::string col = DrawColourToSVG(colour()); d_os << ""; d_os << c; d_os << ""; } // **************************************************************************** void MolDraw2DSVG::drawPolygon(const std::vector &cds) { PRECONDITION(cds.size() >= 3, "must have at least three points"); std::string col = DrawColourToSVG(colour()); unsigned int width = lineWidth(); std::string dashString = ""; d_os << "\n"; } void MolDraw2DSVG::drawEllipse(const Point2D &cds1, const Point2D &cds2) { Point2D c1 = getDrawCoords(cds1); Point2D c2 = getDrawCoords(cds2); double w = c2.x - c1.x; double h = c2.y - c1.y; double cx = c1.x + w / 2; double cy = c1.y + h / 2; w = w > 0 ? w : -1 * w; h = h > 0 ? h : -1 * h; std::string col = DrawColourToSVG(colour()); unsigned int width = lineWidth(); std::string dashString = ""; d_os << "\n"; } // **************************************************************************** void MolDraw2DSVG::clearDrawing() { std::string col = DrawColourToSVG(drawOptions().backgroundColour); d_os << " \n"; } // **************************************************************************** void MolDraw2DSVG::setFontSize(double new_size) { MolDraw2D::setFontSize(new_size); // double font_size_in_points = fontSize() * scale(); } // **************************************************************************** // using the current scale, work out the size of the label in molecule // coordinates void MolDraw2DSVG::getStringSize(const std::string &label, double &label_width, double &label_height) const { label_width = 0.0; label_height = 0.0; TextDrawType draw_mode = TextDrawNormal; bool had_a_super = false; bool had_a_sub = false; for (int i = 0, is = label.length(); i < is; ++i) { // setStringDrawMode moves i along to the end of any or // markup if ('<' == label[i] && setStringDrawMode(label, draw_mode, i)) { continue; } label_height = fontSize(); double char_width = fontSize() * static_cast(MolDraw2D_detail::char_widths[(int)label[i]]) / MolDraw2D_detail::char_widths[(int)'M']; if (TextDrawSubscript == draw_mode) { char_width *= 0.5; had_a_sub = true; } else if (TextDrawSuperscript == draw_mode) { char_width *= 0.5; had_a_super = true; } label_width += char_width; } // subscript keeps its bottom in line with the bottom of the bit chars, // superscript goes above the original char top by a bit (empirical) if (had_a_super) { label_height *= 1.1; } if (had_a_sub) { label_height *= 1.1; } } namespace { void escape_xhtml(std::string &data) { boost::algorithm::replace_all(data, "&", "&"); boost::algorithm::replace_all(data, "\"", """); boost::algorithm::replace_all(data, "\'", "'"); boost::algorithm::replace_all(data, "<", "<"); boost::algorithm::replace_all(data, ">", ">"); } } // **************************************************************************** // draws the string centred on cds void MolDraw2DSVG::drawString(const std::string &str, const Point2D &cds) { unsigned int fontSz = scale() * fontSize(); double string_width, string_height; getStringSize(str, string_width, string_height); double draw_x = cds.x - string_width / 2.0; double draw_y = cds.y - string_height / 2.0; #if 0 // for debugging text output DrawColour tcolour =colour(); setColour(DrawColour(.8,.8,.8)); std::vector poly; poly.push_back(Point2D(draw_x,draw_y)); poly.push_back(Point2D(draw_x+string_width,draw_y)); poly.push_back(Point2D(draw_x+string_width,draw_y+string_height)); poly.push_back(Point2D(draw_x,draw_y+string_height)); drawPolygon(poly); setColour(tcolour); #endif std::string col = DrawColourToSVG(colour()); Point2D draw_coords = getDrawCoords(Point2D(draw_x, draw_y)); d_os << ""; TextDrawType draw_mode = TextDrawNormal; // 0 for normal, 1 for superscript, 2 for subscript std::string span; bool first_span = true; for (int i = 0, is = str.length(); i < is; ++i) { // setStringDrawMode moves i along to the end of any or // markup if ('<' == str[i] && setStringDrawMode(str, draw_mode, i)) { if (!first_span) { escape_xhtml(span); d_os << span << ""; span = ""; } first_span = false; d_os << ""; continue; } if (first_span) { first_span = false; d_os << ""; span = ""; } span += str[i]; } escape_xhtml(span); d_os << span << ""; d_os << "\n"; } void MolDraw2DSVG::tagAtoms(const ROMol &mol) { PRECONDITION(d_os, "no output stream"); ROMol::VERTEX_ITER this_at, end_at; boost::tie(this_at, end_at) = mol.getVertices(); while (this_at != end_at) { int this_idx = mol[*this_at]->getIdx(); ++this_at; Point2D pos = getDrawCoords(atomCoords()[this_idx]); std::string lbl = atomSyms()[this_idx].first; d_os << "" << std::endl; } } } // EO namespace RDKit