mirror of
https://github.com/rdkit/rdkit.git
synced 2026-06-03 21:44:30 +08:00
Implement returnDrawCoords JSON option in MinimalLib to return 2D drawing coordinates when SVG or canvas depictions are generated (#8815)
Co-authored-by: ptosco <paolo.tosco@novartis.com>
This commit is contained in:
@@ -3980,6 +3980,75 @@ M END\n";
|
||||
free(pkl);
|
||||
}
|
||||
|
||||
void test_return_draw_coords() {
|
||||
const char *mb =
|
||||
"\n\
|
||||
RDKit 2D\n\
|
||||
\n\
|
||||
3 3 0 0 0 0 0 0 0 0999 V2000\n\
|
||||
0.0000 0.8930 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\
|
||||
0.7734 -0.4465 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\
|
||||
-0.7734 -0.4465 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0\n\
|
||||
1 2 1 0\n\
|
||||
2 3 1 0\n\
|
||||
1 3 1 0\n\
|
||||
M END\n";
|
||||
char *pkl;
|
||||
char *res;
|
||||
size_t pkl_size;
|
||||
size_t i;
|
||||
unsigned int above_tol;
|
||||
double reference[6];
|
||||
double highlight[6];
|
||||
|
||||
printf("--------------------------\n");
|
||||
printf(" test_return_draw_coords\n");
|
||||
|
||||
pkl = get_mol(mb, &pkl_size, "");
|
||||
assert(pkl && pkl_size);
|
||||
res = get_svg(
|
||||
pkl, pkl_size,
|
||||
"{\"width\":300,\"height\":200,\"padding\":0.2,\"returnDrawCoords\":true}");
|
||||
assert(res);
|
||||
assert(strstr(res, "\"drawCoords\":"));
|
||||
assert(strstr(res, "\"svg\":"));
|
||||
sscanf(res, "{\"drawCoords\":[[%lf,%lf],[%lf,%lf],[%lf,%lf]]", &reference[0],
|
||||
&reference[1], &reference[2], &reference[3], &reference[4],
|
||||
&reference[5]);
|
||||
free(res);
|
||||
res = get_svg(
|
||||
pkl, pkl_size,
|
||||
"{\"width\":300,\"height\":200,\"padding\":0.2,\"atoms\":[0],\"returnDrawCoords\":true}");
|
||||
assert(res);
|
||||
assert(strstr(res, "\"drawCoords\":"));
|
||||
assert(strstr(res, "\"svg\":"));
|
||||
sscanf(res, "{\"drawCoords\":[[%lf,%lf],[%lf,%lf],[%lf,%lf]]", &highlight[0],
|
||||
&highlight[1], &highlight[2], &highlight[3], &highlight[4],
|
||||
&highlight[5]);
|
||||
above_tol = 0;
|
||||
for (i = 0; !above_tol && i < 6; ++i) {
|
||||
above_tol = (fabs(reference[i] - highlight[i]) > 0.1);
|
||||
}
|
||||
assert(above_tol);
|
||||
free(res);
|
||||
res = get_svg(
|
||||
pkl, pkl_size,
|
||||
"{\"width\":300,\"height\":200,\"padding\":0.2,\"atoms\":[0],\"returnDrawCoords\":true,\"drawingExtentsInclude\":{\"ALL\":true,\"HIGHLIGHTS\":false}}");
|
||||
assert(res);
|
||||
assert(strstr(res, "\"drawCoords\":"));
|
||||
assert(strstr(res, "\"svg\":"));
|
||||
sscanf(res, "{\"drawCoords\":[[%lf,%lf],[%lf,%lf],[%lf,%lf]]", &highlight[0],
|
||||
&highlight[1], &highlight[2], &highlight[3], &highlight[4],
|
||||
&highlight[5]);
|
||||
above_tol = 0;
|
||||
for (i = 0; !above_tol && i < 6; ++i) {
|
||||
above_tol = (fabs(reference[i] - highlight[i]) > 0.1);
|
||||
}
|
||||
assert(!above_tol);
|
||||
free(res);
|
||||
free(pkl);
|
||||
}
|
||||
|
||||
int main() {
|
||||
enable_logging();
|
||||
char *vers = version();
|
||||
@@ -4021,5 +4090,6 @@ int main() {
|
||||
test_get_mol_remove_hs();
|
||||
test_png_metadata();
|
||||
test_drawing_extents_include();
|
||||
test_return_draw_coords();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -498,6 +498,7 @@ std::string process_details(rj::Document &doc, const std::string &details,
|
||||
GET_JSON_VALUE(doc, drawingDetails, forceCoords, Bool)
|
||||
GET_JSON_VALUE(doc, drawingDetails, wavyBonds, Bool)
|
||||
GET_JSON_VALUE(doc, drawingDetails, useMolBlockWedging, Bool)
|
||||
GET_JSON_VALUE(doc, drawingDetails, returnDrawCoords, Bool)
|
||||
|
||||
return "";
|
||||
}
|
||||
@@ -756,7 +757,12 @@ class SVGDrawerFromDetails : public DrawerFromDetails {
|
||||
std::string finalizeDrawing() {
|
||||
CHECK_INVARIANT(d_drawer, "d_drawer must not be null");
|
||||
d_drawer->finishDrawing();
|
||||
return d_drawer->getDrawingText();
|
||||
auto svg = d_drawer->getDrawingText();
|
||||
return createDrawingResult(svg);
|
||||
}
|
||||
const char *getDrawingResultKey() {
|
||||
static const char *SVG_KEY = "svg";
|
||||
return SVG_KEY;
|
||||
}
|
||||
std::unique_ptr<MolDraw2DSVG> d_drawer;
|
||||
};
|
||||
|
||||
@@ -11,9 +11,14 @@
|
||||
#include <GraphMol/MolDraw2D/MolDraw2DHelpers.h>
|
||||
#include <GraphMol/MolDraw2D/MolDraw2DUtils.h>
|
||||
#include <GraphMol/Chirality.h>
|
||||
#include <rapidjson/document.h>
|
||||
#include <rapidjson/stringbuffer.h>
|
||||
#include <rapidjson/writer.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rj = rapidjson;
|
||||
|
||||
namespace RDKit {
|
||||
namespace MinimalLib {
|
||||
|
||||
@@ -31,6 +36,7 @@ struct DrawingDetails {
|
||||
bool forceCoords = false;
|
||||
bool wavyBonds = false;
|
||||
bool useMolBlockWedging = false;
|
||||
bool returnDrawCoords = false;
|
||||
std::string legend;
|
||||
std::vector<int> atomIds;
|
||||
std::vector<int> bondIds;
|
||||
@@ -125,6 +131,13 @@ class DrawerFromDetails {
|
||||
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) {
|
||||
@@ -162,14 +175,42 @@ class DrawerFromDetails {
|
||||
MolDraw2DUtils::updateDrawerParamsFromJSON(drawer(), d_details);
|
||||
}
|
||||
}
|
||||
std::string createDrawingResult(const std::string &res) {
|
||||
if (!d_drawCoords) {
|
||||
return res;
|
||||
}
|
||||
rj::Document doc;
|
||||
doc.SetObject();
|
||||
rj::Value rjDrawCoords(rj::kArrayType);
|
||||
for (const auto &drawXY : *d_drawCoords) {
|
||||
rj::Value rjXY(rj::kArrayType);
|
||||
rjXY.PushBack(drawXY.x, doc.GetAllocator());
|
||||
rjXY.PushBack(drawXY.y, doc.GetAllocator());
|
||||
rjDrawCoords.PushBack(rjXY, doc.GetAllocator());
|
||||
}
|
||||
doc.AddMember("drawCoords", rjDrawCoords, doc.GetAllocator());
|
||||
const auto drawingResultKey = getDrawingResultKey();
|
||||
if (drawingResultKey) {
|
||||
doc.AddMember(rj::StringRef(drawingResultKey),
|
||||
rj::Value(res.c_str(), doc.GetAllocator()),
|
||||
doc.GetAllocator());
|
||||
}
|
||||
rj::StringBuffer buffer;
|
||||
rj::Writer<rj::StringBuffer> writer(buffer);
|
||||
writer.SetMaxDecimalPlaces(5);
|
||||
doc.Accept(writer);
|
||||
return buffer.GetString();
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace MinimalLib
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
rxn_callback("[C:1](=[O:2])-O.[N:3]>>[O:2]=[C:1]-[N:3]");
|
||||
});
|
||||
function form_to_details(details) {
|
||||
var controls = ["addAtomIndices", "addBondIndices", "explicitMethyl", "addStereoAnnotation"];
|
||||
var controls = ["addAtomIndices", "addBondIndices", "explicitMethyl", "addStereoAnnotation", "returnDrawCoords"];
|
||||
for (i in controls) {
|
||||
var control = controls[i];
|
||||
details[control] = document.getElementById(control).checked
|
||||
@@ -38,17 +38,33 @@
|
||||
var control = texts[i];
|
||||
details[control] = document.getElementById(control).value
|
||||
}
|
||||
if (document.getElementById("ignoreIndicesWhenCentering").checked) {
|
||||
details.drawingExtentsInclude = { ALL: true, ANNOTATIONS: false };
|
||||
details.padding = 0.1;
|
||||
} else {
|
||||
delete details.drawingExtentsInclude;
|
||||
delete details.padding;
|
||||
}
|
||||
}
|
||||
function draw_with_highlights(mol, details) {
|
||||
form_to_details(details);
|
||||
var tdetails = JSON.stringify(details)
|
||||
var svg = mol.get_svg_with_highlights(tdetails);
|
||||
if (svg == "") return;
|
||||
var res = mol.get_svg_with_highlights(tdetails);
|
||||
if (res == "") return;
|
||||
if (details.returnDrawCoords) {
|
||||
var {drawCoords, svg} = JSON.parse(res);
|
||||
console.log(`SVG drawCoords: ${JSON.stringify(drawCoords)}`);
|
||||
} else {
|
||||
var svg = res;
|
||||
}
|
||||
var ob = document.getElementById("drawing");
|
||||
ob.outerHTML = "<div id='drawing'>" + svg + "</div>";
|
||||
var canvas = document.getElementById("rdkit-canvas");
|
||||
mol.draw_to_canvas_with_highlights(canvas, tdetails);
|
||||
|
||||
res = mol.draw_to_canvas_with_highlights(canvas, tdetails);
|
||||
if (details.returnDrawCoords) {
|
||||
var {drawCoords} = JSON.parse(res);
|
||||
console.log(`canvas drawCoords: ${JSON.stringify(drawCoords)}`);
|
||||
}
|
||||
}
|
||||
function draw(mol) {
|
||||
var details = {};
|
||||
@@ -171,6 +187,11 @@
|
||||
<br />
|
||||
<input type="checkbox" id="explicitMethyl" name="explicitMethyl" onclick="option_changed(this);" />
|
||||
<label for="explicitMethyl">explicitMethyl</label>
|
||||
<input type="checkbox" id="ignoreIndicesWhenCentering" name="ignoreIndicesWhenCentering" onclick="option_changed(this);" />
|
||||
<label for="ignoreIndicesWhenCentering">ignoreIndicesWhenCentering</label>
|
||||
<br />
|
||||
<input type="checkbox" id="returnDrawCoords" name="returnDrawCoords" onclick="option_changed(this);" />
|
||||
<label for="returnDrawCoords">returnDrawCoords</label>
|
||||
<br />
|
||||
<input type="text" id="legend" onkeyup="option_changed(this);"><label for="legend">legend</label>
|
||||
<input type="text" id="legendFontSize" onkeyup="option_changed(this);"><label
|
||||
|
||||
@@ -59,7 +59,7 @@ class JSDrawerFromDetails : public MinimalLib::DrawerFromDetails {
|
||||
drawingDetails.noFreetype));
|
||||
updateDrawerParamsFromJSON();
|
||||
}
|
||||
std::string finalizeDrawing() { return ""; }
|
||||
std::string finalizeDrawing() { return createDrawingResult(""); }
|
||||
std::unique_ptr<MolDraw2DJS> d_drawer;
|
||||
emscripten::val d_ctx;
|
||||
};
|
||||
|
||||
@@ -4163,6 +4163,69 @@ function test_get_coords() {
|
||||
}
|
||||
}
|
||||
|
||||
function test_return_draw_coords() {
|
||||
var mb = `
|
||||
RDKit 2D
|
||||
|
||||
3 3 0 0 0 0 0 0 0 0999 V2000
|
||||
0.0000 0.8930 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
0.7734 -0.4465 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
-0.7734 -0.4465 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
1 2 1 0
|
||||
2 3 1 0
|
||||
1 3 1 0
|
||||
M END`;
|
||||
var reference;
|
||||
var highlight;
|
||||
var aboveTol;
|
||||
var mol = RDKitModule.get_mol(mb);
|
||||
assert(mol);
|
||||
res = mol.get_svg_with_highlights(JSON.stringify({
|
||||
width: 300,
|
||||
height: 200,
|
||||
padding: 0.2,
|
||||
returnDrawCoords: true
|
||||
}));
|
||||
assert(res);
|
||||
res = JSON.parse(res);
|
||||
assert(res.drawCoords);
|
||||
assert(res.svg);
|
||||
reference = res.drawCoords.flat();
|
||||
res = mol.get_svg_with_highlights(JSON.stringify({
|
||||
width: 300,
|
||||
height: 200,
|
||||
padding: 0.2,
|
||||
atoms: [0],
|
||||
returnDrawCoords: true
|
||||
}));
|
||||
assert(res);
|
||||
res = JSON.parse(res);
|
||||
assert(res.drawCoords);
|
||||
assert(res.svg);
|
||||
highlight = res.drawCoords.flat();
|
||||
aboveTol = reference.some((ref, i) => (Math.abs(ref - highlight[i]) > 0.1));
|
||||
assert(aboveTol);
|
||||
res = mol.get_svg_with_highlights(JSON.stringify({
|
||||
width: 300,
|
||||
height: 200,
|
||||
padding: 0.2,
|
||||
atoms: [0],
|
||||
returnDrawCoords: true,
|
||||
drawingExtentsInclude: {
|
||||
ALL: true,
|
||||
HIGHLIGHTS: false
|
||||
}
|
||||
}));
|
||||
assert(res);
|
||||
res = JSON.parse(res);
|
||||
assert(res.drawCoords);
|
||||
assert(res.svg);
|
||||
highlight = res.drawCoords.flat();
|
||||
aboveTol = reference.some((ref, i) => (Math.abs(ref - highlight[i]) > 0.1));
|
||||
assert(!aboveTol);
|
||||
mol.delete();
|
||||
}
|
||||
|
||||
initRDKitModule().then(function(instance) {
|
||||
var done = {};
|
||||
const waitAllTestsFinished = () => {
|
||||
@@ -4260,6 +4323,7 @@ initRDKitModule().then(function(instance) {
|
||||
test_png_metadata();
|
||||
test_combine_with();
|
||||
test_get_coords();
|
||||
test_return_draw_coords();
|
||||
|
||||
waitAllTestsFinished().then(() =>
|
||||
console.log("Tests finished successfully")
|
||||
|
||||
Reference in New Issue
Block a user