diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 94d61a45d..02a599e54 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -5,9 +5,12 @@ #### Improvements 🧹 +- Dagre edges are spaced apart to prevent label overlap. [#618](https://github.com/terrastruct/d2/pull/618) + #### Bugfixes ⛑️ - Appendix seperator line no longer added to PNG export when appendix doesn't exist. [#582](https://github.com/terrastruct/d2/pull/582) - Watch mode only fits to screen on initial load. [#601](https://github.com/terrastruct/d2/pull/601) - Dimensions (`width`/`height`) were incorrectly giving compiler errors when applied on a shape with style. [#614](https://github.com/terrastruct/d2/pull/614) - `near` would collide with labels if they were on the diagram boundaries in the same position. [#617](https://github.com/terrastruct/d2/pull/617) +- Fixes routing between sql table columns if the column name is the prefix of the table name [#615](https://github.com/terrastruct/d2/pull/615) diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 9b3edefd8..48498a767 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -666,7 +666,7 @@ func (c *compiler) compileSQLTable(obj *d2graph.Object) { obj.SQLTable = &d2target.SQLTable{} parentID := obj.Parent.AbsID() - tableID := obj.AbsID() + tableIDPrefix := obj.AbsID() + "." for _, col := range obj.ChildrenArray { if col.IDVal == "style" { continue @@ -702,7 +702,7 @@ func (c *compiler) compileSQLTable(obj *d2graph.Object) { srcID := e.Src.AbsID() dstID := e.Dst.AbsID() // skip edges between columns of the same table - if strings.HasPrefix(srcID, tableID) && strings.HasPrefix(dstID, tableID) { + if strings.HasPrefix(srcID, tableIDPrefix) && strings.HasPrefix(dstID, tableIDPrefix) { continue } if srcID == absID { diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index 885a48da2..81db413f2 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -1852,6 +1852,31 @@ choo: { expErr: `d2/testdata/d2compiler/TestCompile/sql-panic.d2:3:27: constraint value must be a string `, }, + { + name: "wrong_column_index", + text: `Chinchillas: { + shape: sql_table + id: int {constraint: primary_key} + whisker_len: int + fur_color: string + age: int + server: int {constraint: foreign_key} + caretaker: int {constraint: foreign_key} +} + +Chinchillas_Collectibles: { + shape: sql_table + id: int + collectible: id {constraint: foreign_key} + chinchilla: id {constraint: foreign_key} +} + +Chinchillas_Collectibles.chinchilla -> Chinchillas.id`, + assertions: func(t *testing.T, g *d2graph.Graph) { + tassert.Equal(t, 0, *g.Edges[0].DstTableColumnIndex) + tassert.Equal(t, 2, *g.Edges[0].SrcTableColumnIndex) + }, + }, } for _, tc := range testCases { diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go index 9969d2080..0d5c5d569 100644 --- a/d2layouts/d2dagrelayout/layout.go +++ b/d2layouts/d2dagrelayout/layout.go @@ -149,7 +149,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err // for `b <- a`, edge.Edge is `a -> b` and we expect this routing result src, dst = dst, src } - loadScript += generateAddEdgeLine(src.AbsID(), dst.AbsID(), edge.AbsID()) + loadScript += generateAddEdgeLine(src.AbsID(), dst.AbsID(), edge.AbsID(), edge.LabelDimensions.Width, edge.LabelDimensions.Height) } if debugJS { @@ -320,7 +320,6 @@ func generateAddParentLine(childID, parentID string) string { return fmt.Sprintf("g.setParent(`%s`, `%s`);\n", escapeID(childID), escapeID(parentID)) } -func generateAddEdgeLine(fromID, toID, edgeID string) string { - // in dagre v is from, w is to, name is to uniquely identify - return fmt.Sprintf("g.setEdge({v:`%s`, w:`%s`, name:`%s` });\n", escapeID(fromID), escapeID(toID), escapeID(edgeID)) +func generateAddEdgeLine(fromID, toID, edgeID string, width, height int) string { + return fmt.Sprintf("g.setEdge({v:`%s`, w:`%s`, name:`%s`}, { width:%d, height:%d, labelpos: `c` });\n", escapeID(fromID), escapeID(toID), escapeID(edgeID), width, height) } diff --git a/d2renderers/d2sketch/testdata/connection_label/sketch.exp.svg b/d2renderers/d2sketch/testdata/connection_label/sketch.exp.svg index 5bc3c0273..93a126788 100644 --- a/d2renderers/d2sketch/testdata/connection_label/sketch.exp.svg +++ b/d2renderers/d2sketch/testdata/connection_label/sketch.exp.svg @@ -3,7 +3,7 @@ id="d2-svg" style="background: white;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" -width="319" height="556" viewBox="-102 -102 319 556">Kubernetesopensvck8s-master1k8s-master2k8s-master3k8s-worker1k8s-worker2k8s-worker3VM1VM2 keycloakheptapodharborvault + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/regression/overlapping-edge-label/elk/board.exp.json b/e2etests/testdata/regression/overlapping-edge-label/elk/board.exp.json new file mode 100644 index 000000000..6fd038717 --- /dev/null +++ b/e2etests/testdata/regression/overlapping-edge-label/elk/board.exp.json @@ -0,0 +1,588 @@ +{ + "name": "", + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "k8s", + "type": "", + "pos": { + "x": 12, + "y": 12 + }, + "width": 1405, + "height": 276, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#E3E9FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "Kubernetes", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 138, + "labelHeight": 41, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "k8s.m1", + "type": "", + "pos": { + "x": 87, + "y": 87 + }, + "width": 192, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "k8s-master1", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 92, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "k8s.m2", + "type": "", + "pos": { + "x": 299, + "y": 87 + }, + "width": 192, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "k8s-master2", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 92, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "k8s.m3", + "type": "", + "pos": { + "x": 511, + "y": 87 + }, + "width": 192, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "k8s-master3", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 92, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "k8s.w1", + "type": "", + "pos": { + "x": 723, + "y": 87 + }, + "width": 193, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "k8s-worker1", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 93, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "k8s.w2", + "type": "", + "pos": { + "x": 936, + "y": 87 + }, + "width": 193, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "k8s-worker2", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 93, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "k8s.w3", + "type": "", + "pos": { + "x": 1149, + "y": 87 + }, + "width": 193, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "k8s-worker3", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 93, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "osvc", + "type": "", + "pos": { + "x": 397, + "y": 519 + }, + "width": 442, + "height": 276, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#E3E9FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "opensvc", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 102, + "labelHeight": 41, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "osvc.vm1", + "type": "", + "pos": { + "x": 472, + "y": 594 + }, + "width": 136, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "VM1", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 36, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "osvc.vm2", + "type": "", + "pos": { + "x": 628, + "y": 594 + }, + "width": 136, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "#EDF0FD", + "stroke": "#0D32B2", + "shadow": false, + "3d": false, + "multiple": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "VM2", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 36, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "(k8s -> osvc)[0]", + "src": "k8s", + "srcArrow": "none", + "srcLabel": "", + "dst": "osvc", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "keycloak", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 59, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPercentage": 0, + "route": [ + { + "x": 293, + "y": 288 + }, + { + "x": 293, + "y": 459 + }, + { + "x": 485.6, + "y": 459 + }, + { + "x": 485.6, + "y": 519 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(k8s -> osvc)[1]", + "src": "k8s", + "srcArrow": "none", + "srcLabel": "", + "dst": "osvc", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "heptapod", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 65, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPercentage": 0, + "route": [ + { + "x": 574, + "y": 288 + }, + { + "x": 574, + "y": 519 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(k8s -> osvc)[2]", + "src": "k8s", + "srcArrow": "none", + "srcLabel": "", + "dst": "osvc", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "harbor", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 47, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPercentage": 0, + "route": [ + { + "x": 855, + "y": 288 + }, + { + "x": 855, + "y": 459 + }, + { + "x": 662.4000000000001, + "y": 459 + }, + { + "x": 662.4000000000001, + "y": 519 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(k8s -> osvc)[3]", + "src": "k8s", + "srcArrow": "none", + "srcLabel": "", + "dst": "osvc", + "dstArrow": "triangle", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "#0D32B2", + "label": "vault", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 35, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "labelPercentage": 0, + "route": [ + { + "x": 1136, + "y": 288 + }, + { + "x": 1136, + "y": 469 + }, + { + "x": 750.8, + "y": 469 + }, + { + "x": 750.8, + "y": 519 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ] +} diff --git a/e2etests/testdata/regression/overlapping-edge-label/elk/sketch.exp.svg b/e2etests/testdata/regression/overlapping-edge-label/elk/sketch.exp.svg new file mode 100644 index 000000000..c81e90cf4 --- /dev/null +++ b/e2etests/testdata/regression/overlapping-edge-label/elk/sketch.exp.svg @@ -0,0 +1,69 @@ + +Kubernetesopensvck8s-master1k8s-master2k8s-master3k8s-worker1k8s-worker2k8s-worker3VM1VM2 keycloakheptapodharborvault + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/sanity/connection_label/dagre/board.exp.json b/e2etests/testdata/sanity/connection_label/dagre/board.exp.json index d2e114820..4e7beb287 100644 --- a/e2etests/testdata/sanity/connection_label/dagre/board.exp.json +++ b/e2etests/testdata/sanity/connection_label/dagre/board.exp.json @@ -47,7 +47,7 @@ "type": "", "pos": { "x": 0, - "y": 226 + "y": 247 }, "width": 113, "height": 126, @@ -115,15 +115,15 @@ }, { "x": 56.5, - "y": 166 + "y": 174.4 }, { "x": 56.5, - "y": 186 + "y": 198.7 }, { "x": 56.5, - "y": 226 + "y": 247.5 } ], "isCurve": true, diff --git a/e2etests/testdata/sanity/connection_label/dagre/sketch.exp.svg b/e2etests/testdata/sanity/connection_label/dagre/sketch.exp.svg index 719702277..51ae8878c 100644 --- a/e2etests/testdata/sanity/connection_label/dagre/sketch.exp.svg +++ b/e2etests/testdata/sanity/connection_label/dagre/sketch.exp.svg @@ -3,7 +3,7 @@ id="d2-svg" style="background: white;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" -width="317" height="556" viewBox="-102 -102 317 556">aabbllmmnnoocciikkddgghhjjeeff1122 334455667788 - - - - - - - - - +aabbllmmnnoocciikkddgghhjjeeff1122 334455667788 + + + + + + + + + mixed togethersugarsolution we get - - +mixed togethersugarsolution we get + +