class_docstr: Implements a document builder
  
  Args:
  ----
      resolve_lines: whether words should be automatically grouped into lines
      resolve_blocks: whether lines should be automatically grouped into blocks
      paragraph_break: relative length of the minimum space separating paragraphs
      export_as_straight_boxes: if True, force straight boxes in the export (fit a rectangle
          box to all rotated boxes). Else, keep the boxes format unchanged, no matter what it is.
class_docstr_tok: ["Implements","a","document","builder","Args",":","--","--","resolve_lines",":","whether","words","should","be","automatically","grouped","into","lines","resolve_blocks",":","whether","lines","should","be","automatically","grouped","into","blocks","paragraph_break",":","relative","length","of","the","minimum","space","separating","paragraphs","export_as_straight_boxes",":","if","True",",","force","straight","boxes","in","the","export","(","fit","a","rectangle","box","to","all","rotated","boxes",")",".","Else",",","keep","the","boxes","format","unchanged",",","no","matter","what","it","is","."]
docstr: Order boxes to group them in lines
  
  Args:
  ----
      boxes: bounding boxes of shape (N, 4) or (N, 4, 2) in case of rotated bbox
  
  Returns:
  -------
      nested list of box indices
docstr_tok: ["Order","boxes","to","group","them","in","lines","Args",":","--","--","boxes",":","bounding","boxes","of","shape","(","N",",","4",")","or","(","N",",","4",",","2",")","in","case","of","rotated","bbox","Returns",":","--","--","--","-","nested","list","of","box","indices"]
code: def _resolve_lines(self, boxes: np.ndarray) -> List[List[int]]:
      """Order boxes to group them in lines
  
      Args:
      ----
          boxes: bounding boxes of shape (N, 4) or (N, 4, 2) in case of rotated bbox
  
      Returns:
      -------
          nested list of box indices
      """
      # Sort boxes, and straighten the boxes if they are rotated
      idxs, boxes = self._sort_boxes(boxes)
  
      # Compute median for boxes heights
      y_med = np.median(boxes[:, 3] - boxes[:, 1])
  
      lines = []
      words = [idxs[0]]  # Assign the top-left word to the first line
      # Define a mean y-center for the line
      y_center_sum = boxes[idxs[0]][[1, 3]].mean()
  
      for idx in idxs[1:]:
          vert_break = True
  
          # Compute y_dist
          y_dist = abs(
              boxes[idx][[1, 3]].mean() - y_center_sum / len(words)
          )
          # If y-center of the box is close enough to mean y-center of the line, same line
          if y_dist < y_med / 2:
              vert_break = False
  
          if vert_break:
              # Compute sub-lines (horizontal split)
              lines.extend(self._resolve_sub_lines(boxes, words))
              words = []
              y_center_sum = 0
  
          words.append(idx)
          y_center_sum += boxes[idx][[1, 3]].mean()
  
      # Use the remaining words to form the last(s) line(s)
      if len(words) > 0:
          # Compute sub-lines (horizontal split)
          lines.extend(self._resolve_sub_lines(boxes, words))
  
      return lines
  
file_path: doctr/doctr/models/builder.py
imports: from typing import Any, Dict, List, Optional, Tuple
  import numpy
  from scipy.cluster.hierarchy import fclusterdata
  from doctr.io.elements import Block, Document, KIEDocument, KIEPage, Line, Page, Prediction, Word
  from doctr.utils.geometry import estimate_page_angle, resolve_enclosing_bbox, resolve_enclosing_rbbox, rotate_boxes
  from doctr.utils.repr import NestedObject
label_desc: Use image node_id 4 for calling the DocumentBuilder obj's underlying member method code with example usage: obj._resolve_lines(boxes) and returns: lines