# Miscellaneous utility functions for the HotPath software. # Last edited on 2021-05-31 03:35:54 by jstolfi import color import hacks_IMP import pyx # MATH HACKS: def real_quadratic_roots(A, B, C): # Returns the roots {t0} and {t1} of the equation {A t^2 + B t + C = # 0}, with {t0 < t1}, if they are real and distinct. Otherwise returns # {None,None}. Requires {A != 0}. May return {-inf,+inf}. return hacks_IMP.real_quadratic_roots(A, B, C) # GEOMETRIC HACKS def is_point(p): # Returns {True} if {p} is a pair (2-tuple or 2-list) of floats, # {False} otherwise. return hacks_IMP.is_point(p) def same_point(p, q, tol): # Returns {True} if the coordinates of {p} and {q} differ by at most {tol} return hacks_IMP.same_point(p, q, tol) # PLOTTING HACKS def make_canvas(B, dp, frame, grid, nx, ny): # Creates a {Pyx} canvas {c} for an array of plots, each with bounding # box {B}. The array will have {nx} columns and {ny} rows. Optionally # draws a frame and/or a grid on each plot, as requested by the # booleans {frame} and {grid}. # # The procedure returns the new canvas {c} and the sizes {szx,szy} of # each array slot. # # The procedure computes an expanded box {BX} by enlarging the box {B} # with a 1 mm margin and rounding it outwards to integer coordinates. # The slot sizs {szx} and {szy} will be the width and height of {BX}. # The procedure assumes that the individual plots will be # displaced by those amounts from one column or row to the next, and # uses those steps to place the grids and frames for the other slots. # # The frame will be drawn around each slot of the array # but slightly displaced inwards from its edges. The grid # will span the slot minus a slightly wider margin, so it will # almost but not quite touch the frame. # # If {dp} is not {None}, it must be a 2-vector (pair of floats). # Then all frames and grids will be displaced by {dp}. # # The procedure also sets the {Pyx} scale factor so that the # total plot will have a reasonable size in pixels. # return hacks_IMP.make_canvas(B, dp, frame, grid, nx, ny) def round_box(B, mrg): # Returns the given {box} rounded outwards to edges with integer # coordinates, at least {mrg} away from the original ones. return hacks_IMP.round_box(B, mrg) def plot_line(c, clr, wd, p, q): # Plots on the {pyx} context {c} a line with color {crl} and width {wd} from {p} to {q}. hacks_IMP.plot_line(c, clr, wd, p, q) def plot_box(c, clr, dp, B): # Plots on the {pyx} context {c} the box {B} displaced by the vector {dp} # with color {clr}. # # If {clr} is {None}, it defaults to black. If {dp} is {None}, it # defaults to {(0,0)}. hacks_IMP.plot_box(c, clr, dp, B) def plot_frame(c, clr, wd, dp, B, mrg): # Plots on the {pyx} context {c} a frame around the box {B}, displaced by the vector {dp}, # with color {clr} and line width {wd}. # # If {mrg} is not {None} the frame will be displaced inwards by {mrg} # (or outwards by {-mrg}, if negative). If {clr} is None, defaults to # black. If {dp} is {None}, it defaults to {(0,0)}. hacks_IMP.plot_frame(c, clr, wd, dp, B, mrg) def plot_grid(c, clr, wd, dashed, dp, B, mrg, xstep, ystep): # Plots on the {pyx} context {c} a grid spanning the box {B}. The grid # lines will be at coordinates that are integer multiples of {xstep} # and {ystep}. If {dp} is not {None}, the whole grid will be displaced # by {dp}. # # If {mrg} is not {None} the edge of the grid will be displaced # inwards by {mrg} (or outwards by {-mrg}, if negative) relative to # the box {B}. The lines will be dashed if {dashed} is true. If {clr} # is {None}, defaults to a light gray. hacks_IMP.plot_grid(c, clr, wd, dashed, dp, B, mrg, xstep, ystep) def write_plot(c, name): # Writes the {pyx} canvas {c} to an EPS file "{name}.eps", # a PNG file named "{name}.png", and a JPEG file "{name}.jpg". hacks_IMP.write_plot(c, name) def adjust_dash_pattern(rdist, rlen, rgap): # Computes a dash pattern so that a dashed line will begin and end # with full dashes. # # The parameter {rdist} should be the distance to be traced divided by # the line width. The ideal dash pattern should have dashes of length # {rlen} separated bt gaps of lenth {rgap} both relative to the line # width. # # If the procedure succeeds, it returns {True, [a*rlen, a*rgap]} where # {a} is a suitable adjustment factor, near 1. # # The procedure fails if the distance {rdist} is too small to use a # dashed line. In that case it returns {False, None}. # # ??? Generalize to begin and end with dashes of relative length {rend*rlen} ??? return hacks_IMP.adjust_dash_pattern(rdist, rlen, rgap) def choose_grid_steps(sz): # Returns the major and minor grid steps appropriate for a picture # that measures {sz} millimeters in the largest dimension. # They will be powers of 10 times some simple factor like 1, 2, 5. # The minor grid step will be a sub-multiple of the major one. return hacks_IMP.choose_grid_steps(sz) def trace_colors(nc): # Returns an array {C} of {nc} colors that can be used to paint different paths, etc. # Each element is a {pyx.color.rgb} triple. Color {C[nc-1-k]} is complementary # to {C[k]}. return hacks_IMP.trace_colors(nc) def link_colors(nc): # Returns an array {C} of {nc} colors that can be used to paint different links. # Each element is a {pyx.color.rgb} triple. Color {C[nc-1-k]} is complementary # to {C[k]}. return hacks_IMP.link_colors(nc) # IMAGE HACKS def convert_eps_to_png(name): # Reads the file "{name}.eps" and converts it to a PNG image file "{name}.png". hacks_IMP.convert_eps_to_png(name) def convert_eps_to_jpg(name): # Reads the file "{name}.eps" and converts it to a JPEG image file "{name}.jpg". hacks_IMP.convert_eps_to_jpg(name)