# Tools and types for representing contacts between traces. # Last edited on 2021-02-19 14:20:55 by jstolfi import contact_IMP; from contact_IMP import Contact_IMP import move import path import pyx class Contact(Contact_IMP): # An object of the {Contact} class represents a /contact/, a line # segment (possibly a single point) on the boundary of two distinct # traces, that must become a physical weld after both are extruded. # These traces are the /sides/ of the contact, arbitrarily indexed 0 # and 1. # # A {Contact} object {ct} also has two /endpoints/. Their order is # arbitrary. Each endpoint is a list of 2 float coordinates, in # millimeters. # # A contact {ct} is /covered/ by a move {mv} if {mv} is one of its # sides. # # Acontact {ct} is /covered/ a path {ph} if at least one of its sides # is an element of {ph}, and is /closed/ by {ph} if both sides are. pass def make(p0, p1, mv0, mv1, parms): # Creates and returns a {Contact} object {ct} with endpoints {(p0,p1)} # whose sides are the traces {mv0} and {mv1} -- which must be (unoriented) # {move.Move} objects, and should not be jumps. # # The {parms} field is used to compute the cover times # for the contact by each trace. See the {covtimes} function below. return contact_IMP.make(p0, p1, mv0, mv1, parms) def side(ct, i): # The parameter {i} must be 0 or 1. Returns the (unoriented) {Move} # object {mv} that is the trace on side {i} of the contact. return contact_IMP.side(ct, i) def which_side(mv, ct): # The parameter {mv} must be a {Move} object, and {ct} must be a # {Contact} object. If {mv} is one of the sides of {ct}, returns the # index (0 or 1) of that side. Otherwise returns {None}. return contact_IMP.which_side(mv, ct) def endpoints(ct): # Returns the endpoints of {ct}, as a pair of points, in no # particular order. return contact_IMP.endpoints(ct) def pmid(ct): # Retuns the midpoint of the contact {ct}. return contact_IMP.pmid(ct) def tcov(ct, i): # The parameter {i} must be 0 or 1. Returns the (precomputed) time # that the nozzle will take to move from the starting point of the move # {side(ct,i)} to the point on the move axis that is closest to # {pmid(ct)}. return contact_IMP.tcov(ct, i) def covindices(oph, ct): # Returns a pair {ixs} of indices {k} such that the move # {path.elem(oph,k)} is {side(ct,i)}, in either orientation. If the # move {side(ct,i)} does not occur in {oph}, {ixs[i]} is {None}. # # The parameter {oph} may be just an unoriented {Path} object, which # is taken in its native orientation. Note that the indices are # different for {oph} and for {path.rev(oph)} return contact_IMP.covindices(oph, ct) def covtimes(oph, ct): # The parameter {oph} must be an oriented path and {ct} must be a # {Contact} object. Returns a pair {tcs} of floats, where {tcs[i]} is # the time when the nozzle executes the move {side(ct,i)} and and # passes next to the midpoint {m} of the contact {ct} -- specifically, # on the point that is closest to {m} on the axis of that trace. If # the path {oph} does not include the move {side(ct,i)}, then {tcs[i]} # will be {None}. # # The times are counted from the beginning of the execution of the # oriented path {oph}, assuming that it is executed in the direction # specified. return contact_IMP.covtimes(oph, ct) def tcool(oph, ct): # The parameter {oph} must be an oriented path and {ct} must be a # {Contact} object. Returns the cooling time of the contact {ct} if the # slice was executed using the path {oph}; or {None} if that path does # not cover both sides of {ct} # # Namely, if {covtimes(oph, ct)} is {(t0,t1)}, returns {abs(t0-t1)}, # or {None} if either of those times is {None}. return contact_IMP.tcool(oph, ct) # TOOLS FOR LISTS OF CONTACTS def max_tcool(oph, CS): # The parameter {oph} must be an oriented path and {CS} must be a # list or tuple of {Contact} objects. Returns the maximum # value of {tcool(oph,ct)} over every contact {ct} of {CS} # that has been closed by path {oph}. If there are # no such contacts, returns {-math.inf}. return contact_IMP.max_tcool(oph, CS) def min_tcov(oph, CS): # The parameter {oph} must be an oriented path and {CS} must be a list # or tuple of {Contact} objects. Returns the minimum of {tcov(ct,i)} # over every contact {ct} of {CS} that has {side(ct,i)} in {oph} and # {side(cr,1-i)} not in {oph}. If there are no such contacts, returns # {+math.inf}. return contact_IMP.min_tcov(oph, CS) # TOOLS FOR CONTACTS BETWEEN BLOCKS def block_pair_min_tcool(bc0, bc1, ct, parms): # Returns the minimum cooling time of the contact {ct} that can be obtained # if a choice of block {bc0} is immediately followed by a choice of block {bc1} # in a tool-path. # # The procedure considers only pairs of choices of both blocks that cover both # sides of {ct}, one each. If either block has no choice that covers {tc}, # returs {+inf}. Fails if either block has a choice that covers both sides. # return contact_IMP.block_pair_min_tcool(bc0, bc1, ct, parms) def blocks_min_tcool(BS, ct, parms): # Finds the two blocks {bc0} and {bc1} that contain the two sides of # the contact {ct}. Returns {block_pair_min_tcool(bc0, bc1, ct, pair)}. # # Those blocks, if they exist, must be unique and distinct. # If only one of them exists, the procedure returns {+inf}. # If neither of them exists, the procedure fails with error # (the contact is not relevant). return contact_IMP.blocks_min_tcool(BS, ct, parms) # PLOTTING def plot(c, ct, dp, wd, clr): # Plots the contact {ct} on the {pyx} context {c}, # as a solid line segment of width {wd} and color {clr}, # with round caps. The plot will be displaced by the vector {dp} # (a 2-tuple of floats). contact_IMP.plot(c, ct, dp, wd, clr) # PRINTING AND DEBUGGING def show(wr, ct): # Writes {ct} on {wr} in a human-readable format. contact_IMP.show(wr, ct)