Kennedy and Reingold-Tillford algorithms for tree node positioning
23

Configure Feed

Select the types of activity you want to include in your feed.

trees / src / tree.ml
6.3 kB 181 lines
1type 'a tree = Node of 'a * 'a tree list 2type 'a positioned_tree = PNode of 'a * float * 'a positioned_tree list 3type extent = (float * float) list 4type 'a abs_tree = ANode of 'a * float * 'a abs_tree list 5 6let move_tree dx (PNode (label, x, children)) = 7 PNode (label, x +. dx, children) 8 9let move_extent dx : extent -> extent = 10 List.map (fun (l, r) -> (l +. dx, r +. dx)) 11 12let rec merge_extents (e1 : extent) (e2 : extent) : extent = 13 match e1, e2 with 14 | [], qs -> qs 15 | ps, [] -> ps 16 | (l1, r1) :: ps, (l2, r2) :: qs -> 17 (min l1 l2, max r1 r2) :: merge_extents ps qs 18 19let merge_extent_list : extent list -> extent = 20 List.fold_left merge_extents [] 21 22let fit (e1 : extent) (e2 : extent) : float = 23 let rec go e1 e2 = 24 match e1, e2 with 25 | (_, r) :: ps, (l, _) :: qs -> max (r -. l +. 1.0) (go ps qs) 26 | _, _ -> 0.0 27 in 28 go e1 e2 29 30let fit_list_left (es : extent list) : float list = 31 let rec go acc = function 32 | [] -> [] 33 | e :: es -> 34 let x = fit acc e in 35 x :: go (merge_extents acc (move_extent x e)) es 36 in 37 go [] es 38 39let fit_list (es : extent list) : float list = 40 let lefts = fit_list_left es in 41 let rights = List.rev (fit_list_left (List.rev_map (List.map (fun (l, r) -> (-.r, -.l))) es)) in 42 let rights = List.map (fun x -> -.x) rights in 43 List.map2 (fun l r -> (l +. r) /. 2.0) lefts rights 44 45let rec design (Node (label, children) : 'a tree) : 'a positioned_tree * extent = 46 let designed_children = List.map design children in 47 let trees = List.map fst designed_children in 48 let extents = List.map snd designed_children in 49 let positions = fit_list extents in 50 let positioned_trees = List.map2 move_tree positions trees in 51 let positioned_extents = List.map2 move_extent positions extents in 52 let result_extent = (0.0, 0.0) :: merge_extent_list positioned_extents in 53 (PNode (label, 0.0, positioned_trees), result_extent) 54 55let rec absolutize x (PNode (label, dx, children)) : 'a abs_tree = 56 let x' = x +. dx in 57 ANode (label, x', List.map (absolutize x') children) 58 59let flatten tree = 60 let rec go depth (ANode (label, x, children)) = 61 (label, x, depth) :: List.concat_map (go (depth + 1)) children 62 in 63 go 0 tree 64 65let edges tree = 66 let rec go depth (ANode (_, x, children)) = 67 let child_edges = List.map (fun (ANode (_, cx, _)) -> (x, depth, cx, depth + 1)) children in 68 child_edges @ List.concat_map (go (depth + 1)) children 69 in 70 go 0 tree 71 72module ReingoldTilford = struct 73 type 'a rt_tree = { 74 mutable x : float; 75 mutable modifier : float; 76 label : 'a; 77 children : 'a rt_tree list; 78 mutable thread : 'a rt_tree option; 79 mutable ancestor : 'a rt_tree; 80 } 81 82 let rec make_rt tree = 83 let Node (label, children) = tree in 84 let rt_children = List.map make_rt children in 85 let rec node = { 86 x = 0.0; modifier = 0.0; label; children = rt_children; 87 thread = None; ancestor = node; 88 } in 89 node 90 91 let sibling_sep = 1.0 92 let subtree_sep = 1.0 93 94 let rec left_contour node depth acc = 95 let acc = (depth, node) :: acc in 96 match node.children with 97 | [] -> (match node.thread with Some t -> left_contour t (depth + 1) acc | None -> acc) 98 | first :: _ -> left_contour first (depth + 1) acc 99 100 let rec right_contour node depth acc = 101 let acc = (depth, node) :: acc in 102 match List.rev node.children with 103 | [] -> (match node.thread with Some t -> right_contour t (depth + 1) acc | None -> acc) 104 | last :: _ -> right_contour last (depth + 1) acc 105 106 let contour_offset left_tree right_tree = 107 let left_c = left_contour left_tree 0 [] |> List.rev in 108 let right_c = right_contour right_tree 0 [] |> List.rev in 109 let rec compute lc rc l_off r_off max_sep = 110 match lc, rc with 111 | [], _ | _, [] -> max_sep 112 | (ld, ln) :: lrest, (rd, rn) :: rrest when ld = rd -> 113 let l_pos = ln.x +. l_off in 114 let r_pos = rn.x +. r_off in 115 let sep = l_pos -. r_pos +. sibling_sep in 116 let l_off = l_off +. ln.modifier in 117 let r_off = r_off +. rn.modifier in 118 compute lrest rrest l_off r_off (max max_sep sep) 119 | (ld, _) :: _, (rd, _) :: rrest when rd < ld -> 120 compute lc rrest l_off r_off max_sep 121 | (_, _) :: lrest, (_, _) :: _ -> 122 compute lrest rc l_off r_off max_sep 123 in 124 compute right_c left_c 0.0 0.0 0.0 125 126 let set_thread left_tree right_tree = 127 let left_c = left_contour left_tree 0 [] in 128 let right_c = right_contour right_tree 0 [] in 129 let l_depth = match left_c with (d, _) :: _ -> d | [] -> -1 in 130 let r_depth = match right_c with (d, _) :: _ -> d | [] -> -1 in 131 if l_depth > r_depth then 132 match right_c with (_, rnode) :: _ -> 133 (match left_c |> List.find_opt (fun (d, _) -> d = r_depth + 1) with 134 | Some (_, lnode) -> rnode.thread <- Some lnode 135 | None -> ()) 136 | [] -> () 137 else if r_depth > l_depth then 138 match left_c with (_, lnode) :: _ -> 139 (match right_c |> List.find_opt (fun (d, _) -> d = l_depth + 1) with 140 | Some (_, rnode) -> lnode.thread <- Some rnode 141 | None -> ()) 142 | [] -> () 143 144 let rec first_walk node = 145 match node.children with 146 | [] -> () 147 | children -> 148 List.iter first_walk children; 149 let place_subtrees positioned current = 150 match positioned with 151 | [] -> [current] 152 | prev :: _ -> 153 let offset = contour_offset prev current in 154 current.x <- prev.x +. offset +. subtree_sep; 155 current.modifier <- current.x; 156 set_thread prev current; 157 current :: positioned 158 in 159 let _ = List.fold_left place_subtrees [] children in 160 let first_child = List.hd children in 161 let last_child = List.hd (List.rev children) in 162 let mid = (first_child.x +. last_child.x) /. 2.0 in 163 List.iter (fun c -> c.x <- c.x -. mid) children; 164 List.iter (fun c -> c.modifier <- c.modifier -. mid) children 165 166 let rec second_walk node offset = 167 node.x <- node.x +. offset; 168 List.iter (fun c -> second_walk c (offset +. node.modifier)) node.children 169 170 let rec to_abs_tree node : 'a abs_tree = 171 ANode (node.label, node.x, List.map to_abs_tree node.children) 172 173 let layout tree = 174 let rt = make_rt tree in 175 first_walk rt; 176 second_walk rt 0.0; 177 to_abs_tree rt 178end 179 180let design_rt tree = 181 ReingoldTilford.layout tree