text: """Implements the CircularGraphicRecord class.
  """
  
  import matplotlib.patches as mpatches
  import numpy as np
  
  from ..GraphicRecord import GraphicRecord
  from .ArrowWedge import ArrowWedge
  
  
  class CircularGraphicRecord(GraphicRecord):
      """Set of Genetic Features of a same DNA sequence, to be plotted together.
  
      Parameters
      ----------
  
      sequence_length
        Length of the DNA sequence, in number of nucleotides
  
      features
        list of GraphicalFeature objects.
  
      top_position
        The index in the sequence that will end up at the top of the circle
  
      feature_level_height
        Width in inches of one "level" for feature arrows.
  
      annotation_height
        Width in inches of one "level" for feature annotations.
  
      labels_spacing
        Distance in basepairs to keep between labels to avoid "quasi-collisions"
  
      **kw
        Other keyword arguments - do not use, these parameters are allowed for
        making it easier to use GraphicRecord and CircularGraphicRecord
        interchangeably.
  
      """
  
      default_elevate_outline_annotations = True
      min_y_height_of_text_line = 0.1
  
      def __init__(
          self,
          sequence_length,
          features,
          top_position=0,
          feature_level_height=0.2,
          annotation_height="auto",
          labels_spacing=12,
          **kw
      ):
  
          self.radius = 1.0
          self.sequence_length = sequence_length
          self.features = features
          self.top_position = top_position
          self.feature_level_height = feature_level_height
          self.annotation_height = annotation_height
          self.labels_spacing = labels_spacing
  
      def initialize_ax(self, ax, draw_line, with_ruler):
          """Initialize the ax with a circular line, sets limits, aspect etc.
          """
  
          if draw_line:
              circle = mpatches.Circle(
                  (0, -self.radius), self.radius, facecolor="none", edgecolor="k"
              )
              ax.add_patch(circle)
          ax.axis("off")
          if with_ruler:
              # only display the xaxis ticks
              ax.set_frame_on(False)
              ax.yaxis.set_visible(False)
              ax.xaxis.tick_bottom()
          else:
              # don't display anything
              ax.axis("off")
  
          ax.set_xlim(-1.1 * self.radius, 1.1 * self.radius)
          ax.set_ylim(-self.radius, 3 * self.radius)
          ax.set_aspect("equal")
  
      def finalize_ax(
          self,
          ax,
          features_levels,
          annotations_max_level,
          auto_figure_height=False,
          ideal_yspan=None,
          annotations_are_elevated=True
      ):
          """Final display range and figure dimension tweakings."""
          annotation_height = self.determine_annotation_height(
              annotations_max_level
          )
          ymin = -2 * self.radius - self.feature_level_height * (
              features_levels + 1
          )
          ymax = (
              self.feature_level_height * (features_levels + 1)
              + annotation_height * (annotations_max_level + 1)
          )
          if ideal_yspan is not None:
              ymax = max(annotation_height * ideal_yspan + ymin, ymax)
          xmin = -self.radius - self.feature_level_height * (features_levels + 1)
          xmax = -xmin
          ax.set_xlim(xmin, xmax)
          ax.set_ylim(ymin, ymax)
          ratio = 1.0 * (ymax - ymin) / (xmax - xmin)
  
          if auto_figure_height:
              figure_width = ax.figure.get_size_inches()[0]
              ax.figure.set_size_inches(figure_width, figure_width * ratio)
  
      def plot_feature(self, ax, feature, level):
          """Plot an ArrowWedge representing the feature at the giben height
          level.
  
  
          """
          a_start = self.position_to_angle(feature.start)
          a_end = self.position_to_angle(feature.end)
          a_start, a_end = sorted([a_start, a_end])
          r = self.radius + level * self.feature_level_height
          patch = ArrowWedge(
              center=(0, -self.radius),
              radius=r,
              theta1=a_start,
              theta2=a_end,
              width=0.7 * self.feature_level_height,
              direction=feature.strand,
              edgecolor=feature.linecolor,
              linewidth=feature.linewidth,
              facecolor=feature.color,
              zorder=1,
          )
          ax.add_patch(patch)
  
      def position_to_angle(self, position):
          """Convert a sequence position into an angle in the figure."""
          a = 360.0 * (position - self.top_position) / self.sequence_length
          return 90 - a
  
      def coordinates_in_plot(self, position, level):
          """Convert a sequence position and height level to (x, y) coordinates.
          """
          r = self.radius + level * self.feature_level_height
          angle = self.position_to_angle(position)
          rad_angle = np.deg2rad(angle)
          return np.array(
              [r * np.cos(rad_angle), r * np.sin(rad_angle) - self.radius]
          )
  
      def determine_annotation_height(self, max_annotations_level):
          """Auto-select the annotations height.
  
          Annotation height is 0.2 at most, or else whatever will make
          the figure a 5*radius tall rectangle where the circular plasmid
          occupies the bottom-2 5th and the annotations occupy the top-3 5th.
          """
          return min(0.25, 3.0 * self.radius / (1.0 + max_annotations_level))
  
      def compute_padding(self, ax):
          ""
          ax_width = ax.get_window_extent(ax.figure.canvas.get_renderer()).width
          xmin, xmax = ax.get_xlim()
          return 3 * self.labels_spacing * (xmax - xmin) / (1.0 * ax_width)