void MoleculeLayoutGraph::_refineCoordinates()

in core/indigo-core/layout/src/molecule_layout_graph_refine.cpp [132:430]


void MoleculeLayoutGraph::_refineCoordinates(const BiconnectedDecomposer& bc_decomposer, const PtrArray<MoleculeLayoutGraph>& bc_components,
                                             const Array<int>& bc_tree)
{
    RefinementState beg_state(*this);
    RefinementState best_state(*this);
    RefinementState new_state(*this);
    QS_DEF(Array<int>, branch);
    int v1, v2;
    int v1c, v2c;
    int i, j, n;

    v1c = v1 = vertexBegin();
    v2c = v2 = vertexNext(v1);

    // Calculate initial energy
    beg_state.copyFromGraph();
    beg_state.calcEnergy();
    beg_state.calcDistance(v1, v2);

    best_state.copy(beg_state);
    new_state.copy(beg_state);
    // Look through all vertex pairs which are closer than 0.6
    bool improved = true;
    QS_DEF(RedBlackSet<int>, edges);
    QS_DEF(Array<int>, components1);
    QS_DEF(Array<int>, components2);
    EnumContext context;

    context.edges = &edges;
    context.graph = this;
    context.maxIterationNumber = max_iterations;

    int max_improvements = max_iterations * max_iterations;
    int n_improvements = 0;

    while (improved)
    {
        if (max_improvements > 0 && n_improvements > max_improvements)
            break;

        n_improvements++;

        improved = false;

        edges.clear();

        int n_edges = 0;
        int n_enumerations = 0;
        bool to_break = false;

        new_state.copy(beg_state);

        for (v1 = vertexBegin(); v1 < vertexEnd() && !to_break; v1 = vertexNext(v1))
            for (v2 = vertexNext(v1); v2 < vertexEnd(); v2 = vertexNext(v2))
            {
                new_state.calcDistance(v1, v2);

                if (new_state.dist > 0.36f)
                    continue;

                // Check if they are from the same component
                bool next_pair = false;

                bc_decomposer.getVertexComponents(v1, components1);
                bc_decomposer.getVertexComponents(v2, components2);

                for (i = 0; i < components1.size() && !next_pair; i++)
                    for (j = 0; j < components2.size(); j++)
                    {
                        int comp1 = components1[i];
                        int comp2 = components2[j];

                        if (comp1 == comp2 && !bc_components[comp1]->isSingleEdge() && !bc_components[comp2]->isSingleEdge())
                        {
                            next_pair = true;
                            break;
                        }
                    }

                if (next_pair)
                    continue;

                // check iterations limit
                if (max_iterations > 0 && n_enumerations > max_iterations)
                {
                    to_break = true;
                    break;
                }

                n_enumerations++;

                // Find acyclic edges on the all paths between v1 and v2
                PathEnumerator path_enum(*this, v1, v2);

                path_enum.cb_check_edge = _edge_check;
                path_enum.cb_handle_path = _path_handle;
                path_enum.context = &context;

                context.iterationNumber = 0;

                try
                {
                    path_enum.process();
                }
                catch (Error)
                {
                    // iterations limit reached
                }

                if (edges.size() == n_edges)
                    continue;

                n_edges = edges.size();

                if (beg_state.dist - 0.00001 > new_state.dist)
                {
                    beg_state.dist = new_state.dist;
                    v1c = v1;
                    v2c = v2;
                }
            }

        if (edges.size() == 0)
        {
            beg_state.applyToGraph();
            break;
        }

        // Flipping
        // Look through found edges
        for (i = edges.begin(); i < edges.end(); i = edges.next(i))
        {
            if (max_improvements > 0 && n_improvements > max_improvements)
                break;

            n_improvements++;

            // Try to flip branch
            const Edge& edge = getEdge(edges.key(i));

            if (_molecule != 0 && _molecule->cis_trans.getParity(_molecule_edge_mapping[_layout_edges[edges.key(i)].ext_idx]) != 0)
                continue;

            if (getVertex(edge.beg).degree() == 1 || getVertex(edge.end).degree() == 1)
                continue;

            Filter filter;

            _makeBranches(branch, edges.key(i), filter);
            new_state.flipBranch(filter, beg_state, edge.beg, edge.end);
            new_state.calcEnergy();

            if (new_state.energy < best_state.energy - 0.00001)
            {
                improved = true;
                best_state.copy(new_state);
            }
        }

        if (improved)
        { // finished becouse of flipped
            beg_state.copy(best_state);
            continue;
        }

        // Rotations
        // Look through found edges
        for (i = edges.begin(); i < edges.end(); i = edges.next(i))
        {
            if (max_improvements > 0 && n_improvements > max_improvements)
                break;

            n_improvements += 3;

            // Try to rotate one branch by 10 degrees in both directions around both vertices
            const Edge& edge = getEdge(edges.key(i));

            if (_molecule != 0 && _molecule->cis_trans.getParity(_molecule_edge_mapping[_layout_edges[edges.key(i)].ext_idx]) != 0)
                continue;

            Filter filter;

            _makeBranches(branch, edges.key(i), filter);

            bool around_beg = _allowRotateAroundVertex(edge.beg);
            bool around_end = _allowRotateAroundVertex(edge.end);

            if (around_beg)
            {
                new_state.rotateBranch(filter, beg_state, edge.beg, 10);
                new_state.calcDistance(v1c, v2c);
                new_state.calcEnergy();

                if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001)
                {
                    improved = true;
                    best_state.copy(new_state);
                }

                new_state.rotateBranch(filter, beg_state, edge.beg, -10);
                new_state.calcDistance(v1c, v2c);
                new_state.calcEnergy();

                if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001)
                {
                    improved = true;
                    best_state.copy(new_state);
                }
            }

            if (around_end)
            {
                new_state.rotateBranch(filter, beg_state, edge.end, 10);
                new_state.calcDistance(v1c, v2c);
                new_state.calcEnergy();

                if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001)
                {
                    improved = true;
                    best_state.copy(new_state);
                }

                new_state.rotateBranch(filter, beg_state, edge.end, -10);
                new_state.calcDistance(v1c, v2c);
                new_state.calcEnergy();

                if (new_state.dist > beg_state.dist && new_state.energy < best_state.energy - 0.00001)
                {
                    improved = true;
                    best_state.copy(new_state);
                }
            }

            // Stretching
            // Try to stretch each edge with 1.6 ratio
            if ((n = filter.count(*this)) != 1 && n != vertexCount() - 1)
            {
                new_state.stretchBranch(filter, beg_state, edge.beg, edge.end, 6);
                new_state.calcDistance(v1c, v2c);
                new_state.calcEnergy();

                if (new_state.dist > beg_state.dist && new_state.energy + 20 < beg_state.energy && new_state.energy < best_state.energy - 0.00001)
                {
                    improved = true;
                    best_state.copy(new_state);
                }
            }
        }

        if (improved)
            beg_state.copy(best_state);
    }

    if (_n_fixed == 0)
    {
        if (!beg_state.is_small_cycle())
        {
            int center = -1;
            long max_code = 0;

            for (i = vertexBegin(); i < vertexEnd(); i = vertexNext(i))
                if (getLayoutVertex(i).morgan_code > max_code)
                {
                    center = i;
                    max_code = getLayoutVertex(i).morgan_code;
                }

            beg_state.calcHeight();

            if (layout_orientation != UNCPECIFIED)
            {
                new_state.rotateLayout(beg_state, center, _2FLOAT(beg_state.calc_best_angle() / M_PI * 180.));
                beg_state.copy(new_state);

                if (layout_orientation == VERTICAL)
                {
                    new_state.rotateLayout(beg_state, center, 90);
                    beg_state.copy(new_state);
                }
            }
            else
            {

                for (float angle = -90.f; angle < 90.f + EPSILON; angle += 30.f)
                {
                    new_state.rotateLayout(beg_state, center, angle);
                    new_state.calcHeight();

                    if (new_state.height < beg_state.height - EPSILON)
                        beg_state.copy(new_state);
                }
            }
        }
    }

    beg_state.applyToGraph();

    _excludeDandlingIntersections();
}