let to_string c = String.make 1 c let space = to_string ' ' let axis = "┬" let axis_spc = "─" type t = { vals: int list; opts: Options.t } let default = { vals = []; opts = Options.default } let plot v c = { c with vals = v; } let with_options v c = { v with opts = c; } let pp channel {vals: int list; opts: Options.t } = let open Utils in let height = opts.height in let min_val = list_min vals in let max_val = list_max vals in let range = max_val - min_val in let scale v = ((v - min_val) * height) / range in let new_vals = List.map scale vals in let rec pad left right s = if left = 0 then s :: repeat right space else space :: pad (left - 1) right s in let new_vals' = zip new_vals (List.tl new_vals) in let make_marker prefix left right s = prefix :: (if left = 0 then s :: repeat right space else space :: pad (left - 1) right s) in let make_connector s e = match Stdlib.compare s e with | 0 -> make_marker axis_spc s (height - s) "-" | x -> let (ltr_chr, rtl_chr) = match x with | 1 -> ("└", "┐") | _ -> ("┘", "┌") in let (st, en) = Stdlib.((min s e, max s e)) in let l = en - st |> saturating_sub 1 in let (left, right) = (repeat st space, repeat (height - l - st) space) in List.flatten [[axis_spc]; left; [ltr_chr]; repeat l "│"; [rtl_chr]; right] in let m = Printf.sprintf opts.marker in let rec make_graph = function | [] -> [] | (s, e) :: [] -> make_marker axis s (height - s) m :: make_connector s e :: make_marker axis e (height - e) m :: [] | (s, e) :: tl -> make_marker axis s (height - s) m :: make_connector s e :: make_graph tl in make_graph new_vals' |> rot_left |> List.map (String.concat "") |> String.concat "\n" |> Printf.fprintf channel "%s"