# Implementation of module {contact} # Last edited on 2021-02-18 21:22:02 by jstolfi import contact import move import path import block import hacks import rn import pyx from math import nan, inf, sqrt import sys class Contact_IMP: # The endpoints of the contact are {ct.pt[0]} and {ct.pt[1]}, in # arbitrary order. # # The contact is between traces {ct.mv[0]} and {ct.mv[1]}, # two distinct unoriented {Move} objects that are traces (not jumps). # # The field {ct.tcov} is a pair (2-tuple) of times, such # that {ct.tcov[i]} is the time for the nozzle to go past the midpont of # the contact when tracing the move {ct.mv[i]} in its native direction. def __init__(self, p0, p1, mv0, tc0, mv1, tc1): self.pt = (p0, p1) self.mv = (mv0, mv1) self.tcov = (tc0, tc1) def make(p0, p1, mv0, mv1, parms): assert hacks.is_point(p0) assert hacks.is_point(p1) assert isinstance(mv0, move.Move) and not move.is_jump(mv0) assert isinstance(mv1, move.Move) and not move.is_jump(mv1) assert mv0 != mv1 m = rn.mix(0.5, p0, 0.5, p1) tc0 = move.cover_time(mv0, m, parms) tc1 = move.cover_time(mv1, m, parms) return contact.Contact(p0, p1, mv0, tc0, mv1, tc1) def endpoints(ct): assert isinstance(ct, contact.Contact) return ct.pt def pmid(ct): return rn.mix(0.5, ct.pt[0], 0.5, ct.pt[1]) def side(ct, i): return ct.mv[i] def tcov(ct, i): return ct.tcov[i] def which_side(mv, ct): assert isinstance(mv, move.Move) for i in range(2): if ct.mv[i] == mv: return i return None def covindices(oph, ct): ixs = [None, None] n = path.nelems(oph) for k in range(n): omv = path.elem(oph, k) mv, dr = move.unpack(omv) for i in range(2): if side(ct, i) == mv: assert ixs[i] == None, "repeated move in path" ixs[i] = k return tuple(ixs) def covtimes(oph, ct): ixs = covindices(oph, ct) assert len(ixs) == 2 tcs = [ None, None ] for i in range(2): k = ixs[i] if k != None: omvk = path.elem(oph, k) mvk, drk = move.unpack(omvk) if drk == 0: tcs[i] = path.tini(oph, k) + ct.tcov[i] else: tcs[i] = path.tfin(oph, k) - ct.tcov[i] return tuple(tcs) def tcool(oph, ct): tcs = covtimes(oph, ct) assert type(tcs) is list or type(tcs) is tuple assert len(tcs) == 2 if tcs[0] != None and tcs[1] != None: return abs(tcs[0] - tcs[1]) else: return None def max_tcool(oph, CS): assert type(CS) is list or type(CS) is tuple tmax = -inf for ct in CS: tc = tcool(oph, ct) if tc != None and tc > tmax: tmax = tc return tmax def min_tcov(oph, CS): assert type(CS) is list or type(CS) is tuple tmin = +inf for ct in CS: tcs = covtimes(oph, ct) if (tcs[0] == None) != (tcs[1] == None): tci = tcs[0] if tcs[0] != None else tcs[1] if tci < tmin: tmin = tci return tmin def block_pair_min_tcool(bc0, bc1, ct, parms): tcmin = +inf # Enumerate choices of {bc0}: for ip0 in range(block.nchoices(bc0)): # Get choice {oph0} and the cooling time {tc0} to the end of it: oph0 = block.choice(bc0, ip0) tcs0 = covtimes(path.rev(oph0), ct) assert tcs0[0] == None or tcs0[1] == None, "contact has both sides on {bc0}" tc0 = tcs0[0] if tcs0[0] != None else tcs0[1] if tc0 != None: p0 = path.pfin(oph0) # Enumerate choices of {bc1}: for ip1 in range(block.nchoices(bc1)): # Get choice {oph1} and the cooling time {tc1} to the end of it: oph1 = block.choice(bc1, ip1) tcs1 = covtimes(oph1, ct) assert tcs1[0] == None or tcs1[1] == None, "contact has both sides on {bc1}" tc1 = tcs1[0] if tcs1[0] != None else tcs1[1] if tc1 != None: # Compute min time for jump or link between them: p1 = path.pini(oph1) dist = rn.dist(p0, p1) tjmp = move.nozzle_travel_time(dist, True, None, parms) # Jump time. tlnk = move.nozzle_travel_time(dist, False, None, parms) # Link time. Assumes possible. # Cooling time for this pair of choices: tc = tc0 + min(tjmp, tlnk) + tc1 if tc < tcmin: tcmin = tc return tcmin # ---------------------------------------------------------------------- def blocks_min_tcool(BS, ct, parms): mv0 = contact.side(ct,0) mv1 = contact.side(ct,1) # Find the two blocks that have {mv0} and {mv1}: bc0 = None bc1 = None for bc in BS: has0 = block.has_move(bc,mv0) has1 = block.has_move(bc,mv1) assert not (has0 and has1), "contact has two sides in same block" if has0: assert bc0 == None, "same move occurs in two different blocks" bc0 = bc if has1: assert bc1 == None, "same move occurs in two different blocks" bc1 = bc if bc0 != None and bc1 != None: break # Now {bc0} and {bc1} are the blocks that contain {mv0} and {mv1}, if any. assert bc0 != None or bc1 != None, "contact is not relevant to blocks of {BS}" if bc0 != None and bc1 != None: assert bc0 != bc1, "contact has two sides on the same block" tcmin = block_pair_min_tcool(bc0, bc1, ct, parms) else: tcmin = +inf return tcmin # ---------------------------------------------------------------------- def plot(c, ct, dp, wd, clr): p = ct.pt[0] q = ct.pt[1] dpq = rn.dist(p,q) peps = 0.01*wd if dpq < 1.0e-6 else 0 # Perturbation for equal points. sty = [ pyx.style.linewidth(wd), pyx.style.linecap.round, clr, ] if dp != None: sty.append(pyx.trafo.translate(dp[0], dp[1])) c.stroke(pyx.path.line(p[0]-peps, p[1]-peps, q[0]+peps, q[1]+peps), sty) def show(wr, ct): wr.write("\ncontact:\n") for i in range(2): pti = ct.pt[i] wr.write(" pt[%d] = ( %6.3f, %.3f )" % (i, pti[0], pti[1])) wr.write("\n") for i in range(2): mvi = ct.mv[i] wr.write(" mv[%d] =" % i) wr.write(" ( %6.3f, %6.3f )" % move.pini(mvi)) wr.write(" --> ( %6.3f, %6.3f )" % move.pfin(mvi)) wr.write(" wd = %6.3f tex = %8.3f\n" % (move.width(mvi), move.extime(mvi)))