class_docstr: Edge drawer implementation that draws undirected edges as
  straight lines and directed edges as arrows.
class_docstr_tok: ["Edge","drawer","implementation","that","draws","undirected","edges","as","straight","lines","and","directed","edges","as","arrows","."]
code: def draw_directed_edge(self, edge, src_vertex, dest_vertex):
      if src_vertex == dest_vertex:  # TODO
          return self.draw_loop_edge(edge, src_vertex)
  
      ctx = self.context
      (x1, y1), (x2, y2) = src_vertex.position, dest_vertex.position
      (x_src, y_src), (x_dest, y_dest) = (
          src_vertex.position,
          dest_vertex.position,
      )
  
      # Draw the edge
      ctx.set_source_rgba(*edge.color)
      ctx.set_line_width(edge.width)
      ctx.move_to(x1, y1)
  
      if edge.curved:
          # Calculate the curve
          aux1, aux2 = get_bezier_control_points_for_curved_edge(
              x1, y1, x2, y2, edge.curved
          )
  
          # Coordinates of the control points of the Bezier curve
          xc1, yc1 = aux1
          xc2, yc2 = aux2
  
          # Determine where the edge intersects the circumference of the
          # vertex shape: Tip of the arrow
          x2, y2 = intersect_bezier_curve_and_circle(
              x_src,
              y_src,
              xc1,
              yc1,
              xc2,
              yc2,
              x_dest,
              y_dest,
              dest_vertex.size / 2.0,
          )
  
          # Calculate the arrow head coordinates
          angle = atan2(y_dest - y2, x_dest - x2)  # navid
          arrow_size = 15.0 * edge.arrow_size
          arrow_width = 10.0 / edge.arrow_width
          aux_points = [
              (
                  x2 - arrow_size * cos(angle - pi / arrow_width),
                  y2 - arrow_size * sin(angle - pi / arrow_width),
              ),
              (
                  x2 - arrow_size * cos(angle + pi / arrow_width),
                  y2 - arrow_size * sin(angle + pi / arrow_width),
              ),
          ]
  
          # Midpoint of the base of the arrow triangle
          x_arrow_mid, y_arrow_mid = (
              aux_points[0][0] + aux_points[1][0]
          ) / 2.0, (aux_points[0][1] + aux_points[1][1]) / 2.0
  
          # Vector representing the base of the arrow triangle
          x_arrow_base_vec, y_arrow_base_vec = (
              aux_points[0][0] - aux_points[1][0]
          ), (aux_points[0][1] - aux_points[1][1])
  
          # Recalculate the curve such that it lands on the base of the arrow triangle
          aux1, aux2 = get_bezier_control_points_for_curved_edge(
              x1, y1, x_arrow_mid, y_arrow_mid, edge.curved
          )
  
          # Offset the second control point (aux2) such that it falls precisely
          # on the normal to the arrow base vector. Strictly speaking,
          # offset_length is the offset length divided by the length of the
          # arrow base vector.
          offset_length = (x_arrow_mid - aux2[0]) * x_arrow_base_vec + (
              y_arrow_mid - aux2[1]
          ) * y_arrow_base_vec
          offset_length /= (
              euclidean_distance(
                  0, 0, x_arrow_base_vec, y_arrow_base_vec
              )
              ** 2
          )
  
          aux2 = (
              aux2[0] + x_arrow_base_vec * offset_length,
              aux2[1] + y_arrow_base_vec * offset_length,
          )
  
          # Draw the curve from the first vertex to the midpoint of the base
          # of the arrow head
          ctx.curve_to(
              aux1[0],
              aux1[1],
              aux2[0],
              aux2[1],
              x_arrow_mid,
              y_arrow_mid,
          )
      else:
          # Determine where the edge intersects the circumference of the
          # vertex shape.
          x2, y2 = dest_vertex.shape.intersection_point(
              x2, y2, x1, y1, dest_vertex.size
          )
  
          # Draw the arrowhead
          angle = atan2(y_dest - y2, x_dest - x2)
          arrow_size = 15.0 * edge.arrow_size
          arrow_width = 10.0 / edge.arrow_width
          aux_points = [
              (
                  x2 - arrow_size * cos(angle - pi / arrow_width),
                  y2 - arrow_size * sin(angle - pi / arrow_width),
              ),
              (
                  x2 - arrow_size * cos(angle + pi / arrow_width),
                  y2 - arrow_size * sin(angle + pi / arrow_width),
              ),
          ]
  
          # Midpoint of the base of the arrow triangle
          x_arrow_mid, y_arrow_mid = (
              aux_points[0][0] + aux_points[1][0]
          ) / 2.0, (aux_points[0][1] + aux_points[1][1]) / 2.0
  
          # Draw the line
          ctx.line_to(x_arrow_mid, y_arrow_mid)
  
      # Draw the edge
      ctx.stroke()
  
      # Draw the arrow head
      ctx.move_to(x2, y2)
      ctx.line_to(*aux_points[0])
      ctx.line_to(*aux_points[1])
      ctx.line_to(x2, y2)
      ctx.fill()
  
imports: from math import atan2, cos, pi, sin
  from igraph.drawing.baseclasses import AbstractEdgeDrawer
  from igraph.drawing.colors import clamp
  from igraph.drawing.metamagic import AttributeCollectorBase
  from igraph.drawing.utils import euclidean_distance, get_bezier_control_points_for_curved_edge, intersect_bezier_curve_and_circle
  from .utils import find_cairo
label_desc: Use image node_id 1 for calling the CairoArrowEdgeDrawer obj's underlying member method code with example usage: obj.draw_directed_edge(edge, src_vertex, dest_vertex) and returns: self