Files
rdkit/Code/GraphMol/MolDraw2D/AtomSymbol.cpp
David Cosgrove f9b47d907f Add ACS1996 drawing style (#5425)
* Started on ACS 1996 drawing mode.
Significant change (not by itself, sadly) is that MolDrawOptions::lineWidth has changed from int to double to allow for ACS requirement of 0.6px bond widths.

* Wavy lines and dashed wedges.

* Better dashed wedges.

* Rounder wavy bonds, same as SVG.

* Added FreeSans font for ACS1996 mode.

* Add help functions to write enum classes to ostream.

* Dashed wedge separation now 2.5px between line edges rather than line middles.  Therefore wider gap between lines.

* Increase offset for wavy bond.  Get classes for atoms and bonds in wavy bond correct.

* For SMILES input, option to force wavy and crossed bonds for unspecified stereochem.

* Tidy debugging.

* Extra space round atom labels.

* Extra space between chars in freetype string.

* Reformats.

* Change double bond offset.

* Improve ring double bonds.

* Simple non-ring double bonds all working.

* Tidy.

* All double bonds seem good.

* Remove redundant function.
Move calcTripleBondLines into DrawMol for consistency with calcDoubleBondLines.
Use doubleBondOffset for wavy lines.

* Correct spacing between FT chars.

* Tidying.

* Use MolBlock wedging if there is any.  Needs to be made an option.

* If dashed wedge thick end has bonds of it, stop one dash short.

* Adjust solid wedge ends to line with attached bonds.

* Width of wedge ends now based on double bond separation.

* Change catch_tests.cpp

* Rounder waves in wavy lines.

* Dashed wedges same width even if one dash less..

* Embedded Roboto-Regular font in code.

* Fix docstrings.

* doubleBondTerminal swapped ends.
Deal with O2 - 2 terminal atoms of degree 1.

* Fix terminal double bonds.

* Slightly fatter truncated wedge bonds.

* Fix crash on complicated double bonds.

* Control more assert tests with DO_TEST_ASSERT.

* Fix 2 colour solid wedges.
Fix slanted wedge for morphine (test1_5).

* Change definittion of multipleBondOffset to fraction of mean bond length.

* Don't slant end of solid wedge to atom symbol.

* Fix wiggle separation.

* Fix 2-colour terminal double bonds.

* Fix colours on triple bonds.

* Don't attempt to draw non-existent points in triangle..

* Symmetric bond for P=O and like.

* Fix query bonds.

* Reformatting.

* Tidy up use of font.

* Add FreeSans font and license to $RDBASE/Data/Fonts.

* Draw unspecified stereo as unknown.

* Add check_file_hash.

* Tidying.

* Tidying.

* Start Python wrappers.

* Fix solid wedges for 3-connected atoms.

* Docstrings.

* Tidying.

* Alter width of bond highlights in ACS 1996 mode.

* Expose setACS1996Options and mean BondLength in Python.

* Expose drawMolACS1996Cairo in Python.
Docstrings.

* Extra padding between legend and picture in flexicanvas.

* Python tests.

* Tidy catch tests.

* Tidying.

* Fix catch tests.

* Fix no Freetype tests.

* Draw solid wedge more sensibly..

* Fix bond end at solid wedge.

* Tidy.

* Fix Python Cairo build issues.

* Fix wedge end shape for terminal double bonds.

* Hide the joins at the bond ends.

* Fix gcc pickiness.

* Extra test for no atom labels.

* Change where it looks for FreeSans.ttf for ACS1996 drawings.

* Same number of waves for wavy bonds in SVG and Cairo.

* Same number of waves for wavy bonds in SVG and Cairo.

* rename unspecifiedStereoIsUnknown to markUnspecifiedStereoAsUnknown and move to MolFileStereochem.h

* refactor use of iterators

* py docs update

* undo a bunch of bad formatting changes

* remove FreeSans

* get windows builds working

* Fix problem with Windows build.

* Changes in response to review.

* Align description of unspecifiedStereoIsUnknown in C++ to match Python.

* Still working on file open modes.

* Took out extraneous functions for drawing in ACS1996 mode, including the one that was breaking the windows build.

* Add RDKIT_MOLDRAW2D_EXPORT.

* Fix expected test results.

* Clarified warning.

* RDKIT_MOLDRAW2D_EXPORT missing.

* Windows!

* Update Code/GraphMol/MolDraw2D/rxn_test1.cpp

Co-authored-by: David Cosgrove <david@cozchemix.co.uk>
Co-authored-by: greg landrum <greg.landrum@gmail.com>
2022-07-21 18:11:33 +02:00

160 lines
5.4 KiB
C++

//
// 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)
//
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <GraphMol/MolDraw2D/AtomSymbol.h>
#include <GraphMol/MolDraw2D/DrawText.h>
#include <GraphMol/MolDraw2D/MolDraw2D.h>
namespace RDKit {
namespace MolDraw2D_detail {
// ****************************************************************************
AtomSymbol::AtomSymbol(const std::string &symbol, int atIdx, OrientType orient,
const Point2D &cds, const DrawColour &colour,
DrawText &textDrawer)
: symbol_(symbol),
atIdx_(atIdx),
orient_(orient),
cds_(cds),
colour_(colour),
textDrawer_(textDrawer) {
if (symbol_.empty()) {
return;
}
textDrawer_.getStringRects(symbol_, orient_, rects_, drawModes_, drawChars_,
false, TextAlignType::MIDDLE);
adjustColons();
}
// ****************************************************************************
void AtomSymbol::findExtremes(double &xmin, double &xmax, double &ymin,
double &ymax) const {
Point2D bl, br, tl, tr, origTrans;
for (auto rect : rects_) {
origTrans = rect->trans_;
rect->trans_ += cds_;
rect->calcCorners(tl, tr, br, bl, 0.0);
// sometimes the rect is in a coordinate frame where +ve y is down,
// sometimes it's up. For these purposes, we don't care so long as
// the ymax is larger than the ymin. We probably don't need to do
// all the tests for xmin and xmax.
xmin = std::min({tr.x, bl.x, xmin});
ymin = std::min({tr.y, bl.y, ymin});
xmax = std::max({tr.x, bl.x, xmax});
ymax = std::max({tr.y, bl.y, ymax});
rect->trans_ = origTrans;
}
}
// ****************************************************************************
void AtomSymbol::scale(const Point2D &scaleFactor) {
cds_.x *= scaleFactor.x;
cds_.y *= scaleFactor.y;
// rebuild the rectangles, because the fontScale may be different,
// and the widths etc might not scale by the same amount.
rects_.clear();
drawModes_.clear();
drawChars_.clear();
textDrawer_.getStringRects(symbol_, orient_, rects_, drawModes_, drawChars_,
false, TextAlignType::MIDDLE);
}
// ****************************************************************************
void AtomSymbol::move(const Point2D &trans) { cds_ += trans; }
// ****************************************************************************
void AtomSymbol::draw(MolDraw2D &molDrawer) const {
std::string o_class = molDrawer.getActiveClass();
std::string actClass = o_class;
if (!actClass.empty()) {
actClass += " ";
}
actClass += (boost::format("atom-%d") % atIdx_).str();
molDrawer.setActiveClass(actClass);
textDrawer_.setColour(colour_);
textDrawer_.drawString(symbol_, cds_, orient_);
molDrawer.setActiveClass(o_class);
// drawRects(molDrawer);
}
// ****************************************************************************
bool AtomSymbol::doesRectClash(const StringRect &rect, double padding) const {
for (auto &alrect : rects_) {
auto oldTrans = alrect->trans_;
alrect->trans_ += cds_;
bool dii = alrect->doesItIntersect(rect, padding);
alrect->trans_ = oldTrans;
if (dii) {
return true;
}
}
return false;
}
// ****************************************************************************
void AtomSymbol::adjustColons() {
if (symbol_.empty()) {
return; // but probably it's always got something in it.
}
size_t colonPos = symbol_.find(':');
if (colonPos == std::string::npos) {
return;
}
// we need to allow for markup in the symbol, such as <lit>[CH2;X2:4]</lit>
// and the easiest way to do that is to use the fact that atomLabelToPieces
// strips it out.
std::string tmpSym = symbol_;
while (true) {
size_t ltPos = tmpSym.find('<');
if (ltPos == std::string::npos) {
break;
}
size_t gtPos = tmpSym.find('>');
tmpSym = tmpSym.substr(0, ltPos) + tmpSym.substr(gtPos + 1);
}
colonPos = tmpSym.find(':');
if (colonPos == std::string::npos) {
return;
}
CHECK_INVARIANT(colonPos <= rects_.size(), "bad rects_ size");
double leftHeight = colonPos ? rects_[colonPos - 1]->height_ : 0;
double rightHeight =
colonPos < symbol_.size() - 1 ? rects_[colonPos + 1]->height_ : 0;
rects_[colonPos]->height_ = std::min(leftHeight, rightHeight);
}
// ****************************************************************************
void AtomSymbol::drawRects(MolDraw2D &molDrawer) const {
Point2D tl, tr, br, bl, origTrans;
for (auto &rect : rects_) {
origTrans = rect->trans_;
rect->trans_ += cds_;
rect->calcCorners(tl, tr, br, bl, 0.0);
molDrawer.setColour(DrawColour(1.0, 0.0, 0.0));
molDrawer.drawLine(tl, tr, true);
molDrawer.setColour(DrawColour(0.0, 1.0, 0.0));
molDrawer.drawLine(tr, br, true);
molDrawer.setColour(DrawColour(0.0, 0.0, 1.0));
molDrawer.drawLine(br, bl, true);
molDrawer.setColour(DrawColour(0.0, 0.95, 0.95));
molDrawer.drawLine(bl, tl, true);
rect->trans_ = origTrans;
}
}
} // namespace MolDraw2D_detail
} // namespace RDKit