rename to grid diagram

This commit is contained in:
Gavin Nishizawa 2023-04-05 11:11:31 -07:00
parent 44f2d7a47f
commit 06a942cf99
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD
7 changed files with 114 additions and 114 deletions

View file

@ -708,7 +708,7 @@ func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) {
case "rows", "columns": case "rows", "columns":
for _, child := range obj.ChildrenArray { for _, child := range obj.ChildrenArray {
if child.IsContainer() { if child.IsContainer() {
c.errorf(f.LastPrimaryKey(), fmt.Sprintf(`invalid grid %#v. can only set %#v with no descendants (see %#v)`, obj.AbsID(), keyword, child.ChildrenArray[0].AbsID())) c.errorf(f.LastPrimaryKey(), fmt.Sprintf(`invalid grid diagram %#v. can only set %#v with no descendants (see %#v)`, obj.AbsID(), keyword, child.ChildrenArray[0].AbsID()))
} }
} }
} }
@ -796,12 +796,12 @@ func (c *compiler) validateNear(g *d2graph.Graph) {
func (c *compiler) validateEdges(g *d2graph.Graph) { func (c *compiler) validateEdges(g *d2graph.Graph) {
for _, edge := range g.Edges { for _, edge := range g.Edges {
if grid := edge.Src.ClosestGrid(); grid != nil { if gd := edge.Src.ClosestGridDiagram(); gd != nil {
c.errorf(edge.GetAstEdge(), "edge %#v cannot enter grid %#v", d2format.Format(edge.GetAstEdge()), grid.AbsID()) c.errorf(edge.GetAstEdge(), "edge %#v cannot enter grid diagram %#v", d2format.Format(edge.GetAstEdge()), gd.AbsID())
continue continue
} }
if grid := edge.Dst.ClosestGrid(); grid != nil { if gd := edge.Dst.ClosestGridDiagram(); gd != nil {
c.errorf(edge.GetAstEdge(), "edge %#v cannot enter grid %#v", d2format.Format(edge.GetAstEdge()), grid.AbsID()) c.errorf(edge.GetAstEdge(), "edge %#v cannot enter grid diagram %#v", d2format.Format(edge.GetAstEdge()), gd.AbsID())
continue continue
} }
} }

View file

@ -2299,9 +2299,9 @@ obj {
hey -> c: ok hey -> c: ok
`, `,
expErr: `d2/testdata/d2compiler/TestCompile/grid_edge.d2:3:2: edge "a -> b" cannot enter grid "hey" expErr: `d2/testdata/d2compiler/TestCompile/grid_edge.d2:3:2: edge "a -> b" cannot enter grid diagram "hey"
d2/testdata/d2compiler/TestCompile/grid_edge.d2:5:2: edge "c -> hey.b" cannot enter grid "hey" d2/testdata/d2compiler/TestCompile/grid_edge.d2:5:2: edge "c -> hey.b" cannot enter grid diagram "hey"
d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:2: edge "hey.a -> c" cannot enter grid "hey"`, d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:2: edge "hey.a -> c" cannot enter grid diagram "hey"`,
}, },
{ {
name: "grid_nested", name: "grid_nested",
@ -2315,8 +2315,8 @@ d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:2: edge "hey.a -> c" cannot en
d.invalid descendant d.invalid descendant
} }
`, `,
expErr: `d2/testdata/d2compiler/TestCompile/grid_nested.d2:2:2: invalid grid "hey". can only set "rows" with no descendants (see "hey.d.invalid descendant") expErr: `d2/testdata/d2compiler/TestCompile/grid_nested.d2:2:2: invalid grid diagram "hey". can only set "rows" with no descendants (see "hey.d.invalid descendant")
d2/testdata/d2compiler/TestCompile/grid_nested.d2:3:2: invalid grid "hey". can only set "columns" with no descendants (see "hey.d.invalid descendant")`, d2/testdata/d2compiler/TestCompile/grid_nested.d2:3:2: invalid grid diagram "hey". can only set "columns" with no descendants (see "hey.d.invalid descendant")`,
}, },
} }

View file

@ -1,16 +1,16 @@
package d2graph package d2graph
func (obj *Object) IsGrid() bool { func (obj *Object) IsGridDiagram() bool {
return obj != nil && obj.Attributes != nil && return obj != nil && obj.Attributes != nil &&
(obj.Attributes.Rows != nil || obj.Attributes.Columns != nil) (obj.Attributes.Rows != nil || obj.Attributes.Columns != nil)
} }
func (obj *Object) ClosestGrid() *Object { func (obj *Object) ClosestGridDiagram() *Object {
if obj.Parent == nil { if obj.Parent == nil {
return nil return nil
} }
if obj.Parent.IsGrid() { if obj.Parent.IsGridDiagram() {
return obj.Parent return obj.Parent
} }
return obj.Parent.ClosestGrid() return obj.Parent.ClosestGridDiagram()
} }

View file

@ -6,7 +6,7 @@ import (
"oss.terrastruct.com/d2/d2graph" "oss.terrastruct.com/d2/d2graph"
) )
type grid struct { type gridDiagram struct {
root *d2graph.Object root *d2graph.Object
nodes []*d2graph.Object nodes []*d2graph.Object
rows int rows int
@ -18,24 +18,24 @@ type grid struct {
height float64 height float64
} }
func newGrid(root *d2graph.Object) *grid { func newGridDiagram(root *d2graph.Object) *gridDiagram {
g := grid{root: root, nodes: root.ChildrenArray} gd := gridDiagram{root: root, nodes: root.ChildrenArray}
if root.Attributes.Rows != nil { if root.Attributes.Rows != nil {
g.rows, _ = strconv.Atoi(root.Attributes.Rows.Value) gd.rows, _ = strconv.Atoi(root.Attributes.Rows.Value)
} }
if root.Attributes.Columns != nil { if root.Attributes.Columns != nil {
g.columns, _ = strconv.Atoi(root.Attributes.Columns.Value) gd.columns, _ = strconv.Atoi(root.Attributes.Columns.Value)
} }
// compute exact row/column count based on values entered // compute exact row/column count based on values entered
if g.columns == 0 { if gd.columns == 0 {
g.rowDominant = true gd.rowDominant = true
} else if g.rows == 0 { } else if gd.rows == 0 {
g.rowDominant = false gd.rowDominant = false
} else { } else {
// if keyword rows is first, rows are primary, columns secondary. // if keyword rows is first, rows are primary, columns secondary.
if root.Attributes.Rows.MapKey.Range.Before(root.Attributes.Columns.MapKey.Range) { if root.Attributes.Rows.MapKey.Range.Before(root.Attributes.Columns.MapKey.Range) {
g.rowDominant = true gd.rowDominant = true
} }
// rows and columns specified, but we want to continue naturally if user enters more nodes // rows and columns specified, but we want to continue naturally if user enters more nodes
@ -48,34 +48,34 @@ func newGrid(root *d2graph.Object) *grid {
// . └───────┘ ▲ │ └───────┘ ▲ // . └───────┘ ▲ │ └───────┘ ▲
// . ▲ └─existing nodes modified │ ▲ └─existing nodes preserved // . ▲ └─existing nodes modified │ ▲ └─existing nodes preserved
// . └─existing rows preserved │ └─existing rows modified // . └─existing rows preserved │ └─existing rows modified
capacity := g.rows * g.columns capacity := gd.rows * gd.columns
for capacity < len(g.nodes) { for capacity < len(gd.nodes) {
if g.rowDominant { if gd.rowDominant {
g.rows++ gd.rows++
capacity += g.columns capacity += gd.columns
} else { } else {
g.columns++ gd.columns++
capacity += g.rows capacity += gd.rows
} }
} }
} }
return &g return &gd
} }
func (g *grid) shift(dx, dy float64) { func (gd *gridDiagram) shift(dx, dy float64) {
for _, obj := range g.nodes { for _, obj := range gd.nodes {
obj.TopLeft.X += dx obj.TopLeft.X += dx
obj.TopLeft.Y += dy obj.TopLeft.Y += dy
} }
} }
func (g *grid) cleanup(obj *d2graph.Object, graph *d2graph.Graph) { func (gd *gridDiagram) cleanup(obj *d2graph.Object, graph *d2graph.Graph) {
obj.Children = make(map[string]*d2graph.Object) obj.Children = make(map[string]*d2graph.Object)
obj.ChildrenArray = make([]*d2graph.Object, 0) obj.ChildrenArray = make([]*d2graph.Object, 0)
for _, child := range g.nodes { for _, child := range gd.nodes {
obj.Children[child.ID] = child obj.Children[child.ID] = child
obj.ChildrenArray = append(obj.ChildrenArray, child) obj.ChildrenArray = append(obj.ChildrenArray, child)
} }
graph.Objects = append(graph.Objects, g.nodes...) graph.Objects = append(graph.Objects, gd.nodes...)
} }

View file

@ -29,25 +29,25 @@ const (
// 7. Put grid children back in correct location // 7. Put grid children back in correct location
func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) d2graph.LayoutGraph { func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) d2graph.LayoutGraph {
return func(ctx context.Context, g *d2graph.Graph) error { return func(ctx context.Context, g *d2graph.Graph) error {
grids, objectOrder, err := withoutGrids(ctx, g) gridDiagrams, objectOrder, err := withoutGridDiagrams(ctx, g)
if err != nil { if err != nil {
return err return err
} }
if g.Root.IsGrid() && len(g.Root.ChildrenArray) != 0 { if g.Root.IsGridDiagram() && len(g.Root.ChildrenArray) != 0 {
g.Root.TopLeft = geo.NewPoint(0, 0) g.Root.TopLeft = geo.NewPoint(0, 0)
} else if err := layout(ctx, g); err != nil { } else if err := layout(ctx, g); err != nil {
return err return err
} }
cleanup(g, grids, objectOrder) cleanup(g, gridDiagrams, objectOrder)
return nil return nil
} }
} }
func withoutGrids(ctx context.Context, g *d2graph.Graph) (idToGrid map[string]*grid, objectOrder map[string]int, err error) { func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph) (gridDiagrams map[string]*gridDiagram, objectOrder map[string]int, err error) {
toRemove := make(map[*d2graph.Object]struct{}) toRemove := make(map[*d2graph.Object]struct{})
grids := make(map[string]*grid) gridDiagrams = make(map[string]*gridDiagram)
if len(g.Objects) > 0 { if len(g.Objects) > 0 {
queue := make([]*d2graph.Object, 1, len(g.Objects)) queue := make([]*d2graph.Object, 1, len(g.Objects))
@ -58,12 +58,12 @@ func withoutGrids(ctx context.Context, g *d2graph.Graph) (idToGrid map[string]*g
if len(obj.ChildrenArray) == 0 { if len(obj.ChildrenArray) == 0 {
continue continue
} }
if !obj.IsGrid() { if !obj.IsGridDiagram() {
queue = append(queue, obj.ChildrenArray...) queue = append(queue, obj.ChildrenArray...)
continue continue
} }
grid, err := layoutGrid(g, obj) gd, err := layoutGrid(g, obj)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -71,13 +71,13 @@ func withoutGrids(ctx context.Context, g *d2graph.Graph) (idToGrid map[string]*g
obj.ChildrenArray = nil obj.ChildrenArray = nil
var dx, dy float64 var dx, dy float64
width := grid.width + 2*CONTAINER_PADDING width := gd.width + 2*CONTAINER_PADDING
labelWidth := float64(obj.LabelDimensions.Width) + 2*label.PADDING labelWidth := float64(obj.LabelDimensions.Width) + 2*label.PADDING
if labelWidth > width { if labelWidth > width {
dx = (labelWidth - width) / 2 dx = (labelWidth - width) / 2
width = labelWidth width = labelWidth
} }
height := grid.height + 2*CONTAINER_PADDING height := gd.height + 2*CONTAINER_PADDING
labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING
if labelHeight > CONTAINER_PADDING { if labelHeight > CONTAINER_PADDING {
// if the label doesn't fit within the padding, we need to add more // if the label doesn't fit within the padding, we need to add more
@ -87,14 +87,14 @@ func withoutGrids(ctx context.Context, g *d2graph.Graph) (idToGrid map[string]*g
} }
// we need to center children if we have to expand to fit the container label // we need to center children if we have to expand to fit the container label
if dx != 0 || dy != 0 { if dx != 0 || dy != 0 {
grid.shift(dx, dy) gd.shift(dx, dy)
} }
obj.Box = geo.NewBox(nil, width, height) obj.Box = geo.NewBox(nil, width, height)
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
grids[obj.AbsID()] = grid gridDiagrams[obj.AbsID()] = gd
for _, node := range grid.nodes { for _, node := range gd.nodes {
toRemove[node] = struct{}{} toRemove[node] = struct{}{}
} }
} }
@ -110,20 +110,20 @@ func withoutGrids(ctx context.Context, g *d2graph.Graph) (idToGrid map[string]*g
} }
g.Objects = layoutObjects g.Objects = layoutObjects
return grids, objectOrder, nil return gridDiagrams, objectOrder, nil
} }
func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*grid, error) { func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*gridDiagram, error) {
grid := newGrid(obj) gd := newGridDiagram(obj)
if grid.rows != 0 && grid.columns != 0 { if gd.rows != 0 && gd.columns != 0 {
grid.layoutEvenly(g, obj) gd.layoutEvenly(g, obj)
} else { } else {
grid.layoutDynamic(g, obj) gd.layoutDynamic(g, obj)
} }
// position labels and icons // position labels and icons
for _, n := range grid.nodes { for _, n := range gd.nodes {
if n.Attributes.Icon != nil { if n.Attributes.Icon != nil {
n.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) n.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
n.IconPosition = go2.Pointer(string(label.InsideMiddleCenter)) n.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
@ -132,32 +132,32 @@ func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*grid, error) {
} }
} }
return grid, nil return gd, nil
} }
func (grid *grid) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) { func (gd *gridDiagram) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
// layout nodes in a grid with these 2 properties: // layout nodes in a grid with these 2 properties:
// all nodes in the same row should have the same height // all nodes in the same row should have the same height
// all nodes in the same column should have the same width // all nodes in the same column should have the same width
getNode := func(rowIndex, columnIndex int) *d2graph.Object { getNode := func(rowIndex, columnIndex int) *d2graph.Object {
var index int var index int
if grid.rowDominant { if gd.rowDominant {
index = rowIndex*grid.columns + columnIndex index = rowIndex*gd.columns + columnIndex
} else { } else {
index = columnIndex*grid.rows + rowIndex index = columnIndex*gd.rows + rowIndex
} }
if index < len(grid.nodes) { if index < len(gd.nodes) {
return grid.nodes[index] return gd.nodes[index]
} }
return nil return nil
} }
rowHeights := make([]float64, 0, grid.rows) rowHeights := make([]float64, 0, gd.rows)
colWidths := make([]float64, 0, grid.columns) colWidths := make([]float64, 0, gd.columns)
for i := 0; i < grid.rows; i++ { for i := 0; i < gd.rows; i++ {
rowHeight := 0. rowHeight := 0.
for j := 0; j < grid.columns; j++ { for j := 0; j < gd.columns; j++ {
n := getNode(i, j) n := getNode(i, j)
if n == nil { if n == nil {
break break
@ -166,9 +166,9 @@ func (grid *grid) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
} }
rowHeights = append(rowHeights, rowHeight) rowHeights = append(rowHeights, rowHeight)
} }
for j := 0; j < grid.columns; j++ { for j := 0; j < gd.columns; j++ {
columnWidth := 0. columnWidth := 0.
for i := 0; i < grid.rows; i++ { for i := 0; i < gd.rows; i++ {
n := getNode(i, j) n := getNode(i, j)
if n == nil { if n == nil {
break break
@ -179,9 +179,9 @@ func (grid *grid) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
} }
cursor := geo.NewPoint(0, 0) cursor := geo.NewPoint(0, 0)
if grid.rowDominant { if gd.rowDominant {
for i := 0; i < grid.rows; i++ { for i := 0; i < gd.rows; i++ {
for j := 0; j < grid.columns; j++ { for j := 0; j < gd.columns; j++ {
n := getNode(i, j) n := getNode(i, j)
if n == nil { if n == nil {
break break
@ -195,8 +195,8 @@ func (grid *grid) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
cursor.Y += rowHeights[i] + VERTICAL_PAD cursor.Y += rowHeights[i] + VERTICAL_PAD
} }
} else { } else {
for j := 0; j < grid.columns; j++ { for j := 0; j < gd.columns; j++ {
for i := 0; i < grid.rows; i++ { for i := 0; i < gd.rows; i++ {
n := getNode(i, j) n := getNode(i, j)
if n == nil { if n == nil {
break break
@ -220,11 +220,11 @@ func (grid *grid) layoutEvenly(g *d2graph.Graph, obj *d2graph.Object) {
} }
totalWidth -= HORIZONTAL_PAD totalWidth -= HORIZONTAL_PAD
totalHeight -= VERTICAL_PAD totalHeight -= VERTICAL_PAD
grid.width = totalWidth gd.width = totalWidth
grid.height = totalHeight gd.height = totalHeight
} }
func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) { func (gd *gridDiagram) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
// assume we have the following nodes to layout: // assume we have the following nodes to layout:
// . ┌A──────────────┐ ┌B──┐ ┌C─────────┐ ┌D────────┐ ┌E────────────────┐ // . ┌A──────────────┐ ┌B──┐ ┌C─────────┐ ┌D────────┐ ┌E────────────────┐
// . └───────────────┘ │ │ │ │ │ │ │ │ // . └───────────────┘ │ │ │ │ │ │ │ │
@ -242,16 +242,16 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
// we want to split up the total width across the N rows or columns as evenly as possible // we want to split up the total width across the N rows or columns as evenly as possible
var totalWidth, totalHeight float64 var totalWidth, totalHeight float64
for _, n := range grid.nodes { for _, n := range gd.nodes {
totalWidth += n.Width totalWidth += n.Width
totalHeight += n.Height totalHeight += n.Height
} }
totalWidth += HORIZONTAL_PAD * float64(len(grid.nodes)-1) totalWidth += HORIZONTAL_PAD * float64(len(gd.nodes)-1)
totalHeight += VERTICAL_PAD * float64(len(grid.nodes)-1) totalHeight += VERTICAL_PAD * float64(len(gd.nodes)-1)
layout := [][]int{{}} layout := [][]int{{}}
if grid.rowDominant { if gd.rowDominant {
targetWidth := totalWidth / float64(grid.rows) targetWidth := totalWidth / float64(gd.rows)
rowWidth := 0. rowWidth := 0.
rowIndex := 0 rowIndex := 0
addRow := func() { addRow := func() {
@ -264,7 +264,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
rowWidth += n.Width + HORIZONTAL_PAD rowWidth += n.Width + HORIZONTAL_PAD
} }
for i, n := range grid.nodes { for i, n := range gd.nodes {
// if the next node will be past the target, start a new row // if the next node will be past the target, start a new row
if rowWidth+n.Width+HORIZONTAL_PAD > targetWidth { if rowWidth+n.Width+HORIZONTAL_PAD > targetWidth {
// if the node is mostly past the target, put it on the next row // if the node is mostly past the target, put it on the next row
@ -273,7 +273,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
addNode(i, n) addNode(i, n)
} else { } else {
addNode(i, n) addNode(i, n)
if i < len(grid.nodes)-1 { if i < len(gd.nodes)-1 {
addRow() addRow()
} }
} }
@ -282,7 +282,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
} }
} }
} else { } else {
targetHeight := totalHeight / float64(grid.columns) targetHeight := totalHeight / float64(gd.columns)
colHeight := 0. colHeight := 0.
colIndex := 0 colIndex := 0
addCol := func() { addCol := func() {
@ -295,7 +295,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
colHeight += n.Height + VERTICAL_PAD colHeight += n.Height + VERTICAL_PAD
} }
for i, n := range grid.nodes { for i, n := range gd.nodes {
// if the next node will be past the target, start a new row // if the next node will be past the target, start a new row
if colHeight+n.Height+VERTICAL_PAD > targetHeight { if colHeight+n.Height+VERTICAL_PAD > targetHeight {
// if the node is mostly past the target, put it on the next row // if the node is mostly past the target, put it on the next row
@ -304,7 +304,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
addNode(i, n) addNode(i, n)
} else { } else {
addNode(i, n) addNode(i, n)
if i < len(grid.nodes)-1 { if i < len(gd.nodes)-1 {
addCol() addCol()
} }
} }
@ -316,7 +316,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
cursor := geo.NewPoint(0, 0) cursor := geo.NewPoint(0, 0)
var maxY, maxX float64 var maxY, maxX float64
if grid.rowDominant { if gd.rowDominant {
// if we have 2 rows, then each row's nodes should have the same height // if we have 2 rows, then each row's nodes should have the same height
// . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C) // . ┌A─────────────┐ ┌B──┐ ┌C─────────┐ ┬ maxHeight(A,B,C)
// . ├ ─ ─ ─ ─ ─ ─ ─┤ │ │ │ │ │ // . ├ ─ ─ ─ ─ ─ ─ ─┤ │ │ │ │ │
@ -333,7 +333,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
for _, row := range layout { for _, row := range layout {
rowHeight := 0. rowHeight := 0.
for _, nodeIndex := range row { for _, nodeIndex := range row {
n := grid.nodes[nodeIndex] n := gd.nodes[nodeIndex]
n.TopLeft = cursor.Copy() n.TopLeft = cursor.Copy()
cursor.X += n.Width + HORIZONTAL_PAD cursor.X += n.Width + HORIZONTAL_PAD
rowHeight = math.Max(rowHeight, n.Height) rowHeight = math.Max(rowHeight, n.Height)
@ -344,7 +344,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
// set all nodes in row to the same height // set all nodes in row to the same height
for _, nodeIndex := range row { for _, nodeIndex := range row {
n := grid.nodes[nodeIndex] n := gd.nodes[nodeIndex]
n.Height = rowHeight n.Height = rowHeight
} }
@ -375,7 +375,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
nodes := []*d2graph.Object{} nodes := []*d2graph.Object{}
var widest float64 var widest float64
for _, nodeIndex := range row { for _, nodeIndex := range row {
n := grid.nodes[nodeIndex] n := gd.nodes[nodeIndex]
widest = math.Max(widest, n.Width) widest = math.Max(widest, n.Width)
nodes = append(nodes, n) nodes = append(nodes, n)
} }
@ -387,7 +387,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
if n.Width < widest { if n.Width < widest {
var index int var index int
for i, nodeIndex := range row { for i, nodeIndex := range row {
if n == grid.nodes[nodeIndex] { if n == gd.nodes[nodeIndex] {
index = i index = i
break break
} }
@ -396,7 +396,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
n.Width += grow n.Width += grow
// shift following nodes // shift following nodes
for i := index + 1; i < len(row); i++ { for i := index + 1; i < len(row); i++ {
grid.nodes[row[i]].TopLeft.X += grow gd.nodes[row[i]].TopLeft.X += grow
} }
delta -= grow delta -= grow
if delta <= 0 { if delta <= 0 {
@ -407,7 +407,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
if delta > 0 { if delta > 0 {
grow := delta / float64(len(row)) grow := delta / float64(len(row))
for i := len(row) - 1; i >= 0; i-- { for i := len(row) - 1; i >= 0; i-- {
n := grid.nodes[row[i]] n := gd.nodes[row[i]]
n.TopLeft.X += grow * float64(i) n.TopLeft.X += grow * float64(i)
n.Width += grow n.Width += grow
delta -= grow delta -= grow
@ -430,7 +430,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
for _, column := range layout { for _, column := range layout {
colWidth := 0. colWidth := 0.
for _, nodeIndex := range column { for _, nodeIndex := range column {
n := grid.nodes[nodeIndex] n := gd.nodes[nodeIndex]
n.TopLeft = cursor.Copy() n.TopLeft = cursor.Copy()
cursor.Y += n.Height + VERTICAL_PAD cursor.Y += n.Height + VERTICAL_PAD
colWidth = math.Max(colWidth, n.Width) colWidth = math.Max(colWidth, n.Width)
@ -440,7 +440,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
maxY = math.Max(maxY, colHeight) maxY = math.Max(maxY, colHeight)
// set all nodes in column to the same width // set all nodes in column to the same width
for _, nodeIndex := range column { for _, nodeIndex := range column {
n := grid.nodes[nodeIndex] n := gd.nodes[nodeIndex]
n.Width = colWidth n.Width = colWidth
} }
@ -469,7 +469,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
nodes := []*d2graph.Object{} nodes := []*d2graph.Object{}
var tallest float64 var tallest float64
for _, nodeIndex := range column { for _, nodeIndex := range column {
n := grid.nodes[nodeIndex] n := gd.nodes[nodeIndex]
tallest = math.Max(tallest, n.Height) tallest = math.Max(tallest, n.Height)
nodes = append(nodes, n) nodes = append(nodes, n)
} }
@ -481,7 +481,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
if n.Height < tallest { if n.Height < tallest {
var index int var index int
for i, nodeIndex := range column { for i, nodeIndex := range column {
if n == grid.nodes[nodeIndex] { if n == gd.nodes[nodeIndex] {
index = i index = i
break break
} }
@ -490,7 +490,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
n.Height += grow n.Height += grow
// shift following nodes // shift following nodes
for i := index + 1; i < len(column); i++ { for i := index + 1; i < len(column); i++ {
grid.nodes[column[i]].TopLeft.Y += grow gd.nodes[column[i]].TopLeft.Y += grow
} }
delta -= grow delta -= grow
if delta <= 0 { if delta <= 0 {
@ -501,7 +501,7 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
if delta > 0 { if delta > 0 {
grow := delta / float64(len(column)) grow := delta / float64(len(column))
for i := len(column) - 1; i >= 0; i-- { for i := len(column) - 1; i >= 0; i-- {
n := grid.nodes[column[i]] n := gd.nodes[column[i]]
n.TopLeft.Y += grow * float64(i) n.TopLeft.Y += grow * float64(i)
n.Height += grow n.Height += grow
delta -= grow delta -= grow
@ -509,40 +509,40 @@ func (grid *grid) layoutDynamic(g *d2graph.Graph, obj *d2graph.Object) {
} }
} }
} }
grid.width = maxX gd.width = maxX
grid.height = maxY gd.height = maxY
} }
// cleanup restores the graph after the core layout engine finishes // cleanup restores the graph after the core layout engine finishes
// - translating the grid to its position placed by the core layout engine // - translating the grid to its position placed by the core layout engine
// - restore the children of the grid // - restore the children of the grid
// - sorts objects to their original graph order // - sorts objects to their original graph order
func cleanup(graph *d2graph.Graph, grids map[string]*grid, objectsOrder map[string]int) { func cleanup(graph *d2graph.Graph, gridDiagrams map[string]*gridDiagram, objectsOrder map[string]int) {
defer func() { defer func() {
sort.SliceStable(graph.Objects, func(i, j int) bool { sort.SliceStable(graph.Objects, func(i, j int) bool {
return objectsOrder[graph.Objects[i].AbsID()] < objectsOrder[graph.Objects[j].AbsID()] return objectsOrder[graph.Objects[i].AbsID()] < objectsOrder[graph.Objects[j].AbsID()]
}) })
}() }()
if graph.Root.IsGrid() { if graph.Root.IsGridDiagram() {
grid, exists := grids[graph.Root.AbsID()] gd, exists := gridDiagrams[graph.Root.AbsID()]
if exists { if exists {
grid.cleanup(graph.Root, graph) gd.cleanup(graph.Root, graph)
return return
} }
} }
for _, obj := range graph.Objects { for _, obj := range graph.Objects {
grid, exists := grids[obj.AbsID()] gd, exists := gridDiagrams[obj.AbsID()]
if !exists { if !exists {
continue continue
} }
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
// shift the grid from (0, 0) // shift the grid from (0, 0)
grid.shift( gd.shift(
obj.TopLeft.X+CONTAINER_PADDING, obj.TopLeft.X+CONTAINER_PADDING,
obj.TopLeft.Y+CONTAINER_PADDING, obj.TopLeft.Y+CONTAINER_PADDING,
) )
grid.cleanup(obj, graph) gd.cleanup(obj, graph)
} }
} }

View file

@ -5,15 +5,15 @@
"errs": [ "errs": [
{ {
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,2:1:17-2:7:23", "range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,2:1:17-2:7:23",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:3:2: edge \"a -> b\" cannot enter grid \"hey\"" "errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:3:2: edge \"a -> b\" cannot enter grid diagram \"hey\""
}, },
{ {
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,4:1:27-4:11:37", "range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,4:1:27-4:11:37",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:5:2: edge \"c -> hey.b\" cannot enter grid \"hey\"" "errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:5:2: edge \"c -> hey.b\" cannot enter grid diagram \"hey\""
}, },
{ {
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,5:1:39-5:11:49", "range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,5:1:39-5:11:49",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:2: edge \"hey.a -> c\" cannot enter grid \"hey\"" "errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:2: edge \"hey.a -> c\" cannot enter grid diagram \"hey\""
} }
] ]
} }

View file

@ -5,11 +5,11 @@
"errs": [ "errs": [
{ {
"range": "d2/testdata/d2compiler/TestCompile/grid_nested.d2,1:1:8-1:10:17", "range": "d2/testdata/d2compiler/TestCompile/grid_nested.d2,1:1:8-1:10:17",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_nested.d2:2:2: invalid grid \"hey\". can only set \"rows\" with no descendants (see \"hey.d.invalid descendant\")" "errmsg": "d2/testdata/d2compiler/TestCompile/grid_nested.d2:2:2: invalid grid diagram \"hey\". can only set \"rows\" with no descendants (see \"hey.d.invalid descendant\")"
}, },
{ {
"range": "d2/testdata/d2compiler/TestCompile/grid_nested.d2,2:1:19-2:13:31", "range": "d2/testdata/d2compiler/TestCompile/grid_nested.d2,2:1:19-2:13:31",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_nested.d2:3:2: invalid grid \"hey\". can only set \"columns\" with no descendants (see \"hey.d.invalid descendant\")" "errmsg": "d2/testdata/d2compiler/TestCompile/grid_nested.d2:3:2: invalid grid diagram \"hey\". can only set \"columns\" with no descendants (see \"hey.d.invalid descendant\")"
} }
] ]
} }