create dagre object mapper to manage ids for dagre to use

This commit is contained in:
Gavin Nishizawa 2023-09-22 12:57:40 -07:00
parent fa297e1e3f
commit 412ad4a6db
No known key found for this signature in database
GPG key ID: AE3B177777CE55CD

View file

@ -8,6 +8,7 @@ import (
"math" "math"
"regexp" "regexp"
"sort" "sort"
"strconv"
"strings" "strings"
"cdr.dev/slog" "cdr.dev/slog"
@ -140,17 +141,13 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
return err return err
} }
mapper := NewObjectMapper()
loadScript := "" loadScript := ""
idToObj := make(map[string]*d2graph.Object)
for _, obj := range g.Objects { for _, obj := range g.Objects {
id := obj.AbsID() mapper.Register(obj)
idToObj[id] = obj loadScript += mapper.generateAddNodeLine(obj, int(obj.Width), int(obj.Height))
width, height := obj.Width, obj.Height
loadScript += generateAddNodeLine(id, int(width), int(height))
if obj.Parent != g.Root { 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 { 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)) log.Debug(ctx, "graph", slog.F("json", dn))
} }
obj := idToObj[dn.ID] obj := mapper.ToObj(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))
@ -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 { func escapeID(id string) string {
// fixes \\ // fixes \\
id = strings.ReplaceAll(id, "\\", `\\`) id = strings.ReplaceAll(id, "\\", `\\`)
@ -426,17 +449,20 @@ func escapeID(id string) string {
return id return id
} }
func generateAddNodeLine(id string, width, height int) string { func (c objectMapper) generateAddNodeLine(obj *d2graph.Object, width, height int) string {
id = escapeID(id) id := c.ToID(obj)
return fmt.Sprintf("g.setNode(`%s`, { id: `%s`, width: %d, height: %d });\n", id, 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 (c objectMapper) generateAddParentLine(child, parent *d2graph.Object) string {
return fmt.Sprintf("g.setParent(`%s`, `%s`);\n", escapeID(childID), escapeID(parentID)) return fmt.Sprintf("g.setParent(`%s`, `%s`);\n", c.ToID(child), c.ToID(parent))
} }
func generateAddEdgeLine(fromID, toID, edgeID string, width, height int) string { 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", escapeID(fromID), escapeID(toID), escapeID(edgeID), width, height) 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 // getLongestEdgeChainHead finds the longest chain in a container and gets its head