diff --git a/common/base_cpp/tree.h b/common/base_cpp/tree.h new file mode 100644 index 0000000000..0ce70db189 --- /dev/null +++ b/common/base_cpp/tree.h @@ -0,0 +1,135 @@ +/**************************************************************************** +* Copyright (C) 2009-2015 EPAM Systems +* +* This file is part of Indigo toolkit. +* +* This file may be distributed and/or modified under the terms of the +* GNU General Public License version 3 as published by the Free Software +* Foundation and appearing in the file LICENSE.GPL included in the +* packaging of this file. +* +* This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +* WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +***************************************************************************/ + +#ifndef __tree_h__ +#define __tree_h__ + +#include "base_cpp/exception.h" +#include "base_cpp/obj_array.h" + +namespace indigo { + +DECL_EXCEPTION(TreeError); + +template class DataTree +{ +public: + DECL_TPL_ERROR(TreeError); + + explicit DataTree () + { + label = -1; + } + + ~DataTree () + { + } + + DataTree & push () + { + return push(-1); + } + + DataTree & push (int label) + { + DataTree & child = _children.push(); + child.label = label; + return child; + } + + DataTree & push (const T data) + { + DataTree & child = push(); + child.data = data; + return child; + } + + DataTree & push (int label, const T data) + { + DataTree & child = push(label); + child.data = data; + return child; + } + + //There is special value for root = -1 + DataTree & provide (int label) + { + if (label == -1) { + return *this; + } + DataTree * ptr = _find_or_remove(label, false); + if (ptr != nullptr) { + return *ptr; + } + return push(label); + } + + //There is special value for root = -1 + DataTree & insert (int label, int parent) + { + DataTree & ptree = provide(parent); + return ptree.push(label); + } + + bool remove (int label) + { + return _find_or_remove(label, true) != nullptr; + } + + bool find (int label) + { + return _find_or_remove(label, false) != nullptr; + } + + ObjArray> & children() { + return _children; + } + + int label; + T data; + +protected: + ObjArray> _children; + +private: + DataTree(const DataTree &); // no implicit copy + DataTree& operator= (const DataTree& other); // no copy constructor + + DataTree * _find_or_remove (int label, bool remove) + { + for (int i = 0; i < _children.size(); i++) { + DataTree & child = _children[i]; + if (child.label == label) { + if (remove) { + _children.remove(i); + } + return &child; + } else { + DataTree * result = child._find_or_remove(label, remove); + if (result != nullptr) { + return result; + } + } + } + return nullptr; + } +}; + +struct Empty {}; + +typedef DataTree Tree; + +} + +#endif \ No newline at end of file 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_attachments_search.h b/molecule/molecule_attachments_search.h index f4639f2d15..661a3241ef 100644 --- a/molecule/molecule_attachments_search.h +++ b/molecule/molecule_attachments_search.h @@ -15,7 +15,6 @@ #ifndef __molecule_attachments_search__ #define __molecule_attachments_search__ -#include "assert.h" #include "algorithm" #include "base_cpp/array.h" 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/molecule_attachments_search.cpp b/molecule/src/molecule_attachments_search.cpp index 0beee0eca4..bb0489a5c7 100644 --- a/molecule/src/molecule_attachments_search.cpp +++ b/molecule/src/molecule_attachments_search.cpp @@ -12,6 +12,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ***************************************************************************/ +#include "assert.h" #include "base_cpp/array.h" #include "molecule/molecule_attachments_search.h" diff --git a/render2d/render_internal.h b/render2d/render_internal.h index b1880bdc30..e3e918d01e 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,8 +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 _initSGroups(); + void _prepareSGroups(); + void _initSGroups(Tree& sgroups, Vec2f extra); + void _initSGroups(); + void _findAnglesOverPi(); void _renderBondIds(); void _renderAtomIds(); @@ -137,6 +141,43 @@ 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(); + 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; + } + + //TODO: move into Vec2f? + const Vec2f ILLEGAL_POINT = Vec2f(nanf(""), nanf("")); + inline bool _isIllegal(Vec2f point) { + return _isNaN(point.x) && _isNaN(point.y); + } + inline bool _isNaN(float x) { + return x != x; + } + // 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 9f37d03766..6f2ebe0af3 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" @@ -206,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) @@ -581,29 +582,11 @@ void MoleculeRenderInternal::_initRGroups() } } -void MoleculeRenderInternal::_initSGroups() -{ - BaseMolecule& bm = *_mol; +void MoleculeRenderInternal::_initSGroups(Tree& sgroups, Vec2f parentInfo) { + BaseMolecule &mol = *_mol; - Queue queue; - Array resolved; - Array sgroupsCenters; - resolved.resize(bm.sgroups.end()); - sgroupsCenters.resize(bm.sgroups.end()); - queue.setLength(1 + bm.sgroups.getSGroupCount()); - for (int i = bm.sgroups.begin(); i != bm.sgroups.end(); i = bm.sgroups.next(i)) { - resolved[i] = false; - queue.push(i); - } - - while (!queue.isEmpty()) { - int current = queue.pop(); - SGroup &sgroup = bm.sgroups.getSGroup(current); - int parent = sgroup.parent_group - 1; - if (parent != -1 && !resolved[parent]) { - queue.push(current); - continue; - } + if (sgroups.label != -1) { + SGroup& sgroup = mol.sgroups.getSGroup(sgroups.label); if (sgroup.sgroup_type == SGroup::SG_TYPE_DAT) { const DataSGroup& group = (DataSGroup &)sgroup; @@ -620,7 +603,7 @@ void MoleculeRenderInternal::_initSGroups() ad.hcolor.copy(color); ad.hcolorSet = true; } - continue; + return; } Sgroup& sg = _data.sgroups.push(); int tii = _pushTextItem(sg, RenderItem::RIT_DATASGROUP); @@ -638,18 +621,19 @@ void MoleculeRenderInternal::_initSGroups() ti.bbp.y -= ti.bbsz.y / 2; } } else if (group.relative) { - if (parent != -1) { - ti.bbp = sgroupsCenters[parent]; - } else { - _objDistTransform(ti.bbp, group.display_pos); + _objDistTransform(ti.bbp, group.display_pos); + if (_isIllegal(parentInfo)) { if (group.atoms.size() > 0) { ti.bbp.add(_ad(group.atoms[0]).pos); } + } else { + ti.bbp = parentInfo; } } else { _objCoordTransform(ti.bbp, group.display_pos); } - sgroupsCenters[current] = ti.bbp; + + parentInfo = ti.bbp; } if (sgroup.sgroup_type == SGroup::SG_TYPE_SRU) { @@ -661,7 +645,6 @@ void MoleculeRenderInternal::_initSGroups() index.fontsize = FONT_SIZE_ATTR; bprintf(index.text, group.subscript.size() > 0 ? group.subscript.ptr() : "n"); _positionIndex(sg, tiIndex, true); - sgroupsCenters[current] = index.bbp; if (group.connectivity != RepeatingUnit::HEAD_TO_TAIL) { int tiConn = _pushTextItem(sg, RenderItem::RIT_SGROUP); TextItem& conn = _data.textitems[tiConn]; @@ -672,8 +655,10 @@ void MoleculeRenderInternal::_initSGroups() bprintf(conn.text, "eu"); } _positionIndex(sg, tiConn, false); - sgroupsCenters[current] = conn.bbp; } + + Rect2f bound = _bound(sgroup.atoms); + parentInfo = bound.leftTop(); } if (sgroup.sgroup_type == SGroup::SG_TYPE_MUL) { @@ -685,7 +670,8 @@ void MoleculeRenderInternal::_initSGroups() index.fontsize = FONT_SIZE_ATTR; bprintf(index.text, "%d", group.multiplier); _positionIndex(sg, tiIndex, true); - sgroupsCenters[current] = index.bbp; + + parentInfo = index.bbp; } if (sgroup.sgroup_type == SGroup::SG_TYPE_SUP) { @@ -697,11 +683,28 @@ void MoleculeRenderInternal::_initSGroups() index.fontsize = FONT_SIZE_ATTR; bprintf(index.text, "%s", group.subscript.ptr()); _positionIndex(sg, tiIndex, true); - sgroupsCenters[current] = index.bbp; + + parentInfo = index.bbp; } + } + + ObjArray& children = sgroups.children(); + for (int i = 0; i < children.size(); i++) { + _initSGroups(children[i], parentInfo); + } +} + +void MoleculeRenderInternal::_initSGroups() +{ + BaseMolecule& mol = *_mol; - resolved[current] = true; + 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, ILLEGAL_POINT); } void MoleculeRenderInternal::_loadBrackets(Sgroup& sg, const Array& coord, bool transformCoordinates) @@ -709,18 +712,17 @@ void MoleculeRenderInternal::_loadBrackets(Sgroup& sg, const Array& co 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(); @@ -735,10 +737,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) @@ -784,44 +787,43 @@ void MoleculeRenderInternal::_placeBrackets(Sgroup& sg, const Array& atoms) _loadBrackets(sg, brackets, false); } +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()); } @@ -834,21 +836,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)); } @@ -858,25 +860,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); } } } @@ -1254,6 +1257,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); } @@ -1543,16 +1547,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)