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  }