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: For all straight morphisms which form the visual boundary of
  the laid out diagram, puts their labels on their outer sides.
docstr_tok: ["For","all","straight","morphisms","which","form","the","visual","boundary","of","the","laid","out","diagram",",","puts","their","labels","on","their","outer","sides","."]
code: def _push_labels_out(self, morphisms_str_info, grid, object_coords):
      """
      For all straight morphisms which form the visual boundary of
      the laid out diagram, puts their labels on their outer sides.
      """
  
      def set_label_position(
          free1, free2, pos1, pos2, backwards, m_str_info
      ):
          """
          Given the information about room available to one side and
          to the other side of a morphism (``free1`` and ``free2``),
          sets the position of the morphism label in such a way that
          it is on the freer side.  This latter operations involves
          choice between ``pos1`` and ``pos2``, taking ``backwards``
          in consideration.
  
          Thus this function will do nothing if either both ``free1
          == True`` and ``free2 == True`` or both ``free1 == False``
          and ``free2 == False``.  In either case, choosing one side
          over the other presents no advantage.
          """
          if backwards:
              (pos1, pos2) = (pos2, pos1)
  
          if free1 and not free2:
              m_str_info.label_position = pos1
          elif free2 and not free1:
              m_str_info.label_position = pos2
  
      for m, m_str_info in morphisms_str_info.items():
          if m_str_info.curving or m_str_info.forced_label_position:
              # This is either a curved morphism, and curved
              # morphisms have other magic, or the position of this
              # label has already been fixed.
              continue
  
          if m.domain == m.codomain:
              # This is a loop morphism, their labels, again have a
              # different magic.
              continue
  
          (dom_i, dom_j) = object_coords[m.domain]
          (cod_i, cod_j) = object_coords[m.codomain]
  
          if dom_i == cod_i:
              # Horizontal morphism.
              (
                  free_up,
                  free_down,
                  backwards,
              ) = XypicDiagramDrawer._check_free_space_horizontal(
                  dom_i, dom_j, cod_j, grid
              )
  
              set_label_position(
                  free_up, free_down, "^", "_", backwards, m_str_info
              )
          elif dom_j == cod_j:
              # Vertical morphism.
              (
                  free_left,
                  free_right,
                  backwards,
              ) = XypicDiagramDrawer._check_free_space_vertical(
                  dom_i, cod_i, dom_j, grid
              )
  
              set_label_position(
                  free_left, free_right, "_", "^", backwards, m_str_info
              )
          else:
              # A diagonal morphism.
              (
                  free_up,
                  free_down,
                  backwards,
              ) = XypicDiagramDrawer._check_free_space_diagonal(
                  dom_i, cod_i, dom_j, cod_j, grid
              )
  
              set_label_position(
                  free_up, free_down, "^", "_", backwards, m_str_info
              )
  
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 9 for calling the XypicDiagramDrawer obj's underlying member method code with example usage: obj._push_labels_out(morphisms_str_info, grid, object_coords) without return types