github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/drawtree/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "image/color" 6 "math" 7 "math/rand" 8 "os" 9 "strings" 10 "time" 11 12 "loov.dev/diagram" 13 ) 14 15 // Code based on https://llimllib.github.io/pymag-trees/ 16 17 func RandomTree(depth, width int) *Node { 18 root := &Node{} 19 if depth <= 0 { 20 return root 21 } 22 n := rand.Intn(width) + 1 23 for i := 0; i < n; i++ { 24 root.Children = append(root.Children, RandomTree(depth-1, width)) 25 } 26 return root 27 } 28 29 func main() { 30 rand.Seed(time.Now().UnixNano()) 31 root := RandomTree(10, 2) 32 root.Init(root, nil, 0, 1) 33 // Print(root) 34 // fmt.Println() 35 36 Buccheim(root) 37 svg := diagram.NewSVG(10000, 10000) 38 Draw(svg, root) 39 os.WriteFile("tree.svg", svg.Bytes(), 0644) 40 } 41 42 func Print(node *Node) { 43 fmt.Printf("Tree(\"%p\"", node) 44 for _, child := range node.Children { 45 fmt.Print(",\n" + strings.Repeat("\t", node.Y+1)) 46 Print(child) 47 } 48 fmt.Printf(")") 49 } 50 51 const ( 52 R = 30 53 RH = R * 1.5 54 RW = R * 1.5 55 ) 56 57 func Tree(name string, children ...*Node) *Node { 58 return &Node{ 59 Name: name, 60 Children: children, 61 } 62 } 63 64 func Draw(draw diagram.Canvas, n *Node) { 65 DrawConn(draw, n) 66 DrawNode(draw, n) 67 } 68 69 func Square(cx, cy, d diagram.Length) diagram.Rect { 70 return diagram.R(cx-d/2, cy-d/2, cx+d/2, cy+d/2) 71 } 72 73 func DrawNode(draw diagram.Canvas, n *Node) { 74 draw.Rect(Square(float64(n.X*RW), float64(n.Y*RH), R), &diagram.Style{ 75 Stroke: color.Black, 76 Size: 1, 77 Fill: color.White, 78 }) 79 for _, c := range n.Children { 80 DrawNode(draw, c) 81 } 82 } 83 84 func DrawConn(draw diagram.Canvas, n *Node) { 85 for _, c := range n.Children { 86 draw.Poly(diagram.Ps( 87 float64(n.X*RW), float64(n.Y*RH), 88 float64(c.X*RW), float64((n.Y+c.Y)*RH)/2, 89 float64(c.X*RW), float64(c.Y*RH), 90 ), &diagram.Style{Stroke: color.Black, Size: 1}) 91 DrawConn(draw, c) 92 } 93 } 94 95 type Node struct { 96 Name string 97 X float32 98 Y int 99 Children []*Node 100 Parent *Node 101 Thread *Node 102 Ancestor *Node 103 LeftMostSibling *Node 104 LeftBrother *Node 105 Number int // 1..n 106 107 Mod, Change, Shift float32 108 } 109 110 func (node *Node) Init(tree, parent *Node, depth, number int) { 111 node.X = -1 112 node.Y = depth 113 node.Parent = parent 114 node.Ancestor = node 115 node.Number = number 116 117 for number, child := range node.Children { 118 child.Init(tree, node, depth+1, number+1) 119 if number > 0 { 120 child.LeftMostSibling = node.Children[0] 121 child.LeftBrother = node.Children[number-1] 122 } 123 } 124 } 125 126 func (node *Node) Left() *Node { 127 if node.Thread != nil { 128 return node.Thread 129 } 130 if len(node.Children) > 0 { 131 return node.Children[0] 132 } 133 return nil 134 } 135 136 func (node *Node) String() string { 137 return fmt.Sprintf("%v: x=%.1f mod=%v", node.Name, node.X, node.Mod) 138 } 139 140 func (node *Node) Right() *Node { 141 if node.Thread != nil { 142 return node.Thread 143 } 144 if len(node.Children) > 0 { 145 return node.Children[len(node.Children)-1] 146 } 147 return nil 148 } 149 150 const Distance = 1 151 152 func Buccheim(tree *Node) { 153 FirstWalk(tree) 154 min := SecondWalk(tree, 0, float32(math.NaN())) 155 if min < 0 { 156 ThirdWalk(tree, -min) 157 } 158 } 159 160 func FirstWalk(n *Node) { 161 if len(n.Children) == 0 { 162 if n.LeftMostSibling != nil { 163 n.X = n.LeftBrother.X + Distance 164 } else { 165 n.X = 0 166 } 167 return 168 } 169 170 defaultAncestor := n.Children[0] 171 for _, c := range n.Children { 172 FirstWalk(c) 173 defaultAncestor = Apportion(c, defaultAncestor) 174 } 175 // fmt.Println("finished v =", n, "children") 176 177 ExecuteShifts(n) 178 179 first := n.Children[0] 180 last := n.Children[len(n.Children)-1] 181 midpoint := (first.X + last.X) / 2 182 183 c := n.LeftBrother 184 if c != nil { 185 n.X = c.X + Distance 186 n.Mod = n.X - midpoint 187 } else { 188 n.X = midpoint 189 } 190 } 191 192 func SecondWalk(n *Node, m, min float32) float32 { 193 n.X += m 194 if math.IsNaN(float64(min)) || n.X < min { 195 min = n.X 196 } 197 for _, c := range n.Children { 198 min = SecondWalk(c, m+n.Mod, min) 199 } 200 return min 201 } 202 203 func ThirdWalk(n *Node, offset float32) { 204 n.X += offset 205 for _, c := range n.Children { 206 ThirdWalk(c, offset) 207 } 208 } 209 210 func ExecuteShifts(n *Node) { 211 var shift, change float32 212 for i := len(n.Children) - 1; i >= 0; i-- { 213 c := n.Children[i] 214 // fmt.Println("shift:", c, shift, c.Change) 215 c.X += shift 216 c.Mod += shift 217 change += c.Change 218 shift += c.Shift + change 219 } 220 } 221 222 func Apportion(v, defaultAncestor *Node) *Node { 223 w := v.LeftBrother 224 if w == nil { 225 return defaultAncestor 226 } 227 228 // in buchheim notation: 229 // i == inner; o == outer; r == right; l == left; r = +; l = - 230 vir, vor := v, v 231 vil := w 232 vol := v.LeftMostSibling 233 sir, sor := v.Mod, v.Mod 234 sil := vil.Mod 235 sol := vol.Mod 236 237 for vil.Right() != nil && vir.Left() != nil { 238 vil = vil.Right() 239 vir = vir.Left() 240 vol = vol.Left() 241 vor = vor.Right() 242 243 vor.Ancestor = v 244 shift := (vil.X + sil) - (vir.X + sir) + Distance 245 if shift > 0 { 246 MoveSubtree(Ancestor(vil, v, defaultAncestor), v, shift) 247 sir += shift 248 sor += shift 249 } 250 sil += vil.Mod 251 sir += vir.Mod 252 sol += vol.Mod 253 sor += vor.Mod 254 } 255 256 if vil.Right() != nil && vor.Right() == nil { 257 vor.Thread = vil.Right() 258 vor.Mod += sil - sor 259 } else { 260 if vir.Left() != nil && vol.Left() == nil { 261 vol.Thread = vir.Left() 262 vol.Mod += sir - sol 263 } 264 defaultAncestor = v 265 } 266 return defaultAncestor 267 } 268 269 func MoveSubtree(wl, wr *Node, shift float32) { 270 subtrees := float32(wr.Number - wl.Number) 271 // fmt.Println(wl.Name, "is conflicted with", wr.Name, "moving", subtrees, "shift", shift) 272 if subtrees <= 0 { 273 panic("zero subtrees") 274 } 275 wr.Change -= shift / subtrees 276 wr.Shift += shift 277 wl.Change += shift / subtrees 278 wr.X += shift 279 wr.Mod += shift 280 } 281 282 func Ancestor(vil, v, defaultAncestor *Node) *Node { 283 // the relevant text is at the bottom of page 7 of 284 // "Improving Walker's Algorithm to Run in Linear Time" by Buchheim et al, (2002) 285 // http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.16.8757&rep=rep1&type=pdf 286 for _, c := range v.Parent.Children { 287 if c == vil.Ancestor { 288 return vil.Ancestor 289 } 290 } 291 return defaultAncestor 292 }