From fa297e1e3fb4b10de2798b89bcde52cf68e8f83d Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 22 Sep 2023 10:45:18 -0700 Subject: [PATCH 1/6] add dagre_child_id_id test --- e2etests/regression_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/e2etests/regression_test.go b/e2etests/regression_test.go index e098212ba..ede987121 100644 --- a/e2etests/regression_test.go +++ b/e2etests/regression_test.go @@ -1031,6 +1031,10 @@ cf many required: { } `, }, + { + name: "dagre_child_id_id", + script: `direction:right; id -> x.id -> y.z.id`, + }, loadFromFile(t, "slow_grid"), loadFromFile(t, "grid_oom"), loadFromFile(t, "cylinder_grid_label"), From 412ad4a6dbe0f414065167f2736bfb3b8884df1e Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 22 Sep 2023 12:57:40 -0700 Subject: [PATCH 2/6] create dagre object mapper to manage ids for dagre to use --- d2layouts/d2dagrelayout/layout.go | 58 ++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 16 deletions(-) diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go index 6d004e25f..b88e5e6b9 100644 --- a/d2layouts/d2dagrelayout/layout.go +++ b/d2layouts/d2dagrelayout/layout.go @@ -8,6 +8,7 @@ import ( "math" "regexp" "sort" + "strconv" "strings" "cdr.dev/slog" @@ -140,17 +141,13 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err return err } + mapper := NewObjectMapper() loadScript := "" - idToObj := make(map[string]*d2graph.Object) for _, obj := range g.Objects { - id := obj.AbsID() - idToObj[id] = obj - - width, height := obj.Width, obj.Height - - loadScript += generateAddNodeLine(id, int(width), int(height)) + mapper.Register(obj) + loadScript += mapper.generateAddNodeLine(obj, int(obj.Width), int(obj.Height)) if obj.Parent != g.Root { - loadScript += generateAddParentLine(id, obj.Parent.AbsID()) + loadScript += mapper.generateAddParentLine(obj, obj.Parent) } } @@ -178,7 +175,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err } } - loadScript += generateAddEdgeLine(src.AbsID(), dst.AbsID(), edge.AbsID(), width, height) + loadScript += mapper.generateAddEdgeLine(src, dst, edge.AbsID(), width, height) } if debugJS { @@ -209,7 +206,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err log.Debug(ctx, "graph", slog.F("json", dn)) } - obj := idToObj[dn.ID] + obj := mapper.ToObj(dn.ID) // dagre gives center of node obj.TopLeft = geo.NewPoint(math.Round(dn.X-dn.Width/2), math.Round(dn.Y-dn.Height/2)) @@ -415,6 +412,32 @@ func setGraphAttrs(attrs dagreOpts) string { ) } +type objectMapper struct { + objToID map[*d2graph.Object]string + idToObj map[string]*d2graph.Object +} + +func NewObjectMapper() *objectMapper { + return &objectMapper{ + objToID: make(map[*d2graph.Object]string), + idToObj: make(map[string]*d2graph.Object), + } +} + +func (c *objectMapper) Register(obj *d2graph.Object) { + id := strconv.Itoa(len(c.idToObj)) + c.idToObj[id] = obj + c.objToID[obj] = id +} + +func (c *objectMapper) ToID(obj *d2graph.Object) string { + return c.objToID[obj] +} + +func (c *objectMapper) ToObj(id string) *d2graph.Object { + return c.idToObj[id] +} + func escapeID(id string) string { // fixes \\ id = strings.ReplaceAll(id, "\\", `\\`) @@ -426,17 +449,20 @@ func escapeID(id string) string { return id } -func generateAddNodeLine(id string, width, height int) string { - id = escapeID(id) +func (c objectMapper) generateAddNodeLine(obj *d2graph.Object, width, height int) string { + id := c.ToID(obj) return fmt.Sprintf("g.setNode(`%s`, { id: `%s`, width: %d, height: %d });\n", id, id, width, height) } -func generateAddParentLine(childID, parentID string) string { - return fmt.Sprintf("g.setParent(`%s`, `%s`);\n", escapeID(childID), escapeID(parentID)) +func (c objectMapper) generateAddParentLine(child, parent *d2graph.Object) string { + return fmt.Sprintf("g.setParent(`%s`, `%s`);\n", c.ToID(child), c.ToID(parent)) } -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) +func (c objectMapper) generateAddEdgeLine(from, to *d2graph.Object, edgeID string, width, height int) string { + return fmt.Sprintf( + "g.setEdge({v:`%s`, w:`%s`, name:`%s`}, { width:%d, height:%d, labelpos: `c` });\n", + c.ToID(from), c.ToID(to), escapeID(edgeID), width, height, + ) } // getLongestEdgeChainHead finds the longest chain in a container and gets its head From b5449ebd61a45dff4925c19a447a0a4008844cc7 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 22 Sep 2023 12:58:06 -0700 Subject: [PATCH 3/6] test success --- .../dagre_child_id_id/dagre/board.exp.json | 413 ++++++++++++++++++ .../dagre_child_id_id/dagre/sketch.exp.svg | 107 +++++ .../dagre_child_id_id/elk/board.exp.json | 371 ++++++++++++++++ .../dagre_child_id_id/elk/sketch.exp.svg | 107 +++++ 4 files changed, 998 insertions(+) create mode 100644 e2etests/testdata/regression/dagre_child_id_id/dagre/board.exp.json create mode 100644 e2etests/testdata/regression/dagre_child_id_id/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/regression/dagre_child_id_id/elk/board.exp.json create mode 100644 e2etests/testdata/regression/dagre_child_id_id/elk/sketch.exp.svg diff --git a/e2etests/testdata/regression/dagre_child_id_id/dagre/board.exp.json b/e2etests/testdata/regression/dagre_child_id_id/dagre/board.exp.json new file mode 100644 index 000000000..411315457 --- /dev/null +++ b/e2etests/testdata/regression/dagre_child_id_id/dagre/board.exp.json @@ -0,0 +1,413 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "id", + "type": "rectangle", + "pos": { + "x": 0, + "y": 147 + }, + "width": 59, + "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": "id", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "x", + "type": "rectangle", + "pos": { + "x": 179, + "y": 117 + }, + "width": 119, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "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": "x", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 13, + "labelHeight": 36, + "labelPosition": "OUTSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "x.id", + "type": "rectangle", + "pos": { + "x": 209, + "y": 147 + }, + "width": 59, + "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": "id", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "y", + "type": "rectangle", + "pos": { + "x": 458, + "y": 76 + }, + "width": 179, + "height": 197, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "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": "y", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 13, + "labelHeight": 36, + "labelPosition": "OUTSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "y.z", + "type": "rectangle", + "pos": { + "x": 488, + "y": 117 + }, + "width": 119, + "height": 126, + "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": "z", + "fontSize": 24, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 10, + "labelHeight": 31, + "labelPosition": "OUTSIDE_TOP_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "y.z.id", + "type": "rectangle", + "pos": { + "x": 518, + "y": 147 + }, + "width": 59, + "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": "id", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 3 + } + ], + "connections": [ + { + "id": "(id -> x.id)[0]", + "src": "id", + "srcArrow": "none", + "dst": "x.id", + "dstArrow": "triangle", + "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": 59, + "y": 180 + }, + { + "x": 99, + "y": 180 + }, + { + "x": 169, + "y": 180 + }, + { + "x": 209, + "y": 180 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(x.id -> y.z.id)[0]", + "src": "x.id", + "srcArrow": "none", + "dst": "y.z.id", + "dstArrow": "triangle", + "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": 268, + "y": 180 + }, + { + "x": 308, + "y": 180 + }, + { + "x": 328, + "y": 180 + }, + { + "x": 343, + "y": 180 + }, + { + "x": 358, + "y": 180 + }, + { + "x": 378, + "y": 180 + }, + { + "x": 393, + "y": 180 + }, + { + "x": 408, + "y": 180 + }, + { + "x": 478, + "y": 180 + }, + { + "x": 518, + "y": 180 + } + ], + "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/regression/dagre_child_id_id/dagre/sketch.exp.svg b/e2etests/testdata/regression/dagre_child_id_id/dagre/sketch.exp.svg new file mode 100644 index 000000000..cc914e05f --- /dev/null +++ b/e2etests/testdata/regression/dagre_child_id_id/dagre/sketch.exp.svg @@ -0,0 +1,107 @@ +idxyidzid + + + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/regression/dagre_child_id_id/elk/board.exp.json b/e2etests/testdata/regression/dagre_child_id_id/elk/board.exp.json new file mode 100644 index 000000000..c6cab2da2 --- /dev/null +++ b/e2etests/testdata/regression/dagre_child_id_id/elk/board.exp.json @@ -0,0 +1,371 @@ +{ + "name": "", + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "id", + "type": "rectangle", + "pos": { + "x": 12, + "y": 112 + }, + "width": 59, + "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": "id", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "x", + "type": "rectangle", + "pos": { + "x": 146, + "y": 62 + }, + "width": 159, + "height": 166, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "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": "x", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 13, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "x.id", + "type": "rectangle", + "pos": { + "x": 196, + "y": 112 + }, + "width": 59, + "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": "id", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "y", + "type": "rectangle", + "pos": { + "x": 385, + "y": 12 + }, + "width": 264, + "height": 266, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "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": "y", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 13, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "y.z", + "type": "rectangle", + "pos": { + "x": 440, + "y": 62 + }, + "width": 159, + "height": 166, + "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": "z", + "fontSize": 24, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 10, + "labelHeight": 31, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "y.z.id", + "type": "rectangle", + "pos": { + "x": 490, + "y": 112 + }, + "width": 59, + "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": "id", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 3 + } + ], + "connections": [ + { + "id": "(id -> x.id)[0]", + "src": "id", + "srcArrow": "none", + "dst": "x.id", + "dstArrow": "triangle", + "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": 71, + "y": 145 + }, + { + "x": 196, + "y": 145 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(x.id -> y.z.id)[0]", + "src": "x.id", + "srcArrow": "none", + "dst": "y.z.id", + "dstArrow": "triangle", + "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": 255, + "y": 145 + }, + { + "x": 490, + "y": 145 + } + ], + "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/regression/dagre_child_id_id/elk/sketch.exp.svg b/e2etests/testdata/regression/dagre_child_id_id/elk/sketch.exp.svg new file mode 100644 index 000000000..2f9353b57 --- /dev/null +++ b/e2etests/testdata/regression/dagre_child_id_id/elk/sketch.exp.svg @@ -0,0 +1,107 @@ +idxyidzid + + + + + + + + \ No newline at end of file From de7b15cc31dbb3f6bea47f53361d4451c2f3ca0a Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 22 Sep 2023 13:08:53 -0700 Subject: [PATCH 4/6] update tests --- .../stable/ovals/dagre/board.exp.json | 24 +-- .../stable/ovals/dagre/sketch.exp.svg | 176 +++++++++--------- .../stable/people/dagre/board.exp.json | 24 +-- .../stable/people/dagre/sketch.exp.svg | 176 +++++++++--------- 4 files changed, 200 insertions(+), 200 deletions(-) diff --git a/e2etests/testdata/stable/ovals/dagre/board.exp.json b/e2etests/testdata/stable/ovals/dagre/board.exp.json index 78811f99c..a46940e27 100644 --- a/e2etests/testdata/stable/ovals/dagre/board.exp.json +++ b/e2etests/testdata/stable/ovals/dagre/board.exp.json @@ -7,7 +7,7 @@ "id": "a", "type": "oval", "pos": { - "x": 1064, + "x": 0, "y": 83 }, "width": 35, @@ -48,7 +48,7 @@ "id": "b", "type": "oval", "pos": { - "x": 1159, + "x": 95, "y": 86 }, "width": 53, @@ -89,7 +89,7 @@ "id": "c", "type": "oval", "pos": { - "x": 1272, + "x": 208, "y": 90 }, "width": 77, @@ -130,7 +130,7 @@ "id": "d", "type": "oval", "pos": { - "x": 1409, + "x": 345, "y": 96 }, "width": 118, @@ -171,7 +171,7 @@ "id": "e", "type": "oval", "pos": { - "x": 1587, + "x": 523, "y": 98 }, "width": 182, @@ -212,7 +212,7 @@ "id": "f", "type": "oval", "pos": { - "x": 1829, + "x": 765, "y": 78 }, "width": 304, @@ -253,7 +253,7 @@ "id": "g", "type": "oval", "pos": { - "x": 2193, + "x": 1129, "y": 37 }, "width": 545, @@ -294,7 +294,7 @@ "id": "1", "type": "oval", "pos": { - "x": 0, + "x": 1734, "y": 86 }, "width": 28, @@ -335,7 +335,7 @@ "id": "2", "type": "oval", "pos": { - "x": 88, + "x": 1822, "y": 87 }, "width": 64, @@ -376,7 +376,7 @@ "id": "3", "type": "oval", "pos": { - "x": 212, + "x": 1946, "y": 87 }, "width": 128, @@ -417,7 +417,7 @@ "id": "4", "type": "oval", "pos": { - "x": 400, + "x": 2134, "y": 43 }, "width": 512, @@ -458,7 +458,7 @@ "id": "5", "type": "oval", "pos": { - "x": 972, + "x": 2706, "y": 0 }, "width": 32, diff --git a/e2etests/testdata/stable/ovals/dagre/sketch.exp.svg b/e2etests/testdata/stable/ovals/dagre/sketch.exp.svg index 254db9585..0ecae5230 100644 --- a/e2etests/testdata/stable/ovals/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/ovals/dagre/sketch.exp.svg @@ -1,9 +1,9 @@ --------------------------------------------------------------------------------------------------------------------------------12345 + .d2-2659884077 .fill-N1{fill:#0A0F25;} + .d2-2659884077 .fill-N2{fill:#676C7E;} + .d2-2659884077 .fill-N3{fill:#9499AB;} + .d2-2659884077 .fill-N4{fill:#CFD2DD;} + .d2-2659884077 .fill-N5{fill:#DEE1EB;} + .d2-2659884077 .fill-N6{fill:#EEF1F8;} + .d2-2659884077 .fill-N7{fill:#FFFFFF;} + .d2-2659884077 .fill-B1{fill:#0D32B2;} + .d2-2659884077 .fill-B2{fill:#0D32B2;} + .d2-2659884077 .fill-B3{fill:#E3E9FD;} + .d2-2659884077 .fill-B4{fill:#E3E9FD;} + .d2-2659884077 .fill-B5{fill:#EDF0FD;} + .d2-2659884077 .fill-B6{fill:#F7F8FE;} + .d2-2659884077 .fill-AA2{fill:#4A6FF3;} + .d2-2659884077 .fill-AA4{fill:#EDF0FD;} + .d2-2659884077 .fill-AA5{fill:#F7F8FE;} + .d2-2659884077 .fill-AB4{fill:#EDF0FD;} + .d2-2659884077 .fill-AB5{fill:#F7F8FE;} + .d2-2659884077 .stroke-N1{stroke:#0A0F25;} + .d2-2659884077 .stroke-N2{stroke:#676C7E;} + .d2-2659884077 .stroke-N3{stroke:#9499AB;} + .d2-2659884077 .stroke-N4{stroke:#CFD2DD;} + .d2-2659884077 .stroke-N5{stroke:#DEE1EB;} + .d2-2659884077 .stroke-N6{stroke:#EEF1F8;} + .d2-2659884077 .stroke-N7{stroke:#FFFFFF;} + .d2-2659884077 .stroke-B1{stroke:#0D32B2;} + .d2-2659884077 .stroke-B2{stroke:#0D32B2;} + .d2-2659884077 .stroke-B3{stroke:#E3E9FD;} + .d2-2659884077 .stroke-B4{stroke:#E3E9FD;} + .d2-2659884077 .stroke-B5{stroke:#EDF0FD;} + .d2-2659884077 .stroke-B6{stroke:#F7F8FE;} + .d2-2659884077 .stroke-AA2{stroke:#4A6FF3;} + .d2-2659884077 .stroke-AA4{stroke:#EDF0FD;} + .d2-2659884077 .stroke-AA5{stroke:#F7F8FE;} + .d2-2659884077 .stroke-AB4{stroke:#EDF0FD;} + .d2-2659884077 .stroke-AB5{stroke:#F7F8FE;} + .d2-2659884077 .background-color-N1{background-color:#0A0F25;} + .d2-2659884077 .background-color-N2{background-color:#676C7E;} + .d2-2659884077 .background-color-N3{background-color:#9499AB;} + .d2-2659884077 .background-color-N4{background-color:#CFD2DD;} + .d2-2659884077 .background-color-N5{background-color:#DEE1EB;} + .d2-2659884077 .background-color-N6{background-color:#EEF1F8;} + .d2-2659884077 .background-color-N7{background-color:#FFFFFF;} + .d2-2659884077 .background-color-B1{background-color:#0D32B2;} + .d2-2659884077 .background-color-B2{background-color:#0D32B2;} + .d2-2659884077 .background-color-B3{background-color:#E3E9FD;} + .d2-2659884077 .background-color-B4{background-color:#E3E9FD;} + .d2-2659884077 .background-color-B5{background-color:#EDF0FD;} + .d2-2659884077 .background-color-B6{background-color:#F7F8FE;} + .d2-2659884077 .background-color-AA2{background-color:#4A6FF3;} + .d2-2659884077 .background-color-AA4{background-color:#EDF0FD;} + .d2-2659884077 .background-color-AA5{background-color:#F7F8FE;} + .d2-2659884077 .background-color-AB4{background-color:#EDF0FD;} + .d2-2659884077 .background-color-AB5{background-color:#F7F8FE;} + .d2-2659884077 .color-N1{color:#0A0F25;} + .d2-2659884077 .color-N2{color:#676C7E;} + .d2-2659884077 .color-N3{color:#9499AB;} + .d2-2659884077 .color-N4{color:#CFD2DD;} + .d2-2659884077 .color-N5{color:#DEE1EB;} + .d2-2659884077 .color-N6{color:#EEF1F8;} + .d2-2659884077 .color-N7{color:#FFFFFF;} + .d2-2659884077 .color-B1{color:#0D32B2;} + .d2-2659884077 .color-B2{color:#0D32B2;} + .d2-2659884077 .color-B3{color:#E3E9FD;} + .d2-2659884077 .color-B4{color:#E3E9FD;} + .d2-2659884077 .color-B5{color:#EDF0FD;} + .d2-2659884077 .color-B6{color:#F7F8FE;} + .d2-2659884077 .color-AA2{color:#4A6FF3;} + .d2-2659884077 .color-AA4{color:#EDF0FD;} + .d2-2659884077 .color-AA5{color:#F7F8FE;} + .d2-2659884077 .color-AB4{color:#EDF0FD;} + .d2-2659884077 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>-------------------------------------------------------------------------------------------------------------------------------12345 - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/stable/people/dagre/board.exp.json b/e2etests/testdata/stable/people/dagre/board.exp.json index 34a8b5c83..5267ee627 100644 --- a/e2etests/testdata/stable/people/dagre/board.exp.json +++ b/e2etests/testdata/stable/people/dagre/board.exp.json @@ -7,7 +7,7 @@ "id": "a", "type": "person", "pos": { - "x": 1077, + "x": 0, "y": 138 }, "width": 44, @@ -48,7 +48,7 @@ "id": "b", "type": "person", "pos": { - "x": 1181, + "x": 104, "y": 138 }, "width": 44, @@ -89,7 +89,7 @@ "id": "c", "type": "person", "pos": { - "x": 1285, + "x": 208, "y": 138 }, "width": 44, @@ -130,7 +130,7 @@ "id": "d", "type": "person", "pos": { - "x": 1389, + "x": 312, "y": 138 }, "width": 58, @@ -171,7 +171,7 @@ "id": "e", "type": "person", "pos": { - "x": 1507, + "x": 430, "y": 137 }, "width": 100, @@ -212,7 +212,7 @@ "id": "f", "type": "person", "pos": { - "x": 1667, + "x": 590, "y": 109 }, "width": 185, @@ -253,7 +253,7 @@ "id": "g", "type": "person", "pos": { - "x": 1912, + "x": 835, "y": 52 }, "width": 355, @@ -294,7 +294,7 @@ "id": "1", "type": "person", "pos": { - "x": 0, + "x": 1250, "y": 140 }, "width": 41, @@ -335,7 +335,7 @@ "id": "2", "type": "person", "pos": { - "x": 101, + "x": 1351, "y": 140 }, "width": 64, @@ -376,7 +376,7 @@ "id": "3", "type": "person", "pos": { - "x": 225, + "x": 1475, "y": 128 }, "width": 128, @@ -417,7 +417,7 @@ "id": "4", "type": "person", "pos": { - "x": 413, + "x": 1663, "y": 0 }, "width": 512, @@ -458,7 +458,7 @@ "id": "5", "type": "person", "pos": { - "x": 985, + "x": 2235, "y": 43 }, "width": 32, diff --git a/e2etests/testdata/stable/people/dagre/sketch.exp.svg b/e2etests/testdata/stable/people/dagre/sketch.exp.svg index d36be5bb3..42c9f1af6 100644 --- a/e2etests/testdata/stable/people/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/people/dagre/sketch.exp.svg @@ -1,9 +1,9 @@ --------------------------------------------------------------------------------------------------------------------------------12345 + .d2-736845025 .fill-N1{fill:#0A0F25;} + .d2-736845025 .fill-N2{fill:#676C7E;} + .d2-736845025 .fill-N3{fill:#9499AB;} + .d2-736845025 .fill-N4{fill:#CFD2DD;} + .d2-736845025 .fill-N5{fill:#DEE1EB;} + .d2-736845025 .fill-N6{fill:#EEF1F8;} + .d2-736845025 .fill-N7{fill:#FFFFFF;} + .d2-736845025 .fill-B1{fill:#0D32B2;} + .d2-736845025 .fill-B2{fill:#0D32B2;} + .d2-736845025 .fill-B3{fill:#E3E9FD;} + .d2-736845025 .fill-B4{fill:#E3E9FD;} + .d2-736845025 .fill-B5{fill:#EDF0FD;} + .d2-736845025 .fill-B6{fill:#F7F8FE;} + .d2-736845025 .fill-AA2{fill:#4A6FF3;} + .d2-736845025 .fill-AA4{fill:#EDF0FD;} + .d2-736845025 .fill-AA5{fill:#F7F8FE;} + .d2-736845025 .fill-AB4{fill:#EDF0FD;} + .d2-736845025 .fill-AB5{fill:#F7F8FE;} + .d2-736845025 .stroke-N1{stroke:#0A0F25;} + .d2-736845025 .stroke-N2{stroke:#676C7E;} + .d2-736845025 .stroke-N3{stroke:#9499AB;} + .d2-736845025 .stroke-N4{stroke:#CFD2DD;} + .d2-736845025 .stroke-N5{stroke:#DEE1EB;} + .d2-736845025 .stroke-N6{stroke:#EEF1F8;} + .d2-736845025 .stroke-N7{stroke:#FFFFFF;} + .d2-736845025 .stroke-B1{stroke:#0D32B2;} + .d2-736845025 .stroke-B2{stroke:#0D32B2;} + .d2-736845025 .stroke-B3{stroke:#E3E9FD;} + .d2-736845025 .stroke-B4{stroke:#E3E9FD;} + .d2-736845025 .stroke-B5{stroke:#EDF0FD;} + .d2-736845025 .stroke-B6{stroke:#F7F8FE;} + .d2-736845025 .stroke-AA2{stroke:#4A6FF3;} + .d2-736845025 .stroke-AA4{stroke:#EDF0FD;} + .d2-736845025 .stroke-AA5{stroke:#F7F8FE;} + .d2-736845025 .stroke-AB4{stroke:#EDF0FD;} + .d2-736845025 .stroke-AB5{stroke:#F7F8FE;} + .d2-736845025 .background-color-N1{background-color:#0A0F25;} + .d2-736845025 .background-color-N2{background-color:#676C7E;} + .d2-736845025 .background-color-N3{background-color:#9499AB;} + .d2-736845025 .background-color-N4{background-color:#CFD2DD;} + .d2-736845025 .background-color-N5{background-color:#DEE1EB;} + .d2-736845025 .background-color-N6{background-color:#EEF1F8;} + .d2-736845025 .background-color-N7{background-color:#FFFFFF;} + .d2-736845025 .background-color-B1{background-color:#0D32B2;} + .d2-736845025 .background-color-B2{background-color:#0D32B2;} + .d2-736845025 .background-color-B3{background-color:#E3E9FD;} + .d2-736845025 .background-color-B4{background-color:#E3E9FD;} + .d2-736845025 .background-color-B5{background-color:#EDF0FD;} + .d2-736845025 .background-color-B6{background-color:#F7F8FE;} + .d2-736845025 .background-color-AA2{background-color:#4A6FF3;} + .d2-736845025 .background-color-AA4{background-color:#EDF0FD;} + .d2-736845025 .background-color-AA5{background-color:#F7F8FE;} + .d2-736845025 .background-color-AB4{background-color:#EDF0FD;} + .d2-736845025 .background-color-AB5{background-color:#F7F8FE;} + .d2-736845025 .color-N1{color:#0A0F25;} + .d2-736845025 .color-N2{color:#676C7E;} + .d2-736845025 .color-N3{color:#9499AB;} + .d2-736845025 .color-N4{color:#CFD2DD;} + .d2-736845025 .color-N5{color:#DEE1EB;} + .d2-736845025 .color-N6{color:#EEF1F8;} + .d2-736845025 .color-N7{color:#FFFFFF;} + .d2-736845025 .color-B1{color:#0D32B2;} + .d2-736845025 .color-B2{color:#0D32B2;} + .d2-736845025 .color-B3{color:#E3E9FD;} + .d2-736845025 .color-B4{color:#E3E9FD;} + .d2-736845025 .color-B5{color:#EDF0FD;} + .d2-736845025 .color-B6{color:#F7F8FE;} + .d2-736845025 .color-AA2{color:#4A6FF3;} + .d2-736845025 .color-AA4{color:#EDF0FD;} + .d2-736845025 .color-AA5{color:#F7F8FE;} + .d2-736845025 .color-AB4{color:#EDF0FD;} + .d2-736845025 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>-------------------------------------------------------------------------------------------------------------------------------12345 - - - - - - - - - - - - + + + + + + + + + + + + \ No newline at end of file From c2a9a95168f468f4cc416578cb5e457bbf711d53 Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Fri, 22 Sep 2023 13:20:04 -0700 Subject: [PATCH 5/6] changelog --- ci/release/changelogs/next.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 3866a8fc3..163ebafa9 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -17,3 +17,4 @@ - Fixes multiple constant nears overlapping in some cases [#1591](https://github.com/terrastruct/d2/issues/1591) - Fixes error with an empty nested grid [#1594](https://github.com/terrastruct/d2/issues/1594) - Fixes incorrect `d2fmt` with variable substitution mid-string [#1611](https://github.com/terrastruct/d2/issues/1611) +- Fixes dagre error with child named id [#1610](https://github.com/terrastruct/d2/issues/1610) From 451f0e0208bfe34d95c7ed9801caa046320eb02a Mon Sep 17 00:00:00 2001 From: Gavin Nishizawa Date: Mon, 25 Sep 2023 10:25:11 -0700 Subject: [PATCH 6/6] create object_mapper.go --- d2layouts/d2dagrelayout/layout.go | 55 --------------------- d2layouts/d2dagrelayout/object_mapper.go | 63 ++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 55 deletions(-) create mode 100644 d2layouts/d2dagrelayout/object_mapper.go diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go index b88e5e6b9..66ae6c8f3 100644 --- a/d2layouts/d2dagrelayout/layout.go +++ b/d2layouts/d2dagrelayout/layout.go @@ -6,9 +6,7 @@ import ( "encoding/json" "fmt" "math" - "regexp" "sort" - "strconv" "strings" "cdr.dev/slog" @@ -412,59 +410,6 @@ func setGraphAttrs(attrs dagreOpts) string { ) } -type objectMapper struct { - objToID map[*d2graph.Object]string - idToObj map[string]*d2graph.Object -} - -func NewObjectMapper() *objectMapper { - return &objectMapper{ - objToID: make(map[*d2graph.Object]string), - idToObj: make(map[string]*d2graph.Object), - } -} - -func (c *objectMapper) Register(obj *d2graph.Object) { - id := strconv.Itoa(len(c.idToObj)) - c.idToObj[id] = obj - c.objToID[obj] = id -} - -func (c *objectMapper) ToID(obj *d2graph.Object) string { - return c.objToID[obj] -} - -func (c *objectMapper) ToObj(id string) *d2graph.Object { - return c.idToObj[id] -} - -func escapeID(id string) string { - // fixes \\ - id = strings.ReplaceAll(id, "\\", `\\`) - // replaces \n with \\n whenever \n is not preceded by \ (does not replace \\n) - re := regexp.MustCompile(`[^\\]\n`) - id = re.ReplaceAllString(id, `\\n`) - // avoid an unescaped \r becoming a \n in the layout result - id = strings.ReplaceAll(id, "\r", `\r`) - return id -} - -func (c objectMapper) generateAddNodeLine(obj *d2graph.Object, width, height int) string { - id := c.ToID(obj) - return fmt.Sprintf("g.setNode(`%s`, { id: `%s`, width: %d, height: %d });\n", id, id, width, height) -} - -func (c objectMapper) generateAddParentLine(child, parent *d2graph.Object) string { - return fmt.Sprintf("g.setParent(`%s`, `%s`);\n", c.ToID(child), c.ToID(parent)) -} - -func (c objectMapper) generateAddEdgeLine(from, to *d2graph.Object, edgeID string, width, height int) string { - return fmt.Sprintf( - "g.setEdge({v:`%s`, w:`%s`, name:`%s`}, { width:%d, height:%d, labelpos: `c` });\n", - c.ToID(from), c.ToID(to), escapeID(edgeID), width, height, - ) -} - // getLongestEdgeChainHead finds the longest chain in a container and gets its head // If there are multiple chains of the same length, get the head closest to the center func getLongestEdgeChainHead(g *d2graph.Graph, container *d2graph.Object) *d2graph.Object { diff --git a/d2layouts/d2dagrelayout/object_mapper.go b/d2layouts/d2dagrelayout/object_mapper.go new file mode 100644 index 000000000..6ac2b637e --- /dev/null +++ b/d2layouts/d2dagrelayout/object_mapper.go @@ -0,0 +1,63 @@ +package d2dagrelayout + +import ( + "fmt" + "regexp" + "strconv" + "strings" + + "oss.terrastruct.com/d2/d2graph" +) + +type objectMapper struct { + objToID map[*d2graph.Object]string + idToObj map[string]*d2graph.Object +} + +func NewObjectMapper() *objectMapper { + return &objectMapper{ + objToID: make(map[*d2graph.Object]string), + idToObj: make(map[string]*d2graph.Object), + } +} + +func (c *objectMapper) Register(obj *d2graph.Object) { + id := strconv.Itoa(len(c.idToObj)) + c.idToObj[id] = obj + c.objToID[obj] = id +} + +func (c *objectMapper) ToID(obj *d2graph.Object) string { + return c.objToID[obj] +} + +func (c *objectMapper) ToObj(id string) *d2graph.Object { + return c.idToObj[id] +} + +func (c objectMapper) generateAddNodeLine(obj *d2graph.Object, width, height int) string { + id := c.ToID(obj) + return fmt.Sprintf("g.setNode(`%s`, { id: `%s`, width: %d, height: %d });\n", id, id, width, height) +} + +func (c objectMapper) generateAddParentLine(child, parent *d2graph.Object) string { + return fmt.Sprintf("g.setParent(`%s`, `%s`);\n", c.ToID(child), c.ToID(parent)) +} + +func (c objectMapper) generateAddEdgeLine(from, to *d2graph.Object, edgeID string, width, height int) string { + return fmt.Sprintf( + "g.setEdge({v:`%s`, w:`%s`, name:`%s`}, { width:%d, height:%d, labelpos: `c` });\n", + c.ToID(from), c.ToID(to), escapeID(edgeID), width, height, + ) +} + +func escapeID(id string) string { + // fixes \\ + id = strings.ReplaceAll(id, "\\", `\\`) + // replaces \n with \\n whenever \n is not preceded by \ (does not replace \\n) + re := regexp.MustCompile(`[^\\]\n`) + id = re.ReplaceAllString(id, `\\n`) + // avoid an unescaped \r becoming a \n in the layout result + id = strings.ReplaceAll(id, "\r", `\r`) + return id +}