treemap

package module
v0.0.0-...-fd80043 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 3, 2022 License: Apache-2.0 Imports: 8 Imported by: 0

README

treemap

Documentation

Overview

Package treemap implements functions and structs for building treemaps

TreeMaps are an alternative way to display properties of structured layered data (such as filesystems). Every data point can contain up to 4 different dimmensions: 3 numericals and a color encoded one.

Consider the folowing description of a simple, hypotetical code analyzer report. In this case the dimm1 is the number of lines of code contained by the node, dimm2 is the number of types, dimm3, the number of functions and the color is encoding the complexity of the node (CCN-like mesure)

{
	"name": "mypackage",
	"dimm1": 1,
	"dimm2": 2,
	"dimm3": 3,
	"color": "0xff00ff",
	"children": [
		{
			"name": "mypackage/subpackage0",
			"dimm1": 5,
			"dimm2": 6,
			"dimm3": 7,
			"color": "0x00ffff"
		},
		{
			"name": "mypackage/subpackage1",
			"dimm1": 10,
			"dimm2": 8,
			"dimm3": 3,
			"color": "0x00ff00",
			"children": [
				{
					"name": "mypackage/subpackage1/subpackage0",
					"dimm1": 1,
					"dimm2": 4,
					"dimm3": 3,
					"color": "0xffff00"
				}
			]
		}
	]
}

treemap generates an extended version of the tree description, adding spatial coordinates and dimmensions for every package (block) in the tree.

In this extended version, dimm1 will affect the width of the block; dimm2, its depth and dimm3 its height.

The tiling algorithm is based in:

Example
tree := TreeInfo{
	BlockInfo: BlockInfo{
		Name:  "root",
		Dimm1: 1,
		Dimm2: 2,
		Dimm3: 3,
		Color: "0xff00ff",
	},
	Children: []*TreeInfo{
		{
			BlockInfo: BlockInfo{
				Name:  "root #0",
				Dimm1: 5,
				Dimm2: 6,
				Dimm3: 7,
				Color: "0x00ffff",
			},
			Children: []*TreeInfo{},
		},
		{
			BlockInfo: BlockInfo{
				Name:  "root #1",
				Dimm1: 10,
				Dimm2: 8,
				Dimm3: 3,
				Color: "0x00ff00",
			},
			Children: []*TreeInfo{
				{
					BlockInfo: BlockInfo{
						Name:  "root #1 #1",
						Dimm1: 1,
						Dimm2: 4,
						Dimm3: 3,
						Color: "0xffff00",
					},
					Children: []*TreeInfo{},
				}},
		},
	},
}

fmt.Println(tree.Tree(context.Background()).String())
Output:

{
	"depth": 35,
	"height": 6,
	"width": 21,
	"position": {
		"x": 0,
		"y": 0
	},
	"name": "root",
	"dimm1": 1,
	"dimm2": 2,
	"dimm3": 3,
	"color": "0xff00ff",
	"children": [
		{
			"depth": 9,
			"height": 10,
			"width": 8,
			"position": {
				"x": -4.5,
				"y": -10.5,
				"z": 6
			},
			"name": "root #0",
			"dimm1": 5,
			"dimm2": 6,
			"dimm3": 7,
			"color": "0x00ffff"
		},
		{
			"depth": 18,
			"height": 6,
			"width": 17,
			"position": {
				"x": 0,
				"y": 6,
				"z": 6
			},
			"name": "root #1",
			"dimm1": 10,
			"dimm2": 8,
			"dimm3": 3,
			"color": "0x00ff00",
			"children": [
				{
					"depth": 7,
					"height": 6,
					"width": 4,
					"position": {
						"x": 0,
						"y": 0,
						"z": 12
					},
					"name": "root #1 #1",
					"dimm1": 1,
					"dimm2": 4,
					"dimm3": 3,
					"color": "0xffff00"
				}
			]
		}
	]
}
Example (FromJSONDefinition)
jsonDefinition := `{
	"name": "root",
	"dimm1": 1,
	"dimm2": 2,
	"dimm3": 3,
	"color": "0xff00ff",
	"children": [
		{
			"name": "root #0",
			"dimm1": 5,
			"dimm2": 6,
			"dimm3": 7,
			"color": "0x00ffff"
		},
		{
			"name": "root #1",
			"dimm1": 10,
			"dimm2": 8,
			"dimm3": 3,
			"color": "0x00ff00",
			"children": [
				{
					"name": "root #1 #1",
					"dimm1": 1,
					"dimm2": 4,
					"dimm3": 3,
					"color": "0xffff00"
				}
			]
		}
	]
}`
tree := TreeInfo{}
json.Unmarshal([]byte(jsonDefinition), &tree)

fmt.Println("Get the Block")
fmt.Println(tree.Block().String())

fmt.Println("Get the Tree")
fmt.Println(tree.Tree(context.Background()).String())
Output:

Get the Block
{
	"depth": 0,
	"height": 0,
	"width": 0,
	"position": {
		"x": 0,
		"y": 0
	},
	"name": "root",
	"dimm1": 1,
	"dimm2": 2,
	"dimm3": 3,
	"color": "0xff00ff",
	"children": [
		{
			"depth": 0,
			"height": 0,
			"width": 0,
			"position": {
				"x": 0,
				"y": 0
			},
			"name": "root #0",
			"dimm1": 5,
			"dimm2": 6,
			"dimm3": 7,
			"color": "0x00ffff"
		},
		{
			"depth": 0,
			"height": 0,
			"width": 0,
			"position": {
				"x": 0,
				"y": 0
			},
			"name": "root #1",
			"dimm1": 10,
			"dimm2": 8,
			"dimm3": 3,
			"color": "0x00ff00",
			"children": [
				{
					"depth": 0,
					"height": 0,
					"width": 0,
					"position": {
						"x": 0,
						"y": 0
					},
					"name": "root #1 #1",
					"dimm1": 1,
					"dimm2": 4,
					"dimm3": 3,
					"color": "0xffff00"
				}
			]
		}
	]
}
Get the Tree
{
	"depth": 35,
	"height": 6,
	"width": 21,
	"position": {
		"x": 0,
		"y": 0
	},
	"name": "root",
	"dimm1": 1,
	"dimm2": 2,
	"dimm3": 3,
	"color": "0xff00ff",
	"children": [
		{
			"depth": 9,
			"height": 10,
			"width": 8,
			"position": {
				"x": -4.5,
				"y": -10.5,
				"z": 6
			},
			"name": "root #0",
			"dimm1": 5,
			"dimm2": 6,
			"dimm3": 7,
			"color": "0x00ffff"
		},
		{
			"depth": 18,
			"height": 6,
			"width": 17,
			"position": {
				"x": 0,
				"y": 6,
				"z": 6
			},
			"name": "root #1",
			"dimm1": 10,
			"dimm2": 8,
			"dimm3": 3,
			"color": "0x00ff00",
			"children": [
				{
					"depth": 7,
					"height": 6,
					"width": 4,
					"position": {
						"x": 0,
						"y": 0,
						"z": 12
					},
					"name": "root #1 #1",
					"dimm1": 1,
					"dimm2": 4,
					"dimm3": 3,
					"color": "0xffff00"
				}
			]
		}
	]
}

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Walk

func Walk(root *Block, walkFn WalkFunc) error

Walk walks the block tree rooted at root depth-first, calling walkFn for each Block in the tree

Example
root := generateTree(rand.New(rand.NewSource(0)), 5)

if err := Walk(root, func(b *Block) error {
	fmt.Println(b.Name)
	return nil
}); err != nil {
	fmt.Println(err.Error())
}
Output:

root
root #0
root #0 #0
root #0 #0 #0
root #0 #0 #0 #0
root #0 #0 #0 #0 #0
root #0 #0 #1
root #0 #0 #1 #0
root #0 #0 #1 #0 #0
root #0 #0 #1 #1
root #0 #0 #1 #1 #0
root #0 #3
root #0 #3 #0
root #0 #3 #0 #1
root #0 #3 #1
root #0 #3 #1 #0
root #0 #3 #2
root #0 #3 #2 #0
root #0 #3 #2 #0 #0
root #0 #3 #2 #1
root #1
root #1 #0
root #1 #0 #0
root #1 #0 #0 #0
root #1 #0 #1
root #1 #0 #2
root #1 #0 #2 #0
root #1 #0 #2 #0 #0
root #1 #1
root #1 #1 #0
root #1 #1 #0 #0
root #1 #1 #0 #0 #0
root #1 #1 #0 #1
root #1 #1 #0 #1 #0
root #1 #1 #1
root #1 #1 #1 #1
root #1 #1 #1 #1 #0
root #1 #1 #2
root #1 #1 #2 #0
root #1 #2
root #1 #2 #0
root #1 #2 #0 #0
root #1 #2 #0 #1
root #1 #2 #2
root #1 #2 #2 #0
root #1 #2 #2 #0 #0
root #1 #3
root #1 #3 #0
root #1 #3 #0 #1
root #1 #3 #1
root #1 #3 #1 #1
root #1 #3 #1 #1 #0
root #1 #3 #2
root #2
root #2 #0
root #2 #0 #0
root #2 #0 #0 #0
root #2 #0 #0 #0 #0
root #2 #0 #2
root #2 #0 #2 #0
root #2 #0 #2 #1
root #2 #0 #2 #1 #0
root #2 #1
root #2 #1 #0
root #2 #1 #0 #0
root #2 #1 #0 #0 #0
root #2 #1 #1
root #2 #1 #1 #0
root #2 #1 #1 #0 #0
root #2 #1 #1 #1
root #2 #1 #1 #1 #0
root #2 #1 #2
root #2 #1 #2 #1
root #2 #1 #2 #1 #0
root #2 #2
root #2 #2 #0
root #2 #2 #0 #1
root #2 #2 #1
root #2 #2 #1 #0
root #2 #2 #1 #0 #0
root #2 #3
root #2 #3 #0
root #2 #3 #0 #1
root #2 #3 #0 #1 #0
root #2 #3 #2
root #3
root #3 #1
root #3 #1 #0
root #3 #1 #0 #0
root #3 #1 #0 #0 #0
root #3 #1 #0 #1
root #3 #1 #0 #1 #0
root #3 #2
root #3 #2 #2
root #3 #2 #2 #1
root #3 #3
root #3 #3 #0
root #3 #3 #0 #0
root #3 #3 #0 #0 #0
root #3 #3 #0 #1
root #3 #3 #0 #1 #0
root #3 #3 #1
root #3 #3 #1 #0
root #3 #3 #1 #0 #0
root #3 #3 #2
root #3 #3 #2 #0
root #3 #3 #2 #0 #0
root #3 #3 #2 #1
root #3 #3 #2 #1 #0
Example (ContextCanceled)
root := generateTree(rand.New(rand.NewSource(0)), 5)

ok := WalkFunc(func(b *Block) error {
	fmt.Println(b.Name)
	return nil
})
ko := WalkFunc(func(b *Block) error {
	fmt.Println("the walk function should not been called for", b.Name)
	return nil
})

if err := Walk(root, ok.WithContext(context.Background())); err != nil {
	fmt.Println(err.Error())
}

fmt.Println("canceled context?")
ctx, cancel := context.WithCancel(context.Background())
cancel()
fmt.Println(Walk(root, ko.WithContext(ctx)))
Output:

root
root #0
root #0 #0
root #0 #0 #0
root #0 #0 #0 #0
root #0 #0 #0 #0 #0
root #0 #0 #1
root #0 #0 #1 #0
root #0 #0 #1 #0 #0
root #0 #0 #1 #1
root #0 #0 #1 #1 #0
root #0 #3
root #0 #3 #0
root #0 #3 #0 #1
root #0 #3 #1
root #0 #3 #1 #0
root #0 #3 #2
root #0 #3 #2 #0
root #0 #3 #2 #0 #0
root #0 #3 #2 #1
root #1
root #1 #0
root #1 #0 #0
root #1 #0 #0 #0
root #1 #0 #1
root #1 #0 #2
root #1 #0 #2 #0
root #1 #0 #2 #0 #0
root #1 #1
root #1 #1 #0
root #1 #1 #0 #0
root #1 #1 #0 #0 #0
root #1 #1 #0 #1
root #1 #1 #0 #1 #0
root #1 #1 #1
root #1 #1 #1 #1
root #1 #1 #1 #1 #0
root #1 #1 #2
root #1 #1 #2 #0
root #1 #2
root #1 #2 #0
root #1 #2 #0 #0
root #1 #2 #0 #1
root #1 #2 #2
root #1 #2 #2 #0
root #1 #2 #2 #0 #0
root #1 #3
root #1 #3 #0
root #1 #3 #0 #1
root #1 #3 #1
root #1 #3 #1 #1
root #1 #3 #1 #1 #0
root #1 #3 #2
root #2
root #2 #0
root #2 #0 #0
root #2 #0 #0 #0
root #2 #0 #0 #0 #0
root #2 #0 #2
root #2 #0 #2 #0
root #2 #0 #2 #1
root #2 #0 #2 #1 #0
root #2 #1
root #2 #1 #0
root #2 #1 #0 #0
root #2 #1 #0 #0 #0
root #2 #1 #1
root #2 #1 #1 #0
root #2 #1 #1 #0 #0
root #2 #1 #1 #1
root #2 #1 #1 #1 #0
root #2 #1 #2
root #2 #1 #2 #1
root #2 #1 #2 #1 #0
root #2 #2
root #2 #2 #0
root #2 #2 #0 #1
root #2 #2 #1
root #2 #2 #1 #0
root #2 #2 #1 #0 #0
root #2 #3
root #2 #3 #0
root #2 #3 #0 #1
root #2 #3 #0 #1 #0
root #2 #3 #2
root #3
root #3 #1
root #3 #1 #0
root #3 #1 #0 #0
root #3 #1 #0 #0 #0
root #3 #1 #0 #1
root #3 #1 #0 #1 #0
root #3 #2
root #3 #2 #2
root #3 #2 #2 #1
root #3 #3
root #3 #3 #0
root #3 #3 #0 #0
root #3 #3 #0 #0 #0
root #3 #3 #0 #1
root #3 #3 #0 #1 #0
root #3 #3 #1
root #3 #3 #1 #0
root #3 #3 #1 #0 #0
root #3 #3 #2
root #3 #3 #2 #0
root #3 #3 #2 #0 #0
root #3 #3 #2 #1
root #3 #3 #2 #1 #0
canceled context?
context canceled
Example (Errored)
root := generateTree(rand.New(rand.NewSource(0)), 5)
expectedErr := errors.New("wait for me")

var counter int
if err := Walk(root, func(b *Block) error {
	counter++
	if counter == 3 {
		return expectedErr
	}
	if counter > 3 {
		fmt.Println("walk funct called after throwing an error")
	}
	fmt.Println(b.Name)
	return nil
}); err != nil {
	fmt.Println("got error:", err.Error())
}
fmt.Println("total calls to the walk func:", counter)
Output:

root
root #0
got error: wait for me
total calls to the walk func: 3

Types

type Block

type Block struct {
	BlockNode
	BlockInfo
	Children []*Block `json:"children,omitempty"`
}

Block is a tree of blocks. Every Block contains a list of children, a BlockInfo with the input values and a BlockNode with all the information regardind the block position and its dimensions

func NewBlock

func NewBlock(info BlockInfo, children ...*Block) *Block

NewBlock returns a Block with the received info, containing the injected children but without setting the node details. This function is intended to be used when programmatically building all the nodes to be placed in a tree but the root.

Example
b1 := NewBlock(BlockInfo{
	Name:  "b1",
	Dimm1: 1,
	Dimm2: 10,
	Dimm3: 5,
	Color: "0x00ff00",
})
b2 := NewBlock(BlockInfo{
	Name:  "b2",
	Dimm1: 10,
	Dimm2: 1,
	Dimm3: 2,
	Color: "0xff0000",
})
b := NewBlock(BlockInfo{
	Name:  "root",
	Dimm1: 5,
	Dimm2: 5,
	Dimm3: 10,
	Color: "0x0000ff",
}, b1, b2)

fmt.Println(b.String())
Output:

{
	"depth": 0,
	"height": 0,
	"width": 0,
	"position": {
		"x": 0,
		"y": 0
	},
	"name": "root",
	"dimm1": 5,
	"dimm2": 5,
	"dimm3": 10,
	"color": "0x0000ff",
	"children": [
		{
			"depth": 0,
			"height": 0,
			"width": 0,
			"position": {
				"x": 0,
				"y": 0
			},
			"name": "b1",
			"dimm1": 1,
			"dimm2": 10,
			"dimm3": 5,
			"color": "0x00ff00"
		},
		{
			"depth": 0,
			"height": 0,
			"width": 0,
			"position": {
				"x": 0,
				"y": 0
			},
			"name": "b2",
			"dimm1": 10,
			"dimm2": 1,
			"dimm3": 2,
			"color": "0xff0000"
		}
	]
}

func NewTree

func NewTree(ctx context.Context, info BlockInfo, children ...*Block) *Block

NewTree returns a Block with the received info with all the injected children already positioned and sized. This function is intended to be used when programmatically building the root node of a tree, but it is safe to be called more than once. The returned Block can also be used as part of a bigger treemap.

Example
b1 := NewBlock(BlockInfo{
	Name:  "b1",
	Dimm1: 1,
	Dimm2: 10,
	Dimm3: 5,
	Color: "0x00ff00",
})
b2 := NewBlock(BlockInfo{
	Name:  "b2",
	Dimm1: 10,
	Dimm2: 1,
	Dimm3: 2,
	Color: "0xff0000",
})
b := NewTree(context.Background(), BlockInfo{
	Name:  "root",
	Dimm1: 5,
	Dimm2: 5,
	Dimm3: 10,
	Color: "0x0000ff",
}, b1, b2)

fmt.Println(b.String())
Output:

{
	"depth": 28,
	"height": 13,
	"width": 21,
	"position": {
		"x": 0,
		"y": 0
	},
	"name": "root",
	"dimm1": 5,
	"dimm2": 5,
	"dimm3": 10,
	"color": "0x0000ff",
	"children": [
		{
			"depth": 13,
			"height": 8,
			"width": 4,
			"position": {
				"x": -4.5,
				"y": -3.5,
				"z": 13
			},
			"name": "b1",
			"dimm1": 1,
			"dimm2": 10,
			"dimm3": 5,
			"color": "0x00ff00"
		},
		{
			"depth": 4,
			"height": 5,
			"width": 13,
			"position": {
				"x": 0,
				"y": 8,
				"z": 13
			},
			"name": "b2",
			"dimm1": 10,
			"dimm2": 1,
			"dimm3": 2,
			"color": "0xff0000"
		}
	]
}

func (*Block) Generate

func (b *Block) Generate(rand *rand.Rand, size int) reflect.Value

Generate implements the quick.Generator interface (https://golang.org/pkg/testing/quick/#Generator)

func (*Block) String

func (b *Block) String() string

String implements the fmt.Stringer interace by returning an indentated json serialization of the Block

type BlockInfo

type BlockInfo struct {
	Name  string `json:"name"`
	Dimm1 int    `json:"dimm1"`
	Dimm2 int    `json:"dimm2"`
	Dimm3 int    `json:"dimm3"`
	Color Color  `json:"color,omitempty"`
}

BlockInfo contains all the user defined details of every Block in the tree map

func (BlockInfo) Generate

func (b BlockInfo) Generate(rand *rand.Rand, size int) reflect.Value

Generate implements the quick.Generator interface (https://golang.org/pkg/testing/quick/#Generator)

type BlockNode

type BlockNode struct {
	Depth    float64  `json:"depth"`
	Height   float64  `json:"height"`
	Width    float64  `json:"width"`
	Position Position `json:"position"`
}

BlockNode contains information regarding the size and position of every Block in the tree map

type Color

type Color string

Color is a wrapper over a string, useful for string to color conversions and quick serialization. The expected format is "0xff00ff"

func (Color) Decode

func (c Color) Decode() (color.Color, error)

Decode returns the color described as hex at c

Example
for _, tc := range []string{
	"0xff00ff",
	"0x",
	"0x00ff",
	"0xffff",
	"0x00ffff",
	"0xzzzzzz",
} {
	c, err := Color(tc).Decode()
	if err != nil {
		fmt.Printf("!!! %s: %s\n", tc, err.Error())
		continue
	}
	r, g, b, a := c.RGBA()
	fmt.Printf("%s: {%d, %d, %d, %d}\n", tc, r, g, b, a)
}
Output:

0xff00ff: {65535, 0, 65535, 65535}
0x: {0, 0, 0, 65535}
0x00ff: {0, 65535, 0, 65535}
0xffff: {65535, 65535, 0, 65535}
0x00ffff: {0, 65535, 65535, 65535}
!!! 0xzzzzzz: encoding/hex: invalid byte: U+007A 'z'

type Position

type Position struct {
	X float64 `json:"x"`
	Y float64 `json:"y"`
	Z float64 `json:"z,omitempty"`
}

Position is an X, Y, Z tuple.

func (Position) Add

func (p Position) Add(q Position) Position

Add returns the 3D vector p + q

type Tiler

type Tiler struct {
	// contains filtered or unexported fields
}

Tiler is a quick implementation of a solution for the tiling problem. It takes an amount of tiles to place in one plane starting at {0, 0} and identifies where a tile should be placed and what is the smaller rectangle containing all the passed tiles.

func NewTiler

func NewTiler(totalTiles int) *Tiler

NewTiler returns a tiler expecting to place totalTiles with a default margin

func NewTilerWithMargin

func NewTilerWithMargin(totalTiles int, margin float64) *Tiler

NewTilerWithMargin returns a tiler expecting to place totalTiles with the injected margin

func (*Tiler) GetBounds

func (g *Tiler) GetBounds() Position

GetBounds returns the size of the rectangle containing all the tiles placed so far

func (*Tiler) NextPosition

func (g *Tiler) NextPosition(width, height float64) Position

NextPosition calculates where a tile of the passed dimensions should be placed

type TreeInfo

type TreeInfo struct {
	BlockInfo
	Children []*TreeInfo `json:"children,omitempty"`
}

TreeInfo is a tree of BlockInfo structs describing a treemap

func (*TreeInfo) Block

func (t *TreeInfo) Block() *Block

Block returns the tree described by t without initializing the positions of the blocks

func (*TreeInfo) Generate

func (t *TreeInfo) Generate(rand *rand.Rand, size int) reflect.Value

Generate implements the quick.Generator interface (https://golang.org/pkg/testing/quick/#Generator)

func (*TreeInfo) String

func (t *TreeInfo) String() string

String implements the fmt.Stringer interace by returning an indentated json serialization of the TreeInfo

func (*TreeInfo) Tree

func (t *TreeInfo) Tree(ctx context.Context) *Block

Tree returns the initialized tree described by t

type WalkFunc

type WalkFunc func(*Block) error

WalkFunc is the type of the function called for every Block visited by Walk. After the first error returned by the WalkFunc, the Walk is finished and the error propagated

func (WalkFunc) WithContext

func (w WalkFunc) WithContext(ctx context.Context) WalkFunc

WithContext wraps the WalfFunc w with another WalfFunc containing a check for context cancelations before executing the actual function w

Directories

Path Synopsis
cmd
treemap
Treemap converts the tree definition into a treemap, exporting it as a JSON or as an image
Treemap converts the tree definition into a treemap, exporting it as a JSON or as an image
Package plain exposes functions for rendering vertical projections of treemaps
Package plain exposes functions for rendering vertical projections of treemaps
Package volume exposes functions for rendering 3D views of treemaps
Package volume exposes functions for rendering 3D views of treemaps

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL