use grid-gap values for layout

This commit is contained in:
Gavin Nishizawa 2023-04-10 20:02:54 -07:00
parent c8cd27dc5d
commit a7050f3ca7
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD
2 changed files with 45 additions and 25 deletions

View file

@ -19,10 +19,19 @@ type gridDiagram struct {
width float64 width float64
height float64 height float64
gapRows float64
gapColumns float64
} }
func newGridDiagram(root *d2graph.Object) *gridDiagram { func newGridDiagram(root *d2graph.Object) *gridDiagram {
gd := gridDiagram{root: root, objects: root.ChildrenArray} gd := gridDiagram{
root: root,
objects: root.ChildrenArray,
gapRows: DEFAULT_GAP,
gapColumns: DEFAULT_GAP,
}
if root.Attributes.GridRows != nil { if root.Attributes.GridRows != nil {
gd.rows, _ = strconv.Atoi(root.Attributes.GridRows.Value) gd.rows, _ = strconv.Atoi(root.Attributes.GridRows.Value)
} }
@ -66,6 +75,18 @@ func newGridDiagram(root *d2graph.Object) *gridDiagram {
gd.rowDirected = true gd.rowDirected = true
} }
// grid gap sets both, but can be overridden
if root.Attributes.GridGap != nil {
gd.gapRows, _ = strconv.ParseFloat(root.Attributes.GridGap.Value, 64)
gd.gapColumns = gd.gapRows
}
if root.Attributes.GridGapRows != nil {
gd.gapRows, _ = strconv.ParseFloat(root.Attributes.GridGapRows.Value, 64)
}
if root.Attributes.GridGapColumns != nil {
gd.gapColumns, _ = strconv.ParseFloat(root.Attributes.GridGapColumns.Value, 64)
}
return &gd return &gd
} }

View file

@ -13,8 +13,7 @@ import (
const ( const (
CONTAINER_PADDING = 60 CONTAINER_PADDING = 60
HORIZONTAL_PAD = 40. DEFAULT_GAP = 40.
VERTICAL_PAD = 40.
) )
// Layout runs the grid layout on containers with rows/columns // Layout runs the grid layout on containers with rows/columns
@ -189,10 +188,10 @@ func (gd *gridDiagram) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
o.Width = colWidths[j] o.Width = colWidths[j]
o.Height = rowHeights[i] o.Height = rowHeights[i]
o.TopLeft = cursor.Copy() o.TopLeft = cursor.Copy()
cursor.X += o.Width + HORIZONTAL_PAD cursor.X += o.Width + gd.gapColumns
} }
cursor.X = 0 cursor.X = 0
cursor.Y += rowHeights[i] + VERTICAL_PAD cursor.Y += rowHeights[i] + gd.gapRows
} }
} else { } else {
for j := 0; j < gd.columns; j++ { for j := 0; j < gd.columns; j++ {
@ -204,22 +203,22 @@ func (gd *gridDiagram) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
o.Width = colWidths[j] o.Width = colWidths[j]
o.Height = rowHeights[i] o.Height = rowHeights[i]
o.TopLeft = cursor.Copy() o.TopLeft = cursor.Copy()
cursor.Y += o.Height + VERTICAL_PAD cursor.Y += o.Height + gd.gapRows
} }
cursor.X += colWidths[j] + HORIZONTAL_PAD cursor.X += colWidths[j] + gd.gapColumns
cursor.Y = 0 cursor.Y = 0
} }
} }
var totalWidth, totalHeight float64 var totalWidth, totalHeight float64
for _, w := range colWidths { for _, w := range colWidths {
totalWidth += w + HORIZONTAL_PAD totalWidth += w + gd.gapColumns
} }
for _, h := range rowHeights { for _, h := range rowHeights {
totalHeight += h + VERTICAL_PAD totalHeight += h + gd.gapRows
} }
totalWidth -= HORIZONTAL_PAD totalWidth -= gd.gapColumns
totalHeight -= VERTICAL_PAD totalHeight -= gd.gapRows
gd.width = totalWidth gd.width = totalWidth
gd.height = totalHeight gd.height = totalHeight
} }
@ -246,8 +245,8 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
totalWidth += o.Width totalWidth += o.Width
totalHeight += o.Height totalHeight += o.Height
} }
totalWidth += HORIZONTAL_PAD * float64(len(gd.objects)-gd.rows) totalWidth += gd.gapColumns * float64(len(gd.objects)-gd.rows)
totalHeight += VERTICAL_PAD * float64(len(gd.objects)-gd.columns) totalHeight += gd.gapRows * float64(len(gd.objects)-gd.columns)
var layout [][]*d2graph.Object var layout [][]*d2graph.Object
if gd.rowDirected { if gd.rowDirected {
@ -278,10 +277,10 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
rowHeight := 0. rowHeight := 0.
for _, o := range row { for _, o := range row {
o.TopLeft = cursor.Copy() o.TopLeft = cursor.Copy()
cursor.X += o.Width + HORIZONTAL_PAD cursor.X += o.Width + gd.gapColumns
rowHeight = math.Max(rowHeight, o.Height) rowHeight = math.Max(rowHeight, o.Height)
} }
rowWidth := cursor.X - HORIZONTAL_PAD rowWidth := cursor.X - gd.gapColumns
rowWidths = append(rowWidths, rowWidth) rowWidths = append(rowWidths, rowWidth)
maxX = math.Max(maxX, rowWidth) maxX = math.Max(maxX, rowWidth)
@ -292,9 +291,9 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
// new row // new row
cursor.X = 0 cursor.X = 0
cursor.Y += rowHeight + VERTICAL_PAD cursor.Y += rowHeight + gd.gapRows
} }
maxY = cursor.Y - VERTICAL_PAD maxY = cursor.Y - gd.gapRows
// then expand thinnest objects to make each row the same width // then expand thinnest objects to make each row the same width
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C) // . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
@ -372,10 +371,10 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
colWidth := 0. colWidth := 0.
for _, o := range column { for _, o := range column {
o.TopLeft = cursor.Copy() o.TopLeft = cursor.Copy()
cursor.Y += o.Height + VERTICAL_PAD cursor.Y += o.Height + gd.gapRows
colWidth = math.Max(colWidth, o.Width) colWidth = math.Max(colWidth, o.Width)
} }
colHeight := cursor.Y - VERTICAL_PAD colHeight := cursor.Y - gd.gapRows
colHeights = append(colHeights, colHeight) colHeights = append(colHeights, colHeight)
maxY = math.Max(maxY, colHeight) maxY = math.Max(maxY, colHeight)
// set all objects in column to the same width // set all objects in column to the same width
@ -385,9 +384,9 @@ func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
// new column // new column
cursor.Y = 0 cursor.Y = 0
cursor.X += colWidth + HORIZONTAL_PAD cursor.X += colWidth + gd.gapColumns
} }
maxX = cursor.X - HORIZONTAL_PAD maxX = cursor.X - gd.gapColumns
// then expand shortest objects to make each column the same height // then expand shortest objects to make each column the same height
// . ├maxWidth(A,B)─┤ ├maxW(C,D)─┤ ├maxWidth(E)──────┤ // . ├maxWidth(A,B)─┤ ├maxW(C,D)─┤ ├maxWidth(E)──────┤
// . ┌A─────────────┐ ┌C─────────┐ ┌E────────────────┐ // . ┌A─────────────┐ ┌C─────────┐ ┌E────────────────┐
@ -479,7 +478,7 @@ func (gd *gridDiagram) getBestLayout(targetSize float64, columns bool) [][]*d2gr
// of these divisions, find the layout with rows closest to the targetSize // of these divisions, find the layout with rows closest to the targetSize
for _, division := range divisions { for _, division := range divisions {
layout := genLayout(gd.objects, division) layout := genLayout(gd.objects, division)
dist := getDistToTarget(layout, targetSize, columns) dist := getDistToTarget(layout, targetSize, gd.gapRows, gd.gapColumns, columns)
if dist < bestDist { if dist < bestDist {
bestLayout = layout bestLayout = layout
bestDist = dist bestDist = dist
@ -527,15 +526,15 @@ func genLayout(objects []*d2graph.Object, cutIndices []int) [][]*d2graph.Object
return layout return layout
} }
func getDistToTarget(layout [][]*d2graph.Object, targetSize float64, columns bool) float64 { func getDistToTarget(layout [][]*d2graph.Object, targetSize, gapRows, gapColumns float64, columns bool) float64 {
totalDelta := 0. totalDelta := 0.
for _, row := range layout { for _, row := range layout {
rowSize := 0. rowSize := 0.
for _, o := range row { for _, o := range row {
if columns { if columns {
rowSize += o.Height + VERTICAL_PAD rowSize += o.Height + gapRows
} else { } else {
rowSize += o.Width + HORIZONTAL_PAD rowSize += o.Width + gapColumns
} }
} }
totalDelta += math.Abs(rowSize - targetSize) totalDelta += math.Abs(rowSize - targetSize)