code: def draw(vertices, edges):
      """Build a DAG and draw it in ASCII.
  
      Args:
          vertices (list): list of graph vertices.
          edges (list): list of graph edges.
  
      Returns:
          str: ASCII representation
  
      Example:
          >>> from dvc.dagascii import draw
          >>> vertices = [1, 2, 3, 4]
          >>> edges = [(1, 2), (2, 3), (2, 4), (1, 4)]
          >>> print(draw(vertices, edges))
          +---+     +---+
          | 3 |     | 4 |
          +---+    *+---+
            *    **   *
            *  **     *
            * *       *
          +---+       *
          | 2 |      *
          +---+     *
               *    *
                *  *
                 **
               +---+
               | 1 |
               +---+
      """
  
      # NOTE: coordinates might me negative, so we need to shift
      # everything to the positive plane before we actually draw it.
      Xs = []  # noqa: N806
      Ys = []  # noqa: N806
  
      sug = _build_sugiyama_layout(vertices, edges)
  
      for vertex in sug.g.sV:
          # NOTE: moving boxes w/2 to the left
          Xs.append(vertex.view.xy[0] - vertex.view.w / 2.0)
          Xs.append(vertex.view.xy[0] + vertex.view.w / 2.0)
          Ys.append(vertex.view.xy[1])
          Ys.append(vertex.view.xy[1] + vertex.view.h)
  
      for edge in sug.g.sE:
          for x, y in edge.view._pts:
              Xs.append(x)
              Ys.append(y)
  
      minx = min(Xs)
      miny = min(Ys)
      maxx = max(Xs)
      maxy = max(Ys)
  
      canvas_cols = (
          int(math.ceil(math.ceil(maxx) - math.floor(minx))) + 1
      )
      canvas_lines = int(round(maxy - miny))
  
      canvas = AsciiCanvas(canvas_cols, canvas_lines)
  
      # NOTE: first draw edges so that node boxes could overwrite them
      for edge in sug.g.sE:
          assert len(edge.view._pts) > 1
          for index in range(1, len(edge.view._pts)):
              start = edge.view._pts[index - 1]
              end = edge.view._pts[index]
  
              start_x = int(round(start[0] - minx))
              start_y = int(round(start[1] - miny))
              end_x = int(round(end[0] - minx))
              end_y = int(round(end[1] - miny))
  
              assert start_x >= 0
              assert start_y >= 0
              assert end_x >= 0
              assert end_y >= 0
  
              canvas.line(start_x, start_y, end_x, end_y, "*")
  
      for vertex in sug.g.sV:
          # NOTE: moving boxes w/2 to the left
          x = vertex.view.xy[0] - vertex.view.w / 2.0
          y = vertex.view.xy[1]
  
          canvas.box(
              int(round(x - minx)),
              int(round(y - miny)),
              vertex.view.w,
              vertex.view.h,
          )
  
          canvas.text(
              int(round(x - minx)) + 1,
              int(round(y - miny)) + 1,
              vertex.data,
          )
  
      return canvas.draw()
  
file_path: dvc/dvc/dagascii.py
imports: import math
  import os
  from grandalf.graphs import Edge, Graph, Vertex
  from grandalf.layouts import SugiyamaLayout
  from grandalf.routing import EdgeViewer, route_with_lines
  from dvc.log import logger
label_desc: Use image node_id 2 for calling a global function with example usage: draw(vertices, edges) and returns: canvas