@@ -30,24 +30,40 @@ func (g graph) lineToDrawing(line []gridCoord) []drawingCoord {
3030}
3131
3232type graph struct {
33- nodes []* node
34- edges []* edge
35- drawing * drawing
36- grid map [gridCoord ]* node
37- columnWidth map [int ]int
38- rowHeight map [int ]int
39- styleClasses map [string ]styleClass
40- styleType string
41- paddingX int
42- paddingY int
43- subgraphs []* subgraph
44- offsetX int
45- offsetY int
46- useAscii bool
33+ nodes []* node
34+ edges []* edge
35+ drawing * drawing
36+ grid map [gridCoord ]* node
37+ edgeCounts map [edgePair ]int
38+ columnWidth map [int ]int
39+ rowHeight map [int ]int
40+ styleClasses map [string ]styleClass
41+ styleType string
42+ boxBorderPadding int
43+ graphDirection string
44+ paddingX int
45+ paddingY int
46+ subgraphs []* subgraph
47+ offsetX int
48+ offsetY int
49+ useAscii bool
50+ }
51+
52+ type edgePair struct {
53+ from int
54+ to int
55+ }
56+
57+ func newEdgePair (from , to int ) edgePair {
58+ if from < to {
59+ return edgePair {from : from , to : to }
60+ }
61+ return edgePair {from : to , to : from }
4762}
4863
4964type subgraph struct {
5065 name string
66+ label graphLabel
5167 nodes []* node
5268 parent * subgraph
5369 children []* subgraph
@@ -58,28 +74,30 @@ type subgraph struct {
5874 maxY int
5975}
6076
61- func mkGraph (data * orderedmap.OrderedMap [string , []textEdge ]) graph {
77+ func mkGraph (data * orderedmap.OrderedMap [string , []textEdge ], nodeSpecs map [ string ] graphNodeSpec ) graph {
6278 g := graph {drawing : mkDrawing (0 , 0 )}
6379 g .grid = make (map [gridCoord ]* node )
80+ g .edgeCounts = make (map [edgePair ]int )
6481 g .columnWidth = make (map [int ]int )
6582 g .rowHeight = make (map [int ]int )
6683 g .styleClasses = make (map [string ]styleClass )
6784 index := 0
6885 for el := data .Front (); el != nil ; el = el .Next () {
6986 nodeName := el .Key
7087 children := el .Value
88+ spec := nodeSpecs [nodeName ]
7189 // Get or create parent node
7290 parentNode , err := g .getNode (nodeName )
7391 if err != nil {
74- parentNode = & node {name : nodeName , index : index , styleClassName : "" }
92+ parentNode = & node {name : nodeName , label : spec . label , index : index , styleClassName : spec . styleClass }
7593 g .appendNode (parentNode )
7694 index += 1
7795 }
7896 for _ , textEdge := range children {
97+ childSpec := nodeSpecs [textEdge .child .name ]
7998 childNode , err := g .getNode (textEdge .child .name )
8099 if err != nil {
81- childNode = & node {name : textEdge .child .name , index : index , styleClassName : textEdge .child .styleClass }
82- parentNode .styleClassName = textEdge .parent .styleClass
100+ childNode = & node {name : textEdge .child .name , label : childSpec .label , index : index , styleClassName : childSpec .styleClass }
83101 g .appendNode (childNode )
84102 index += 1
85103 }
@@ -94,6 +112,8 @@ func (g *graph) setStyleClasses(properties *graphProperties) {
94112 log .Debugf ("Setting style classes to %v" , properties .styleClasses )
95113 g .styleClasses = * properties .styleClasses
96114 g .styleType = properties .styleType
115+ g .boxBorderPadding = properties .boxBorderPadding
116+ g .graphDirection = properties .graphDirection
97117 g .paddingX = properties .paddingX
98118 g .paddingY = properties .paddingY
99119 for _ , n := range g .nodes {
@@ -111,6 +131,7 @@ func (g *graph) setSubgraphs(textSubgraphs []*textSubgraph) {
111131 for _ , tsg := range textSubgraphs {
112132 sg := & subgraph {
113133 name : tsg .name ,
134+ label : tsg .label ,
114135 nodes : []* node {},
115136 children : []* subgraph {},
116137 }
@@ -194,7 +215,7 @@ func (g *graph) createMapping() {
194215
195216 // Separate root nodes by whether they're in subgraphs, but only if we have both types
196217 // AND there are edges in subgraphs (indicating intentional layout structure)
197- shouldSeparate := graphDirection == "LR" && hasExternalRoots && hasSubgraphRootsWithEdges
218+ shouldSeparate := g . graphDirection == "LR" && hasExternalRoots && hasSubgraphRootsWithEdges
198219
199220 externalRootNodes := []* node {}
200221 subgraphRootNodes := []* node {}
@@ -214,7 +235,7 @@ func (g *graph) createMapping() {
214235 // Place external root nodes first at level 0
215236 for _ , n := range externalRootNodes {
216237 var mappingCoord * gridCoord
217- if graphDirection == "LR" {
238+ if g . graphDirection == "LR" {
218239 mappingCoord = g .reserveSpotInGrid (g .nodes [n .index ], & gridCoord {x : 0 , y : highestPositionPerLevel [0 ]})
219240 } else {
220241 mappingCoord = g .reserveSpotInGrid (g .nodes [n .index ], & gridCoord {x : highestPositionPerLevel [0 ], y : 0 })
@@ -230,7 +251,7 @@ func (g *graph) createMapping() {
230251 subgraphLevel := 4
231252 for _ , n := range subgraphRootNodes {
232253 var mappingCoord * gridCoord
233- if graphDirection == "LR" {
254+ if g . graphDirection == "LR" {
234255 mappingCoord = g .reserveSpotInGrid (g .nodes [n .index ], & gridCoord {x : subgraphLevel , y : highestPositionPerLevel [subgraphLevel ]})
235256 } else {
236257 mappingCoord = g .reserveSpotInGrid (g .nodes [n .index ], & gridCoord {x : highestPositionPerLevel [subgraphLevel ], y : subgraphLevel })
@@ -245,7 +266,7 @@ func (g *graph) createMapping() {
245266 log .Debugf ("Creating mapping for node %s at %v" , n .name , n .gridCoord )
246267 var childLevel int
247268 // Next column is 4 coords further. This is because every node is 3 coords wide + 1 coord inbetween.
248- if graphDirection == "LR" {
269+ if g . graphDirection == "LR" {
249270 childLevel = n .gridCoord .x + 4
250271 } else {
251272 childLevel = n .gridCoord .y + 4
@@ -258,7 +279,7 @@ func (g *graph) createMapping() {
258279 }
259280
260281 var mappingCoord * gridCoord
261- if graphDirection == "LR" {
282+ if g . graphDirection == "LR" {
262283 mappingCoord = g .reserveSpotInGrid (g .nodes [child .index ], & gridCoord {x : childLevel , y : highestPosition })
263284 } else {
264285 mappingCoord = g .reserveSpotInGrid (g .nodes [child .index ], & gridCoord {x : highestPosition , y : childLevel })
@@ -471,9 +492,18 @@ func (g *graph) calculateSubgraphBoundingBox(sg *subgraph) {
471492 maxY = Max (maxY , nodeMaxY )
472493 }
473494
495+ // Ensure the title fits inside the frame after padding is applied.
496+ currentWidth := maxX - minX
497+ currentInnerWidth := currentWidth + 3
498+ if currentInnerWidth < sg .label .width {
499+ extraWidth := sg .label .width - currentInnerWidth
500+ minX -= extraWidth / 2
501+ maxX += extraWidth - (extraWidth / 2 )
502+ }
503+
474504 // Add padding (allow negative coordinates, we'll offset later)
475505 const subgraphPadding = 2
476- const subgraphLabelSpace = 2 // Extra space for label at top
506+ subgraphLabelSpace := sg . label . contentHeight () + 1
477507 sg .minX = minX - subgraphPadding
478508 sg .minY = minY - subgraphPadding - subgraphLabelSpace
479509 sg .maxX = maxX + subgraphPadding
0 commit comments