Jason (jcreed) wrote,
Jason
jcreed

New poster.

I wrote a little sml code to generate the diagrams, implementing something akin to TeX's (and many other programs', I'm sure) notion of "stretchable glue". At one point I had an obvious bug that scaling all glue by a constant factor affected the results, which it totally shouldn't have — stretchability is only meaningful up to a constant factor. So I made an abstract type for stretchability, and edited my program until it should have been right, but lo and behold there was a type error: I had swapped around two elements of a tuple that prior to abstraction were both just real numbers.

Score one more for types!

signature TORSOR =
sig
 type t
 val tor : real -> t
 val ratio : t * t -> real
 val max : t * t -> t
 val + : t * t -> t
end

structure Torsor :> TORSOR =
struct
 type t = real
 fun tor x = 20.0 * x
 fun ratio (x:real, y) = x / y
 fun max (x, y) = Real.max (x, y)
 fun op+ (x, y) = Real.+ (x, y)
end

structure Comp =
struct

val tor = Torsor.tor
fun r2s r = if r < 0.0 then "-" ^ Real.toString (~r) else Real.toString r

datatype diag = Cell 
	      | Concat of diag list

fun single 0 = Concat[Cell]
  | single n = Concat[Cell, single (n-1), Cell]
fun horizontal n = Concat[Cell, single n, Cell, single n, Cell]
fun nwrap 0 c = c
  | nwrap n c = Concat[Cell, nwrap (n-1) c, Cell] 

type mag = real
type glue = mag * Torsor.t (* extent, stretch *)
type xy = mag * mag
datatype layout = Hbox of Torsor.t * layout list (* t is stretchiness *)
		| Vbox of Torsor.t * layout list
		| Prim of (xy -> xy -> string) * glue * glue (* SVG, xglue, yglue *) (* SVG takes pos, size *)

fun sum l = foldl op+ 0.0 l
fun foldx f [] = raise Match
  | foldx f [x] = x
  | foldx f (h::tl) = f (h,foldx f tl)
fun gplus ((m:real,s),(m',s')) = (m + m', Torsor.+(s, s'))
fun fst (x,y) = x
fun gsum l = foldl gplus (0.0, tor 0.0) l
fun gmaxs l = foldx Real.max (map fst l)

fun ljoin sep [] = [sep]
  | ljoin sep [x] = [sep, x, sep]
  | ljoin sep (h::tl) = sep :: h :: ljoin sep tl

val HSTRETCH = tor 50.0
val VSTRETCH = tor 50.0
val VXGLUE = (2.0, tor 50.0)
val VYGLUE = (2.0, tor 0.0)
val HXGLUE = (2.0, tor 0.0)
val HYGLUE = (2.0, tor 50.0)

val HSEP = Prim ((fn x => fn y => ""), (1.5, tor 5.0), (0.0, tor 0.0))
val VSEP = Prim ((fn x => fn y => ""), (0.0, tor 0.0), (1.5, tor 5.0))

fun marker 1 = ""
  | marker n = "marker-end=\"url(#Triangle)\""
fun dc (false, dimension) (px,py) (sx,sy) =
     "<line x1=\"" ^ r2s (px + sx/2.0) ^ "\" x2 = \"" ^ r2s (px + sx/2.0) ^ "\" y1=\"" 
    ^ r2s py ^ "\" y2=\"" ^ r2s (py + sy) ^ "\" style=\"file:none;stroke:black;\" " ^ marker dimension ^ " />\n" 
  | dc (true, dimension) (px, py) (sx, sy) = 
     "<line y1=\"" ^ r2s (py + sy/2.0) ^ "\" y2 = \"" ^ r2s (py + sy/2.0) ^ "\" x1=\"" 
    ^ r2s px ^ "\" x2=\"" ^ r2s (px + sx) ^ "\" style=\"file:none;stroke:black;\" " ^ marker dimension ^ "/>\n" 
fun drawcell (p, d) pos size = dc (p, d) pos size ^ "\n" ^ dc (p, 1) pos size

fun layout c =
    let
	fun go true n Cell = Prim (drawcell (true, n), VXGLUE, VYGLUE)
	  | go false n Cell = Prim (drawcell (false, n), HXGLUE, HYGLUE)
	  | go true n (Concat ds) = Hbox (HSTRETCH, ljoin HSEP (map (go false (n+1)) ds))
	  | go false n (Concat ds) = Vbox (VSTRETCH, ljoin VSEP (map (go true (n+1)) ds))
    in 
	go true 0 c
    end

fun xextent (Hbox (st, ls)) = gsum (map xextent ls)
  | xextent (Vbox (st, ls)) = (gmaxs (map xextent ls), st)
  | xextent (Prim (svg, xg, yg)) = xg
fun yextent (Hbox (st, ls)) = (gmaxs (map yextent ls), st)
  | yextent (Vbox (st, ls)) = gsum (map yextent ls)
  | yextent (Prim (svg, xg, yg)) = yg

(* distribute takes the amount of slack, and the total amount of stretch
   and a glue and computes the size of an item *)
fun distribute sl pst (m : real, st) = m + sl * Torsor.ratio(st, pst)

fun render (px,py) (sx,sy) (l as Hbox (s, ls)) = 
    let
	val (mx,stx) = xextent l
	val (my,sty) = yextent l 
	(* its is a list of pairs of items and extents *) 
	val its = map (fn it => (it, (distribute (sx - mx) stx (xextent it)))) ls
	fun addone ((item, sx'), (px', str)) = (px' + sx', str ^ render (px', py) (sx', sy) item)
	val (_, str) = foldl addone (px, "") its
    in
	str
    end
  | render (px,py) (sx,sy) (l as Vbox (s, ls)) = 
    let
	val (mx,stx) = xextent l
	val (my,sty) = yextent l 
	(* its is a list of pairs of items and extents *) 
	val its = map (fn it => (it, (distribute (sy - my) sty (yextent it)))) ls
	fun addone ((item, sy'), (py', str)) = (py' + sy', str ^ render (px, py') (sx, sy') item)
	val (_, str) = foldl addone (py, "") its
    in
	str
    end
  | render pos size (Prim (svg, xg, yg)) = svg pos size 

fun xml s = 
    "<?xml version=\"1.0\" standalone=\"no\"?>\n" ^
    "<svg width=\"100%\" height=\"100%\" version=\"1.1\" " ^
    "xmlns=\"http://www.w3.org/2000/svg\">\n" ^ (StringUtil.readfile "defs") ^
    s ^ "\n</svg>"

val size = (100.0, 100.0) 
fun row ~1 n pos = ""
  | row m n (px, py) = render (px,py) size (layout (nwrap m (horizontal n))) ^
		  row (m-1) (n+1) (px + 120.0, py)

fun diag 0 (px, py) = row 0 0 (px, py)
  | diag n (px, py) = row n 0 (px, py) ^ diag (n-1) (px + 60.0, py - 120.0)

fun go () =
    let
	val s = xml (diag 7 (0.0,0.0))
    in
	StringUtil.writefile "/home/jcreed/desktop/diagram.svg" s
    end
end

Tags: posters, sigbovik
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 6 comments