diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 6e6703812..4086e8759 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -16,3 +16,4 @@ - Fixes `d2` erroring on malformed user paths (`fdopendir` error). [util-go#10](https://github.com/terrastruct/util-go/pull/10) - Arrowhead labels being set without maps wasn't being picked up. [#1015](https://github.com/terrastruct/d2/pull/1015) +- Fixes a dagre layout error with connections to a container shape with a block type label. [#1032](https://github.com/terrastruct/d2/pull/1032) diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go index 3683051d6..dec8c164e 100644 --- a/d2layouts/d2dagrelayout/layout.go +++ b/d2layouts/d2dagrelayout/layout.go @@ -467,7 +467,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err // if an edge to a container runs into its label, stop the edge at the label instead overlapsContainerLabel := false - if edge.Dst.IsContainer() && edge.Dst.Attributes.Label.Value != "" { + if edge.Dst.IsContainer() && edge.Dst.Attributes.Label.Value != "" && !dstShape.Is(shape.TEXT_TYPE) { // assumes LabelPosition, LabelWidth, LabelHeight are all set if there is a label labelWidth := float64(*edge.Dst.LabelWidth) labelHeight := float64(*edge.Dst.LabelHeight) diff --git a/d2layouts/d2sequence/sequence_diagram.go b/d2layouts/d2sequence/sequence_diagram.go index a6e13b9e7..f30307d7d 100644 --- a/d2layouts/d2sequence/sequence_diagram.go +++ b/d2layouts/d2sequence/sequence_diagram.go @@ -355,13 +355,13 @@ func (sd *sequenceDiagram) placeActors() { } // addLifelineEdges adds a new edge for each actor in the graph that represents the its lifeline -// ┌──────────────┐ -// │ actor │ -// └──────┬───────┘ -// │ -// │ lifeline -// │ -// │ +// . ┌──────────────┐ +// . │ actor │ +// . └──────┬───────┘ +// . │ +// . │ lifeline +// . │ +// . │ func (sd *sequenceDiagram) addLifelineEdges() { endY := 0. if len(sd.messages) > 0 { @@ -433,17 +433,17 @@ func (sd *sequenceDiagram) placeNotes() { } // placeSpans places spans over the object lifeline -// ┌──────────┐ -// │ actor │ -// └────┬─────┘ -// ┌─┴──┐ -// │ │ -// |span| -// │ │ -// └─┬──┘ -// │ -// lifeline -// │ +// . ┌──────────┐ +// . │ actor │ +// . └────┬─────┘ +// . ┌─┴──┐ +// . │ │ +// . |span| +// . │ │ +// . └─┬──┘ +// . │ +// . lifeline +// . │ func (sd *sequenceDiagram) placeSpans() { // quickly find the span center X rankToX := make(map[int]float64) diff --git a/d2parser/parse.go b/d2parser/parse.go index b4c3bf9e8..cd6e7094e 100644 --- a/d2parser/parse.go +++ b/d2parser/parse.go @@ -97,11 +97,12 @@ func ParseValue(value string) (d2ast.Value, error) { } // TODO: refactor parser to keep entire file in memory as []rune -// - trivial to then convert positions -// - lookahead is gone, just forward back as much as you want :) -// - streaming parser isn't really helpful. -// - just read into a string even and decode runes forward/back as needed -// - the whole file essentially exists within the parser as the AST anyway... +// - trivial to then convert positions +// - lookahead is gone, just forward back as much as you want :) +// - streaming parser isn't really helpful. +// - just read into a string even and decode runes forward/back as needed +// - the whole file essentially exists within the parser as the AST anyway... +// // TODO: ast struct that combines map & errors and pass that around type parser struct { path string @@ -315,13 +316,15 @@ func (p *parser) commit() { // // TODO: make each parse function read its delimiter and return nil if not as expected // TODO: lookahead *must* always be empty in between parse calls. you either commit or -// rewind in each function. if you don't, you pass a hint. +// +// rewind in each function. if you don't, you pass a hint. // // TODO: omg we don't need two buffers, just a single lookahead and an index... // TODO: get rid of lookaheadPos or at least never use directly. maybe rename to beforePeekPos? -// or better yet keep positions in the lookahead buffer. -// ok so plan here is to get rid of lookaheadPos and add a rewindPos that stores -// the pos to rewind to. +// +// or better yet keep positions in the lookahead buffer. +// ok so plan here is to get rid of lookaheadPos and add a rewindPos that stores +// the pos to rewind to. func (p *parser) rewind() { if len(p.lookahead) == 0 { return diff --git a/d2plugin/exec.go b/d2plugin/exec.go index 04f0743f7..dc9548dd9 100644 --- a/d2plugin/exec.go +++ b/d2plugin/exec.go @@ -22,18 +22,18 @@ import ( // The layout plugin protocol works as follows. // // Info -// 1. The binary is invoked with info as the first argument. -// 2. The stdout of the binary is unmarshalled into PluginInfo. +// 1. The binary is invoked with info as the first argument. +// 2. The stdout of the binary is unmarshalled into PluginInfo. // // Layout -// 1. The binary is invoked with layout as the first argument and the json marshalled -// d2graph.Graph on stdin. -// 2. The stdout of the binary is unmarshalled into a d2graph.Graph +// 1. The binary is invoked with layout as the first argument and the json marshalled +// d2graph.Graph on stdin. +// 2. The stdout of the binary is unmarshalled into a d2graph.Graph // // PostProcess -// 1. The binary is invoked with postprocess as the first argument and the -// bytes of the SVG render on stdin. -// 2. The stdout of the binary is bytes of SVG with any post-processing. +// 1. The binary is invoked with postprocess as the first argument and the +// bytes of the SVG render on stdin. +// 2. The stdout of the binary is bytes of SVG with any post-processing. // // If any errors occur the binary will exit with a non zero status code and write // the error to stderr. diff --git a/d2plugin/plugin.go b/d2plugin/plugin.go index 56002678d..5a74ab415 100644 --- a/d2plugin/plugin.go +++ b/d2plugin/plugin.go @@ -132,12 +132,12 @@ func ListPluginInfos(ctx context.Context, ps []Plugin) ([]*PluginInfo, error) { } // FindPlugin finds the plugin with the given name. -// 1. It first searches the bundled plugins in the global plugins slice. -// 2. If not found, it then searches each directory in $PATH for a binary with the name -// d2plugin-. -// **NOTE** When D2 upgrades to go 1.19, remember to ignore exec.ErrDot -// 3. If such a binary is found, it builds an execPlugin in exec.go -// to get a plugin implementation around the binary and returns it. +// 1. It first searches the bundled plugins in the global plugins slice. +// 2. If not found, it then searches each directory in $PATH for a binary with the name +// d2plugin-. +// **NOTE** When D2 upgrades to go 1.19, remember to ignore exec.ErrDot +// 3. If such a binary is found, it builds an execPlugin in exec.go +// to get a plugin implementation around the binary and returns it. func FindPlugin(ctx context.Context, ps []Plugin, name string) (Plugin, error) { for _, p := range ps { info, err := p.Info(ctx) diff --git a/e2etests/testdata/todo/dagre_container_md_label_panic/dagre/board.exp.json b/e2etests/testdata/todo/dagre_container_md_label_panic/dagre/board.exp.json new file mode 100644 index 000000000..d9a322df0 --- /dev/null +++ b/e2etests/testdata/todo/dagre_container_md_label_panic/dagre/board.exp.json @@ -0,0 +1,697 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "OEM Factory", + "type": "rectangle", + "pos": { + "x": 77, + "y": 0 + }, + "width": 135, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "OEM Factory", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 90, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "company Warehouse", + "type": "text", + "pos": { + "x": 0, + "y": 166 + }, + "width": 353, + "height": 664, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "### company Warehouse\n- Asset Tagging\n- Inventory\n- Staging\n- Dispatch to Site", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "markdown", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 184, + "labelHeight": 150, + "zIndex": 0, + "level": 1 + }, + { + "id": "company Warehouse.Master", + "type": "rectangle", + "pos": { + "x": 97, + "y": 216 + }, + "width": 94, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Master", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 49, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "company Warehouse.Regional-1", + "type": "rectangle", + "pos": { + "x": 194, + "y": 382 + }, + "width": 120, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Regional-1", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 75, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "company Warehouse.Regional-2", + "type": "rectangle", + "pos": { + "x": 94, + "y": 548 + }, + "width": 120, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Regional-2", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 75, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "company Warehouse.Regional-N", + "type": "rectangle", + "pos": { + "x": 93, + "y": 714 + }, + "width": 122, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Regional-N", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 77, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "(OEM Factory -> company Warehouse)[0]", + "src": "OEM Factory", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 144, + "y": 66 + }, + { + "x": 144, + "y": 106 + }, + { + "x": 144, + "y": 126 + }, + { + "x": 144, + "y": 166 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Master -> Regional-1)[0]", + "src": "company Warehouse.Master", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-1", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 187.53614457831327, + "y": 282 + }, + { + "x": 240.30722891566265, + "y": 322 + }, + { + "x": 253.5, + "y": 342 + }, + { + "x": 253.5, + "y": 382 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Master -> Regional-2)[0]", + "src": "company Warehouse.Master", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-2", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 141.01807228915663, + "y": 282 + }, + { + "x": 137.40361445783134, + "y": 322 + }, + { + "x": 136.5, + "y": 348.6 + }, + { + "x": 136.5, + "y": 373.5 + }, + { + "x": 136.5, + "y": 398.4 + }, + { + "x": 138.5, + "y": 508 + }, + { + "x": 146.5, + "y": 548 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Master -> Regional-N)[0]", + "src": "company Warehouse.Master", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-N", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 108.01807228915663, + "y": 282 + }, + { + "x": 64.40361445783132, + "y": 322 + }, + { + "x": 53.5, + "y": 348.6 + }, + { + "x": 53.5, + "y": 373.5 + }, + { + "x": 53.5, + "y": 398.4 + }, + { + "x": 53.5, + "y": 431.6 + }, + { + "x": 53.5, + "y": 456.5 + }, + { + "x": 53.5, + "y": 481.4 + }, + { + "x": 53.5, + "y": 514.6 + }, + { + "x": 53.5, + "y": 539.5 + }, + { + "x": 53.5, + "y": 564.4 + }, + { + "x": 65.5, + "y": 674 + }, + { + "x": 113.5, + "y": 714 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Regional-1 -> Regional-2)[0]", + "src": "company Warehouse.Regional-1", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-2", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 233.62048192771084, + "y": 448 + }, + { + "x": 209.52409638554218, + "y": 488 + }, + { + "x": 197.5, + "y": 508 + }, + { + "x": 173.5, + "y": 548 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Regional-2 -> Regional-N)[0]", + "src": "company Warehouse.Regional-2", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-N", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 153.5, + "y": 614 + }, + { + "x": 153.5, + "y": 654 + }, + { + "x": 153.5, + "y": 674 + }, + { + "x": 153.5, + "y": 714 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Regional-N -> Regional-1)[0]", + "src": "company Warehouse.Regional-N", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-1", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 209.16265060240966, + "y": 714 + }, + { + "x": 276.63253012048193, + "y": 674 + }, + { + "x": 293.5, + "y": 647.4 + }, + { + "x": 293.5, + "y": 622.5 + }, + { + "x": 293.5, + "y": 597.6 + }, + { + "x": 288.7, + "y": 488 + }, + { + "x": 269.5, + "y": 448 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/todo/dagre_container_md_label_panic/dagre/sketch.exp.svg b/e2etests/testdata/todo/dagre_container_md_label_panic/dagre/sketch.exp.svg new file mode 100644 index 000000000..66955e8f5 --- /dev/null +++ b/e2etests/testdata/todo/dagre_container_md_label_panic/dagre/sketch.exp.svg @@ -0,0 +1,851 @@ +OEM Factory

company Warehouse

+
    +
  • Asset Tagging
  • +
  • Inventory
  • +
  • Staging
  • +
  • Dispatch to Site
  • +
+
MasterRegional-1Regional-2Regional-N + + +
\ No newline at end of file diff --git a/e2etests/testdata/todo/dagre_container_md_label_panic/elk/board.exp.json b/e2etests/testdata/todo/dagre_container_md_label_panic/elk/board.exp.json new file mode 100644 index 000000000..9d37683b7 --- /dev/null +++ b/e2etests/testdata/todo/dagre_container_md_label_panic/elk/board.exp.json @@ -0,0 +1,622 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "OEM Factory", + "type": "rectangle", + "pos": { + "x": 115, + "y": 12 + }, + "width": 135, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "OEM Factory", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 90, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "company Warehouse", + "type": "text", + "pos": { + "x": 12, + "y": 148 + }, + "width": 342, + "height": 604, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "transparent", + "stroke": "N1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "### company Warehouse\n- Asset Tagging\n- Inventory\n- Staging\n- Dispatch to Site", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "markdown", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 184, + "labelHeight": 150, + "zIndex": 0, + "level": 1 + }, + { + "id": "company Warehouse.Master", + "type": "rectangle", + "pos": { + "x": 162, + "y": 198 + }, + "width": 120, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Master", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 49, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "company Warehouse.Regional-1", + "type": "rectangle", + "pos": { + "x": 62, + "y": 344 + }, + "width": 120, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Regional-1", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 75, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "company Warehouse.Regional-2", + "type": "rectangle", + "pos": { + "x": 143, + "y": 490 + }, + "width": 120, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Regional-2", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 75, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "company Warehouse.Regional-N", + "type": "rectangle", + "pos": { + "x": 142, + "y": 636 + }, + "width": 122, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Regional-N", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 77, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "(OEM Factory -> company Warehouse)[0]", + "src": "OEM Factory", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 183, + "y": 78 + }, + { + "x": 183, + "y": 148 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Master -> Regional-1)[0]", + "src": "company Warehouse.Master", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-1", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 192.75, + "y": 264 + }, + { + "x": 192.75, + "y": 304 + }, + { + "x": 122, + "y": 304 + }, + { + "x": 122, + "y": 344 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Master -> Regional-2)[0]", + "src": "company Warehouse.Master", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-2", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 222.75, + "y": 264 + }, + { + "x": 222.75, + "y": 304 + }, + { + "x": 223, + "y": 304 + }, + { + "x": 223, + "y": 490 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Master -> Regional-N)[0]", + "src": "company Warehouse.Master", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-N", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 252.75, + "y": 264 + }, + { + "x": 252.75, + "y": 304 + }, + { + "x": 303, + "y": 304 + }, + { + "x": 303, + "y": 596 + }, + { + "x": 233.5, + "y": 596 + }, + { + "x": 233.5, + "y": 636 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Regional-1 -> Regional-2)[0]", + "src": "company Warehouse.Regional-1", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-2", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 142, + "y": 410 + }, + { + "x": 142, + "y": 450 + }, + { + "x": 183, + "y": 450 + }, + { + "x": 183, + "y": 490 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Regional-2 -> Regional-N)[0]", + "src": "company Warehouse.Regional-2", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-N", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 203, + "y": 556 + }, + { + "x": 203, + "y": 636 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "company Warehouse.(Regional-N -> Regional-1)[0]", + "src": "company Warehouse.Regional-N", + "srcArrow": "none", + "srcLabel": "", + "dst": "company Warehouse.Regional-1", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "B1", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N2", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 172.5, + "y": 636 + }, + { + "x": 172.5, + "y": 596 + }, + { + "x": 102, + "y": 596 + }, + { + "x": 102, + "y": 410 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ], + "root": { + "id": "", + "type": "", + "pos": { + "x": 0, + "y": 0 + }, + "width": 0, + "height": 0, + "opacity": 0, + "strokeDash": 0, + "strokeWidth": 0, + "borderRadius": 0, + "fill": "N7", + "stroke": "", + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "", + "fontSize": 0, + "fontFamily": "", + "language": "", + "color": "", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "zIndex": 0, + "level": 0 + } +} diff --git a/e2etests/testdata/todo/dagre_container_md_label_panic/elk/sketch.exp.svg b/e2etests/testdata/todo/dagre_container_md_label_panic/elk/sketch.exp.svg new file mode 100644 index 000000000..29712be07 --- /dev/null +++ b/e2etests/testdata/todo/dagre_container_md_label_panic/elk/sketch.exp.svg @@ -0,0 +1,851 @@ +OEM Factory

company Warehouse

+
    +
  • Asset Tagging
  • +
  • Inventory
  • +
  • Staging
  • +
  • Dispatch to Site
  • +
+
MasterRegional-1Regional-2Regional-N + + +
\ No newline at end of file diff --git a/e2etests/todo_test.go b/e2etests/todo_test.go index 24b52e03d..232db0b66 100644 --- a/e2etests/todo_test.go +++ b/e2etests/todo_test.go @@ -251,6 +251,27 @@ x -> y: { } y: bar {z} +`, + }, + { + name: "dagre_container_md_label_panic", + script: ` +OEM Factory -> company Warehouse + +company Warehouse.Master -> company Warehouse.Regional-1 +company Warehouse.Master -> company Warehouse.Regional-2 +company Warehouse.Master -> company Warehouse.Regional-N +company Warehouse.Regional-1 -> company Warehouse.Regional-2 +company Warehouse.Regional-2 -> company Warehouse.Regional-N +company Warehouse.Regional-N -> company Warehouse.Regional-1 + +company Warehouse: |md + ### company Warehouse + - Asset Tagging + - Inventory + - Staging + - Dispatch to Site +| `, }, } diff --git a/lib/geo/bezier.go b/lib/geo/bezier.go index 458ff70e0..3e0e9691b 100644 --- a/lib/geo/bezier.go +++ b/lib/geo/bezier.go @@ -65,7 +65,7 @@ func (bc BezierCurve) At(point float64) *Point { return NewPoint(float64(curvePoint.X), float64(curvePoint.Y)) } -//nolint +// nolint func ComputeIntersections(px, py, lx, ly []float64) []*Point { out := make([]*Point, 0) @@ -111,7 +111,7 @@ func ComputeIntersections(px, py, lx, ly []float64) []*Point { return out } -//nolint +// nolint func cubicRoots(P []float64) []float64 { if PrecisionCompare(P[0], 0, PRECISION) == 0 { if PrecisionCompare(P[1], 0, PRECISION) == 0 { @@ -209,7 +209,7 @@ func cubicRoots(P []float64) []float64 { return t } -//nolint +// nolint func sortSpecial(a []float64) []float64 { var flip bool var temp float64 @@ -235,7 +235,7 @@ func sortSpecial(a []float64) []float64 { return a } -//nolint +// nolint func sgn(x float64) float64 { if x < 0.0 { return -1 @@ -243,7 +243,7 @@ func sgn(x float64) float64 { return 1 } -//nolint +// nolint func bezierCoeffs(P0, P1, P2, P3 float64) []float64 { Z := make([]float64, 4) Z[0] = -P0 + 3*P1 + -3*P2 + P3 diff --git a/lib/geo/segment.go b/lib/geo/segment.go index be71812f8..ea29df98c 100644 --- a/lib/geo/segment.go +++ b/lib/geo/segment.go @@ -59,24 +59,24 @@ func (segment Segment) Intersections(otherSegment Segment) []*Point { // If there is no floor or ceiling, negative or positive infinity is used, respectively // The direction is inferred, e.g. b/c the passed in segment is vertical, it's inferred we want horizontal bounds // buffer says how close the segment can be, on both axes, to other segments given -// │ │ -// │ │ -// │ │ -// │ │ -// │ non-overlap -// │ -// │ -// │ -// │ segment -// │ │ -// │ │ ceil -// │ │ │ -// │ │ -// floor │ │ -// │ -// │ -// │ -// │ +// . │ │ +// . │ │ +// . │ │ +// . │ │ +// . │ non-overlap +// . │ +// . │ +// . │ +// . │ segment +// . │ │ +// . │ │ ceil +// . │ │ │ +// . │ │ +// . floor │ │ +// . │ +// . │ +// . │ +// . │ // NOTE: the assumption is that all segments given are orthogonal func (segment *Segment) GetBounds(segments []*Segment, buffer float64) (float64, float64) { ceil := math.Inf(1) diff --git a/lib/shape/shape.go b/lib/shape/shape.go index d3dc97114..2eb83cd21 100644 --- a/lib/shape/shape.go +++ b/lib/shape/shape.go @@ -175,22 +175,22 @@ func NewShape(shapeType string, box *geo.Box) Shape { // p is the prev point (used to calculate slope) // s is the point on the actual shape border that'll be returned // -// p -// │ -// │ -// ▼ -// ┌────r─────────────────────────┐ -// │ │ -// │ │ │ -// │ │ xxxxxxxx │ -// │ ▼ xxxxx xxxx │ -// │ sxxx xx │ -// │ x xx │ -// │ xx xx │ -// │ x xx │ -// │ xx xxx │ -// │ xxxx xxxx │ -// └──────xxxxxxxxxxxxxx──────────┘ +// . p +// . │ +// . │ +// . ▼ +// . ┌────r─────────────────────────┐ +// . │ │ +// . │ │ │ +// . │ │ xxxxxxxx │ +// . │ ▼ xxxxx xxxx │ +// . │ sxxx xx │ +// . │ x xx │ +// . │ xx xx │ +// . │ x xx │ +// . │ xx xxx │ +// . │ xxxx xxxx │ +// . └──────xxxxxxxxxxxxxx──────────┘ func TraceToShapeBorder(shape Shape, rectBorderPoint, prevPoint *geo.Point) *geo.Point { if shape.Is("") || shape.IsRectangular() { return rectBorderPoint diff --git a/lib/textmeasure/textmeasure.go b/lib/textmeasure/textmeasure.go index 2e539e86d..ea104e18a 100644 --- a/lib/textmeasure/textmeasure.go +++ b/lib/textmeasure/textmeasure.go @@ -32,23 +32,27 @@ func init() { // Ruler allows for effiecient and convenient text drawing. // // To create a Ruler object, use the New constructor: -// txt := text.New(pixel.ZV, text.NewAtlas(face, text.ASCII)) +// +// txt := text.New(pixel.ZV, text.NewAtlas(face, text.ASCII)) // // As suggested by the constructor, a Ruler object is always associated with one font face and a // fixed set of runes. For example, the Ruler we created above can draw text using the font face // contained in the face variable and is capable of drawing ASCII characters. // // Here we create a Ruler object which can draw ASCII and Katakana characters: -// txt := text.New(0, text.NewAtlas(face, text.ASCII, text.RangeTable(unicode.Katakana))) +// +// txt := text.New(0, text.NewAtlas(face, text.ASCII, text.RangeTable(unicode.Katakana))) // // Similarly to IMDraw, Ruler functions as a buffer. It implements io.Writer interface, so writing // text to it is really simple: -// fmt.Print(txt, "Hello, world!") +// +// fmt.Print(txt, "Hello, world!") // // Newlines, tabs and carriage returns are supported. // // Finally, if we want the written text to show up on some other Target, we can draw it: -// txt.Draw(target) +// +// txt.Draw(target) // // Ruler exports two important fields: Orig and Dot. Dot is the position where the next character // will be written. Dot is automatically moved when writing to a Ruler object, but you can also @@ -93,14 +97,15 @@ type Ruler struct { // will be initially set to orig. // // Here we create a Ruler capable of drawing ASCII characters using the Go Regular font. -// ttf, err := truetype.Parse(goregular.TTF) -// if err != nil { -// panic(err) -// } -// face := truetype.NewFace(ttf, &truetype.Options{ -// Size: 14, -// }) -// txt := text.New(orig, text.NewAtlas(face, text.ASCII)) +// +// ttf, err := truetype.Parse(goregular.TTF) +// if err != nil { +// panic(err) +// } +// face := truetype.NewFace(ttf, &truetype.Options{ +// Size: 14, +// }) +// txt := text.New(orig, text.NewAtlas(face, text.ASCII)) func NewRuler() (*Ruler, error) { origin := geo.NewPoint(0, 0) r := &Ruler{