From 0c7212686bfd54c51442f1b7bd58d07c3a44b1fe Mon Sep 17 00:00:00 2001 From: Kirill Taran Date: Thu, 7 Apr 2016 18:37:20 +0300 Subject: [PATCH] indigo-utils: rendering sgroups #8, depict version + inchi parameters #18, charged pseudos rendering #41 --- common/math/algebra.h | 53 +++ molecule/molecule_sgroups.h | 1 - molecule/src/base_molecule.cpp | 8 +- molecule/src/molecule_auto_loader.cpp | 51 ++- molecule/src/molfile_loader.cpp | 5 + render2d/render_internal.h | 60 +++- render2d/render_item_fragment.h | 2 +- render2d/src/render.cpp | 3 +- render2d/src/render_internal.cpp | 473 ++++++++++++++------------ utils/indigo-depict/main.c | 7 +- 10 files changed, 414 insertions(+), 249 deletions(-) diff --git a/common/math/algebra.h b/common/math/algebra.h index 72b8c842ca..9fcf4275aa 100644 --- a/common/math/algebra.h +++ b/common/math/algebra.h @@ -238,6 +238,59 @@ struct Vec2f DLLEXPORT static Vec2f get_circle_center(Vec2f a, Vec2f b, Vec2f c); }; +struct Rect2f { + + explicit Rect2f () {} + + Rect2f (Vec2f a, Vec2f b) + { + _leftBottom = a; + _leftBottom.min(b); + _rightTop = a; + _rightTop.max(b); + } + + Rect2f (Rect2f a, Rect2f b) + { + _leftBottom = a._leftBottom; + _leftBottom.min(b._leftBottom); + _rightTop = a._rightTop; + _rightTop.max(b._rightTop); + } + + inline void copy (Rect2f &other) + { + _leftBottom = other._leftBottom; + _rightTop = other._rightTop; + } + + inline float left() const { return _leftBottom.x; } + inline float right() const { return _rightTop.x; } + inline float bottom() const { return _leftBottom.y; } + inline float top() const { return _rightTop.y; } + + inline float middleX() const { return (_leftBottom.x + _rightTop.x) / 2; } + inline float middleY() const { return (_leftBottom.y + _rightTop.y) / 2; } + + inline Vec2f leftBottom() const { return _leftBottom; } + inline Vec2f rightTop() const { return _rightTop; } + + inline Vec2f leftTop() const { return Vec2f(left(), top()); } + inline Vec2f rightBottom() const { return Vec2f(right(), bottom()); } + + inline Vec2f leftMiddle() const { return Vec2f(left(), middleY()); } + inline Vec2f rightMiddle() const { return Vec2f(right(), middleY()); } + + inline Vec2f bottomMiddle() const { return Vec2f(middleX(), bottom()); } + inline Vec2f topMiddle() const { return Vec2f(middleX(), top()); } + + inline Vec2f center() const { return Vec2f(middleX(), middleY()); } + +protected: + Vec2f _leftBottom; + Vec2f _rightTop; +}; + struct Vec3f { Vec3f () : x(0), y(0), z(0) {} diff --git a/molecule/molecule_sgroups.h b/molecule/molecule_sgroups.h index 978d1bc5a9..c51268c973 100644 --- a/molecule/molecule_sgroups.h +++ b/molecule/molecule_sgroups.h @@ -235,7 +235,6 @@ class DLLEXPORT MoleculeSGroups bool _cmpIndices (Array &t_inds, Array &q_inds); }; - } #ifdef _WIN32 diff --git a/molecule/src/base_molecule.cpp b/molecule/src/base_molecule.cpp index 19fd7fc9c3..c5c31751ba 100644 --- a/molecule/src/base_molecule.cpp +++ b/molecule/src/base_molecule.cpp @@ -119,7 +119,9 @@ void BaseMolecule::mergeSGroupsWithSubmolecule (BaseMolecule &mol, Array &m SGroup &supersg = mol.sgroups.getSGroup(i); int idx = sgroups.addSGroup(supersg.sgroup_type); SGroup &sg = sgroups.getSGroup(idx); - if (_mergeSGroupWithSubmolecule(sg, supersg, mol, mapping, edge_mapping)) + + //TODO: check + _mergeSGroupWithSubmolecule(sg, supersg, mol, mapping, edge_mapping); { if (sg.sgroup_type == SGroup::SG_TYPE_DAT) { @@ -193,8 +195,6 @@ void BaseMolecule::mergeSGroupsWithSubmolecule (BaseMolecule &mol, Array &m mg.parent_atoms.push(mapping[supermg.parent_atoms[j]]); } } - else - sgroups.remove(idx); } } @@ -1514,6 +1514,8 @@ bool BaseMolecule::_mergeSGroupWithSubmolecule (SGroup &sgroup, SGroup &super, B int i; bool merged = false; + sgroup.parent_group = super.parent_group; + sgroup.sgroup_subtype = super.sgroup_subtype; sgroup.brackets.copy(super.brackets); diff --git a/molecule/src/molecule_auto_loader.cpp b/molecule/src/molecule_auto_loader.cpp index 654d6030fc..2e6241aeb5 100644 --- a/molecule/src/molecule_auto_loader.cpp +++ b/molecule/src/molecule_auto_loader.cpp @@ -25,6 +25,7 @@ #include "molecule/molecule_cml_loader.h" #include "molecule/sdf_loader.h" #include "molecule/molecule_cdx_loader.h" +#include "molecule/inchi_wrapper.h" using namespace indigo; @@ -240,22 +241,44 @@ void MoleculeAutoLoader::_loadMolecule (BaseMolecule &mol, bool query) _scanner->seek(pos, SEEK_SET); } - // check for SMILES format - if (Scanner::isSingleLine(*_scanner)) - { - SmilesLoader loader(*_scanner); + // check for single line formats + if (Scanner::isSingleLine(*_scanner)) { + // check for InChI format + { + char prefix[6]; + int start = _scanner->tell(); + _scanner->readCharsFix(6, prefix); + _scanner->seek(start, SEEK_SET); - loader.ignore_closing_bond_direction_mismatch = - ignore_closing_bond_direction_mismatch; - loader.stereochemistry_options = stereochemistry_options; - loader.ignore_cistrans_errors = ignore_cistrans_errors; - if (query) - loader.loadQueryMolecule((QueryMolecule &)mol); - else - loader.loadMolecule((Molecule &)mol); - return; - } + if (!strncmp(prefix, "InChI=", 6)) { + if (query) { + throw Error("InChI input doesn't support query molecules"); + } + + Array inchi; + _scanner->readWord(inchi, " "); + InchiWrapper loader; + loader.loadMoleculeFromInchi(inchi.ptr(), (Molecule &)mol); + return; + } + } + + // if not InChI then SMILES + { + SmilesLoader loader(*_scanner); + + loader.ignore_closing_bond_direction_mismatch = + ignore_closing_bond_direction_mismatch; + loader.stereochemistry_options = stereochemistry_options; + loader.ignore_cistrans_errors = ignore_cistrans_errors; + if (query) + loader.loadQueryMolecule((QueryMolecule &)mol); + else + loader.loadMolecule((Molecule &)mol); + return; + } + } // check for CDX format /* diff --git a/molecule/src/molfile_loader.cpp b/molecule/src/molfile_loader.cpp index fa6eefd004..317a259e53 100644 --- a/molecule/src/molfile_loader.cpp +++ b/molecule/src/molfile_loader.cpp @@ -2986,6 +2986,8 @@ void MolfileLoader::_readSGroup3000 (const char *str) BufferScanner scanner(str); QS_DEF(Array, type); QS_DEF(Array, entity); + entity.clear(); + type.clear(); MoleculeSGroups *sgroups = &_bmol->sgroups; @@ -3062,6 +3064,7 @@ void MolfileLoader::_readSGroup3000 (const char *str) else if (strcmp(entity.ptr(), "SUBTYPE") == 0) { QS_DEF(Array, subtype); + subtype.clear(); scanner.readWord(subtype, 0); if (strcmp(subtype.ptr(), "ALT") == 0) sgroup->sgroup_subtype = SGroup::SG_SUBTYPE_ALT; @@ -3084,6 +3087,7 @@ void MolfileLoader::_readSGroup3000 (const char *str) else if (strcmp(entity.ptr(), "BRKTYP") == 0) { QS_DEF(Array, style); + style.clear(); scanner.readWord(style, 0); if (strcmp(style.ptr(), "BRACKET") == 0) sgroup->brk_style = _BRKTYP_SQUARE; @@ -3137,6 +3141,7 @@ void MolfileLoader::_readSGroup3000 (const char *str) else if (strcmp(entity.ptr(), "FIELDDISP") == 0) { QS_DEF(Array, substr); + substr.clear(); _readStringInQuotes(scanner, &substr); if (dsg != 0) { diff --git a/render2d/render_internal.h b/render2d/render_internal.h index cee32ffb6a..cd9380eb66 100644 --- a/render2d/render_internal.h +++ b/render2d/render_internal.h @@ -16,6 +16,7 @@ #define __render_internal_h__ #include "render_common.h" +#include "base_cpp/tree.h" namespace indigo { @@ -76,11 +77,11 @@ class MoleculeRenderInternal { void _placeBrackets(Sgroup& sg, const Array& atoms); void _positionIndex(Sgroup& sg, int ti, bool lower); void _loadBracketsAuto(const SGroup& group, Sgroup& sg); - void _initDataSGroups(); - void _initSruGroups(); - void _initMulGroups(); - void _initSupGroups(); + void _prepareSGroups(); + void _initSGroups(Tree& sgroups, Rect2f parent); + void _initSGroups(); + void _findAnglesOverPi(); void _renderBondIds(); void _renderAtomIds(); @@ -111,6 +112,7 @@ class MoleculeRenderInternal { int _findClosestCircle (Vec2f& p, int aid, float radius, int skip = -1); int _findClosestBox (Vec2f& p, int aid, const Vec2f& sz, float mrg, int skip = -1); void _preparePseudoAtom (int aid, int color, bool highlighted); + void _prepareChargeLabel(int aid, int color, bool highlighted); void _prepareLabelText (int aid); void _prepareAAM (); int _pushTextItem (RenderItem::TYPE type, int color, bool highlighted); @@ -140,6 +142,56 @@ class MoleculeRenderInternal { void _bondAny (BondDescr& bd, const BondEnd& be1, const BondEnd& be2); int _parseColorString (Scanner& str, float& r, float& g, float& b); + void _cloneAndFillMappings(); + + //TODO: remove dublicate with _placeBrackets(..) + inline Rect2f _bound(Array& atoms) const { + const int n = atoms.size(); + if (n <= 0) { + return Rect2f(Vec2f(0, 0), Vec2f(0, 0)); + } + Array points; + points.resize(n); + for (int i = 0; i < n; i++) { + points[i] = _ad(atoms[i]).pos; + } + return _bound(points, 0, n-1); + } + + Rect2f _bound(Array& points, int l, int r) const { + if (r == l || r == l + 1) { + return Rect2f(points[l], points[r]); + } + int m = (l + r) / 2; + return Rect2f( + _bound(points, l, m), + _bound(points, m+1, r) + ); + } + + inline Vec2f _firstPosition(Array& atoms) { + return _ad(atoms[0]).pos; + } + + inline static Vec2f ILLEGAL_POINT() { + return Vec2f(nanf(""), nanf("")); + } + + //TODO: eliminate + inline static Rect2f ILLEGAL_RECT () { + return Rect2f(ILLEGAL_POINT(), ILLEGAL_POINT()); + } + + inline static bool IS_NAN(float x) { + return x != x; + } + inline static bool IS_ILLEGAL(Vec2f point) { + return IS_NAN(point.x) && IS_NAN(point.y); + } + inline static bool IS_ILLEGAL(Rect2f rect) { + return IS_ILLEGAL(rect.leftBottom()) && IS_ILLEGAL(rect.rightTop()); + } + // local void* _hdc; BaseMolecule* _mol; diff --git a/render2d/render_item_fragment.h b/render2d/render_item_fragment.h index 5824e479fe..e9975cc206 100644 --- a/render2d/render_item_fragment.h +++ b/render2d/render_item_fragment.h @@ -30,7 +30,7 @@ class RenderItemFragment : public RenderItemBase { virtual void estimateSize (); virtual void setObjScale (float scale) { - _scaleFactor = scale; + _scaleFactor = scale; } virtual void init (); virtual void render (); diff --git a/render2d/src/render.cpp b/render2d/src/render.cpp index 9cc7cdf07e..b9bc87d73a 100644 --- a/render2d/src/render.cpp +++ b/render2d/src/render.cpp @@ -51,8 +51,9 @@ float Render::_getObjScale (int item) } else { avgBondLength = _factory.getItem(item).getTotalClosestAtomDistance() / atomCount; } - if (avgBondLength < 1e-4) + if (avgBondLength < 1e-4) { avgBondLength = 1.0f; + } float objScale = 1 / avgBondLength; return objScale; } diff --git a/render2d/src/render_internal.cpp b/render2d/src/render_internal.cpp index b4dcd8d0cc..a2394e7c96 100644 --- a/render2d/src/render_internal.cpp +++ b/render2d/src/render_internal.cpp @@ -12,6 +12,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ +#include "base_cpp/tree.h" #include "base_cpp/output.h" #include "base_cpp/scanner.h" #include "molecule/molecule.h" @@ -20,6 +21,7 @@ #include "reaction/query_reaction.h" #include "render_context.h" #include "render_internal.h" +#include "base_cpp/queue.h" using namespace indigo; @@ -190,7 +192,8 @@ IMPL_ERROR(MoleculeRenderInternal, "molecule render internal"); CP_DEF(MoleculeRenderInternal); MoleculeRenderInternal::MoleculeRenderInternal (const RenderOptions& opt, const RenderSettings& settings, RenderContext& cw) : -_mol(NULL), _cw(cw), _settings(settings), _opt(opt), CP_INIT, TL_CP_GET(_data), TL_CP_GET(_atomMapping), TL_CP_GET(_atomMappingInv), TL_CP_GET(_bondMappingInv), isRFragment(false) +_mol(NULL), _cw(cw), _settings(settings), _opt(opt), CP_INIT, TL_CP_GET(_data), TL_CP_GET(_atomMapping), TL_CP_GET(_atomMappingInv), TL_CP_GET(_bondMappingInv), +isRFragment(false) { _data.clear(); _atomMapping.clear(); @@ -204,23 +207,23 @@ void MoleculeRenderInternal::setMolecule (BaseMolecule* mol) _data.clear(); _atomMapping.clear(); - if ((_opt.collapseSuperatoms && _mol->sgroups.getSGroupCount(SGroup::SG_TYPE_SUP) > 0) || - _mol->sgroups.getSGroupCount(SGroup::SG_TYPE_MUL) > 0) { + bool superatoms = _mol->sgroups.getSGroupCount(SGroup::SG_TYPE_SUP) > 0; + bool mulsgroups = _mol->sgroups.getSGroupCount(SGroup::SG_TYPE_MUL) > 0; + if (mulsgroups || _opt.collapseSuperatoms && superatoms) { _prepareSGroups(); } - int i; - - // data _data.atoms.clear(); _data.atoms.resize(_mol->vertexEnd()); - for (i = _mol->vertexBegin(); i != _mol->vertexEnd(); i = _mol->vertexNext(i)) + for (auto i = _mol->vertexBegin(); i != _mol->vertexEnd(); i = _mol->vertexNext(i)) { _ad(i).clear(); + } _data.bonds.clear(); _data.bonds.resize(_mol->edgeEnd()); - for (i = _mol->edgeBegin(); i != _mol->edgeEnd(); i = _mol->edgeNext(i)) + for (auto i = _mol->edgeBegin(); i != _mol->edgeEnd(); i = _mol->edgeNext(i)) { _bd(i).clear(); + } } void MoleculeRenderInternal::setIsRFragment (bool isRFragment) @@ -287,13 +290,7 @@ void MoleculeRenderInternal::render () _prepareLabels(); - _initDataSGroups(); - - _initSruGroups(); - - _initMulGroups(); - - _initSupGroups(); + _initSGroups(); _extendRenderItems(); @@ -553,16 +550,7 @@ const char* MoleculeRenderInternal::_getStereoGroupText (int type) } } -void MoleculeRenderInternal::_initRGroups() -{ - if (_mol->attachmentPointCount() > 0) { - for (int i = 1; i <= _mol->attachmentPointCount(); ++i) - for (int j = 0, k; (k = _mol->getAttachmentPoint(i, j)) >= 0; ++j) - _ad(k).isRGroupAttachmentPoint = true; - } -} - -int MoleculeRenderInternal::_parseColorString (Scanner& scanner, float& r, float& g, float& b) +int MoleculeRenderInternal::_parseColorString(Scanner& scanner, float& r, float& g, float& b) { if (!scanner.tryReadFloat(r)) return -1; @@ -585,17 +573,26 @@ int MoleculeRenderInternal::_parseColorString (Scanner& scanner, float& r, float return 1; } -void MoleculeRenderInternal::_initDataSGroups() +void MoleculeRenderInternal::_initRGroups() { - BaseMolecule& bm = *_mol; - const char* atomColorProp = _opt.atomColorProp.size() > 0 ? _opt.atomColorProp.ptr() : NULL; + if (_mol->attachmentPointCount() > 0) { + for (int i = 1; i <= _mol->attachmentPointCount(); ++i) + for (int j = 0, k; (k = _mol->getAttachmentPoint(i, j)) >= 0; ++j) + _ad(k).isRGroupAttachmentPoint = true; + } +} - for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) - { - SGroup &sgroup = bm.sgroups.getSGroup(i); - if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) - { +void MoleculeRenderInternal::_initSGroups(Tree& sgroups, Rect2f parent) { + BaseMolecule &mol = *_mol; + + if (sgroups.label != -1) { + SGroup& sgroup = mol.sgroups.getSGroup(sgroups.label); + + const Rect2f bound = _bound(sgroup.atoms); + + if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { const DataSGroup& group = (DataSGroup &)sgroup; + const char* atomColorProp = _opt.atomColorProp.size() > 0 ? _opt.atomColorProp.ptr() : NULL; if (atomColorProp != NULL && strcmp(atomColorProp, group.name.ptr()) == 0) { Vec3f color; BufferScanner scanner(group.data); @@ -608,47 +605,126 @@ void MoleculeRenderInternal::_initDataSGroups() ad.hcolor.copy(color); ad.hcolorSet = true; } - continue; + return; } Sgroup& sg = _data.sgroups.push(); int tii = _pushTextItem(sg, RenderItem::RIT_DATASGROUP); TextItem& ti = _data.textitems[tii]; - ti.text.copy(group.data); - ti.text.push(0); + ti.text.push(group.tag); + ti.text.appendString(" = ", false); + ti.text.appendString(group.data.ptr(), true); ti.fontsize = FONT_SIZE_DATA_SGROUP; _cw.setTextItemSize(ti); - const AtomDesc& ad = _ad(group.atoms[0]); + if (!group.detached) { - ti.bbp.copy(_ad(group.atoms[0]).pos); - ti.bbp.x += ad.boundBoxMax.x + _settings.unit * 2; - ti.bbp.y -= ti.bbsz.y/2; + if (group.atoms.size() > 0) { + const AtomDesc& ad = _ad(group.atoms[0]); + ti.bbp.copy(_ad(group.atoms[0]).pos); + ti.bbp.x += ad.boundBoxMax.x + _settings.unit * 2; + ti.bbp.y -= ti.bbsz.y / 2; + } } else if (group.relative) { _objDistTransform(ti.bbp, group.display_pos); - ti.bbp.add(_ad(group.atoms[0]).pos); + if (IS_ILLEGAL(parent)) { + if (group.atoms.size() > 0) { + ti.bbp.add(_ad(group.atoms[0]).pos); + } + } else { + ti.bbp.add(parent.rightTop()); + } } else { _objCoordTransform(ti.bbp, group.display_pos); } + + parent = ILLEGAL_RECT(); } + + if (sgroup.sgroup_type == SGroup::SG_TYPE_SRU) { + const RepeatingUnit& group = (RepeatingUnit &)sgroup; + Sgroup& sg = _data.sgroups.push(); + _loadBracketsAuto(group, sg); + int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); + TextItem& index = _data.textitems[tiIndex]; + index.fontsize = FONT_SIZE_ATTR; + bprintf(index.text, group.subscript.size() > 0 ? group.subscript.ptr() : "n"); + _positionIndex(sg, tiIndex, true); + if (group.connectivity != RepeatingUnit::HEAD_TO_TAIL) { + int tiConn = _pushTextItem(sg, RenderItem::RIT_SGROUP); + TextItem& conn = _data.textitems[tiConn]; + conn.fontsize = FONT_SIZE_ATTR; + if (group.connectivity == RepeatingUnit::HEAD_TO_HEAD) { + bprintf(conn.text, "hh"); + } else { + bprintf(conn.text, "eu"); + } + _positionIndex(sg, tiConn, false); + } + + parent = bound; + } + + if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) { + const MultipleGroup& group = (MultipleGroup &)sgroup; + Sgroup& sg = _data.sgroups.push(); + _loadBracketsAuto(group, sg); + int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); + TextItem& index = _data.textitems[tiIndex]; + index.fontsize = FONT_SIZE_ATTR; + bprintf(index.text, "%d", group.multiplier); + _positionIndex(sg, tiIndex, true); + + parent = ILLEGAL_RECT(); + } + + if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) { + const Superatom& group = (Superatom &)sgroup; + Sgroup& sg = _data.sgroups.push(); + _placeBrackets(sg, group.atoms); + int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); + TextItem& index = _data.textitems[tiIndex]; + index.fontsize = FONT_SIZE_ATTR; + bprintf(index.text, "%s", group.subscript.ptr()); + _positionIndex(sg, tiIndex, true); + + parent = ILLEGAL_RECT(); + } + } + + ObjArray& children = sgroups.children(); + for (int i = 0; i < children.size(); i++) { + _initSGroups(children[i], parent); } } +void MoleculeRenderInternal::_initSGroups() +{ + BaseMolecule& mol = *_mol; + + Tree sgroups; + for (auto i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { + SGroup &sgroup = mol.sgroups.getSGroup(i); + sgroups.insert(i, sgroup.parent_group - 1); + } + + _initSGroups(sgroups, Rect2f(_min, _max)); +} + void MoleculeRenderInternal::_loadBrackets(Sgroup& sg, const Array& coord, bool transformCoordinates) { for (int j = 0; j < coord.size(); ++j) { Vec2f a(coord[j][0]), b(coord[j][1]); int bracketId = _data.brackets.size(); - if (j == 0) + if (j == 0) { sg.bibegin = bracketId, sg.bicount = 1; - else + } else { sg.bicount++; - RenderItemBracket& bracket =_data.brackets.push(); + } + RenderItemBracket& bracket = _data.brackets.push(); bracket.p0.copy(a); bracket.p1.copy(b); if (transformCoordinates) { - bracket.p0.set(a.x - _min.x, _max.y - a.y); - bracket.p0.scale(_scale); - bracket.p1.set(b.x - _min.x, _max.y - b.y); - bracket.p1.scale(_scale); + _objCoordTransform(bracket.p0, a); + _objCoordTransform(bracket.p1, b); } bracket.d.diff(bracket.p1, bracket.p0); bracket.length = bracket.d.length(); @@ -663,10 +739,11 @@ void MoleculeRenderInternal::_loadBrackets(Sgroup& sg, const Array& co } void MoleculeRenderInternal::_loadBracketsAuto(const SGroup& group, Sgroup& sg) { - if (group.brackets.size() == 0 || Vec2f::distSqr(group.brackets.at(0)[0], group.brackets.at(0)[1]) < EPSILON) + if (group.brackets.size() == 0 || Vec2f::distSqr(group.brackets.at(0)[0], group.brackets.at(0)[1]) < EPSILON) { _placeBrackets(sg, group.atoms); - else + } else { _loadBrackets(sg, group.brackets, true); + } } void MoleculeRenderInternal::_positionIndex(Sgroup& sg, int ti, bool lower) @@ -682,57 +759,6 @@ void MoleculeRenderInternal::_positionIndex(Sgroup& sg, int ti, bool lower) index.bbp.addScaled(bracket.d, lower ? -yShift : yShift); } -void MoleculeRenderInternal::_initSruGroups() -{ - BaseMolecule& bm = *_mol; - for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) - { - SGroup &sgroup = bm.sgroups.getSGroup(i); - if (sgroup.sgroup_type == SGroup::SG_TYPE_SRU) - { - const RepeatingUnit& group = (RepeatingUnit &)sgroup; - Sgroup& sg = _data.sgroups.push(); - _loadBracketsAuto(group, sg); - int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); - TextItem& index = _data.textitems[tiIndex]; - index.fontsize = FONT_SIZE_ATTR; - bprintf(index.text, group.subscript.size() > 0 ? group.subscript.ptr() : "n"); - _positionIndex(sg, tiIndex, true); - if (group.connectivity != RepeatingUnit::HEAD_TO_TAIL) { - int tiConn = _pushTextItem(sg, RenderItem::RIT_SGROUP); - TextItem& conn = _data.textitems[tiConn]; - conn.fontsize = FONT_SIZE_ATTR; - if (group.connectivity == RepeatingUnit::HEAD_TO_HEAD) { - bprintf(conn.text, "hh"); - } else { - bprintf(conn.text, "eu"); - } - _positionIndex(sg, tiConn, false); - } - } - } -} - -void MoleculeRenderInternal::_initMulGroups() -{ - BaseMolecule& bm = *_mol; - for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) - { - SGroup &sgroup = bm.sgroups.getSGroup(i); - if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) - { - const MultipleGroup& group = (MultipleGroup &)sgroup; - Sgroup& sg = _data.sgroups.push(); - _loadBracketsAuto(group, sg); - int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); - TextItem& index = _data.textitems[tiIndex]; - index.fontsize = FONT_SIZE_ATTR; - bprintf(index.text, "%d", group.multiplier); - _positionIndex(sg, tiIndex, true); - } - } -} - void MoleculeRenderInternal::_placeBrackets(Sgroup& sg, const Array& atoms) { QS_DEF(Array, brackets); @@ -763,64 +789,43 @@ void MoleculeRenderInternal::_placeBrackets(Sgroup& sg, const Array& atoms) _loadBrackets(sg, brackets, false); } -void MoleculeRenderInternal::_initSupGroups() -{ - BaseMolecule& bm = *_mol; - for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) - { - SGroup &sgroup = bm.sgroups.getSGroup(i); - if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) - { - const Superatom& group = (Superatom &)sgroup; - Sgroup& sg = _data.sgroups.push(); - _placeBrackets(sg, group.atoms); - int tiIndex = _pushTextItem(sg, RenderItem::RIT_SGROUP); - TextItem& index = _data.textitems[tiIndex]; - index.fontsize = FONT_SIZE_ATTR; - bprintf(index.text, "%s", group.subscript.ptr()); - _positionIndex(sg, tiIndex, true); - } +void MoleculeRenderInternal::_cloneAndFillMappings() { + BaseMolecule* clone = _mol->neu(); + clone->clone(*_mol, &_atomMapping, &_atomMappingInv); + + _bondMappingInv.clear(); + for (int i = clone->edgeBegin(); i < clone->edgeEnd(); i = clone->edgeNext(i)) { + _bondMappingInv.insert(i, BaseMolecule::findMappedEdge(*clone, *_mol, i, _atomMappingInv.ptr())); } + _mol = clone; } void MoleculeRenderInternal::_prepareSGroups() { - { - BaseMolecule* newMol = NULL; - BaseMolecule& bm1 = *_mol; - if (bm1.isQueryMolecule()) - newMol = new QueryMolecule(); - else - newMol = new Molecule(); - newMol->clone(bm1, &_atomMapping, &_atomMappingInv); - _bondMappingInv.clear(); - for (int i = newMol->edgeBegin(); i < newMol->edgeEnd(); i = newMol->edgeNext(i)) - _bondMappingInv.insert(i, BaseMolecule::findMappedEdge(*newMol, *_mol, i, _atomMappingInv.ptr())); - _mol = newMol; - } + _cloneAndFillMappings(); - BaseMolecule& bm = *_mol; + BaseMolecule& mol = *_mol; if (_opt.collapseSuperatoms) { - for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) + for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { - SGroup &sgroup = bm.sgroups.getSGroup(i); + SGroup &sgroup = mol.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) { const Superatom& group = (Superatom &)sgroup; Vec3f centre; for (int i = 0; i < group.atoms.size(); ++i) { int aid = group.atoms[i]; - centre.add(bm.getAtomXyz(aid)); + centre.add(mol.getAtomXyz(aid)); } centre.scale(1.0f / group.atoms.size()); int said = -1; - if (bm.isQueryMolecule()) { + if (mol.isQueryMolecule()) { AutoPtr atom; atom.reset(new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, group.subscript.ptr())); - said = bm.asQueryMolecule().addAtom(atom.release()); + said = mol.asQueryMolecule().addAtom(atom.release()); } else { - Molecule& mol = bm.asMolecule(); + Molecule& mol = mol.asMolecule(); said = mol.addAtom(ELEM_PSEUDO); mol.setPseudoAtom(said, group.subscript.ptr()); } @@ -833,21 +838,21 @@ void MoleculeRenderInternal::_prepareSGroups() int posCnt = 0; while (group.atoms.size() > 0) { int aid = group.atoms[0]; - const Vertex& v = bm.getVertex(aid); + const Vertex& v = mol.getVertex(aid); bool posCounted = false; for (int j = v.neiBegin(); j < v.neiEnd(); j = v.neiNext(j)) { int naid = v.neiVertex(j); if (!groupAtoms.find(naid)) { - pos.add(bm.getAtomXyz(aid)); + pos.add(mol.getAtomXyz(aid)); posCounted = true; posCnt++; int nbid = v.neiEdge(j), bid = -1; - if (bm.findEdgeIndex(naid, said) < 0) { - if (bm.isQueryMolecule()) { - QueryMolecule& qm = bm.asQueryMolecule(); + if (mol.findEdgeIndex(naid, said) < 0) { + if (mol.isQueryMolecule()) { + QueryMolecule& qm = mol.asQueryMolecule(); bid = qm.addBond(said, naid, qm.getBond(nbid).clone()); - }else{ - Molecule& mol = bm.asMolecule(); + } else { + Molecule& mol = mol.asMolecule(); bid = mol.addBond(said, naid, mol.getBondOrder(nbid)); mol.setEdgeTopology(bid, mol.getBondTopology(nbid)); } @@ -857,25 +862,26 @@ void MoleculeRenderInternal::_prepareSGroups() } } } - bm.removeAtom(aid); + mol.removeAtom(aid); } - if (posCnt == 0) + if (posCnt == 0) { pos.copy(centre); - else + } else { pos.scale(1.f / posCnt); - bm.setAtomXyz(said, pos.x, pos.y, pos.z); + } + mol.setAtomXyz(said, pos.x, pos.y, pos.z); } } } QS_DEF(BaseMolecule::Mapping, mapAtom); mapAtom.clear(); - for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) + for (int i = mol.sgroups.begin(); i != mol.sgroups.end(); i = mol.sgroups.next(i)) { - SGroup &sgroup = bm.sgroups.getSGroup(i); + SGroup &sgroup = mol.sgroups.getSGroup(i); if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) { - BaseMolecule::collapse(bm, i, mapAtom, _bondMappingInv); + BaseMolecule::collapse(mol, i, mapAtom, _bondMappingInv); } } } @@ -1253,6 +1259,7 @@ void MoleculeRenderInternal::_prepareLabels() void MoleculeRenderInternal::_objCoordTransform(Vec2f& p, const Vec2f& v) const { + //shift, mirror of Y axis, scale p.set((v.x - _min.x) * _scale, (_max.y - v.y) * _scale); } @@ -1542,16 +1549,14 @@ void MoleculeRenderInternal::_initAtomData () { ad.type = AtomDesc::TYPE_PSEUDO; ad.pseudo.readString(bm.getPseudoAtom(i), true); - } - else if (bm.isTemplateAtom(i)) - { + } else if (bm.isTemplateAtom(i)) { ad.type = AtomDesc::TYPE_PSEUDO; ad.pseudo.readString(bm.getTemplateAtom(i), true); - } - else if (atomNumber < 0 || atomNumber == ELEM_RSITE) + } else if (atomNumber < 0 || atomNumber == ELEM_RSITE) { ad.type = AtomDesc::TYPE_QUERY; - else + } else { ad.type = AtomDesc::TYPE_REGULAR; + } ad.label = -1; if (ad.type == AtomDesc::TYPE_REGULAR) @@ -2655,7 +2660,7 @@ void MoleculeRenderInternal::_preparePseudoAtom (int aid, int color, bool highli _cw.setTextItemSize(fake, ad.pos); float xpos = fake.bbp.x, width = fake.bbsz.x, - offset = _settings.unit/2, + offset = _settings.unit / 2, totalwdt = 0, upshift = -0.6f, downshift = 0.2f, @@ -2677,45 +2682,49 @@ void MoleculeRenderInternal::_preparePseudoAtom (int aid, int color, bool highli totalwdt += item.bbsz.x; } else { for (int i = 0; i <= len; ++i) { - a = b; i1 = i; + a = b; + bool tag = false; - char c = (i == len ? ' ' : str[i]); - if (isspace(c)) - b = WHITESPACE; - else if (isdigit(c)) - b = DIGIT; - else if (c == '+' || c == '-') - b = SIGN, signType = ((c == '+') ? GraphItem::PLUS : GraphItem::MINUS); - else if (c == '\\' && i < len - 1 && str[i+1] == 'S') - b = TAG_SUPERSCRIPT, ++i, tag = true; - else if (c == '\\' && i < len - 1 && str[i+1] == 's') - b = TAG_SUBSCRIPT, ++i, tag = true; - else if (c == '\\' && i < len - 1 && str[i+1] == 'n') - b = TAG_NORMAL, ++i, tag = true; - else - b = LETTER; - - bool aTag = a == TAG_SUPERSCRIPT || a == TAG_SUBSCRIPT || a == TAG_NORMAL; - if (b == TAG_SUPERSCRIPT) { - newscript = SUPER; - } else if (b == TAG_SUBSCRIPT) { - newscript = SUB; - } else if (b == TAG_NORMAL) { - newscript = MAIN; - } else if ((b == WHITESPACE && a != WHITESPACE) || (b != WHITESPACE && a == WHITESPACE) || (b == LETTER && !aTag)) { - newscript = MAIN; - } else if (b == DIGIT && a == SIGN) { - newscript = script; - } else if (b == DIGIT && a != DIGIT && !aTag) { - newscript = ((a == LETTER) ? SUB : MAIN); - } else if (b == SIGN) { - if (a == LETTER || a == DIGIT) - newscript = SUPER; - } else if (a == SIGN && script == SUPER) { - newscript = MAIN; - } else { - continue; + { char c = (i == len ? ' ' : str[i]); + if (isspace(c)) { + b = WHITESPACE; + } else if (isdigit(c)) { + b = DIGIT; + } else if (c == '+' || c == '-') { + b = SIGN, signType = ((c == '+') ? GraphItem::PLUS : GraphItem::MINUS); + } else if (c == '\\' && i < len - 1 && str[i + 1] == 'S') { + b = TAG_SUPERSCRIPT, ++i, tag = true; + } else if (c == '\\' && i < len - 1 && str[i + 1] == 's') { + b = TAG_SUBSCRIPT, ++i, tag = true; + } else if (c == '\\' && i < len - 1 && str[i + 1] == 'n') { + b = TAG_NORMAL, ++i, tag = true; + } else { + b = LETTER; + } } + + { bool aTag = a == TAG_SUPERSCRIPT || a == TAG_SUBSCRIPT || a == TAG_NORMAL; + if (b == TAG_SUPERSCRIPT) { + newscript = SUPER; + } else if (b == TAG_SUBSCRIPT) { + newscript = SUB; + } else if (b == TAG_NORMAL) { + newscript = MAIN; + } else if ((b == WHITESPACE && a != WHITESPACE) || (b != WHITESPACE && a == WHITESPACE) || (b == LETTER && !aTag)) { + newscript = MAIN; + } else if (b == DIGIT && a == SIGN) { + newscript = script; + } else if (b == DIGIT && a != DIGIT && !aTag) { + newscript = ((a == LETTER) ? SUB : MAIN); + } else if (b == SIGN) { + if (a == LETTER || a == DIGIT) { + newscript = SUPER; + } + } else if (a == SIGN && script == SUPER) { + newscript = MAIN; + } else { + continue; + } } if (i1 > i0) { @@ -2765,6 +2774,38 @@ void MoleculeRenderInternal::_preparePseudoAtom (int aid, int color, bool highli } } +void MoleculeRenderInternal::_prepareChargeLabel (int aid, int color, bool highlighted) { + AtomDesc& ad = _ad(aid); + BaseMolecule& bm = *_mol; + + int charge = bm.getAtomCharge(aid); + if (charge != CHARGE_UNKNOWN && charge != 0) { + ad.rightMargin += _settings.labelInternalOffset; + if (abs(charge) != 1) { + int tiChargeValue = _pushTextItem(ad, RenderItem::RIT_CHARGEVAL, color, highlighted); + + TextItem& itemChargeValue = _data.textitems[tiChargeValue]; + itemChargeValue.fontsize = FONT_SIZE_ATTR; + bprintf(itemChargeValue.text, "%i", abs(charge)); + _cw.setTextItemSize(itemChargeValue); + + itemChargeValue.bbp.set(ad.rightMargin, ad.ypos + _settings.upperIndexShift * ad.height); + _expandBoundRect(ad, itemChargeValue); + ad.rightMargin += itemChargeValue.bbsz.x; + } + + GraphItem::TYPE type = charge > 0 ? GraphItem::PLUS : GraphItem::MINUS; + int giChargeSign = _pushGraphItem(ad, RenderItem::RIT_CHARGESIGN, color, highlighted); + + GraphItem& itemChargeSign = _data.graphitems[giChargeSign]; + _cw.setGraphItemSizeSign(itemChargeSign, type); + + itemChargeSign.bbp.set(ad.rightMargin, ad.ypos + _settings.upperIndexShift * ad.height); + _expandBoundRect(ad, itemChargeSign); + ad.rightMargin += itemChargeSign.bbsz.x; + } +} + void MoleculeRenderInternal::_prepareLabelText (int aid) { AtomDesc& ad = _ad(aid); @@ -2782,6 +2823,18 @@ void MoleculeRenderInternal::_prepareLabelText (int aid) if (ad.type == AtomDesc::TYPE_PSEUDO) { _preparePseudoAtom(aid, CWC_BASE, highlighted); + + bool chargeSignAdded = false; + for (auto i = 0; i < _data.graphitems.size(); i++) { + if (_data.graphitems[i].ritype == RenderItem::RIT_CHARGESIGN) { + chargeSignAdded = true; + break; + } + } + + if (!chargeSignAdded) { + _prepareChargeLabel(aid, color, highlighted); + } } else if (ad.showLabel) { tilabel = _pushTextItem(ad, RenderItem::RIT_LABEL, color, highlighted); { @@ -2946,33 +2999,7 @@ void MoleculeRenderInternal::_prepareLabelText (int aid) } // charge - int charge = bm.getAtomCharge(aid); - if (charge != CHARGE_UNKNOWN && charge != 0) { - ad.rightMargin += _settings.labelInternalOffset; - if (abs(charge) != 1) - { - tiChargeValue = _pushTextItem(ad, RenderItem::RIT_CHARGEVAL, color, highlighted); - - TextItem& itemChargeValue = _data.textitems[tiChargeValue]; - itemChargeValue.fontsize = FONT_SIZE_ATTR; - bprintf(itemChargeValue.text, "%i", abs(charge)); - _cw.setTextItemSize(itemChargeValue); - - itemChargeValue.bbp.set(ad.rightMargin, ad.ypos + _settings.upperIndexShift * ad.height); - _expandBoundRect(ad, itemChargeValue); - ad.rightMargin += itemChargeValue.bbsz.x; - } - - GraphItem::TYPE type = charge > 0 ? GraphItem::PLUS : GraphItem::MINUS; - giChargeSign = _pushGraphItem(ad, RenderItem::RIT_CHARGESIGN, color, highlighted); - - GraphItem& itemChargeSign = _data.graphitems[giChargeSign]; - _cw.setGraphItemSizeSign(itemChargeSign, type); - - itemChargeSign.bbp.set(ad.rightMargin, ad.ypos + _settings.upperIndexShift * ad.height); - _expandBoundRect(ad, itemChargeSign); - ad.rightMargin += itemChargeSign.bbsz.x; - } + _prepareChargeLabel(aid, color, highlighted); // valence int valence = bm.getExplicitValence(aid); diff --git a/utils/indigo-depict/main.c b/utils/indigo-depict/main.c index afbc9991df..b82a238f35 100644 --- a/utils/indigo-depict/main.c +++ b/utils/indigo-depict/main.c @@ -24,6 +24,7 @@ void usage (void) { fprintf(stderr, + "Indigo version: %s\n\n" "Usage:\n" " indigo-depict infile.{mol,rxn,cml,smi} outfile.{png,svg,pdf} [parameters]\n" " indigo-depict infile.{sdf,rdf,cml,smi} outfile_%%s.{png,svg,pdf} [parameters]\n" @@ -32,6 +33,8 @@ void usage (void) " indigo-depict infile.smi outfile.{sdf,rdf,cml} [parameters]\n" " indigo-depict - SMILES outfile.{png,svg,pdf} [parameters]\n" " indigo-depict - SMILES outfile.{mol,rxn,cml} [parameters]\n" + " indigo-depict - InChI outfile.{png,svg,pdf} [parameters]\n" + " indigo-depict - InChI outfile.{mol,rxn,cml} [parameters]\n" "\nParameters:\n" "-w \n" " Picture width in pixels\n" @@ -108,8 +111,8 @@ void usage (void) " indigo-depict database.sdf molecule_%%s.png -id cdbregno -thickness 1.1\n" " indigo-depict database.smi database.sdf\n" " indigo-depict - \"CC.[O-][*-]([O-])=O\" query.png -query\n" - " indigo-depict - \"OCO>>CC(C)N\" reaction.rxn\n" - ); + " indigo-depict - \"OCO>>CC(C)N\" reaction.rxn\n", + indigoVersion()); } #define USAGE() do { usage(); return -1; } while (0)