diff --git a/api/c/indigo/src/indigo_savers.cpp b/api/c/indigo/src/indigo_savers.cpp index 7704c7a240..441f03e3a7 100644 --- a/api/c/indigo/src/indigo_savers.cpp +++ b/api/c/indigo/src/indigo_savers.cpp @@ -207,6 +207,11 @@ void IndigoSmilesSaver::generateSmarts(IndigoObject& obj, Array& out_buffe { BaseMolecule& mol = obj.getBaseMolecule(); + if (mol.tgroups.getTGroupCount()) + { + mol.transformTemplatesToSuperatoms(); + } + SmilesSaver saver(output); saver.smarts_mode = true; if (mol.isQueryMolecule()) diff --git a/api/tests/integration/ref/formats/ket_to_ket.py.out b/api/tests/integration/ref/formats/ket_to_ket.py.out index 1293106554..6cc65d74c2 100644 --- a/api/tests/integration/ref/formats/ket_to_ket.py.out +++ b/api/tests/integration/ref/formats/ket_to_ket.py.out @@ -1,6 +1,8 @@ *** KET to KET *** images.ket:SUCCEED -ambiguous_monomer.ket doc: SUCCEED -ambiguous_monomer.ket mol: SUCCEED -monomer_shape.ket doc: SUCCEED -monomer_shape.ket mol: SUCCEED +ambiguous_monomer.ket doc loadKetDocument: SUCCEED +ambiguous_monomer.ket mol loadMolecule: SUCCEED +ambiguous_monomer.ket mol loadQueryMolecule: SUCCEED +monomer_shape.ket doc loadKetDocument: SUCCEED +monomer_shape.ket mol loadMolecule: SUCCEED +monomer_shape.ket mol loadQueryMolecule: SUCCEED diff --git a/api/tests/integration/tests/formats/custom_query.py b/api/tests/integration/tests/formats/custom_query.py index 7c3ceb417a..a93260e82e 100644 --- a/api/tests/integration/tests/formats/custom_query.py +++ b/api/tests/integration/tests/formats/custom_query.py @@ -27,7 +27,12 @@ def test_smarts_to_ket(smarts_in, expected_str): def test_ket_to_smarts(filename, expected_str): - mol = indigo.loadQueryMoleculeFromFile(os.path.join(ref_path, filename)) + try: + mol = indigo.loadMoleculeFromFile(os.path.join(ref_path, filename)) + except IndigoException as e: + mol = indigo.loadQueryMoleculeFromFile( + os.path.join(ref_path, filename) + ) smarts = mol.smarts() if smarts == expected_str: print( diff --git a/api/tests/integration/tests/formats/ket_to_ket.py b/api/tests/integration/tests/formats/ket_to_ket.py index f091c39912..a3bd406ee4 100644 --- a/api/tests/integration/tests/formats/ket_to_ket.py +++ b/api/tests/integration/tests/formats/ket_to_ket.py @@ -59,16 +59,17 @@ def check_res(filename, format, ket_ref, ket): "ambiguous_monomer", ] formats = { - "doc": indigo.loadKetDocument, - "mol": indigo.loadMolecule, + "doc": [indigo.loadKetDocument], + "mol": [indigo.loadMolecule, indigo.loadQueryMolecule], } for filename in sorted(files): for format in sorted(formats.keys()): file_path = os.path.join(ref_path, filename) with open("{}_{}.ket".format(file_path, format), "r") as file: ket_ref = file.read() - mol = formats[format](ket_ref) - # with open("{}_{}.ket".format(file_path, format), "w") as file: - # file.write(mol.json()) - ket = mol.json() - check_res(filename, format, ket_ref, ket) + for loader in formats[format]: + mol = loader(ket_ref) + # with open("{}_{}.ket".format(file_path, format), "w") as file: + # file.write(mol.json()) + ket = mol.json() + check_res(filename, format + " " + loader.__name__, ket_ref, ket) diff --git a/core/indigo-core/molecule/base_molecule.h b/core/indigo-core/molecule/base_molecule.h index 1f40385bd1..04def60ddd 100644 --- a/core/indigo-core/molecule/base_molecule.h +++ b/core/indigo-core/molecule/base_molecule.h @@ -215,13 +215,55 @@ namespace indigo virtual bool isPseudoAtom(int idx) = 0; virtual const char* getPseudoAtom(int idx) = 0; + struct _AttachOrder + { + int ap_idx; + Array ap_id; + }; + + struct _TemplateOccurrence + { + int name_idx; // index in _template_names + int class_idx; // index in _template_classes + int seq_id; // sequence id + int template_idx; // template idx + Array seq_name; // sequence name + DisplayOption contracted; // display option (-1 if undefined, 0 - expanded, 1 - contracted) + Array<_AttachOrder> order; // attach order info + _TemplateOccurrence() : name_idx(-1), class_idx(-1), seq_id(-1), template_idx(-1), contracted(DisplayOption::Undefined) + { + } + _TemplateOccurrence(const _TemplateOccurrence& other) + : name_idx(other.name_idx), class_idx(other.class_idx), seq_id(other.seq_id), template_idx(other.template_idx), contracted(other.contracted) + { + seq_name.copy(other.seq_name); + order.copy(other.order); + } + }; + ObjPool<_TemplateOccurrence> _template_occurrences; + + StringPool _template_classes; + StringPool _template_names; + + virtual int addTemplateAtom(const char* text) = 0; virtual bool isTemplateAtom(int idx) = 0; - virtual const char* getTemplateAtom(int idx) = 0; - virtual const int getTemplateAtomSeqid(int idx) = 0; - virtual const char* getTemplateAtomSeqName(int idx) = 0; - virtual const char* getTemplateAtomClass(int idx) = 0; - virtual const int getTemplateAtomDisplayOption(int idx) = 0; - virtual const int getTemplateAtomTemplateIndex(int idx) = 0; + virtual int getTemplateAtomOccurrence(int idx) = 0; + + const char* getTemplateAtom(int idx); + const int getTemplateAtomSeqid(int idx); + const char* getTemplateAtomSeqName(int idx); + const char* getTemplateAtomClass(int idx); + const int getTemplateAtomDisplayOption(int idx); + const int getTemplateAtomTemplateIndex(int idx); + + void renameTemplateAtom(int idx, const char* text); + void setTemplateAtomName(int idx, const char* text); + void setTemplateAtomClass(int idx, const char* text); + void setTemplateAtomSeqid(int idx, int seq_id); + void setTemplateAtomSeqName(int idx, const char* seq_name); + + void setTemplateAtomDisplayOption(int idx, int contracted); + void setTemplateAtomTemplateIndex(int idx, int temp_idx); bool getUnresolvedTemplatesList(BaseMolecule& bmol, std::string& unresolved); @@ -313,6 +355,7 @@ namespace indigo virtual int addAtom(int label) = 0; virtual int addBond(int beg, int end, int order) = 0; + virtual int addBond_Silent(int beg, int end, int order) = 0; void unfoldHydrogens(Array* markers_out, int max_h_cnt = -1, bool impl_h_no_throw = false, bool only_selected = false); diff --git a/core/indigo-core/molecule/molecule.h b/core/indigo-core/molecule/molecule.h index dafb85e84a..eac68878a2 100644 --- a/core/indigo-core/molecule/molecule.h +++ b/core/indigo-core/molecule/molecule.h @@ -45,18 +45,13 @@ namespace indigo void setPseudoAtom(int idx, const char* text); - void renameTemplateAtom(int idx, const char* text); + bool isTemplateAtom(int idx) override; + int getTemplateAtomOccurrence(int idx) override; + virtual int addTemplateAtom(const char* text) override; void setTemplateAtom(int idx, const char* text); - void setTemplateAtomName(int idx, const char* text); - void setTemplateAtomClass(int idx, const char* text); - void setTemplateAtomSeqid(int idx, int seq_id); - void setTemplateAtomSeqName(int idx, const char* seq_name); - - void setTemplateAtomDisplayOption(int idx, int contracted); - void setTemplateAtomTemplateIndex(int idx, int temp_idx); int addBond(int beg, int end, int order) override; - int addBond_Silent(int beg, int end, int order); + int addBond_Silent(int beg, int end, int order) override; void setAtomCharge(int idx, int charge); void setAtomCharge_Silent(int idx, int charge); @@ -98,14 +93,6 @@ namespace indigo bool isPseudoAtom(int idx) override; const char* getPseudoAtom(int idx) override; - bool isTemplateAtom(int idx) override; - const char* getTemplateAtom(int idx) override; - const int getTemplateAtomSeqid(int idx) override; - const char* getTemplateAtomSeqName(int idx) override; - const char* getTemplateAtomClass(int idx) override; - const int getTemplateAtomTemplateIndex(int idx) override; - const int getTemplateAtomDisplayOption(int idx) override; - bool isRSite(int atom_idx) override; dword getRSiteBits(int atom_idx) override; void allowRGroupOnRSite(int atom_idx, int rg_idx) override; @@ -202,27 +189,6 @@ namespace indigo StringPool _pseudo_atom_values; - struct _AttachOrder - { - int ap_idx; - Array ap_id; - }; - - struct _TemplateOccurrence - { - int name_idx; // index in _template_names - int class_idx; // index in _template_classes - int seq_id; // sequence id - int template_idx; // template idx - Array seq_name; // sequence name - DisplayOption contracted; // display option (-1 if undefined, 0 - expanded, 1 - contracted) - Array<_AttachOrder> order; // attach order info - }; - ObjPool<_TemplateOccurrence> _template_occurrences; - - StringPool _template_classes; - StringPool _template_names; - bool _aromatized; bool _ignore_bad_valence; diff --git a/core/indigo-core/molecule/molecule_layered_molecules.h b/core/indigo-core/molecule/molecule_layered_molecules.h index 6d4f794112..efbfeff5db 100644 --- a/core/indigo-core/molecule/molecule_layered_molecules.h +++ b/core/indigo-core/molecule/molecule_layered_molecules.h @@ -97,14 +97,9 @@ namespace indigo bool isPseudoAtom(int idx) override; const char* getPseudoAtom(int idx) override; + int addTemplateAtom(const char* text) override; bool isTemplateAtom(int idx) override; - const char* getTemplateAtom(int idx) override; - const int getTemplateAtomSeqid(int idx) override; - const char* getTemplateAtomSeqName(int idx) override; - const int getTemplateAtomTemplateIndex(int idx) override; - - const char* getTemplateAtomClass(int idx) override; - const int getTemplateAtomDisplayOption(int idx) override; + int getTemplateAtomOccurrence(int idx) override; bool isRSite(int atom_idx) override; dword getRSiteBits(int atom_idx) override; @@ -132,6 +127,7 @@ namespace indigo int addAtom(int label) override; int addBond(int beg, int end, int order) override; + int addBond_Silent(int beg, int end, int order) override; int getImplicitH(int idx, bool impl_h_no_throw) override; void setImplicitH(int idx, int impl_h) override; diff --git a/core/indigo-core/molecule/query_molecule.h b/core/indigo-core/molecule/query_molecule.h index 8b924f9bfd..2e57e35a3c 100644 --- a/core/indigo-core/molecule/query_molecule.h +++ b/core/indigo-core/molecule/query_molecule.h @@ -215,6 +215,7 @@ namespace indigo int value_min; int value_max; + int occurrence_idx; // available only when type is ATOM_PSEUDO or ATOM_TEMPLATE or ATOM_TEMPLATE_CLASS Array alias; @@ -319,13 +320,9 @@ namespace indigo bool isPseudoAtom(int idx) override; const char* getPseudoAtom(int idx) override; + int addTemplateAtom(const char* text) override; bool isTemplateAtom(int idx) override; - const char* getTemplateAtom(int idx) override; - const int getTemplateAtomSeqid(int idx) override; - const char* getTemplateAtomSeqName(int idx) override; - const char* getTemplateAtomClass(int idx) override; - const int getTemplateAtomDisplayOption(int idx) override; - const int getTemplateAtomTemplateIndex(int idx) override; + int getTemplateAtomOccurrence(int idx) override; bool isRSite(int atom_idx) override; dword getRSiteBits(int atom_idx) override; @@ -411,6 +408,7 @@ namespace indigo int addAtom(int label) override; int addBond(int beg, int end, int order) override; + int addBond_Silent(int beg, int end, int order) override; int getImplicitH(int idx, bool impl_h_no_throw) override; void setImplicitH(int idx, int impl_h) override; diff --git a/core/indigo-core/molecule/src/base_molecule.cpp b/core/indigo-core/molecule/src/base_molecule.cpp index 65381c986f..3e52f0d8e8 100644 --- a/core/indigo-core/molecule/src/base_molecule.cpp +++ b/core/indigo-core/molecule/src/base_molecule.cpp @@ -89,6 +89,10 @@ void BaseMolecule::clear() tgroups.clear(); template_attachment_points.clear(); template_attachment_indexes.clear(); + _template_occurrences.clear(); + _template_names.clear(); + _template_classes.clear(); + Graph::clear(); _hl_atoms.clear(); _hl_bonds.clear(); @@ -688,6 +692,12 @@ void BaseMolecule::clone(BaseMolecule& other, Array* mapping, Array* i copyProperties(other, *mapping); for (int i = 0; i < other.monomer_shapes.size(); ++i) monomer_shapes.add(new KetMonomerShape(*other.monomer_shapes[i])); + for (int i = 0; i < other._template_occurrences.size(); ++i) + std::ignore = _template_occurrences.add(other._template_occurrences[i]); + for (int i = 0; i < other._template_names.size(); ++i) + _template_names.add(other._template_names.at(i)); + for (int i = 0; i < other._template_classes.size(); ++i) + _template_classes.add(other._template_classes.at(i)); } void BaseMolecule::clone_KeepIndices(BaseMolecule& other, int skip_flags) @@ -723,6 +733,12 @@ void BaseMolecule::clone_KeepIndices(BaseMolecule& other, int skip_flags) copyProperties(other, mapping); for (int j = 0; j < other.monomer_shapes.size(); ++j) monomer_shapes.add(new KetMonomerShape(*other.monomer_shapes[j])); + for (i = 0; i < other._template_occurrences.size(); ++i) + std::ignore = _template_occurrences.add(other._template_occurrences[i]); + for (i = 0; i < other._template_names.size(); ++i) + _template_names.add(other._template_names.at(i)); + for (i = 0; i < other._template_classes.size(); ++i) + _template_classes.add(other._template_classes.at(i)); } void BaseMolecule::mergeWithMolecule(BaseMolecule& other, Array* mapping, int skip_flags) @@ -1816,8 +1832,7 @@ int BaseMolecule::transformFullCTABtoSCSR(ObjArray& templates) continue; } - int idx = this->asMolecule().addAtom(-1); - this->asMolecule().setTemplateAtom(idx, tg.tgroup_name.ptr()); + int idx = this->addTemplateAtom(tg.tgroup_name.ptr()); this->asMolecule().setTemplateAtomClass(idx, tg.tgroup_class.ptr()); count_occur++; @@ -2091,7 +2106,7 @@ int BaseMolecule::transformFullCTABtoSCSR(ObjArray& templates) int idx = this->asMolecule().addAtom(-1); - this->asMolecule().setTemplateAtom(idx, tg.tgroup_name.ptr()); + int idx = this->addTemplateAtom(tg.tgroup_name.ptr()); this->asMolecule().setTemplateAtomClass(idx, tg.tgroup_class.ptr()); count_occur++; @@ -2571,8 +2586,7 @@ int BaseMolecule::transformFullCTABtoSCSR(ObjArray& templates) continue; } - int idx = this->asMolecule().addAtom(-1); - this->asMolecule().setTemplateAtom(idx, tg.tgroup_name.ptr()); + int idx = this->addTemplateAtom(tg.tgroup_name.ptr()); this->asMolecule().setTemplateAtomClass(idx, tg.tgroup_class.ptr()); count_occur++; @@ -2988,7 +3002,6 @@ void BaseMolecule::getTemplateAtomDirectionsMap(std::vector>& int BaseMolecule::_transformTGroupToSGroup(int idx, int t_idx) { int result = 0; - QS_DEF(Molecule, fragment); QS_DEF(Array, sgs); QS_DEF(Array, base_sgs); QS_DEF(Array, mapping); @@ -2999,6 +3012,7 @@ int BaseMolecule::_transformTGroupToSGroup(int idx, int t_idx) QS_DEF(StringPool, ap_points_ids); QS_DEF(Array, ap_ids); QS_DEF(Array, ap_id); + std::unique_ptr fragment(neu()); int tg_idx = t_idx; if (t_idx == -1) @@ -3007,8 +3021,8 @@ int BaseMolecule::_transformTGroupToSGroup(int idx, int t_idx) TGroup& tgroup = tgroups.getTGroup(tg_idx); if (tgroup.ambiguous) throw Error("Ambiguous monomer cannot be transform to SGroup."); - fragment.clear(); - fragment.clone(*tgroup.fragment.get()); + fragment->clear(); + fragment->clone(*tgroup.fragment.get()); sgs.clear(); att_atoms.clear(); @@ -3018,10 +3032,10 @@ int BaseMolecule::_transformTGroupToSGroup(int idx, int t_idx) ap_points_ids.clear(); ap_ids.clear(); - for (int j = fragment.sgroups.begin(); j != fragment.sgroups.end(); j = fragment.sgroups.next(j)) + for (int j = fragment->sgroups.begin(); j != fragment->sgroups.end(); j = fragment->sgroups.next(j)) { // how to check if group is connected? - auto& sg = fragment.sgroups.getSGroup(j); + auto& sg = fragment->sgroups.getSGroup(j); if (sg.sgroup_type == SGroup::SG_TYPE_SUP) { Superatom& sa = (Superatom&)sg; @@ -3040,7 +3054,7 @@ int BaseMolecule::_transformTGroupToSGroup(int idx, int t_idx) if (base_sgs.size() > 1) throw Error("transformTGroupToSGroup(): wrong template structure found (more then one base SGroup detected)"); - SGroup& sgu = fragment.sgroups.getSGroup(base_sgs[0]); + SGroup& sgu = fragment->sgroups.getSGroup(base_sgs[0]); if (sgu.sgroup_type != SGroup::SG_TYPE_SUP) throw Error("transformTGroupToSGroup(): wrong template structure found (base SGroup is not Superatom type)"); @@ -3064,17 +3078,17 @@ int BaseMolecule::_transformTGroupToSGroup(int idx, int t_idx) } } - mergeWithMolecule(fragment, &mapping); + mergeWithMolecule(*fragment, &mapping); for (const auto sg_index : sgs) { - const SGroup& lvg = fragment.sgroups.getSGroup(sg_index); + const SGroup& lvg = fragment->sgroups.getSGroup(sg_index); for (const auto lvgroup_index : lvgroups) { if (lvg.atoms.find(lvgroup_index) > -1) { atoms_to_delete.push(mapping[lvg.atoms[0]]); - fragment.removeSGroup(sg_index); - if (!fragment.sgroups.hasSGroup(sg_index)) + fragment->removeSGroup(sg_index); + if (!fragment->sgroups.hasSGroup(sg_index)) { break; } @@ -3082,11 +3096,11 @@ int BaseMolecule::_transformTGroupToSGroup(int idx, int t_idx) } } - for (auto i : fragment.vertices()) + for (auto i : fragment->vertices()) { int aidx = mapping[i]; auto tpos = getAtomXyz(idx); - tpos.add(fragment.getAtomXyz(i)); + tpos.add(fragment->getAtomXyz(i)); if (aidx > -1) setAtomXyz(aidx, tpos); } @@ -3339,8 +3353,7 @@ int BaseMolecule::_transformSGroupToTGroup(int sg_idx, int& tg_id) tg.fragment->sgroups.remove((sgs[j])); } - int idx = this->asMolecule().addAtom(-1); - this->asMolecule().setTemplateAtom(idx, tg.tgroup_name.ptr()); + int idx = this->addTemplateAtom(tg.tgroup_name.ptr()); this->asMolecule().setTemplateAtomClass(idx, tg.tgroup_class.ptr()); this->asMolecule().setTemplateAtomSeqid(idx, su.seqid); this->asMolecule().setTemplateAtomTemplateIndex(idx, tg_idx); @@ -5034,6 +5047,117 @@ KetDocument& BaseMolecule::getKetDocument() return *_document; } +const char* BaseMolecule::getTemplateAtom(int idx) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + const char* res = _template_names.at(occur.name_idx); + + if (res == 0) + throw Error("template atom string is zero"); + + return res; +} + +const char* BaseMolecule::getTemplateAtomClass(int idx) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + const char* res = _template_classes.at(occur.class_idx); + + return res; +} + +const char* BaseMolecule::getTemplateAtomSeqName(int idx) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + return occur.seq_name.ptr(); +} + +const int BaseMolecule::getTemplateAtomTemplateIndex(int idx) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + const int res = occur.template_idx; + return res; +} + +const int BaseMolecule::getTemplateAtomSeqid(int idx) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + const int res = occur.seq_id; + + return res; +} + +const int BaseMolecule::getTemplateAtomDisplayOption(int idx) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + const int res = static_cast(occur.contracted); + // const int res = occur.contracted; + + return res; +} + +void BaseMolecule::renameTemplateAtom(int idx, const char* text) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + _template_names.set(occur.name_idx, text); + updateEditRevision(); +} + +void BaseMolecule::setTemplateAtomName(int idx, const char* text) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + occur.name_idx = _template_names.add(text); + updateEditRevision(); +} + +void BaseMolecule::setTemplateAtomClass(int idx, const char* text) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + occur.class_idx = _template_classes.add(text); + updateEditRevision(); +} + +void BaseMolecule::setTemplateAtomSeqid(int idx, int seq_id) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + occur.seq_id = seq_id; + updateEditRevision(); +} + +void BaseMolecule::setTemplateAtomSeqName(int idx, const char* seq_name) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + occur.seq_name.readString(seq_name, true); + updateEditRevision(); +} + +void BaseMolecule::setTemplateAtomTemplateIndex(int idx, int temp_idx) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + occur.template_idx = temp_idx; + updateEditRevision(); +} + +void BaseMolecule::setTemplateAtomDisplayOption(int idx, int option) +{ + int template_occur_idx = getTemplateAtomOccurrence(idx); + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + occur.contracted = (DisplayOption)option; + updateEditRevision(); +} + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/core/indigo-core/molecule/src/molecule.cpp b/core/indigo-core/molecule/src/molecule.cpp index c78e08b3d7..54f7097989 100644 --- a/core/indigo-core/molecule/src/molecule.cpp +++ b/core/indigo-core/molecule/src/molecule.cpp @@ -56,9 +56,6 @@ void Molecule::clear() _total_h.clear(); _valence.clear(); _radicals.clear(); - _template_occurrences.clear(); - _template_names.clear(); - _template_classes.clear(); _aromatized = false; _ignore_bad_valence = false; @@ -342,15 +339,11 @@ void Molecule::setPseudoAtom(int idx, const char* text) updateEditRevision(); } -void Molecule::renameTemplateAtom(int idx, const char* text) +int Molecule::addTemplateAtom(const char* text) { - auto occur_idx = _atoms[idx].template_occur_idx; - if (_atoms[idx].number == ELEM_TEMPLATE) - { - _TemplateOccurrence& occur = _template_occurrences.at(occur_idx); - _template_names.set(occur.name_idx, text); - updateEditRevision(); - } + int idx = addAtom(ELEM_TEMPLATE); + setTemplateAtom(idx, text); + return idx; } void Molecule::setTemplateAtom(int idx, const char* text) @@ -365,66 +358,6 @@ void Molecule::setTemplateAtom(int idx, const char* text) updateEditRevision(); } -void Molecule::setTemplateAtomName(int idx, const char* text) -{ - if (_atoms[idx].number != ELEM_TEMPLATE) - throw Error("setTemplateAtomClass(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(_atoms[idx].template_occur_idx); - occur.name_idx = _template_names.add(text); - updateEditRevision(); -} - -void Molecule::setTemplateAtomClass(int idx, const char* text) -{ - if (_atoms[idx].number != ELEM_TEMPLATE) - throw Error("setTemplateAtomClass(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(_atoms[idx].template_occur_idx); - occur.class_idx = _template_classes.add(text); - updateEditRevision(); -} - -void Molecule::setTemplateAtomSeqid(int idx, int seq_id) -{ - if (_atoms[idx].number != ELEM_TEMPLATE) - throw Error("setTemplateAtomSeqid(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(_atoms[idx].template_occur_idx); - occur.seq_id = seq_id; - updateEditRevision(); -} - -void Molecule::setTemplateAtomSeqName(int idx, const char* seq_name) -{ - if (_atoms[idx].number != ELEM_TEMPLATE) - throw Error("setTemplateAtomSeqName(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(_atoms[idx].template_occur_idx); - occur.seq_name.readString(seq_name, true); - updateEditRevision(); -} - -void Molecule::setTemplateAtomTemplateIndex(int idx, int temp_idx) -{ - if (_atoms[idx].number != ELEM_TEMPLATE) - throw Error("setTemplateAtomTemplateIndex(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(_atoms[idx].template_occur_idx); - occur.template_idx = temp_idx; - updateEditRevision(); -} - -void Molecule::setTemplateAtomDisplayOption(int idx, int option) -{ - if (_atoms[idx].number != ELEM_TEMPLATE) - throw Error("setTemplateAtomDisplayOption(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(_atoms[idx].template_occur_idx); - occur.contracted = (DisplayOption)option; - updateEditRevision(); -} - int Molecule::getVacantPiOrbitals(int atom_idx, int conn, int* lonepairs_out) { int group = Element::group(getAtomNumber(atom_idx)); @@ -1406,83 +1339,14 @@ bool Molecule::isTemplateAtom(int idx) return _atoms[idx].number == ELEM_TEMPLATE; } -const char* Molecule::getTemplateAtom(int idx) +int Molecule::getTemplateAtomOccurrence(int idx) { const _Atom& atom = _atoms[idx]; if (atom.number != ELEM_TEMPLATE) - throw Error("getTemplateAtom(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(atom.template_occur_idx); - const char* res = _template_names.at(occur.name_idx); - - if (res == 0) - throw Error("template atom string is zero"); + throw Error("getTemplateAtomOccurrence(): atom #%d is not a template atom", idx); - return res; -} - -const char* Molecule::getTemplateAtomClass(int idx) -{ - const _Atom& atom = _atoms[idx]; - - if (atom.number != ELEM_TEMPLATE) - throw Error("getTemplateAtomClass(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(atom.template_occur_idx); - const char* res = _template_classes.at(occur.class_idx); - - return res; -} - -const char* Molecule::getTemplateAtomSeqName(int idx) -{ - const _Atom& atom = _atoms[idx]; - - if (atom.number != ELEM_TEMPLATE) - throw Error("getTemplateAtomClass(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(atom.template_occur_idx); - return occur.seq_name.ptr(); -} - -const int Molecule::getTemplateAtomTemplateIndex(int idx) -{ - const _Atom& atom = _atoms[idx]; - - if (atom.number != ELEM_TEMPLATE) - throw Error("getTemplateAtomTemplateIndex(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(atom.template_occur_idx); - const int res = occur.template_idx; - return res; -} - -const int Molecule::getTemplateAtomSeqid(int idx) -{ - const _Atom& atom = _atoms[idx]; - - if (atom.number != ELEM_TEMPLATE) - throw Error("getTemplateAtomSeqid(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(atom.template_occur_idx); - const int res = occur.seq_id; - - return res; -} - -const int Molecule::getTemplateAtomDisplayOption(int idx) -{ - const _Atom& atom = _atoms[idx]; - - if (atom.number != ELEM_TEMPLATE) - throw Error("getTemplateAtomDisplayOption(): atom #%d is not a template atom", idx); - - _TemplateOccurrence& occur = _template_occurrences.at(atom.template_occur_idx); - const int res = static_cast(occur.contracted); - // const int res = occur.contracted; - - return res; + return atom.template_occur_idx; } BaseMolecule* Molecule::neu() diff --git a/core/indigo-core/molecule/src/molecule_auto_loader.cpp b/core/indigo-core/molecule/src/molecule_auto_loader.cpp index 86bae67ac0..37845787a4 100644 --- a/core/indigo-core/molecule/src/molecule_auto_loader.cpp +++ b/core/indigo-core/molecule/src/molecule_auto_loader.cpp @@ -94,48 +94,7 @@ void MoleculeAutoLoader::loadQueryMolecule(QueryMolecule& qmol) void MoleculeAutoLoader::loadMolecule(BaseMolecule& bmol) { - try - { - _loadMolecule(bmol); - } - catch (Exception e) - { - bool error_flag = false; - if (bmol.isQueryMolecule()) - { - // trying to load as molecule - Molecule mol; - _scanner->seek(0, SEEK_SET); - try - { - _loadMolecule(mol); - if (mol.tgroups.getTGroupCount()) - { - mol.transformTemplatesToSuperatoms(); - Array mol_out_buffer; - ArrayOutput mol_output(mol_out_buffer); - MolfileSaver saver_tmp(mol_output); - saver_tmp.saveMolecule(mol.asMolecule()); - mol_out_buffer.push(0); - if (_own_scanner) - delete _scanner; - _own_scanner = true; - _scanner = new BufferScanner(mol_out_buffer); - _loadMolecule(bmol); - } - else - error_flag = true; - } - catch (...) - { - error_flag = true; - } - } - else - error_flag = true; - if (error_flag) - throw; - } + _loadMolecule(bmol); if (!bmol.isQueryMolecule()) { diff --git a/core/indigo-core/molecule/src/molecule_json_loader.cpp b/core/indigo-core/molecule/src/molecule_json_loader.cpp index 5db75e343b..a5d074665b 100644 --- a/core/indigo-core/molecule/src/molecule_json_loader.cpp +++ b/core/indigo-core/molecule/src/molecule_json_loader.cpp @@ -1636,15 +1636,16 @@ void MoleculeJsonLoader::loadMolecule(BaseMolecule& mol, bool load_arrows) for (SizeType i = 0; i < _monomer_array.Size(); i++) { auto& ma = _monomer_array[i]; - int idx = mol.asMolecule().addAtom(-1); + + if (!ma.HasMember("alias")) + throw Error("Monomer alias is missing"); + + int idx = mol.addTemplateAtom(ma["alias"].GetString()); int monomer_id = std::stoi(std::string(ma["id"].GetString())); monomer_id_mapping.emplace(monomer_id, idx); - if (ma.HasMember("alias")) - mol.asMolecule().setTemplateAtom(idx, ma["alias"].GetString()); - if (ma.HasMember("seqid")) - mol.asMolecule().setTemplateAtomSeqid(idx, ma["seqid"].GetInt()); + mol.setTemplateAtomSeqid(idx, ma["seqid"].GetInt()); if (ma.HasMember("position")) { @@ -1656,8 +1657,8 @@ void MoleculeJsonLoader::loadMolecule(BaseMolecule& mol, bool load_arrows) auto temp_it = _id_to_template.find(template_id); if (temp_it != _id_to_template.end()) { - mol.asMolecule().setTemplateAtomClass(idx, monomerMolClass(monomerTemplateClass(_templates[temp_it->second])).c_str()); - mol.asMolecule().setTemplateAtomTemplateIndex(idx, temp_it->second); + mol.setTemplateAtomClass(idx, monomerMolClass(monomerTemplateClass(_templates[temp_it->second])).c_str()); + mol.setTemplateAtomTemplateIndex(idx, temp_it->second); } } @@ -1805,7 +1806,7 @@ void MoleculeJsonLoader::loadMolecule(BaseMolecule& mol, bool load_arrows) if (atp2.size()) mol.setTemplateAtomAttachmentOrder(id2, id1, atp2.c_str()); - mol.asMolecule().addBond_Silent(id1, id2, order); + mol.addBond_Silent(id1, id2, order); } MoleculeLayout ml(mol, false); diff --git a/core/indigo-core/molecule/src/molecule_json_saver.cpp b/core/indigo-core/molecule/src/molecule_json_saver.cpp index fe5ab34634..149f85249c 100644 --- a/core/indigo-core/molecule/src/molecule_json_saver.cpp +++ b/core/indigo-core/molecule/src/molecule_json_saver.cpp @@ -1606,8 +1606,7 @@ void MoleculeJsonSaver::saveMolecule(BaseMolecule& bmol, JsonWriter& writer) } BaseMolecule::collapse(*mol); - if (!mol->isQueryMolecule()) - mol->getTemplatesMap(_templates); + mol->getTemplatesMap(_templates); // save root elements saveRoot(*mol, writer); diff --git a/core/indigo-core/molecule/src/molecule_layered_molecules.cpp b/core/indigo-core/molecule/src/molecule_layered_molecules.cpp index 4bef0e268a..c57a2794d8 100644 --- a/core/indigo-core/molecule/src/molecule_layered_molecules.cpp +++ b/core/indigo-core/molecule/src/molecule_layered_molecules.cpp @@ -356,39 +356,19 @@ const char* LayeredMolecules::getPseudoAtom(int idx) return _proto.getPseudoAtom(idx); } -bool LayeredMolecules::isTemplateAtom(int idx) -{ - return _proto.isTemplateAtom(idx); -} - -const char* LayeredMolecules::getTemplateAtom(int idx) -{ - return _proto.getTemplateAtom(idx); -} - -const int LayeredMolecules::getTemplateAtomSeqid(int idx) -{ - return _proto.getTemplateAtomSeqid(idx); -} - -const char* LayeredMolecules::getTemplateAtomSeqName(int idx) +int LayeredMolecules::addTemplateAtom(const char* text) { - return _proto.getTemplateAtomSeqName(idx); + return _proto.addTemplateAtom(text); } -const int LayeredMolecules::getTemplateAtomTemplateIndex(int idx) -{ - return _proto.getTemplateAtomTemplateIndex(idx); -} - -const char* LayeredMolecules::getTemplateAtomClass(int idx) +bool LayeredMolecules::isTemplateAtom(int idx) { - return _proto.getTemplateAtomClass(idx); + return _proto.isTemplateAtom(idx); } -const int LayeredMolecules::getTemplateAtomDisplayOption(int idx) +int LayeredMolecules::getTemplateAtomOccurrence(int idx) { - return _proto.getTemplateAtomDisplayOption(idx); + return _proto.getTemplateAtomOccurrence(idx); } bool LayeredMolecules::isRSite(int idx) @@ -491,6 +471,11 @@ int LayeredMolecules::addBond(int beg, int end, int order) return _proto.addBond(beg, end, order); } +int LayeredMolecules::addBond_Silent(int beg, int end, int order) +{ + return _proto.addBond(beg, end, order); +} + int LayeredMolecules::getImplicitH(int idx, bool impl_h_no_throw) { return _proto.getImplicitH(idx, impl_h_no_throw); diff --git a/core/indigo-core/molecule/src/molfile_loader.cpp b/core/indigo-core/molecule/src/molfile_loader.cpp index 13728809ca..b71aa374e5 100644 --- a/core/indigo-core/molecule/src/molfile_loader.cpp +++ b/core/indigo-core/molecule/src/molfile_loader.cpp @@ -2127,29 +2127,32 @@ void MolfileLoader::_postLoad() std::set templates_to_remove; std::unordered_map new_templates; - for (int atom_idx = _bmol->vertexBegin(); atom_idx != _bmol->vertexEnd(); atom_idx = _bmol->vertexNext(atom_idx)) + if (!_bmol->isQueryMolecule()) { - if (_bmol->isTemplateAtom(atom_idx) && isNucleotideClass(_bmol->getTemplateAtomClass(atom_idx))) + for (int atom_idx = _bmol->vertexBegin(); atom_idx != _bmol->vertexEnd(); atom_idx = _bmol->vertexNext(atom_idx)) { - int tg_idx = _bmol->getTemplateAtomTemplateIndex(atom_idx); - if (tg_idx == -1) + if (_bmol->isTemplateAtom(atom_idx) && isNucleotideClass(_bmol->getTemplateAtomClass(atom_idx))) { - auto atom_name = _bmol->getTemplateAtom(atom_idx); - auto nt_it = nucleo_templates.find(atom_name); - if (nt_it != nucleo_templates.end()) + int tg_idx = _bmol->getTemplateAtomTemplateIndex(atom_idx); + if (tg_idx == -1) { - if (_expandNucleotide(atom_idx, nt_it->second, new_templates)) - templates_to_remove.insert(nt_it->second); + auto atom_name = _bmol->getTemplateAtom(atom_idx); + auto nt_it = nucleo_templates.find(atom_name); + if (nt_it != nucleo_templates.end()) + { + if (_expandNucleotide(atom_idx, nt_it->second, new_templates)) + templates_to_remove.insert(nt_it->second); + } + else + { + // TODO: handle missing template case + } } - else + else // tg_idx != -1 means the template is converted from S-Group { - // TODO: handle missing template case + // TODO: handle modified monomer. Currently it leaves as is. } } - else // tg_idx != -1 means the template is converted from S-Group - { - // TODO: handle modified monomer. Currently it leaves as is. - } } } @@ -2185,103 +2188,98 @@ bool MolfileLoader::_expandNucleotide(int nuc_atom_idx, int tg_idx, std::unorder GranularNucleotide nuc; if (MonomerTemplates::splitNucleotide(tg.tgroup_class.ptr(), tg.tgroup_name.ptr(), nuc)) { - if (_mol) - { - auto& ph = nuc.at(MonomerClass::Phosphate).get(); - auto& sugar = nuc.at(MonomerClass::Sugar).get(); - auto& base = nuc.at(MonomerClass::Base).get(); - int seq_id = _mol->getTemplateAtomSeqid(nuc_atom_idx); - // collect attachment points. only left attachment remains untouched. - std::unordered_map atp_map; - for (int j = _mol->template_attachment_points.begin(); j != _mol->template_attachment_points.end(); j = _mol->template_attachment_points.next(j)) - { - auto& ap = _mol->template_attachment_points.at(j); - if (ap.ap_occur_idx == nuc_atom_idx && std::string(ap.ap_id.ptr()) > kLeftAttachmentPoint) - { - atp_map.emplace(ap.ap_id.ptr(), ap.ap_aidx); - _mol->template_attachment_points.remove(j); - auto att_idxs = _mol->getTemplateAtomAttachmentPointIdxs(nuc_atom_idx, j); - if (att_idxs.has_value()) - att_idxs.value().second.get().remove(att_idxs.value().first); - } - } - - // patch existing nucleotide atom with phosphate - _mol->renameTemplateAtom(nuc_atom_idx, ph.first.second.c_str()); - _mol->setTemplateAtomClass(nuc_atom_idx, MonomerTemplates::classToStr(ph.first.first).c_str()); - - // add sugar - int sugar_idx = _mol->addAtom(-1); - _mol->setTemplateAtom(sugar_idx, sugar.first.second.c_str()); - _mol->setTemplateAtomClass(sugar_idx, MonomerTemplates::classToStr(sugar.first.first).c_str()); - - // add base - int base_idx = _mol->addAtom(-1); - _mol->setTemplateAtom(base_idx, base.first.second.c_str()); - _mol->setTemplateAtomClass(base_idx, MonomerTemplates::classToStr(base.first.first).c_str()); - - // modify connections - auto right_it = atp_map.find(std::string(kRightAttachmentPoint)); - int right_idx = -1; - if (right_it != atp_map.end()) - { - // nucleotide had Br attachment point. Now it should be moved to sugar. - // disconnect right nucleotide - right_idx = right_it->second; - _mol->removeEdge(_mol->findEdgeIndex(nuc_atom_idx, right_idx)); - // connect right nucleotide to the sugar - _mol->addBond_Silent(sugar_idx, right_it->second, BOND_SINGLE); - // [sugar <- (Al) right nucleotide] - _mol->updateTemplateAtomAttachmentDestination(right_idx, nuc_atom_idx, sugar_idx); - // [sugar (Br) -> right nucleotide] - _mol->setTemplateAtomAttachmentOrder(sugar_idx, right_idx, kRightAttachmentPoint); - atp_map.erase(right_it); - } - - for (auto& atp : atp_map) - { - _mol->removeEdge(_mol->findEdgeIndex(nuc_atom_idx, atp.second)); - // connect branch to base. which is incorrect!!! TODO: use substructure matcher to determine right monomer!!! - _mol->addBond_Silent(base_idx, atp.second, BOND_SINGLE); - // [sugar <- (Al) right nucleotide] - _mol->updateTemplateAtomAttachmentDestination(atp.second, nuc_atom_idx, base_idx); - // [sugar (Br) -> right nucleotide] - _mol->setTemplateAtomAttachmentOrder(base_idx, right_it->second, atp.first.c_str()); - } - - // connect phosphate to the sugar - _mol->addBond_Silent(nuc_atom_idx, sugar_idx, BOND_SINGLE); - // [phosphate (Br) -> sugar] - _mol->setTemplateAtomAttachmentOrder(nuc_atom_idx, sugar_idx, kRightAttachmentPoint); - // [phosphate <- (Al) sugar] - _mol->setTemplateAtomAttachmentOrder(sugar_idx, nuc_atom_idx, kLeftAttachmentPoint); - // connect base to sugar - _mol->addBond_Silent(sugar_idx, base_idx, BOND_SINGLE); - // [sugar (Cx) -> base] - _mol->setTemplateAtomAttachmentOrder(sugar_idx, base_idx, kBranchAttachmentPoint); - // [sugar <- (Al) base] - _mol->setTemplateAtomAttachmentOrder(base_idx, sugar_idx, kLeftAttachmentPoint); - // fix coordinates - Vec3f sugar_pos, base_pos; - if (!_bmol->getMiddlePoint(nuc_atom_idx, right_idx, sugar_pos)) - { - sugar_pos.copy(_bmol->getAtomXyz(nuc_atom_idx)); - sugar_pos.x += LayoutOptions::DEFAULT_BOND_LENGTH; - } - base_pos.copy(sugar_pos); - base_pos.y -= LayoutOptions::DEFAULT_BOND_LENGTH; - _bmol->setAtomXyz(sugar_idx, sugar_pos); - _bmol->setAtomXyz(base_idx, base_pos); - // set seqid - _mol->setTemplateAtomSeqid(nuc_atom_idx, seq_id++); // increment seq_id after phosphate - _mol->setTemplateAtomSeqid(sugar_idx, seq_id); - _mol->setTemplateAtomSeqid(base_idx, seq_id); - // handle templates - _mol->setTemplateAtomTemplateIndex(nuc_atom_idx, _insertTemplate(ph, new_templates)); - _mol->setTemplateAtomTemplateIndex(sugar_idx, _insertTemplate(sugar, new_templates)); - _mol->setTemplateAtomTemplateIndex(base_idx, _insertTemplate(base, new_templates)); - return true; - } + auto& ph = nuc.at(MonomerClass::Phosphate).get(); + auto& sugar = nuc.at(MonomerClass::Sugar).get(); + auto& base = nuc.at(MonomerClass::Base).get(); + int seq_id = _bmol->getTemplateAtomSeqid(nuc_atom_idx); + // collect attachment points. only left attachment remains untouched. + std::unordered_map atp_map; + for (int j = _bmol->template_attachment_points.begin(); j != _bmol->template_attachment_points.end(); j = _bmol->template_attachment_points.next(j)) + { + auto& ap = _bmol->template_attachment_points.at(j); + if (ap.ap_occur_idx == nuc_atom_idx && std::string(ap.ap_id.ptr()) > kLeftAttachmentPoint) + { + atp_map.emplace(ap.ap_id.ptr(), ap.ap_aidx); + _bmol->template_attachment_points.remove(j); + auto att_idxs = _bmol->getTemplateAtomAttachmentPointIdxs(nuc_atom_idx, j); + if (att_idxs.has_value()) + att_idxs.value().second.get().remove(att_idxs.value().first); + } + } + + // patch existing nucleotide atom with phosphate + _bmol->renameTemplateAtom(nuc_atom_idx, ph.first.second.c_str()); + _bmol->setTemplateAtomClass(nuc_atom_idx, MonomerTemplates::classToStr(ph.first.first).c_str()); + + // add sugar + int sugar_idx = _bmol->addTemplateAtom(sugar.first.second.c_str()); + _bmol->setTemplateAtomClass(sugar_idx, MonomerTemplates::classToStr(sugar.first.first).c_str()); + + // add base + int base_idx = _bmol->addTemplateAtom(base.first.second.c_str()); + _bmol->setTemplateAtomClass(base_idx, MonomerTemplates::classToStr(base.first.first).c_str()); + + // modify connections + auto right_it = atp_map.find(std::string(kRightAttachmentPoint)); + int right_idx = -1; + if (right_it != atp_map.end()) + { + // nucleotide had Br attachment point. Now it should be moved to sugar. + // disconnect right nucleotide + right_idx = right_it->second; + _bmol->removeEdge(_bmol->findEdgeIndex(nuc_atom_idx, right_idx)); + // connect right nucleotide to the sugar + _bmol->addBond_Silent(sugar_idx, right_it->second, BOND_SINGLE); + // [sugar <- (Al) right nucleotide] + _bmol->updateTemplateAtomAttachmentDestination(right_idx, nuc_atom_idx, sugar_idx); + // [sugar (Br) -> right nucleotide] + _bmol->setTemplateAtomAttachmentOrder(sugar_idx, right_idx, kRightAttachmentPoint); + atp_map.erase(right_it); + } + + for (auto& atp : atp_map) + { + _bmol->removeEdge(_bmol->findEdgeIndex(nuc_atom_idx, atp.second)); + // connect branch to base. which is incorrect!!! TODO: use substructure matcher to determine right monomer!!! + _bmol->addBond_Silent(base_idx, atp.second, BOND_SINGLE); + // [sugar <- (Al) right nucleotide] + _bmol->updateTemplateAtomAttachmentDestination(atp.second, nuc_atom_idx, base_idx); + // [sugar (Br) -> right nucleotide] + _bmol->setTemplateAtomAttachmentOrder(base_idx, right_it->second, atp.first.c_str()); + } + + // connect phosphate to the sugar + _bmol->addBond_Silent(nuc_atom_idx, sugar_idx, BOND_SINGLE); + // [phosphate (Br) -> sugar] + _bmol->setTemplateAtomAttachmentOrder(nuc_atom_idx, sugar_idx, kRightAttachmentPoint); + // [phosphate <- (Al) sugar] + _bmol->setTemplateAtomAttachmentOrder(sugar_idx, nuc_atom_idx, kLeftAttachmentPoint); + // connect base to sugar + _bmol->addBond_Silent(sugar_idx, base_idx, BOND_SINGLE); + // [sugar (Cx) -> base] + _bmol->setTemplateAtomAttachmentOrder(sugar_idx, base_idx, kBranchAttachmentPoint); + // [sugar <- (Al) base] + _bmol->setTemplateAtomAttachmentOrder(base_idx, sugar_idx, kLeftAttachmentPoint); + // fix coordinates + Vec3f sugar_pos, base_pos; + if (!_bmol->getMiddlePoint(nuc_atom_idx, right_idx, sugar_pos)) + { + sugar_pos.copy(_bmol->getAtomXyz(nuc_atom_idx)); + sugar_pos.x += LayoutOptions::DEFAULT_BOND_LENGTH; + } + base_pos.copy(sugar_pos); + base_pos.y -= LayoutOptions::DEFAULT_BOND_LENGTH; + _bmol->setAtomXyz(sugar_idx, sugar_pos); + _bmol->setAtomXyz(base_idx, base_pos); + // set seqid + _bmol->setTemplateAtomSeqid(nuc_atom_idx, seq_id++); // increment seq_id after phosphate + _bmol->setTemplateAtomSeqid(sugar_idx, seq_id); + _bmol->setTemplateAtomSeqid(base_idx, seq_id); + // handle templates + _bmol->setTemplateAtomTemplateIndex(nuc_atom_idx, _insertTemplate(ph, new_templates)); + _bmol->setTemplateAtomTemplateIndex(sugar_idx, _insertTemplate(sugar, new_templates)); + _bmol->setTemplateAtomTemplateIndex(base_idx, _insertTemplate(base, new_templates)); + return true; } return false; } @@ -2599,16 +2597,19 @@ void MolfileLoader::_readCtab3000() if (_mol != 0) { - _mol->addAtom(label); - if (atom_type == _ATOM_PSEUDO) + if (atom_type == _ATOM_TEMPLATE) { _preparePseudoAtomLabel(buf); - _mol->setPseudoAtom(i, buf.ptr()); + _mol->addTemplateAtom(buf.ptr()); } - else if (atom_type == _ATOM_TEMPLATE) + else { - _preparePseudoAtomLabel(buf); - _mol->setTemplateAtom(i, buf.ptr()); + _mol->addAtom(label); + if (atom_type == _ATOM_PSEUDO) + { + _preparePseudoAtomLabel(buf); + _mol->setPseudoAtom(i, buf.ptr()); + } } } else @@ -2622,7 +2623,7 @@ void MolfileLoader::_readCtab3000() else if (atom_type == _ATOM_PSEUDO) _qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_PSEUDO, buf.ptr())); else if (atom_type == _ATOM_TEMPLATE) - _qmol->addAtom(new QueryMolecule::Atom(QueryMolecule::ATOM_TEMPLATE, buf.ptr())); + _qmol->addTemplateAtom(buf.ptr()); else if (atom_type == _ATOM_A) _qmol->addAtom(QueryMolecule::Atom::nicht(new QueryMolecule::Atom(QueryMolecule::ATOM_NUMBER, ELEM_H))); else if (atom_type == _ATOM_AH) @@ -2920,24 +2921,12 @@ void MolfileLoader::_readCtab3000() QS_DEF(Array, temp_class); strscan.readWord(temp_class, 0); temp_class.push(0); - if (_mol != 0) - _mol->setTemplateAtomClass(i, temp_class.ptr()); - else - { - _qmol->resetAtom( - i, QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_TEMPLATE_CLASS, temp_class.ptr()))); - } + _bmol->setTemplateAtomClass(i, temp_class.ptr()); } else if (strcmp(prop, "SEQID") == 0) { int seq_id = strscan.readInt1(); - if (_mol != 0) - _mol->setTemplateAtomSeqid(i, seq_id); - else - { - _qmol->resetAtom(i, - QueryMolecule::Atom::und(_qmol->releaseAtom(i), new QueryMolecule::Atom(QueryMolecule::ATOM_TEMPLATE_SEQID, seq_id))); - } + _bmol->setTemplateAtomSeqid(i, seq_id); } else if (strcmp(prop, "SEQNAME") == 0) { diff --git a/core/indigo-core/molecule/src/query_molecule.cpp b/core/indigo-core/molecule/src/query_molecule.cpp index 0a18a14713..19634f0406 100644 --- a/core/indigo-core/molecule/src/query_molecule.cpp +++ b/core/indigo-core/molecule/src/query_molecule.cpp @@ -809,6 +809,10 @@ void QueryMolecule::writeSmartsAtom(Output& output, Atom* atom, int aam, int chi output.writeChar('*'); break; } + case ATOM_TEMPLATE: { + output.writeString(atom->alias.ptr()); + break; + } case ATOM_CONNECTIVITY: { output.printf("X%d", atom->value_min); @@ -827,7 +831,6 @@ void QueryMolecule::writeSmartsAtom(Output& output, Atom* atom, int aam, int chi case ATOM_RSITE: output.printf("*:%d", atom->value_min); break; - default: { throw Error("Unknown atom attribute %d", atom->type); break; @@ -974,6 +977,19 @@ const char* QueryMolecule::getPseudoAtom(int idx) throw Error("getPseudoAtom() applied to something that is not a pseudo-atom"); } +int QueryMolecule::addTemplateAtom(const char* alias) +{ + std::unique_ptr atom(new Atom(ATOM_TEMPLATE, alias)); + int template_occur_idx = _template_occurrences.add(); + atom->occurrence_idx = template_occur_idx; + _TemplateOccurrence& occur = _template_occurrences.at(template_occur_idx); + occur.name_idx = _template_names.add(alias); + occur.seq_id = -1; + occur.template_idx = -1; + occur.contracted = DisplayOption::Undefined; + return addAtom(atom.release()); +} + bool QueryMolecule::isTemplateAtom(int idx) { // This is dirty hack; however, it is legal here, as template atoms @@ -996,48 +1012,13 @@ bool QueryMolecule::isTemplateAtom(int idx) return false; } -const char* QueryMolecule::getTemplateAtom(int idx) +int QueryMolecule::getTemplateAtomOccurrence(int idx) { // see the comment above in isTemplateAtom() + if (!isTemplateAtom(idx)) + throw Error("getTemplateAtomOccurrence() applied to something that is not a template atom"); - if (_atoms[idx]->type == ATOM_TEMPLATE) - return _atoms[idx]->alias.ptr(); - - if (_atoms[idx]->type == OP_AND) - { - int i; - - for (i = 0; i < _atoms[idx]->children.size(); i++) - if (_atoms[idx]->children[i]->type == ATOM_TEMPLATE) - return ((Atom*)_atoms[idx]->children[i])->alias.ptr(); - } - - throw Error("getTemplateAtom() applied to something that is not a template atom"); -} - -const char* QueryMolecule::getTemplateAtomClass(int /*idx*/) -{ - return 0; -} - -const int QueryMolecule::getTemplateAtomSeqid(int /*idx*/) -{ - return -1; -} - -const char* QueryMolecule::getTemplateAtomSeqName(int /*idx*/) -{ - return nullptr; -} - -const int QueryMolecule::getTemplateAtomTemplateIndex(int /*idx*/) -{ - return -1; -} - -const int QueryMolecule::getTemplateAtomDisplayOption(int /*idx*/) -{ - return -1; + return _atoms[idx]->occurrence_idx; } bool QueryMolecule::isSaturatedAtom(int /*idx*/) @@ -1056,13 +1037,13 @@ QueryMolecule::Node::~Node() IMPL_ERROR(QueryMolecule::Atom, "query atom"); -QueryMolecule::Atom::Atom() : Node(OP_NONE) +QueryMolecule::Atom::Atom() : Node(OP_NONE), occurrence_idx(0) { value_min = 0; value_max = 0; } -QueryMolecule::Atom::Atom(int type_, int value) : Node(type_) +QueryMolecule::Atom::Atom(int type_, int value) : Node(type_), occurrence_idx(0) { if (type_ == ATOM_NUMBER || type_ == ATOM_CHARGE || type_ == ATOM_ISOTOPE || type_ == ATOM_RADICAL || type_ == ATOM_AROMATICITY || type_ == ATOM_VALENCE || type_ == ATOM_RING_BONDS || type_ == ATOM_RING_BONDS_AS_DRAWN || type_ == ATOM_SUBSTITUENTS || type_ == ATOM_SUBSTITUENTS_AS_DRAWN || @@ -1075,13 +1056,13 @@ QueryMolecule::Atom::Atom(int type_, int value) : Node(type_) throw Error("bad type: %d", type_); } -QueryMolecule::Atom::Atom(int type_, int val_min, int val_max) : Node(type_) +QueryMolecule::Atom::Atom(int type_, int val_min, int val_max) : Node(type_), occurrence_idx(0) { value_min = val_min; value_max = val_max; } -QueryMolecule::Atom::Atom(int type_, const char* value) : Node(type_) +QueryMolecule::Atom::Atom(int type_, const char* value) : Node(type_), occurrence_idx(0) { if (type_ == ATOM_PSEUDO || type_ == ATOM_TEMPLATE || type_ == ATOM_TEMPLATE_CLASS) alias.readString(value, true); @@ -1089,7 +1070,7 @@ QueryMolecule::Atom::Atom(int type_, const char* value) : Node(type_) throw Error("bad type: %d", type_); } -QueryMolecule::Atom::Atom(int type_, QueryMolecule* value) : Node(type_) +QueryMolecule::Atom::Atom(int type_, QueryMolecule* value) : Node(type_), occurrence_idx(0) { if (type_ == ATOM_FRAGMENT) fragment.reset(value); @@ -2051,6 +2032,22 @@ int QueryMolecule::addBond(int beg, int end, QueryMolecule::Bond* bond) return idx; } +int QueryMolecule::addBond_Silent(int beg, int end, int order) +{ + updateEditRevision(); + int idx = _addBaseBond(beg, end); + + _bonds.expand(idx + 1); + _bonds.set(idx, QueryMolecule::createQueryMoleculeBond(order, BOND_ZERO, BOND_ZERO)); + + aromaticity.setCanBeAromatic(idx, false); + setBondStereoCare(idx, false); + + updateEditRevision(); + + return idx; +} + QueryMolecule::Bond& QueryMolecule::getBond(int idx) { return *_bonds[idx]; @@ -2075,6 +2072,7 @@ void QueryMolecule::Atom::copy(const Atom& other) type = other.type; value_max = other.value_max; value_min = other.value_min; + occurrence_idx = other.occurrence_idx; fragment.reset(nullptr); if (other.fragment.get() != 0) diff --git a/core/indigo-core/molecule/src/sequence_loader.cpp b/core/indigo-core/molecule/src/sequence_loader.cpp index 37c1cde738..a4577433eb 100644 --- a/core/indigo-core/molecule/src/sequence_loader.cpp +++ b/core/indigo-core/molecule/src/sequence_loader.cpp @@ -259,8 +259,7 @@ void SequenceLoader::addAminoAcid(BaseMolecule& mol, char ch) { Vec3f pos(_col * LayoutOptions::DEFAULT_MONOMER_BOND_LENGTH, -LayoutOptions::DEFAULT_MONOMER_BOND_LENGTH * _row, 0); std::string aa(1, ch); - int amino_idx = mol.asMolecule().addAtom(-1); - mol.asMolecule().setTemplateAtom(amino_idx, monomerNameByAlias(kMonomerClassAA, aa).c_str()); + int amino_idx = mol.addTemplateAtom(monomerNameByAlias(kMonomerClassAA, aa).c_str()); mol.asMolecule().setTemplateAtomClass(amino_idx, kMonomerClassAA); mol.asMolecule().setTemplateAtomSeqid(amino_idx, _seq_id); mol.asMolecule().setAtomXyz(amino_idx, pos); @@ -275,8 +274,7 @@ void SequenceLoader::addAminoAcid(BaseMolecule& mol, char ch) int SequenceLoader::addTemplateAtom(BaseMolecule& mol, const char* alias, const char* monomer_class, int seq_id) { - int idx = mol.asMolecule().addAtom(-1); - mol.asMolecule().setTemplateAtom(idx, alias); + int idx = mol.addTemplateAtom(alias); mol.asMolecule().setTemplateAtomClass(idx, monomer_class); mol.asMolecule().setTemplateAtomSeqid(idx, seq_id); return idx;