d2/d2layouts/d2layouts.go

125 lines
3.5 KiB
Go
Raw Normal View History

2023-09-14 20:38:59 +00:00
package d2layouts
2023-09-14 21:17:18 +00:00
import (
2023-09-14 22:38:52 +00:00
"strings"
2023-09-14 21:17:18 +00:00
"oss.terrastruct.com/d2/d2graph"
)
2023-09-14 20:38:59 +00:00
2023-09-14 21:17:18 +00:00
type GraphType string
const (
DefaultGraphType GraphType = ""
ConstantNearGraph GraphType = "constant-near"
GridDiagram GraphType = "grid-diagram"
SequenceDiagram GraphType = "sequence-diagram"
)
2023-09-14 21:55:52 +00:00
func LayoutNested(g *d2graph.Graph, graphType GraphType, coreLayout d2graph.LayoutGraph) {
2023-09-14 20:38:59 +00:00
// Before we can layout these nodes, we need to handle all nested diagrams first.
extracted := make(map[*d2graph.Object]*d2graph.Graph)
// Iterate top-down from Root so all nested diagrams can process their own contents
queue := make([]*d2graph.Object, 0, len(g.Root.ChildrenArray))
queue = append(queue, g.Root.ChildrenArray...)
for _, child := range queue {
2023-09-14 21:55:52 +00:00
if graphType := NestedGraphType(child); graphType != DefaultGraphType {
2023-09-14 20:38:59 +00:00
// There is a nested diagram here, so extract its contents and process in the same way
nestedGraph := ExtractNested(child)
// Layout of nestedGraph is completed
2023-09-14 21:55:52 +00:00
LayoutNested(nestedGraph, graphType, coreLayout)
2023-09-14 20:38:59 +00:00
// Fit child to size of nested layout
FitToGraph(child, nestedGraph)
// We will restore the contents after running layout with child as the placeholder
extracted[child] = nestedGraph
} else if len(child.Children) > 0 {
queue = append(queue, child.ChildrenArray...)
}
}
// We can now run layout with accurate sizes of nested layout containers
// Layout according to the type of diagram
LayoutDiagram(g, graphType, coreLayout)
// With the layout set, inject all the extracted graphs
for n, nestedGraph := range extracted {
InjectNested(n, nestedGraph)
}
}
2023-09-14 21:55:52 +00:00
func NestedGraphType(obj *d2graph.Object) GraphType {
if obj.Graph.RootLevel == 0 && obj.IsConstantNear() {
2023-09-14 21:17:18 +00:00
return ConstantNearGraph
}
2023-09-14 21:55:52 +00:00
if obj.IsGridDiagram() {
2023-09-14 21:17:18 +00:00
return GridDiagram
}
2023-09-14 21:55:52 +00:00
if obj.IsSequenceDiagram() {
2023-09-14 21:17:18 +00:00
return SequenceDiagram
}
return DefaultGraphType
2023-09-14 20:38:59 +00:00
}
func ExtractNested(container *d2graph.Object) *d2graph.Graph {
2023-09-14 22:38:52 +00:00
tempGraph := d2graph.NewGraph()
tempGraph.RootLevel = int(container.Level())
// separate out nested edges
g := container.Graph
remainingEdges := make([]*d2graph.Edge, 0, len(g.Edges))
for _, edge := range g.Edges {
if edge.Src.Parent.IsDescendantOf(container) && edge.Dst.Parent.IsDescendantOf(container) {
tempGraph.Edges = append(tempGraph.Edges, edge)
} else {
remainingEdges = append(remainingEdges, edge)
}
}
g.Edges = remainingEdges
// separate out nested objects
remainingObjects := make([]*d2graph.Object, 0, len(g.Objects))
for _, obj := range g.Objects {
if obj.IsDescendantOf(container) {
tempGraph.Objects = append(tempGraph.Objects, obj)
} else {
remainingObjects = append(remainingObjects, obj)
}
}
g.Objects = remainingObjects
// update object and new root references
for _, o := range tempGraph.Objects {
o.Graph = tempGraph
}
// set root references
tempGraph.Root.ChildrenArray = append(tempGraph.Root.ChildrenArray, container.ChildrenArray...)
for _, child := range container.ChildrenArray {
child.Parent = tempGraph.Root
tempGraph.Root.Children[strings.ToLower(child.ID)] = child
}
// remove container's references
for k := range container.Children {
delete(container.Children, k)
}
container.ChildrenArray = nil
return tempGraph
2023-09-14 20:38:59 +00:00
}
func InjectNested(container *d2graph.Object, graph *d2graph.Graph) {
// TODO
}
func FitToGraph(container *d2graph.Object, graph *d2graph.Graph) {
// TODO
}
2023-09-14 21:17:18 +00:00
func LayoutDiagram(graph *d2graph.Graph, graphType GraphType, coreLayout d2graph.LayoutGraph) {
2023-09-14 20:38:59 +00:00
// TODO
}