diff --git a/d2layouts/d2sequence/constants.go b/d2layouts/d2sequence/constants.go index b042db3d9..16b5baf48 100644 --- a/d2layouts/d2sequence/constants.go +++ b/d2layouts/d2sequence/constants.go @@ -12,13 +12,13 @@ const MIN_ACTOR_DISTANCE = 200. const MIN_EDGE_DISTANCE = 100. // default size -const ACTIVATION_BOX_WIDTH = 20. +const SPAN_BOX_WIDTH = 20. -// small pad so that edges don't touch lifelines and activation boxes -const ACTIVATION_BOX_EDGE_PAD = 5. +// small pad so that edges don't touch lifelines and span boxes +const SPAN_BOX_EDGE_PAD = 5. -// as the activation boxes start getting nested, their size grows -const ACTIVATION_BOX_DEPTH_GROW_FACTOR = 10. +// as the span boxes start getting nested, their size grows +const SPAN_BOX_DEPTH_GROW_FACTOR = 10. -// when a activation box has a single edge -const MIN_ACTIVATION_BOX_HEIGHT = MIN_EDGE_DISTANCE / 2. +// when a span box has a single edge +const MIN_SPAN_BOX_HEIGHT = MIN_EDGE_DISTANCE / 2. diff --git a/d2layouts/d2sequence/layout.go b/d2layouts/d2sequence/layout.go index 1411f1cf9..f08ba6c21 100644 --- a/d2layouts/d2sequence/layout.go +++ b/d2layouts/d2sequence/layout.go @@ -27,7 +27,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) { sd.init() sd.placeActors() - sd.placeActivationBoxes() + sd.placeSpanBoxes() sd.routeEdges() sd.addLifelineEdges() @@ -37,14 +37,14 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) { type sequenceDiagram struct { graph *d2graph.Graph - edges []*d2graph.Edge - actors []*d2graph.Object - activations []*d2graph.Object + edges []*d2graph.Edge + actors []*d2graph.Object + spans []*d2graph.Object - // can be either actors or activation boxes - // rank: left to right position of actors/activations + // can be either actors or span boxes + // rank: left to right position of actors/span boxes objectRank map[*d2graph.Object]int - // depth: the nested levels of a given actor/activation + // depth: the nested levels of a given actor/span objectDepth map[*d2graph.Object]int // keep track of the first and last edge of a given actor @@ -73,10 +73,10 @@ func (sd *sequenceDiagram) init() { sd.objectDepth[obj] = 0 sd.maxActorHeight = math.Max(sd.maxActorHeight, obj.Height) } else { - // activations boxes are always rectangles and have no labels + // span boxes are always rectangles and have no labels obj.Attributes.Label = d2graph.Scalar{Value: ""} obj.Attributes.Shape = d2graph.Scalar{Value: shape.SQUARE_TYPE} - sd.activations = append(sd.activations, obj) + sd.spans = append(sd.spans, obj) sd.objectRank[obj] = sd.objectRank[obj.Parent] sd.objectDepth[obj] = sd.objectDepth[obj.Parent] + 1 } @@ -156,72 +156,72 @@ func (sd *sequenceDiagram) addLifelineEdges() { } } -// placeActivationBoxes places activation boxes over the object lifeline +// placeSpanBoxes places span boxes over the object lifeline // ┌──────────┐ // │ actor │ // └────┬─────┘ // ┌─┴──┐ // │ │ -// activation +// |span| // │ │ // └─┬──┘ // │ // lifeline // │ -func (sd *sequenceDiagram) placeActivationBoxes() { - // quickly find the activation box center X +func (sd *sequenceDiagram) placeSpanBoxes() { + // quickly find the span box center X rankToX := make(map[int]float64) for _, actor := range sd.actors { rankToX[sd.objectRank[actor]] = actor.Center().X } - // places activation boxes from most to least nested - // the order is important because the only way a child activation box exists is if there'e an edge to it - // however, the parent activation might not have an edge to it and then its position is based on the child position + // places span boxes from most to least nested + // the order is important because the only way a child span box exists is if there'e an edge to it + // however, the parent span might not have an edge to it and then its position is based on the child position // or, there can be edge to it, but it comes after the child one meaning the top left position is still based on the child // and not on its own edge - activationFromMostNested := make([]*d2graph.Object, len(sd.activations)) - copy(activationFromMostNested, sd.activations) - sort.SliceStable(activationFromMostNested, func(i, j int) bool { - return sd.objectDepth[activationFromMostNested[i]] > sd.objectDepth[activationFromMostNested[j]] + spanFromMostNested := make([]*d2graph.Object, len(sd.spans)) + copy(spanFromMostNested, sd.spans) + sort.SliceStable(spanFromMostNested, func(i, j int) bool { + return sd.objectDepth[spanFromMostNested[i]] > sd.objectDepth[spanFromMostNested[j]] }) - for _, activation := range activationFromMostNested { + for _, span := range spanFromMostNested { // finds the position based on children minChildY := math.Inf(1) maxChildY := math.Inf(-1) - for _, child := range activation.ChildrenArray { + for _, child := range span.ChildrenArray { minChildY = math.Min(minChildY, child.TopLeft.Y) maxChildY = math.Max(maxChildY, child.TopLeft.Y+child.Height) } - // finds the position if there are edges to this activation box + // finds the position if there are edges to this span box minEdgeY := math.Inf(1) - if minRank, exists := sd.minEdgeRank[activation]; exists { + if minRank, exists := sd.minEdgeRank[span]; exists { minEdgeY = sd.getEdgeY(minRank) } maxEdgeY := math.Inf(-1) - if maxRank, exists := sd.maxEdgeRank[activation]; exists { + if maxRank, exists := sd.maxEdgeRank[span]; exists { maxEdgeY = sd.getEdgeY(maxRank) } // if it is the same as the child top left, add some padding minY := math.Min(minEdgeY, minChildY) if minY == minChildY { - minY -= ACTIVATION_BOX_DEPTH_GROW_FACTOR + minY -= SPAN_BOX_DEPTH_GROW_FACTOR } else { - minY -= ACTIVATION_BOX_EDGE_PAD + minY -= SPAN_BOX_EDGE_PAD } maxY := math.Max(maxEdgeY, maxChildY) if maxY == maxChildY { - maxY += ACTIVATION_BOX_DEPTH_GROW_FACTOR + maxY += SPAN_BOX_DEPTH_GROW_FACTOR } else { - maxY += ACTIVATION_BOX_EDGE_PAD + maxY += SPAN_BOX_EDGE_PAD } - height := math.Max(maxY-minY, MIN_ACTIVATION_BOX_HEIGHT) - width := ACTIVATION_BOX_WIDTH + (float64(sd.objectDepth[activation]-1) * ACTIVATION_BOX_DEPTH_GROW_FACTOR) - x := rankToX[sd.objectRank[activation]] - (width / 2.) - activation.Box = geo.NewBox(geo.NewPoint(x, minY), width, height) + height := math.Max(maxY-minY, MIN_SPAN_BOX_HEIGHT) + width := SPAN_BOX_WIDTH + (float64(sd.objectDepth[span]-1) * SPAN_BOX_DEPTH_GROW_FACTOR) + x := rankToX[sd.objectRank[span]] - (width / 2.) + span.Box = geo.NewBox(geo.NewPoint(x, minY), width, height) } } @@ -249,11 +249,11 @@ func (sd *sequenceDiagram) routeEdges() { } if isLeftToRight { - startX += ACTIVATION_BOX_EDGE_PAD - endX -= ACTIVATION_BOX_EDGE_PAD + startX += SPAN_BOX_EDGE_PAD + endX -= SPAN_BOX_EDGE_PAD } else { - startX -= ACTIVATION_BOX_EDGE_PAD - endX += ACTIVATION_BOX_EDGE_PAD + startX -= SPAN_BOX_EDGE_PAD + endX += SPAN_BOX_EDGE_PAD } edgeY := sd.getEdgeY(rank) diff --git a/d2layouts/d2sequence/layout_test.go b/d2layouts/d2sequence/layout_test.go index 90320a464..be2e83d85 100644 --- a/d2layouts/d2sequence/layout_test.go +++ b/d2layouts/d2sequence/layout_test.go @@ -92,19 +92,19 @@ func TestBasicSequenceDiagram(t *testing.T) { } if edge.Src.TopLeft.X < edge.Dst.TopLeft.X { // left to right - if edge.Route[0].X != edge.Src.Center().X+ACTIVATION_BOX_EDGE_PAD { + if edge.Route[0].X != edge.Src.Center().X+SPAN_BOX_EDGE_PAD { t.Fatalf("expected edge[%d] x to be at the actor center", i) } - if edge.Route[1].X != edge.Dst.Center().X-ACTIVATION_BOX_EDGE_PAD { + if edge.Route[1].X != edge.Dst.Center().X-SPAN_BOX_EDGE_PAD { t.Fatalf("expected edge[%d] x to be at the actor center", i) } } else { - if edge.Route[0].X != edge.Src.Center().X-ACTIVATION_BOX_EDGE_PAD { + if edge.Route[0].X != edge.Src.Center().X-SPAN_BOX_EDGE_PAD { t.Fatalf("expected edge[%d] x to be at the actor center", i) } - if edge.Route[1].X != edge.Dst.Center().X+ACTIVATION_BOX_EDGE_PAD { + if edge.Route[1].X != edge.Dst.Center().X+SPAN_BOX_EDGE_PAD { t.Fatalf("expected edge[%d] x to be at the actor center", i) } } @@ -146,7 +146,7 @@ func TestBasicSequenceDiagram(t *testing.T) { } } -func TestActivationBoxesSequenceDiagram(t *testing.T) { +func TestSpanBoxesSequenceDiagram(t *testing.T) { // ┌─────┐ ┌─────┐ // │ a │ │ b │ // └──┬──┘ └──┬──┘ @@ -197,11 +197,11 @@ func TestActivationBoxesSequenceDiagram(t *testing.T) { } if a_t1.Attributes.Label.Value != "" { - t.Fatalf("expected no label for activation box, got %s", a_t1.Attributes.Label.Value) + t.Fatalf("expected no label for span box, got %s", a_t1.Attributes.Label.Value) } if a_t1.Attributes.Shape.Value != shape.SQUARE_TYPE { - t.Fatalf("expected square shape for activation box, got %s", a_t1.Attributes.Shape.Value) + t.Fatalf("expected square shape for span box, got %s", a_t1.Attributes.Shape.Value) } if a_t1.Height != b_t1.Height { @@ -209,13 +209,13 @@ func TestActivationBoxesSequenceDiagram(t *testing.T) { } // Y diff of the 2 first edges - expectedHeight := g.Edges[1].Route[0].Y - g.Edges[0].Route[0].Y + (2 * ACTIVATION_BOX_EDGE_PAD) + expectedHeight := g.Edges[1].Route[0].Y - g.Edges[0].Route[0].Y + (2 * SPAN_BOX_EDGE_PAD) if a_t1.Height != expectedHeight { t.Fatalf("expected a.t1 height to be %.5f, got %.5f", expectedHeight, a_t1.Height) } - if a_t1.Width != ACTIVATION_BOX_WIDTH { - t.Fatalf("expected activation box width to be %.5f, got %.5f", ACTIVATION_BOX_WIDTH, a_t1.Width) + if a_t1.Width != SPAN_BOX_WIDTH { + t.Fatalf("expected span box width to be %.5f, got %.5f", SPAN_BOX_WIDTH, a_t1.Width) } // check positions @@ -231,20 +231,20 @@ func TestActivationBoxesSequenceDiagram(t *testing.T) { if a_t1.TopLeft.Y != b_t1.TopLeft.Y { t.Fatal("expected a.t1 and b.t1 to be placed at the same Y") } - if a_t1.TopLeft.Y != g.Edges[0].Route[0].Y-ACTIVATION_BOX_EDGE_PAD { + if a_t1.TopLeft.Y != g.Edges[0].Route[0].Y-SPAN_BOX_EDGE_PAD { t.Fatal("expected a.t1 to be placed at the same Y of the first edge") } // check routes - if g.Edges[0].Route[0].X != a_t1.TopLeft.X+a_t1.Width+ACTIVATION_BOX_EDGE_PAD { + if g.Edges[0].Route[0].X != a_t1.TopLeft.X+a_t1.Width+SPAN_BOX_EDGE_PAD { t.Fatal("expected the first edge to start on a.t1 top right X") } - if g.Edges[0].Route[1].X != b_t1.TopLeft.X-ACTIVATION_BOX_EDGE_PAD { + if g.Edges[0].Route[1].X != b_t1.TopLeft.X-SPAN_BOX_EDGE_PAD { t.Fatal("expected the first edge to end on b.t1 top left X") } - if g.Edges[2].Route[1].X != b.Center().X-ACTIVATION_BOX_EDGE_PAD { + if g.Edges[2].Route[1].X != b.Center().X-SPAN_BOX_EDGE_PAD { t.Fatal("expected the third edge to end on b.t1 center X") } }