# Implementation of module {path} # Last edited on 2021-10-02 13:55:36 by stolfi import path import path_hp import move import move_parms import block import hacks import rn from shapely.geometry import Point from shapely.geometry.polygon import Polygon import pyx import sys from math import nan, inf, sqrt, sin, cos, pi, floor class Path_IMP: # The initial and final points of the {Path} object, in its native # orientation, are {ph.endpoints[0]} and {ph.endpoints[1]}, respectively. # # The list of oriented moves (traces and jumps) that comprise a path # {ph}, in the native order, is {ph.OMVS}. Namely, tracing the oriented # path {(ph,0)} means executing the oriented moves # {ph.OMVS[0],ph.OMVS[1],...,ph.OMVS[nmv-1]}, in this sequence, where # {nmv=len(ph.OMVS)}. Tracing the oriented path {(ph,1)} means executing # {rev(ph.OMVS[nmv-1]),rev(ph.OMVS[nmv-2]),...,rev(ph.OMVS[0])}, in this # sequence. # # The field {ph.cumtex} is a list such that {ph.cumtex[k]} is the # cumulative time to execute moves {ph.OMVS[0]} through {ph.OMVS[k]}, # for each {k} in {0..nmv-1}. These times include the penalty times for # each trace/move or move/trace transition. The transition penalty is # always assumed to be added to the execution time of the jump. # # These times assume that the nozzle is stationary at the beginning # and end of each move, so that {ph.cumtex[k]} is just the sum of the # execution times of the individual moves plus the applicable # transition penalties. def __init__(self, p, q, OMVS, cumtex): # Read-only fields: self.OMVS = OMVS # Ordered list of oriented moves. self.cumtex = cumtex # Cumulative execution time up to each move. self.endpoints = (p, q) # Endpoints of the path. self.cr_children = None # List of contour paths that are immediately contained in {self}. self.cr_parent = None # Contour path that imediately conatains {self}. self.name = None # Mutable fields for the {hotpath} heuristic: self.links = ([], []) # A pair of lists of links that connect to this path. self.contacts = None # A pair of lists of contacts to moves of the path. self.block = None # The owning block of path that is a block choice. self.fill_BCS = None # A list of candidate blocks immediately contained in this contour path. self.group = None # Group of path in a set of filling elements. # ---------------------------------------------------------------------- # ATTRIBUTES def nelems(oph): ph, dr = unpack(oph) return len(ph.OMVS) # ---------------------------------------------------------------------- def elem(oph, imv): ph, dr = unpack(oph) # sys.stderr.write("oph = %s ph = %s dr = %s\n" % (str(oph), str(ph), str(dr))) if dr == 0: return ph.OMVS[imv] else: nmv = len(ph.OMVS) return move.rev(ph.OMVS[nmv-1-imv]) # ---------------------------------------------------------------------- def find_move(oph, omv): nmv = nelems(oph) mv, dr = move.unpack(omv) for imv in range(nmv): omvk = elem(oph,imv) mvk, drk = move.unpack(omvk) if mvk == mv: return imv return None # ---------------------------------------------------------------------- # GEOMETRY def pini(oph): ph, dr = unpack(oph) return ph.endpoints[dr] # ---------------------------------------------------------------------- def pfin(oph): ph, dr = unpack(oph) return ph.endpoints[1-dr] # ---------------------------------------------------------------------- def endpoints(oph): ph, dr = unpack(oph) if dr == 0: return tuple(ph.endpoints) else: return (ph.endpoints[1], ph.endpoints[0]) # ---------------------------------------------------------------------- def points(oph): PTS = [ pini(oph) ] for i in range(nelems(oph)): PTS.append(move.pfin(elem(oph, i))) return PTS # ---------------------------------------------------------------------- def bbox(OPHS): B = None for oph in OPHS: ph, dr = unpack(oph) B = rn.box_include_point(B, pini(oph)) # In case the path is empty. B = rn.box_join(B, move.bbox(ph.OMVS)) return B # ---------------------------------------------------------------------- def find_nearest_point(oph, pt): nmv = nelems(oph) if nmv > 0 and tuple(pini(oph)) == tuple(pfin(oph)): iMin = None dMin = None else: iMin = nmv dMin = rn.dist(pt, pfin(oph)) for i in range(nmv): q = move.pini(elem(oph, i)) d = rn.dist(pt, q) if dMin == None or d < dMin: iMin = i dMin = d return iMin, dMin # ---------------------------------------------------------------------- def mean_projections(oph, xdir, ydir): p = pini(oph) q = pfin(oph) if xdir != None: xp = rn.dot(p, xdir) xq = rn.dot(q, xdir) xm = (xp + xq)/2 else: xm = None if ydir != None: yp = rn.dot(p, ydir) yq = rn.dot(q, ydir) ym = (yp + yq)/2 else: ym = None return ( xm, ym ) # ---------------------------------------------------------------------- # CREATION def make_empty(p): return path.Path(p, p, (), ()) # ---------------------------------------------------------------------- def from_move(p, q, mp): mv = move.make(p, q, mp) return path.Path(p, q, ( mv, ), ( move.fabtime(mv), ) ) # ---------------------------------------------------------------------- def from_moves(omvs): assert type(omvs) is list or type(omvs) is tuple nmv = len(omvs) # Number of moves. assert nmv >= 1, "must have at least one move" p = move.pini(omvs[0]) # Initial point of final path. q = move.pfin(omvs[nmv-1]) # Final point of final path. p_prev = p t_prev = 0 cumtex = [] for imv in range(nmv): omvk = omvs[imv] mvk, drk = move.unpack(omvk) # For type checking. pk, qk = move.endpoints(omvk) assert pk == p_prev, "moves are not connected: %s %s" % (str(p_prev),str(pk)) tfink = t_prev + move.fabtime(omvk) if move.is_jump(omvk): udk = move.ud_penalty(omvk) # Add the applicable move/jump transition penalties: if imv > 0 and not move.is_jump(omvs[imv-1]): tfink += udk if imv < nmv-1 and not move.is_jump(omvs[imv+1]): tfink += udk cumtex.append(tfink) p_prev = qk t_prev = tfink assert p_prev == q return path.Path(p, q, tuple(omvs), tuple(cumtex)) # ---------------------------------------------------------------------- def from_points(pts, mp_trace, mp_jump): assert type(pts) is list or type(pts) is tuple m = len(pts) # Number of points or lists. assert m >= 1, "must have at least one point" p_prev = None omvs = [] cumtex = [] jmp = None # True if previous element of {pts} was list, false if it was point. for ptj in pts: if hacks.is_point(ptj): # Append a single move with the given width: if p_prev != None: mv = move.make(p_prev, ptj, mp_trace) omvs.append(mv) p_prev = ptj jmp = False else: # {ptj} must be a list of points. Appends a sequence of moves from {p_prev} # to those points. The first move, if any, will be a jump, the rest # will be traces. Then forces a jump to whatever comes next (if any). assert type(ptj) is list or type(ptj) is tuple mpjk = mp_jump # Parameters of of next move. for ptjk in ptj: assert hacks.is_point(ptjk) if p_prev != None: mv = move.make(p_prev, ptjk, mpjk) omvs.append(mv) p_prev = ptjk mpjk = mp_trace mpjk = mp_jump assert p_prev != None # Since there must have been at least one point. if len(omvs) == 0: return make_empty(p_prev) else: return from_moves(omvs) # ---------------------------------------------------------------------- def concat(ophs, use_links, mp_jump): assert type(ophs) is list or type(ophs) is tuple assert type(use_links) is bool assert mp_jump == None or isinstance(mp_jump, move_parms.Move_Parms) m = len(ophs) # Number of paths. assert m >= 1, "must have at least one path" p = pini(ophs[0]) # Initial point of final path. q = pfin(ophs[m-1]) # Final point of final path. oph_prev = None p_prev = None omvs = [] for ophj in ophs: phj, drj = unpack(ophj) # For type checking. nmvj = nelems(ophj) pj = pini(ophj) qj = pfin(ophj) if p_prev != None and p_prev != pj: # Get the connector {olk}, possibly {None}: olk = get_connecting_link(oph_prev, ophj) if use_links else None if olk != None: assert pini(olk) == p_prev assert pfin(olk) == pj # Insert a copy of the link path for k in range(nelems(olk)): omvk = elem(olk,k) assert move.pini(omvk) == p_prev omvs.append(omvk) p_prev = move.pfin(omvk) else: # Create and insert a jump: cnj = move.make(p_prev, pj, mp_jump) omvs.append(cnj) p_prev = pj # Append the moves of {ophj}: for k in range(nmvj): omvk = elem(ophj, k) pk, qk = move.endpoints(omvk) assert p_prev == None or p_prev == pk omvs.append(omvk) p_prev = qk oph_prev = ophj assert p_prev == q # Create a path from all moves: ph = from_moves(omvs) # Copy the initial and final link paths: clear_links(ph) set_links(ph, get_links(ophs[0])) set_links(rev(ph), get_links(rev(ophs[-1]))) return ph # ---------------------------------------------------------------------- def displace(oph, ang, v): nmv = nelems(oph) if (nmv == 0): p = pini(oph) return empty(p) else: omvs = [] for k in range(nmv): omvk = elem(oph, k) mpk = move.parameters(omvk) omvs.append(move.displace(omvk, ang, v, mpk)) return from_moves(omvs) # ---------------------------------------------------------------------- # ORIENTATION def rev(oph): ph, dr = unpack(oph) return (ph, 1-dr) # ---------------------------------------------------------------------- def spin(oph, dr): ph1, dr1 = unpack(oph) return (ph1, (dr1 + dr) % 2) # ---------------------------------------------------------------------- def unpack(oph): # sys.stderr.write("oph = %s\n" % str(oph)) if isinstance(oph, path.Path): # sys.stderr.write("returning %s, 0\n" % str(oph)) return oph, 0 else: assert type(oph) is tuple assert len(oph) == 2 ph, dr = oph # sys.stderr.write("ph = %s dr = %s\n" % (str(ph), str(dr))) assert isinstance(ph, path.Path) assert dr == 0 or dr == 1 return ph, dr # ---------------------------------------------------------------------- def obj_in_list(ph, OPHS): # Returns {True} iff the oriented path {OPHS} has a an element with the {Path} # object {ph}, in any orientationa. for ophi in OPHS: phi, dri = unpack(ophi) if phi == ph: return True return False # ---------------------------------------------------------------------- # TIMING def fabtime(oph): ph, dr = unpack(oph) nmv = len(ph.OMVS) return 0 if nmv == 0 else ph.cumtex[nmv-1] # ---------------------------------------------------------------------- def tini(oph, imv): ph, dr = unpack(oph) nmv = len(ph.OMVS) assert imv >= 0 and imv <= nmv if imv == 0: return 0 if imv == nmv: return fabtime(oph) if dr == 0: # Native direction: return ph.cumtex[imv-1] else: # Reverse direction return ph.cumtex[nmv-1] - ph.cumtex[nmv-1-imv] # ---------------------------------------------------------------------- def tfin(oph, imv): return tini(oph, imv+1) # ---------------------------------------------------------------------- # INCREMENTAL PATH BUILDING def move_to(MVS, p, q, mp, tag): if p != None: mv = move.make(p, q, mp) move.set_name(mv, "M%s%d" % (tag,len(MVS))) MVS.append(mv) return q # ...................................................................... def finish(PHS, MVS, tag): assert len(MVS) != 0 ph = from_moves(MVS) set_name(ph, "P%s" % tag, False) PHS.append(ph) MVS.clear() return # ...................................................................... # LINK PATHS # The list {ph.links[0]} has all link paths that end # at {pini(ph)}. The list {ph.links[1]} has all link paths # that start at {pfin(ph)}. def clear_links(oph): ph, dr = unpack(oph) ph.links = ([],[]) return # ---------------------------------------------------------------------- def add_link(oph, olk): assert tuple(pfin(olk)) == tuple(pini(oph)) ph, dr = unpack(oph) if dr == 0: # The link ends at {pini(ph)}: ph.links[0].append(olk) else: # The link ends at {pfin(ph)} ph.links[1].append(rev(olk)) return # ---------------------------------------------------------------------- def set_links(oph, OLKS): clear_links(oph); for olk in OLKS: add_link(oph, olk) return # ---------------------------------------------------------------------- def get_links(oph): ph, dr = unpack(oph) if dr == 0: OLKS = ph.links[0] else: OLKS = [rev(olk) for olk in ph.links[1]] return OLKS # ---------------------------------------------------------------------- def get_connecting_link(oph0, oph1): OLKS0 = get_links(rev(oph0)) OLKS1 = get_links(oph1) for olk0 in OLKS0: for olk1 in OLKS1: if (unpack(rev(olk0))) == (unpack(olk1)): olk = olk1 assert pini(olk) == pfin(oph0) assert pfin(olk) == pini(oph1) return olk return None # ---------------------------------------------------------------------- def connection_time(oph0, oph1, use_links, mp_jump): assert oph0 != None and oph1 != None # Get the moves that need to be joined: n0 = nelems(oph0); n1 = nelems(oph1) omv0 = None if n0 == 0 else elem(oph0,n0-1) omv1 = None if n1 == 0 else elem(oph1,0) p = pfin(oph0); q = pini(oph1) if p == q: tconn = move.transition_penalty(omv0, omv1) else: if use_links: olk = get_connecting_link(oph0, oph1) else: olk = None if olk != None: tconn = path.fabtime(olk) # Assume that the link starts and ends with trace: if move.is_jump(omv0): tconn += move.ud_penalty(omv0) if move.is_jump(omv1): tconn += move.ud_penalty(omv1) else: tconn = move_parms.nozzle_travel_time(rn.dist(p, q), None, mp_jump) udp = move_parms.ud_penalty(mp_jump) if move.is_trace(omv0): tconn += udp if move.is_trace(omv1): tconn += udp return tconn # ---------------------------------------------------------------------- # CONTOUR PATH NESTING def compute_contour_nesting(OCRS): ncr = len(OCRS) POLS = [None]*ncr # Contour paths converted to {shapely} polygons. # Clear all nesting lists and convert all contour paths to polygons: for icr in range(ncr): ocr = OCRS[icr] ph, dr = unpack(ocr) assert pini(ph) == pfin(ph) ph.cr_children = [] ph.cr_parent = None PTS = [ move.pini(elem(ocr,k)) for k in range(nelems(ocr)) ] POLS[icr] = Polygon(PTS) # Find the parent of each contour: for icr1 in range(ncr): # Find the index {ipar} of the parent of {OCRS[icr1]}: ipar = None pg1 = POLS[icr1] for icr2 in range(ncr): pg2 = POLS[icr2] if icr1 != icr2: # See if {icr2} can be the parent of {icr1}: if pg2.contains(pg1): if ipar == None: # First container of {icr1}, keep it for now: ipar = icr2 else: # We already have a container {ipar}. Check which is closer: pg3 = POLS[ipar] if pg3.contains(pg2): # {icr2} is a closer container: ipar = icr2 else: assert pg2.contains(pg3), "contours are not properly nested" if ipar != None: ocr1 = OCRS[icr1] ph1, dr1 = unpack(ocr1) ocr3 = OCRS[ipar] ph1.cr_parent = ocr3 # Now define the children of each contour: for ocr1 in OCRS: ph1, dr1 = unpack(ocr1) ocr2 = ph1.cr_parent if ocr2 != None: ph2, dr2 = unpack(ocr2) ph2.cr_children.append(ocr1) # ---------------------------------------------------------------------- def inner_contours(ocr): ph, dr = unpack(ocr) return ph.cr_children # ---------------------------------------------------------------------- def outer_contour(ocr): ph, dr = unpack(ocr) return ph.cr_parent # ---------------------------------------------------------------------- def contour_nesting(ocr0, ocr1): # sys.stderr.write("ocr0 = %s\n" % str(ocr0)) # sys.stderr.write("ocr1 = %s\n" % str(ocr1)) ph0, dr0 = unpack(ocr0) ph1, dr1 = unpack(ocr1) if ph0 == ph1: return 0 ph0a = ph0 ph1a = ph1 while ph0a != None or ph1a != None: if ph0a != None: # Walk {ph0a} up: ocr0a = ph0a.cr_parent if ocr0a == None: ph0a = None else: ph0a, dr0a = unpack(ocr0a) if ph0a == ph1: # {ph0} is contained in {ph1} return -1 if ph1a != None: # Walk {ph1a} up: ocr1a = ph1a.cr_parent if ocr1a == None: ph1a = None else: ph1a, dr1a = unpack(ocr1a) if ph1a == ph0: # {ph1} is contained in {ph0} return +1 return 0 # ---------------------------------------------------------------------- def shift_contour(ocr, k): assert pini(ocr) == pfin(ocr) nmv = nelems(ocr) OMVS = [ elem(ocr, (i + k) % nmv) for i in range(nmv) ] ocrk = from_moves(OMVS) assert pini(ocrk) == pfin(ocrk) return ocrk # ---------------------------------------------------------------------- # PLOTTING def plot_to_files(fname, OPHS, CLRS, rwd, wd_axes, grid, deco): assert type(OPHS) is list or type(OPHS) is tuple assert len(OPHS) > 0 # Compute the plot's bounding box: B = bbox(OPHS) dp = (0,0) c, szx,szy = hacks.make_canvas(B, dp, False, grid, 1, 1) axes = deco dots = deco arrows = True # ??? Separate parameter? Also {deco}? ??? matter = deco plot_standard(c, OPHS, dp, None, CLRS, rwd, wd_axes, axes, dots, arrows, matter) hacks.write_plot(c, fname) return # ---------------------------------------------------------------------- def plot_standard(c, OPHS, dp, layer, CLRS, rwd, wd_axes, axes, dots, arrows, matter): assert type(OPHS) is list or type(OPHS) is tuple nph = len(OPHS) if nph == 0: return assert wd_axes != None and wd_axes > 0, "invalid wd_axes" if CLRS == None: CLRS = [ pyx.color.rgb(0.050, 0.800, 0.000), ] # Default trace color. else: assert type(CLRS) is list or type(CLRS) is tuple nclr = len(CLRS) assert nclr == 1 or nclr == nph def pick_colors(k): # Returns the colors for trace sausages and axes of path {OPHS[k]}. if nclr == 1: ctraces = CLRS[0] else: ctraces = CLRS[k] caxes = pyx.color.rgb(0.6*ctraces.r, 0.6*ctraces.g, 0.6*ctraces.b) # Color of trace axis, dots, arrow. return ctraces, caxes # Colors: cmatter = pyx.color.rgb(0.850, 0.800, 0.750) # Material footprint color. cjumps = pyx.color.rgb.black # Color of jump axes, dots, arrows. # Dimensions relative to nominal trace widths: rwd_matter = 1.13; # Material footprint. # Absolute dimensions (mm): wd_dots = 2.5*wd_axes; # Dots at ends of moves. sz_arrows = 6*wd_axes # Size of arrows. # Get list {lys} of layers to plot: if layer == None: # Plot all four layers. lys = (range(4)) else: # Plot only the selected layer. assert type (layer) is int assert layer >= 0 and layer < 4 lys = (layer,) # Plot the layers: for ly in lys: for k in range(nph): oph = OPHS[k] ctraces, caxes = pick_colors(k) if ly == 0 and matter: # plots the estimate of actual material: plot_layer\ ( c, oph, dp, jmp=False, \ clr=cmatter, rwd=rwd_matter, wd=0, dashed=False, wd_dots=0, sz_arrows=None ) elif ly == 1: # plots the nominal trace material: plot_layer \ ( c, oph, dp, jmp=False, clr=ctraces, rwd=rwd, wd=0, dashed=False, wd_dots=0, sz_arrows=None ) elif ly == 2 and (axes or dots or arrows): # Plots the trace axis: t_wd = wd_axes if axes else 0 t_wd_dots = wd_dots if dots else 0 t_sz_arrows = sz_arrows if arrows else 0 plot_layer \ ( c, oph, dp, jmp=False, clr=caxes, rwd = 0, wd=t_wd, dashed=False, wd_dots=t_wd_dots, sz_arrows=t_sz_arrows ) elif ly == 3: # Plots the jump: j_wd = wd_axes; j_wd_dots = wd_dots; j_sz_arrows = sz_arrows plot_layer \ ( c, oph, dp, jmp=True, clr=cjumps, rwd = 0, wd=j_wd, dashed=True, wd_dots=j_wd_dots, sz_arrows=j_sz_arrows ) # ---------------------------------------------------------------------- def plot_layer(c, oph, dp, jmp, clr, rwd, wd, dashed, wd_dots, sz_arrows): # Get the {Path} object {ph} and the direction {dr} ({None} if un-oriented): ph, dr = unpack(oph) if clr == None: return # Simplfications: if rwd == None: rwd = 0 if wd == None: wd = 0 if wd_dots == None: wd_dots = 0 if sz_arrows == None: sz_arrows = 0 assert rwd >= 0 and wd >= 0 and wd_dots >= 0 and sz_arrows >= 0 for imv in range(nelems(oph)): omvk = elem(oph, imv) if move.is_jump(omvk) == jmp: wdk = rwd*move.width(omvk) + wd move.plot_layer(c, omvk, dp, clr, wdk, dashed, wd_dots, sz_arrows) return # ---------------------------------------------------------------------- def plot_single(c, oph, dp, split, clr): ph, d = unpack(oph) # For typechecking. if split: assert clr == None OPHS, OJMS = split_at_jumps(oph) CLRS = hacks.trace_colors(len(OPHS)) else: CLRS = [clr,] if clr != None else None OPHS = [oph,] OJMS = [] nph = len(OPHS) njm = len(OJMS) assert nph > 0 wd_axes = 0.05 * move.width(elem(OPHS[0], 0)) if nph > 0: # Plot the continuous raster sequences: if CLRS == None: CLRS = hacks.trace_colors(nph) rwd = 0.80; # Relative width of trace sausages. axes = True dots = True arrows = True matter = True plot_standard(c, OPHS, dp, None, CLRS, rwd, wd_axes, axes, dots, arrows, matter) if njm > 0: # Plot the jumps: clr_jmp = pyx.color.rgb.black rwd = 0 axes = True dots = True arrows = True matter = False move.plot_standard(c, OJMS, dp, None, [clr_jmp,], rwd, wd_axes, axes, dots, arrows, matter) return # ---------------------------------------------------------------------- def split_at_jumps(oph): OPHS = [] # Complete trace-only paths found so far. OJMS = [] # Jumps found so far. OTRS = [] # Traces of last (possibly incomplete) trace-only path found so far. nmv = nelems(oph) for k in range(nmv + 1): omvk = elem(oph, k) if k < nmv else None if omvk == None or move.is_jump(omvk): if len(OTRS) != 0: ophi = from_moves(OTRS) OPHS.append(ophi) OTRS = [] if omvk != None: OJMS.append(omvk) else: OTRS.append(omvk) assert len(OTRS) == 0 return OPHS, OJMS # ---------------------------------------------------------------------- # DEBUGGING AND TESTING def validate(oph): nmv = nelems(oph) ph, dr = unpack(oph) assert len(ph.OMVS) == nmv assert len(ph.cumtex) == nmv # sys.stderr.write("dr = %d cumtex = %s\n" % (dr, str(ph.cumtex))) # Validate the list of moves: p_prev = pini(oph) # Final point of previous move. t_prev = 0 # Cumulative execution time. for imv in range(nmv): omvk = elem(oph, imv) # sys.stderr.write("omvk = %s\n" % str(omvk)) mvk, drk = move.unpack(omvk) # Just to typecheck. # sys.stderr.write("mvk = %s drk = %s\n" % (str(mvk), str(drk))) mv_pini, mv_pfin = move.endpoints(omvk) # sys.stderr.write("p_prev = %s pini = %s\n" % (str(p_prev), str(mv_pini))) assert p_prev == mv_pini mv_tini = tini(oph, imv) mv_tfin = tfin(oph, imv) mv_tex = move.fabtime(omvk) # Account for up/down penalties: if move.is_jump(omvk): # Add the applicable move/jump transition penalties: udk = move.ud_penalty(omvk) if imv > 0 and not move.is_jump(elem(oph,imv-1)): mv_tex += udk if imv < nmv-1 and not move.is_jump(elem(oph,imv+1)): mv_tex += udk # sys.stderr.write("imv = %d" % imv) # sys.stderr.write(" t_prev = %12.8f tini = %12.8f tfin = %12.8f" % (t_prev,mv_tini,mv_tfin)) # sys.stderr.write(" tex = %12.8f\n" % mv_tex) assert abs(t_prev - mv_tini) < 1.0e-8 assert abs((mv_tfin - mv_tini) - mv_tex) < 1.0e-8 p_prev = mv_pfin t_prev = mv_tfin assert p_prev == pfin(oph) assert abs(t_prev - fabtime(oph)) < 1.0e-8 # ---------------------------------------------------------------------- def get_name(oph): ph, dr = unpack(oph) name = ph.name if name == None: name = "P?" if dr == 1: name = "~" + name return name # ---------------------------------------------------------------------- def set_name(oph, name, moves): assert type(name) is str ph, dr = unpack(oph) ph.name = name if moves: nmv = path.nelems(ph) # Number of moves. ndg = len(str(nmv-1)) # Number of digits to use. for k in range(nmv): omv = path.elem(ph, k) move.set_name(omv, "name.%0*d" % (ndg,k)) return # ---------------------------------------------------------------------- def tag_names(OPHS, tag): if tag != None and tag != "": assert type(tag) is str for oph in OPHS: ph, dr = unpack(oph) ph.name = tag + get_name(ph) return # ---------------------------------------------------------------------- def show(wr, oph, moves, ind, wna, wnm): ph, dr = unpack(oph) wr.write(" "*ind) wr.write(" " if dr == 0 else "~") wr.write("%-*s" % (wna,get_name(ph))) nmv = nelems(oph) wr.write(" %*d" % (wnm,nmv)) p = pini(oph) q = pfin(oph) wr.write(" (%6.1f,%6.1f)" % (p[0],p[1])) wr.write(" (%6.1f,%6.1f)" % (q[0],q[1])) if moves: wr.write(" ") for imv in range(nmv): omv = elem(oph, imv) mvname = move.get_name(omv) if imv > 0: wr.write(",") wr.write(mvname) return # ---------------------------------------------------------------------- def show_list(wr, OPHS, moves, ind): xind = " "*ind assert type(OPHS) is list or type (OPHS) is tuple nph = len(OPHS) if nph == 0: return wna = 3 # Width of "name" column; min 3 because of the header. wnm = 1 # Width of "number of moves" column. for oph in OPHS: ph, dr = unpack(oph) wna = max(wna, len(get_name(ph))) wnm = max(wnm, len(str(nelems(oph)))) wix = len(str(nph-1)) # Num digits in index. wr.write("\n") # Write header: wr.write("%s%*s %-*s %*s %*s %*s moves \n" % (xind,wix,"k",wna+1,"name",wnm,"n",15,"pini",15,"pfin")) wr.write("%s%s %s %s %s %s %s\n" % (xind,"-"*wix,"-"*(wna+1),"-"*wnm,"-"*15,"-"*15,"-"*15)) # Write paths: for k in range(len(OPHS)): oph = OPHS[k] wr.write("%s%*d " % (xind,wix,k)) show(wr, oph, moves, 0, wna, wnm) wr.write("\n") wr.write("\n") return # ----------------------------------------------------------------------