# Implementation of module {input_data} # Last edited on 2021-02-19 14:30:36 by jstolfi import input_data import block import path import move import contact import example_path import example_block import hacks import rn import pyx import sys from math import sqrt, sin, cos, floor, ceil, inf, nan, pi def multi_raster_rivers(nrd, nbc, nmv, wdf, parms): xsz_rd = 4*wdf # Length of rasters on the rivers. xsz_gp = 3*wdf # Width of gap between rivers. xstep = xsz_rd + xsz_gp # {X} step between roads. ystep = wdf # {Y} step between scan-lines. org = (2,2) # Lower left corner of leftmost road. def make_single_raster_block(imv, ird0, ird1): # Returns a block that is a raster line on scan-line number {imv}, # spanning roads from {ird0} to {ird1}, in both orientations. xlo = org[0]+ ird0*xstep xhi = xlo + (ird1-ird0)*xstep + xsz_rd y = org[1] + imv*wdf bc = example_block.single_raster(xlo, xhi, y, wdf, parms) return bc def make_serpentine_raster_block(imv0, imv1, ird0, ird1): # Returns a block that is a serpentine path comprising raster traces # on scanlines from number {imv0} to number {imv1} # spanning roads from {ird0} to {ird1}, in the four orders and # orientations. xlo = org[0]+ ird0*xstep xhi = xlo + (ird1-ird0)*xstep + xsz_rd y = org[1] + imv0*wdf nr = imv1 - imv0 + 1 bc = example_block.raster_rectangle(xlo, xhi, y, wdf, nr, parms) return bc H0 = make_single_raster_block(-1, 0, nrd-1) H1 = make_single_raster_block(nbc*nmv, 0, nrd-1) BS = [ H0, H1 ] CS = [] for ird in range(nrd): bc_prev = H0 for ibc in range(nbc): imv0 = ibc*nmv imv1 = imv0 + nmv - 1 bc_this = make_serpentine_raster_block(imv0, imv1, ird, ird) BS.append(bc_this) ct = example_block.raster_raster_contact(bc_prev, bc_this, parms) CS.append(ct) bc_prev = bc_this ct = example_block.raster_raster_contact(bc_prev, H1, parms) CS.append(ct) return BS, CS # ---------------------------------------------------------------------- def two_roads_and_islands(nmv, nmg, nis, wdc, wdf, parms): org = (2,2) # Lower left corner of whole thing. # Road and island dimensions do not include the width of traces. xsz_rd = 5*wdf # Length of rasters on the rivers. xsz_gp = 6*wdf # Width of gap between rivers. xstep_rd = xsz_rd + xsz_gp # {X} step between roads. ystep_sc = wdf # {Y} step between scan-lines. # Parameters of islands and island groups: Ric = wdf + wdc # Radius of endpoints in island contour. xstep_is = xstep_rd # X spacing between centers of islands. ystep_is = 2*Ric + wdc + wdf # Y spacing between centers of islands. xorg_is = Ric + 0.5*wdc # X of centers of left island group, rel {org}. xorg_rd = xorg_is + 0.5*xsz_gp # X of left endpoints of left road. ytot_rd = (nmv-1)*wdf # Total height of roads. ytot_is = (nis-1)*ystep_is + 2*Ric + wdc # Total height of islands yctr = 0.5*max(ytot_rd, ytot_is) # Y of center of stuff rel to {org}. yorg_rd = max(0, yctr - 0.5*ytot_rd) # Y of bottom road yorg_is = max(0, yctr - 0.5*ytot_is) + 0.5*wdc + Ric # Y of center of bottom island. def make_raster_block(imv0, imv1, ird): # Returns a block that is a serpentine raster path (or a single raster line) # spanning scan-lines with numbers {imv0} to {imv1} in road {ird}. # The block has {ird0=ird1} are equal, the block is a single raster line. See # {example_block.raster_rectangle}. xlo = org[0] + xorg_rd + ird*xstep_rd xhi = xlo + xsz_rd ylo = org[1] + yorg_rd + imv0*wdf nr = imv1 - imv0 + 1 bc = example_block.raster_rectangle(xlo, xhi, ylo, wdf, nr, parms) return bc def make_island_block(igr, iis): # Returns block that is a single island, specifically # islans {iis} from bottom to top in group {igr} from left to right. # See {example_block.onion}. dx0 = xorg_is + igr*xstep_is dy0 = yorg_is + iis*ystep_is ctr = rn.add(org, (dx0, dy0)) if igr == 0: phase = (0 if nis == 1 else pi/4 - pi/2*(iis/(nis-1))) elif igr == 1: phase = pi/2 if iis < nis//2 else -pi/2 elif igr == 2: phase = (pi if nis == 1 else 3*pi/4 + pi/2*(iis/(nis-1))) else: assert False bc = example_block.onion(ctr, Ric, wdc, 0, wdf, phase, parms) return bc BS = [] CS = [] # Compute the number {nbc} of multiraster blocks at each end of a road: nbc = ((nmv - 1)//(2*nmg)) assert nbc >= 0 # Total number of blocks in each road: nbt = 2*nbc + (nmv - 2*nbc*nmg) for ird in range(2): # Blocks in road {ird}: bc_prev = None # Previous block in same road. imv1 = -1 # Scanline index of top of previous block. for ib in range(nbt): # Decide san line span for block {ib: imv0 = imv1 + 1 # Decide number of raster traces in this block: nmvi = nmg if ib < nbc or ib >= nbt - nbc else 1 imv1 = imv0 + nmvi - 1 assert imv0 <= imv1 and imv1 < nmv bc_this = make_raster_block(imv0, imv1, ird) BS.append(bc_this) if bc_prev != None: ct = example_block.raster_raster_contact(bc_prev, bc_this, parms) CS.append(ct) bc_prev = bc_this assert imv1 == nmv - 1 # Add island blocks: for igr in range(3): for iis in range(nis): bc_this = make_island_block(igr, iis) BS.append(bc_this) return BS, CS # ---------------------------------------------------------------------- def plot(fname, BS, CS, o, wd): ncolors = 17 colors = hacks.colors_RGB(ncolors) # Colors to use for different layers. # Find the max number of choices in each block: npmax = max(block.nchoices(bc) for bc in BS) # Find the bounding box of all blocks: BSbox = rn.box_from_point(o) for bc in BS: bcbox = block.bbox(bc) BSbox = rn.box_join(BSbox, bcbox) plo = (floor(BSbox[0][0])-1, floor(BSbox[0][1])-1) phi = (ceil(BSbox[1][0])+1, ceil(BSbox[1][1])+1) szx = phi[0] - plo[0] szy = phi[1] - plo[1] # Plot one choice from each block: waxes = 0.05*wd wconts = 0.15*wd; cconts = pyx.color.rgb( 0.900, 0.100, 0.000 ) # Contacts. wstart = 5*waxes; cstart = pyx.color.rgb( 0.800, 0.200, 0.000 ) # Initial nozzle position. for ip in range(npmax): ic = ip % ncolors ctraces = colors[ic] c = pyx.canvas.canvas() hacks.plot_grid(c, None, 0.03, plo, szx, szy, 0.20, 1, 1) plot_choice(c, BS, ip, ctraces, waxes, CS, cconts, wconts) hacks.plot_line(c, cstart, wstart, o, o) hacks.write_plot(c, "%s_ip%02d" % (fname,ip)) return # ---------------------------------------------------------------------- def plot_choice(c, BS, ip, ctraces, waxes, CS, cconts, wconts): if len(BS) == 0: # Nothing to plot: assert len(CS) == 0 return # Plots the blocks: for bc in BS: np = block.nchoices(bc) if ip >= np: # Show only the ghost: oph = block.choice(bc,0) layer = 0 # Only the "matter" layer. else: # Plot the choice {ip}: oph = block.choice(bc,ip) layer = None # All layers. path.plot_standard \ ( c, oph, None, layer, ctraces, waxes, axes = True, dots = True, arrows = True, matter = True ) # plot the contacts: for ct in CS: # Decide whether the contact is relevant for choice {ip} of some block: relevant = False for bc in BS: if relevant: break np = block.nchoices(bc) if ip < np: oph = block.choice(bc, ip) ixs = contact.covindices(oph, ct) if ixs[0] != None or ixs[1] != None: relevant = True if relevant: # Plot the contact in a contrasting color: contact.plot(c, ct, None, wd = wconts, clr = cconts) return # ----------------------------------------------------------------------