Merge pull request #1629 from gavin-ts/nested-edges

nested edges within grid cells
This commit is contained in:
gavin-ts 2023-10-02 11:03:57 -07:00 committed by GitHub
commit f7d48b24a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 2010 additions and 410 deletions

View file

@ -2,6 +2,8 @@
#### Improvements 🧹
- Grid cells can now contain nested edges [#1629](https://github.com/terrastruct/d2/pull/1629)
#### Bugfixes ⛑️
- Grid layout now accounts for each cell's outside labels and icons [#1624](https://github.com/terrastruct/d2/pull/1624)

View file

@ -1089,34 +1089,59 @@ func (c *compiler) validateNear(g *d2graph.Graph) {
func (c *compiler) validateEdges(g *d2graph.Graph) {
for _, edge := range g.Edges {
// edges from a grid to something outside is ok
// grid -> outside : ok
// grid -> grid.cell : not ok
// grid -> grid.cell.inner : not ok
if edge.Src.IsGridDiagram() && edge.Dst.IsDescendantOf(edge.Src) {
c.errorf(edge.GetAstEdge(), "edge from grid diagram %#v cannot enter itself", edge.Src.AbsID())
continue
}
if edge.Dst.IsGridDiagram() && edge.Src.IsDescendantOf(edge.Dst) {
c.errorf(edge.GetAstEdge(), "edge from grid diagram %#v cannot enter itself", edge.Dst.AbsID())
continue
}
srcGrid := edge.Src.Parent.ClosestGridDiagram()
dstGrid := edge.Dst.Parent.ClosestGridDiagram()
if srcGrid != nil || dstGrid != nil {
if top := srcGrid.TopGridDiagram(); srcGrid != top {
// valid: grid.child1 -> grid.child2
// invalid: grid.childGrid.child1 -> grid.childGrid.child2
c.errorf(edge.GetAstEdge(), "edge must be on direct child of grid diagram %#v", top.AbsID())
continue
}
if top := dstGrid.TopGridDiagram(); dstGrid != top {
// valid: grid.child1 -> grid.child2
// invalid: grid.childGrid.child1 -> grid.childGrid.child2
c.errorf(edge.GetAstEdge(), "edge must be on direct child of grid diagram %#v", top.AbsID())
continue
}
if srcGrid != dstGrid {
// valid: a -> grid
// invalid: a -> grid.child
c.errorf(edge.GetAstEdge(), "edges into grid diagrams are not supported yet")
if dstGrid != nil && !(srcGrid != nil && srcGrid.IsDescendantOf(dstGrid)) {
c.errorf(edge.GetAstEdge(), "edge cannot enter grid diagram %#v", dstGrid.AbsID())
} else {
c.errorf(edge.GetAstEdge(), "edge cannot exit grid diagram %#v", srcGrid.AbsID())
}
continue
}
if srcGrid != edge.Src.Parent || dstGrid != edge.Dst.Parent {
// valid: grid.child1 -> grid.child2
// invalid: grid.child1 -> grid.child2.child1
c.errorf(edge.GetAstEdge(), "grid diagrams can only have edges between children right now")
srcCell := edge.Src.ClosestGridCell()
dstCell := edge.Dst.ClosestGridCell()
// edges within a grid cell are ok now
// grid.cell.a -> grid.cell.b : ok
// grid.cell.a.c -> grid.cell.b.d : ok
// edges between grid cells themselves are ok
// grid.cell -> grid.cell2 : ok
// grid.cell -> grid.cell.inside : not ok
// grid.cell -> grid.cell2.inside : not ok
srcIsGridCell := edge.Src == srcCell
dstIsGridCell := edge.Dst == dstCell
if srcIsGridCell != dstIsGridCell {
if srcIsGridCell {
c.errorf(edge.GetAstEdge(), "grid cell %#v can only connect to another grid cell", edge.Src.AbsID())
} else {
c.errorf(edge.GetAstEdge(), "grid cell %#v can only connect to another grid cell", edge.Dst.AbsID())
}
continue
}
if srcCell != dstCell && (!srcIsGridCell || !dstIsGridCell) {
c.errorf(edge.GetAstEdge(), "edge cannot exit grid cell %#v", srcCell.AbsID())
continue
}
}
}
}

View file

@ -2478,15 +2478,15 @@ d2/testdata/d2compiler/TestCompile/grid_gap_negative.d2:3:16: vertical-gap must
grid-rows: 1
a -> b: ok
}
c -> hey.b
hey.a -> c
hey -> hey.a
c -> hey.b
hey.a -> c
hey -> hey.a
hey -> c: ok
hey -> c: ok
`,
expErr: `d2/testdata/d2compiler/TestCompile/grid_edge.d2:5:2: edges into grid diagrams are not supported yet
d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:2: edges into grid diagrams are not supported yet
d2/testdata/d2compiler/TestCompile/grid_edge.d2:7:2: edges into grid diagrams are not supported yet`,
expErr: `d2/testdata/d2compiler/TestCompile/grid_edge.d2:5:1: edge cannot enter grid diagram "hey"
d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:1: edge cannot exit grid diagram "hey"
d2/testdata/d2compiler/TestCompile/grid_edge.d2:7:1: edge from grid diagram "hey" cannot enter itself`,
},
{
name: "grid_deeper_edge",
@ -2494,16 +2494,31 @@ d2/testdata/d2compiler/TestCompile/grid_edge.d2:7:2: edges into grid diagrams ar
grid-rows: 1
a -> b: ok
b: {
c -> d: not yet
c -> d: ok now
c.e -> c.f.g: ok
c.e -> d.h: ok
c -> d.h: ok
}
a: {
grid-columns: 1
e -> f: also not yet
e -> f: also ok now
e: {
g -> h: ok
g -> h.h: ok
}
e -> f.i: not ok
e.g -> f.i: not ok
}
a -> b.c: not yet
a.e -> b.c: also not yet
a -> a.e: not ok
}
`,
expErr: `d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:9:3: edge must be on direct child of grid diagram "hey"
d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:5:3: grid diagrams can only have edges between children right now`,
expErr: `d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:17:3: grid cell "hey.a.e" can only connect to another grid cell
d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:18:3: edge cannot exit grid cell "hey.a.e"
d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:20:2: grid cell "hey.a" can only connect to another grid cell
d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:21:2: edge cannot exit grid diagram "hey.a"
d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:22:2: edge from grid diagram "hey.a" cannot enter itself`,
},
{
name: "grid_nested",

View file

@ -15,6 +15,17 @@ func (obj *Object) ClosestGridDiagram() *Object {
return obj.Parent.ClosestGridDiagram()
}
func (obj *Object) ClosestGridCell() *Object {
if obj == nil {
return nil
}
// grid cells can be a nested grid diagram
if obj.Parent.IsGridDiagram() {
return obj
}
return obj.Parent.ClosestGridCell()
}
// TopGridDiagram returns the least nested (outermost) grid diagram
func (obj *Object) TopGridDiagram() *Object {
if obj == nil {

View file

@ -106,6 +106,7 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
if err != nil {
return err
}
InjectNested(g.Root, nestedGraph, false)
restoreOrder()
@ -122,15 +123,25 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
}
curr = obj
dx := -curr.TopLeft.X
dy := -curr.TopLeft.Y
// position nested graph (excluding curr) relative to curr
dx := 0 - curr.TopLeft.X
dy := 0 - curr.TopLeft.Y
for _, o := range nestedGraph.Objects {
if o.AbsID() == curr.AbsID() {
continue
}
o.TopLeft.X += dx
o.TopLeft.Y += dy
}
for _, e := range nestedGraph.Edges {
e.Move(dx, dy)
}
// now we keep the descendants out until after grid layout
nestedGraph = ExtractSubgraph(curr, false)
extracted[id] = nestedGraph
extractedOrder = append(extractedOrder, id)
continue
}
@ -326,7 +337,6 @@ func ExtractSubgraph(container *d2graph.Object, includeSelf bool) *d2graph.Graph
}
func InjectNested(container *d2graph.Object, nestedGraph *d2graph.Graph, isRoot bool) {
// TODO restore order of objects
g := container.Graph
for _, obj := range nestedGraph.Root.ChildrenArray {
obj.Parent = container
@ -358,6 +368,9 @@ func PositionNested(container *d2graph.Object, nestedGraph *d2graph.Graph) {
// Note: assumes nestedGraph's layout has contents positioned relative to 0,0
dx := container.TopLeft.X //- tl.X
dy := container.TopLeft.Y //- tl.Y
if dx == 0 && dy == 0 {
return
}
for _, o := range nestedGraph.Objects {
o.TopLeft.X += dx
o.TopLeft.Y += dy

View file

@ -8,18 +8,18 @@ outer-grid: {
container: {
label.near: top-left
# edges not yet supported here since they must be direct grid children
a
b
c
(** -> **)[*].class: red
# edges on grid descendant now supported
a -> b -> c -> a
d -> e -> g.h.i
d -> f -> g.h
b -> g
}
inner-grid: {
grid-rows: 1
1
2
3
# edges here are not supported yet since this is inside another grid
# edges inside another grid now supported
1 -> 2 -> 3: {class: red}
}
}
@ -41,3 +41,5 @@ outer-container: {
}
}
}
classes.red.style.stroke: red

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 32 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View file

@ -3,12 +3,24 @@
"err": {
"errs": [
{
"range": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2,8:2:86-8:8:92",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:9:3: edge must be on direct child of grid diagram \"hey\""
"range": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2,16:2:199-16:10:207",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:17:3: grid cell \"hey.a.e\" can only connect to another grid cell"
},
{
"range": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2,4:2:41-4:8:47",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:5:3: grid diagrams can only have edges between children right now"
"range": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2,17:2:218-17:12:228",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:18:3: edge cannot exit grid cell \"hey.a.e\""
},
{
"range": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2,19:1:241-19:9:249",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:20:2: grid cell \"hey.a\" can only connect to another grid cell"
},
{
"range": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2,20:1:260-20:11:270",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:21:2: edge cannot exit grid diagram \"hey.a\""
},
{
"range": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2,21:1:286-21:9:294",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_deeper_edge.d2:22:2: edge from grid diagram \"hey.a\" cannot enter itself"
}
]
}

View file

@ -3,16 +3,16 @@
"err": {
"errs": [
{
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,4:1:36-4:11:46",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:5:2: edges into grid diagrams are not supported yet"
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,4:0:35-4:10:45",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:5:1: edge cannot enter grid diagram \"hey\""
},
{
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,5:1:48-5:11:58",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:2: edges into grid diagrams are not supported yet"
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,5:0:46-5:10:56",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:6:1: edge cannot exit grid diagram \"hey\""
},
{
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,6:1:60-6:13:72",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:7:2: edges into grid diagrams are not supported yet"
"range": "d2/testdata/d2compiler/TestCompile/grid_edge.d2,6:0:57-6:12:69",
"errmsg": "d2/testdata/d2compiler/TestCompile/grid_edge.d2:7:1: edge from grid diagram \"hey\" cannot enter itself"
}
]
}