From 6934d7da6f13b4146dfc97f33b3c49aca11c51f3 Mon Sep 17 00:00:00 2001 From: Aliaksandr Dziarkach <18146690+AliaksandrDziarkach@users.noreply.github.com> Date: Mon, 22 Jan 2024 12:23:24 +0300 Subject: [PATCH] Backmerge: #1452 Convert from implicit hydrogens change layout (#1554) --- api/c/indigo/src/indigo_misc.cpp | 20 +- .../ref/basic/unfold_layout_hydrogens.py.out | 76 +++++ .../tests/basic/unfold_layout_hydrogens.py | 145 +++++++++ api/wasm/indigo-ketcher/indigo-ketcher.cpp | 2 - .../layout/molecule_layout_graph.h | 37 +-- .../layout/src/molecule_layout_graph.cpp | 210 +++++++------ .../src/molecule_layout_graph_smart.cpp | 289 ------------------ .../backend/service/v2/indigo_api.py | 3 - 8 files changed, 375 insertions(+), 407 deletions(-) create mode 100644 api/tests/integration/ref/basic/unfold_layout_hydrogens.py.out create mode 100644 api/tests/integration/tests/basic/unfold_layout_hydrogens.py diff --git a/api/c/indigo/src/indigo_misc.cpp b/api/c/indigo/src/indigo_misc.cpp index 00fda0cf1d..0a1fb01723 100644 --- a/api/c/indigo/src/indigo_misc.cpp +++ b/api/c/indigo/src/indigo_misc.cpp @@ -18,6 +18,7 @@ #include "base_cpp/output.h" #include "base_cpp/scanner.h" +#include "layout/molecule_layout.h" #include "molecule/elements.h" #include "molecule/icm_loader.h" #include "molecule/icm_saver.h" @@ -456,7 +457,24 @@ CEXPORT int indigoUnfoldHydrogens(int item) if (IndigoBaseMolecule::is(obj)) { - obj.getBaseMolecule().unfoldHydrogens(&markers, -1); + BaseMolecule& bmol = obj.getBaseMolecule(); + bmol.unfoldHydrogens(&markers, -1); + // Layout hydrogens + MoleculeLayoutGraphSimple layout; + layout.preserve_existing_layout = true; + layout.makeOnGraph(bmol); + for (int i = layout.vertexBegin(); i < layout.vertexEnd(); i = layout.vertexNext(i)) + { + const Vec3f& pos = bmol.getAtomXyz(layout.getVertexExtIdx(i)); + layout.getPos(i).set(pos.x, pos.y); + } + Filter new_filter(markers.ptr(), Filter::EQ, 1); + layout.layout(bmol, 1, &new_filter, true); + for (int i = layout.vertexBegin(); i < layout.vertexEnd(); i = layout.vertexNext(i)) + { + const LayoutVertex& vert = layout.getLayoutVertex(i); + bmol.setAtomXyz(vert.ext_idx, vert.pos.x, vert.pos.y, 0.f); + } } else if (IndigoBaseReaction::is(obj)) { diff --git a/api/tests/integration/ref/basic/unfold_layout_hydrogens.py.out b/api/tests/integration/ref/basic/unfold_layout_hydrogens.py.out new file mode 100644 index 0000000000..4d71fa960a --- /dev/null +++ b/api/tests/integration/ref/basic/unfold_layout_hydrogens.py.out @@ -0,0 +1,76 @@ + +testing molfile: + + -INDIGO-01102422162D + + 7 7 0 0 0 0 0 0 0 0999 V2000 + 1.9394 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9406 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4401 6.0909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0972 2.5109 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0935 3.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8603 0.9988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0828 3.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 1 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 7 1 0 0 0 0 + 7 6 1 0 0 0 0 + 6 4 1 0 0 0 0 +M END + + +Unfolded mol equal to expected + +After fold molfile: + + -INDIGO-01000000002D + + 7 7 0 0 0 0 0 0 0 0999 V2000 + 1.9394 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9406 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4401 6.0909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0972 2.5109 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0935 3.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8603 0.9988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0828 3.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 1 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 7 1 0 0 0 0 + 7 6 1 0 0 0 0 + 6 4 1 0 0 0 0 +M END + + +testing molfile: + + -INDIGO-01102422162D + + 3 3 0 0 0 0 0 0 0 0999 V2000 + 1.9394 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9406 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4401 6.0909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 1 1 0 0 0 0 +M END + + +Unfolded mol equal to expected + +After fold molfile: + + -INDIGO-01000000002D + + 3 3 0 0 0 0 0 0 0 0999 V2000 + 1.9394 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9406 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4401 6.0909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 1 1 0 0 0 0 +M END + diff --git a/api/tests/integration/tests/basic/unfold_layout_hydrogens.py b/api/tests/integration/tests/basic/unfold_layout_hydrogens.py new file mode 100644 index 0000000000..990bab57bc --- /dev/null +++ b/api/tests/integration/tests/basic/unfold_layout_hydrogens.py @@ -0,0 +1,145 @@ +from __future__ import print_function + +import difflib +import os +import sys + +sys.path.append( + os.path.normpath( + os.path.join(os.path.abspath(__file__), "..", "..", "..", "common") + ) +) +from env_indigo import * # noqa + +indigo = Indigo() +indigo.setOption("molfile-saving-skip-date", "1") + + +def test_mol_unfold(mol, expected_unfolded): + print("\ntesting molfile:\n%s" % mol) + molecule = indigo.loadMolecule(mol) + molecule.unfoldHydrogens() + unfolded = molecule.molfile() + unfolded_list = sorted(unfolded.split("\n")) + expected_unfolded_list = sorted(expected_unfolded.split("\n")) + diff = "".join(difflib.context_diff(unfolded_list, expected_unfolded_list)) + if diff: + print("\nDiff between expected and after unfold molfile:\n%s" % diff) + else: + print("Unfolded mol equal to expected") + molecule.foldHydrogens() + print("\nAfter fold molfile:\n%s" % molecule.molfile()) + + +multi_component_mol = """ + -INDIGO-01102422162D + + 7 7 0 0 0 0 0 0 0 0999 V2000 + 1.9394 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9406 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4401 6.0909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0972 2.5109 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0935 3.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8603 0.9988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0828 3.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 1 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 7 1 0 0 0 0 + 7 6 1 0 0 0 0 + 6 4 1 0 0 0 0 +M END + +""" + +multi_component_mol_unfolded = """ + -INDIGO-01000000002D + + 21 21 0 0 0 0 0 0 0 0999 V2000 + 1.9394 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9406 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4401 6.0909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0972 2.5109 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0935 3.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 5.8603 0.9988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0828 3.5060 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.0000 5.5688 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7654 4.2413 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8801 5.5686 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1146 4.2413 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6743 6.7340 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2060 6.7339 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.3856 1.5534 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0862 2.6585 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0923 4.5060 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0935 3.5085 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 5.9099 0.0000 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 4.9168 1.3303 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1974 3.9709 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3222 4.4769 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 1 1 0 0 0 0 + 4 5 1 0 0 0 0 + 5 7 1 0 0 0 0 + 7 6 1 0 0 0 0 + 6 4 1 0 0 0 0 + 1 8 1 0 0 0 0 + 1 9 1 0 0 0 0 + 2 10 1 0 0 0 0 + 2 11 1 0 0 0 0 + 3 12 1 0 0 0 0 + 3 13 1 0 0 0 0 + 4 14 1 0 0 0 0 + 4 15 1 0 0 0 0 + 5 16 1 0 0 0 0 + 5 17 1 0 0 0 0 + 6 18 1 0 0 0 0 + 6 19 1 0 0 0 0 + 7 20 1 0 0 0 0 + 7 21 1 0 0 0 0 +M END +""" +test_mol_unfold(multi_component_mol, multi_component_mol_unfolded) + +single_component_mol = """ + -INDIGO-01102422162D + + 3 3 0 0 0 0 0 0 0 0999 V2000 + 1.9394 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9406 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4401 6.0909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 1 1 0 0 0 0 +M END + +""" + +single_component_mol_unfolded = """ + -INDIGO-01000000002D + + 9 9 0 0 0 0 0 0 0 0999 V2000 + 1.9394 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.9406 5.2260 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 2.4401 6.0909 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 1.0000 5.5688 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.7654 4.2413 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.8801 5.5686 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.1146 4.2413 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1.6743 6.7340 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 3.2060 6.7339 0.0000 H 0 0 0 0 0 0 0 0 0 0 0 0 + 1 2 1 0 0 0 0 + 2 3 1 0 0 0 0 + 3 1 1 0 0 0 0 + 1 4 1 0 0 0 0 + 1 5 1 0 0 0 0 + 2 6 1 0 0 0 0 + 2 7 1 0 0 0 0 + 3 8 1 0 0 0 0 + 3 9 1 0 0 0 0 +M END +""" + +test_mol_unfold(single_component_mol, single_component_mol_unfolded) diff --git a/api/wasm/indigo-ketcher/indigo-ketcher.cpp b/api/wasm/indigo-ketcher/indigo-ketcher.cpp index c06201f0ee..5c527fe97f 100644 --- a/api/wasm/indigo-ketcher/indigo-ketcher.cpp +++ b/api/wasm/indigo-ketcher/indigo-ketcher.cpp @@ -466,9 +466,7 @@ namespace indigo } else { - indigoSetOptionBool("layout-preserve-existing", true); _checkResult(indigoUnfoldHydrogens(iko.id())); - indigoSetOptionBool("layout-preserve-existing", false); } return iko.toString(options, outputFormat.size() ? outputFormat : "ket"); } diff --git a/core/indigo-core/layout/molecule_layout_graph.h b/core/indigo-core/layout/molecule_layout_graph.h index 5b148f7d71..f2b87f67ef 100644 --- a/core/indigo-core/layout/molecule_layout_graph.h +++ b/core/indigo-core/layout/molecule_layout_graph.h @@ -150,7 +150,7 @@ namespace indigo void cloneLayoutGraph(MoleculeLayoutGraph& other, Array* mapping); void copyLayoutTo(MoleculeLayoutGraph& other, const Array& mapping) const; - virtual void layout(BaseMolecule& molecule, float bond_length, const Filter* filter, bool respect_existing) = 0; + virtual void layout(BaseMolecule& molecule, float bond_length, const Filter* filter, bool respect_existing); const BaseMolecule* getMolecule(const int** molecule_edge_mapping) const { @@ -169,6 +169,8 @@ namespace indigo int max_iterations; layout_orientation_value layout_orientation; + bool preserve_existing_layout; + CancellationHandler* cancellation; DECL_ERROR; @@ -295,8 +297,12 @@ namespace indigo bool _isPointOutsideCycleEx(const Cycle& cycle, const Vec2f& p, const Array& mapping) const; static bool _border_cb(Graph& graph, const Array& vertices, const Array& edges, void* context); + void getBoundingBox(Rect2f& bbox) const; + void getBoundingBox(Vec2f& left_bottom, Vec2f& right_top) const; + void copyCoordsFromComponent(MoleculeLayoutGraph& component, Vec2f shift = {0, 0}); + // for components - virtual void _calcMorganCodes() = 0; + virtual void _calcMorganCodes(); // for whole graph virtual void _assignAbsoluteCoordinates(float bond_length) = 0; @@ -336,6 +342,12 @@ namespace indigo void _attachCrossingEdges(); void _buildOutline(void); + + // make tree of biconnected components (tree[i] - component incoming to vertex i or -1) + static void _makeComponentsTree(BiconnectedDecomposer& decon, PtrArray& components, Array& tree); + + void _layoutMultipleComponents(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length); + void _layoutSingleComponent(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length); }; class DLLEXPORT MoleculeLayoutGraphSimple : public MoleculeLayoutGraph @@ -352,8 +364,6 @@ namespace indigo void makeLayoutSubgraph(MoleculeLayoutGraph& graph, Filter& filter) override; - void layout(BaseMolecule& molecule, float bond_length, const Filter* filter, bool respect_existing) override; - void flipped() override { _flipped = true; @@ -378,9 +388,6 @@ namespace indigo // for whole graph void _assignAbsoluteCoordinates(float bond_length) override; - // for components - void _calcMorganCodes() override; - // assigning coordinates void _assignRelativeCoordinates(int& fixed_component, const MoleculeLayoutGraph& supergraph) override; bool _tryToFindPattern(int& fixed_component); @@ -404,12 +411,6 @@ namespace indigo bool _isPointOutsideCycle(const Cycle& cycle, const Vec2f& p) const override; static bool _edge_check(Graph& graph, int e_idx, void* context); - - // make tree of biconnected components (tree[i] - component incoming to vertex i or -1) - static void _makeComponentsTree(BiconnectedDecomposer& decon, PtrArray& components, Array& tree); - - void _layoutMultipleComponents(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length); - void _layoutSingleComponent(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length); }; struct local_pair_ii @@ -571,7 +572,6 @@ namespace indigo void makeLayoutSubgraph(MoleculeLayoutGraph& graph, Filter& vertex_filter) override; void makeLayoutSubgraph(MoleculeLayoutGraph& graph, Filter& vertex_filter, Filter* edge_filter); - void layout(BaseMolecule& molecule, float bond_length, const Filter* filter, bool respect_existing) override; void calcMorganCode(); long getMorganCode(); @@ -608,9 +608,6 @@ namespace indigo // for whole graph void _assignAbsoluteCoordinates(float bond_length) override; - // for components - void _calcMorganCodes() override; - // assigning coordinates struct interval { @@ -674,12 +671,6 @@ namespace indigo const float _energyOfPoint(Vec2f p) const; int _isCisConfiguratuin(Vec2f p1, Vec2f p2, Vec2f p3, Vec2f p4); - // make tree of biconnected components (tree[i] - -1 or component incoming to vertex i) - static void _makeComponentsTree(BiconnectedDecomposer& decon, PtrArray& components, Array& tree); - - void _layoutMultipleComponents(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length); - void _layoutSingleComponent(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length); - Array _layout_component_number; // number of layout component of certain edge int _layout_component_count; diff --git a/core/indigo-core/layout/src/molecule_layout_graph.cpp b/core/indigo-core/layout/src/molecule_layout_graph.cpp index b22cf349bb..2199043939 100644 --- a/core/indigo-core/layout/src/molecule_layout_graph.cpp +++ b/core/indigo-core/layout/src/molecule_layout_graph.cpp @@ -35,6 +35,7 @@ MoleculeLayoutGraph::MoleculeLayoutGraph() : Graph() _molecule_edge_mapping = 0; cancellation = 0; _flipped = false; + preserve_existing_layout = false; } MoleculeLayoutGraph::~MoleculeLayoutGraph() @@ -263,7 +264,7 @@ void MoleculeLayoutGraphSimple::makeLayoutSubgraph(MoleculeLayoutGraph& graph, F } } -void MoleculeLayoutGraphSimple::layout(BaseMolecule& molecule, float bond_length, const Filter* filter, bool respect_existing) +void MoleculeLayoutGraph::layout(BaseMolecule& molecule, float bond_length, const Filter* filter, bool respect_existing) { if (molecule.vertexCount() == 0) return; @@ -273,24 +274,29 @@ void MoleculeLayoutGraphSimple::layout(BaseMolecule& molecule, float bond_length if (fabs(bond_length) < EPSILON) throw Error("zero bond length"); + _molecule = &molecule; if (n_components > 1) _layoutMultipleComponents(molecule, respect_existing, filter, bond_length); else _layoutSingleComponent(molecule, respect_existing, filter, bond_length); } -void MoleculeLayoutGraphSimple::_calcMorganCodes() +void MoleculeLayoutGraph::_calcMorganCodes() { MorganCode morgan(*this); QS_DEF(Array, morgan_codes); morgan.calculate(morgan_codes, 3, 7); + _total_morgan_code = 0; for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) + { _layout_vertices[i].morgan_code = morgan_codes[i]; + _total_morgan_code += morgan_codes[i]; + } } -void MoleculeLayoutGraphSimple::_makeComponentsTree(BiconnectedDecomposer& decon, PtrArray& components, Array& tree) +void MoleculeLayoutGraph::_makeComponentsTree(BiconnectedDecomposer& decon, PtrArray& components, Array& tree) { int i, j, v, k; bool from; @@ -344,7 +350,7 @@ int MoleculeLayoutGraphSimple::_pattern_cmp2(const PatternLayout& p1, int n_v, i return n_e - p1.edgeCount(); } -void MoleculeLayoutGraphSimple::_layoutMultipleComponents(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length) +void MoleculeLayoutGraph::_layoutMultipleComponents(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length) { QS_DEF(Array, src_layout); QS_DEF(Array, molecule_edge_mapping); @@ -359,6 +365,8 @@ void MoleculeLayoutGraphSimple::_layoutMultipleComponents(BaseMolecule& molecule for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) molecule_edge_mapping[i] = getEdgeExtIdx(i); + _molecule_edge_mapping = molecule_edge_mapping.ptr(); + PtrArray components; components.clear(); @@ -410,119 +418,116 @@ void MoleculeLayoutGraphSimple::_layoutMultipleComponents(BaseMolecule& molecule component._assignFinalCoordinates(bond_length, src_layout); } - // position components - float x_min, x_max, x_start = 0.f, dx; - float y_min, y_max, y_start = 0.f, max_height = 0.f, dy; - int col_count; - int row, col; - int n_fixed = 0; - - // fixed first - if (filter != 0) + if (respect_existing && preserve_existing_layout) // TODO: { - x_min = 1.0E+20f; - y_min = 1.0E+20f; - - // find fixed components - for (i = 0; i < n_components; i++) + for (int i = 0; i < n_components; i++) { - MoleculeLayoutGraph& component = *components[i]; - - if (component._n_fixed > 0) - { - n_fixed++; - - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - { - const Vec2f& pos = component.getPos(j); - - if (pos.x < x_min) - x_min = pos.x; - if (pos.y < y_min) - y_min = pos.y; - if (pos.y > y_start) - y_start = pos.y; - } - } + copyCoordsFromComponent(*components[i]); } + } + else // Move fixed componets to touch (0,0), layout rest of components in grid + { + // position components + float row_bottom = 0.f; // where to place non-fixed components + int n_fixed = 0; - // position fixed - if (n_fixed > 0) + // fixed first + if (filter != 0) { - dy = -y_min; - dx = -x_min; + bool first_component = true; + Rect2f fixed_comps_bbox; + // find left bottom corner and top of fixed components for (i = 0; i < n_components; i++) { MoleculeLayoutGraph& component = *components[i]; if (component._n_fixed > 0) - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - _layout_vertices[component.getVertexExtIdx(j)].pos.sum(component.getPos(j), Vec2f(dx, dy)); + { + n_fixed++; + Rect2f bbox; + component.getBoundingBox(bbox); + if (first_component) + { + fixed_comps_bbox.copy(bbox); + first_component = false; + } + else + { + fixed_comps_bbox.extend(bbox); + } + } } - y_start += dy + 2 * bond_length; - } - } + // position fixed - shift all rectangle with atoms to point left down corner to (0, 0) + if (n_fixed > 0) + { + Vec2f shift = fixed_comps_bbox.leftBottom(); + shift.negate(); + for (i = 0; i < n_components; i++) + { + MoleculeLayoutGraph& component = *components[i]; + if (component._n_fixed > 0) + copyCoordsFromComponent(component, shift); + } - col_count = (int)ceil(sqrt((float)n_components - n_fixed)); + row_bottom += fixed_comps_bbox.height() + 2 * bond_length; + } + } - for (i = 0, k = 0; i < n_components; i++) - { - MoleculeLayoutGraph& component = *components[i]; + // layout non-fixed components in square + int col_count = (int)ceil(sqrt((float)n_components - n_fixed)); + float column_left = 0.f; + float row_height = 0.f; - if (component._n_fixed > 0) - continue; - - // Component shifting - row = k / col_count; - col = k % col_count; + for (i = 0, k = 0; i < n_components; i++) + { + MoleculeLayoutGraph& component = *components[i]; - x_min = 1.0E+20f; - x_max = -1.0E+20f; - y_min = 1.0E+20f; - y_max = -1.0E+20f; + if (component._n_fixed > 0) + continue; - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - { - const Vec2f& pos = component.getPos(j); - - if (pos.x < x_min) - x_min = pos.x; - if (pos.x > x_max) - x_max = pos.x; - if (pos.y < y_min) - y_min = pos.y; - if (pos.y > y_max) - y_max = pos.y; - } + int row = k / col_count; + int col = k % col_count; - if (col == 0 && row > 0) - { - y_start += max_height + 2 * bond_length; - max_height = 0.f; - } + // first column in row - add previous row height+space to row_bottom + if (col == 0 && row > 0) + { + row_bottom += row_height + 2 * bond_length; + row_height = 0.f; + column_left = 0.f; + } + // Add space between columns + if (col > 0) + column_left += 2 * bond_length; - if (col > 0) - dx = x_start - x_min + 2 * bond_length; - else - dx = -x_min; + // Component shifting + Rect2f bbox; + component.getBoundingBox(bbox); + Vec2f shift = bbox.leftBottom(); + shift.negate(); + shift.add(Vec2f(column_left, row_bottom)); - dy = y_start - y_min; + copyCoordsFromComponent(component, shift); - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - _layout_vertices[component.getVertexExtIdx(j)].pos.sum(component.getPos(j), Vec2f(dx, dy)); + column_left += bbox.width(); - x_start = x_max + dx; + row_height = std::max(row_height, bbox.height()); - if (y_max - y_min > max_height) - max_height = y_max - y_min; + k++; + } + } +} - k++; +void MoleculeLayoutGraph::copyCoordsFromComponent(MoleculeLayoutGraph& component, Vec2f shift) +{ + for (int i = component.vertexBegin(); i < component.vertexEnd(); i = component.vertexNext(i)) + { + _layout_vertices[component.getVertexExtIdx(i)].pos.sum(component.getPos(i), shift); } } -void MoleculeLayoutGraphSimple::_layoutSingleComponent(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length) +void MoleculeLayoutGraph::_layoutSingleComponent(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length) { QS_DEF(Array, src_layout); QS_DEF(Array, molecule_edge_mapping); @@ -566,6 +571,33 @@ void MoleculeLayoutGraphSimple::_layoutSingleComponent(BaseMolecule& molecule, b _assignFinalCoordinates(bond_length, src_layout); } +void MoleculeLayoutGraph::getBoundingBox(Vec2f& left_bottom, Vec2f& right_top) const +{ + bool first = true; + for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) + { + const Vec2f& pos = getPos(i); + if (first) + { + left_bottom = right_top = pos; + first = false; + } + else + { + left_bottom.min(pos); + right_top.max(pos); + } + } +} + +void MoleculeLayoutGraph::getBoundingBox(Rect2f& bbox) const +{ + Vec2f left_bottom; + Vec2f right_top; + getBoundingBox(left_bottom, right_top); + bbox = Rect2f(left_bottom, right_top); +} + #ifdef M_LAYOUT_DEBUG #include "base_cpp/output.h" diff --git a/core/indigo-core/layout/src/molecule_layout_graph_smart.cpp b/core/indigo-core/layout/src/molecule_layout_graph_smart.cpp index d8ca59dd98..b6bc7090d1 100644 --- a/core/indigo-core/layout/src/molecule_layout_graph_smart.cpp +++ b/core/indigo-core/layout/src/molecule_layout_graph_smart.cpp @@ -106,295 +106,6 @@ void MoleculeLayoutGraphSmart::makeLayoutSubgraph(MoleculeLayoutGraph& graph, Fi _layout_component_count = 0; } -void MoleculeLayoutGraphSmart::layout(BaseMolecule& molecule, float bond_length, const Filter* filter, bool respect_existing) -{ - if (molecule.vertexCount() == 0) - return; - - int n_components = countComponents(); - - if (fabs(bond_length) < EPSILON) - throw Error("zero bond length"); - - _molecule = &molecule; - if (n_components > 1) - _layoutMultipleComponents(molecule, respect_existing, filter, bond_length); - else - _layoutSingleComponent(molecule, respect_existing, filter, bond_length); -} - -void MoleculeLayoutGraphSmart::_calcMorganCodes() -{ - MorganCode morgan(*this); - QS_DEF(Array, morgan_codes); - - morgan.calculate(morgan_codes, 3, 7); - - _total_morgan_code = 0; - for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) - { - _layout_vertices[i].morgan_code = morgan_codes[i]; - _total_morgan_code += morgan_codes[i]; - } -} - -void MoleculeLayoutGraphSmart::_makeComponentsTree(BiconnectedDecomposer& decon, PtrArray& components, Array& tree) -{ - int i, j, v, k; - bool from; - - for (i = 0; i < tree.size(); i++) - tree[i] = -1; - - for (i = 0; i < components.size(); i++) - { - for (k = components[i]->vertexBegin(); k < components[i]->vertexEnd(); k = components[i]->vertexNext(k)) - { - v = components[i]->getLayoutVertex(k).ext_idx; - - if (decon.isArticulationPoint(v)) - { - // if connection vertex belongs to i-th component - from = false; - - for (j = 0; j < decon.getIncomingComponents(v).size(); j++) - { - // and component doesn't come from this vertex - if (decon.getIncomingComponents(v)[j] == i) - from = true; - } - - // TODO: try to remove tree[]; - if (!from) - tree[v] = i; - } - } - } -} - -void MoleculeLayoutGraphSmart::_layoutMultipleComponents(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length) -{ - QS_DEF(Array, src_layout); - QS_DEF(Array, molecule_edge_mapping); - - int n_components = countComponents(); - - const Array& decomposition = getDecomposition(); - int i, j, k; - - molecule_edge_mapping.clear_resize(edgeEnd()); - - for (i = edgeBegin(); i < edgeEnd(); i = edgeNext(i)) - molecule_edge_mapping[i] = getEdgeExtIdx(i); - - _molecule_edge_mapping = molecule_edge_mapping.ptr(); - - PtrArray components; - - components.clear(); - - for (i = 0; i < n_components; i++) - { - Filter comp_filter(decomposition.ptr(), Filter::EQ, i); - std::unique_ptr current_component(getInstance()); - components.add(current_component.release()); - MoleculeLayoutGraph& component = *components.top(); - - component.cancellation = cancellation; - - component.makeLayoutSubgraph(*this, comp_filter); - component.max_iterations = max_iterations; - component.layout_orientation = layout_orientation; - - component._molecule = &molecule; - component._molecule_edge_mapping = molecule_edge_mapping.ptr(); - - src_layout.clear_resize(component.vertexEnd()); - - if (respect_existing) - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - src_layout[j] = getPos(component.getVertexExtIdx(j)); - else - src_layout.zerofill(); - - if (filter != 0) - { - component._fixed_vertices.resize(component.vertexEnd()); - component._fixed_vertices.zerofill(); - - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - if (!filter->valid(component.getVertexExtIdx(j))) - { - component._fixed_vertices[j] = 1; - component._n_fixed++; - component._layout_vertices[j].pos = getPos(component.getVertexExtIdx(j)); - } - } - - if (component.vertexCount() > 1) - { - component._calcMorganCodes(); - component._assignAbsoluteCoordinates(bond_length); - } - component._assignFinalCoordinates(bond_length, src_layout); - } - - // position components - float x_min, x_max, x_start = 0.f, dx; - float y_min, y_max, y_start = 0.f, max_height = 0.f, dy; - int col_count; - int row, col; - int n_fixed = 0; - - // fixed first - if (filter != 0) - { - x_min = 1.0E+20f; - y_min = 1.0E+20f; - - // find fixed components - for (i = 0; i < n_components; i++) - { - MoleculeLayoutGraph& component = *components[i]; - - if (component._n_fixed > 0) - { - n_fixed++; - - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - { - const Vec2f& pos = component.getPos(j); - - if (pos.x < x_min) - x_min = pos.x; - if (pos.y < y_min) - y_min = pos.y; - if (pos.y > y_start) - y_start = pos.y; - } - } - } - - // position fixed - if (n_fixed > 0) - { - dy = -y_min; - dx = -x_min; - - for (i = 0; i < n_components; i++) - { - MoleculeLayoutGraph& component = *components[i]; - - if (component._n_fixed > 0) - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - _layout_vertices[component.getVertexExtIdx(j)].pos.sum(component.getPos(j), Vec2f(dx, dy)); - } - - y_start += dy + 2 * bond_length; - } - } - - col_count = (int)ceil(sqrt((float)n_components - n_fixed)); - - for (i = 0, k = 0; i < n_components; i++) - { - MoleculeLayoutGraph& component = *components[i]; - - if (component._n_fixed > 0) - continue; - - // Component shifting - row = k / col_count; - col = k % col_count; - - x_min = 1.0E+20f; - x_max = -1.0E+20f; - y_min = 1.0E+20f; - y_max = -1.0E+20f; - - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - { - const Vec2f& pos = component.getPos(j); - - if (pos.x < x_min) - x_min = pos.x; - if (pos.x > x_max) - x_max = pos.x; - if (pos.y < y_min) - y_min = pos.y; - if (pos.y > y_max) - y_max = pos.y; - } - - if (col == 0 && row > 0) - { - y_start += max_height + 2 * bond_length; - max_height = 0.f; - } - - if (col > 0) - dx = x_start - x_min + 2 * bond_length; - else - dx = -x_min; - - dy = y_start - y_min; - - for (j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j)) - _layout_vertices[component.getVertexExtIdx(j)].pos.sum(component.getPos(j), Vec2f(dx, dy)); - - x_start = x_max + dx; - - if (y_max - y_min > max_height) - max_height = y_max - y_min; - - k++; - } -} - -void MoleculeLayoutGraphSmart::_layoutSingleComponent(BaseMolecule& molecule, bool respect_existing, const Filter* filter, float bond_length) -{ - QS_DEF(Array, src_layout); - QS_DEF(Array, molecule_edge_mapping); - - int i; - - molecule_edge_mapping.clear_resize(molecule.edgeEnd()); - - for (i = 0; i < molecule_edge_mapping.size(); i++) - molecule_edge_mapping[i] = i; - - _molecule = &molecule; - _molecule_edge_mapping = molecule_edge_mapping.ptr(); - - src_layout.clear_resize(vertexEnd()); - - if (respect_existing) - for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) - src_layout[i] = getPos(i); - else - src_layout.zerofill(); - - if (filter != 0) - { - _fixed_vertices.resize(vertexEnd()); - _fixed_vertices.zerofill(); - - for (int i = vertexBegin(); i < vertexEnd(); i = vertexNext(i)) - if (!filter->valid(i)) - { - _fixed_vertices[i] = 1; - _n_fixed++; - } - } - - if (vertexCount() > 1) - { - _calcMorganCodes(); - _assignAbsoluteCoordinates(bond_length); - } - _assignFinalCoordinates(bond_length, src_layout); -} - MoleculeLayoutSmoothingSegment::MoleculeLayoutSmoothingSegment(MoleculeLayoutGraphSmart& mol, Vec2f& start, Vec2f& finish) : _graph(mol), _start(start), _finish(finish) { diff --git a/utils/indigo-service/backend/service/v2/indigo_api.py b/utils/indigo-service/backend/service/v2/indigo_api.py index 064b045dec..466526708e 100644 --- a/utils/indigo-service/backend/service/v2/indigo_api.py +++ b/utils/indigo-service/backend/service/v2/indigo_api.py @@ -998,9 +998,6 @@ def convert_explicit_hydrogens(): md.struct.foldHydrogens() else: md.struct.unfoldHydrogens() - indigo.setOption("layout-preserve-existing", True) - md.struct.layout() - indigo.setOption("layout-preserve-existing", False) return get_response( md, data["output_format"],