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 = { c with opts = v } let pp formatter { vals : int list; opts : Options.t } = let open Utils in let height = opts.height - 1 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 = int_of_float (float_of_int (v - min_val) /. float_of_int range *. float_of_int height) 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 dr, rd, ru, ur = Connector.of_kind opts.connector in let ltr_chr, rtl_chr = match x with 1 -> (dr, rd) | _ -> (ru, ur) 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 = Format.asprintf "%a" Marker.pp 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 let unscale v = (float_of_int (v * range) /. float_of_int height) +. float_of_int min_val in let y_vals = Format.sprintf "%10s" "┤" :: List.map (fun x -> Format.sprintf "%6.2f ┤" (unscale x)) (0 -- (height + 1)) in y_vals :: make_graph new_vals' |> rot_left |> List.map (String.concat "") |> String.concat "\n" |> Format.fprintf formatter "%s"