From b27235a4760cc7746cc5ea09fc3009b0c30686dd Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 3 Feb 2025 16:48:59 -0700 Subject: [PATCH] d2near: constant nears avoid edge routes --- d2layouts/d2near/layout.go | 11 + .../txtar/elk-title-near/dagre/board.exp.json | 503 ++++++++++++++++++ .../txtar/elk-title-near/dagre/sketch.exp.svg | 107 ++++ .../txtar/elk-title-near/elk/board.exp.json | 444 ++++++++++++++++ .../txtar/elk-title-near/elk/sketch.exp.svg | 107 ++++ e2etests/txtar.txt | 12 + 6 files changed, 1184 insertions(+) create mode 100644 e2etests/testdata/txtar/elk-title-near/dagre/board.exp.json create mode 100644 e2etests/testdata/txtar/elk-title-near/dagre/sketch.exp.svg create mode 100644 e2etests/testdata/txtar/elk-title-near/elk/board.exp.json create mode 100644 e2etests/testdata/txtar/elk-title-near/elk/sketch.exp.svg diff --git a/d2layouts/d2near/layout.go b/d2layouts/d2near/layout.go index cfe568d05..eedecd40d 100644 --- a/d2layouts/d2near/layout.go +++ b/d2layouts/d2near/layout.go @@ -190,6 +190,17 @@ func boundingBox(g *d2graph.Graph) (tl, br *geo.Point) { } } + for _, edge := range g.Edges { + if edge.Route != nil { + for _, point := range edge.Route { + x1 = math.Min(x1, point.X-pad/2) + y1 = math.Min(y1, point.Y-pad/2) + x2 = math.Max(x2, point.X+pad/2) + y2 = math.Max(y2, point.Y+pad/2) + } + } + } + if math.IsInf(x1, 1) && math.IsInf(x2, -1) { x1 = 0 x2 = 0 diff --git a/e2etests/testdata/txtar/elk-title-near/dagre/board.exp.json b/e2etests/testdata/txtar/elk-title-near/dagre/board.exp.json new file mode 100644 index 000000000..ba19475f6 --- /dev/null +++ b/e2etests/testdata/txtar/elk-title-near/dagre/board.exp.json @@ -0,0 +1,503 @@ +{ + "name": "", + "config": { + "sketch": false, + "themeID": 0, + "darkThemeID": null, + "pad": null, + "center": null, + "layoutEngine": null + }, + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "title", + "type": "rectangle", + "pos": { + "x": -121, + "y": -107 + }, + "width": 408, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "diagram title : Red-line hits 'near: top-center' in elk", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 363, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "a", + "type": "rectangle", + "pos": { + "x": 10, + "y": 20 + }, + "width": 146, + "height": 292, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 12, + "labelHeight": 36, + "labelPosition": "OUTSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "a.a", + "type": "rectangle", + "pos": { + "x": 73, + "y": 50 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "a.b", + "type": "rectangle", + "pos": { + "x": 40, + "y": 216 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "b", + "type": "rectangle", + "pos": { + "x": 43, + "y": 452 + }, + "width": 113, + "height": 126, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "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": "b.c", + "type": "rectangle", + "pos": { + "x": 73, + "y": 482 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "a.(a -> b)[0]", + "src": "a.a", + "srcArrow": "none", + "dst": "a.b", + "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, + "link": "", + "route": [ + { + "x": 86.5, + "y": 116 + }, + { + "x": 70.5, + "y": 156 + }, + { + "x": 66.5, + "y": 176 + }, + { + "x": 66.5, + "y": 216 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(a.b -> b.c)[0]", + "src": "a.b", + "srcArrow": "none", + "dst": "b.c", + "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, + "link": "", + "route": [ + { + "x": 66.5, + "y": 282 + }, + { + "x": 66.5, + "y": 322 + }, + { + "x": 66.5, + "y": 342 + }, + { + "x": 66.5, + "y": 357 + }, + { + "x": 66.5, + "y": 372 + }, + { + "x": 70.5, + "y": 442 + }, + { + "x": 86.5, + "y": 482 + } + ], + "isCurve": true, + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(b.c -> a.a)[0]", + "src": "b.c", + "srcArrow": "none", + "dst": "a.a", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "red", + "fill": "mistyrose", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "red", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 113, + "y": 482 + }, + { + "x": 129, + "y": 442 + }, + { + "x": 133, + "y": 422 + }, + { + "x": 133, + "y": 407 + }, + { + "x": 133, + "y": 392 + }, + { + "x": 133, + "y": 372 + }, + { + "x": 133, + "y": 357 + }, + { + "x": 133, + "y": 342 + }, + { + "x": 133, + "y": 315.3999938964844 + }, + { + "x": 133, + "y": 290.5 + }, + { + "x": 133, + "y": 265.6000061035156 + }, + { + "x": 129, + "y": 156 + }, + { + "x": 113, + "y": 116 + } + ], + "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": "", + "animated": false, + "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/txtar/elk-title-near/dagre/sketch.exp.svg b/e2etests/testdata/txtar/elk-title-near/dagre/sketch.exp.svg new file mode 100644 index 000000000..aeefbd124 --- /dev/null +++ b/e2etests/testdata/txtar/elk-title-near/dagre/sketch.exp.svg @@ -0,0 +1,107 @@ +diagram title : Red-line hits 'near: top-center' in elkababc + + + + + + + + \ No newline at end of file diff --git a/e2etests/testdata/txtar/elk-title-near/elk/board.exp.json b/e2etests/testdata/txtar/elk-title-near/elk/board.exp.json new file mode 100644 index 000000000..dc901df0c --- /dev/null +++ b/e2etests/testdata/txtar/elk-title-near/elk/board.exp.json @@ -0,0 +1,444 @@ +{ + "name": "", + "config": { + "sketch": false, + "themeID": 0, + "darkThemeID": null, + "pad": null, + "center": null, + "layoutEngine": null + }, + "isFolderOnly": false, + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "title", + "type": "rectangle", + "pos": { + "x": -100, + "y": -84 + }, + "width": 408, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B6", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "diagram title : Red-line hits 'near: top-center' in elk", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 363, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "a", + "type": "rectangle", + "pos": { + "x": 52, + "y": 57 + }, + "width": 153, + "height": 302, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 28, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": false, + "underline": false, + "labelWidth": 12, + "labelHeight": 36, + "labelPosition": "INSIDE_TOP_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "a.a", + "type": "rectangle", + "pos": { + "x": 102, + "y": 107 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "a.b", + "type": "rectangle", + "pos": { + "x": 102, + "y": 243 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + }, + { + "id": "b", + "type": "rectangle", + "pos": { + "x": 52, + "y": 439 + }, + "width": 153, + "height": 166, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B4", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "b", + "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": "b.c", + "type": "rectangle", + "pos": { + "x": 102, + "y": 489 + }, + "width": 53, + "height": 66, + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "borderRadius": 0, + "fill": "B5", + "stroke": "B1", + "animated": false, + "shadow": false, + "3d": false, + "multiple": false, + "double-border": false, + "tooltip": "", + "link": "", + "icon": null, + "iconPosition": "", + "blend": false, + "fields": null, + "methods": null, + "columns": null, + "label": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "N1", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 8, + "labelHeight": 21, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 2 + } + ], + "connections": [ + { + "id": "a.(a -> b)[0]", + "src": "a.a", + "srcArrow": "none", + "dst": "a.b", + "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, + "link": "", + "route": [ + { + "x": 128.5, + "y": 173 + }, + { + "x": 128.5, + "y": 243 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(a.b -> b.c)[0]", + "src": "a.b", + "srcArrow": "none", + "dst": "b.c", + "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, + "link": "", + "route": [ + { + "x": 128.5, + "y": 309 + }, + { + "x": 128.5, + "y": 489 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(b.c -> a.a)[0]", + "src": "b.c", + "srcArrow": "none", + "dst": "a.a", + "dstArrow": "triangle", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 2, + "stroke": "red", + "fill": "mistyrose", + "borderRadius": 10, + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "red", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "link": "", + "route": [ + { + "x": 128.5, + "y": 555 + }, + { + "x": 128.5, + "y": 650 + }, + { + "x": 12, + "y": 650 + }, + { + "x": 12, + "y": 12 + }, + { + "x": 128.5, + "y": 12 + }, + { + "x": 128.5, + "y": 107 + } + ], + "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": "", + "animated": false, + "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/txtar/elk-title-near/elk/sketch.exp.svg b/e2etests/testdata/txtar/elk-title-near/elk/sketch.exp.svg new file mode 100644 index 000000000..9b5c267d3 --- /dev/null +++ b/e2etests/testdata/txtar/elk-title-near/elk/sketch.exp.svg @@ -0,0 +1,107 @@ +diagram title : Red-line hits 'near: top-center' in elkababc + + + + + + + + \ No newline at end of file diff --git a/e2etests/txtar.txt b/e2etests/txtar.txt index fae92f88b..46e7e9dfe 100644 --- a/e2etests/txtar.txt +++ b/e2etests/txtar.txt @@ -747,3 +747,15 @@ alice -> alice: "Self-messages" alice -> alice: "Self-messages" bob."In the eyes of my dog, I'm a man." + +-- elk-title-near -- +title: "diagram title : Red-line hits 'near: top-center' in elk" {near: top-center} +a: { + a -> b +} +b: { + c +} + +a.b -> b.c +b.c -> a.a: {style.font-color: red; style.stroke: red; style.fill: mistyrose}