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 @@
+
+
\ 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
+
+