# Procedures to group ratser filling elements in various ways.
# Last edited on 2021-10-15 20:05:48 by stolfi

import raster_regroup_IMP

# The procedures in this module assume that the parameter {OPHS} is a
# list of oriented paths that are elements of a solid raster filling, each
# possibly assigned to a /group/ whose index is accessed through
# {path.get_group}. The procedures split or redefine those groups
# into new groups according to various criteria, and attach the new
# group indices to the paths of {OPHS} with {path.set_group}.
# 
# Each path in {OPHS} should consist of a single raster trace. The paths
# must be pairwise disjoint and all parallel to some unit direction
# vector {xdir}. They must be sorted in /scanline order/, which is
# defined by increasing projection on an axis {ydir} perpendicular to
# {xdir}, ignoring roundoff errors; with ties broken by increasing
# projection along {xdir}. The orientations of the paths and traces are
# ignored by these procedures.
#
# Some of these procedures use the contact-path information provided bt
# {path.get_contacts} and {contact.get_side_paths}, which must be
# consistent with each other and with the list {OPHS}. Namely, for every path {oph} in {OPHS},
# and every contact {ct} in {path.get_contacts(oph,isd)},
# {contact.get_side_path(ct,isd)} is a subset of {OPHS} that has {oph}
# and/or its reverse.  Moreover, for this module, every contact {ct}
# must have side 0 below and side 1 above.
#
# Every procedure that is supposed to change the current group
# assignments, such as {split_at_forks} or {split_by_size}, will
# renumber the new groups with consecutive integers starting at 0, even
# if the partition of {OPHS} into groups remains the same. They return
# the number of new groups created.
#

def merge_all(OPHS):
  # Ignores the current group assignments of the paths in {OPHS}, even
  # those that have group index {None}, and reassigns all to group 0.
  return raster_regroup_IMP.merge_all(OPHS)

def by_contours(OPHS, OCRS):
  # Ignores the current group assignment and instead separates the filler
  # elements from {OPHS} into new groups, one for each contour (closed
  # oriented path) in the list {OCRS}. Each filler will be assigned to
  # the group with index {icr} such that {ocr=OCRS[icr]} is the
  # innermost contour that contains it.
  #
  # It is assumed that all fillers are in the interior of the slice.
  # Therefore, there will not be any filler assigned to a group index
  # {icr} if {icr >= len(OCRS)}, if {OCRS[icr]} is an inner (hole)
  # contour, or if contour {OCRS[icr]} is an outer contour but there is
  # no filler inside it except inside nested contours.
  return raster_regroup_IMP.by_contours(OPHS, OCRS)
  
def split_at_forks(OPHS): 
  # Splits the current groups of fillers from {OPHS} into maximal
  # sub-groups connected by single contacts.
  #
  # Specifically, each of the original groups is split into maximal
  # sub-groups such that, in every sub-group whose fillers are
  # {GPH[0..m-1]}, and for every {iph} in {1..m-1}, there is a contact
  # between {GPH[iph-1]} and {GPH[iph]}, which is the only contact on the
  # upper side of {GPH[iph-1]} and the only contact on the lower side of
  # {GPH[iph]}.
  #
  # Paths of {OPHS} which have group index {None} are not reassigned.
  #
  # This procedure uses the contact-path information provided by
  # {path.get_contacts} and {contact.get_side_paths}, which must be
  # consistent as spcified above.
  return raster_regroup_IMP.split_at_forks(OPHS)
  
def split_by_size(OPHS, ydir, max_lines):
  # Splits each current group, if needed, into sug-groups with at most 
  # {max_lines} whole scan-lines each.  
  #
  # Paths of {OPHS} which have group index {None} are not reassigned.
  #
  # In particular, if {max_lines} is 1, each new group will be contained
  # in a single scan-line. If that operation is applied after
  # {split_at_forks} each group will be a single filler element.
  raster_regroup_IMP.split_by_size(OPHS, ydir, max_lines)
