d2/d2layouts/d2grid/grid.go
2023-04-03 13:49:10 -07:00

98 lines
4.5 KiB
Go

package d2grid
import (
"math"
"strconv"
"oss.terrastruct.com/d2/d2graph"
)
type grid struct {
root *d2graph.Object
nodes []*d2graph.Object
rows int
columns int
cellWidth float64
cellHeight float64
width float64
height float64
}
func newGrid(root *d2graph.Object) *grid {
g := grid{root: root, nodes: root.ChildrenArray}
if root.Attributes.Rows != nil {
g.rows, _ = strconv.Atoi(root.Attributes.Rows.Value)
}
if root.Attributes.Columns != nil {
g.columns, _ = strconv.Atoi(root.Attributes.Columns.Value)
}
// compute exact row/column count based on values entered
if g.rows == 0 {
// set rows based on number of columns
g.rows = len(g.nodes) / g.columns
if len(g.nodes)%g.columns != 0 {
g.rows++
}
} else if g.columns == 0 {
// set columns based on number of rows
g.columns = len(g.nodes) / g.rows
if len(g.nodes)%g.rows != 0 {
g.columns++
}
} else {
// rows and columns specified (add more rows if needed)
capacity := g.rows * g.columns
for capacity < len(g.nodes) {
g.rows++
capacity += g.columns
}
}
// if we have the following nodes for a 2 row, 3 column grid
// . ┌A──────────────────┐ ┌B─────┐ ┌C────────────┐ ┌D───────────┐ ┌E───────────────────┐
// . │ │ │ │ │ │ │ │ │ │
// . │ │ │ │ │ │ │ │ │ │
// . └───────────────────┘ │ │ │ │ │ │ │ │
// . │ │ └─────────────┘ │ │ │ │
// . │ │ │ │ └────────────────────┘
// . └──────┘ │ │
// . └────────────┘
// Then we must get the max width and max height to determine the grid cell size
// . maxWidth├────────────────────┤
// . ┌A───────────────────┐ ┌B───────────────────┐ ┌C───────────────────┐ ┬maxHeight
// . │ │ │ │ │ │ │
// . │ │ │ │ │ │ │
// . │ │ │ │ │ │ │
// . │ │ │ │ │ │ │
// . │ │ │ │ │ │ │
// . │ │ │ │ │ │ │
// . └────────────────────┘ └────────────────────┘ └────────────────────┘ ┴
// . ┌D───────────────────┐ ┌E───────────────────┐
// . │ │ │ │
// . │ │ │ │
// . │ │ │ │
// . │ │ │ │
// . │ │ │ │
// . │ │ │ │
// . └────────────────────┘ └────────────────────┘
var maxWidth, maxHeight float64
for _, n := range g.nodes {
maxWidth = math.Max(maxWidth, n.Width)
maxHeight = math.Max(maxHeight, n.Height)
}
g.cellWidth = maxWidth
g.cellHeight = maxHeight
g.width = maxWidth + (float64(g.columns)-1)*(maxWidth+HORIZONTAL_PAD)
g.height = maxHeight + (float64(g.rows)-1)*(maxHeight+VERTICAL_PAD)
return &g
}
func (g *grid) shift(dx, dy float64) {
for _, obj := range g.nodes {
obj.TopLeft.X += dx
obj.TopLeft.Y += dy
}
}