fix dagre node ordering

This commit is contained in:
Alexander Wang 2022-12-02 15:51:57 -08:00
parent 0d370cc600
commit 557e097a42
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
4 changed files with 31 additions and 27 deletions

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

@ -1213,7 +1213,6 @@ finally.sequence.scorer -> finally.sequence.itemResponse.c`,
{ {
name: "number_connections", name: "number_connections",
script: `1 -> 2 script: `1 -> 2
foo baz: Foo Baz foo baz: Foo Baz
foo baz -> hello foo baz -> hello

View file

@ -5,10 +5,10 @@
"id": "foo baz", "id": "foo baz",
"type": "", "type": "",
"pos": { "pos": {
"x": 1, "x": 173,
"y": 0 "y": 0
}, },
"width": 112, "width": 159,
"height": 126, "height": 126,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
@ -44,10 +44,10 @@
"id": "1", "id": "1",
"type": "", "type": "",
"pos": { "pos": {
"x": 0, "x": 1,
"y": 226 "y": 0
}, },
"width": 113, "width": 112,
"height": 126, "height": 126,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
@ -83,10 +83,10 @@
"id": "2", "id": "2",
"type": "", "type": "",
"pos": { "pos": {
"x": 173, "x": 0,
"y": 0 "y": 226
}, },
"width": 159, "width": 113,
"height": 126, "height": 126,
"opacity": 1, "opacity": 1,
"strokeDash": 0, "strokeDash": 0,
@ -186,15 +186,15 @@
"route": [ "route": [
{ {
"x": 56.5, "x": 56.5,
"y": 226 "y": 126
}, },
{ {
"x": 56.5, "x": 56.5,
"y": 226 "y": 166
}, },
{ {
"x": 56.5, "x": 56.5,
"y": 226 "y": 186
}, },
{ {
"x": 56.5, "x": 56.5,

View file

@ -14,7 +14,7 @@ width="532" height="552" viewBox="-100 -100 532 552"><style type="text/css">
} }
]]> ]]>
</style><g id="foo baz"><g class="shape" ><rect x="1" y="0" width="112" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="57.000000" y="66.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">Foo Baz</text></g><g id="1"><g class="shape" ><rect x="0" y="226" width="113" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="56.500000" y="292.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">1</text></g><g id="2"><g class="shape" ><rect x="173" y="0" width="159" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="252.500000" y="66.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">2</text></g><g id="hello"><g class="shape" ><rect x="182" y="226" width="140" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="252.000000" y="292.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">hello</text></g><g id="(1 -&gt; 2)[0]"><marker id="mk-3990223579" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon class="connection" fill="#0D32B2" stroke-width="2" points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" /> </marker><path d="M NaN NaN C 56.500000 226.000000 56.500000 226.000000 NaN NaN" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /></g><g id="(foo baz -&gt; hello)[0]"><path d="M 252.000000 128.000000 C 252.000000 166.000000 252.000000 186.000000 252.000000 222.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /></g><style type="text/css"><![CDATA[ </style><g id="foo baz"><g class="shape" ><rect x="173" y="0" width="159" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="252.500000" y="66.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">Foo Baz</text></g><g id="1"><g class="shape" ><rect x="1" y="0" width="112" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="57.000000" y="66.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">1</text></g><g id="2"><g class="shape" ><rect x="0" y="226" width="113" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="56.500000" y="292.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">2</text></g><g id="hello"><g class="shape" ><rect x="182" y="226" width="140" height="126" style="fill:#F7F8FE;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" /></g><text class="text-bold" x="252.000000" y="292.000000" style="text-anchor:middle;font-size:16px;fill:#0A0F25">hello</text></g><g id="(1 -&gt; 2)[0]"><marker id="mk-3990223579" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon class="connection" fill="#0D32B2" stroke-width="2" points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" /> </marker><path d="M 56.500000 128.000000 C 56.500000 166.000000 56.500000 186.000000 56.500000 222.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /></g><g id="(foo baz -&gt; hello)[0]"><path d="M 252.000000 128.000000 C 252.000000 166.000000 252.000000 186.000000 252.000000 222.000000" class="connection" style="fill:none;stroke:#0D32B2;opacity:1.000000;stroke-width:2;" marker-end="url(#mk-3990223579)" /></g><style type="text/css"><![CDATA[
.text-bold { .text-bold {
font-family: "font-bold"; font-family: "font-bold";
} }

Before

Width:  |  Height:  |  Size: 325 KiB

After

Width:  |  Height:  |  Size: 325 KiB