Mentions légales du service

Skip to content
Snippets Groups Projects
Forked from aevol / aevol
3384 commits behind, 51 commits ahead of the upstream repository.
Individual_X11.cpp 29.90 KiB
// ****************************************************************************
//
//          Aevol - An in silico experimental evolution platform
//
// ****************************************************************************
//
// Copyright: See the AUTHORS file provided with the package or <www.aevol.fr>
// Web: http://www.aevol.fr/
// E-mail: See <http://www.aevol.fr/contact/>
// Original Authors : Guillaume Beslon, Carole Knibbe, David Parsons
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// ****************************************************************************




// =================================================================
//                              Libraries
// =================================================================
#include <stdio.h>
#include <math.h>



// =================================================================
//                            Project Files
// =================================================================
#include "Individual_X11.h"

#include "ExpManager.h"
#include "ExpSetup.h"
#include "Utils.h"

namespace aevol {

//##############################################################################
//                                                                             #
//                           Class Individual_X11                           #
//                                                                             #
//##############################################################################

// =================================================================
//                    Definition of static attributes
// =================================================================

// =================================================================
//                             Constructors
// =================================================================
Individual_X11::Individual_X11(ExpManager * exp_manager, gzFile backup_file) :
    Individual(exp_manager, backup_file)
{
  init_occupied_sectors();
}

Individual_X11::Individual_X11(const Individual_X11 &model) :
    Individual(model)
{
  init_occupied_sectors();
}

Individual_X11::Individual_X11(Individual_X11 * const parent,
                                     int32_t id,
                                     std::shared_ptr<JumpingMT> mut_prng,
                                     std::shared_ptr<JumpingMT> stoch_prng) :
    Individual(parent, id, mut_prng, stoch_prng)
{
  init_occupied_sectors();
}

Individual_X11::Individual_X11(ExpManager * exp_m, std::shared_ptr<JumpingMT> mut_prng,
    std::shared_ptr<JumpingMT> stoch_prng, std::shared_ptr<MutationParams> param_mut,
    double w_max, int32_t min_genome_length, int32_t max_genome_length, bool allow_plasmids,
    int32_t id, const char* strain_name, int32_t age) :
    Individual(exp_m,mut_prng,stoch_prng,param_mut,w_max,min_genome_length,
      max_genome_length,allow_plasmids,id,strain_name,age) {
init_occupied_sectors();
}


/*Individual_X11::Individual_X11(char* genome, int32_t genome_size) : Individual(genome, genome_size)
{
  init_occupied_sectors();
}*/

// =================================================================
//                             Destructors
// =================================================================
Individual_X11::~Individual_X11()
{
  for (int16_t layer = 0 ; layer < outmost_layer_ ; layer++)
  {
    delete [] occupied_sectors_[LEADING][layer];
    delete [] occupied_sectors_[LAGGING][layer];
  }
}

// =================================================================
//                            Public Methods
// =================================================================
void Individual_X11::display()
{
}

void Individual_X11::display_cdss(X11Window * win)
{
  // Retreive the genetic unit corresponding to the main chromosome
  GeneticUnit* gen_unit = &genetic_unit_list_.front();
  int32_t genome_length = gen_unit->dna()->length();

  // Display the number of CDSs
  char display_string[40];
  sprintf(display_string, "Main chromosome size : %" PRId32 "bp", genome_length);
  win->draw_string(15, 25, display_string);
  sprintf(display_string, "Leading : %" PRId32 " CDSs", static_cast<int32_t>(gen_unit->protein_list(LEADING).size()));
  win->draw_string(15, 40, display_string);
  sprintf(display_string, "Lagging : %" PRId32 " CDSs", static_cast<int32_t>(gen_unit->protein_list(LAGGING).size()));
  win->draw_string(15, 55, display_string);

  // Compute display diameter according to genome length and window size
  int16_t canvas_width;
  if (allow_plasmids_) canvas_width = win->width() / 2;
  else canvas_width = win->width();
  int16_t canvas_height = win->height();

  int16_t canvas_size = Utils::min(canvas_width, canvas_height);
  int16_t diam        = round(canvas_size * log((double)genome_length) / 16);
  // Prevent diameter from getting greater than 2/3 of the window size
  if (diam > 2 * canvas_size / 3)
  {
    diam = 2 * canvas_size / 3;
  }

  // Compute coordinates of the upper-left corner of the containing square
  int16_t pos_x = (canvas_width - diam) / 2;
  int16_t pos_y = (canvas_height - diam) / 2;

  // Draw main circle
  win->draw_circle(pos_x, pos_y, diam);

  // Sector occupation management
  reset_sectors();


  // ---------------
  //  Draw each CDS
  // ---------------
  // NB : As we want OriC to be at the "top" of the circle and the orientation
  //      to be clockwise, the drawing angle (theta) will be given as
  //      (90 - alpha), alpha being the "classical" trigonometric angle
  int16_t alpha_first, alpha_last; // Angles of first and last transcribed bases from OriC (degrees)
  int16_t theta_first; //, theta_last; // Transposed angles on the trigonometric circle (degrees)
  int16_t nb_sect;
  // Same as above with precision = 1/64 degree
  int16_t alpha_first_64, alpha_last_64;
  int16_t theta_first_64; //, theta_last_64;
  int16_t nb_sect_64;

  // ----------------
  //  LEADING strand
  // ----------------
  for (const auto& cds: gen_unit->protein_list(LEADING))
  {
    // Alpha : angles from OriC (in degrees)
    // Theta : angles on the trigonometric circle (in degrees)
    // nb_sect : "length" in degrees of the arc to be drawn
    alpha_first   = (int16_t) round(360 * ((double)cds.first_translated_pos() / (double)genome_length));
    alpha_last    = (int16_t) round(360 * ((double)cds.last_translated_pos()  / (double)genome_length));
    theta_first   = Utils::mod(90 - alpha_first, 360);
    // theta_last    = Utils::mod(90 - alpha_last, 360);
    nb_sect       = Utils::mod(alpha_last - alpha_first + 1,  360);

    // These are the same as above but with a higher precision (1/64 degrees)
    alpha_first_64   = (int16_t) round(64 * 360 * ((double)cds.first_translated_pos() / (double)genome_length));
    alpha_last_64    = (int16_t) round(64 * 360 * ((double)cds.last_translated_pos() / (double)genome_length));
    theta_first_64   = Utils::mod(64 * 90 - alpha_first_64, 64 * 360);
    // theta_last_64    = Utils::mod(64 * 90 - alpha_last_64, 64 * 360);
    nb_sect_64       = Utils::mod(alpha_last_64 - alpha_first_64 + 1,  64 * 360);


    // Look for the inmost layer that has all the sectors between
    // theta_first and theta_last free
    int16_t layer = 0;
    bool sectors_free = false;
    while (! sectors_free)
    {
      sectors_free = true;

      for (int16_t rho = 0 ; rho < nb_sect ; rho++)
      {
        if (occupied_sectors_[LEADING][layer][Utils::mod(theta_first-rho, 360)])
        {
          sectors_free = false;
          break;
        }
      }
      if (sectors_free)
      {
        break; // All the needed sectors are free on the current layer
      }
      else
      {
        layer++;

        if (layer >= outmost_layer_)
        {
          add_layer();
          break; // An added layer is necessarily free, no need to look further
        }
      }
    }

    // Mark sectors to be drawn as occupied
    for (int16_t rho = 0 ; rho < nb_sect ; rho++)
    {
      occupied_sectors_[LEADING][layer][Utils::mod(theta_first-rho, 360)] = true;
    }
    // Mark flanking sectors as occupied
    occupied_sectors_[LEADING][layer][Utils::mod(theta_first+1, 360)] = true;
    occupied_sectors_[LEADING][layer][Utils::mod(theta_first-nb_sect, 360)] = true;


    // Draw
    const int8_t cds_layer_spacing = 5; // TODO : param?
    layer++; // index starting at 0 but needed to start at 1

    int16_t diam2 = diam + (layer * 2 * cds_layer_spacing);
    pos_x         = (int16_t) round((double)(canvas_width  - diam2) / 2.0);
    pos_y         = (int16_t) round((double)(canvas_height - diam2) / 2.0);

    char* color = X11Window::color(cds.mean());
    win->draw_arc_64(pos_x, pos_y, diam2, theta_first_64 - nb_sect_64, nb_sect_64, color);
    delete [] color;
  }

  // ----------------
  //  LAGGING strand
  // ----------------
  for (const auto& cds: gen_unit->protein_list(LAGGING))
  {
    // Alpha : angles from OriC (in degrees)
    // Theta : angles on the trigonometric circle (in degrees)
    // nb_sect : "length" in degrees of the arc to be drawn
    alpha_first   = (int16_t) round(360 * ((double)cds.first_translated_pos() / (double)genome_length));
    alpha_last    = (int16_t) round(360 * ((double)cds.last_translated_pos()  / (double)genome_length));
    theta_first   = Utils::mod(90 - alpha_first, 360);
    // theta_last    = Utils::mod(90 - alpha_last, 360);
    nb_sect = Utils::mod(alpha_first - alpha_last + 1,  360);

    // These are the same as above but with a higher precision (1/64 degrees)
    alpha_first_64   = (int16_t) round(64 * 360 * ((double)cds.first_translated_pos() / (double)genome_length));
    alpha_last_64    = (int16_t) round(64 * 360 * ((double)cds.last_translated_pos() / (double)genome_length));
    theta_first_64   = Utils::mod(64 * 90 - alpha_first_64, 64 * 360);
    // theta_last_64    = Utils::mod(64 * 90 - alpha_last_64, 64 * 360);
    nb_sect_64 = Utils::mod(alpha_first_64 - alpha_last_64 + 1,  64 * 360);


    // Look for the inmost layer that has all the sectors between
    // theta_first and theta_last free
    int16_t layer = 0;
    bool sectors_free = false;
    while (! sectors_free)
    {
      sectors_free = true;

      for (int16_t rho = 0 ; rho < nb_sect ; rho++)
      {
        if (occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+rho, 360)])
        {
          sectors_free = false;
          break;
        }
      }

      if (sectors_free)
      {
        break; // All the needed sectors are free on the current layer
      }
      else
      {
        layer++;

        if (layer >= outmost_layer_)
        {
          add_layer();
          break; // An added layer is necessarily free, no need to look further
        }
      }
    }

    // Mark sectors to be drawn as occupied
    for (int16_t rho = 0 ; rho < nb_sect ; rho++)
    {
      occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+rho, 360)] = true;
    }
    // Mark flanking sectors as occupied
    occupied_sectors_[LAGGING][layer][Utils::mod(theta_first-1, 360)] = true;
    occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+nb_sect, 360)] = true;


    // Draw
    const int8_t cds_layer_spacing = 5; // TODO : param?
    layer++; // index starting at 0 but needed to start at 1

    int16_t diam2 = diam - (layer * 2 * cds_layer_spacing);
    pos_x         = (int16_t) round((double)(canvas_width  - diam2) / 2.0);
    pos_y         = (int16_t) round((double)(canvas_height - diam2) / 2.0);

    char* color = X11Window::color(cds.mean());
    win->draw_arc_64(pos_x, pos_y, diam2, theta_first_64, nb_sect_64, color);
    delete [] color;
  }









  // --------------------------------------------------------------------------This is temporary, it is a big copy-paste of what's above.
  if (allow_plasmids_)
  {
    // Retreive the genetic unit corresponding to the plasmid (i.e. index 1 in genetic_unit_list_)
    GeneticUnit* gen_unit = &*std::next(genetic_unit_list_.begin());
    if (gen_unit == NULL) return;

    int32_t genome_length = gen_unit->dna()->length();


    // Compute display diameter according to genome length and window size
    int16_t canvas_width;
    int16_t canvas_height;
    int16_t canvas_size ;
    if (allow_plasmids_)
    {
      canvas_width  = win->width() / 2;
      canvas_size   = canvas_width;
    }
    else
    {
      canvas_width  = win->width();
      canvas_size   = Utils::min(canvas_width, canvas_width);
    }
    canvas_height = win->height();

    int16_t diam  = round(canvas_size * log((double)genome_length) / 16);

    // Prevent diameter from getting greater than 2/3 of the window size
    if (diam > 2 * canvas_size / 3)
    {
      diam = 2 * canvas_size / 3;
    }

    // Compute coordinates of the upper-left corner of the containing square
    int16_t pos_x = canvas_width + (canvas_width - diam) / 2;
    int16_t pos_y = (canvas_height - diam) / 2;


    // Draw main circle
    win->draw_circle(pos_x, pos_y, diam);

    // Sector occupation management
    reset_sectors();


    // ---------------
    //  Draw each CDS
    // ---------------
    // NB : As we want OriC to be at the "top" of the circle and the orientation
    //      to be clockwise, the drawing angle (theta) will be given as
    //      (90 - alpha), alpha being the "classical" trigonometric angle
    int16_t alpha_first, alpha_last; // Angles of first and last transcribed bases from OriC (degrees)
    int16_t theta_first; //, theta_last; // Transposed angles on the trigonometric circle (degrees)
    int16_t nb_sect;
    // Same as above with precision = 1/64 degree
    int16_t alpha_first_64, alpha_last_64;
    int16_t theta_first_64; //, theta_last_64;
    int16_t nb_sect_64;

    // ----------------
    //  LEADING strand
    // ----------------
    for (const auto& cds: gen_unit->protein_list(LEADING))
    {
      // Alpha : angles from OriC (in degrees)
      // Theta : angles on the trigonometric circle (in degrees)
      // nb_sect : "length" in degrees of the arc to be drawn
      alpha_first   = (int16_t) round(360 * ((double)cds.first_translated_pos() / (double)genome_length));
      alpha_last    = (int16_t) round(360 * ((double)cds.last_translated_pos()  / (double)genome_length));
      theta_first   = Utils::mod(90 - alpha_first, 360);
      // theta_last    = Utils::mod(90 - alpha_last, 360);
      nb_sect       = Utils::mod(alpha_last - alpha_first + 1,  360);

      // These are the same as above but with a higher precision (1/64 degrees)
      alpha_first_64   = (int16_t) round(64 * 360 * ((double)cds.first_translated_pos() / (double)genome_length));
      alpha_last_64    = (int16_t) round(64 * 360 * ((double)cds.last_translated_pos() / (double)genome_length));
      theta_first_64   = Utils::mod(64 * 90 - alpha_first_64, 64 * 360);
      // theta_last_64    = Utils::mod(64 * 90 - alpha_last_64, 64 * 360);
      nb_sect_64       = Utils::mod(alpha_last_64 - alpha_first_64 + 1,  64 * 360);


      // Look for the inmost layer that has all the sectors between
      // theta_first and theta_last free
      int16_t layer = 0;
      bool sectors_free = false;
      while (! sectors_free)
      {
        sectors_free = true;

        for (int16_t rho = 0 ; rho < nb_sect ; rho++)
        {
          if (occupied_sectors_[LEADING][layer][Utils::mod(theta_first-rho, 360)])
          {
            sectors_free = false;
            break;
          }
        }

        if (sectors_free)
        {
          break; // All the needed sectors are free on the current layer
        }
        else
        {
          layer++;

          if (layer >= outmost_layer_)
          {
            add_layer();
            break; // An added layer is necessarily free, no need to look further
          }
        }
      }

      // Mark sectors to be drawn as occupied
      for (int16_t rho = 0 ; rho < nb_sect ; rho++)
      {
        occupied_sectors_[LEADING][layer][Utils::mod(theta_first-rho, 360)] = true;
      }
      // Mark flanking sectors as occupied
      occupied_sectors_[LEADING][layer][Utils::mod(theta_first+1, 360)] = true;
      occupied_sectors_[LEADING][layer][Utils::mod(theta_first-nb_sect, 360)] = true;


      // Draw
      const int8_t cds_layer_spacing = 5; // TODO : param?
      layer++; // index starting at 0 but needed to start at 1

      int16_t diam2 = diam + (layer * 2 * cds_layer_spacing);
      pos_x         = canvas_width + (int16_t) round((double)(canvas_width  - diam2) / 2.0);
      pos_y         = (int16_t) round((double)(canvas_height - diam2) / 2.0);

      char* color = X11Window::color(cds.mean());
      win->draw_arc_64(pos_x, pos_y, diam2, theta_first_64 - nb_sect_64, nb_sect_64, color);
      delete [] color;
    }

    // ----------------
    //  LAGGING strand
    // ----------------
    for (const auto& cds: gen_unit->protein_list(LAGGING))
    {
      // Alpha : angles from OriC (in degrees)
      // Theta : angles on the trigonometric circle (in degrees)
      // nb_sect : "length" in degrees of the arc to be drawn
      alpha_first   = (int16_t) round(360 * ((double)cds.first_translated_pos() / (double)genome_length));
      alpha_last    = (int16_t) round(360 * ((double)cds.last_translated_pos()  / (double)genome_length));
      theta_first   = Utils::mod(90 - alpha_first, 360);
      // theta_last    = Utils::mod(90 - alpha_last, 360);
      nb_sect = Utils::mod(alpha_first - alpha_last + 1,  360);

      // These are the same as above but with a higher precision (1/64 degrees)
      alpha_first_64   = (int16_t) round(64 * 360 * ((double)cds.first_translated_pos() / (double)genome_length));
      alpha_last_64    = (int16_t) round(64 * 360 * ((double)cds.last_translated_pos() / (double)genome_length));
      theta_first_64   = Utils::mod(64 * 90 - alpha_first_64, 64 * 360);
      // theta_last_64    = Utils::mod(64 * 90 - alpha_last_64, 64 * 360);
      nb_sect_64 = Utils::mod(alpha_first_64 - alpha_last_64 + 1,  64 * 360);


      // Look for the inmost layer that has all the sectors between
      // theta_first and theta_last free
      int16_t layer = 0;
      bool sectors_free = false;
      while (! sectors_free)
      {
        sectors_free = true;

        for (int16_t rho = 0 ; rho < nb_sect ; rho++)
        {
          if (occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+rho, 360)])
          {
            sectors_free = false;
            break;
          }
        }

        if (sectors_free)
        {
          break; // All the needed sectors are free on the current layer
        }
        else
        {
          layer++;

          if (layer >= outmost_layer_)
          {
            add_layer();
            break; // An added layer is necessarily free, no need to look further
          }
        }
      }

      // Mark sectors to be drawn as occupied
      for (int16_t rho = 0 ; rho < nb_sect ; rho++)
      {
        occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+rho, 360)] = true;
      }
      // Mark flanking sectors as occupied
      occupied_sectors_[LAGGING][layer][Utils::mod(theta_first-1, 360)] = true;
      occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+nb_sect, 360)] = true;


      // Draw
      const int8_t cds_layer_spacing = 5; // TODO : param?
      layer++; // index starting at 0 but needed to start at 1

      int16_t diam2 = diam - (layer * 2 * cds_layer_spacing);
      pos_x         = canvas_width + (int16_t) round((double)(canvas_width  - diam2) / 2.0);
      pos_y         = (int16_t) round((double)(canvas_height - diam2) / 2.0);

      char* color = X11Window::color(cds.mean());
      win->draw_arc_64(pos_x, pos_y, diam2, theta_first_64, nb_sect_64, color);
      delete [] color;
    }
  }
}

void Individual_X11::display_rnas(X11Window * win)
{
  // Retreive the genetic unit corresponding to the main chromosome
  const GeneticUnit* gen_unit = &genetic_unit_list_.front();
  int32_t genome_length = gen_unit->dna()->length();

  // Display the number of RNAs
  char nb_rna[40];
  sprintf(nb_rna, "Leading : %" PRId32 " RNAs", static_cast<int32_t>(gen_unit->rna_list()[LEADING].size()));
  win->draw_string(15, 15, nb_rna);
  sprintf(nb_rna, "Lagging : %" PRId32 " RNAs", static_cast<int32_t>(gen_unit->rna_list()[LAGGING].size()));
  win->draw_string(15, 30, nb_rna);

  // Compute display diameter according to genome length and window size
  int16_t win_size      = Utils::min(win->width(), win->height());
  int16_t diam          = round(win_size * log((double)genome_length) / 16);

  // Prevent diameter from getting greater than 2/3 of the window size
  if (diam > 2 * win_size / 3)
  {
    diam = 2 * win_size / 3;
  }

  // Compute coordinates of the upper-left corner of the containing square
  int16_t pos_x = (win->width() - diam) / 2;
  int16_t pos_y = (win->height() - diam) / 2;

  // Draw main circle
  win->draw_circle(pos_x, pos_y, diam);

  // Sector occupation management
  reset_sectors();


  // ---------------
  //  Draw each RNA
  // ---------------
  // NB : As we want OriC to be at the "top" of the circle and the orientation
  //      to be clockwise, the drawing angle (theta) will be given as
  //      (90 - alpha), alpha being the "classical" trigonometric angle
  int16_t alpha_first, alpha_last; // Angles of first and last transcribed bases from OriC (degrees)
  int16_t theta_first, theta_last; // Transposed angles on the trigonometric circle (degrees)
  int16_t nb_sect;
  // Same as above with precision = 1/64 degree
  int16_t alpha_first_64, alpha_last_64;
  int16_t theta_first_64, theta_last_64;
  int16_t nb_sect_64;

  // ----------------
  //  LEADING strand
  // ----------------
  const auto& rna_lists = gen_unit->rna_list();
  for (const auto& rna: rna_lists[LEADING])
  {
    // Alpha : angles from OriC (in degrees)
    // Theta : angles on the trigonometric circle (in degrees)
    // nb_sect : "length" in degrees of the arc to be drawn
    alpha_first   = (int16_t) round(360 * ((double)rna.first_transcribed_pos() / (double)genome_length));
    alpha_last    = (int16_t) round(360 * ((double)rna.last_transcribed_pos() / (double)genome_length));
    theta_first   = Utils::mod(90 - alpha_first, 360);
    theta_last    = Utils::mod(90 - alpha_last, 360);
    nb_sect       = Utils::mod(alpha_last - alpha_first + 1,  360);

    // These are the same as above but with a higher precision (1/64 degrees)
    alpha_first_64   = (int16_t) round(64 * 360 * ((double)rna.first_transcribed_pos() / (double)genome_length));
    alpha_last_64    = (int16_t) round(64 * 360 * ((double)rna.last_transcribed_pos() / (double)genome_length));
    theta_first_64   = Utils::mod(64 * 90 - alpha_first_64, 64 * 360);
    theta_last_64    = Utils::mod(64 * 90 - alpha_last_64, 64 * 360);
    nb_sect_64       = Utils::mod(alpha_last_64 - alpha_first_64 + 1,  64 * 360);

    //~ printf("    LEADING RNA %"PRId32" => %"PRId32" :: %"PRId16" %"PRId16"\n", rna->first_transcribed_pos(), rna->last_transcribed_pos(), theta_first, theta_last);


    // Look for the inmost layer that has all the sectors between
    // theta_first and theta_last free
    int16_t layer = 0;
    bool sectors_free = false;
    while (! sectors_free)
    {
      sectors_free = true;

      for (int16_t rho = 0 ; rho < nb_sect ; rho++)
      {
        if (occupied_sectors_[LEADING][layer][Utils::mod(theta_first-rho, 360)])
        {
          sectors_free = false;
          break;
        }
      }

      if (sectors_free)
      {
        break; // All the needed sectors are free on the current layer
      }
      else
      {
        layer++;

        if (layer >= outmost_layer_)
        {
          add_layer();
          break; // An added layer is necessarily free, no need to look further
        }
      }
    }

    // Mark sectors to be drawn as occupied
    for (int16_t rho = 0 ; rho < nb_sect ; rho++)
    {
      occupied_sectors_[LEADING][layer][Utils::mod(theta_first-rho, 360)] = true;
    }
    // Mark flanking sectors as occupied
    occupied_sectors_[LEADING][layer][Utils::mod(theta_first+1, 360)] = true;
    occupied_sectors_[LEADING][layer][Utils::mod(theta_first-nb_sect, 360)] = true;


    // Determine drawing color
    char* color;
    if (rna.is_coding())
    {
      color = X11Window::color(rna.basal_level());
    }
    else
    {
      color = new char[8];
      strcpy(color, "#FFFFFF");
    }

    // Draw arrow body
    const int8_t rna_layer_spacing = 5; // TODO : param?
    layer++; // index starting at 0 but needed to start at 1

    int16_t diam2 = diam + (layer * 2 * rna_layer_spacing);
    pos_x         = (int16_t) round((double)(win->width()  - diam2) / 2.0);
    pos_y         = (int16_t) round((double)(win->height() - diam2) / 2.0);

    win->draw_arc_64(pos_x, pos_y, diam2, theta_first_64 - nb_sect_64, nb_sect_64, color);

    // Draw arrow head
    int8_t arrow_thick = 6; // Must be an even value
    pos_x = (win->width() / 2.0) + (cos((theta_first_64 - nb_sect_64)/(64*180.0)*M_PI) * diam2 / 2.0) - (arrow_thick / 2.0);
    pos_y = (win->height() / 2.0) - (sin((theta_first_64 - nb_sect_64)/(64*180.0)*M_PI) * diam2 / 2.0) - (arrow_thick / 2.0);

    win->fill_arc(pos_x, pos_y, arrow_thick, Utils::mod(180+theta_last, 360), 180, color);

    delete [] color;
  }

  // ----------------
  //  LAGGING strand
  // ----------------
  for (const auto& rna: rna_lists[LAGGING]) {
    // Alpha : angles from OriC (in degrees)
    // Theta : angles on the trigonometric circle (in degrees)
    // nb_sect : "length" in degrees of the arc to be drawn
    alpha_first   = (int16_t) round(360 * ((double)rna.first_transcribed_pos() / (double)genome_length));
    alpha_last    = (int16_t) round(360 * ((double)rna.last_transcribed_pos()  / (double)genome_length));
    theta_first   = Utils::mod(90 - alpha_first, 360);
    theta_last    = Utils::mod(90 - alpha_last, 360);
    nb_sect = Utils::mod(alpha_first - alpha_last + 1,  360);

    // These are the same as above but with a higher precision (1/64 degrees)
    alpha_first_64   = (int16_t) round(64 * 360 * ((double)rna.first_transcribed_pos() / (double)genome_length));
    alpha_last_64    = (int16_t) round(64 * 360 * ((double)rna.last_transcribed_pos()  / (double)genome_length));
    theta_first_64   = Utils::mod(64 * 90 - alpha_first_64, 64 * 360);
    theta_last_64    = Utils::mod(64 * 90 - alpha_last_64, 64 * 360);
    nb_sect_64 = Utils::mod(alpha_first_64 - alpha_last_64 + 1,  64 * 360);

    //~ printf("    LAGGING RNA %"PRId32" => %"PRId32" :: %"PRId16" %"PRId16"\n", rna->first_transcribed_pos(), rna->last_transcribed_pos(), theta_first, theta_last);


    // Look for the inmost layer that has all the sectors between
    // theta_first and theta_last free
    int16_t layer = 0;
    bool sectors_free = false;
    while (! sectors_free)
    {
      sectors_free = true;

      for (int16_t rho = 0 ; rho < nb_sect ; rho++)
      {
        if (occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+rho, 360)])
        {
          sectors_free = false;
          break;
        }
      }

      if (sectors_free)
      {
        break; // All the needed sectors are free on the current layer
      }
      else
      {
        layer++;

        if (layer >= outmost_layer_)
        {
          add_layer();
          break; // An added layer is necessarily free, no need to look further
        }
      }
    }

    // Mark sectors to be drawn as occupied
    for (int16_t rho = 0 ; rho < nb_sect ; rho++)
    {
      occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+rho, 360)] = true;
    }
    // Mark flanking sectors as occupied
    occupied_sectors_[LAGGING][layer][Utils::mod(theta_first-1, 360)] = true;
    occupied_sectors_[LAGGING][layer][Utils::mod(theta_first+nb_sect, 360)] = true;


    // Determine drawing color
    char* color;
    if (rna.is_coding())
    {
      color = X11Window::color(rna.basal_level());
    }
    else
    {
      color = new char[8];
      strcpy(color, "#FFFFFF");
    }

    // Draw arrow body
    const int8_t rna_layer_spacing = 5; // TODO : param?
    layer++; // index starting at 0 but needed to start at 1

    int16_t diam2 = diam - (layer * 2 * rna_layer_spacing);
    pos_x         = (int16_t) round((double)(win->width()  - diam2) / 2.0);
    pos_y         = (int16_t) round((double)(win->height() - diam2) / 2.0);

    win->draw_arc_64(pos_x, pos_y, diam2, theta_first_64, nb_sect_64, color);

    // Draw arrow head
    int8_t arrow_thick = 6; // Must be an even value
    pos_x = (win->width() / 2.0) + (cos((theta_last_64+1)/(64*180.0)*M_PI) * diam2 / 2.0) - (arrow_thick / 2.0);
    pos_y = (win->height() / 2.0) - (sin((theta_last_64+1)/(64*180.0)*M_PI) * diam2 / 2.0) - (arrow_thick / 2.0);

    win->fill_arc(pos_x, pos_y, arrow_thick, theta_last, 180, color);

    delete [] color;
  }
}

// =================================================================
//                           Protected Methods
// =================================================================
void Individual_X11::add_layer()
{
  occupied_sectors_[LEADING][outmost_layer_] = new bool[360];
  occupied_sectors_[LAGGING][outmost_layer_] = new bool[360];

  for (int16_t angle = 0 ; angle < 360 ; angle++)
  {
    occupied_sectors_[LEADING][outmost_layer_][angle] = false;
    occupied_sectors_[LAGGING][outmost_layer_][angle] = false;
  }

  outmost_layer_++;
}

void Individual_X11::init_occupied_sectors()
{
  outmost_layer_ = 1;

  for (int16_t layer = 0 ; layer < outmost_layer_ ; layer++)
  {
    occupied_sectors_[LEADING][layer] = new bool[360];
    occupied_sectors_[LAGGING][layer] = new bool[360];
  }
}

void Individual_X11::reset_sectors()
{
  for (int16_t layer = 0 ; layer < outmost_layer_ ; layer++)
  {
    for (int16_t angle = 0 ; angle < 360 ; angle++)
    {
      occupied_sectors_[LEADING][layer][angle] = false;
      occupied_sectors_[LAGGING][layer][angle] = false;
    }
  }
}
} // namespace aevol