mirror of
https://github.com/rdkit/rdkit.git
synced 2026-06-03 21:44:30 +08:00
* First pass at port Mostly auto-converted using claude sonnet 4 Things are a bit slower in this initial port. Here's some timing data for molecules from SMILES (no coords) and from SDF (with coords) # MASTER ## smiles read: 50000 mols. 9.260000s wall, 8.650000s user + 0.600000s system = 9.250000s CPU (99.9%) serialize 3.060000s wall, 2.400000s user + 0.660000s system = 3.060000s CPU (100.0%) deserialize 1.350000s wall, 1.250000s user + 0.090000s system = 1.340000s CPU (99.3%) ## SDF read: 50000 mols. 9.340000s wall, 8.930000s user + 0.400000s system = 9.330000s CPU (99.9%) serialize 6.630000s wall, 5.960000s user + 0.680000s system = 6.640000s CPU (100.2%) deserialize 1.450000s wall, 1.450000s user + 0.000000s system = 1.450000s CPU (100.0%) # Boost::JSON ## smiles read: 50000 mols. 9.250000s wall, 8.830000s user + 0.420000s system = 9.250000s CPU (100.0%) serialize 4.770000s wall, 4.410000s user + 0.350000s system = 4.760000s CPU (99.8%) deserialize 2.320000s wall, 2.100000s user + 0.230000s system = 2.330000s CPU (100.4%) ## SDF read: 50000 mols. 9.500000s wall, 9.100000s user + 0.400000s system = 9.500000s CPU (100.0%) serialize 8.760000s wall, 8.330000s user + 0.420000s system = 8.750000s CPU (99.9%) deserialize 2.540000s wall, 2.330000s user + 0.210000s system = 2.540000s CPU (100.0%) * some json parser optimization * around the edges * optimizations for the writer * hopefully get things compiling * convert the MinimalLib stuff to use boost::json Again, a lot of the lifting here was done using Claude Sonnet 4 in VS Code Copilot * fix Windows DLL build * response to review Co-authored-by: Paolo Tosco <paolo.tosco.mail@gmail.com> * better not to blindly accept suggestions * fix the problems in MinimalLib --------- Co-authored-by: Paolo Tosco <paolo.tosco.mail@gmail.com> Co-authored-by: = <=>
214 lines
7.5 KiB
C++
214 lines
7.5 KiB
C++
//
|
|
// Copyright (C) 2024 Novartis 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.
|
|
//
|
|
#include <GraphMol/MolDraw2D/MolDraw2D.h>
|
|
#include <GraphMol/MolDraw2D/MolDraw2DHelpers.h>
|
|
#include <GraphMol/MolDraw2D/MolDraw2DUtils.h>
|
|
#include <GraphMol/Chirality.h>
|
|
#include <boost/json.hpp>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace bj = boost::json;
|
|
|
|
namespace RDKit {
|
|
namespace MinimalLib {
|
|
|
|
struct DrawingDetails {
|
|
int width = -1;
|
|
int height = -1;
|
|
int offsetx = 0;
|
|
int offsety = 0;
|
|
int panelWidth = -1;
|
|
int panelHeight = -1;
|
|
bool noFreetype = false;
|
|
bool kekulize = true;
|
|
bool addChiralHs = true;
|
|
bool wedgeBonds = true;
|
|
bool forceCoords = false;
|
|
bool wavyBonds = false;
|
|
bool useMolBlockWedging = false;
|
|
bool returnDrawCoords = false;
|
|
std::string legend;
|
|
std::vector<int> atomIds;
|
|
std::vector<int> bondIds;
|
|
};
|
|
|
|
struct MolDrawingDetails : public DrawingDetails {
|
|
std::map<int, DrawColour> atomMap;
|
|
std::map<int, DrawColour> bondMap;
|
|
std::map<int, std::vector<DrawColour>> atomMultiMap;
|
|
std::map<int, std::vector<DrawColour>> bondMultiMap;
|
|
std::map<int, double> radiiMap;
|
|
std::map<int, int> lineWidthMultiplierMap;
|
|
};
|
|
|
|
struct RxnDrawingDetails : public DrawingDetails {
|
|
bool highlightByReactant = false;
|
|
std::vector<DrawColour> highlightColorsReactants;
|
|
};
|
|
|
|
extern std::string process_mol_details(const std::string &details,
|
|
MolDrawingDetails &molDrawingDetails);
|
|
[[deprecated(
|
|
"please use the overload taking MolDrawingDetails& as parameter")]] extern std::
|
|
string
|
|
process_mol_details(const std::string &details, int &width, int &height,
|
|
int &offsetx, int &offsety, std::string &legend,
|
|
std::vector<int> &atomIds, std::vector<int> &bondIds,
|
|
std::map<int, DrawColour> &atomMap,
|
|
std::map<int, DrawColour> &bondMap,
|
|
std::map<int, double> &radiiMap, bool &kekulize,
|
|
bool &addChiralHs, bool &wedgeBonds, bool &forceCoords,
|
|
bool &wavyBonds);
|
|
|
|
extern std::string process_rxn_details(const std::string &details,
|
|
RxnDrawingDetails &rxnDrawingDetails);
|
|
[[deprecated(
|
|
"please use the overload taking RxnDrawingDetails& as parameter")]] extern std::
|
|
string
|
|
process_rxn_details(const std::string &details, int &width, int &height,
|
|
int &offsetx, int &offsety, std::string &legend,
|
|
std::vector<int> &atomIds, std::vector<int> &bondIds,
|
|
bool &kekulize, bool &highlightByReactant,
|
|
std::vector<DrawColour> &highlightColorsReactants);
|
|
|
|
class DrawerFromDetails {
|
|
public:
|
|
virtual ~DrawerFromDetails() {}
|
|
std::string draw_mol(const ROMol &mol) {
|
|
MolDrawingDetails molDrawingDetails;
|
|
molDrawingDetails.width = d_width;
|
|
molDrawingDetails.height = d_height;
|
|
if (!d_details.empty()) {
|
|
auto problems = process_mol_details(d_details, molDrawingDetails);
|
|
if (!problems.empty()) {
|
|
return problems;
|
|
}
|
|
}
|
|
initDrawer(molDrawingDetails);
|
|
const ROMol *molPtr = &mol;
|
|
std::unique_ptr<RWMol> drawnMol;
|
|
bool haveMultiMap = (!molDrawingDetails.atomMultiMap.empty() ||
|
|
!molDrawingDetails.bondMultiMap.empty());
|
|
if (molDrawingDetails.useMolBlockWedging || haveMultiMap) {
|
|
drawnMol.reset(new RWMol(mol));
|
|
molPtr = static_cast<ROMol *>(drawnMol.get());
|
|
if (molDrawingDetails.useMolBlockWedging) {
|
|
Chirality::reapplyMolBlockWedging(*drawnMol);
|
|
drawer().drawOptions().useMolBlockWedging = false;
|
|
}
|
|
}
|
|
drawer().setOffset(molDrawingDetails.offsetx, molDrawingDetails.offsety);
|
|
if (!haveMultiMap) {
|
|
MolDraw2DUtils::prepareAndDrawMolecule(
|
|
drawer(), *molPtr, molDrawingDetails.legend,
|
|
&molDrawingDetails.atomIds, &molDrawingDetails.bondIds,
|
|
molDrawingDetails.atomMap.empty() ? nullptr
|
|
: &molDrawingDetails.atomMap,
|
|
molDrawingDetails.bondMap.empty() ? nullptr
|
|
: &molDrawingDetails.bondMap,
|
|
molDrawingDetails.radiiMap.empty() ? nullptr
|
|
: &molDrawingDetails.radiiMap,
|
|
-1, molDrawingDetails.kekulize, molDrawingDetails.addChiralHs,
|
|
molDrawingDetails.wedgeBonds, molDrawingDetails.forceCoords,
|
|
molDrawingDetails.wavyBonds);
|
|
} else {
|
|
MolDraw2DUtils::prepareMolForDrawing(
|
|
*drawnMol, molDrawingDetails.kekulize, molDrawingDetails.addChiralHs,
|
|
molDrawingDetails.wedgeBonds, molDrawingDetails.forceCoords,
|
|
molDrawingDetails.wavyBonds);
|
|
drawer().drawMoleculeWithHighlights(
|
|
*drawnMol, molDrawingDetails.legend, molDrawingDetails.atomMultiMap,
|
|
molDrawingDetails.bondMultiMap, molDrawingDetails.radiiMap,
|
|
molDrawingDetails.lineWidthMultiplierMap);
|
|
}
|
|
if (molDrawingDetails.returnDrawCoords) {
|
|
d_drawCoords.reset(new std::vector<RDGeom::Point2D>());
|
|
d_drawCoords->reserve(molPtr->getNumAtoms());
|
|
for (size_t i = 0; i < molPtr->getNumAtoms(); ++i) {
|
|
d_drawCoords->push_back(drawer().getDrawCoords(i));
|
|
}
|
|
}
|
|
return finalizeDrawing();
|
|
}
|
|
std::string draw_rxn(const ChemicalReaction &rxn) {
|
|
RxnDrawingDetails rxnDrawingDetails;
|
|
rxnDrawingDetails.width = d_width;
|
|
rxnDrawingDetails.height = d_height;
|
|
if (!d_details.empty()) {
|
|
auto problems = process_rxn_details(d_details, rxnDrawingDetails);
|
|
if (!problems.empty()) {
|
|
return problems;
|
|
}
|
|
}
|
|
initDrawer(rxnDrawingDetails);
|
|
if (!rxnDrawingDetails.kekulize) {
|
|
drawer().drawOptions().prepareMolsBeforeDrawing = false;
|
|
}
|
|
drawer().drawReaction(
|
|
rxn, rxnDrawingDetails.highlightByReactant,
|
|
!rxnDrawingDetails.highlightByReactant ||
|
|
rxnDrawingDetails.highlightColorsReactants.empty()
|
|
? nullptr
|
|
: &rxnDrawingDetails.highlightColorsReactants);
|
|
return finalizeDrawing();
|
|
}
|
|
|
|
protected:
|
|
void init(int w = -1, int h = -1,
|
|
const std::string &details = std::string()) {
|
|
d_width = w;
|
|
d_height = h;
|
|
d_details = details;
|
|
}
|
|
void updateDrawerParamsFromJSON() {
|
|
if (!d_details.empty()) {
|
|
MolDraw2DUtils::updateDrawerParamsFromJSON(drawer(), d_details);
|
|
}
|
|
}
|
|
std::string createDrawingResult(const std::string &res) {
|
|
if (!d_drawCoords) {
|
|
return res;
|
|
}
|
|
bj::object doc;
|
|
bj::array bjDrawCoords;
|
|
for (const auto &drawXY : *d_drawCoords) {
|
|
bj::array bjXY;
|
|
bjXY.push_back(drawXY.x);
|
|
bjXY.push_back(drawXY.y);
|
|
bjDrawCoords.push_back(std::move(bjXY));
|
|
}
|
|
doc["drawCoords"] = bjDrawCoords;
|
|
const auto drawingResultKey = getDrawingResultKey();
|
|
if (drawingResultKey) {
|
|
doc[drawingResultKey] = res;
|
|
}
|
|
return bj::serialize(doc);
|
|
}
|
|
|
|
private:
|
|
virtual MolDraw2D &drawer() const = 0;
|
|
virtual void initDrawer(const DrawingDetails &drawingDetails) = 0;
|
|
virtual std::string finalizeDrawing() = 0;
|
|
virtual const char *getDrawingResultKey() { return nullptr; };
|
|
int d_width;
|
|
int d_height;
|
|
std::string d_details;
|
|
std::unique_ptr<std::vector<RDGeom::Point2D>> d_drawCoords;
|
|
};
|
|
|
|
enum class MDLVersion {
|
|
AUTO,
|
|
V2000,
|
|
V3000
|
|
};
|
|
} // namespace MinimalLib
|
|
} // namespace RDKit
|