cleanup
This commit is contained in:
parent
9ecbc61945
commit
cee6a92722
7 changed files with 10 additions and 553 deletions
|
|
@ -17,9 +17,8 @@ import (
|
|||
"oss.terrastruct.com/d2/d2compiler"
|
||||
"oss.terrastruct.com/d2/d2exporter"
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2grid"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||
"oss.terrastruct.com/d2/d2lib"
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
"oss.terrastruct.com/d2/lib/geo"
|
||||
|
|
@ -235,7 +234,8 @@ func run(t *testing.T, tc testCase) {
|
|||
err = g.SetDimensions(nil, ruler, nil)
|
||||
assert.JSON(t, nil, err)
|
||||
|
||||
err = d2sequence.Layout(ctx, g, d2grid.Layout(ctx, g, d2dagrelayout.DefaultLayout))
|
||||
graphInfo := d2layouts.NestedGraphInfo(g.Root)
|
||||
err = d2layouts.LayoutNested(ctx, g, graphInfo, d2dagrelayout.DefaultLayout)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
|
|
@ -21,42 +20,15 @@ const (
|
|||
|
||||
// Layout runs the grid layout on containers with rows/columns
|
||||
// Note: children are not allowed edges or descendants
|
||||
//
|
||||
// 1. Traverse graph from root, skip objects with no rows/columns
|
||||
// 2. Construct a grid with the container children
|
||||
// 3. Remove the children from the main graph
|
||||
// 4. Run grid layout
|
||||
// 5. Set the resulting dimensions to the main graph shape
|
||||
// 6. Run core layouts (without grid children)
|
||||
// 7. Put grid children back in correct location
|
||||
func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) d2graph.LayoutGraph {
|
||||
return func(ctx context.Context, g *d2graph.Graph) error {
|
||||
gridDiagrams, objectOrder, edgeOrder, err := withoutGridDiagrams(ctx, g, layout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if g.Root.IsGridDiagram() && len(g.Root.ChildrenArray) != 0 {
|
||||
g.Root.TopLeft = geo.NewPoint(0, 0)
|
||||
} else if err := layout(ctx, g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cleanup(g, gridDiagrams, objectOrder, edgeOrder)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func Layout2(ctx context.Context, g *d2graph.Graph) error {
|
||||
|
||||
// 1. Run grid layout on the graph root
|
||||
// 2. Set the resulting dimensions to the graph root
|
||||
func Layout(ctx context.Context, g *d2graph.Graph) error {
|
||||
obj := g.Root
|
||||
|
||||
gd, err := layoutGrid(g, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// obj.Children = make(map[string]*d2graph.Object)
|
||||
// obj.ChildrenArray = nil
|
||||
|
||||
if obj.Box != nil {
|
||||
// CONTAINER_PADDING is default, but use gap value if set
|
||||
|
|
@ -163,13 +135,11 @@ func Layout2(ctx context.Context, g *d2graph.Graph) error {
|
|||
|
||||
// simple straight line edge routing between grid objects
|
||||
for _, e := range g.Edges {
|
||||
// edgeOrder[e.AbsID()] = i
|
||||
if !e.Src.Parent.IsDescendantOf(obj) && !e.Dst.Parent.IsDescendantOf(obj) {
|
||||
continue
|
||||
}
|
||||
// if edge is within grid, remove it from outer layout
|
||||
gd.edges = append(gd.edges, e)
|
||||
// edgeToRemove[e] = struct{}{}
|
||||
|
||||
if e.Src.Parent != obj || e.Dst.Parent != obj {
|
||||
continue
|
||||
|
|
@ -206,233 +176,6 @@ func Layout2(ctx context.Context, g *d2graph.Graph) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func withoutGridDiagrams(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) (gridDiagrams map[string]*gridDiagram, objectOrder, edgeOrder map[string]int, err error) {
|
||||
toRemove := make(map[*d2graph.Object]struct{})
|
||||
edgeToRemove := make(map[*d2graph.Edge]struct{})
|
||||
gridDiagrams = make(map[string]*gridDiagram)
|
||||
|
||||
objectOrder = make(map[string]int)
|
||||
for i, obj := range g.Objects {
|
||||
objectOrder[obj.AbsID()] = i
|
||||
}
|
||||
edgeOrder = make(map[string]int)
|
||||
for i, edge := range g.Edges {
|
||||
edgeOrder[edge.AbsID()] = i
|
||||
}
|
||||
|
||||
var processGrid func(obj *d2graph.Object) error
|
||||
processGrid = func(obj *d2graph.Object) error {
|
||||
for _, child := range obj.ChildrenArray {
|
||||
if len(child.ChildrenArray) == 0 {
|
||||
continue
|
||||
}
|
||||
if child.IsGridDiagram() {
|
||||
if err := processGrid(child); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
tempGraph := g.ExtractAsNestedGraph(child)
|
||||
if err := layout(ctx, tempGraph); err != nil {
|
||||
return err
|
||||
}
|
||||
g.InjectNestedGraph(tempGraph, obj)
|
||||
|
||||
sort.SliceStable(g.Objects, func(i, j int) bool {
|
||||
return objectOrder[g.Objects[i].AbsID()] < objectOrder[g.Objects[j].AbsID()]
|
||||
})
|
||||
sort.SliceStable(child.ChildrenArray, func(i, j int) bool {
|
||||
return objectOrder[child.ChildrenArray[i].AbsID()] < objectOrder[child.ChildrenArray[j].AbsID()]
|
||||
})
|
||||
sort.SliceStable(obj.ChildrenArray, func(i, j int) bool {
|
||||
return objectOrder[obj.ChildrenArray[i].AbsID()] < objectOrder[obj.ChildrenArray[j].AbsID()]
|
||||
})
|
||||
sort.SliceStable(g.Edges, func(i, j int) bool {
|
||||
return edgeOrder[g.Edges[i].AbsID()] < edgeOrder[g.Edges[j].AbsID()]
|
||||
})
|
||||
|
||||
for _, o := range tempGraph.Objects {
|
||||
toRemove[o] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gd, err := layoutGrid(g, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
obj.Children = make(map[string]*d2graph.Object)
|
||||
obj.ChildrenArray = nil
|
||||
|
||||
if obj.Box != nil {
|
||||
// CONTAINER_PADDING is default, but use gap value if set
|
||||
horizontalPadding, verticalPadding := CONTAINER_PADDING, CONTAINER_PADDING
|
||||
if obj.GridGap != nil || obj.HorizontalGap != nil {
|
||||
horizontalPadding = gd.horizontalGap
|
||||
}
|
||||
if obj.GridGap != nil || obj.VerticalGap != nil {
|
||||
verticalPadding = gd.verticalGap
|
||||
}
|
||||
|
||||
// size shape according to grid
|
||||
obj.SizeToContent(gd.width, gd.height, float64(2*horizontalPadding), float64(2*verticalPadding))
|
||||
|
||||
// compute where the grid should be placed inside shape
|
||||
s := obj.ToShape()
|
||||
innerBox := s.GetInnerBox()
|
||||
if innerBox.TopLeft.X != 0 || innerBox.TopLeft.Y != 0 {
|
||||
gd.shift(innerBox.TopLeft.X, innerBox.TopLeft.Y)
|
||||
}
|
||||
|
||||
// compute how much space the label and icon occupy
|
||||
var occupiedWidth, occupiedHeight float64
|
||||
if obj.Icon != nil {
|
||||
iconSpace := float64(d2target.MAX_ICON_SIZE + 2*label.PADDING)
|
||||
occupiedWidth = iconSpace
|
||||
occupiedHeight = iconSpace
|
||||
}
|
||||
|
||||
var dx, dy float64
|
||||
if obj.LabelDimensions.Height != 0 {
|
||||
occupiedHeight = math.Max(
|
||||
occupiedHeight,
|
||||
float64(obj.LabelDimensions.Height)+2*label.PADDING,
|
||||
)
|
||||
}
|
||||
if obj.LabelDimensions.Width != 0 {
|
||||
// . ├────┤───────├────┤
|
||||
// . icon label icon
|
||||
// with an icon in top left we need 2x the space to fit the label in the center
|
||||
occupiedWidth *= 2
|
||||
occupiedWidth += float64(obj.LabelDimensions.Width) + 2*label.PADDING
|
||||
if occupiedWidth > obj.Width {
|
||||
dx = (occupiedWidth - obj.Width) / 2
|
||||
obj.Width = occupiedWidth
|
||||
}
|
||||
}
|
||||
|
||||
// also check for grid cells with outside top labels or icons
|
||||
// the first grid object is at the top (and always exists)
|
||||
topY := gd.objects[0].TopLeft.Y
|
||||
highestOutside := topY
|
||||
for _, o := range gd.objects {
|
||||
// we only want to compute label positions for objects at the top of the grid
|
||||
if o.TopLeft.Y > topY {
|
||||
if gd.rowDirected {
|
||||
// if the grid is rowDirected (row1, row2, etc) we can stop after finishing the first row
|
||||
break
|
||||
} else {
|
||||
// otherwise we continue until the next column
|
||||
continue
|
||||
}
|
||||
}
|
||||
if o.LabelPosition != nil {
|
||||
labelPosition := label.Position(*o.LabelPosition)
|
||||
if labelPosition.IsOutside() {
|
||||
labelTL := o.GetLabelTopLeft()
|
||||
if labelTL.Y < highestOutside {
|
||||
highestOutside = labelTL.Y
|
||||
}
|
||||
}
|
||||
}
|
||||
if o.IconPosition != nil {
|
||||
switch label.Position(*o.IconPosition) {
|
||||
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
|
||||
iconSpace := float64(d2target.MAX_ICON_SIZE + label.PADDING)
|
||||
if topY-iconSpace < highestOutside {
|
||||
highestOutside = topY - iconSpace
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if highestOutside < topY {
|
||||
occupiedHeight += topY - highestOutside + 2*label.PADDING
|
||||
}
|
||||
if occupiedHeight > float64(verticalPadding) {
|
||||
// if the label doesn't fit within the padding, we need to add more
|
||||
dy = occupiedHeight - float64(verticalPadding)
|
||||
obj.Height += dy
|
||||
}
|
||||
|
||||
// we need to center children if we have to expand to fit the container label
|
||||
if dx != 0 || dy != 0 {
|
||||
gd.shift(dx, dy)
|
||||
}
|
||||
}
|
||||
|
||||
if obj.HasLabel() {
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
}
|
||||
if obj.Icon != nil {
|
||||
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
|
||||
}
|
||||
gridDiagrams[obj.AbsID()] = gd
|
||||
|
||||
for _, o := range gd.objects {
|
||||
toRemove[o] = struct{}{}
|
||||
}
|
||||
|
||||
// simple straight line edge routing between grid objects
|
||||
for i, e := range g.Edges {
|
||||
edgeOrder[e.AbsID()] = i
|
||||
if !e.Src.Parent.IsDescendantOf(obj) && !e.Dst.Parent.IsDescendantOf(obj) {
|
||||
continue
|
||||
}
|
||||
// if edge is within grid, remove it from outer layout
|
||||
gd.edges = append(gd.edges, e)
|
||||
edgeToRemove[e] = struct{}{}
|
||||
|
||||
if e.Src.Parent != obj || e.Dst.Parent != obj {
|
||||
continue
|
||||
}
|
||||
// if edge is grid child, use simple routing
|
||||
e.Route = []*geo.Point{e.Src.Center(), e.Dst.Center()}
|
||||
e.TraceToShape(e.Route, 0, 1)
|
||||
if e.Label.Value != "" {
|
||||
e.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(g.Objects) > 0 {
|
||||
queue := make([]*d2graph.Object, 1, len(g.Objects))
|
||||
queue[0] = g.Root
|
||||
for len(queue) > 0 {
|
||||
obj := queue[0]
|
||||
queue = queue[1:]
|
||||
if len(obj.ChildrenArray) == 0 {
|
||||
continue
|
||||
}
|
||||
if !obj.IsGridDiagram() {
|
||||
queue = append(queue, obj.ChildrenArray...)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := processGrid(obj); err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutObjects := make([]*d2graph.Object, 0, len(toRemove))
|
||||
for _, obj := range g.Objects {
|
||||
if _, exists := toRemove[obj]; !exists {
|
||||
layoutObjects = append(layoutObjects, obj)
|
||||
}
|
||||
}
|
||||
g.Objects = layoutObjects
|
||||
layoutEdges := make([]*d2graph.Edge, 0, len(edgeToRemove))
|
||||
for _, e := range g.Edges {
|
||||
if _, exists := edgeToRemove[e]; !exists {
|
||||
layoutEdges = append(layoutEdges, e)
|
||||
}
|
||||
}
|
||||
g.Edges = layoutEdges
|
||||
|
||||
return gridDiagrams, objectOrder, edgeOrder, nil
|
||||
}
|
||||
|
||||
func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*gridDiagram, error) {
|
||||
gd := newGridDiagram(obj)
|
||||
|
||||
|
|
@ -1128,57 +871,3 @@ func getDistToTarget(layout [][]*d2graph.Object, targetSize float64, horizontalG
|
|||
}
|
||||
return totalDelta
|
||||
}
|
||||
|
||||
// cleanup restores the graph after the core layout engine finishes
|
||||
// - translating the grid to its position placed by the core layout engine
|
||||
// - restore the children of the grid
|
||||
// - sorts objects to their original graph order
|
||||
func cleanup(graph *d2graph.Graph, gridDiagrams map[string]*gridDiagram, objectsOrder, edgeOrder map[string]int) {
|
||||
defer func() {
|
||||
sort.SliceStable(graph.Objects, func(i, j int) bool {
|
||||
return objectsOrder[graph.Objects[i].AbsID()] < objectsOrder[graph.Objects[j].AbsID()]
|
||||
})
|
||||
sort.SliceStable(graph.Edges, func(i, j int) bool {
|
||||
return edgeOrder[graph.Edges[i].AbsID()] < edgeOrder[graph.Edges[j].AbsID()]
|
||||
})
|
||||
}()
|
||||
|
||||
var restore func(obj *d2graph.Object)
|
||||
restore = func(obj *d2graph.Object) {
|
||||
gd, exists := gridDiagrams[obj.AbsID()]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
|
||||
horizontalPadding, verticalPadding := CONTAINER_PADDING, CONTAINER_PADDING
|
||||
if obj.GridGap != nil || obj.HorizontalGap != nil {
|
||||
horizontalPadding = gd.horizontalGap
|
||||
}
|
||||
if obj.GridGap != nil || obj.VerticalGap != nil {
|
||||
verticalPadding = gd.verticalGap
|
||||
}
|
||||
|
||||
// shift the grid from (0, 0)
|
||||
gd.shift(
|
||||
obj.TopLeft.X+float64(horizontalPadding),
|
||||
obj.TopLeft.Y+float64(verticalPadding),
|
||||
)
|
||||
gd.cleanup(obj, graph)
|
||||
|
||||
for _, child := range obj.ChildrenArray {
|
||||
restore(child)
|
||||
}
|
||||
}
|
||||
|
||||
if graph.Root.IsGridDiagram() {
|
||||
gd, exists := gridDiagrams[graph.Root.AbsID()]
|
||||
if exists {
|
||||
gd.cleanup(graph.Root, graph)
|
||||
}
|
||||
}
|
||||
|
||||
for _, obj := range graph.Objects {
|
||||
restore(obj)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,13 +168,13 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
|
|||
switch graphInfo.DiagramType {
|
||||
case GridDiagram:
|
||||
log.Debug(ctx, "layout grid", slog.F("rootlevel", g.RootLevel), slog.F("shapes", g.PrintString()))
|
||||
if err = d2grid.Layout2(ctx, g); err != nil {
|
||||
if err = d2grid.Layout(ctx, g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case SequenceDiagram:
|
||||
log.Debug(ctx, "layout sequence", slog.F("rootlevel", g.RootLevel), slog.F("shapes", g.PrintString()))
|
||||
err = d2sequence.Layout2(ctx, g, coreLayout)
|
||||
err = d2sequence.Layout(ctx, g, coreLayout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,10 +91,6 @@ func Layout(ctx context.Context, g *d2graph.Graph, constantNearGraphs []*d2graph
|
|||
return nil
|
||||
}
|
||||
|
||||
func Place(obj *d2graph.Object) (float64, float64) {
|
||||
return place(obj)
|
||||
}
|
||||
|
||||
// place returns the position of obj, taking into consideration its near value and the diagram
|
||||
func place(obj *d2graph.Object) (float64, float64) {
|
||||
tl, br := boundingBox(obj.Graph)
|
||||
|
|
@ -149,28 +145,6 @@ func place(obj *d2graph.Object) (float64, float64) {
|
|||
return x, y
|
||||
}
|
||||
|
||||
// WithoutConstantNears plucks out the graph objects which have "near" set to a constant value
|
||||
// This is to be called before layout engines so they don't take part in regular positioning
|
||||
func WithoutConstantNears(ctx context.Context, g *d2graph.Graph) (constantNearGraphs []*d2graph.Graph) {
|
||||
for i := 0; i < len(g.Objects); i++ {
|
||||
obj := g.Objects[i]
|
||||
if obj.NearKey == nil {
|
||||
continue
|
||||
}
|
||||
_, isKey := g.Root.HasChild(d2graph.Key(obj.NearKey))
|
||||
if isKey {
|
||||
continue
|
||||
}
|
||||
_, isConst := d2graph.NearConstants[d2graph.Key(obj.NearKey)[0]]
|
||||
if isConst {
|
||||
tempGraph := g.ExtractAsNestedGraph(obj)
|
||||
constantNearGraphs = append(constantNearGraphs, tempGraph)
|
||||
i--
|
||||
}
|
||||
}
|
||||
return constantNearGraphs
|
||||
}
|
||||
|
||||
// boundingBox gets the center of the graph as defined by shapes
|
||||
// The bounds taking into consideration only shapes gives more of a feeling of true center
|
||||
// It differs from d2target.BoundingBox which needs to include every visible thing
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package d2sequence
|
|||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"oss.terrastruct.com/util-go/go2"
|
||||
|
|
@ -15,32 +14,9 @@ import (
|
|||
|
||||
// Layout runs the sequence diagram layout engine on objects of shape sequence_diagram
|
||||
//
|
||||
// 1. Traverse graph from root, skip objects with shape not `sequence_diagram`
|
||||
// 2. Construct a sequence diagram from all descendant objects and edges
|
||||
// 3. Remove those objects and edges from the main graph
|
||||
// 4. Run layout on sequence diagrams
|
||||
// 5. Set the resulting dimensions to the main graph shape
|
||||
// 6. Run core layouts (still without sequence diagram innards)
|
||||
// 7. Put back sequence diagram innards in correct location
|
||||
// 1. Run layout on sequence diagrams
|
||||
// 2. Set the resulting dimensions to the main graph shape
|
||||
func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) error {
|
||||
sequenceDiagrams, objectOrder, edgeOrder, err := WithoutSequenceDiagrams(ctx, g)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if g.Root.IsSequenceDiagram() {
|
||||
// the sequence diagram is the only layout engine if the whole diagram is
|
||||
// shape: sequence_diagram
|
||||
g.Root.TopLeft = geo.NewPoint(0, 0)
|
||||
} else if err := layout(ctx, g); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cleanup(g, sequenceDiagrams, objectOrder, edgeOrder)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Layout2(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) error {
|
||||
// used in layout code
|
||||
g.Root.Shape.Value = d2target.ShapeSequenceDiagram
|
||||
|
||||
|
|
@ -84,62 +60,6 @@ func Layout2(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph)
|
|||
return nil
|
||||
}
|
||||
|
||||
func WithoutSequenceDiagrams(ctx context.Context, g *d2graph.Graph) (map[string]*sequenceDiagram, map[string]int, map[string]int, error) {
|
||||
objectsToRemove := make(map[*d2graph.Object]struct{})
|
||||
edgesToRemove := make(map[*d2graph.Edge]struct{})
|
||||
sequenceDiagrams := make(map[string]*sequenceDiagram)
|
||||
|
||||
if len(g.Objects) > 0 {
|
||||
queue := make([]*d2graph.Object, 1, len(g.Objects))
|
||||
queue[0] = g.Root
|
||||
for len(queue) > 0 {
|
||||
obj := queue[0]
|
||||
queue = queue[1:]
|
||||
if len(obj.ChildrenArray) == 0 {
|
||||
continue
|
||||
}
|
||||
if obj.Shape.Value != d2target.ShapeSequenceDiagram {
|
||||
queue = append(queue, obj.ChildrenArray...)
|
||||
continue
|
||||
}
|
||||
|
||||
sd, err := layoutSequenceDiagram(g, obj)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
obj.Children = make(map[string]*d2graph.Object)
|
||||
obj.ChildrenArray = nil
|
||||
obj.Box = geo.NewBox(nil, sd.getWidth()+GROUP_CONTAINER_PADDING*2, sd.getHeight()+GROUP_CONTAINER_PADDING*2)
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
sequenceDiagrams[obj.AbsID()] = sd
|
||||
|
||||
for _, edge := range sd.messages {
|
||||
edgesToRemove[edge] = struct{}{}
|
||||
}
|
||||
for _, obj := range sd.actors {
|
||||
objectsToRemove[obj] = struct{}{}
|
||||
}
|
||||
for _, obj := range sd.notes {
|
||||
objectsToRemove[obj] = struct{}{}
|
||||
}
|
||||
for _, obj := range sd.groups {
|
||||
objectsToRemove[obj] = struct{}{}
|
||||
}
|
||||
for _, obj := range sd.spans {
|
||||
objectsToRemove[obj] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layoutEdges, edgeOrder := getLayoutEdges(g, edgesToRemove)
|
||||
g.Edges = layoutEdges
|
||||
layoutObjects, objectOrder := getLayoutObjects(g, objectsToRemove)
|
||||
// TODO this isn't a proper deletion because the objects still appear as children of the object
|
||||
g.Objects = layoutObjects
|
||||
|
||||
return sequenceDiagrams, objectOrder, edgeOrder, nil
|
||||
}
|
||||
|
||||
// layoutSequenceDiagram finds the edges inside the sequence diagram and performs the layout on the object descendants
|
||||
func layoutSequenceDiagram(g *d2graph.Graph, obj *d2graph.Object) (*sequenceDiagram, error) {
|
||||
var edges []*d2graph.Edge
|
||||
|
|
@ -157,97 +77,3 @@ func layoutSequenceDiagram(g *d2graph.Graph, obj *d2graph.Object) (*sequenceDiag
|
|||
err = sd.layout()
|
||||
return sd, err
|
||||
}
|
||||
|
||||
func getLayoutEdges(g *d2graph.Graph, toRemove map[*d2graph.Edge]struct{}) ([]*d2graph.Edge, map[string]int) {
|
||||
edgeOrder := make(map[string]int)
|
||||
layoutEdges := make([]*d2graph.Edge, 0, len(g.Edges)-len(toRemove))
|
||||
|
||||
for i, edge := range g.Edges {
|
||||
edgeOrder[edge.AbsID()] = i
|
||||
if _, exists := toRemove[edge]; !exists {
|
||||
layoutEdges = append(layoutEdges, edge)
|
||||
}
|
||||
}
|
||||
return layoutEdges, edgeOrder
|
||||
}
|
||||
|
||||
func getLayoutObjects(g *d2graph.Graph, toRemove map[*d2graph.Object]struct{}) ([]*d2graph.Object, map[string]int) {
|
||||
objectOrder := make(map[string]int)
|
||||
layoutObjects := make([]*d2graph.Object, 0, len(toRemove))
|
||||
for i, obj := range g.Objects {
|
||||
objectOrder[obj.AbsID()] = i
|
||||
if _, exists := toRemove[obj]; !exists {
|
||||
layoutObjects = append(layoutObjects, obj)
|
||||
}
|
||||
}
|
||||
return layoutObjects, objectOrder
|
||||
}
|
||||
|
||||
// cleanup restores the graph after the core layout engine finishes
|
||||
// - translating the sequence diagram to its position placed by the core layout engine
|
||||
// - restore the children of the sequence diagram graph object
|
||||
// - adds the sequence diagram edges (messages) back to the graph
|
||||
// - adds the sequence diagram lifelines to the graph edges
|
||||
// - adds the sequence diagram descendants back to the graph objects
|
||||
// - sorts edges and objects to their original graph order
|
||||
func cleanup(g *d2graph.Graph, sequenceDiagrams map[string]*sequenceDiagram, objectsOrder, edgesOrder map[string]int) {
|
||||
var objects []*d2graph.Object
|
||||
if g.Root.IsSequenceDiagram() {
|
||||
objects = []*d2graph.Object{g.Root}
|
||||
} else {
|
||||
objects = g.Objects
|
||||
}
|
||||
for _, obj := range objects {
|
||||
sd, exists := sequenceDiagrams[obj.AbsID()]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||
|
||||
// shift the sequence diagrams as they are always placed at (0, 0) with some padding
|
||||
sd.shift(
|
||||
geo.NewPoint(
|
||||
obj.TopLeft.X+GROUP_CONTAINER_PADDING,
|
||||
obj.TopLeft.Y+GROUP_CONTAINER_PADDING,
|
||||
),
|
||||
)
|
||||
|
||||
obj.Children = make(map[string]*d2graph.Object)
|
||||
obj.ChildrenArray = make([]*d2graph.Object, 0)
|
||||
for _, child := range sd.actors {
|
||||
obj.Children[strings.ToLower(child.ID)] = child
|
||||
obj.ChildrenArray = append(obj.ChildrenArray, child)
|
||||
}
|
||||
for _, child := range sd.groups {
|
||||
if child.Parent.AbsID() == obj.AbsID() {
|
||||
obj.Children[strings.ToLower(child.ID)] = child
|
||||
obj.ChildrenArray = append(obj.ChildrenArray, child)
|
||||
}
|
||||
}
|
||||
|
||||
g.Edges = append(g.Edges, sd.messages...)
|
||||
g.Edges = append(g.Edges, sd.lifelines...)
|
||||
g.Objects = append(g.Objects, sd.actors...)
|
||||
g.Objects = append(g.Objects, sd.notes...)
|
||||
g.Objects = append(g.Objects, sd.groups...)
|
||||
g.Objects = append(g.Objects, sd.spans...)
|
||||
}
|
||||
|
||||
// no new objects, so just keep the same position
|
||||
sort.SliceStable(g.Objects, func(i, j int) bool {
|
||||
return objectsOrder[g.Objects[i].AbsID()] < objectsOrder[g.Objects[j].AbsID()]
|
||||
})
|
||||
|
||||
// sequence diagrams add lifelines, and they must be the last ones in this slice
|
||||
sort.SliceStable(g.Edges, func(i, j int) bool {
|
||||
iOrder, iExists := edgesOrder[g.Edges[i].AbsID()]
|
||||
jOrder, jExists := edgesOrder[g.Edges[j].AbsID()]
|
||||
if iExists && jExists {
|
||||
return iOrder < jOrder
|
||||
} else if iExists && !jExists {
|
||||
return true
|
||||
}
|
||||
// either both don't exist or i doesn't exist and j exists
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
|
|
|||
26
d2lib/d2.go
26
d2lib/d2.go
|
|
@ -12,9 +12,6 @@ import (
|
|||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2grid"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2near"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
|
|
@ -90,29 +87,6 @@ func compile(ctx context.Context, g *d2graph.Graph, compileOpts *CompileOptions,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if false {
|
||||
constantNearGraphs := d2near.WithoutConstantNears(ctx, g)
|
||||
|
||||
layoutWithGrids := d2grid.Layout(ctx, g, coreLayout)
|
||||
|
||||
// run core layout for constantNears
|
||||
for _, tempGraph := range constantNearGraphs {
|
||||
if err = layoutWithGrids(ctx, tempGraph); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err = d2sequence.Layout(ctx, g, layoutWithGrids)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = d2near.Layout(ctx, g, constantNearGraphs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d, err := d2exporter.Export(ctx, g, compileOpts.FontFamily)
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ import (
|
|||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2near"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||
"oss.terrastruct.com/d2/d2lib"
|
||||
"oss.terrastruct.com/d2/d2plugin"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2animate"
|
||||
|
|
@ -108,8 +106,6 @@ func runa(t *testing.T, tcs []testCase) {
|
|||
// serde exercises serializing and deserializing the graph
|
||||
// We want to run all the steps leading up to serialization in the course of regular layout
|
||||
func serde(t *testing.T, tc testCase, ruler *textmeasure.Ruler) {
|
||||
ctx := context.Background()
|
||||
ctx = log.WithTB(ctx, t, nil)
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader(tc.script), &d2compiler.CompileOptions{
|
||||
UTF16Pos: false,
|
||||
})
|
||||
|
|
@ -117,8 +113,6 @@ func serde(t *testing.T, tc testCase, ruler *textmeasure.Ruler) {
|
|||
if len(g.Objects) > 0 {
|
||||
err = g.SetDimensions(nil, ruler, nil)
|
||||
trequire.Nil(t, err)
|
||||
d2near.WithoutConstantNears(ctx, g)
|
||||
d2sequence.WithoutSequenceDiagrams(ctx, g)
|
||||
}
|
||||
b, err := d2graph.SerializeGraph(g)
|
||||
trequire.Nil(t, err)
|
||||
|
|
|
|||
Loading…
Reference in a new issue