class_docstr: Abstract edge drawer object from which all concrete edge drawer
  implementations are derived.
class_docstr_tok: ["Abstract","edge","drawer","object","from","which","all","concrete","edge","drawer","implementations","are","derived","."]
docstr: Returns the position where the label of an edge should be drawn. the
  default implementation returns the midpoint of the edge and an alignment
  that tries to avoid overlapping the label with the edge.
  
  @param edge: the edge to be drawn. visual properties of the edge
    are defined by the attributes of this object.
  @param src_vertex: the source vertex. visual properties are given
    again as attributes.
  @param dest_vertex: the target vertex. visual properties are given
    again as attributes.
  @return: a tuple containing two more tuples: the desired position of the
    label and the desired alignment of the label, where the position is
    given as c{(x, y)} and the alignment is given as c{(horizontal, vertical)}.
    members of the alignment tuple are taken from constants in the
    l{textalignment} class.
docstr_tok: ["Returns","the","position","where","the","label","of","an","edge","should","be","drawn",".","the","default","implementation","returns","the","midpoint","of","the","edge","and","an","alignment","that","tries","to","avoid","overlapping","the","label","with","the","edge",".","@","param","edge",":","the","edge","to","be","drawn",".","visual","properties","of","the","edge","are","defined","by","the","attributes","of","this","object",".","@","param","src_vertex",":","the","source","vertex",".","visual","properties","are","given","again","as","attributes",".","@","param","dest_vertex",":","the","target","vertex",".","visual","properties","are","given","again","as","attributes",".","@","return",":","a","tuple","containing","two","more","tuples",":","the","desired","position","of","the","label","and","the","desired","alignment","of","the","label",",","where","the","position","is","given","as","c","{","(","x",",","y",")","}","and","the","alignment","is","given","as","c","{","(","horizontal",",","vertical",")","}",".","members","of","the","alignment","tuple","are","taken","from","constants","in","the","l","{","textalignment","}","class","."]
code: def get_label_position(self, edge, src_vertex, dest_vertex):
      """Returns the position where the label of an edge should be drawn. the
      default implementation returns the midpoint of the edge and an alignment
      that tries to avoid overlapping the label with the edge.
  
      @param edge: the edge to be drawn. visual properties of the edge
        are defined by the attributes of this object.
      @param src_vertex: the source vertex. visual properties are given
        again as attributes.
      @param dest_vertex: the target vertex. visual properties are given
        again as attributes.
      @return: a tuple containing two more tuples: the desired position of the
        label and the desired alignment of the label, where the position is
        given as c{(x, y)} and the alignment is given as c{(horizontal, vertical)}.
        members of the alignment tuple are taken from constants in the
        l{textalignment} class.
      """
      # TODO: curved edges don't play terribly well with this function,
      # we could try to get the mid point of the actual curved arrow
      # (Bezier curve) and use that.
  
      # Determine the angle of the line
      dx = dest_vertex.position[0] - src_vertex.position[0]
      dy = dest_vertex.position[1] - src_vertex.position[1]
      if dx != 0 or dy != 0:
          # Note that we use -dy because the Y axis points downwards
          angle = atan2(-dy, dx) % (2 * pi)
      else:
          angle = None
  
      # Determine the midpoint
      if edge.curved:
          (x1, y1), (x2, y2) = src_vertex.position, dest_vertex.position
          aux1, aux2 = get_bezier_control_points_for_curved_edge(
              x1,
              y1,
              x2,
              y2,
              edge.curved,
          )
          pos = evaluate_cubic_bezier(x1, y1, *aux1, *aux2, x2, y2, 0.5)
      else:
          pos = (
              (src_vertex.position[0] + dest_vertex.position[0]) / 2.0,
              (src_vertex.position[1] + dest_vertex.position[1]) / 2.0,
          )
  
      # Determine the alignment based on the angle
      pi4 = pi / 4
      if angle is None:
          halign, valign = TextAlignment.CENTER, TextAlignment.CENTER
      else:
          index = int((angle / pi4) % 8)
          halign = [
              TextAlignment.RIGHT,
              TextAlignment.RIGHT,
              TextAlignment.RIGHT,
              TextAlignment.RIGHT,
              TextAlignment.LEFT,
              TextAlignment.LEFT,
              TextAlignment.LEFT,
              TextAlignment.LEFT,
          ][index]
          valign = [
              TextAlignment.BOTTOM,
              TextAlignment.CENTER,
              TextAlignment.CENTER,
              TextAlignment.TOP,
              TextAlignment.TOP,
              TextAlignment.CENTER,
              TextAlignment.CENTER,
              TextAlignment.BOTTOM,
          ][index]
  
      return pos, (halign, valign)
  
imports: from abc import ABCMeta, abstractmethod
  from math import atan2, pi
  from .text import TextAlignment
  from .utils import get_bezier_control_points_for_curved_edge, evaluate_cubic_bezier
label_desc: Use image node_id 4 for calling the AbstractEdgeDrawer obj's underlying member method code with example usage: obj.get_label_position(edge, src_vertex, dest_vertex) and returns: pos,  halign,  valign