Fix bond join smoothing in images of reaction products (#8210)

* Fix bond smoothing - apply atom index offset to products.
Update Hash codes.
Tweak side_by_side_images.py.

* Added hash code for new file.  Doh!

---------

Co-authored-by: David Cosgrove <david@cozchemix.co.uk>
This commit is contained in:
David Cosgrove
2025-01-27 11:44:50 +00:00
committed by greg landrum
parent 6c77e3940a
commit d45395848f
5 changed files with 48 additions and 16 deletions

View File

@@ -3485,6 +3485,10 @@ void DrawMol::smoothBondJoins() {
// classes for the atoms and bond it involves, and people use this to
// identify the lines for other purposes.
for (auto atom : drawMol_->atoms()) {
// If there's an atom label, there is no join.
if (atomLabels_[atom->getIdx()]) {
continue;
}
bool doIt = false;
if (atom->getDegree() == 2) {
doIt = true;
@@ -3499,15 +3503,16 @@ void DrawMol::smoothBondJoins() {
}
}
}
int adjAtomIdx = atom->getIdx() + activeAtmIdxOffset_;
if (doIt) {
bool done = false;
for (unsigned int i = 0; i < singleBondLines_.size(); ++i) {
auto &sbl1 = bonds_[singleBondLines_[i]];
int p1 = -1;
int p2 = -1;
if (static_cast<int>(atom->getIdx()) == sbl1->atom1_) {
if (adjAtomIdx == sbl1->atom1_) {
p1 = 0;
} else if (static_cast<int>(atom->getIdx()) == sbl1->atom2_) {
} else if (adjAtomIdx == sbl1->atom2_) {
p1 = 1;
}
if (p1 != -1) {
@@ -3516,9 +3521,9 @@ void DrawMol::smoothBondJoins() {
continue;
}
auto &sbl2 = bonds_[singleBondLines_[j]];
if (static_cast<int>(atom->getIdx()) == sbl2->atom1_) {
if (adjAtomIdx == sbl2->atom1_) {
p2 = 0;
} else if (static_cast<int>(atom->getIdx()) == sbl2->atom2_) {
} else if (adjAtomIdx == sbl2->atom2_) {
p2 = 1;
}
if (p2 != -1) {

View File

@@ -315,7 +315,7 @@ void MolDraw2D::drawReaction(
frac *= 5 / delta;
}
xOffset = drawReactionPart(agents, 0, xOffset, offsets);
xOffset = drawReactionPart(products, plusWidth, xOffset, offsets);
drawReactionPart(products, plusWidth, xOffset, offsets);
auto osbw = drawOptions().scaleBondWidth;
if (reagents.empty() && products.empty() && agents.empty()) {
// if it's an empty reaction, we won't have a DrawMol with a scale,

View File

@@ -159,8 +159,8 @@ const std::map<std::string, std::hash_result_t> SVG_HASHES = {
{"testHydrogenBonds2.svg", 645414593U},
{"testGithub3912.1.svg", 2513727029U},
{"testGithub3912.2.svg", 3814673891U},
{"testGithub2976.svg", 2669316911U},
{"testReactionCoords.svg", 402445764U},
{"testGithub2976.svg", 3717916234U},
{"testReactionCoords.svg", 2572146469U},
{"testAnnotationColors.svg", 2216313312U},
{"testGithub4323_1.svg", 2536621192U},
{"testGithub4323_2.svg", 2120846759U},
@@ -319,9 +319,9 @@ const std::map<std::string, std::hash_result_t> SVG_HASHES = {
{"lasso_highlights_6.svg", 2113147733U},
{"lasso_highlights_7.svg", 514868036U},
{"lasso_highlights_8.svg", 3231367552U},
{"testGithub6685_1.svg", 1835717197U},
{"testGithub6685_2.svg", 116380465U},
{"testGithub6685_3.svg", 409385402U},
{"testGithub6685_1.svg", 1206031802U},
{"testGithub6685_2.svg", 1946154328U},
{"testGithub6685_3.svg", 617181155U},
{"testGithub6685_4.svg", 1239628830U},
{"bad_lasso_1.svg", 726527516U},
{"AtropCanon1.svg", 526339583U},
@@ -351,6 +351,7 @@ const std::map<std::string, std::hash_result_t> SVG_HASHES = {
{"testAtomAbbreviationsClash.svg", 1847939197U},
{"testBlackAtomsUnderHighlight.svg", 3916069581U},
{"testSmallReactionCanvas.svg", 1288652415U},
{"testReactionProductSmoothCorners.svg", 1712682118U},
};
// These PNG hashes aren't completely reliable due to floating point cruft,
@@ -10264,4 +10265,30 @@ TEST_CASE("Github8195 - Reaction rendering looks odd at small scales") {
}
}
#endif
}
}
TEST_CASE("Github8209 - Reaction products not having bond corners smoothed") {
std::string smiles =
"[#6]1-[#6]=[#6]-[#6]=[#6]-[#6]=1-[#6:1](=[#8])-[#8]."
"[#1:7]-[#7:4](-[#1,#6:5])-[#1,#6:6]>>"
"[#6]1(-[#6:1](-[#7:4](-[#1,#6:5])-[#1,#6:6])"
"=[#8])-[#6]=[#6]-[#6]=[#6]-[#6]=1";
bool useSmiles = false;
std::unique_ptr<ChemicalReaction> rxn(
RxnSmartsToChemicalReaction(smiles, nullptr, useSmiles));
REQUIRE(rxn);
MolDraw2DSVG drawer(450, 200, -1, -1, NO_FREETYPE);
drawer.drawReaction(*rxn);
drawer.finishDrawing();
auto text = drawer.getDrawingText();
std::ofstream outs("testReactionProductSmoothCorners.svg");
outs << text;
outs.close();
std::regex path(
"<path d='M (\\d+\\.\\d+),(\\d+\\.\\d+) L (\\d+\\.\\d+),(\\d+\\.\\d+) L (\\d+\\.\\d+),(\\d+\\.\\d+)' style='fill:none;stroke:#000000");
size_t nOccurrences =
std::distance(std::sregex_token_iterator(text.begin(), text.end(), path),
std::sregex_token_iterator());
CHECK(nOccurrences == 10);
check_file_hash("testReactionProductSmoothCorners.svg");
}

View File

@@ -51,10 +51,10 @@ static const std::map<std::string, std::hash_result_t> SVG_HASHES = {
{"rxn_test1_1.svg", 2645695747U}, {"rxn_test1_2.svg", 2640646320U},
{"rxn_test1_3.svg", 2781928588U}, {"rxn_test1_4.svg", 437863065U},
{"rxn_test1_5.svg", 2762972460U}, {"rxn_test1_6.svg", 2941728011U},
{"rxn_test1_7.svg", 1389845691U}, {"rxn_test2_1.svg", 3791815067U},
{"rxn_test2_2_1.svg", 2068242269U}, {"rxn_test2_2_2.svg", 269990608U},
{"rxn_test2_2_3.svg", 1544919807U}, {"rxn_test2_2_4.svg", 2015871327U},
{"rxn_test3_1.svg", 1359061920U}, {"rxn_test4_1.svg", 1727161552U},
{"rxn_test1_7.svg", 1831465890U}, {"rxn_test2_1.svg", 250961412U},
{"rxn_test2_2_1.svg", 3044844617U}, {"rxn_test2_2_2.svg", 269990608U},
{"rxn_test2_2_3.svg", 1940920292U}, {"rxn_test2_2_4.svg", 2015871327U},
{"rxn_test3_1.svg", 3415200111U}, {"rxn_test4_1.svg", 2557609172U},
{"rxn_test4_2.svg", 2032588691U},
};
#else

View File

@@ -51,7 +51,7 @@ with open(args.outfile, 'w') as f:
fns = fn.replace('.svg', '')
f.write(f''' <tr>
<td>{fns}</td>
<td><img src="{d1}/{fn}" alt="{fns}"/></td>
<td><span style="float:right"><img src="{d1}/{fn}" alt="{fns}"/></span></td>
<td><img src="{d2}/{fn}" alt="{fns}"/></td>
</tr>\n''')