class_docstr: Matplotlib-specific abstract edge drawer object.
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)
  
      fig = 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
      path = [
          format_path_step("M", [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(
              x_src, y_src, 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
          path.append(
              format_path_step(
                  "C", [aux1, aux2, [x_arrow_mid, y_arrow_mid]]
              )
          )
  
      else:
          # FIXME: this is tricky in plotly, let's skip for now
          ## 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
          path.append(
              format_path_step(
                  "L",
                  Point(x_arrow_mid, y_arrow_mid),
              )
          )
  
      path = " ".join(path)
  
      # Draw the edge
      stroke = {
          "type": "path",
          "path": path,
          "line_color": format_rgba(edge.color),
          "line_width": edge.width,
      }
      fig.add_shape(stroke)
  
      # Draw the arrow head
      arrowhead = plotly.graph_objects.Scatter(
          x=[x2, aux_points[0][0], aux_points[1][0], x2],
          y=[y2, aux_points[0][1], aux_points[1][1], y2],
          fillcolor=format_rgba(edge.color),
          mode="lines",
      )
      fig.add_trace(arrowhead)
  
file_path: python-igraph/src/igraph/drawing/plotly/edge.py
imports: from math import atan2, cos, pi, sin
  from igraph.drawing.baseclasses import AbstractEdgeDrawer
  from igraph.drawing.metamagic import AttributeCollectorBase
  from igraph.drawing.plotly.utils import find_plotly, format_path_step, format_arc, format_rgba
  from igraph.drawing.utils import Point, euclidean_distance, get_bezier_control_points_for_curved_edge, intersect_bezier_curve_and_circle
label_desc: Use image node_id 3 for calling the PlotlyEdgeDrawer obj's underlying member method code with example usage: obj.draw_directed_edge(edge, src_vertex, dest_vertex) and returns: self