// Increase minimal distance between vertices
void MoleculeLayoutGraph::_refineCoordinates (const BiconnectedDecomposer &bc_decomposer, const ObjArray<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;

      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)
      {
         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)
   {
      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();

      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();
}
void MoleculeLayoutGraph::_findFixedComponents (BiconnectedDecomposer &bc_decom, Array<int> &fixed_components, ObjArray<MoleculeLayoutGraph> & bc_components )
{
   // 1. Find biconnected components forming connected subgraph from fixed vertices
   if (_n_fixed == 0)
      return;

   int n_comp = bc_decom.componentsCount();
   QS_DEF(Array<int>, fixed_count);

   fixed_count.clear_resize(n_comp);
   fixed_count.zerofill();

   // calculate number of fixed vertices in each component
   for (int i = 0; i < n_comp; i++)
   {
      Filter filter;

      bc_decom.getComponent(i, filter);

      for (int j = vertexBegin(); j < vertexEnd(); j = vertexNext(j))
         if (filter.valid(j) && _fixed_vertices[j])
            fixed_count[i]++;
   }

   // keep only with fixed number greater than a half
   for (int i = 0; i < n_comp; i++)
   {
      Filter filter;

      bc_decom.getComponent(i, filter);

      if (fixed_count[i] > filter.count(*this) / 2)
         fixed_components[i] = 1;
   }

   _fixed_vertices.zerofill();

   // update fixed vertices
   for (int i = 0; i < n_comp; i++)
   {
      if (!fixed_components[i])
         continue;

      MoleculeLayoutGraph &component = bc_components[i];

      for (int j = component.vertexBegin(); j < component.vertexEnd(); j = component.vertexNext(j))
         _fixed_vertices[component.getVertexExtIdx(j)] = 1;
   }

   Filter fixed_filter(_fixed_vertices.ptr(), Filter::EQ, 1);

   Graph fixed_graph;
   QS_DEF(Array<int>, fixed_mapping);

   fixed_graph.makeSubgraph(*this, fixed_filter, &fixed_mapping, 0);

   if (Graph::isConnected(fixed_graph))
      _n_fixed = fixed_filter.count(*this);
   else
   { 
      // fixed subgraph is not connected - choose its greatest component
      int n = fixed_graph.countComponents();
      const Array<int> &decomposition = fixed_graph.getDecomposition();

      fixed_count.clear_resize(n);
      fixed_count.zerofill();

      for (int i = fixed_graph.vertexBegin(); i < fixed_graph.vertexEnd(); i = fixed_graph.vertexNext(i))
         fixed_count[decomposition[i]]++;

      int j = 0;
      for (int i = 1; i < n; i++)
         if (fixed_count[i] > fixed_count[j])
            j = i;

      Filter max_filter(decomposition.ptr(), Filter::EQ, j);

      // update fixed vertices
      _fixed_vertices.zerofill();
      _n_fixed = 0;

      for (int i = fixed_graph.vertexBegin(); i < fixed_graph.vertexEnd(); i = fixed_graph.vertexNext(i))
      {
         if (max_filter.valid(i))
         {
            _fixed_vertices[fixed_mapping[i]] = 1;
            _n_fixed++;
         }
      }

      for (int i = 0; i < n_comp; i++)
      {
         if (!fixed_components[i])
            continue;

         MoleculeLayoutGraph &component = bc_components[i];

         if (!max_filter.valid(component.getVertexExtIdx(component.vertexBegin())))
            fixed_components[i] = 0;
      }
   }
}