void MoleculeLayoutGraphSmart::_assignEveryCycle()

in core/indigo-core/layout/src/molecule_layout_graph_assign_smart.cpp [289:1017]


void MoleculeLayoutGraphSmart::_assignEveryCycle(const Cycle& cycle)
{
    profTimerStart(t, "_assignFirstCycle");
    const int size = cycle.vertexCount();
    _first_vertex_idx = cycle.getVertex(0);

    MoleculeLayoutMacrocyclesLattice layout(size);

    if (size <= 6)
        for (int i = 0; i < size; i++)
            _molecule->cis_trans.setParity(_layout_edges[cycle.getEdge(i)].orig_idx, 0);

    for (int i = 0; i < size; i++)
    {

        // edge parallels

        // !!
        int order_next = 0;
        int edge_number = cycle.getEdge(i);
        LayoutEdge edge = _layout_edges[edge_number];
        int ext_edge_number = edge.orig_idx;
        int order = _molecule->getBondOrder(ext_edge_number);
        switch (order)
        {
        case BOND_SINGLE:
            order_next = 1;
            break;
        case BOND_DOUBLE:
            order_next = 2;
            break;
        case BOND_TRIPLE:
            order_next = 3;
            break;
        default:
            order_next = 1;
        }
        int order_prev;
        int ext_edge_number_prev = _layout_edges[cycle.getEdgeC(i - 1)].orig_idx;
        switch (_molecule->getBondOrder(ext_edge_number_prev))
        {
        case BOND_SINGLE:
            order_prev = 1;
            break;
        case BOND_DOUBLE:
            order_prev = 2;
            break;
        case BOND_TRIPLE:
            order_prev = 3;
            break;
        default:
            order_prev = 1;
        }

        layout.setVertexEdgeParallel(i, order_next + order_prev >= 4);

        // tras-cis configuration
        int next_vertex = _layout_vertices[cycle.getEdgeFinish(i + 1)].orig_idx;
        int prev_vertex = _layout_vertices[cycle.getEdgeStart(i - 1)].orig_idx;

        if (_molecule->cis_trans.getParity(ext_edge_number))
        {
            int _sameside = _molecule->cis_trans.sameside(ext_edge_number, prev_vertex, next_vertex);
            if (_sameside)
                layout.setEdgeStereo(i, MoleculeCisTrans::CIS);
            else
                layout.setEdgeStereo(i, MoleculeCisTrans::TRANS);
        }
        else
        {
            //         if (_layout_vertices[cycle.getVertex(i)].type != ELEMENT_NOT_DRAWN &&
            //          _layout_vertices[cycle.getVertex((i + 1) % size)].type != ELEMENT_NOT_DRAWN) {
            if (_layout_edges[cycle.getEdge(i)].type != ELEMENT_NOT_DRAWN)
            {

                Vec2f prev_point;
                if (_layout_edges[cycle.getEdgeC(i - 1)].type != ELEMENT_NOT_DRAWN)
                    prev_point = _layout_vertices[cycle.getVertexC(i - 1)].pos;
                else
                {
                    for (int j = getVertex(cycle.getVertex(i)).neiBegin(); j != getVertex(cycle.getVertex(i)).neiEnd();
                         j = getVertex(cycle.getVertex(i)).neiNext(j))
                        if (_layout_edges[getVertex(cycle.getVertex(i)).neiEdge(j)].type != ELEMENT_NOT_DRAWN &&
                            getVertex(cycle.getVertex(i)).neiVertex(j) != cycle.getVertexC(i + 1))
                            prev_point = _layout_vertices[getVertex(cycle.getVertex(i)).neiVertex(j)].pos;
                }

                Vec2f next_point;
                if (_layout_edges[cycle.getEdgeC(i + 1)].type != ELEMENT_NOT_DRAWN)
                    next_point = _layout_vertices[cycle.getVertexC(i + 2)].pos;
                else
                {
                    for (int j = getVertex(cycle.getVertexC(i + 1)).neiBegin(); j != getVertex(cycle.getVertexC(i + 1)).neiEnd();
                         j = getVertex(cycle.getVertexC(i + 1)).neiNext(j))
                        if (_layout_edges[getVertex(cycle.getVertexC(i + 1)).neiEdge(j)].type != ELEMENT_NOT_DRAWN &&
                            getVertex(cycle.getVertexC(i + 1)).neiVertex(j) != cycle.getVertex(i))
                            next_point = _layout_vertices[getVertex(cycle.getVertexC(i + 1)).neiVertex(j)].pos;
                }

                int _sameside =
                    _isCisConfiguratuin(prev_point, _layout_vertices[cycle.getVertexC(i)].pos, _layout_vertices[cycle.getVertexC(i + 1)].pos, next_point);

                if (_layout_edges[cycle.getEdgeC(i - 1)].type != ELEMENT_NOT_DRAWN && _layout_edges[cycle.getEdgeC(i + 1)].type != ELEMENT_NOT_DRAWN)
                {
                    if (_sameside)
                        layout.setEdgeStereo(i, MoleculeCisTrans::CIS);
                    else
                        layout.setEdgeStereo(i, MoleculeCisTrans::TRANS);
                }
                else
                {
                    if ((_layout_edges[cycle.getEdgeC(i - 1)].type != ELEMENT_NOT_DRAWN) ^ (_layout_edges[cycle.getEdgeC(i + 1)].type != ELEMENT_NOT_DRAWN))
                    {
                        if (_sameside)
                            layout.setEdgeStereo(i, MoleculeCisTrans::TRANS);
                        else
                            layout.setEdgeStereo(i, MoleculeCisTrans::CIS);
                    }
                    else
                        layout.setEdgeStereo(i, MoleculeCisTrans::CIS);
                }
            }
        }

        layout.setVertexDrawn(i, _layout_vertices[cycle.getVertex(i)].type != ELEMENT_NOT_DRAWN);

        // trees sizes
    }

    /*
     * A patrial fix for Ketcher issues 486 and 487
     *
     * The _segment_smoothing_prepearing() contains an error which leads to
     * a crash when cycles with double bonds are being processed. Currently,
     * we skip smoothing altogether. This seems not to create any regressions
     */
    QS_DEF(ObjArray<MoleculeLayoutSmoothingSegment>, segment);
    QS_DEF(Array<Vec2f>, rotation_point);
    QS_DEF(Array<int>, rotation_vertex);

    segment.clear();
    rotation_point.zerofill();
    rotation_vertex.zerofill();

    //   _segment_smoothing_prepearing(cycle, rotation_vertex, rotation_point, segment, layout);

    int segment_count = segment.size();

    for (int i = 0; i < segment_count; i++)
    {
        for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v))
        {
            if (segment[i].is_start(v))
                if (segment[i]._graph.getVertex(v).degree() > 2)
                    layout.setEdgeStereo(rotation_vertex[i], 0);
            if (segment[i].is_finish(v))
                if (segment[i]._graph.getVertex(v).degree() > 2)
                    layout.setEdgeStereo((rotation_vertex[(i + 1) % segment_count] - 1 + size) % size, 0);
        }
    }
    /*bool easy_case = size <= 9;
    if (easy_case) {
    QS_DEF(Array<int>, last);
    last.clear_resize(_layout_component_count);
    last.fill(-1);
    for (int i = 0; i < size; i++) {
    int comp = _layout_component_number[cycle.getEdge(i)];
    if (comp >= 0) {
    if (last[comp] >= 0) easy_case = false;
    last[comp] = i;
    }
    }

    QS_DEF(Array<int>, order);
    order.clear_resize(size);
    for (int i = 0; i < size; i++) {
    order[i] = _molecule->getBondOrder(getEdgeOrigIdx(cycle.getEdge(i)));
    if (order[i] > 3) order[i] = 1;
    }
    order.push(order[0]);
    for (int i = 0; i < size; i++) easy_case &= (order[i] + order[i + 1] < 4);

    for (int i = 0; i < size; i++) {
    int next_vertex = _layout_vertices[cycle.getEdgeFinish(i + 1)].orig_idx;
    int prev_vertex = _layout_vertices[cycle.getEdgeStart(i - 1)].orig_idx;

    if (_molecule->cis_trans.getParity(getEdgeOrigIdx(cycle.getEdge(i)))) {
    easy_case &= _molecule->cis_trans.sameside(getEdgeOrigIdx(cycle.getEdge(i)), prev_vertex, next_vertex);
    }
    }
    if (easy_case) {
    for (int i = 0; i < size; i++) {
    layout.getPos(cycle.getVertex(i)) = Vec2f(1, 0);
    layout.getPos(cycle.getVertex(i)).rotate(2 * M_PI / size * i);
    }

    for (int i = 0; i < size; i++)
    if (getVertexType(cycle.getVertex(i)) == ELEMENT_NOT_DRAWN)
    getPos(cycle.getVertex(i)) = layout.getPos(i);

    for (int i = 0; i < size; i++)
    {
    setVertexType(cycle.getVertex(i), ELEMENT_DRAWN);
    setEdgeType(cycle.getEdge(i), ELEMENT_DRAWN);
    }


    }
    */
    // printf("%d do layout cycle \n", size);

    // calculate target angle

    for (int s = 0; s < segment_count; s++)
    {
        for (int i = rotation_vertex[s]; i != rotation_vertex[(s + 1) % segment_count]; i = (i + 1) % size)
        {
            int prev_layout_component = _layout_component_number[cycle.getEdgeC(i - 1)];
            int next_layout_component = _layout_component_number[cycle.getEdge(i)];

            if (prev_layout_component < 0 && next_layout_component < 0)
            {
                layout.setTargetAngle(i, _2FLOAT(2. * M_PI / 3.));
                layout.setAngleImportance(i, 0.2f);
            }
            else if ((prev_layout_component < 0) ^ (next_layout_component < 0))
            {
                const MoleculeLayoutSmoothingSegment& calc_segment = prev_layout_component < 0 ? segment[s] : segment[(s + segment_count - 1) % segment_count];
                int calc_vertex = prev_layout_component < 0 ? calc_segment.get_start() : calc_segment.get_finish();

                Cycle border;
                calc_segment._graph._getBorder(border);
                int calc_vertex_in_border = -1;
                for (int j = 0; j < border.vertexCount(); j++)
                {
                    if (border.getVertex(j) == calc_vertex)
                    {
                        calc_vertex_in_border = j;
                        break;
                    }
                }

                float angle = 0;
                int prev_vertex = -1;
                int next_vertex = -1;
                if (border.vertexCount() != 0 && calc_vertex_in_border >= 0)
                {
                    prev_vertex = border.getVertexC(calc_vertex_in_border - 1);
                    next_vertex = border.getVertexC(calc_vertex_in_border + 1);
                }
                else
                {
                    for (int n : calc_segment._graph.getVertex(calc_vertex).neighbors())
                    {
                        int v = calc_segment._graph.getVertex(calc_vertex).neiVertex(n);
                        if (prev_vertex < 0 || calc_segment.getIntPosition(v).y > calc_segment.getIntPosition(prev_vertex).y)
                            prev_vertex = v;
                        if (next_vertex < 0 || calc_segment.getIntPosition(v).y < calc_segment.getIntPosition(next_vertex).y)
                            next_vertex = v;
                    }
                    if (next_layout_component < 0)
                    {
                        int temp = prev_vertex;
                        prev_vertex = next_vertex;
                        next_vertex = temp;
                    }
                }
                angle = (calc_segment.getIntPosition(next_vertex) - calc_segment.getIntPosition(calc_vertex)).tiltAngle2();
                angle -= (calc_segment.getIntPosition(prev_vertex) - calc_segment.getIntPosition(calc_vertex)).tiltAngle2();

                while (angle < 0.f)
                    angle += _2FLOAT(2. * M_PI);
                while (angle >= _2FLOAT(2. * M_PI))
                    angle -= _2FLOAT(2 * M_PI);

                layout.setTargetAngle(i, _2FLOAT(M_PI - angle / 2.));
            }
            else if (prev_layout_component == next_layout_component)
            {
                float angle = (getPos(cycle.getVertexC(i - 1)) - getPos(cycle.getVertexC(i))).tiltAngle2();
                angle -= (getPos(cycle.getVertexC(i + 1)) - getPos(cycle.getVertexC(i))).tiltAngle2();

                while (angle < 0.f)
                    angle += _2FLOAT(2. * M_PI);
                while (angle >= _2FLOAT(2. * M_PI))
                    angle -= _2FLOAT(2. * M_PI);
                if (angle > _2FLOAT(M_PI))
                    angle = _2FLOAT(2. * M_PI - angle);

                layout.setTargetAngle(i, angle);
            }
            // temporary value
            else
            {
                layout.setTargetAngle(i, _2FLOAT(M_PI));
                layout.setAngleImportance(i, 0.2f);
            }
        }
    }

    QS_DEF(Array<int>, _is_vertex_taken);
    enum
    {
        NOT_CONSIDERED,
        IN_LIST,
        NOT_IN_LIST
    };
    QS_DEF(Array<int>, _list_of_vertex);
    QS_DEF(Array<int>, _segment_weight_outside);

    _segment_weight_outside.clear_resize(segment_count);
    _segment_weight_outside.zerofill();

    for (int i = 0; i < size; i++)
        if (_layout_component_number[cycle.getEdge(i)] < 0)
            _layout_component_number[cycle.getEdge(i)] = _layout_component_count;

    _layout_component_count++;

    QS_DEF(Array<bool>, _is_layout_component_incoming);
    _is_layout_component_incoming.clear_resize(_layout_component_count);
    _is_layout_component_incoming.zerofill();
    for (int i = 0; i < size; i++)
        _is_layout_component_incoming[_layout_component_number[cycle.getEdge(i)]] = true;

    for (int i = 0; i < segment_count; i++)
    {
        for (int up = 0; up <= 1; up++)
        {
            _is_vertex_taken.clear_resize(_graph->vertexEnd());
            _is_vertex_taken.fill(NOT_CONSIDERED);

            if (i == segment_count - 1)
            {
                // int x = 5;
            }

            _list_of_vertex.clear_resize(0);

            bool is_segment_trivial =
                segment[i].get_layout_component_number() == -1 && segment[(i + segment_count - 1) % segment_count].get_layout_component_number() == -1 && up;

            for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v))
            {
                if ((!segment[i].is_finish(v) && !segment[i].is_start(v) && segment[i].isVertexUp(v) ^ !up) || (is_segment_trivial && !segment[i].is_finish(v)))
                {

                    int ext_v = segment[i]._graph.getVertexExtIdx(v);
                    _is_vertex_taken[getVertexExtIdx(ext_v)] = IN_LIST;
                    _list_of_vertex.push(ext_v);
                }
            }

            bool touch_to_another_segment = false;

            for (int j = 0; j < _list_of_vertex.size(); j++)
            {
                const Vertex& vert = getVertex(_list_of_vertex[j]);
                for (int n = vert.neiBegin(); n != vert.neiEnd(); n = vert.neiNext(n))
                {
                    int vn = vert.neiVertex(n);

                    if (_is_vertex_taken[getVertexExtIdx(vn)] != NOT_CONSIDERED)
                        continue;

                    bool is_this_comp = false;
                    for (int n2 = getVertex(vn).neiBegin(); n2 != getVertex(vn).neiEnd(); n2 = getVertex(vn).neiNext(n2))
                        if (_layout_component_number[getVertex(vn).neiEdge(n2)] >= 0)
                        {
                            if (_is_layout_component_incoming[_layout_component_number[getVertex(vn).neiEdge(n2)]])
                                is_this_comp = true;
                            if (!is_segment_trivial && _layout_component_number[getVertex(vn).neiEdge(n2)] != segment[i].get_layout_component_number() &&
                                _layout_component_number[getVertex(vn).neiEdge(n2)] != _layout_component_count - 1)
                                touch_to_another_segment = true;
                        }

                    if (!is_this_comp)
                    {
                        _list_of_vertex.push(vn);
                        _is_vertex_taken[getVertexExtIdx(vn)] = IN_LIST;
                    }
                    else
                        _is_vertex_taken[getVertexExtIdx(vn)] = NOT_IN_LIST;
                }
            }

            for (int j = 0; j < _list_of_vertex.size(); j++)
                _list_of_vertex[j] = getVertexExtIdx(_list_of_vertex[j]);

            for (int j = 0; j < _list_of_vertex.size(); j++)
            {
                const Vertex& vert = _graph->getVertex(_list_of_vertex[j]);
                for (int n = vert.neiBegin(); n != vert.neiEnd(); n = vert.neiNext(n))
                {
                    int vn = vert.neiVertex(n);

                    if (_is_vertex_taken[vn] != NOT_CONSIDERED)
                        continue;

                    _list_of_vertex.push(vn);
                    _is_vertex_taken[vn] = IN_LIST;
                }
            }

            _segment_weight_outside[i] += (up ? 1 : -1) * (touch_to_another_segment ? 3 : 1) * _list_of_vertex.size();
        }
    }

    QS_DEF(Array<int>, _index_in_cycle);
    _index_in_cycle.clear_resize(vertexEnd());
    _index_in_cycle.fffill();
    for (int i = 0; i < size; i++)
        _index_in_cycle[cycle.getVertex(i)] = i;

    for (int i = 0; i < segment_count; i++)
    {
        if (segment[i].get_layout_component_number() < 0 && segment[(i + segment_count - 1) % segment_count].get_layout_component_number() < 0)
            layout.addVertexOutsideWeight(rotation_vertex[i], _segment_weight_outside[i] - 1);
        else
        {
            layout.setComponentFinish(rotation_vertex[i], rotation_vertex[(i + 1) % segment_count]);
            layout.setVertexAddedSquare(rotation_vertex[i], segment[i].get_square());

            Cycle border;
            if (segment[i].get_layout_component_number() >= 0)
                segment[i]._graph._getBorder(border);

            int count_neibourhoods_outside = 0;

            if (segment[i].get_layout_component_number() >= 0 && border.vertexCount() != 0)
            {
                int start_in_border = -1;
                int finish_in_border = -1;
                for (int j = 0; j < border.vertexCount(); j++)
                {
                    if (border.getVertex(j) == segment[i].get_start())
                        start_in_border = j;
                    if (border.getVertex(j) == segment[i].get_finish())
                        finish_in_border = j;
                }

                if (start_in_border >= 0 && finish_in_border >= 0)
                {
                    for (int j = (start_in_border + 1) % border.vertexCount(); j != finish_in_border; j = (j + 1) % border.vertexCount())
                    {
                        if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(border.getVertex(j))] == (rotation_vertex[i] + 1) % size)
                            count_neibourhoods_outside++;
                        if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(border.getVertex(j))] ==
                            (rotation_vertex[(i + 1) % segment_count] - 1 + size) % size)
                            count_neibourhoods_outside++;
                    }

                    for (int j = (finish_in_border + 1) % border.vertexCount(); j != start_in_border; j = (j + 1) % border.vertexCount())
                    {
                        if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(border.getVertex(j))] == (rotation_vertex[i] + 1) % size)
                            count_neibourhoods_outside--;
                        if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(border.getVertex(j))] ==
                            (rotation_vertex[(i + 1) % segment_count] - 1 + size) % size)
                            count_neibourhoods_outside--;
                    }
                }
            }
            bool right_orientation;

            if (count_neibourhoods_outside > 0)
                right_orientation = true;
            else if (count_neibourhoods_outside < 0)
                right_orientation = false;
            else
            {
                float y1 = 0, y2 = 0;
                for (int v = segment[i]._graph.vertexBegin(); v != segment[i]._graph.vertexEnd(); v = segment[i]._graph.vertexNext(v))
                {
                    if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(v)] == (rotation_vertex[i] + 1) % size)
                    {
                        y1 = segment[i].getIntPosition(v).y;
                    }
                    if (_index_in_cycle[segment[i]._graph.getVertexExtIdx(v)] == (rotation_vertex[(i + 1) % segment_count] + size - 1) % size)
                    {
                        y2 = segment[i].getIntPosition(v).y;
                    }
                }

                if ((y1 + y2) / 2 > EPSILON || ((fabs((y1 + y2) / 2) <= EPSILON) && (y1 + y2) / 2 > segment[i].getIntCenter().y))
                {
                    right_orientation = true;
                }
                else
                {
                    right_orientation = false;
                }
            }
            if (right_orientation)
            {
                layout.addVertexOutsideWeight(rotation_vertex[i], -_segment_weight_outside[i]);
                layout.addVertexOutsideWeight(rotation_vertex[(i + 1) % segment_count], -_segment_weight_outside[i]);
            }
            else
            {
                layout.addVertexOutsideWeight(rotation_vertex[i], _segment_weight_outside[i]);
                layout.addVertexOutsideWeight(rotation_vertex[(i + 1) % segment_count], _segment_weight_outside[i]);
            }
        }
    }

    layout.doLayout();

    // now we must to smooth just made layout
    // lets check if all cycle is layouted ealier in single biconnected compenent

    /*int start = -1;
    bool undrawn = false;
    for (int i = 0; i < size; i++) undrawn |= _layout_vertices[cycle.getVertex(i)].type == ELEMENT_NOT_DRAWN;
    if (undrawn) {
    for (int i = size - 1; i >= 0; i--) if (_layout_vertices[cycle.getVertex(i)].type != ELEMENT_NOT_DRAWN) start = i;
    if (start == 0 && _layout_vertices[cycle.getVertex(size - 1)].type != ELEMENT_NOT_DRAWN) {
    while (_layout_vertices[cycle.getVertex(start)].type != ELEMENT_NOT_DRAWN) start = (start + 1) % size;
    while (_layout_vertices[cycle.getVertex(start)].type == ELEMENT_NOT_DRAWN) start = (start + 1) % size;
    }
    }*/

    QS_DEF(Array<bool>, need_to_insert);
    need_to_insert.clear_resize(size);
    need_to_insert.zerofill();

    for (int i = 0; i < size; i++)
        need_to_insert[i] = _layout_vertices[cycle.getVertex(i)].type != ELEMENT_NOT_DRAWN;

    // int start = 0;

    bool componentIsWholeCycle = false;

    QS_DEF(Array<bool>, _is_component_touch);
    _is_component_touch.clear_resize(_layout_component_count);

    for (int index = 0; index < size; index++)
        if (need_to_insert[index])
        {
            // 1. search of connected component
            QS_DEF(Array<int>, insideVertex);
            insideVertex.clear_resize(0);
            insideVertex.push(cycle.getVertex(index));

            QS_DEF(Array<bool>, takenVertex);
            takenVertex.clear_resize(vertexCount());
            takenVertex.zerofill();
            takenVertex[cycle.getVertex(index)] = true;

            _is_component_touch.zerofill();

            for (int i = 0; i < insideVertex.size(); i++)
                for (int j = getVertex(insideVertex[i]).neiBegin(); j != getVertex(insideVertex[i]).neiEnd(); j = getVertex(insideVertex[i]).neiNext(j))
                {
                    int vertj = getVertex(insideVertex[i]).neiVertex(j);
                    if (_layout_edges[getVertex(insideVertex[i]).neiEdge(j)].type != ELEMENT_NOT_DRAWN && !takenVertex[vertj])
                    {
                        _is_component_touch[_layout_component_number[getVertex(insideVertex[i]).neiEdge(j)]] = true;
                        insideVertex.push(vertj);
                        takenVertex[vertj] = true;
                    }
                }

            if (!componentIsWholeCycle)
            {
                componentIsWholeCycle = true;
                for (int i = 0; i < size; i++)
                    componentIsWholeCycle &= takenVertex[cycle.getVertex(i)];
            }

            for (int i = 0; i < size; i++)
                if (takenVertex[cycle.getVertex(i)])
                    need_to_insert[i] = false;

            if (componentIsWholeCycle)
                break;

            int startIndex = index;
            int endIndex = index;

            while (takenVertex[cycle.getVertex(startIndex)])
                startIndex = (startIndex - 1 + size) % size;
            startIndex = (startIndex + 1) % size;
            while (takenVertex[cycle.getVertex(endIndex)])
                endIndex = (endIndex + 1) % size;

            // 2. flip
            bool need_to_flip = false;
            float rotate1 = Vec2f::cross(layout.getPos((startIndex + 1) % size) - layout.getPos(startIndex),
                                         layout.getPos((startIndex + 2) % size) - layout.getPos((startIndex + 1) % size));
            if (isEdgeDrawn(cycle.getEdgeC(startIndex + 1)))
            {
                float rotate2 = Vec2f::cross(getPos(cycle.getVertexC(startIndex + 1)) - getPos(cycle.getVertexC(startIndex)),
                                             getPos(cycle.getVertexC(startIndex + 2)) - getPos(cycle.getVertexC(startIndex + 1)));

                if (isEdgeDrawn(cycle.getEdgeC(startIndex)))
                    need_to_flip = rotate1 * rotate2 < 0;
            }
            else
            {
                float rotate1_next = rotate1;
                float rotate1_prev = Vec2f::cross(layout.getPos(startIndex) - layout.getPos((startIndex - 1 + size) % size),
                                                  layout.getPos((startIndex + 1) % size) - layout.getPos(startIndex));
                int do_flip_cnt = 0;
                int dont_flip_cnt = 0;

                int ind0 = cycle.getVertexC(startIndex);
                int ind1 = cycle.getVertexC(startIndex + 1);
                const Vertex& v0 = getVertex(ind0);
                const Vertex& v1 = getVertex(ind1);

                for (int j = v0.neiBegin(); j != v0.neiEnd(); j = v0.neiNext(j))
                    if (v0.neiVertex(j) != ind1 && isEdgeDrawn(v0.neiEdge(j)))
                    {
                        float current_rotate = Vec2f::cross(getPos(ind0) - getPos(v0.neiVertex(j)), getPos(ind1) - getPos(ind0));
                        if (current_rotate * rotate1_prev > 0)
                            do_flip_cnt++;
                        else
                            dont_flip_cnt++;
                    }

                for (int j = v1.neiBegin(); j != v1.neiEnd(); j = v1.neiNext(j))
                    if (v1.neiVertex(j) != ind0 && isEdgeDrawn(v1.neiEdge(j)))
                    {
                        float current_rotate = Vec2f::cross(getPos(ind1) - getPos(ind0), getPos(v1.neiVertex(j)) - getPos(ind1));
                        if (current_rotate * rotate1_next > 0)
                            do_flip_cnt++;
                        else
                            dont_flip_cnt++;
                    }

                need_to_flip = do_flip_cnt > dont_flip_cnt;
            }

            /*float rotate1 = Vec2f::cross(layout.getPos((startIndex + 1) % size) - layout.getPos(startIndex), layout.getPos((startIndex + 2) % size) -
            layout.getPos((startIndex + 1) % size)); Vec2f next_point; if (isEdgeDrawn(cycle.getEdgeC(startIndex + 1))) next_point =
            getPos(cycle.getVertexC(startIndex + 2)); else { for (int j = getVertex(cycle.getVertexC(startIndex + 1)).neiBegin(); j !=
            getVertex(cycle.getVertexC(startIndex + 1)).neiEnd(); j = getVertex(cycle.getVertexC(startIndex + 1)).neiNext(j)) if
            (isEdgeDrawn(getVertex(cycle.getVertexC(startIndex + 1)).neiEdge(j)) && getVertex(cycle.getVertexC(startIndex + 1)).neiVertex(j) !=
            cycle.getVertex(startIndex)) next_point = _layout_vertices[getVertex(cycle.getVertexC(startIndex + 1)).neiVertex(j)].pos;
            }

            float rotate2 = Vec2f::cross(getPos(cycle.getVertexC(startIndex + 1)) - getPos(cycle.getVertexC(startIndex)),
                next_point - getPos(cycle.getVertexC(startIndex + 1)));

            if (!isEdgeDrawn(cycle.getEdgeC(startIndex + 1))) {
                need_to_flip = rotate1 * rotate2 > 0;
            }
            else if (isEdgeDrawn(cycle.getEdgeC(startIndex))) need_to_flip = rotate1 * rotate2 < 0;
            */

            if (need_to_flip)
            {
                for (int i = 0; i < insideVertex.size(); i++)
                    getPos(insideVertex[i]).x *= -1;

                for (int i = 0; i < segment.size(); i++)
                    if (segment[i].get_layout_component_number() >= 0 && _is_component_touch[segment[i].get_layout_component_number()])
                        segment[i].inverse();
            }

            // 3. shift

            Vec2f middle_host;
            Vec2f middle_new;
            int countVertex = 0;
            for (int i = startIndex; i != endIndex; i = (i + 1) % size)
            {
                middle_host += _layout_vertices[cycle.getVertex(i)].pos;
                middle_new += layout.getPos(i);
                countVertex++;
            }
            middle_host /= _2FLOAT(countVertex);
            middle_new /= _2FLOAT(countVertex);

            for (int i = 0; i < insideVertex.size(); i++)
                _layout_vertices[insideVertex[i]].pos += middle_new - middle_host;

            // 4. rotate

            Vec2f direction_host;
            Vec2f direction_new;
            if (countVertex > 1)
            {
                int currentIndex = 0;
                for (int i = startIndex; i != endIndex; i = (i + 1) % size)
                {
                    if (2 * currentIndex < countVertex - 1)
                    {
                        direction_host += _layout_vertices[cycle.getVertex(i)].pos;
                        direction_new += layout.getPos(i);
                    }
                    else if (2 * currentIndex > countVertex - 1)
                    {
                        direction_host -= _layout_vertices[cycle.getVertex(i)].pos;
                        direction_new -= layout.getPos(i);
                    }
                    currentIndex++;
                }

                float dot = Vec2f::dot(direction_host, direction_new) / (direction_host.length() * direction_new.length());
                if (dot > 1)
                    dot = 1;
                if (dot < -1)
                    dot = -1;
                float angle = acos(dot);
                if (Vec2f::cross(direction_host, direction_new) < 0)
                    angle = -angle;
                for (int i = 0; i < insideVertex.size(); i++)
                    _layout_vertices[insideVertex[i]].pos.rotateAroundSegmentEnd(_layout_vertices[insideVertex[i]].pos, middle_new, angle);
            }
        }

    for (int i = 0; i < size; i++)
        if (getVertexType(cycle.getVertex(i)) == ELEMENT_NOT_DRAWN)
            getPos(cycle.getVertex(i)) = layout.getPos(i);

    for (int i = 0; i < size; i++)
    {
        setVertexType(cycle.getVertex(i), ELEMENT_DRAWN);
        setEdgeType(cycle.getEdge(i), ELEMENT_DRAWN);
    }

    // 5. smoothing
    for (int e = edgeBegin(); e != edgeEnd(); e = edgeNext(e))
        if (_layout_component_number[e] >= 0 && _is_layout_component_incoming[_layout_component_number[e]])
            _layout_component_number[e] = _layout_component_count - 1;

    _segment_smoothing(cycle, layout, rotation_vertex, rotation_point, segment);
}