diff --git a/d2exporter/export.go b/d2exporter/export.go index c65542362..1f2a15b8d 100644 --- a/d2exporter/export.go +++ b/d2exporter/export.go @@ -99,6 +99,10 @@ func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape { shape.Italic = text.IsItalic shape.FontSize = text.FontSize + if obj.IsSequenceDiagram() { + shape.StrokeWidth = 0 + } + if obj.IsSequenceDiagramGroup() { shape.StrokeWidth = 0 shape.Blend = true diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index 8b5f7003e..b7af9f2a3 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -305,18 +305,6 @@ func (s *Style) Apply(key, value string) error { type ContainerLevel int -func (l ContainerLevel) Fill() string { - // Darkest (least nested) to lightest (most nested) - if l == 1 { - return "#E3E9FD" - } else if l == 2 { - return "#EDF0FD" - } else if l == 3 { - return "#F7F8FE" - } - return "#FFFFFF" -} - func (l ContainerLevel) LabelSize() int { // Largest to smallest if l == 1 { @@ -339,6 +327,26 @@ func (obj *Object) GetFill(theme *d2themes.Theme) string { return theme.Colors.B5 } + // fill for spans + sd := obj.OuterSequenceDiagram() + if sd != nil { + level -= int(sd.Level()) + if level == 1 { + return theme.Colors.B3 + } else if level == 2 { + return theme.Colors.B4 + } else if level == 3 { + return theme.Colors.B5 + } else if level == 4 { + return theme.Colors.Neutrals.N6 + } + return theme.Colors.Neutrals.N7 + } + + if obj.IsSequenceDiagram() { + return theme.Colors.Neutrals.N7 + } + shape := obj.Attributes.Shape.Value if shape == "" || strings.EqualFold(shape, d2target.ShapeSquare) || strings.EqualFold(shape, d2target.ShapeCircle) || strings.EqualFold(shape, d2target.ShapeOval) || strings.EqualFold(shape, d2target.ShapeRectangle) { diff --git a/d2layouts/d2sequence/layout.go b/d2layouts/d2sequence/layout.go index 33e82a0a4..6a9498642 100644 --- a/d2layouts/d2sequence/layout.go +++ b/d2layouts/d2sequence/layout.go @@ -43,7 +43,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, layout func(ctx context.Conte } obj.Children = make(map[string]*d2graph.Object) obj.ChildrenArray = nil - obj.Box = geo.NewBox(nil, sd.getWidth(), sd.getHeight()) + 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 @@ -141,14 +141,26 @@ func cleanup(g *d2graph.Graph, sequenceDiagrams map[string]*sequenceDiagram, obj obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter)) sd := sequenceDiagrams[obj.AbsID()] - // shift the sequence diagrams as they are always placed at (0, 0) - sd.shift(obj.TopLeft) + // 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[child.ID] = child + obj.ChildrenArray = append(obj.ChildrenArray, child) + } + for _, child := range sd.groups { + if child.Parent.AbsID() == obj.AbsID() { + obj.Children[child.ID] = child + obj.ChildrenArray = append(obj.ChildrenArray, child) + } } - obj.ChildrenArray = sd.actors g.Edges = append(g.Edges, sequenceDiagrams[obj.AbsID()].messages...) g.Edges = append(g.Edges, sequenceDiagrams[obj.AbsID()].lifelines...) diff --git a/d2layouts/d2sequence/sequence_diagram.go b/d2layouts/d2sequence/sequence_diagram.go index 6f9eb67d1..1845a8d93 100644 --- a/d2layouts/d2sequence/sequence_diagram.go +++ b/d2layouts/d2sequence/sequence_diagram.go @@ -540,6 +540,8 @@ func (sd *sequenceDiagram) getHeight() float64 { func (sd *sequenceDiagram) shift(tl *geo.Point) { allObjects := append([]*d2graph.Object{}, sd.actors...) allObjects = append(allObjects, sd.spans...) + allObjects = append(allObjects, sd.groups...) + allObjects = append(allObjects, sd.notes...) for _, obj := range allObjects { obj.TopLeft.X += tl.X obj.TopLeft.Y += tl.Y diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 6bf9135a9..77214ce3f 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -637,7 +637,7 @@ func drawShape(writer io.Writer, targetShape d2target.Shape) error { targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, style) // TODO should standardize "" to rectangle - case d2target.ShapeRectangle, "": + case d2target.ShapeRectangle, d2target.ShapeSequenceDiagram, "": if targetShape.ThreeDee { fmt.Fprint(writer, render3dRect(targetShape)) } else { diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go index 22d6b86ec..230528899 100644 --- a/e2etests/stable_test.go +++ b/e2etests/stable_test.go @@ -1451,7 +1451,8 @@ c: "just an actor" d2exporter.export -> CLI: resulting SVG } `, - }, { + }, + { name: "sequence_diagram_actor_distance", script: `shape: sequence_diagram a: "an actor with a really long label that will break everything" diff --git a/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/board.exp.json b/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/board.exp.json index 775f8b93e..2341f4127 100644 --- a/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/board.exp.json +++ b/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/board.exp.json @@ -5,8 +5,8 @@ "id": "a", "type": "", "pos": { - "x": 0, - "y": 210 + "x": 24, + "y": 234 }, "width": 487, "height": 126, @@ -45,8 +45,8 @@ "id": "c", "type": "", "pos": { - "x": 578, - "y": 50 + "x": 602, + "y": 74 }, "width": 177, "height": 286, @@ -85,8 +85,8 @@ "id": "d", "type": "", "pos": { - "x": 1014, - "y": 210 + "x": 1038, + "y": 234 }, "width": 150, "height": 126, @@ -125,8 +125,8 @@ "id": "e", "type": "", "pos": { - "x": 1460, - "y": 210 + "x": 1484, + "y": 234 }, "width": 180, "height": 126, @@ -165,8 +165,8 @@ "id": "b", "type": "", "pos": { - "x": 1718, - "y": 210 + "x": 1742, + "y": 234 }, "width": 163, "height": 126, @@ -205,8 +205,8 @@ "id": "f", "type": "", "pos": { - "x": 1931, - "y": 210 + "x": 1955, + "y": 234 }, "width": 561, "height": 126, @@ -269,12 +269,12 @@ "labelPercentage": 0, "route": [ { - "x": 243.5, - "y": 466 + "x": 267.5, + "y": 490 }, { - "x": 1799.5, - "y": 466 + "x": 1823.5, + "y": 490 } ], "animated": false, @@ -308,12 +308,12 @@ "labelPercentage": 0, "route": [ { - "x": 243.5, - "y": 596 + "x": 267.5, + "y": 620 }, { - "x": 1799.5, - "y": 596 + "x": 1823.5, + "y": 620 } ], "animated": false, @@ -347,12 +347,12 @@ "labelPercentage": 0, "route": [ { - "x": 666.5, - "y": 726 + "x": 690.5, + "y": 750 }, { - "x": 1089, - "y": 726 + "x": 1113, + "y": 750 } ], "animated": false, @@ -386,12 +386,12 @@ "labelPercentage": 0, "route": [ { - "x": 243.5, - "y": 856 + "x": 267.5, + "y": 880 }, { - "x": 1089, - "y": 856 + "x": 1113, + "y": 880 } ], "animated": false, @@ -425,12 +425,12 @@ "labelPercentage": 0, "route": [ { - "x": 1089, - "y": 986 + "x": 1113, + "y": 1010 }, { - "x": 1550, - "y": 986 + "x": 1574, + "y": 1010 } ], "animated": false, @@ -464,12 +464,12 @@ "labelPercentage": 0, "route": [ { - "x": 243.5, - "y": 1116 + "x": 267.5, + "y": 1140 }, { - "x": 2211.5, - "y": 1116 + "x": 2235.5, + "y": 1140 } ], "animated": false, @@ -503,12 +503,12 @@ "labelPercentage": 0, "route": [ { - "x": 243.5, - "y": 336 + "x": 267.5, + "y": 360 }, { - "x": 243.5, - "y": 1246 + "x": 267.5, + "y": 1270 } ], "animated": false, @@ -542,12 +542,12 @@ "labelPercentage": 0, "route": [ { - "x": 666.5, - "y": 336 + "x": 690.5, + "y": 360 }, { - "x": 666.5, - "y": 1246 + "x": 690.5, + "y": 1270 } ], "animated": false, @@ -581,12 +581,12 @@ "labelPercentage": 0, "route": [ { - "x": 1089, - "y": 336 + "x": 1113, + "y": 360 }, { - "x": 1089, - "y": 1246 + "x": 1113, + "y": 1270 } ], "animated": false, @@ -620,12 +620,12 @@ "labelPercentage": 0, "route": [ { - "x": 1550, - "y": 336 + "x": 1574, + "y": 360 }, { - "x": 1550, - "y": 1246 + "x": 1574, + "y": 1270 } ], "animated": false, @@ -659,12 +659,12 @@ "labelPercentage": 0, "route": [ { - "x": 1799.5, - "y": 336 + "x": 1823.5, + "y": 360 }, { - "x": 1799.5, - "y": 1246 + "x": 1823.5, + "y": 1270 } ], "animated": false, @@ -698,12 +698,12 @@ "labelPercentage": 0, "route": [ { - "x": 2211.5, - "y": 336 + "x": 2235.5, + "y": 360 }, { - "x": 2211.5, - "y": 1246 + "x": 2235.5, + "y": 1270 } ], "animated": false, diff --git a/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/sketch.exp.svg index a2518e2ba..0e9876acc 100644 --- a/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_actor_distance/dagre/sketch.exp.svg @@ -2,7 +2,7 @@ an actor with a really long label that will break everythinganactorwithareallylonglabelthatwillbreakeverythingsimplea short onefar awaywhat if there were no labels between this actor and the previous one shortlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actorslong label for testing purposes and it must be really, really long - - - - - + + + + + an actor with a really long label that will break everythinganactorwithareallylonglabelthatwillbreakeverythingsimplea short onefar awaywhat if there were no labels between this actor and the previous one shortlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actorslong label for testing purposes and it must be really, really long +an actor with a really long label that will break everythinganactorwithareallylonglabelthatwillbreakeverythingsimplea short onefar awaywhat if there were no labels between this actor and the previous one shortlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actorslong label for testing purposes and it must be really, really long - - - - - + + + + + a labelblabelsa class+ -public() bool -void- -private() int -voidcloudyyyy:= 5 +a labelblabelsa class+ +public() bool +void- +private() int +voidcloudyyyy:= 5 := a + 7 -fmt.Printf("%d", b)cyldiadocssix cornersa random iconoverpackdocs pagetoohard o saysinglepersona queuea squarea step at a timedatausersid -int -name -varchar - result := callThisFunction(obj, 5) midthis sideother side +fmt.Printf("%d", b)cyldiadocssix cornersa random iconoverpackdocs pagetoohard o saysinglepersona queuea squarea step at a timedatausersid +int +name +varchar + result := callThisFunction(obj, 5) midthis sideother side - - + + a labelblabelsa class+ -public() bool -void- -private() int -voidcloudyyyy:= 5 +a labelblabelsa class+ +public() bool +void- +private() int +voidcloudyyyy:= 5 := a + 7 -fmt.Printf("%d", b)cyldiadocssix cornersa random iconoverpackdocs pagetoohard o saysinglepersona queuea squarea step at a timedatausersid -int -name -varchar - result := callThisFunction(obj, 5) midthis sideother side +fmt.Printf("%d", b)cyldiadocssix cornersa random iconoverpackdocs pagetoohard o saysinglepersona queuea squarea step at a timedatausersid +int +name +varchar + result := callThisFunction(obj, 5) midthis sideother side - - + + abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note +abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note - - - - + + + + abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note +abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note - - - - + + + + ba a note here to remember that padding must consider notes toojustalongnotehereba a note here to remember that padding must consider notes toojustalongnotehereabjust an actorthis is a message groupaltand this is a nested message groupcase 1case 2case 3case 4what about more nestingcrazy townwhoa a notea note here to remember that padding must consider notes toojustalongnotehereabjust an actorthis is a message groupaltand this is a nested message groupcase 1case 2case 3case 4what about more nestingcrazy townwhoa a notea note here to remember that padding must consider notes toojustalongnoteherescoreritemResponseitemessayRubricconceptitemOutcome scoreritemResponseitemessayRubricconceptitemOutcome abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier +abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier - + abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier +abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier - + How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place - - - - - - - - - - - - - - +How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place + + + + + + + + + + + + + + How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place - - - - - - - - - - - - - - +How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place + + + + + + + + + + + + + + ab a self edge herebetween actorsto descendantto deeper descendantto parentactor +ab a self edge herebetween actorsto descendantto deeper descendantto parentactor - - - - - - + + + + + + ab a self edge herebetween actorsto descendantto deeper descendantto parentactor +ab a self edge herebetween actorsto descendantto deeper descendantto parentactor - - - - - - + + + + + + AlicelinebreakerBobdbqueueanoddservicewithanameinmultiple lines Authentication Requestmake request for something that is quite far away and requires a really long label to take all the space between the objectsvalidate credentialsAuthentication ResponseAnother authentication Requestdo it later storedAnother authentication Response +AlicelinebreakerBobdbqueueanoddservicewithanameinmultiple lines Authentication Requestmake request for something that is quite far away and requires a really long label to take all the space between the objectsvalidate credentialsAuthentication ResponseAnother authentication Requestdo it later storedAnother authentication Response - - - - - - - - + + + + + + + + AlicelinebreakerBobdbqueueanoddservicewithanameinmultiple lines Authentication Requestmake request for something that is quite far away and requires a really long label to take all the space between the objectsvalidate credentialsAuthentication ResponseAnother authentication Requestdo it later storedAnother authentication Response +AlicelinebreakerBobdbqueueanoddservicewithanameinmultiple lines Authentication Requestmake request for something that is quite far away and requires a really long label to take all the space between the objectsvalidate credentialsAuthentication ResponseAnother authentication Requestdo it later storedAnother authentication Response - - - - - - - - + + + + + + + + scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) +scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) - - - - - - - - - - - - + + + + + + + + + + + + scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) +scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) - - - - - - - - - - - - + + + + + + + + + + + + a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) - - - - - - - - - - - - - - - - - - - - - - - - - +a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + + + + + + + + + + + + + + + + + + + + + + + + + a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) - - - - - - - - - - - - - - - - - - - - - - - - - +a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + + + + + + + + + + + + + + + + + + + + + + + + + bacthis is a message groupand this is a nested message groupwhat about more nestingyoyo bacthis is a message groupand this is a nested message groupwhat about more nestingyoyo