Skip to content

Commit aca2416

Browse files
Merge pull request #71 from William9923/feat/add-bidirectional-support
2 parents 876b5b4 + e026540 commit aca2416

20 files changed

Lines changed: 323 additions & 173 deletions

cmd/arrow.go

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,25 @@ func (g *graph) drawArrow(from gridCoord, to gridCoord, e *edge) (*drawing, *dra
114114
dPath, linesDrawn, lineDirs := g.drawPath(e.path)
115115
dBoxStart := g.drawBoxStart(e.path, linesDrawn[0])
116116
dArrowHead := g.drawArrowHead(linesDrawn[len(linesDrawn)-1], lineDirs[len(lineDirs)-1])
117+
if e.isBidirectional && len(linesDrawn) > 0 {
118+
dStartArrowHead := g.drawArrowHead(reverseDrawingLine(linesDrawn[0]), lineDirs[0].getOpposite())
119+
dArrowHead = g.mergeDrawings(dArrowHead, drawingCoord{0, 0}, dStartArrowHead)
120+
}
117121
dCorners := g.drawCorners(e.path)
118122
return dPath, dBoxStart, dArrowHead, dCorners, dLabel
119123
}
120124

125+
func reverseDrawingLine(line []drawingCoord) []drawingCoord {
126+
if len(line) == 0 {
127+
return line
128+
}
129+
reversed := make([]drawingCoord, len(line))
130+
for i, coord := range line {
131+
reversed[len(line)-1-i] = coord
132+
}
133+
return reversed
134+
}
135+
121136
func mergePath(path []gridCoord) []gridCoord {
122137
// If two steps are in the same direction, merge them to one step.
123138
if len(path) <= 2 {
@@ -321,10 +336,46 @@ func (g *graph) drawArrowLabel(e *edge) *drawing {
321336
}
322337

323338
log.Debugf("Drawing text '%s' on gridline %v", e.text, e.labelLine)
324-
d.drawTextOnLine(g.lineToDrawing(e.labelLine), e.text)
339+
line := g.lineToDrawing(e.labelLine)
340+
if e.isBidirectional {
341+
line = insetLine(line, 2, 2) // reserve for label placement calc: <- (2 char) + {label} + -> (2 char)
342+
} else {
343+
line = insetLine(line, 1, 2) // reserve for label placement calc: - (1 char) + {label} + -> (2 char)
344+
}
345+
d.drawTextOnLine(line, e.text)
325346
return d
326347
}
327348

349+
// insetLine returns a sub-segment with each endpoint moved inward along the line.
350+
func insetLine(line []drawingCoord, insetStart, insetEnd int) []drawingCoord {
351+
if len(line) < 2 || (insetStart == 0 && insetEnd == 0) {
352+
return line
353+
}
354+
endInset := insetEnd
355+
if insetEnd > 0 {
356+
endInset = insetEnd - 1 // shift label right ~1 char so the start dash survives the centered placement
357+
}
358+
dir := determineDirection(genericCoord(line[0]), genericCoord(line[1]))
359+
a, b := line[0], line[1]
360+
switch dir {
361+
case Right:
362+
a.x += insetStart
363+
b.x -= endInset
364+
case Left:
365+
a.x -= insetStart
366+
b.x += endInset
367+
case Down:
368+
a.y += insetStart
369+
b.y -= endInset
370+
case Up:
371+
a.y -= insetStart
372+
b.y += endInset
373+
default:
374+
return line
375+
}
376+
return []drawingCoord{a, b}
377+
}
378+
328379
func (d *drawing) drawTextOnLine(line []drawingCoord, label string) {
329380
// Write text in middle of the line
330381
// 123456789

cmd/graph.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ func mkGraph(data *orderedmap.OrderedMap[string, []textEdge], nodeSpecs map[stri
101101
g.appendNode(childNode)
102102
index += 1
103103
}
104-
e := edge{from: parentNode, to: childNode, text: textEdge.label}
104+
e := edge{
105+
from: parentNode,
106+
to: childNode,
107+
text: textEdge.label,
108+
isBidirectional: textEdge.isBidirectional,
109+
}
105110
g.edges = append(g.edges, &e)
106111
}
107112
}

cmd/mapping_edge.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import (
77
)
88

99
type edge struct {
10-
from *node
11-
to *node
12-
text string
13-
path []gridCoord
14-
labelLine []gridCoord
15-
startDir direction
16-
endDir direction
10+
from *node
11+
to *node
12+
text string
13+
isBidirectional bool
14+
path []gridCoord
15+
labelLine []gridCoord
16+
startDir direction
17+
endDir direction
1718
}
1819

1920
func (g *graph) determinePath(e *edge) {
@@ -152,8 +153,12 @@ func (g *graph) determineLabelLine(e *edge) {
152153
}
153154

154155
middleX := labelMiddleX(largestLine)
155-
log.Debugf("Increasing column width for column %v from size %v to %v", middleX, g.columnWidth[middleX], lenLabel+2)
156-
g.columnWidth[middleX] = Max(g.columnWidth[middleX], lenLabel+2) // Wrap with dashes + arrowhead
156+
labelPadding := 3 // Wrap with -{label}-> (dashes + end arrowhead, 3 char)
157+
if e.isBidirectional {
158+
labelPadding = 4 // Wrap with <-{label}-> (start arrowhead+ dashes + end arrowhead, 4 char)
159+
}
160+
log.Debugf("Increasing column width for column %v from size %v to %v", middleX, g.columnWidth[middleX], lenLabel+labelPadding)
161+
g.columnWidth[middleX] = Max(g.columnWidth[middleX], lenLabel+labelPadding)
157162
log.Debugf("New column sizes: %v", g.columnWidth)
158163
e.labelLine = largestLine
159164
}

cmd/parse.go

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ type graphNodeSpec struct {
3838
}
3939

4040
type textEdge struct {
41-
parent textNode
42-
child textNode
43-
label string
41+
parent textNode
42+
child textNode
43+
label string
44+
isBidirectional bool
4445
}
4546

4647
type textSubgraph struct {
@@ -144,18 +145,22 @@ func parseStyleClass(matchedLine []string) styleClass {
144145
return styleClass{className, styleMap}
145146
}
146147

147-
func setArrowWithLabel(lhs, rhs []textNode, label string, gp *graphProperties) []textNode {
148+
func setArrowWithLabel(lhs, rhs []textNode, label string, isBidirectional bool, gp *graphProperties) []textNode {
148149
log.Debug("Setting arrow from ", lhs, " to ", rhs, " with label ", label)
149150
for _, l := range lhs {
150151
for _, r := range rhs {
151-
setData(l, textEdge{l, r, label}, gp.data, gp.nodeSpecs)
152+
setData(l, textEdge{l, r, label, isBidirectional}, gp.data, gp.nodeSpecs)
152153
}
153154
}
154155
return rhs
155156
}
156157

157158
func setArrow(lhs, rhs []textNode, gp *graphProperties) []textNode {
158-
return setArrowWithLabel(lhs, rhs, "", gp)
159+
return setArrowWithLabel(lhs, rhs, "", false, gp)
160+
}
161+
162+
func setBidirectionalArrow(lhs, rhs []textNode, gp *graphProperties) []textNode {
163+
return setArrowWithLabel(lhs, rhs, "", true, gp)
159164
}
160165

161166
func rememberNode(node textNode, nodeSpecs map[string]graphNodeSpec) {
@@ -213,6 +218,30 @@ func (gp *graphProperties) parseString(line string) ([]textNode, error) {
213218
return []textNode{}, nil
214219
},
215220
},
221+
{
222+
regex: regexp.MustCompile(`(?s)^(.+)\s*<-->\s*\|(.+)\|\s*(.+)$`),
223+
handler: func(match []string) ([]textNode, error) {
224+
if lhs, err = gp.parseString(match[0]); err != nil {
225+
lhs = []textNode{parseNode(match[0])}
226+
}
227+
if rhs, err = gp.parseString(match[2]); err != nil {
228+
rhs = []textNode{parseNode(match[2])}
229+
}
230+
return setArrowWithLabel(lhs, rhs, match[1], true, gp), nil
231+
},
232+
},
233+
{
234+
regex: regexp.MustCompile(`(?s)^(.+)\s*<-->\s*(.+)$`),
235+
handler: func(match []string) ([]textNode, error) {
236+
if lhs, err = gp.parseString(match[0]); err != nil {
237+
lhs = []textNode{parseNode(match[0])}
238+
}
239+
if rhs, err = gp.parseString(match[1]); err != nil {
240+
rhs = []textNode{parseNode(match[1])}
241+
}
242+
return setBidirectionalArrow(lhs, rhs, gp), nil
243+
},
244+
},
216245
{
217246
regex: regexp.MustCompile(`(?s)^(.+)\s*-->\s*\|(.+)\|\s*(.+)$`),
218247
handler: func(match []string) ([]textNode, error) {
@@ -222,7 +251,7 @@ func (gp *graphProperties) parseString(line string) ([]textNode, error) {
222251
if rhs, err = gp.parseString(match[2]); err != nil {
223252
rhs = []textNode{parseNode(match[2])}
224253
}
225-
return setArrowWithLabel(lhs, rhs, match[1], gp), nil
254+
return setArrowWithLabel(lhs, rhs, match[1], false, gp), nil
226255
},
227256
},
228257
{

cmd/testdata/ascii/back_edges_two_labels_td.txt

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,28 @@ B --> C
44
C -->|back1| A
55
C -->|back2| B
66
---
7-
+---+
8-
| |
9-
| A |<--+
10-
| | |
11-
+---+ |
12-
| |
13-
| |
14-
| |
15-
| |
16-
v |
17-
+---+ |
18-
| | |
19-
| B |<back1
20-
| | |
21-
+---+ |
22-
| |
23-
| |
24-
| back2
25-
| |
26-
v |
27-
+---+ |
28-
| | |
29-
| C |---+
30-
| |
31-
+---+
7+
+---+
8+
| |
9+
| A |<---+
10+
| | |
11+
+---+ |
12+
| |
13+
| |
14+
| |
15+
| |
16+
v |
17+
+---+ |
18+
| | |
19+
| B |<-back1
20+
| | |
21+
+---+ |
22+
| |
23+
| |
24+
| back2
25+
| |
26+
v |
27+
+---+ |
28+
| | |
29+
| C |----+
30+
| |
31+
+---+

cmd/testdata/ascii/bidirectional_edge_labels_lr.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ graph LR
22
A -->|workload exits| B
33
B -->|run| A
44
---
5-
+---+ +---+
6-
| | | |
7-
| A |workload-exits->| B |
8-
| | | |
9-
+---+ +---+
10-
^ |
11-
+--------run---------+
5+
+---+ +---+
6+
| | | |
7+
| A |-workload-exits->| B |
8+
| | | |
9+
+---+ +---+
10+
^ |
11+
+---------run---------+

cmd/testdata/ascii/bidirectional_edge_labels_td.txt

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ graph TD
22
A -->|forward| B
33
B -->|back| A
44
---
5-
+---------+
6-
| |
7-
| A |<--+
8-
| | |
9-
+---------+ |
10-
| |
11-
| |
12-
forward back
13-
| |
14-
v |
15-
+---------+ |
16-
| | |
17-
| B |---+
18-
| |
19-
+---------+
5+
+----------+
6+
| |
7+
| A |<--+
8+
| | |
9+
+----------+ |
10+
| |
11+
| |
12+
forward back
13+
| |
14+
v |
15+
+----------+ |
16+
| | |
17+
| B |---+
18+
| |
19+
+----------+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
graph LR
2+
A <-->|sync| B
3+
---
4+
+---+ +---+
5+
| | | |
6+
| A |<-sync->| B |
7+
| | | |
8+
+---+ +---+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
graph LR
2+
A <--> B
3+
---
4+
+---+ +---+
5+
| | | |
6+
| A |<--->| B |
7+
| | | |
8+
+---+ +---+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
graph TD
2+
A <--> B
3+
---
4+
+---+
5+
| |
6+
| A |
7+
| |
8+
+---+
9+
^
10+
|
11+
|
12+
|
13+
v
14+
+---+
15+
| |
16+
| B |
17+
| |
18+
+---+

0 commit comments

Comments
 (0)