rename to grid diagram
This commit is contained in:
parent
44f2d7a47f
commit
06a942cf99
7 changed files with 114 additions and 114 deletions
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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")`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
@ -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...)
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
testdata/d2compiler/TestCompile/grid_edge.exp.json
generated
vendored
6
testdata/d2compiler/TestCompile/grid_edge.exp.json
generated
vendored
|
|
@ -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\""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
testdata/d2compiler/TestCompile/grid_nested.exp.json
generated
vendored
4
testdata/d2compiler/TestCompile/grid_nested.exp.json
generated
vendored
|
|
@ -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\")"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue