class_docstr: Given a :class:`~.Diagram` and the corresponding
  :class:`DiagramGrid`, produces the Xy-pic representation of the
  diagram.
  
  The most important method in this class is ``draw``.  Consider the
  following triangle diagram:
  
  >>> from sympy.categories import Object, NamedMorphism, Diagram
  >>> from sympy.categories import DiagramGrid, XypicDiagramDrawer
  >>> A = Object("A")
  >>> B = Object("B")
  >>> C = Object("C")
  >>> f = NamedMorphism(A, B, "f")
  >>> g = NamedMorphism(B, C, "g")
  >>> diagram = Diagram([f, g], {g * f: "unique"})
  
  To draw this diagram, its objects need to be laid out with a
  :class:`DiagramGrid`::
  
  >>> grid = DiagramGrid(diagram)
  
  Finally, the drawing:
  
  >>> drawer = XypicDiagramDrawer()
  >>> print(drawer.draw(diagram, grid))
  \xymatrix{
  A \ar[d]_{g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
  C &
  }
  
  For further details see the docstring of this method.
  
  To control the appearance of the arrows, formatters are used.  The
  dictionary ``arrow_formatters`` maps morphisms to formatter
  functions.  A formatter is accepts an
  :class:`ArrowStringDescription` and is allowed to modify any of
  the arrow properties exposed thereby.  For example, to have all
  morphisms with the property ``unique`` appear as dashed arrows,
  and to have their names prepended with `\exists !`, the following
  should be done:
  
  >>> def formatter(astr):
  ...   astr.label = r"\exists !" + astr.label
  ...   astr.arrow_style = "{-->}"
  >>> drawer.arrow_formatters["unique"] = formatter
  >>> print(drawer.draw(diagram, grid))
  \xymatrix{
  A \ar@{-->}[d]_{\exists !g\circ f} \ar[r]^{f} & B \ar[ld]^{g} \\
  C &
  }
  
  To modify the appearance of all arrows in the diagram, set
  ``default_arrow_formatter``.  For example, to place all morphism
  labels a little bit farther from the arrow head so that they look
  more centred, do as follows:
  
  >>> def default_formatter(astr):
  ...   astr.label_displacement = "(0.45)"
  >>> drawer.default_arrow_formatter = default_formatter
  >>> print(drawer.draw(diagram, grid))
  \xymatrix{
  A \ar@{-->}[d]_(0.45){\exists !g\circ f} \ar[r]^(0.45){f} & B \ar[ld]^(0.45){g} \\
  C &
  }
  
  In some diagrams some morphisms are drawn as curved arrows.
  Consider the following diagram:
  
  >>> D = Object("D")
  >>> E = Object("E")
  >>> h = NamedMorphism(D, A, "h")
  >>> k = NamedMorphism(D, B, "k")
  >>> diagram = Diagram([f, g, h, k])
  >>> grid = DiagramGrid(diagram)
  >>> drawer = XypicDiagramDrawer()
  >>> print(drawer.draw(diagram, grid))
  \xymatrix{
  A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_3mm/[ll]_{h} \\
  & C &
  }
  
  To control how far the morphisms are curved by default, one can
  use the ``unit`` and ``default_curving_amount`` attributes:
  
  >>> drawer.unit = "cm"
  >>> drawer.default_curving_amount = 1
  >>> print(drawer.draw(diagram, grid))
  \xymatrix{
  A \ar[r]_{f} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_1cm/[ll]_{h} \\
  & C &
  }
  
  In some diagrams, there are multiple curved morphisms between the
  same two objects.  To control by how much the curving changes
  between two such successive morphisms, use
  ``default_curving_step``:
  
  >>> drawer.default_curving_step = 1
  >>> h1 = NamedMorphism(A, D, "h1")
  >>> diagram = Diagram([f, g, h, k, h1])
  >>> grid = DiagramGrid(diagram)
  >>> print(drawer.draw(diagram, grid))
  \xymatrix{
  A \ar[r]_{f} \ar@/^1cm/[rr]^{h_{1}} & B \ar[d]^{g} & D \ar[l]^{k} \ar@/_2cm/[ll]_{h} \\
  & C &
  }
  
  The default value of ``default_curving_step`` is 4 units.
  
  See Also
  ========
  
  draw, ArrowStringDescription
class_docstr_tok: ["Given","a",":","class",":","`","~.Diagram","`","and","the","corresponding",":","class",":","`","DiagramGrid","`",",","produces","the","Xy-pic","representation","of","the","diagram",".","The","most","important","method","in","this","class","is","``","draw","``",".","Consider","the","following","triangle","diagram",":",">",">",">","from","sympy.categories","import","Object",",","NamedMorphism",",","Diagram",">",">",">","from","sympy.categories","import","DiagramGrid",",","XypicDiagramDrawer",">",">",">","A","=","Object","(","``","A","''",")",">",">",">","B","=","Object","(","``","B","''",")",">",">",">","C","=","Object","(","``","C","''",")",">",">",">","f","=","NamedMorphism","(","A",",","B",",","``","f","''",")",">",">",">","g","=","NamedMorphism","(","B",",","C",",","``","g","''",")",">",">",">","diagram","=","Diagram","(","[","f",",","g","]",",","{","g","*","f",":","``","unique","''","}",")","To","draw","this","diagram",",","its","objects","need","to","be","laid","out","with","a",":","class",":","`","DiagramGrid","`",":",":",">",">",">","grid","=","DiagramGrid","(","diagram",")","Finally",",","the","drawing",":",">",">",">","drawer","=","XypicDiagramDrawer","(",")",">",">",">","print","(","drawer.draw","(","diagram",",","grid",")",")","\\xymatrix","{","A","\\ar","[","d","]","_","{","g\\circ","f","}","\\ar","[","r","]","^","{","f","}","&","B","\\ar","[","ld","]","^","{","g","}","\\\\","C","&","}","For","further","details","see","the","docstring","of","this","method",".","To","control","the","appearance","of","the","arrows",",","formatters","are","used",".","The","dictionary","``","arrow_formatters","``","maps","morphisms","to","formatter","functions",".","A","formatter","is","accepts","an",":","class",":","`","ArrowStringDescription","`","and","is","allowed","to","modify","any","of","the","arrow","properties","exposed","thereby",".","For","example",",","to","have","all","morphisms","with","the","property","``","unique","``","appear","as","dashed","arrows",",","and","to","have","their","names","prepended","with","`","\\exists","!","`",",","the","following","should","be","done",":",">",">",">","def","formatter","(","astr",")",":","...","astr.label","=","r","''","\\exists","!","''","+","astr.label","...","astr.arrow_style","=","``","{","--",">","}","''",">",">",">","drawer.arrow_formatters","[","``","unique","''","]","=","formatter",">",">",">","print","(","drawer.draw","(","diagram",",","grid",")",")","\\xymatrix","{","A","\\ar","@","{","--",">","}","[","d","]","_","{","\\exists","!","g\\circ","f","}","\\ar","[","r","]","^","{","f","}","&","B","\\ar","[","ld","]","^","{","g","}","\\\\","C","&","}","To","modify","the","appearance","of","all","arrows","in","the","diagram",",","set","``","default_arrow_formatter","``",".","For","example",",","to","place","all","morphism","labels","a","little","bit","farther","from","the","arrow","head","so","that","they","look","more","centred",",","do","as","follows",":",">",">",">","def","default_formatter","(","astr",")",":","...","astr.label_displacement","=","``","(","0.45",")","''",">",">",">","drawer.default_arrow_formatter","=","default_formatter",">",">",">","print","(","drawer.draw","(","diagram",",","grid",")",")","\\xymatrix","{","A","\\ar","@","{","--",">","}","[","d","]","_","(","0.45",")","{","\\exists","!","g\\circ","f","}","\\ar","[","r","]","^","(","0.45",")","{","f","}","&","B","\\ar","[","ld","]","^","(","0.45",")","{","g","}","\\\\","C","&","}","In","some","diagrams","some","morphisms","are","drawn","as","curved","arrows",".","Consider","the","following","diagram",":",">",">",">","D","=","Object","(","``","D","''",")",">",">",">","E","=","Object","(","``","E","''",")",">",">",">","h","=","NamedMorphism","(","D",",","A",",","``","h","''",")",">",">",">","k","=","NamedMorphism","(","D",",","B",",","``","k","''",")",">",">",">","diagram","=","Diagram","(","[","f",",","g",",","h",",","k","]",")",">",">",">","grid","=","DiagramGrid","(","diagram",")",">",">",">","drawer","=","XypicDiagramDrawer","(",")",">",">",">","print","(","drawer.draw","(","diagram",",","grid",")",")","\\xymatrix","{","A","\\ar","[","r","]","_","{","f","}","&","B","\\ar","[","d","]","^","{","g","}","&","D","\\ar","[","l","]","^","{","k","}","\\ar","@","\/_3mm\/","[","ll","]","_","{","h","}","\\\\","&","C","&","}","To","control","how","far","the","morphisms","are","curved","by","default",",","one","can","use","the","``","unit","``","and","``","default_curving_amount","``","attributes",":",">",">",">","drawer.unit","=","``","cm","''",">",">",">","drawer.default_curving_amount","=","1",">",">",">","print","(","drawer.draw","(","diagram",",","grid",")",")","\\xymatrix","{","A","\\ar","[","r","]","_","{","f","}","&","B","\\ar","[","d","]","^","{","g","}","&","D","\\ar","[","l","]","^","{","k","}","\\ar","@","\/_1cm\/","[","ll","]","_","{","h","}","\\\\","&","C","&","}","In","some","diagrams",",","there","are","multiple","curved","morphisms","between","the","same","two","objects",".","To","control","by","how","much","the","curving","changes","between","two","such","successive","morphisms",",","use","``","default_curving_step","``",":",">",">",">","drawer.default_curving_step","=","1",">",">",">","h1","=","NamedMorphism","(","A",",","D",",","``","h1","''",")",">",">",">","diagram","=","Diagram","(","[","f",",","g",",","h",",","k",",","h1","]",")",">",">",">","grid","=","DiagramGrid","(","diagram",")",">",">",">","print","(","drawer.draw","(","diagram",",","grid",")",")","\\xymatrix","{","A","\\ar","[","r","]","_","{","f","}","\\ar","@","\/^1cm\/","[","rr","]","^","{","h_","{","1","}","}","&","B","\\ar","[","d","]","^","{","g","}","&","D","\\ar","[","l","]","^","{","k","}","\\ar","@","\/_2cm\/","[","ll","]","_","{","h","}","\\\\","&","C","&","}","The","default","value","of","``","default_curving_step","``","is","4","units",".","See","Also","========","draw",",","ArrowStringDescription"]
docstr: Produces the information required for constructing the string
  representation of a loop morphism.  This function is invoked
  from ``_process_morphism``.
  
  See Also
  ========
  
  _process_morphism
docstr_tok: ["Produces","the","information","required","for","constructing","the","string","representation","of","a","loop","morphism",".","This","function","is","invoked","from","``","_process_morphism","``",".","See","Also","========","_process_morphism"]
code: def _process_loop_morphism(
      i, j, grid, morphisms_str_info, object_coords
  ):
      """
      Produces the information required for constructing the string
      representation of a loop morphism.  This function is invoked
      from ``_process_morphism``.
  
      See Also
      ========
  
      _process_morphism
      """
      curving = ""
      label_pos = "^"
      looping_start = ""
      looping_end = ""
  
      # This is a loop morphism.  Count how many morphisms stick
      # in each of the four quadrants.  Note that straight
      # vertical and horizontal morphisms count in two quadrants
      # at the same time (i.e., a morphism going up counts both
      # in the first and the second quadrants).
  
      # The usual numbering (counterclockwise) of quadrants
      # applies.
      quadrant = [0, 0, 0, 0]
  
      obj = grid[i, j]
  
      for m, m_str_info in morphisms_str_info.items():
          if (m.domain == obj) and (m.codomain == obj):
              # That's another loop morphism.  Check how it
              # loops and mark the corresponding quadrants as
              # busy.
              (l_s, l_e) = (
                  m_str_info.looping_start,
                  m_str_info.looping_end,
              )
  
              if (l_s, l_e) == ("r", "u"):
                  quadrant[0] += 1
              elif (l_s, l_e) == ("u", "l"):
                  quadrant[1] += 1
              elif (l_s, l_e) == ("l", "d"):
                  quadrant[2] += 1
              elif (l_s, l_e) == ("d", "r"):
                  quadrant[3] += 1
  
              continue
          if m.domain == obj:
              (end_i, end_j) = object_coords[m.codomain]
              goes_out = True
          elif m.codomain == obj:
              (end_i, end_j) = object_coords[m.domain]
              goes_out = False
          else:
              continue
  
          d_i = end_i - i
          d_j = end_j - j
          m_curving = m_str_info.curving
  
          if (d_i != 0) and (d_j != 0):
              # This is really a diagonal morphism.  Detect the
              # quadrant.
              if (d_i > 0) and (d_j > 0):
                  quadrant[0] += 1
              elif (d_i > 0) and (d_j < 0):
                  quadrant[1] += 1
              elif (d_i < 0) and (d_j < 0):
                  quadrant[2] += 1
              elif (d_i < 0) and (d_j > 0):
                  quadrant[3] += 1
          elif d_i == 0:
              # Knowing where the other end of the morphism is
              # and which way it goes, we now have to decide
              # which quadrant is now the upper one and which is
              # the lower one.
              if d_j > 0:
                  if goes_out:
                      upper_quadrant = 0
                      lower_quadrant = 3
                  else:
                      upper_quadrant = 3
                      lower_quadrant = 0
              else:
                  if goes_out:
                      upper_quadrant = 2
                      lower_quadrant = 1
                  else:
                      upper_quadrant = 1
                      lower_quadrant = 2
  
              if m_curving:
                  if m_curving == "^":
                      quadrant[upper_quadrant] += 1
                  elif m_curving == "_":
                      quadrant[lower_quadrant] += 1
              else:
                  # This morphism counts in both upper and lower
                  # quadrants.
                  quadrant[upper_quadrant] += 1
                  quadrant[lower_quadrant] += 1
          elif d_j == 0:
              # Knowing where the other end of the morphism is
              # and which way it goes, we now have to decide
              # which quadrant is now the left one and which is
              # the right one.
              if d_i < 0:
                  if goes_out:
                      left_quadrant = 1
                      right_quadrant = 0
                  else:
                      left_quadrant = 0
                      right_quadrant = 1
              else:
                  if goes_out:
                      left_quadrant = 3
                      right_quadrant = 2
                  else:
                      left_quadrant = 2
                      right_quadrant = 3
  
              if m_curving:
                  if m_curving == "^":
                      quadrant[left_quadrant] += 1
                  elif m_curving == "_":
                      quadrant[right_quadrant] += 1
              else:
                  # This morphism counts in both upper and lower
                  # quadrants.
                  quadrant[left_quadrant] += 1
                  quadrant[right_quadrant] += 1
  
      # Pick the freest quadrant to curve our morphism into.
      freest_quadrant = 0
      for i in range(4):
          if quadrant[i] < quadrant[freest_quadrant]:
              freest_quadrant = i
  
      # Now set up proper looping.
      (looping_start, looping_end) = [
          ("r", "u"),
          ("u", "l"),
          ("l", "d"),
          ("d", "r"),
      ][freest_quadrant]
  
      return (curving, label_pos, looping_start, looping_end)
  
imports: from sympy.categories import CompositeMorphism, IdentityMorphism, NamedMorphism, Diagram
  from sympy.core import Dict, Symbol, default_sort_key
  from sympy.printing.latex import latex
  from sympy.sets import FiniteSet
  from sympy.utilities.iterables import iterable
  from sympy.utilities.decorator import doctest_depends_on
  from itertools import chain
label_desc: Use image node_id 2 for calling the XypicDiagramDrawer obj's underlying member method code with example usage: obj._process_loop_morphism(i, j, grid, morphisms_str_info, object_coords) and returns: curving,  label_pos,  looping_start,  looping_end