-
Notifications
You must be signed in to change notification settings - Fork 171
Expand file tree
/
Copy pathshiftLabel.gvpr
More file actions
197 lines (180 loc) · 7.39 KB
/
Copy pathshiftLabel.gvpr
File metadata and controls
197 lines (180 loc) · 7.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
BEGIN{
double bbMinX, bbMinY, bbMaxX, bbMaxY, lpX, lpY, lpMinX, lpMaxY, newX, newY, centerX;
double fudgeX, fudgeY;
double cloudMinX, cloudMinY, cloudMaxX, cloudMaxY, cloudCenterX;
double clusterMinX, clusterMinY, clusterMaxX, clusterMaxY, clusterCenterX;
double labelX, labelY;
double topMostY, bottomMostY;
int hasLegend;
string fld[int];
node_t n;
hasLegend = 0;
// recursive routine, processing from the top (root) down
graph_t labelShift (graph_t Gr) {
graph_t thisG;
// First recurse into subgraphs
for (thisG = fstsubg(Gr); thisG; thisG=nxtsubg(thisG)) {
thisG = labelShift(thisG);
}
if (match(Gr.name,"cluster")==0 ){
// Check for main cloud provider group using _cloudgroup flag
if (hasAttr(Gr, "_cloudgroup") && Gr._cloudgroup=="1"){
sscanf (Gr.bb, "%lf,%lf,%lf,%lf", &cloudMinX, &cloudMinY, &cloudMaxX, &cloudMaxY);
cloudCenterX = (cloudMinX + cloudMaxX) / 2.0;
}
// Position cluster label nodes within this cluster
sscanf (Gr.bb, "%lf,%lf,%lf,%lf", &clusterMinX, &clusterMinY, &clusterMaxX, &clusterMaxY);
clusterCenterX = (clusterMinX + clusterMaxX) / 2.0;
for (n = fstnode(Gr); n; n = nxtnode_sg(Gr, n)) {
if (hasAttr(n, "_clusterlabel") && n._clusterlabel == "1") {
// Only position label nodes that belong to THIS cluster (not nested subgraphs)
if (hasAttr(n, "_clusterid") && hasAttr(n, "_labelposition")) {
if (n._clusterid == Gr.name) {
// Calculate position based on label_position attribute
// Position inside cluster at bottom with X offset to prevent neato from moving them
if (n._labelposition == "bottom-left") {
// Different X offsets for different cluster types
labelY = clusterMinY + 50.0;
if (hasAttr(n, "_clustertype") && n._clustertype == "SubnetGroup") {
labelX = clusterMinX + 120.0;
} else if (hasAttr(n, "_clustertype") && n._clustertype == "AZUREGroup") {
labelX = clusterMinX + 250.0;
labelY = clusterMinY - 20;
} else {
labelX = clusterMinX + 150.0;
}
} else if (n._labelposition == "bottom-right") {
labelX = clusterMaxX - 120.0;
labelY = clusterMinY + 50.0;
} else if (n._labelposition == "bottom-center") {
labelX = clusterCenterX;
labelY = clusterMinY + 50.0;
}
// Pin the node position
n.pos = sprintf("%.1f,%.1f!", labelX, labelY);
}
}
}
}
// Handle top positioning for AWS clusters (original logic)
if (hasAttr(Gr, "_shift") && Gr._shift!="" && Gr._shift!="0"){
fudgeX=2.; // aim for just inside periphery - increase to shift left
fudgeY=1.; // aim for just inside periphery - increase to shift up
if (index(Gr._shift,",")>0){
split(Gr._shift,fld,",");
fudgeX=(double)fld[0];
fudgeY=(double)fld[1];
}
sscanf (Gr.bb, "%lf,%lf,%lf,%lf", &bbMinX, &bbMinY, &bbMaxX, &bbMaxY);
lpX=xOf(Gr.lp);
lpY=yOf(Gr.lp);
lpMinX=lpX-((Gr.lwidth*72.)/2.);
lpMaxY=lpY+((Gr.lheight*72.)/2.);
//print(" // bb: ", Gr.bb, " lpMinX: ", lpMinX, " lpMaxY: ", lpMaxY);
Gr._oldlp=Gr.lp;
newX=lpX-(lpMinX-bbMinX)-fudgeX;
newY=lpY+(bbMaxY-lpMaxY)+fudgeY;
Gr.lp=sprintf("%.1f,%.1f", newX, newY);
}
}
return Gr;
} // end of labelShift
}
BEG_G{
double nodeY;
topMostY = 0;
bottomMostY = 999999.0;
labelShift($G);
// Center edge nodes (CDN, API GW, etc.) among their targets so ortho edges
// fan out in multiple directions instead of all routing in parallel.
// Only nodes tagged with _edgenode="1" by drawing.py are candidates.
edge_t e;
node_t src, tgt;
int fanoutCount;
double sumX, avgX, srcX, srcY;
int centeredCount;
centeredCount = 0;
for (src = fstnode($G); src; src = nxtnode(src)) {
if (!hasAttr(src, "_edgenode") || src._edgenode != "1") continue;
fanoutCount = 0;
sumX = 0.0;
for (e = fstout(src); e; e = nxtout(e)) {
tgt = e.head;
if (hasAttr(tgt, "_titlenode") && tgt._titlenode == "1") continue;
if (hasAttr(tgt, "_footernode") && tgt._footernode == "1") continue;
if (hasAttr(tgt, "_legendnode") && tgt._legendnode == "1") continue;
if (hasAttr(tgt, "_clusterlabel") && tgt._clusterlabel == "1") continue;
sumX = sumX + xOf(tgt.pos);
fanoutCount = fanoutCount + 1;
}
if (fanoutCount > 3) {
avgX = sumX / (double)fanoutCount;
srcX = xOf(src.pos);
srcY = yOf(src.pos);
// Only center if significantly off-center from targets (>1000pt ~14in)
double xDist = srcX - avgX;
if (xDist < 0) xDist = -xDist;
if (xDist > 1000.0) {
// Clamp avgX within cloud group bounds
if (cloudMaxX > 0) {
if (avgX < cloudMinX + 50.0) avgX = cloudMinX + 50.0;
if (avgX > cloudMaxX - 50.0) avgX = cloudMaxX - 50.0;
}
// Position just inside cloud group top — well above VPC/sub-clusters
src.pos = sprintf("%.1f,%.1f!", avgX, cloudMaxY - 60.0);
// Clear edge positions so neato -n2 re-routes them from the new location
edge_t oe;
for (oe = fstout(src); oe; oe = nxtout(oe)) {
oe.pos = "";
}
for (oe = fstin(src); oe; oe = nxtin(oe)) {
oe.pos = "";
}
centeredCount = centeredCount + 1;
}
}
}
// Find topmost and bottommost node positions (including outer nodes).
// Also note whether a legend node exists so the footer can be centred
// when it has to stand alone.
for (n = fstnode($G); n; n = nxtnode(n)) {
if (hasAttr(n, "_titlenode") && n._titlenode == "1") continue;
if (hasAttr(n, "_footernode") && n._footernode == "1") continue;
if (hasAttr(n, "_legendnode") && n._legendnode == "1") { hasLegend = 1; continue; }
nodeY = yOf(n.pos);
if (nodeY > topMostY) topMostY = nodeY;
if (nodeY < bottomMostY) bottomMostY = nodeY;
}
// Use higher of cloudMaxY or topMostY
if (cloudMaxY > topMostY) topMostY = cloudMaxY;
// Use lower of cloudMinY or bottomMostY
if (cloudMinY < bottomMostY) bottomMostY = cloudMinY;
}
END_G{
double gbbMinX, gbbMinY, gbbMaxX, gbbMaxY, gcenterX, glpY;
// Center root graph label at the END after all processing
sscanf ($G.bb, "%lf,%lf,%lf,%lf", &gbbMinX, &gbbMinY, &gbbMaxX, &gbbMaxY);
gcenterX = (gbbMinX + gbbMaxX) / 2.0;
glpY = yOf($G.lp);
$G.lp = sprintf("%.1f,%.1f", gcenterX, glpY);
}
N[_titlenode=="1"]{
pos=sprintf("%.0f,%.0f!", cloudCenterX, topMostY + 200);
}
// Footer + optional legend sit on the same Y below the cloud.
// If a legend exists, footer shifts left and legend goes right so the
// two occupy a single row. If no legend, footer stays centred.
// Vertical offset of 400 matches the old legend-below-footer position
// so the outer cluster border keeps its usual breathing room.
// Footer width=12in → half-width ~432 pts; ±500 keeps the pair clear
// of each other either side of cloudCenterX.
N[_footernode=="1"]{
if (hasLegend) {
pos=sprintf("%.0f,%.0f!", cloudCenterX - 500, bottomMostY - 400);
} else {
pos=sprintf("%.0f,%.0f!", cloudCenterX, bottomMostY - 180);
}
}
N[_legendnode=="1"]{
pos=sprintf("%.0f,%.0f!", cloudCenterX + 500, bottomMostY - 400);
}