fix reconnecting nested edges through serialization

This commit is contained in:
Gavin Nishizawa 2023-11-28 10:24:39 -08:00
parent bd16b04244
commit f9060a90d6
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD

View file

@ -83,6 +83,7 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
extracted := make(map[string]*d2graph.Graph) extracted := make(map[string]*d2graph.Graph)
var extractedOrder []string var extractedOrder []string
var extractedEdges []*d2graph.Edge var extractedEdges []*d2graph.Edge
var extractedEdgeIDs []edgeIDs
var constantNears []*d2graph.Graph var constantNears []*d2graph.Graph
restoreOrder := SaveOrder(g) restoreOrder := SaveOrder(g)
@ -114,7 +115,7 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
// │ │ │ │ └──┘ └──┘ │ │ │ │ │ │ // │ │ │ │ └──┘ └──┘ │ │ │ │ │ │
// │ └────┘ └───────────────┘ │ │ └────┘ │ // │ └────┘ └───────────────┘ │ │ └────┘ │
// └───────────────────────────────┘ └───────────────────────────────┘ // └───────────────────────────────┘ └───────────────────────────────┘
nestedGraph, externalEdges := ExtractSubgraph(curr, true) nestedGraph, externalEdges, externalEdgeIDs := ExtractSubgraph(curr, true)
// Then we layout curr as a nested graph and re-inject it // Then we layout curr as a nested graph and re-inject it
id := curr.AbsID() id := curr.AbsID()
@ -142,13 +143,13 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
if err != nil { if err != nil {
return err return err
} }
for _, e := range externalEdges { for i, e := range externalEdges {
src, err := lookup(e.Src.AbsID()) src, err := lookup(externalEdgeIDs[i].srcID)
if err != nil { if err != nil {
return err return err
} }
e.Src = src e.Src = src
dst, err := lookup(e.Dst.AbsID()) dst, err := lookup(externalEdgeIDs[i].dstID)
if err != nil { if err != nil {
return err return err
} }
@ -182,8 +183,9 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
// │ │ │ │ └──┘ └──┘ │ │ │ │ │ │ │ │ // │ │ │ │ └──┘ └──┘ │ │ │ │ │ │ │ │
// │ └────┘ └───────────────┘ │ │ └────┘ └───────────────┘ │ // │ └────┘ └───────────────┘ │ │ └────┘ └───────────────┘ │
// └───────────────────────────────┘ └───────────────────────────────┘ // └───────────────────────────────┘ └───────────────────────────────┘
nestedGraph, externalEdges = ExtractSubgraph(curr, false) nestedGraph, externalEdges, externalEdgeIDs = ExtractSubgraph(curr, false)
extractedEdges = append(extractedEdges, externalEdges...) extractedEdges = append(extractedEdges, externalEdges...)
extractedEdgeIDs = append(extractedEdgeIDs, externalEdgeIDs...)
extracted[id] = nestedGraph extracted[id] = nestedGraph
extractedOrder = append(extractedOrder, id) extractedOrder = append(extractedOrder, id)
@ -197,8 +199,9 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
} }
// There is a nested diagram here, so extract its contents and process in the same way // There is a nested diagram here, so extract its contents and process in the same way
nestedGraph, externalEdges := ExtractSubgraph(curr, gi.IsConstantNear) nestedGraph, externalEdges, externalEdgeIDs := ExtractSubgraph(curr, gi.IsConstantNear)
extractedEdges = append(extractedEdges, externalEdges...) extractedEdges = append(extractedEdges, externalEdges...)
extractedEdgeIDs = append(extractedEdgeIDs, externalEdgeIDs...)
log.Info(ctx, "layout nested", slog.F("level", curr.Level()), slog.F("child", curr.AbsID()), slog.F("gi", gi)) log.Info(ctx, "layout nested", slog.F("level", curr.Level()), slog.F("child", curr.AbsID()), slog.F("gi", gi))
nestedInfo := gi nestedInfo := gi
@ -299,16 +302,17 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
// Restore cross-graph edges and route them // Restore cross-graph edges and route them
g.Edges = append(g.Edges, extractedEdges...) g.Edges = append(g.Edges, extractedEdges...)
for _, e := range extractedEdges { for i, e := range extractedEdges {
ids := extractedEdgeIDs[i]
// update object references // update object references
src, exists := idToObj[e.Src.AbsID()] src, exists := idToObj[ids.srcID]
if !exists { if !exists {
return fmt.Errorf("could not find object %#v after layout", e.Src.AbsID()) return fmt.Errorf("could not find object %#v after layout", ids.srcID)
} }
e.Src = src e.Src = src
dst, exists := idToObj[e.Dst.AbsID()] dst, exists := idToObj[ids.dstID]
if !exists { if !exists {
return fmt.Errorf("could not find object %#v after layout", e.Dst.AbsID()) return fmt.Errorf("could not find object %#v after layout", ids.dstID)
} }
e.Dst = dst e.Dst = dst
} }
@ -318,15 +322,16 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
return err return err
} }
// need to update pointers if plugin performs edge routing // need to update pointers if plugin performs edge routing
for _, e := range extractedEdges { for i, e := range extractedEdges {
src, exists := idToObj[e.Src.AbsID()] ids := extractedEdgeIDs[i]
src, exists := idToObj[ids.srcID]
if !exists { if !exists {
return fmt.Errorf("could not find object %#v after routing", e.Src.AbsID()) return fmt.Errorf("could not find object %#v after routing", ids.srcID)
} }
e.Src = src e.Src = src
dst, exists := idToObj[e.Dst.AbsID()] dst, exists := idToObj[ids.dstID]
if !exists { if !exists {
return fmt.Errorf("could not find object %#v after routing", e.Dst.AbsID()) return fmt.Errorf("could not find object %#v after routing", ids.dstID)
} }
e.Dst = dst e.Dst = dst
} }
@ -360,7 +365,11 @@ func NestedGraphInfo(obj *d2graph.Object) (gi GraphInfo) {
return gi return gi
} }
func ExtractSubgraph(container *d2graph.Object, includeSelf bool) (nestedGraph *d2graph.Graph, externalEdges []*d2graph.Edge) { type edgeIDs struct {
srcID, dstID string
}
func ExtractSubgraph(container *d2graph.Object, includeSelf bool) (nestedGraph *d2graph.Graph, externalEdges []*d2graph.Edge, externalEdgeIDs []edgeIDs) {
// includeSelf: when we have a constant near or a grid cell that is a container, // includeSelf: when we have a constant near or a grid cell that is a container,
// we want to include itself in the nested graph, not just its descendants, // we want to include itself in the nested graph, not just its descendants,
nestedGraph = d2graph.NewGraph() nestedGraph = d2graph.NewGraph()
@ -399,6 +408,11 @@ func ExtractSubgraph(container *d2graph.Object, includeSelf bool) (nestedGraph *
nestedGraph.Edges = append(nestedGraph.Edges, edge) nestedGraph.Edges = append(nestedGraph.Edges, edge)
} else if srcIsNested || dstIsNested { } else if srcIsNested || dstIsNested {
externalEdges = append(externalEdges, edge) externalEdges = append(externalEdges, edge)
// we need these AbsIDs when reconnecting since parent references may become stale
externalEdgeIDs = append(externalEdgeIDs, edgeIDs{
srcID: edge.Src.AbsID(),
dstID: edge.Dst.AbsID(),
})
} else { } else {
remainingEdges = append(remainingEdges, edge) remainingEdges = append(remainingEdges, edge)
} }
@ -446,7 +460,7 @@ func ExtractSubgraph(container *d2graph.Object, includeSelf bool) (nestedGraph *
container.ChildrenArray = nil container.ChildrenArray = nil
} }
return nestedGraph, externalEdges return nestedGraph, externalEdges, externalEdgeIDs
} }
func InjectNested(container *d2graph.Object, nestedGraph *d2graph.Graph, isRoot bool) { func InjectNested(container *d2graph.Object, nestedGraph *d2graph.Graph, isRoot bool) {