# Tools and types for representing seams (sets of contacts) between blocks. # Last edited on 2021-03-15 02:51:07 by jstolfi import seam_IMP; from seam_IMP import Seam_IMP import contact import block import move import move_parms import path import pyx class Seam(Seam_IMP): # An object of the {Seam} class represents a /seam/ between two # {Block} ojects: a set of zero or more {Contact} objects between # traces of that occur in those blocks. # # These blocks are the /sides/ of the seam, indexed 0 and 1. The # indexing of sides of the seam must be consistent with the indexing # of sides of its contacts. Namely, for each contact {ct} in a seam # {sm}, the trace that is side {i} of {ct} must be in the block that # is side {i} of {sm} # # A seam may be /directed/, in which case it specifies that some # choice of the block that is side 0 must be executed before the any # choice of the block that is side 1. Unless stated otherwise, the # procedures below do not care whether the {Seam} argument {sm} is # directed or not. # # While an undirected seam with zero contacts has no effect, a # directed one still provides non-trivial information, namely # the ordering constraint on the two blocks. # # A path {ph} is said to /cover/ side {i} of a seam {sm} if {ph} has a # subpath that is a choice of the block that is a side {i} of {sm}. # The path /closes/ the seam if it covers both sides of {sm}. pass def make(bc0, bc1, cts, drc): # Creates and returns a {Seam} object {sm} whose sides 0 and 1 are # the {Block} objects {bc0} and {bc1} (which must be distinct). # # The parameter {cts} must be a list of {Contact} objects. For every # element {ct} of the list, side {i} (0 or 1) of {ct} must be a trace # {mvi} that occurs in at least one choice of the corresponding block # ({bc0} or {bc1}, respectively). # # The {drc} parameter is a bool; if {True}, it specifies that the # seam is drected (meaning that any choice of{bc0} must be executed # before any choice of {bc1}). return seam_IMP.make(bc0, bc1, cts, drc) def get_contacts(sm): # Returns the contacts of {sm}, as a tuple of {Contact} objects. return seam_IMP.get_contacts(sm) def side(sm, i): # The parameter {i} must be 0 or 1. Returns the {Block} # object {bc} that is the trace on side {i} of the seam. return seam_IMP.side(sm, i) def which_side(bc, sm): # The parameter {bc} must be a {Block} object, and {sm} must be a # {Seam} object. If {bc} is one of the sides of {sm}, returns the # index (0 or 1) of that side. Otherwise returns {None}. return seam_IMP.which_side(bc, sm) def is_directed(sm): # Returns {True} if {sm} is a directed seam, {False} otherwise. return seam_IMP.is_directed(sm) def bbox(SMS): # The parameter {CTS} must be a list of {Seam} objects. Returns a # bounding box for all the contacts in all those seams, as a pair of # points {(plo, phi)} that its lower left and upper right corners. If # the list is empty, or all seams have zero contacts, returns {None}. # # The bounding box is the smallest axis-aligned rectangle that # contains endpoints (only) of all those contacts. Note that # decorations such as tics and arrowheads, as well as bits of the # contact lines, may extend outside the box. Moreover, the traces that # are the sides of those contacts are NOT included in the result. return seam_IMP.bbox(SMS) # TIMING ESTIMATORS # def min_max_tcool(sm, mp_jump): # # The parameter {sm} must be a {Seam} object. Let its sides # # be the {block} objects {bc0} and {bc1}. # # # # The procedure considers every pair of choices {oph0} of {bc0} and # # {oph1} of {bc1}. If the seam {sm} is not directed, it also # # considers every pair of choices {oph0} from {bc1} and {oph1} from # # {bc0}. # # # # For each pair, the procedure computes {tc = # # contact.pair_max_tcool(oph0,tconn,oph1,CTS)} where {CTS} is the set of # # trace-trace contacts listed in {sm}, and {tconn} is the extra cost # # of the connector betwee the two paths, if needed (including any # # applicable trace-jump transition penalties). # # # # The procedure then returns the minimum value of {tc} over all pairs # # of choices {oph0,oph1}. In particular, if there is some pair # # {oph0,oph1} that fails to close every contact of {sm}, the # # procedure returns {-inf}. # # # # The procedure assumes that the connector between {oph0} and {oph1}, # # if necessary, would be either a jump with parameters {mp_jump}, or # # a link with same parameters as the connected moves, as determined by # # {move.connector} with {use_jumps=use_links=False}. # return seam_IMP.pair_max_tcool(bc0, tconn, bc1, CTS) def print_contact_table(wr, BCS, SMS, MVS): # Given a list {BCS} of {Block} objects and a list {SMS} of {Seam} # objects, prints a table that shows which choice of which block contains # each side of each contact in each seam. # # Each row of the table starts with "C{I}:{K}:{S} {D}" to mean side {S} (0 # or 1) of contact with index {K} in the seam {SMS[I]). the field {D} is 0 if {sm} is # undirected, 1 if directed. Each column is headed with "B{J}:{P}" to # mean choice {P} of block {bc = BCS[J]}. The entry in that row and # column is the index of the trace {contact.side(ct,S)} (or its # reverse) among the moves of the path; or "---" if the trace does not # occur in that path. # # If {MVS} is not {None}, it must be a list of {Move} objects. In that case, # for each contact side, the index {L} of the move in that list is printed # as "M{L}" at the start of each row. return block_IMP.print_seam_table(wr, BCS, CTS, MVS) # PLOTTING def plot_to_files(fname, SMS, clr, BCS, CLRS, wd_axes, matter): # The arguments {SMS}, {BCS}, and {CLRS} must be lists or tuples of # {Seam} objects, {Block} objects, and {pyx.color.rgb} objects, # respectively. # # The procedure plots that information using {plot_standard} below, # with the given parameters and a default choice for the path style # (trace axes, dots, and arrows). As explained for that procedure, # the plot will be a vertical stack of sub-plots. Each sub-plot will # have its own millimeter grid in the background. # # The procedure then writes the whole plot to files "{fname}.{ext}" # where {ext} is "eps", "png", and "jpg". seam_IMP.plot_to_files(fname, SMS, clr, BCS, CLRS, wd_axes, matter) def plot_standard \ ( c, SMS, dp, clr, wd_line, BCS, CLRS, wd_axes, axes, dots, arrows, matter, grid ): # The arguments {SMS}, {BCS}, and {CLRS} must be lists or tuples of # {Seam} objects, {Block} objects, and {pyx.color.rgb} objects, # respectively. The list {BCS} must include all the blocks that are # sides of the seams of {SMS}. # # The procedure plots selected choices (alternative paths) of each # block of {BCS} usng {path.plot_standard}, and each contact {ct} of # each seam {sm} of {SMS} with {contact.plot_single}, using the color # {clr} and lines of width {wd_line}. It will draw a transversal # arrowhead over the midpoint of {ct} if the seam is directed, or just # a tic otherwise. # # More precisely, the plot will be a stack of several sub-plots. In # each sub-plot, every block {BCS[i]} will be drawn as its matter # shadow if {matter} is true. In any case, one of its choices (one of # its oriented paths) may then be drawn too. If two blocks {BCS[i]} # and {BCS[j]} are represented in a sub-plot by their choices {ophi} # and {ophj}, then, for every seam {sm} that has those two blocks as # sides, in either order, the procedure will draw every contact {ct} # of {sm} whose sides are moves of {ophi} and {ophj}, in either order. # # Trace axes and jump lines will be drawn with line width {wd_axes}. # If {grid} is true, a millimeter grid and a frame will be drawn on # the background of each sub-plot. # # If {dp} is not {None}, it must be a 2-vector (pair of floats), and # all elements of the bottom-most sub-plot will be displaced by {dp}. # In any case, successive sub-plots will be displaced vertically by # a fixed distance sufficient to avoid overlaps. # # If {CLRS} has a single element, the procedure uses that color for # all trace sausages of all blocks. If {CLRS} is {None}, uses some # default color, Otherwise {CLRS} must have the same length as {BCS}, # and each entry will be used to plot the traces of any choices of the # corresponding block. # # The decision of which block choices to show in each sub-plot is # complex and partially arbitrary. However, there will be enough # sub-plots for all contacts of all seams of {SMS} to be drawn. Block # choices that are not necessary for this goal will not be plotted; in # particular, if the choices of a block include a path and its # reversal, only one of them will appear. A block choice may be # omitted also if the traces that involve contacts of {SMS} are shared # with other choices that have been plotted and used to show those # contacts. Conversely, the same choice of a block {BCS[i]} may be # plotted several times, if necessary to show all contacts. # seam_IMP.plot_standard \ ( c, SMS, dp, clr, wd_line, BCS, CLRS, wd_axes, axes, dots, arrows, matter, grid ) def plot_single(c, sm, dp, clr, oph0, oph1, wd_line): # Plots on the {pyx} context {c} the contacts of the seam {sm} that # have their sides 0 and 1 on the oriented paths {oph0} and {oph1}, # respectively, using {contact.plot_single}. The paths are assumed to # have been drawn by the caller. The plots will all be displaced by # the vector {dp} (a 2-tuple of floats). # # If the seam is directed, an arrowhead with size proportional to # {wd_line} will be drawn perpendicular to each contact line at its # midpoint, pointing from its side 0 towards its side 1. seam_IMP.plot_sngle(c, sm, dp, wd, clr, oph0, oph1, sz_tic) # PRINTING AND DEBUGGING def show(wr, sm): # Writes the data of {sm} to {wr} in a human-readable format. seam_IMP.show(wr, sm) # FIELDS AND TOOLS FOR THE HEURISTIC # In addition to the read-only fields above, each {Seam} object also has # some mutable fields that are used during the {hotpath.best_path} # algorithm. They are handled by the procedures in this section. # # These attributes are not defined automatically. Their initial values # are undefined and must be properly defined by the {set_*} procedures # below, just before or during the heuristic. def get_coverage_status(sm): # Returns /coverage status/ of the {Seam} object {sm}. This is a list # {iscov} of two bools, such that {iscov[i]} is true if and only if # some choice of the block {bc = side(sm,i)} has been included in the # current tentative path {Q}. return seam_IMP.get_coverage_status(sm) def set_coverage_status(sm, iscov): # Sets the coverage status of {sm} to {iscov}, which should be a list # of two bools. seam_IMP.set_coverage_status(sm, iscov) # def get_paths(sm): # # Returns the /intial candidate paths/ of the {Seam} object {sm}. # # This is a pair {ophss} of lists of oriented paths, such that {ophss[i]} has # # all choices of all blocks in the initial input list {BCS} # # that contain the block {bc = side(sm,i)}. # return seam_IMP.get_paths(sm) # # def set_paths(sm, ophss): # # Sets the initial candidate paths of {sm} to {ophss}, which should be a list # # of two lists of oriented paths. See {get_paths}. # seam_IMP.set_paths(sm, ophss) #