5
5
6
6
"github.com/nulab/autog/graph"
7
7
ig "github.com/nulab/autog/internal/graph"
8
+ "github.com/nulab/autog/internal/graph/connected"
8
9
imonitor "github.com/nulab/autog/internal/monitor"
9
10
"github.com/nulab/autog/internal/processor"
10
11
)
@@ -20,7 +21,7 @@ func Layout(source graph.Source, opts ...Option) graph.Layout {
20
21
imonitor .Set (layoutOpts .monitor )
21
22
defer imonitor .Reset ()
22
23
23
- pipeline := [... ]processor.P {
24
+ pipeline := []processor.P {
24
25
layoutOpts .p1 , // cycle breaking
25
26
layoutOpts .p2 , // layering
26
27
layoutOpts .p3 , // ordering
@@ -29,46 +30,75 @@ func Layout(source graph.Source, opts ...Option) graph.Layout {
29
30
}
30
31
31
32
// populate the graph struct from the graph source
32
- g := from (source )
33
+ G := from (source )
33
34
34
35
if layoutOpts .params .NodeFixedSizeFunc != nil {
35
- for _ , n := range g .Nodes {
36
+ for _ , n := range G .Nodes {
36
37
layoutOpts .params .NodeFixedSizeFunc (n )
37
38
}
38
39
}
39
40
40
- // run it through the pipeline
41
- for _ , phase := range pipeline {
42
- phase .Process (g , layoutOpts .params )
43
- }
44
-
45
41
// return only relevant data to the caller
46
- out := graph.Layout {
47
- Nodes : make ([]graph.Node , 0 , len (g .Nodes )),
48
- Edges : make ([]graph.Edge , 0 , len (g .Edges )),
49
- }
50
- for _ , n := range g .Nodes {
51
- if n .IsVirtual && ! layoutOpts .output .keepVirtualNodes {
52
- continue
42
+ out := graph.Layout {}
43
+
44
+ // shift disconnected sub-graphs to the right
45
+ shift := 0.0
46
+
47
+ // process each connected components and collect results into the same layout output
48
+ for _ , g := range connected .Components (G ) {
49
+ out .Nodes = slices .Grow (out .Nodes , len (g .Nodes ))
50
+ out .Edges = slices .Grow (out .Edges , len (g .Edges ))
51
+
52
+ // run subgraph through the pipeline
53
+ for _ , phase := range pipeline {
54
+ phase .Process (g , layoutOpts .params )
55
+ }
56
+
57
+ // collect nodes
58
+ for _ , n := range g .Nodes {
59
+ if n .IsVirtual && ! layoutOpts .output .keepVirtualNodes {
60
+ continue
61
+ }
62
+
63
+ m := graph.Node {
64
+ ID : n .ID ,
65
+ Size : n .Size ,
66
+ }
67
+ // apply subgraph's left shift
68
+ m .X += shift
69
+
70
+ out .Nodes = append (out .Nodes , m )
71
+ // todo: clients can't reliably tell virtual nodes from concrete nodes
72
+ }
73
+
74
+ // collect edges
75
+ for _ , e := range g .Edges {
76
+ f := graph.Edge {
77
+ FromID : e .From .ID ,
78
+ ToID : e .To .ID ,
79
+ Points : slices .Clone (e .Points ),
80
+ ArrowHeadStart : e .ArrowHeadStart ,
81
+ }
82
+ // apply subgraph's left shift
83
+ for i := range f .Points {
84
+ f .Points [i ][0 ] += shift
85
+ }
86
+
87
+ out .Edges = append (out .Edges , f )
53
88
}
54
- out .Nodes = append (out .Nodes , graph.Node {
55
- ID : n .ID ,
56
- Size : n .Size ,
57
- })
58
- // todo: clients can't reliably tell virtual nodes from concrete nodes
89
+
90
+ // compute shift for subsequent subgraphs
91
+ rightmostX := 0.0
92
+ for _ , l := range g .Layers {
93
+ n := l .Nodes [len (l .Nodes )- 1 ]
94
+ rightmostX = max (rightmostX , n .X + n .W )
95
+ }
96
+ shift += rightmostX + layoutOpts .params .NodeSpacing
59
97
}
98
+
60
99
if ! layoutOpts .output .keepVirtualNodes {
61
100
out .Nodes = slices .Clip (out .Nodes )
62
101
}
63
-
64
- for _ , e := range g .Edges {
65
- out .Edges = append (out .Edges , graph.Edge {
66
- FromID : e .From .ID ,
67
- ToID : e .To .ID ,
68
- Points : e .Points ,
69
- ArrowHeadStart : e .ArrowHeadStart ,
70
- })
71
- }
72
102
return out
73
103
}
74
104
0 commit comments