| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
|
|
2
|
|
|
|
|
|
|
package Graph::Easy::Introspect ; |
|
3
|
|
|
|
|
|
|
|
|
4
|
4
|
|
|
4
|
|
1233126
|
use strict ; |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
179
|
|
|
5
|
4
|
|
|
4
|
|
25
|
use warnings ; |
|
|
4
|
|
|
|
|
11
|
|
|
|
4
|
|
|
|
|
479
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = '0.01' ; |
|
8
|
|
|
|
|
|
|
|
|
9
|
4
|
|
|
|
|
566
|
use Graph::Easy::Edge::Cell qw( |
|
10
|
|
|
|
|
|
|
EDGE_HOR EDGE_VER EDGE_CROSS EDGE_HOLE |
|
11
|
|
|
|
|
|
|
EDGE_N_E EDGE_N_W EDGE_S_E EDGE_S_W |
|
12
|
|
|
|
|
|
|
EDGE_START_E EDGE_START_W EDGE_START_N EDGE_START_S |
|
13
|
|
|
|
|
|
|
EDGE_END_E EDGE_END_W EDGE_END_N EDGE_END_S |
|
14
|
4
|
|
|
4
|
|
36
|
) ; |
|
|
4
|
|
|
|
|
8
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
4
|
|
|
4
|
|
31
|
use constant EDGE_TYPE_MASK => 0x000F ; |
|
|
4
|
|
|
|
|
14
|
|
|
|
4
|
|
|
|
|
433
|
|
|
17
|
4
|
|
|
4
|
|
48
|
use constant EDGE_END_MASK => 0x00F0 ; |
|
|
4
|
|
|
|
|
9
|
|
|
|
4
|
|
|
|
|
248
|
|
|
18
|
4
|
|
|
4
|
|
24
|
use constant EDGE_START_MASK => 0x0F00 ; |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
290
|
|
|
19
|
4
|
|
|
4
|
|
26
|
use constant EDGE_LABEL_CELL => 0x1000 ; |
|
|
4
|
|
|
|
|
9
|
|
|
|
4
|
|
|
|
|
803
|
|
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my %CELL_TYPE_NAME = |
|
22
|
|
|
|
|
|
|
( |
|
23
|
|
|
|
|
|
|
EDGE_CROSS() => 'CROSS', |
|
24
|
|
|
|
|
|
|
EDGE_HOR() => 'HOR', |
|
25
|
|
|
|
|
|
|
EDGE_VER() => 'VER', |
|
26
|
|
|
|
|
|
|
EDGE_N_E() => 'N_E', |
|
27
|
|
|
|
|
|
|
EDGE_N_W() => 'N_W', |
|
28
|
|
|
|
|
|
|
EDGE_S_E() => 'S_E', |
|
29
|
|
|
|
|
|
|
EDGE_S_W() => 'S_W', |
|
30
|
|
|
|
|
|
|
EDGE_HOLE() => 'HOLE', |
|
31
|
|
|
|
|
|
|
7 => 'S_E_W', |
|
32
|
|
|
|
|
|
|
8 => 'N_E_W', |
|
33
|
|
|
|
|
|
|
9 => 'E_N_S', |
|
34
|
|
|
|
|
|
|
10 => 'W_N_S', |
|
35
|
|
|
|
|
|
|
12 => 'N_W_S', |
|
36
|
|
|
|
|
|
|
13 => 'S_W_N', |
|
37
|
|
|
|
|
|
|
14 => 'E_S_W', |
|
38
|
|
|
|
|
|
|
15 => 'W_S_E', |
|
39
|
|
|
|
|
|
|
) ; |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
our $WRAPPER_INSTALLED = 0 ; |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub install_layout_wrapper |
|
46
|
|
|
|
|
|
|
{ |
|
47
|
31
|
100
|
|
31
|
0
|
157
|
return if $WRAPPER_INSTALLED ; |
|
48
|
|
|
|
|
|
|
|
|
49
|
4
|
|
|
|
|
2711
|
require Graph::Easy::Layout::Grid ; |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
{ |
|
52
|
4
|
|
|
4
|
|
46
|
no warnings 'redefine' ; |
|
|
4
|
|
|
|
|
9
|
|
|
|
4
|
|
|
|
|
9187
|
|
|
|
4
|
|
|
|
|
6779
|
|
|
53
|
|
|
|
|
|
|
|
|
54
|
4
|
|
|
|
|
13
|
my $orig = \&Graph::Easy::_prepare_layout ; |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
*Graph::Easy::_prepare_layout = sub |
|
57
|
|
|
|
|
|
|
{ |
|
58
|
31
|
|
|
31
|
|
236364
|
my ($self, $format) = @_ ; |
|
59
|
|
|
|
|
|
|
|
|
60
|
31
|
|
|
|
|
179
|
my ($rows, $cols, $max_x, $max_y) = $orig->($self, $format) ; |
|
61
|
|
|
|
|
|
|
|
|
62
|
31
|
50
|
50
|
|
|
92260
|
if (($format // '') eq 'ascii' && !$self->{_introspect_captured}) |
|
|
|
|
33
|
|
|
|
|
|
63
|
|
|
|
|
|
|
{ |
|
64
|
31
|
|
50
|
|
|
103
|
my $align = eval { $self->attribute('align') } // 'left' ; |
|
|
31
|
|
|
|
|
117
|
|
|
65
|
31
|
|
|
|
|
2877
|
my ($label) = eval { $self->_aligned_label($align) } ; |
|
|
31
|
|
|
|
|
132
|
|
|
66
|
31
|
|
50
|
|
|
3287
|
$label //= [] ; |
|
67
|
31
|
|
50
|
|
|
91
|
my $label_pos = eval { $self->attribute('graph', 'label-pos') } // 'top' ; |
|
|
31
|
|
|
|
|
89
|
|
|
68
|
|
|
|
|
|
|
|
|
69
|
31
|
|
|
|
|
2196
|
my $y_start = 0 ; |
|
70
|
31
|
|
|
|
|
61
|
my $x_start = 0 ; |
|
71
|
|
|
|
|
|
|
|
|
72
|
31
|
50
|
|
|
|
127
|
if (@$label > 0) |
|
73
|
|
|
|
|
|
|
{ |
|
74
|
0
|
|
|
|
|
0
|
unshift @$label, '' ; |
|
75
|
0
|
|
|
|
|
0
|
push @$label, '' ; |
|
76
|
|
|
|
|
|
|
|
|
77
|
0
|
0
|
|
|
|
0
|
$y_start = scalar @$label if $label_pos eq 'top' ; |
|
78
|
|
|
|
|
|
|
|
|
79
|
0
|
|
|
|
|
0
|
my $old_max_x = $max_x ; |
|
80
|
|
|
|
|
|
|
|
|
81
|
0
|
|
|
|
|
0
|
for my $l (@$label) |
|
82
|
|
|
|
|
|
|
{ |
|
83
|
0
|
0
|
|
|
|
0
|
$max_x = length($l) + 2 if length($l) > $max_x + 2 ; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
|
|
86
|
0
|
|
|
|
|
0
|
$x_start = int(($max_x - $old_max_x) / 2) ; |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
31
|
|
|
|
|
62
|
my %char_pos ; |
|
90
|
|
|
|
|
|
|
|
|
91
|
31
|
|
|
|
|
56
|
for my $c (values %{$self->{cells}}) |
|
|
31
|
|
|
|
|
154
|
|
|
92
|
|
|
|
|
|
|
{ |
|
93
|
|
|
|
|
|
|
$char_pos{"$c->{x},$c->{y}"} = |
|
94
|
|
|
|
|
|
|
{ |
|
95
|
|
|
|
|
|
|
char_x => ($cols->{$c->{x}} // 0) + $x_start, |
|
96
|
|
|
|
|
|
|
char_y => ($rows->{$c->{y}} // 0) + $y_start, |
|
97
|
|
|
|
|
|
|
render_w => $c->{w} // 0, |
|
98
|
186
|
50
|
50
|
|
|
2801
|
render_h => $c->{h} // 0, |
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
99
|
|
|
|
|
|
|
ref => ref($c), |
|
100
|
|
|
|
|
|
|
name => ($c->can('name') ? ($c->name // '') : ''), |
|
101
|
|
|
|
|
|
|
} ; |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
31
|
|
|
|
|
369
|
$self->{_introspect_char_pos} = \%char_pos ; |
|
105
|
31
|
|
|
|
|
118
|
$self->{_introspect_captured} = 1 ; |
|
106
|
|
|
|
|
|
|
} |
|
107
|
|
|
|
|
|
|
|
|
108
|
31
|
|
|
|
|
173
|
return ($rows, $cols, $max_x, $max_y) ; |
|
109
|
4
|
|
|
|
|
59
|
} ; |
|
110
|
|
|
|
|
|
|
} |
|
111
|
|
|
|
|
|
|
|
|
112
|
4
|
|
|
|
|
11
|
$WRAPPER_INSTALLED = 1 ; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub node_bbox |
|
118
|
|
|
|
|
|
|
{ |
|
119
|
145
|
|
|
145
|
0
|
255
|
my ($node) = @_ ; |
|
120
|
|
|
|
|
|
|
|
|
121
|
145
|
|
50
|
|
|
315
|
my $x = $node->x // 0 ; |
|
122
|
145
|
|
50
|
|
|
854
|
my $y = $node->y // 0 ; |
|
123
|
145
|
|
50
|
|
|
857
|
my $cx = $node->{cx} // 1 ; |
|
124
|
145
|
|
50
|
|
|
329
|
my $cy = $node->{cy} // 1 ; |
|
125
|
|
|
|
|
|
|
|
|
126
|
145
|
|
|
|
|
531
|
return ($x, $y, $x + $cx - 1, $y + $cy - 1) ; |
|
127
|
|
|
|
|
|
|
} |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub port_side |
|
132
|
|
|
|
|
|
|
{ |
|
133
|
70
|
|
|
70
|
0
|
137
|
my ($node, $port) = @_ ; |
|
134
|
|
|
|
|
|
|
|
|
135
|
70
|
100
|
|
|
|
158
|
return undef unless $port ; |
|
136
|
|
|
|
|
|
|
|
|
137
|
68
|
|
|
|
|
143
|
my ($x1, $y1, $x2, $y2) = node_bbox($node) ; |
|
138
|
68
|
|
|
|
|
146
|
my ($px, $py) = @{$port}{qw/x y/} ; |
|
|
68
|
|
|
|
|
149
|
|
|
139
|
|
|
|
|
|
|
|
|
140
|
68
|
50
|
66
|
|
|
274
|
return 'right' if $px > $x2 && $py >= $y1 && $py <= $y2 ; |
|
|
|
|
66
|
|
|
|
|
|
141
|
50
|
50
|
66
|
|
|
282
|
return 'left' if $px < $x1 && $py >= $y1 && $py <= $y2 ; |
|
|
|
|
66
|
|
|
|
|
|
142
|
34
|
50
|
66
|
|
|
173
|
return 'bottom' if $py > $y2 && $px >= $x1 && $px <= $x2 ; |
|
|
|
|
66
|
|
|
|
|
|
143
|
17
|
50
|
33
|
|
|
107
|
return 'top' if $py < $y1 && $px >= $x1 && $px <= $x2 ; |
|
|
|
|
33
|
|
|
|
|
|
144
|
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
0
|
return 'unknown' ; |
|
146
|
|
|
|
|
|
|
} |
|
147
|
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub cell_type_name |
|
151
|
|
|
|
|
|
|
{ |
|
152
|
37
|
|
|
37
|
0
|
79
|
my ($type_code) = @_ ; |
|
153
|
|
|
|
|
|
|
|
|
154
|
37
|
|
33
|
|
|
578
|
return $CELL_TYPE_NAME{$type_code} // "UNKNOWN_$type_code" ; |
|
155
|
|
|
|
|
|
|
} |
|
156
|
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub extract_attrs |
|
160
|
|
|
|
|
|
|
{ |
|
161
|
110
|
|
|
110
|
0
|
1518
|
my ($obj) = @_ ; |
|
162
|
|
|
|
|
|
|
|
|
163
|
110
|
|
|
|
|
213
|
my $att = $obj->{att} ; |
|
164
|
110
|
50
|
33
|
|
|
587
|
return {} unless defined $att && ref $att eq 'HASH' ; |
|
165
|
|
|
|
|
|
|
|
|
166
|
110
|
|
|
|
|
199
|
my %attrs ; |
|
167
|
|
|
|
|
|
|
|
|
168
|
110
|
|
|
|
|
370
|
for my $key (sort keys %$att) |
|
169
|
|
|
|
|
|
|
{ |
|
170
|
54
|
|
|
|
|
124
|
my $val = $att->{$key} ; |
|
171
|
54
|
50
|
|
|
|
136
|
next unless defined $val ; |
|
172
|
54
|
50
|
|
|
|
113
|
next if ref $val ; |
|
173
|
54
|
100
|
|
|
|
129
|
next if $val eq '' ; |
|
174
|
35
|
|
|
|
|
102
|
$attrs{$key} = $val ; |
|
175
|
|
|
|
|
|
|
} |
|
176
|
|
|
|
|
|
|
|
|
177
|
110
|
|
|
|
|
1473
|
return \%attrs ; |
|
178
|
|
|
|
|
|
|
} |
|
179
|
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
181
|
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub extract_graph_attrs |
|
183
|
|
|
|
|
|
|
{ |
|
184
|
31
|
|
|
31
|
0
|
148
|
my ($g) = @_ ; |
|
185
|
|
|
|
|
|
|
|
|
186
|
31
|
|
|
|
|
68
|
my $att = $g->{att} ; |
|
187
|
31
|
50
|
33
|
|
|
212
|
return {} unless defined $att && ref $att eq 'HASH' ; |
|
188
|
|
|
|
|
|
|
|
|
189
|
31
|
|
|
|
|
100
|
my $graph_att = $att->{graph} ; |
|
190
|
31
|
50
|
33
|
|
|
199
|
return {} unless defined $graph_att && ref $graph_att eq 'HASH' ; |
|
191
|
|
|
|
|
|
|
|
|
192
|
31
|
|
|
|
|
57
|
my %attrs ; |
|
193
|
|
|
|
|
|
|
|
|
194
|
31
|
|
|
|
|
129
|
for my $key (sort keys %$graph_att) |
|
195
|
|
|
|
|
|
|
{ |
|
196
|
53
|
|
|
|
|
95
|
my $val = $graph_att->{$key} ; |
|
197
|
53
|
50
|
|
|
|
107
|
next unless defined $val ; |
|
198
|
53
|
50
|
|
|
|
99
|
next if ref $val ; |
|
199
|
53
|
50
|
|
|
|
109
|
next if $val eq '' ; |
|
200
|
53
|
|
|
|
|
125
|
$attrs{$key} = $val ; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
31
|
|
|
|
|
115
|
return \%attrs ; |
|
204
|
|
|
|
|
|
|
} |
|
205
|
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
207
|
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
sub compute_components |
|
209
|
|
|
|
|
|
|
{ |
|
210
|
31
|
|
|
31
|
0
|
78
|
my ($g) = @_ ; |
|
211
|
|
|
|
|
|
|
|
|
212
|
31
|
|
|
|
|
53
|
my %adj ; |
|
213
|
|
|
|
|
|
|
|
|
214
|
31
|
|
|
|
|
87
|
for my $e ($g->edges) |
|
215
|
|
|
|
|
|
|
{ |
|
216
|
35
|
|
|
|
|
472
|
my $f = $e->from->name ; |
|
217
|
35
|
|
|
|
|
394
|
my $t = $e->to->name ; |
|
218
|
|
|
|
|
|
|
|
|
219
|
35
|
|
|
|
|
312
|
push @{$adj{$f}}, $t ; |
|
|
35
|
|
|
|
|
119
|
|
|
220
|
35
|
100
|
|
|
|
117
|
push @{$adj{$t}}, $f unless $f eq $t ; |
|
|
34
|
|
|
|
|
112
|
|
|
221
|
|
|
|
|
|
|
} |
|
222
|
|
|
|
|
|
|
|
|
223
|
31
|
|
|
|
|
86
|
my %component ; |
|
224
|
31
|
|
|
|
|
92
|
my $comp_id = 0 ; |
|
225
|
|
|
|
|
|
|
|
|
226
|
31
|
|
|
|
|
96
|
for my $node (sort { $a->name cmp $b->name } $g->nodes) |
|
|
45
|
|
|
|
|
596
|
|
|
227
|
|
|
|
|
|
|
{ |
|
228
|
69
|
|
|
|
|
370
|
my $name = $node->name ; |
|
229
|
69
|
100
|
|
|
|
407
|
next if exists $component{$name} ; |
|
230
|
|
|
|
|
|
|
|
|
231
|
36
|
|
|
|
|
93
|
my @queue = ($name) ; |
|
232
|
|
|
|
|
|
|
|
|
233
|
36
|
|
|
|
|
97
|
while (@queue) |
|
234
|
|
|
|
|
|
|
{ |
|
235
|
70
|
|
|
|
|
157
|
my $n = shift @queue ; |
|
236
|
70
|
100
|
|
|
|
160
|
next if exists $component{$n} ; |
|
237
|
|
|
|
|
|
|
|
|
238
|
69
|
|
|
|
|
157
|
$component{$n} = $comp_id ; |
|
239
|
|
|
|
|
|
|
|
|
240
|
69
|
100
|
|
|
|
112
|
push @queue, grep { !exists $component{$_} } @{$adj{$n} || []} ; |
|
|
69
|
|
|
|
|
248
|
|
|
|
69
|
|
|
|
|
234
|
|
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
36
|
|
|
|
|
85
|
$comp_id++ ; |
|
244
|
|
|
|
|
|
|
} |
|
245
|
|
|
|
|
|
|
|
|
246
|
31
|
|
|
|
|
213
|
return %component ; |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
250
|
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
sub sorted_nodes |
|
252
|
|
|
|
|
|
|
{ |
|
253
|
31
|
|
|
31
|
0
|
121
|
my ($g) = @_ ; |
|
254
|
|
|
|
|
|
|
|
|
255
|
31
|
|
|
|
|
195
|
return sort { $a->name cmp $b->name } $g->nodes ; |
|
|
45
|
|
|
|
|
863
|
|
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub sorted_edges |
|
261
|
|
|
|
|
|
|
{ |
|
262
|
31
|
|
|
31
|
0
|
80
|
my ($g) = @_ ; |
|
263
|
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
return sort |
|
265
|
|
|
|
|
|
|
{ |
|
266
|
31
|
50
|
|
|
|
121
|
$a->from->name cmp $b->from->name || |
|
|
7
|
|
|
|
|
163
|
|
|
267
|
|
|
|
|
|
|
$a->to->name cmp $b->to->name |
|
268
|
|
|
|
|
|
|
} $g->edges ; |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
272
|
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub group_char_bbox |
|
274
|
|
|
|
|
|
|
{ |
|
275
|
6
|
|
|
6
|
0
|
19
|
my ($g, $g_ast, $char_pos) = @_ ; |
|
276
|
|
|
|
|
|
|
|
|
277
|
6
|
|
|
|
|
19
|
my $bx1 = $g_ast->{bbox}{x1} ; |
|
278
|
6
|
|
|
|
|
15
|
my $by1 = $g_ast->{bbox}{y1} ; |
|
279
|
6
|
|
|
|
|
16
|
my $bx2 = $g_ast->{bbox}{x2} ; |
|
280
|
6
|
|
|
|
|
47
|
my $by2 = $g_ast->{bbox}{y2} ; |
|
281
|
|
|
|
|
|
|
|
|
282
|
6
|
|
|
|
|
23
|
my $tl_key = ($bx1 - 1) . ',' . ($by1 - 1) ; |
|
283
|
6
|
|
|
|
|
19
|
my $br_key = ($bx2 + 1) . ',' . ($by2 + 1) ; |
|
284
|
|
|
|
|
|
|
|
|
285
|
6
|
|
|
|
|
15
|
my $tl = $char_pos->{$tl_key} ; |
|
286
|
6
|
|
|
|
|
14
|
my $br = $char_pos->{$br_key} ; |
|
287
|
|
|
|
|
|
|
|
|
288
|
6
|
50
|
33
|
|
|
30
|
return undef unless $tl && $br ; |
|
289
|
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
return |
|
291
|
|
|
|
|
|
|
{ |
|
292
|
|
|
|
|
|
|
x => $tl->{char_x}, |
|
293
|
|
|
|
|
|
|
y => $tl->{char_y}, |
|
294
|
|
|
|
|
|
|
x2 => $br->{char_x} + $br->{render_w} - 1, |
|
295
|
|
|
|
|
|
|
y2 => $br->{char_y} + $br->{render_h} - 1, |
|
296
|
|
|
|
|
|
|
w => ($br->{char_x} + $br->{render_w} - 1) - $tl->{char_x} + 1, |
|
297
|
6
|
|
|
|
|
70
|
h => ($br->{char_y} + $br->{render_h} - 1) - $tl->{char_y} + 1, |
|
298
|
|
|
|
|
|
|
} ; |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
302
|
|
|
|
|
|
|
# Compute the char-space face coordinates for an edge port attachment point. |
|
303
|
|
|
|
|
|
|
# Returns the point ON the node face (border character row/col) at which the |
|
304
|
|
|
|
|
|
|
# edge enters or leaves the node. This is distinct from the edge cell's |
|
305
|
|
|
|
|
|
|
# top-left corner, which for short single-cell edges is the same cell for |
|
306
|
|
|
|
|
|
|
# both from_port and to_port. |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
sub face_char |
|
309
|
|
|
|
|
|
|
{ |
|
310
|
136
|
|
|
136
|
0
|
244
|
my ($node_ast, $side) = @_ ; |
|
311
|
|
|
|
|
|
|
|
|
312
|
136
|
|
|
|
|
222
|
my $nx = $node_ast->{char_x} ; |
|
313
|
136
|
|
|
|
|
209
|
my $ny = $node_ast->{char_y} ; |
|
314
|
136
|
|
|
|
|
205
|
my $nw = $node_ast->{char_width} ; |
|
315
|
136
|
|
|
|
|
206
|
my $nh = $node_ast->{char_height} ; |
|
316
|
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
# Use intrinsic width/height for the center calculation. |
|
318
|
|
|
|
|
|
|
# Group membership expands char_width but the edge path uses the intrinsic |
|
319
|
|
|
|
|
|
|
# node center, so int(intrinsic/2) gives the correct column/row offset. |
|
320
|
136
|
|
33
|
|
|
288
|
my $iw = $node_ast->{width} || $nw ; |
|
321
|
136
|
|
33
|
|
|
277
|
my $ih = $node_ast->{height} || $nh ; |
|
322
|
|
|
|
|
|
|
|
|
323
|
136
|
100
|
|
|
|
344
|
return ($nx + $nw, $ny + int($ih / 2)) if $side eq 'right' ; |
|
324
|
100
|
100
|
|
|
|
263
|
return ($nx - 1, $ny + int($ih / 2)) if $side eq 'left' ; |
|
325
|
68
|
100
|
|
|
|
200
|
return ($nx + int($iw / 2), $ny + $nh) if $side eq 'bottom' ; |
|
326
|
34
|
50
|
|
|
|
124
|
return ($nx + int($iw / 2), $ny - 1) if $side eq 'top' ; |
|
327
|
|
|
|
|
|
|
|
|
328
|
0
|
|
|
|
|
0
|
return ($nx, $ny) ; |
|
329
|
|
|
|
|
|
|
} |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
package Graph::Easy::Edge ; |
|
334
|
|
|
|
|
|
|
|
|
335
|
4
|
|
|
4
|
|
42
|
use strict ; |
|
|
4
|
|
|
|
|
9
|
|
|
|
4
|
|
|
|
|
207
|
|
|
336
|
4
|
|
|
4
|
|
24
|
use warnings ; |
|
|
4
|
|
|
|
|
8
|
|
|
|
4
|
|
|
|
|
4171
|
|
|
337
|
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
sub path |
|
339
|
|
|
|
|
|
|
{ |
|
340
|
0
|
|
|
0
|
0
|
0
|
my ($self) = @_ ; |
|
341
|
|
|
|
|
|
|
|
|
342
|
0
|
0
|
|
|
|
0
|
return [ map { [$_->{x}, $_->{y}] } @{$self->{cells} || []} ] ; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
346
|
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
sub from_port |
|
348
|
|
|
|
|
|
|
{ |
|
349
|
35
|
|
|
35
|
0
|
78
|
my ($self) = @_ ; |
|
350
|
|
|
|
|
|
|
|
|
351
|
35
|
100
|
|
|
|
87
|
return undef if $self->from->name eq $self->to->name ; |
|
352
|
|
|
|
|
|
|
|
|
353
|
34
|
|
|
|
|
561
|
my $from = $self->from ; |
|
354
|
34
|
|
|
|
|
198
|
my ($x1, $y1, $x2, $y2) = Graph::Easy::Introspect::node_bbox($from) ; |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
my @adjacent = grep |
|
357
|
|
|
|
|
|
|
{ |
|
358
|
36
|
|
|
|
|
108
|
my ($cx, $cy) = ($_->{x}, $_->{y}) ; |
|
359
|
36
|
100
|
33
|
|
|
464
|
($cx > $x2 && $cy >= $y1 && $cy <= $y2) || |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
360
|
|
|
|
|
|
|
($cx < $x1 && $cy >= $y1 && $cy <= $y2) || |
|
361
|
|
|
|
|
|
|
($cy > $y2 && $cx >= $x1 && $cx <= $x2) || |
|
362
|
|
|
|
|
|
|
($cy < $y1 && $cx >= $x1 && $cx <= $x2) |
|
363
|
34
|
50
|
|
|
|
73
|
} @{$self->{cells} || []} ; |
|
|
34
|
|
|
|
|
164
|
|
|
364
|
|
|
|
|
|
|
|
|
365
|
34
|
50
|
|
|
|
92
|
return undef unless @adjacent ; |
|
366
|
|
|
|
|
|
|
|
|
367
|
34
|
|
|
|
|
94
|
my $ncx = ($x1 + $x2) / 2 ; |
|
368
|
34
|
|
|
|
|
75
|
my $ncy = ($y1 + $y2) / 2 ; |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
my ($best) = sort |
|
371
|
|
|
|
|
|
|
{ |
|
372
|
34
|
|
|
|
|
87
|
abs($a->{x} - $ncx) + abs($a->{y} - $ncy) |
|
373
|
|
|
|
|
|
|
<=> |
|
374
|
0
|
|
|
|
|
0
|
abs($b->{x} - $ncx) + abs($b->{y} - $ncy) |
|
375
|
|
|
|
|
|
|
} @adjacent ; |
|
376
|
|
|
|
|
|
|
|
|
377
|
34
|
|
|
|
|
180
|
return { x => $best->{x}, y => $best->{y} } ; |
|
378
|
|
|
|
|
|
|
} |
|
379
|
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
381
|
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub to_port |
|
383
|
|
|
|
|
|
|
{ |
|
384
|
35
|
|
|
35
|
0
|
74
|
my ($self) = @_ ; |
|
385
|
|
|
|
|
|
|
|
|
386
|
35
|
100
|
|
|
|
109
|
return undef if $self->from->name eq $self->to->name ; |
|
387
|
|
|
|
|
|
|
|
|
388
|
34
|
|
|
|
|
526
|
my $to = $self->to ; |
|
389
|
34
|
|
|
|
|
185
|
my ($x1, $y1, $x2, $y2) = Graph::Easy::Introspect::node_bbox($to) ; |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
my @adjacent = grep |
|
392
|
|
|
|
|
|
|
{ |
|
393
|
36
|
|
|
|
|
91
|
my ($cx, $cy) = ($_->{x}, $_->{y}) ; |
|
394
|
36
|
50
|
33
|
|
|
503
|
($cx > $x2 && $cy >= $y1 && $cy <= $y2) || |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
395
|
|
|
|
|
|
|
($cx < $x1 && $cy >= $y1 && $cy <= $y2) || |
|
396
|
|
|
|
|
|
|
($cy > $y2 && $cx >= $x1 && $cx <= $x2) || |
|
397
|
|
|
|
|
|
|
($cy < $y1 && $cx >= $x1 && $cx <= $x2) |
|
398
|
34
|
50
|
|
|
|
69
|
} @{$self->{cells} || []} ; |
|
|
34
|
|
|
|
|
163
|
|
|
399
|
|
|
|
|
|
|
|
|
400
|
34
|
50
|
|
|
|
127
|
return undef unless @adjacent ; |
|
401
|
|
|
|
|
|
|
|
|
402
|
34
|
|
|
|
|
72
|
my $ncx = ($x1 + $x2) / 2 ; |
|
403
|
34
|
|
|
|
|
74
|
my $ncy = ($y1 + $y2) / 2 ; |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
my ($best) = sort |
|
406
|
|
|
|
|
|
|
{ |
|
407
|
34
|
|
|
|
|
70
|
abs($a->{x} - $ncx) + abs($a->{y} - $ncy) |
|
408
|
|
|
|
|
|
|
<=> |
|
409
|
0
|
|
|
|
|
0
|
abs($b->{x} - $ncx) + abs($b->{y} - $ncy) |
|
410
|
|
|
|
|
|
|
} @adjacent ; |
|
411
|
|
|
|
|
|
|
|
|
412
|
34
|
|
|
|
|
154
|
return { x => $best->{x}, y => $best->{y} } ; |
|
413
|
|
|
|
|
|
|
} |
|
414
|
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
416
|
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
sub arrow_dir |
|
418
|
|
|
|
|
|
|
{ |
|
419
|
35
|
|
|
35
|
0
|
73
|
my ($self) = @_ ; |
|
420
|
|
|
|
|
|
|
|
|
421
|
35
|
50
|
|
|
|
56
|
for my $c (@{$self->{cells} || []}) |
|
|
35
|
|
|
|
|
152
|
|
|
422
|
|
|
|
|
|
|
{ |
|
423
|
37
|
|
50
|
|
|
124
|
my $end = ($c->{type} // 0) & Graph::Easy::Introspect::EDGE_END_MASK ; |
|
424
|
|
|
|
|
|
|
|
|
425
|
37
|
100
|
|
|
|
133
|
return 'right' if $end == Graph::Easy::Introspect::EDGE_END_E ; |
|
426
|
22
|
100
|
|
|
|
74
|
return 'left' if $end == Graph::Easy::Introspect::EDGE_END_W ; |
|
427
|
19
|
100
|
|
|
|
65
|
return 'down' if $end == Graph::Easy::Introspect::EDGE_END_S ; |
|
428
|
5
|
50
|
|
|
|
17
|
return 'up' if $end == Graph::Easy::Introspect::EDGE_END_N ; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
|
|
|
|
|
|
|
|
431
|
3
|
|
|
|
|
10
|
return undef ; |
|
432
|
|
|
|
|
|
|
} |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
package Graph::Easy ; |
|
437
|
|
|
|
|
|
|
|
|
438
|
4
|
|
|
4
|
|
35
|
use strict ; |
|
|
4
|
|
|
|
|
37
|
|
|
|
4
|
|
|
|
|
206
|
|
|
439
|
4
|
|
|
4
|
|
26
|
use warnings ; |
|
|
4
|
|
|
|
|
11
|
|
|
|
4
|
|
|
|
|
23524
|
|
|
440
|
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
sub ast |
|
442
|
|
|
|
|
|
|
{ |
|
443
|
31
|
|
|
31
|
0
|
973484
|
my ($self) = @_ ; |
|
444
|
|
|
|
|
|
|
|
|
445
|
31
|
|
|
|
|
153
|
Graph::Easy::Introspect::install_layout_wrapper() ; |
|
446
|
|
|
|
|
|
|
|
|
447
|
31
|
|
|
|
|
144
|
delete $self->{_introspect_captured} ; |
|
448
|
31
|
|
|
|
|
80
|
delete $self->{_introspect_char_pos} ; |
|
449
|
31
|
|
|
|
|
65
|
delete $self->{_introspect_grid} ; |
|
450
|
31
|
|
|
|
|
67
|
delete $self->{_introspect_cell_grid} ; |
|
451
|
|
|
|
|
|
|
|
|
452
|
31
|
|
|
|
|
61
|
my $ascii ; |
|
453
|
|
|
|
|
|
|
|
|
454
|
31
|
|
|
|
|
62
|
eval { $ascii = $self->as_ascii } ; |
|
|
31
|
|
|
|
|
148
|
|
|
455
|
|
|
|
|
|
|
|
|
456
|
31
|
50
|
|
|
|
129894
|
if ($@) |
|
457
|
|
|
|
|
|
|
{ |
|
458
|
|
|
|
|
|
|
return |
|
459
|
|
|
|
|
|
|
{ |
|
460
|
0
|
|
|
|
|
0
|
error => "$@", |
|
461
|
|
|
|
|
|
|
meta => { introspect_version => $Graph::Easy::Introspect::VERSION }, |
|
462
|
|
|
|
|
|
|
} ; |
|
463
|
|
|
|
|
|
|
} |
|
464
|
|
|
|
|
|
|
|
|
465
|
31
|
|
50
|
|
|
133
|
my $char_pos = $self->{_introspect_char_pos} || {} ; |
|
466
|
31
|
|
|
|
|
165
|
my $grid = [ map { [split //, $_] } split /\n/, $ascii ] ; |
|
|
243
|
|
|
|
|
1514
|
|
|
467
|
|
|
|
|
|
|
|
|
468
|
31
|
|
|
|
|
122
|
my $total_height = scalar @$grid ; |
|
469
|
31
|
|
|
|
|
107
|
my $total_width = 0 ; |
|
470
|
|
|
|
|
|
|
|
|
471
|
31
|
|
|
|
|
84
|
for my $row (@$grid) |
|
472
|
|
|
|
|
|
|
{ |
|
473
|
243
|
100
|
|
|
|
560
|
$total_width = scalar @$row if scalar @$row > $total_width ; |
|
474
|
|
|
|
|
|
|
} |
|
475
|
|
|
|
|
|
|
|
|
476
|
31
|
|
|
|
|
88
|
$self->{_introspect_grid} = $grid ; |
|
477
|
|
|
|
|
|
|
|
|
478
|
31
|
|
|
|
|
146
|
my @sorted_nodes = Graph::Easy::Introspect::sorted_nodes($self) ; |
|
479
|
31
|
|
|
|
|
429
|
my @sorted_edges = Graph::Easy::Introspect::sorted_edges($self) ; |
|
480
|
|
|
|
|
|
|
|
|
481
|
31
|
|
|
|
|
512
|
my %node_index ; |
|
482
|
31
|
|
|
|
|
54
|
my $ni = 0 ; |
|
483
|
|
|
|
|
|
|
|
|
484
|
31
|
|
|
|
|
74
|
for my $n (@sorted_nodes) |
|
485
|
|
|
|
|
|
|
{ |
|
486
|
69
|
|
|
|
|
322
|
$node_index{$n->name} = $ni++ ; |
|
487
|
|
|
|
|
|
|
} |
|
488
|
|
|
|
|
|
|
|
|
489
|
31
|
|
|
|
|
229
|
my %component = Graph::Easy::Introspect::compute_components($self) ; |
|
490
|
|
|
|
|
|
|
|
|
491
|
31
|
|
|
|
|
71
|
my %mult_groups ; |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
{ |
|
494
|
31
|
|
|
|
|
55
|
my $eid = 0 ; |
|
|
31
|
|
|
|
|
65
|
|
|
495
|
|
|
|
|
|
|
|
|
496
|
31
|
|
|
|
|
127
|
for my $e (@sorted_edges) |
|
497
|
|
|
|
|
|
|
{ |
|
498
|
35
|
|
|
|
|
104
|
my $key = $e->from->name . '|' . $e->to->name ; |
|
499
|
35
|
|
|
|
|
513
|
push @{$mult_groups{$key}}, $eid++ ; |
|
|
35
|
|
|
|
|
139
|
|
|
500
|
|
|
|
|
|
|
} |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
my $is_directed = do |
|
504
|
31
|
|
|
|
|
54
|
{ |
|
505
|
31
|
|
|
|
|
76
|
my $r = eval { $self->is_directed } ; |
|
|
31
|
|
|
|
|
150
|
|
|
506
|
31
|
100
|
|
|
|
2795
|
defined $r ? ($r ? 1 : 0) : 1 ; |
|
|
|
50
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
} ; |
|
508
|
|
|
|
|
|
|
|
|
509
|
31
|
|
|
|
|
119
|
my $graph_attrs = Graph::Easy::Introspect::extract_graph_attrs($self) ; |
|
510
|
31
|
50
|
33
|
|
|
57
|
my $graph_label = do { my $l = eval { $self->label } ; (defined $l && $l ne '') ? $l : undef } ; |
|
|
31
|
|
|
|
|
55
|
|
|
|
31
|
|
|
|
|
119
|
|
|
|
31
|
|
|
|
|
442
|
|
|
511
|
31
|
|
50
|
|
|
60
|
my $layout_algo = eval { $self->attribute('flow') } // 'default' ; |
|
|
31
|
|
|
|
|
97
|
|
|
512
|
|
|
|
|
|
|
|
|
513
|
31
|
|
|
|
|
2038
|
my @nodes_ast ; |
|
514
|
|
|
|
|
|
|
|
|
515
|
31
|
|
|
|
|
69
|
for my $node (@sorted_nodes) |
|
516
|
|
|
|
|
|
|
{ |
|
517
|
69
|
|
|
|
|
219
|
my $name = $node->name ; |
|
518
|
69
|
|
50
|
|
|
432
|
my $cell_x = $node->x // 0 ; |
|
519
|
69
|
|
50
|
|
|
527
|
my $cell_y = $node->y // 0 ; |
|
520
|
69
|
|
50
|
|
|
482
|
my $cx = $node->{cx} // 1 ; |
|
521
|
69
|
|
50
|
|
|
215
|
my $cy = $node->{cy} // 1 ; |
|
522
|
69
|
|
|
|
|
138
|
my $cell_key = "$cell_x,$cell_y" ; |
|
523
|
69
|
|
50
|
|
|
214
|
my $cp = $char_pos->{$cell_key} // {} ; |
|
524
|
69
|
|
50
|
|
|
180
|
my $char_x = $cp->{char_x} // 0 ; |
|
525
|
69
|
|
50
|
|
|
200
|
my $char_y = $cp->{char_y} // 0 ; |
|
526
|
69
|
|
0
|
|
|
186
|
my $char_w = $cp->{render_w} // ($node->width // 0) ; |
|
|
|
|
33
|
|
|
|
|
|
527
|
69
|
|
0
|
|
|
157
|
my $char_h = $cp->{render_h} // ($node->height // 0) ; |
|
|
|
|
33
|
|
|
|
|
|
528
|
69
|
50
|
33
|
|
|
106
|
my $label = do { my $l = eval { $node->label } ; (defined $l && $l ne '') ? $l : $name } ; |
|
|
69
|
|
|
|
|
107
|
|
|
|
69
|
|
|
|
|
181
|
|
|
|
69
|
|
|
|
|
13189
|
|
|
529
|
69
|
50
|
|
|
|
127
|
my $is_anon = eval { $node->isa('Graph::Easy::Node::Anon') } ? 1 : 0 ; |
|
|
69
|
|
|
|
|
465
|
|
|
530
|
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
push @nodes_ast, |
|
532
|
|
|
|
|
|
|
{ |
|
533
|
|
|
|
|
|
|
id => $name, |
|
534
|
|
|
|
|
|
|
label => $label, |
|
535
|
|
|
|
|
|
|
is_anon => $is_anon, |
|
536
|
|
|
|
|
|
|
is_isolated => 0, |
|
537
|
|
|
|
|
|
|
x => $cell_x, |
|
538
|
|
|
|
|
|
|
y => $cell_y, |
|
539
|
|
|
|
|
|
|
char_x => $char_x, |
|
540
|
|
|
|
|
|
|
char_y => $char_y, |
|
541
|
|
|
|
|
|
|
char_width => $char_w, |
|
542
|
|
|
|
|
|
|
char_height => $char_h, |
|
543
|
|
|
|
|
|
|
width => $node->width // 0, |
|
544
|
|
|
|
|
|
|
height => $node->height // 0, |
|
545
|
|
|
|
|
|
|
bbox => |
|
546
|
|
|
|
|
|
|
{ |
|
547
|
|
|
|
|
|
|
x1 => $cell_x, |
|
548
|
|
|
|
|
|
|
y1 => $cell_y, |
|
549
|
|
|
|
|
|
|
x2 => $cell_x + $cx - 1, |
|
550
|
|
|
|
|
|
|
y2 => $cell_y + $cy - 1, |
|
551
|
|
|
|
|
|
|
}, |
|
552
|
69
|
|
50
|
|
|
283
|
component => $component{$name} // 0, |
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
553
|
|
|
|
|
|
|
groups => [], |
|
554
|
|
|
|
|
|
|
edges_in => [], |
|
555
|
|
|
|
|
|
|
edges_out => [], |
|
556
|
|
|
|
|
|
|
ports => |
|
557
|
|
|
|
|
|
|
{ |
|
558
|
|
|
|
|
|
|
left => [], |
|
559
|
|
|
|
|
|
|
right => [], |
|
560
|
|
|
|
|
|
|
top => [], |
|
561
|
|
|
|
|
|
|
bottom => [], |
|
562
|
|
|
|
|
|
|
unknown => [], |
|
563
|
|
|
|
|
|
|
}, |
|
564
|
|
|
|
|
|
|
attrs => Graph::Easy::Introspect::extract_attrs($node), |
|
565
|
|
|
|
|
|
|
} ; |
|
566
|
|
|
|
|
|
|
} |
|
567
|
|
|
|
|
|
|
|
|
568
|
31
|
|
|
|
|
55
|
my @edges_ast ; |
|
569
|
31
|
|
|
|
|
60
|
my $edge_id = 0 ; |
|
570
|
|
|
|
|
|
|
|
|
571
|
31
|
|
|
|
|
104
|
for my $edge (@sorted_edges) |
|
572
|
|
|
|
|
|
|
{ |
|
573
|
35
|
|
|
|
|
126
|
my $from = $edge->from ; |
|
574
|
35
|
|
|
|
|
221
|
my $to = $edge->to ; |
|
575
|
|
|
|
|
|
|
|
|
576
|
35
|
100
|
|
|
|
210
|
my $is_self_loop = $from->name eq $to->name ? 1 : 0 ; |
|
577
|
|
|
|
|
|
|
my $is_bidi = do |
|
578
|
35
|
|
|
|
|
315
|
{ |
|
579
|
35
|
|
|
|
|
59
|
my $b = 0 ; |
|
580
|
35
|
50
|
|
|
|
195
|
$b = 1 if ref($edge) =~ /Bidirectional/i ; |
|
581
|
35
|
100
|
100
|
|
|
116
|
$b = 1 if !$b && (eval { $edge->bidirectional } // 0) ; |
|
|
35
|
|
66
|
|
|
112
|
|
|
582
|
35
|
|
|
|
|
390
|
$b ; |
|
583
|
|
|
|
|
|
|
} ; |
|
584
|
|
|
|
|
|
|
|
|
585
|
35
|
|
|
|
|
163
|
my $from_port = $edge->from_port ; |
|
586
|
35
|
|
|
|
|
127
|
my $to_port = $edge->to_port ; |
|
587
|
35
|
|
|
|
|
123
|
my $arrow_dir = $edge->arrow_dir ; |
|
588
|
|
|
|
|
|
|
|
|
589
|
35
|
|
|
|
|
111
|
my $from_side = Graph::Easy::Introspect::port_side($from, $from_port) ; |
|
590
|
35
|
|
|
|
|
78
|
my $to_side = Graph::Easy::Introspect::port_side($to, $to_port) ; |
|
591
|
|
|
|
|
|
|
|
|
592
|
35
|
|
|
|
|
107
|
my $key = $from->name . '|' . $to->name ; |
|
593
|
35
|
50
|
|
|
|
320
|
my $multiplicity = scalar @{$mult_groups{$key} || []} ; |
|
|
35
|
|
|
|
|
124
|
|
|
594
|
|
|
|
|
|
|
|
|
595
|
35
|
100
|
66
|
|
|
54
|
my $edge_label = do { my $l = eval { $edge->label } ; (defined $l && $l ne '') ? $l : undef } ; |
|
|
35
|
|
|
|
|
80
|
|
|
|
35
|
|
|
|
|
132
|
|
|
|
35
|
|
|
|
|
1767
|
|
|
596
|
|
|
|
|
|
|
|
|
597
|
|
|
|
|
|
|
# from_port and to_port char coords are ON the node face, not the edge cell. |
|
598
|
|
|
|
|
|
|
# This gives correct distinct coords even for single-cell short edges. |
|
599
|
35
|
|
|
|
|
123
|
my $from_ast_node = $nodes_ast[ $node_index{$from->name} ] ; |
|
600
|
35
|
|
|
|
|
211
|
my $to_ast_node = $nodes_ast[ $node_index{$to->name} ] ; |
|
601
|
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
my $build_port = sub |
|
603
|
|
|
|
|
|
|
{ |
|
604
|
70
|
|
|
70
|
|
441
|
my ($port, $node_ast, $side) = @_ ; |
|
605
|
70
|
100
|
|
|
|
149
|
return undef unless $port ; |
|
606
|
|
|
|
|
|
|
|
|
607
|
68
|
|
50
|
|
|
171
|
my ($cx, $cy) = Graph::Easy::Introspect::face_char($node_ast, $side // 'unknown') ; |
|
608
|
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
return |
|
610
|
|
|
|
|
|
|
{ |
|
611
|
|
|
|
|
|
|
x => $port->{x}, |
|
612
|
|
|
|
|
|
|
y => $port->{y}, |
|
613
|
68
|
|
|
|
|
407
|
char_x => $cx, |
|
614
|
|
|
|
|
|
|
char_y => $cy, |
|
615
|
|
|
|
|
|
|
} ; |
|
616
|
35
|
|
|
|
|
328
|
} ; |
|
617
|
|
|
|
|
|
|
|
|
618
|
35
|
|
|
|
|
102
|
my ($label_x, $label_y, $label_char_x, $label_char_y) ; |
|
619
|
35
|
|
|
|
|
0
|
my @path ; |
|
620
|
|
|
|
|
|
|
|
|
621
|
35
|
50
|
|
|
|
60
|
for my $c (@{$edge->{cells} || []}) |
|
|
35
|
|
|
|
|
145
|
|
|
622
|
|
|
|
|
|
|
{ |
|
623
|
37
|
|
50
|
|
|
111
|
my $type = $c->{type} // 0 ; |
|
624
|
37
|
|
|
|
|
78
|
my $type_base = $type & Graph::Easy::Introspect::EDGE_TYPE_MASK ; |
|
625
|
37
|
100
|
|
|
|
102
|
my $is_label = ($type & Graph::Easy::Introspect::EDGE_LABEL_CELL) ? 1 : 0 ; |
|
626
|
37
|
|
|
|
|
98
|
my $ckey = "$c->{x},$c->{y}" ; |
|
627
|
37
|
|
50
|
|
|
112
|
my $cp = $char_pos->{$ckey} // {} ; |
|
628
|
|
|
|
|
|
|
|
|
629
|
37
|
100
|
66
|
|
|
163
|
if ($is_label && !defined $label_x) |
|
630
|
|
|
|
|
|
|
{ |
|
631
|
35
|
|
|
|
|
78
|
$label_x = $c->{x} ; |
|
632
|
35
|
|
|
|
|
64
|
$label_y = $c->{y} ; |
|
633
|
35
|
|
|
|
|
64
|
$label_char_x = $cp->{char_x} ; |
|
634
|
35
|
|
|
|
|
65
|
$label_char_y = $cp->{char_y} ; |
|
635
|
|
|
|
|
|
|
} |
|
636
|
|
|
|
|
|
|
|
|
637
|
37
|
|
50
|
|
|
91
|
my $cx = $cp->{char_x} // 0 ; |
|
638
|
37
|
|
50
|
|
|
92
|
my $cy = $cp->{char_y} // 0 ; |
|
639
|
37
|
|
50
|
|
|
173
|
my $cx2 = $cx + ($cp->{render_w} // 1) - 1 ; |
|
640
|
37
|
|
50
|
|
|
116
|
my $cy2 = $cy + ($cp->{render_h} // 1) - 1 ; |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
push @path, |
|
643
|
|
|
|
|
|
|
{ |
|
644
|
|
|
|
|
|
|
x => $c->{x}, |
|
645
|
|
|
|
|
|
|
y => $c->{y}, |
|
646
|
37
|
|
|
|
|
182
|
char_x => $cx, |
|
647
|
|
|
|
|
|
|
char_y => $cy, |
|
648
|
|
|
|
|
|
|
char_x2 => $cx2, |
|
649
|
|
|
|
|
|
|
char_y2 => $cy2, |
|
650
|
|
|
|
|
|
|
line_x1 => 0, |
|
651
|
|
|
|
|
|
|
line_y1 => 0, |
|
652
|
|
|
|
|
|
|
line_x2 => 0, |
|
653
|
|
|
|
|
|
|
line_y2 => 0, |
|
654
|
|
|
|
|
|
|
type => Graph::Easy::Introspect::cell_type_name($type_base), |
|
655
|
|
|
|
|
|
|
type_code => $type_base, |
|
656
|
|
|
|
|
|
|
is_label => $is_label, |
|
657
|
|
|
|
|
|
|
} ; |
|
658
|
|
|
|
|
|
|
} |
|
659
|
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
# Sort path cells into traversal order by adjacency walk from from_port. |
|
661
|
|
|
|
|
|
|
# $edge->{cells} is a hash so storage order is undefined. |
|
662
|
35
|
100
|
66
|
|
|
268
|
if (!$is_self_loop && $from_port && @path > 1) |
|
|
|
|
100
|
|
|
|
|
|
663
|
|
|
|
|
|
|
{ |
|
664
|
1
|
|
|
|
|
2
|
my %by_pos = map { my $k = "$_->{x},$_->{y}" ; $k => $_ } @path ; |
|
|
3
|
|
|
|
|
4
|
|
|
|
3
|
|
|
|
|
7
|
|
|
665
|
1
|
|
|
|
|
3
|
my $start_key = "$from_port->{x},$from_port->{y}" ; |
|
666
|
|
|
|
|
|
|
|
|
667
|
1
|
50
|
|
|
|
4
|
unless (exists $by_pos{$start_key}) |
|
668
|
|
|
|
|
|
|
{ |
|
669
|
0
|
|
|
|
|
0
|
for my $d ([-1,0],[1,0],[0,-1],[0,1]) |
|
670
|
|
|
|
|
|
|
{ |
|
671
|
0
|
|
|
|
|
0
|
my $k = ($from_port->{x}+$d->[0]) . ',' . ($from_port->{y}+$d->[1]) ; |
|
672
|
0
|
0
|
|
|
|
0
|
if (exists $by_pos{$k}) { $start_key = $k ; last } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
673
|
|
|
|
|
|
|
} |
|
674
|
|
|
|
|
|
|
} |
|
675
|
|
|
|
|
|
|
|
|
676
|
1
|
|
|
|
|
1
|
my @sorted ; |
|
677
|
|
|
|
|
|
|
my %visited ; |
|
678
|
1
|
|
|
|
|
2
|
my $cur = $start_key ; |
|
679
|
|
|
|
|
|
|
|
|
680
|
1
|
|
33
|
|
|
4
|
while (exists $by_pos{$cur} && !$visited{$cur}) |
|
681
|
|
|
|
|
|
|
{ |
|
682
|
3
|
|
|
|
|
5
|
$visited{$cur} = 1 ; |
|
683
|
3
|
|
|
|
|
5
|
push @sorted, $by_pos{$cur} ; |
|
684
|
3
|
|
|
|
|
3
|
my $c = $by_pos{$cur} ; |
|
685
|
3
|
|
|
|
|
3
|
my $next ; |
|
686
|
|
|
|
|
|
|
|
|
687
|
3
|
|
|
|
|
8
|
for my $d ([-1,0],[1,0],[0,-1],[0,1]) |
|
688
|
|
|
|
|
|
|
{ |
|
689
|
12
|
|
|
|
|
17
|
my $nk = ($c->{x}+$d->[0]) . ',' . ($c->{y}+$d->[1]) ; |
|
690
|
12
|
100
|
100
|
|
|
20
|
next unless exists $by_pos{$nk} && !$visited{$nk} ; |
|
691
|
2
|
|
|
|
|
3
|
$next = $nk ; |
|
692
|
2
|
|
|
|
|
3
|
last ; |
|
693
|
|
|
|
|
|
|
} |
|
694
|
|
|
|
|
|
|
|
|
695
|
3
|
100
|
|
|
|
7
|
last unless defined $next ; |
|
696
|
2
|
|
|
|
|
5
|
$cur = $next ; |
|
697
|
|
|
|
|
|
|
} |
|
698
|
|
|
|
|
|
|
|
|
699
|
1
|
50
|
|
|
|
5
|
@path = @sorted if @sorted == @path ; |
|
700
|
|
|
|
|
|
|
} |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
# Compute line_* for each path cell using a waypoint-based polyline model. |
|
703
|
|
|
|
|
|
|
# |
|
704
|
|
|
|
|
|
|
# Waypoints: [from_port_char, corner_0_bend, ..., corner_n_bend, to_port_char] |
|
705
|
|
|
|
|
|
|
# Each corner cell either introduces a straight run (next cell is VER/HOR) |
|
706
|
|
|
|
|
|
|
# or terminates one (prev cell is VER/HOR). |
|
707
|
|
|
|
|
|
|
# Introducing corner: assign wp[ci]->wp[ci+1], then advance ci. |
|
708
|
|
|
|
|
|
|
# Terminating corner: advance ci first, then assign wp[ci]->wp[ci+1]. |
|
709
|
|
|
|
|
|
|
# VER/HOR cells: always assign wp[ci]->wp[ci+1], never advance ci. |
|
710
|
|
|
|
|
|
|
# |
|
711
|
|
|
|
|
|
|
# This gives contiguous, directed segments: each cell's endpoint equals |
|
712
|
|
|
|
|
|
|
# the next cell's start point. |
|
713
|
|
|
|
|
|
|
|
|
714
|
35
|
|
|
|
|
257
|
my %is_straight_type = (VER => 1, HOR => 1, CROSS => 1, HOLE => 1) ; |
|
715
|
|
|
|
|
|
|
|
|
716
|
35
|
|
|
|
|
89
|
my ($fp_lx, $fp_ly) = (0, 0) ; |
|
717
|
35
|
|
|
|
|
85
|
my ($tp_lx, $tp_ly) = (0, 0) ; |
|
718
|
|
|
|
|
|
|
|
|
719
|
35
|
100
|
|
|
|
90
|
unless ($is_self_loop) |
|
720
|
|
|
|
|
|
|
{ |
|
721
|
34
|
50
|
|
|
|
146
|
($fp_lx, $fp_ly) = Graph::Easy::Introspect::face_char($from_ast_node, $from_side) |
|
722
|
|
|
|
|
|
|
if $from_side ne 'unknown' ; |
|
723
|
34
|
50
|
|
|
|
114
|
($tp_lx, $tp_ly) = Graph::Easy::Introspect::face_char($to_ast_node, $to_side) |
|
724
|
|
|
|
|
|
|
if $to_side ne 'unknown' ; |
|
725
|
|
|
|
|
|
|
} |
|
726
|
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
# Build mid_x/mid_y for each cell (needed for corner bends). |
|
728
|
35
|
|
|
|
|
72
|
my %cell_mid ; |
|
729
|
35
|
|
|
|
|
77
|
for my $p (@path) |
|
730
|
|
|
|
|
|
|
{ |
|
731
|
37
|
|
50
|
|
|
151
|
my $cp2 = $char_pos->{"$p->{x},$p->{y}"} // {} ; |
|
732
|
37
|
|
50
|
|
|
154
|
my $midx = ($cp2->{char_x} // 0) + int(($cp2->{render_w} // 1) / 2) ; |
|
|
|
|
50
|
|
|
|
|
|
733
|
37
|
|
50
|
|
|
192
|
my $midy = ($cp2->{char_y} // 0) + int(($cp2->{render_h} // 1) / 2) ; |
|
|
|
|
50
|
|
|
|
|
|
734
|
37
|
|
|
|
|
181
|
$cell_mid{"$p->{x},$p->{y}"} = [$midx, $midy] ; |
|
735
|
|
|
|
|
|
|
} |
|
736
|
|
|
|
|
|
|
|
|
737
|
|
|
|
|
|
|
# Build waypoint list. |
|
738
|
35
|
|
|
|
|
113
|
my @wp = ([$fp_lx, $fp_ly]) ; |
|
739
|
|
|
|
|
|
|
|
|
740
|
35
|
|
|
|
|
71
|
for my $p (@path) |
|
741
|
|
|
|
|
|
|
{ |
|
742
|
37
|
100
|
|
|
|
136
|
next if $is_straight_type{$p->{type}} ; |
|
743
|
3
|
|
|
|
|
8
|
my $m = $cell_mid{"$p->{x},$p->{y}"} ; |
|
744
|
3
|
|
|
|
|
7
|
push @wp, [$m->[0], $m->[1]] ; |
|
745
|
|
|
|
|
|
|
} |
|
746
|
|
|
|
|
|
|
|
|
747
|
35
|
|
|
|
|
86
|
push @wp, [$tp_lx, $tp_ly] ; |
|
748
|
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
# Assign line_* to each cell. |
|
750
|
35
|
|
|
|
|
73
|
my $ci = 0 ; |
|
751
|
|
|
|
|
|
|
|
|
752
|
35
|
|
|
|
|
107
|
for my $i (0 .. $#path) |
|
753
|
|
|
|
|
|
|
{ |
|
754
|
37
|
|
|
|
|
71
|
my $p = $path[$i] ; |
|
755
|
37
|
100
|
|
|
|
134
|
my $prev = $i > 0 ? $path[$i-1] : undef ; |
|
756
|
|
|
|
|
|
|
|
|
757
|
37
|
100
|
|
|
|
113
|
if ($is_straight_type{$p->{type}}) |
|
758
|
|
|
|
|
|
|
{ |
|
759
|
34
|
100
|
|
|
|
123
|
my $next_ci = $ci + 1 < $#wp ? $ci + 1 : $#wp ; |
|
760
|
34
|
|
|
|
|
82
|
$p->{line_x1} = $wp[$ci][0] ; |
|
761
|
34
|
|
|
|
|
65
|
$p->{line_y1} = $wp[$ci][1] ; |
|
762
|
34
|
|
|
|
|
124
|
$p->{line_x2} = $wp[$next_ci][0] ; |
|
763
|
34
|
|
|
|
|
90
|
$p->{line_y2} = $wp[$next_ci][1] ; |
|
764
|
|
|
|
|
|
|
} |
|
765
|
|
|
|
|
|
|
else |
|
766
|
|
|
|
|
|
|
{ |
|
767
|
3
|
|
66
|
|
|
8
|
my $prev_straight = $prev && $is_straight_type{$prev->{type}} ; |
|
768
|
|
|
|
|
|
|
|
|
769
|
3
|
100
|
|
|
|
23
|
$ci++ if $prev_straight ; |
|
770
|
|
|
|
|
|
|
|
|
771
|
3
|
100
|
|
|
|
16
|
my $next_ci = $ci + 1 < $#wp ? $ci + 1 : $#wp ; |
|
772
|
3
|
|
|
|
|
7
|
$p->{line_x1} = $wp[$ci][0] ; |
|
773
|
3
|
|
|
|
|
4
|
$p->{line_y1} = $wp[$ci][1] ; |
|
774
|
3
|
|
|
|
|
6
|
$p->{line_x2} = $wp[$next_ci][0] ; |
|
775
|
3
|
|
|
|
|
4
|
$p->{line_y2} = $wp[$next_ci][1] ; |
|
776
|
|
|
|
|
|
|
|
|
777
|
3
|
|
|
|
|
8
|
my $m = $cell_mid{"$p->{x},$p->{y}"} ; |
|
778
|
3
|
|
|
|
|
5
|
$p->{bend_x} = $m->[0] ; |
|
779
|
3
|
|
|
|
|
6
|
$p->{bend_y} = $m->[1] ; |
|
780
|
|
|
|
|
|
|
|
|
781
|
3
|
100
|
|
|
|
9
|
$ci++ unless $prev_straight ; |
|
782
|
|
|
|
|
|
|
} |
|
783
|
|
|
|
|
|
|
} |
|
784
|
|
|
|
|
|
|
|
|
785
|
35
|
|
|
|
|
134
|
push @edges_ast, |
|
786
|
|
|
|
|
|
|
{ |
|
787
|
|
|
|
|
|
|
id => $edge_id, |
|
788
|
|
|
|
|
|
|
from => $from->name, |
|
789
|
|
|
|
|
|
|
to => $to->name, |
|
790
|
|
|
|
|
|
|
is_self_loop => $is_self_loop, |
|
791
|
|
|
|
|
|
|
is_bidirectional => $is_bidi, |
|
792
|
|
|
|
|
|
|
multiplicity => $multiplicity, |
|
793
|
|
|
|
|
|
|
arrow_dir => $arrow_dir, |
|
794
|
|
|
|
|
|
|
from_port => $build_port->($from_port, $from_ast_node, $from_side), |
|
795
|
|
|
|
|
|
|
to_port => $build_port->($to_port, $to_ast_node, $to_side), |
|
796
|
|
|
|
|
|
|
from_side => $from_side, |
|
797
|
|
|
|
|
|
|
to_side => $to_side, |
|
798
|
|
|
|
|
|
|
label => $edge_label, |
|
799
|
|
|
|
|
|
|
label_x => $label_x, |
|
800
|
|
|
|
|
|
|
label_y => $label_y, |
|
801
|
|
|
|
|
|
|
label_char_x => $label_char_x, |
|
802
|
|
|
|
|
|
|
label_char_y => $label_char_y, |
|
803
|
|
|
|
|
|
|
path => \@path, |
|
804
|
|
|
|
|
|
|
attrs => Graph::Easy::Introspect::extract_attrs($edge), |
|
805
|
|
|
|
|
|
|
} ; |
|
806
|
|
|
|
|
|
|
|
|
807
|
35
|
|
|
|
|
430
|
$edge_id++ ; |
|
808
|
|
|
|
|
|
|
} |
|
809
|
|
|
|
|
|
|
|
|
810
|
31
|
|
|
|
|
67
|
my @groups_ast ; |
|
811
|
|
|
|
|
|
|
my %group_index ; |
|
812
|
31
|
|
|
|
|
54
|
my $gi = 0 ; |
|
813
|
|
|
|
|
|
|
|
|
814
|
31
|
|
|
|
|
52
|
my @graph_groups ; |
|
815
|
31
|
|
|
|
|
60
|
eval { @graph_groups = $self->groups } ; |
|
|
31
|
|
|
|
|
139
|
|
|
816
|
|
|
|
|
|
|
|
|
817
|
31
|
|
|
|
|
298
|
for my $group (sort { $a->name cmp $b->name } @graph_groups) |
|
|
1
|
|
|
|
|
4
|
|
|
818
|
|
|
|
|
|
|
{ |
|
819
|
6
|
|
|
|
|
35
|
my $gname = $group->name ; |
|
820
|
6
|
|
|
|
|
40
|
my @gnodes = eval { map { $_->name } $group->nodes } ; |
|
|
6
|
|
|
|
|
33
|
|
|
|
9
|
|
|
|
|
135
|
|
|
821
|
|
|
|
|
|
|
|
|
822
|
6
|
|
|
|
|
46
|
$group_index{$gname} = $gi ; |
|
823
|
|
|
|
|
|
|
|
|
824
|
6
|
|
|
|
|
17
|
my ($bx1, $by1, $bx2, $by2) ; |
|
825
|
|
|
|
|
|
|
|
|
826
|
6
|
|
|
|
|
15
|
for my $nname (@gnodes) |
|
827
|
|
|
|
|
|
|
{ |
|
828
|
9
|
|
|
|
|
36
|
my $n = $self->node($nname) ; |
|
829
|
9
|
50
|
|
|
|
96
|
next unless $n ; |
|
830
|
|
|
|
|
|
|
|
|
831
|
9
|
|
|
|
|
40
|
my ($nx1, $ny1, $nx2, $ny2) = Graph::Easy::Introspect::node_bbox($n) ; |
|
832
|
|
|
|
|
|
|
|
|
833
|
9
|
100
|
66
|
|
|
39
|
$bx1 = $nx1 if !defined $bx1 || $nx1 < $bx1 ; |
|
834
|
9
|
100
|
66
|
|
|
32
|
$by1 = $ny1 if !defined $by1 || $ny1 < $by1 ; |
|
835
|
9
|
100
|
100
|
|
|
32
|
$bx2 = $nx2 if !defined $bx2 || $nx2 > $bx2 ; |
|
836
|
9
|
100
|
100
|
|
|
34
|
$by2 = $ny2 if !defined $by2 || $ny2 > $by2 ; |
|
837
|
|
|
|
|
|
|
} |
|
838
|
|
|
|
|
|
|
|
|
839
|
6
|
|
50
|
|
|
80
|
my $cell_bbox = |
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
840
|
|
|
|
|
|
|
{ |
|
841
|
|
|
|
|
|
|
x1 => $bx1 // 0, |
|
842
|
|
|
|
|
|
|
y1 => $by1 // 0, |
|
843
|
|
|
|
|
|
|
x2 => $bx2 // 0, |
|
844
|
|
|
|
|
|
|
y2 => $by2 // 0, |
|
845
|
|
|
|
|
|
|
} ; |
|
846
|
|
|
|
|
|
|
|
|
847
|
6
|
50
|
33
|
|
|
13
|
my $glabel = do { my $l = eval { $group->label } ; (defined $l && $l ne '') ? $l : $gname } ; |
|
|
6
|
|
|
|
|
11
|
|
|
|
6
|
|
|
|
|
24
|
|
|
|
6
|
|
|
|
|
1315
|
|
|
848
|
|
|
|
|
|
|
|
|
849
|
6
|
|
|
|
|
48
|
my $gcb = Graph::Easy::Introspect::group_char_bbox($self, { bbox => $cell_bbox }, $char_pos) ; |
|
850
|
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
push @groups_ast, |
|
852
|
|
|
|
|
|
|
{ |
|
853
|
|
|
|
|
|
|
id => $gname, |
|
854
|
|
|
|
|
|
|
label => $glabel, |
|
855
|
|
|
|
|
|
|
nodes => \@gnodes, |
|
856
|
|
|
|
|
|
|
bbox => $cell_bbox, |
|
857
|
|
|
|
|
|
|
char_x => $gcb ? $gcb->{x} : 0, |
|
858
|
|
|
|
|
|
|
char_y => $gcb ? $gcb->{y} : 0, |
|
859
|
|
|
|
|
|
|
char_width => $gcb ? $gcb->{w} : 0, |
|
860
|
6
|
50
|
|
|
|
56
|
char_height => $gcb ? $gcb->{h} : 0, |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
861
|
|
|
|
|
|
|
attrs => Graph::Easy::Introspect::extract_attrs($group), |
|
862
|
|
|
|
|
|
|
} ; |
|
863
|
|
|
|
|
|
|
|
|
864
|
6
|
|
|
|
|
26
|
$gi++ ; |
|
865
|
|
|
|
|
|
|
} |
|
866
|
|
|
|
|
|
|
|
|
867
|
31
|
|
|
|
|
68
|
$edge_id = 0 ; |
|
868
|
|
|
|
|
|
|
|
|
869
|
31
|
|
|
|
|
76
|
for my $e_ast (@edges_ast) |
|
870
|
|
|
|
|
|
|
{ |
|
871
|
35
|
|
|
|
|
100
|
my $from_idx = $node_index{$e_ast->{from}} ; |
|
872
|
35
|
|
|
|
|
82
|
my $to_idx = $node_index{$e_ast->{to}} ; |
|
873
|
35
|
|
|
|
|
59
|
my $eid = $e_ast->{id} ; |
|
874
|
|
|
|
|
|
|
|
|
875
|
35
|
|
|
|
|
89
|
push @{$nodes_ast[$from_idx]{edges_out}}, $eid ; |
|
|
35
|
|
|
|
|
104
|
|
|
876
|
35
|
|
|
|
|
58
|
push @{$nodes_ast[$to_idx]{edges_in}}, $eid ; |
|
|
35
|
|
|
|
|
90
|
|
|
877
|
|
|
|
|
|
|
|
|
878
|
35
|
100
|
|
|
|
97
|
if ($e_ast->{from_port}) |
|
879
|
|
|
|
|
|
|
{ |
|
880
|
34
|
|
50
|
|
|
100
|
my $side = $e_ast->{from_side} || 'unknown' ; |
|
881
|
|
|
|
|
|
|
|
|
882
|
34
|
|
|
|
|
313
|
push @{$nodes_ast[$from_idx]{ports}{$side}}, |
|
883
|
|
|
|
|
|
|
{ |
|
884
|
|
|
|
|
|
|
edge_id => $eid, |
|
885
|
|
|
|
|
|
|
role => 'out', |
|
886
|
|
|
|
|
|
|
x => $e_ast->{from_port}{x}, |
|
887
|
|
|
|
|
|
|
y => $e_ast->{from_port}{y}, |
|
888
|
|
|
|
|
|
|
char_x => $e_ast->{from_port}{char_x}, |
|
889
|
|
|
|
|
|
|
char_y => $e_ast->{from_port}{char_y}, |
|
890
|
34
|
|
|
|
|
52
|
} ; |
|
891
|
|
|
|
|
|
|
} |
|
892
|
|
|
|
|
|
|
|
|
893
|
35
|
100
|
|
|
|
117
|
if ($e_ast->{to_port}) |
|
894
|
|
|
|
|
|
|
{ |
|
895
|
34
|
|
50
|
|
|
120
|
my $side = $e_ast->{to_side} || 'unknown' ; |
|
896
|
|
|
|
|
|
|
|
|
897
|
34
|
|
|
|
|
288
|
push @{$nodes_ast[$to_idx]{ports}{$side}}, |
|
898
|
|
|
|
|
|
|
{ |
|
899
|
|
|
|
|
|
|
edge_id => $eid, |
|
900
|
|
|
|
|
|
|
role => 'in', |
|
901
|
|
|
|
|
|
|
x => $e_ast->{to_port}{x}, |
|
902
|
|
|
|
|
|
|
y => $e_ast->{to_port}{y}, |
|
903
|
|
|
|
|
|
|
char_x => $e_ast->{to_port}{char_x}, |
|
904
|
|
|
|
|
|
|
char_y => $e_ast->{to_port}{char_y}, |
|
905
|
34
|
|
|
|
|
95
|
} ; |
|
906
|
|
|
|
|
|
|
} |
|
907
|
|
|
|
|
|
|
|
|
908
|
35
|
|
|
|
|
84
|
$edge_id++ ; |
|
909
|
|
|
|
|
|
|
} |
|
910
|
|
|
|
|
|
|
|
|
911
|
31
|
|
|
|
|
60
|
for my $n_ast (@nodes_ast) |
|
912
|
|
|
|
|
|
|
{ |
|
913
|
69
|
|
|
|
|
133
|
for my $side (qw/left right/) |
|
914
|
|
|
|
|
|
|
{ |
|
915
|
|
|
|
|
|
|
$n_ast->{ports}{$side} = |
|
916
|
138
|
|
|
|
|
216
|
[sort { $a->{y} <=> $b->{y} } @{$n_ast->{ports}{$side}}] ; |
|
|
0
|
|
|
|
|
0
|
|
|
|
138
|
|
|
|
|
420
|
|
|
917
|
|
|
|
|
|
|
} |
|
918
|
|
|
|
|
|
|
|
|
919
|
69
|
|
|
|
|
120
|
for my $side (qw/top bottom/) |
|
920
|
|
|
|
|
|
|
{ |
|
921
|
|
|
|
|
|
|
$n_ast->{ports}{$side} = |
|
922
|
138
|
|
|
|
|
182
|
[sort { $a->{x} <=> $b->{x} } @{$n_ast->{ports}{$side}}] ; |
|
|
0
|
|
|
|
|
0
|
|
|
|
138
|
|
|
|
|
361
|
|
|
923
|
|
|
|
|
|
|
} |
|
924
|
|
|
|
|
|
|
|
|
925
|
69
|
|
|
|
|
160
|
for my $side (qw/left right top bottom unknown/) |
|
926
|
|
|
|
|
|
|
{ |
|
927
|
345
|
|
|
|
|
493
|
my $seq = 0 ; |
|
928
|
|
|
|
|
|
|
|
|
929
|
345
|
|
|
|
|
493
|
for my $p (@{$n_ast->{ports}{$side}}) |
|
|
345
|
|
|
|
|
701
|
|
|
930
|
|
|
|
|
|
|
{ |
|
931
|
68
|
|
|
|
|
188
|
$p->{seq} = $seq++ ; |
|
932
|
|
|
|
|
|
|
} |
|
933
|
|
|
|
|
|
|
} |
|
934
|
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
$n_ast->{is_isolated} = |
|
936
|
69
|
100
|
100
|
|
|
99
|
(scalar(@{$n_ast->{edges_in}}) == 0 && scalar(@{$n_ast->{edges_out}}) == 0) ? 1 : 0 ; |
|
937
|
|
|
|
|
|
|
} |
|
938
|
|
|
|
|
|
|
|
|
939
|
31
|
|
|
|
|
65
|
for my $g_ast (@groups_ast) |
|
940
|
|
|
|
|
|
|
{ |
|
941
|
6
|
|
|
|
|
12
|
for my $nname (@{$g_ast->{nodes}}) |
|
|
6
|
|
|
|
|
13
|
|
|
942
|
|
|
|
|
|
|
{ |
|
943
|
9
|
|
|
|
|
22
|
my $idx = $node_index{$nname} ; |
|
944
|
9
|
50
|
|
|
|
22
|
next unless defined $idx ; |
|
945
|
9
|
|
|
|
|
17
|
push @{$nodes_ast[$idx]{groups}}, $g_ast->{id} ; |
|
|
9
|
|
|
|
|
36
|
|
|
946
|
|
|
|
|
|
|
} |
|
947
|
|
|
|
|
|
|
} |
|
948
|
|
|
|
|
|
|
|
|
949
|
31
|
|
|
|
|
63
|
my %cell_grid ; |
|
950
|
|
|
|
|
|
|
|
|
951
|
31
|
|
|
|
|
120
|
for my $key (keys %$char_pos) |
|
952
|
|
|
|
|
|
|
{ |
|
953
|
186
|
|
|
|
|
320
|
my $cp = $char_pos->{$key} ; |
|
954
|
186
|
|
|
|
|
284
|
my $type = 'unknown' ; |
|
955
|
|
|
|
|
|
|
|
|
956
|
186
|
100
|
|
|
|
808
|
if ($cp->{ref} =~ /::Node$/) |
|
|
|
100
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
{ |
|
958
|
69
|
|
|
|
|
139
|
$type = 'node' ; |
|
959
|
|
|
|
|
|
|
} |
|
960
|
|
|
|
|
|
|
elsif ($cp->{ref} =~ /Edge/) |
|
961
|
|
|
|
|
|
|
{ |
|
962
|
37
|
|
|
|
|
64
|
$type = 'edge' ; |
|
963
|
|
|
|
|
|
|
} |
|
964
|
|
|
|
|
|
|
elsif ($cp->{ref} =~ /Group/) |
|
965
|
|
|
|
|
|
|
{ |
|
966
|
80
|
|
|
|
|
147
|
$type = 'group' ; |
|
967
|
|
|
|
|
|
|
} |
|
968
|
|
|
|
|
|
|
elsif ($cp->{ref} =~ /Node::Cell/) |
|
969
|
|
|
|
|
|
|
{ |
|
970
|
0
|
|
|
|
|
0
|
$type = 'empty' ; |
|
971
|
|
|
|
|
|
|
} |
|
972
|
|
|
|
|
|
|
|
|
973
|
|
|
|
|
|
|
$cell_grid{$key} = |
|
974
|
|
|
|
|
|
|
{ |
|
975
|
|
|
|
|
|
|
type => $type, |
|
976
|
|
|
|
|
|
|
name => $cp->{name}, |
|
977
|
|
|
|
|
|
|
char_x => $cp->{char_x}, |
|
978
|
|
|
|
|
|
|
char_y => $cp->{char_y}, |
|
979
|
|
|
|
|
|
|
render_w => $cp->{render_w}, |
|
980
|
|
|
|
|
|
|
render_h => $cp->{render_h}, |
|
981
|
186
|
|
|
|
|
1123
|
} ; |
|
982
|
|
|
|
|
|
|
} |
|
983
|
|
|
|
|
|
|
|
|
984
|
31
|
|
|
|
|
115
|
$self->{_introspect_cell_grid} = \%cell_grid ; |
|
985
|
|
|
|
|
|
|
|
|
986
|
|
|
|
|
|
|
return |
|
987
|
|
|
|
|
|
|
{ |
|
988
|
31
|
|
|
|
|
660
|
meta => |
|
989
|
|
|
|
|
|
|
{ |
|
990
|
|
|
|
|
|
|
introspect_version => $Graph::Easy::Introspect::VERSION, |
|
991
|
|
|
|
|
|
|
graph_easy_version => $Graph::Easy::VERSION, |
|
992
|
|
|
|
|
|
|
generated_at => time(), |
|
993
|
|
|
|
|
|
|
layout_algorithm => $layout_algo, |
|
994
|
|
|
|
|
|
|
}, |
|
995
|
|
|
|
|
|
|
graph => |
|
996
|
|
|
|
|
|
|
{ |
|
997
|
|
|
|
|
|
|
is_directed => $is_directed, |
|
998
|
|
|
|
|
|
|
label => $graph_label, |
|
999
|
|
|
|
|
|
|
total_width => $total_width, |
|
1000
|
|
|
|
|
|
|
total_height => $total_height, |
|
1001
|
|
|
|
|
|
|
attrs => $graph_attrs, |
|
1002
|
|
|
|
|
|
|
}, |
|
1003
|
|
|
|
|
|
|
nodes => \@nodes_ast, |
|
1004
|
|
|
|
|
|
|
edges => \@edges_ast, |
|
1005
|
|
|
|
|
|
|
groups => \@groups_ast, |
|
1006
|
|
|
|
|
|
|
} ; |
|
1007
|
|
|
|
|
|
|
} |
|
1008
|
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
1010
|
|
|
|
|
|
|
|
|
1011
|
|
|
|
|
|
|
sub ast_grid |
|
1012
|
|
|
|
|
|
|
{ |
|
1013
|
2
|
|
|
2
|
0
|
4447
|
my ($self) = @_ ; |
|
1014
|
|
|
|
|
|
|
|
|
1015
|
2
|
|
|
|
|
11
|
return $self->{_introspect_grid} ; |
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------ |
|
1019
|
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
sub ast_cell_grid |
|
1021
|
|
|
|
|
|
|
{ |
|
1022
|
0
|
|
|
0
|
0
|
|
my ($self) = @_ ; |
|
1023
|
|
|
|
|
|
|
|
|
1024
|
0
|
|
|
|
|
|
return $self->{_introspect_cell_grid} ; |
|
1025
|
|
|
|
|
|
|
} |
|
1026
|
|
|
|
|
|
|
|
|
1027
|
|
|
|
|
|
|
1 ; |
|
1028
|
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
__END__ |