Merge branch 'master' into beautify

This commit is contained in:
Júlio César Batista 2022-12-02 17:18:16 -08:00
commit bf6a79f136
No known key found for this signature in database
GPG key ID: 10C4B861BF314878
8 changed files with 446 additions and 131 deletions

View file

@ -49,3 +49,6 @@
- `$BROWSER` now works to open a custom browser correctly. - `$BROWSER` now works to open a custom browser correctly.
For example, to open Firefox on macOS: `BROWSER='open -aFirefox'` For example, to open Firefox on macOS: `BROWSER='open -aFirefox'`
[#311](https://github.com/terrastruct/d2/pull/311) [#311](https://github.com/terrastruct/d2/pull/311)
- Fixes numbered IDs being wrongly positioned in `dagre`
[#321](https://github.com/terrastruct/d2/issues/321). Thank you @pleshevskiy for the
report.

View file

@ -30,6 +30,7 @@ var setupJS string
var dagreJS string var dagreJS string
type DagreNode struct { type DagreNode struct {
ID string `json:"id"`
X float64 `json:"x"` X float64 `json:"x"`
Y float64 `json:"y"` Y float64 `json:"y"`
Width float64 `json:"width"` Width float64 `json:"width"`
@ -49,7 +50,7 @@ type dagreGraphAttrs struct {
rankdir string rankdir string
} }
func Layout(ctx context.Context, d2graph *d2graph.Graph) (err error) { func Layout(ctx context.Context, g *d2graph.Graph) (err error) {
defer xdefer.Errorf(&err, "failed to dagre layout") defer xdefer.Errorf(&err, "failed to dagre layout")
debugJS := false debugJS := false
@ -66,7 +67,7 @@ func Layout(ctx context.Context, d2graph *d2graph.Graph) (err error) {
edgesep: 40, edgesep: 40,
nodesep: 60, nodesep: 60,
} }
switch d2graph.Root.Attributes.Direction.Value { switch g.Root.Attributes.Direction.Value {
case "down": case "down":
rootAttrs.rankdir = "TB" rootAttrs.rankdir = "TB"
case "right": case "right":
@ -84,14 +85,16 @@ func Layout(ctx context.Context, d2graph *d2graph.Graph) (err error) {
} }
loadScript := "" loadScript := ""
for _, obj := range d2graph.Objects { idToObj := make(map[string]*d2graph.Object)
for _, obj := range g.Objects {
id := obj.AbsID() id := obj.AbsID()
idToObj[id] = obj
loadScript += generateAddNodeLine(id, int(obj.Width), int(obj.Height)) loadScript += generateAddNodeLine(id, int(obj.Width), int(obj.Height))
if obj.Parent != d2graph.Root { if obj.Parent != g.Root {
loadScript += generateAddParentLine(id, obj.Parent.AbsID()) loadScript += generateAddParentLine(id, obj.Parent.AbsID())
} }
} }
for _, edge := range d2graph.Edges { for _, edge := range g.Edges {
// dagre doesn't work with edges to containers so we connect container edges to their first child instead (going all the way down) // dagre doesn't work with edges to containers so we connect container edges to their first child instead (going all the way down)
// we will chop the edge where it intersects the container border so it only shows the edge from the container // we will chop the edge where it intersects the container border so it only shows the edge from the container
src := edge.Src src := edge.Src
@ -124,13 +127,7 @@ func Layout(ctx context.Context, d2graph *d2graph.Graph) (err error) {
return err return err
} }
// val, err := v8ctx.RunScript("JSON.stringify(dagre.graphlib.json.write(g))", "q.js") for i := range g.Objects {
// if err != nil {
// return err
// }
// log.Debug(ctx, "graph", slog.F("json", val.String()))
for i, obj := range d2graph.Objects {
val, err := v8ctx.RunScript(fmt.Sprintf("JSON.stringify(g.node(g.nodes()[%d]))", i), "value.js") val, err := v8ctx.RunScript(fmt.Sprintf("JSON.stringify(g.node(g.nodes()[%d]))", i), "value.js")
if err != nil { if err != nil {
return err return err
@ -139,6 +136,11 @@ func Layout(ctx context.Context, d2graph *d2graph.Graph) (err error) {
if err := json.Unmarshal([]byte(val.String()), &dn); err != nil { if err := json.Unmarshal([]byte(val.String()), &dn); err != nil {
return err return err
} }
if debugJS {
log.Debug(ctx, "graph", slog.F("json", dn))
}
obj := idToObj[dn.ID]
// dagre gives center of node // dagre gives center of node
obj.TopLeft = geo.NewPoint(math.Round(dn.X-dn.Width/2), math.Round(dn.Y-dn.Height/2)) obj.TopLeft = geo.NewPoint(math.Round(dn.X-dn.Width/2), math.Round(dn.Y-dn.Height/2))
@ -159,7 +161,7 @@ func Layout(ctx context.Context, d2graph *d2graph.Graph) (err error) {
} }
} }
for i, edge := range d2graph.Edges { for i, edge := range g.Edges {
val, err := v8ctx.RunScript(fmt.Sprintf("JSON.stringify(g.edge(g.edges()[%d]))", i), "value.js") val, err := v8ctx.RunScript(fmt.Sprintf("JSON.stringify(g.edge(g.edges()[%d]))", i), "value.js")
if err != nil { if err != nil {
return err return err
@ -168,6 +170,9 @@ func Layout(ctx context.Context, d2graph *d2graph.Graph) (err error) {
if err := json.Unmarshal([]byte(val.String()), &de); err != nil { if err := json.Unmarshal([]byte(val.String()), &de); err != nil {
return err return err
} }
if debugJS {
log.Debug(ctx, "graph", slog.F("json", de))
}
points := make([]*geo.Point, len(de.Points)) points := make([]*geo.Point, len(de.Points))
for i := range de.Points { for i := range de.Points {
@ -250,7 +255,7 @@ func setGraphAttrs(attrs dagreGraphAttrs) string {
} }
func generateAddNodeLine(id string, width, height int) string { func generateAddNodeLine(id string, width, height int) string {
return fmt.Sprintf("g.setNode(`%s`, { width: %d, height: %d });\n", id, width, height) return fmt.Sprintf("g.setNode(`%s`, { id: `%s`, width: %d, height: %d });\n", id, id, width, height)
} }
func generateAddParentLine(childID, parentID string) string { func generateAddParentLine(childID, parentID string) string {

View file

@ -125,6 +125,8 @@ func run(t *testing.T, tc testCase) {
svgBytes, err := d2svg.Render(diagram) svgBytes, err := d2svg.Render(diagram)
assert.Success(t, err) assert.Success(t, err)
err = os.MkdirAll(dataPath, 0755)
assert.Success(t, err)
err = ioutil.WriteFile(pathGotSVG, svgBytes, 0600) err = ioutil.WriteFile(pathGotSVG, svgBytes, 0600)
assert.Success(t, err) assert.Success(t, err)
defer os.Remove(pathGotSVG) defer os.Remove(pathGotSVG)

View file

@ -1210,6 +1210,14 @@ finally.sequence.scorer.abc -> finally.sequence.item.a
finally.sequence.itemOutcome.a.b.c.d.e -> finally.sequence.scorer finally.sequence.itemOutcome.a.b.c.d.e -> finally.sequence.scorer
finally.sequence.scorer -> finally.sequence.itemResponse.c`, finally.sequence.scorer -> finally.sequence.itemResponse.c`,
}, },
{
name: "number_connections",
script: `1 -> 2
foo baz: Foo Baz
foo baz -> hello
`,
},
} }
runa(t, tcs) runa(t, tcs)

View file

@ -0,0 +1,259 @@
{
"name": "",
"shapes": [
{
"id": "foo baz",
"type": "",
"pos": {
"x": 173,
"y": 0
},
"width": 159,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "Foo Baz",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 59,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "1",
"type": "",
"pos": {
"x": 1,
"y": 0
},
"width": 112,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "1",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 12,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "2",
"type": "",
"pos": {
"x": 0,
"y": 226
},
"width": 113,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 13,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "hello",
"type": "",
"pos": {
"x": 182,
"y": 226
},
"width": 140,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "hello",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 40,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
}
],
"connections": [
{
"id": "(1 -> 2)[0]",
"src": "1",
"srcArrow": "none",
"srcLabel": "",
"dst": "2",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 56.5,
"y": 126
},
{
"x": 56.5,
"y": 166
},
{
"x": 56.5,
"y": 186
},
{
"x": 56.5,
"y": 226
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(foo baz -> hello)[0]",
"src": "foo baz",
"srcArrow": "none",
"srcLabel": "",
"dst": "hello",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "#0D32B2",
"label": "",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#676C7E",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 252,
"y": 126
},
{
"x": 252,
"y": 166
},
{
"x": 252,
"y": 186
},
{
"x": 252,
"y": 226
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
}
]
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 325 KiB

View file

@ -2,58 +2,19 @@
"name": "", "name": "",
"shapes": [ "shapes": [
{ {
"id": "container", "id": "foo baz",
"type": "", "type": "",
"pos": { "pos": {
"x": 0, "x": 12,
"y": 0 "y": 12
}, },
"width": 255, "width": 159,
"height": 452,
"level": 1,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#E3E9FD",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "container",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 117,
"labelHeight": 41,
"labelPosition": "INSIDE_TOP_CENTER"
},
{
"id": "container.first",
"type": "",
"pos": {
"x": 60,
"y": 50
},
"width": 135,
"height": 126, "height": 126,
"level": 2,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "#EDF0FD", "fill": "#F7F8FE",
"stroke": "#0D32B2", "stroke": "#0D32B2",
"shadow": false, "shadow": false,
"3d": false, "3d": false,
@ -65,7 +26,7 @@
"fields": null, "fields": null,
"methods": null, "methods": null,
"columns": null, "columns": null,
"label": "first", "label": "Foo Baz",
"fontSize": 16, "fontSize": 16,
"fontFamily": "DEFAULT", "fontFamily": "DEFAULT",
"language": "", "language": "",
@ -73,25 +34,26 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 35, "labelWidth": 59,
"labelHeight": 26, "labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER" "labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
}, },
{ {
"id": "container.second", "id": "1",
"type": "", "type": "",
"pos": { "pos": {
"x": 50, "x": 191,
"y": 276 "y": 12
}, },
"width": 155, "width": 112,
"height": 126, "height": 126,
"level": 2,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"borderRadius": 0, "borderRadius": 0,
"fill": "#EDF0FD", "fill": "#F7F8FE",
"stroke": "#0D32B2", "stroke": "#0D32B2",
"shadow": false, "shadow": false,
"3d": false, "3d": false,
@ -103,7 +65,7 @@
"fields": null, "fields": null,
"methods": null, "methods": null,
"columns": null, "columns": null,
"label": "second", "label": "1",
"fontSize": 16, "fontSize": 16,
"fontFamily": "DEFAULT", "fontFamily": "DEFAULT",
"language": "", "language": "",
@ -111,25 +73,105 @@
"italic": false, "italic": false,
"bold": true, "bold": true,
"underline": false, "underline": false,
"labelWidth": 55, "labelWidth": 12,
"labelHeight": 26, "labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER" "labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "2",
"type": "",
"pos": {
"x": 191,
"y": 238
},
"width": 113,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "2",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 13,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "hello",
"type": "",
"pos": {
"x": 22,
"y": 238
},
"width": 140,
"height": 126,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "#F7F8FE",
"stroke": "#0D32B2",
"shadow": false,
"3d": false,
"multiple": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"fields": null,
"methods": null,
"columns": null,
"label": "hello",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "#0A0F25",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 40,
"labelHeight": 26,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
} }
], ],
"connections": [ "connections": [
{ {
"id": "container.(first -> second)[0]", "id": "(1 -> 2)[0]",
"src": "container.first", "src": "1",
"srcArrow": "none", "srcArrow": "none",
"srcLabel": "", "srcLabel": "",
"dst": "container.second", "dst": "2",
"dstArrow": "triangle", "dstArrow": "triangle",
"dstLabel": "", "dstLabel": "",
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"stroke": "#0D32B2", "stroke": "#0D32B2",
"label": "1->2", "label": "",
"fontSize": 16, "fontSize": 16,
"fontFamily": "DEFAULT", "fontFamily": "DEFAULT",
"language": "", "language": "",
@ -137,46 +179,38 @@
"italic": true, "italic": true,
"bold": false, "bold": false,
"underline": false, "underline": false,
"labelWidth": 29, "labelWidth": 0,
"labelHeight": 21, "labelHeight": 0,
"labelPosition": "INSIDE_MIDDLE_CENTER", "labelPosition": "",
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 103.10840707964601, "x": 247,
"y": 176 "y": 138
}, },
{ {
"x": 87.6216814159292, "x": 247,
"y": 216 "y": 238
},
{
"x": 87.55,
"y": 236
},
{
"x": 102.75,
"y": 276
} }
], ],
"isCurve": true,
"animated": false, "animated": false,
"tooltip": "", "tooltip": "",
"icon": null "icon": null,
"zIndex": 0
}, },
{ {
"id": "(container -> container.second)[0]", "id": "(foo baz -> hello)[0]",
"src": "container", "src": "foo baz",
"srcArrow": "none", "srcArrow": "none",
"srcLabel": "", "srcLabel": "",
"dst": "container.second", "dst": "hello",
"dstArrow": "triangle", "dstArrow": "triangle",
"dstLabel": "", "dstLabel": "",
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
"strokeWidth": 2, "strokeWidth": 2,
"stroke": "#0D32B2", "stroke": "#0D32B2",
"label": "c->2", "label": "",
"fontSize": 16, "fontSize": 16,
"fontFamily": "DEFAULT", "fontFamily": "DEFAULT",
"language": "", "language": "",
@ -184,32 +218,24 @@
"italic": true, "italic": true,
"bold": false, "bold": false,
"underline": false, "underline": false,
"labelWidth": 28, "labelWidth": 0,
"labelHeight": 21, "labelHeight": 0,
"labelPosition": "INSIDE_MIDDLE_CENTER", "labelPosition": "",
"labelPercentage": 0, "labelPercentage": 0,
"route": [ "route": [
{ {
"x": 151.891592920354, "x": 91.5,
"y": 176 "y": 138
}, },
{ {
"x": 167.3783185840708, "x": 91.5,
"y": 216 "y": 238
},
{
"x": 167.45,
"y": 236
},
{
"x": 152.25,
"y": 276
} }
], ],
"isCurve": true,
"animated": false, "animated": false,
"tooltip": "", "tooltip": "",
"icon": null "icon": null,
"zIndex": 0
} }
] ]
} }

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 468 KiB

After

Width:  |  Height:  |  Size: 325 KiB