line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
############################################################################# |
2
|
|
|
|
|
|
|
# An edge connecting two nodes in Graph::Easy. |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
############################################################################# |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
package Graph::Easy::Edge; |
7
|
|
|
|
|
|
|
|
8
|
49
|
|
|
49
|
|
45724
|
use Graph::Easy::Node; |
|
49
|
|
|
|
|
197
|
|
|
49
|
|
|
|
|
3418
|
|
9
|
|
|
|
|
|
|
@ISA = qw/Graph::Easy::Node/; # an edge is just a special node |
10
|
|
|
|
|
|
|
$VERSION = '0.75'; |
11
|
|
|
|
|
|
|
|
12
|
49
|
|
|
49
|
|
532
|
use strict; |
|
49
|
|
|
|
|
95
|
|
|
49
|
|
|
|
|
2268
|
|
13
|
49
|
|
|
49
|
|
308
|
use warnings; |
|
49
|
|
|
|
|
86
|
|
|
49
|
|
|
|
|
1866
|
|
14
|
|
|
|
|
|
|
|
15
|
49
|
|
|
49
|
|
269
|
use constant isa_cell => 1; |
|
49
|
|
|
|
|
87
|
|
|
49
|
|
|
|
|
147020
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
############################################################################# |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
sub _init |
20
|
|
|
|
|
|
|
{ |
21
|
|
|
|
|
|
|
# generic init, override in subclasses |
22
|
1173
|
|
|
1173
|
|
2585
|
my ($self,$args) = @_; |
23
|
|
|
|
|
|
|
|
24
|
1173
|
|
|
|
|
3628
|
$self->{class} = 'edge'; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# leave this unitialized until we need it |
27
|
|
|
|
|
|
|
# $self->{cells} = [ ]; |
28
|
|
|
|
|
|
|
|
29
|
1173
|
|
|
|
|
5775
|
foreach my $k (sort keys %$args) |
30
|
|
|
|
|
|
|
{ |
31
|
2040
|
50
|
|
|
|
9946
|
if ($k !~ /^(label|name|style)\z/) |
32
|
|
|
|
|
|
|
{ |
33
|
0
|
|
|
|
|
0
|
require Carp; |
34
|
0
|
|
|
|
|
0
|
Carp::confess ("Invalid argument '$k' passed to Graph::Easy::Node->new()"); |
35
|
|
|
|
|
|
|
} |
36
|
2040
|
100
|
|
|
|
3769
|
my $n = $k; $n = 'label' if $k eq 'name'; |
|
2040
|
|
|
|
|
5443
|
|
37
|
|
|
|
|
|
|
|
38
|
2040
|
|
|
|
|
8750
|
$self->{att}->{$n} = $args->{$k}; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
1173
|
|
|
|
|
4601
|
$self; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
############################################################################# |
45
|
|
|
|
|
|
|
# accessor methods |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub bidirectional |
48
|
|
|
|
|
|
|
{ |
49
|
605
|
|
|
605
|
1
|
2948
|
my $self = shift; |
50
|
|
|
|
|
|
|
|
51
|
605
|
100
|
|
|
|
7883
|
if (@_ > 0) |
52
|
|
|
|
|
|
|
{ |
53
|
36
|
|
100
|
|
|
199
|
my $old = $self->{bidirectional} || 0; |
54
|
36
|
100
|
|
|
|
156
|
$self->{bidirectional} = $_[0] ? 1 : 0; |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
# invalidate layout? |
57
|
36
|
100
|
100
|
|
|
296
|
$self->{graph}->{score} = undef if $old != $self->{bidirectional} && ref($self->{graph}); |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
605
|
|
|
|
|
3643
|
$self->{bidirectional}; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub undirected |
64
|
|
|
|
|
|
|
{ |
65
|
653
|
|
|
653
|
1
|
2214
|
my $self = shift; |
66
|
|
|
|
|
|
|
|
67
|
653
|
100
|
|
|
|
3169
|
if (@_ > 0) |
68
|
|
|
|
|
|
|
{ |
69
|
30
|
|
100
|
|
|
191
|
my $old = $self->{undirected} || 0; |
70
|
30
|
100
|
|
|
|
130
|
$self->{undirected} = $_[0] ? 1 : 0; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# invalidate layout? |
73
|
30
|
100
|
66
|
|
|
275
|
$self->{graph}->{score} = undef if $old != $self->{undirected} && ref($self->{graph}); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
|
76
|
653
|
|
|
|
|
2709
|
$self->{undirected}; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
sub has_ports |
80
|
|
|
|
|
|
|
{ |
81
|
1782
|
|
|
1782
|
1
|
3020
|
my $self = shift; |
82
|
|
|
|
|
|
|
|
83
|
1782
|
|
100
|
|
|
31654
|
my $s_port = $self->{att}->{start} || $self->attribute('start'); |
84
|
|
|
|
|
|
|
|
85
|
1782
|
100
|
|
|
|
5690
|
return 1 if $s_port ne ''; |
86
|
|
|
|
|
|
|
|
87
|
1682
|
|
66
|
|
|
14660
|
my $e_port = $self->{att}->{end} || $self->attribute('end'); |
88
|
|
|
|
|
|
|
|
89
|
1682
|
100
|
|
|
|
4753
|
return 1 if $e_port ne ''; |
90
|
|
|
|
|
|
|
|
91
|
1655
|
|
|
|
|
7355
|
0; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub start_port |
95
|
|
|
|
|
|
|
{ |
96
|
|
|
|
|
|
|
# return the side and portnumber if the edge has a shared source port |
97
|
|
|
|
|
|
|
# undef for none |
98
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
99
|
|
|
|
|
|
|
|
100
|
0
|
|
0
|
|
|
0
|
my $s = $self->{att}->{start} || $self->attribute('start'); |
101
|
0
|
0
|
0
|
|
|
0
|
return undef if !defined $s || $s !~ /,/; # "south, 0" => ok, "south" => no |
102
|
|
|
|
|
|
|
|
103
|
0
|
0
|
|
|
|
0
|
return (split /\s*,\s*/, $s) if wantarray; |
104
|
|
|
|
|
|
|
|
105
|
0
|
|
|
|
|
0
|
$s =~ s/\s+//g; # remove spaces to normalize "south, 0" to "south,0" |
106
|
0
|
|
|
|
|
0
|
$s; |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub end_port |
110
|
|
|
|
|
|
|
{ |
111
|
|
|
|
|
|
|
# return the side and portnumber if the edge has a shared source port |
112
|
|
|
|
|
|
|
# undef for none |
113
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
114
|
|
|
|
|
|
|
|
115
|
0
|
|
0
|
|
|
0
|
my $s = $self->{att}->{end} || $self->attribute('end'); |
116
|
0
|
0
|
0
|
|
|
0
|
return undef if !defined $s || $s !~ /,/; # "south, 0" => ok, "south" => no |
117
|
|
|
|
|
|
|
|
118
|
0
|
0
|
|
|
|
0
|
return split /\s*,\s*/, $s if wantarray; |
119
|
|
|
|
|
|
|
|
120
|
0
|
|
|
|
|
0
|
$s =~ s/\s+//g; # remove spaces to normalize "south, 0" to "south,0" |
121
|
0
|
|
|
|
|
0
|
$s; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
sub style |
125
|
|
|
|
|
|
|
{ |
126
|
2812
|
|
|
2812
|
1
|
4701
|
my $self = shift; |
127
|
|
|
|
|
|
|
|
128
|
2812
|
100
|
|
|
|
16210
|
$self->{att}->{style} || $self->attribute('style'); |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub name |
132
|
|
|
|
|
|
|
{ |
133
|
|
|
|
|
|
|
# returns actually the label |
134
|
539
|
|
|
539
|
1
|
1945
|
my $self = shift; |
135
|
|
|
|
|
|
|
|
136
|
539
|
100
|
|
|
|
3202
|
$self->{att}->{label} || ''; |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
############################################################################# |
140
|
|
|
|
|
|
|
# cell management - used by the cell-based layouter |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub _cells |
143
|
|
|
|
|
|
|
{ |
144
|
|
|
|
|
|
|
# return all the cells this edge currently occupies |
145
|
6
|
|
|
6
|
|
17
|
my $self = shift; |
146
|
|
|
|
|
|
|
|
147
|
6
|
100
|
|
|
|
23
|
$self->{cells} = [] unless defined $self->{cells}; |
148
|
|
|
|
|
|
|
|
149
|
6
|
|
|
|
|
7
|
@{$self->{cells}}; |
|
6
|
|
|
|
|
33
|
|
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub _clear_cells |
153
|
|
|
|
|
|
|
{ |
154
|
|
|
|
|
|
|
# remove all belonging cells |
155
|
902
|
|
|
902
|
|
2452
|
my $self = shift; |
156
|
|
|
|
|
|
|
|
157
|
902
|
|
|
|
|
2715
|
$self->{cells} = []; |
158
|
|
|
|
|
|
|
|
159
|
902
|
|
|
|
|
1890
|
$self; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub _unplace |
163
|
|
|
|
|
|
|
{ |
164
|
|
|
|
|
|
|
# Take an edge, and remove all the cells it covers from the cells area |
165
|
0
|
|
|
0
|
|
0
|
my ($self, $cells) = @_; |
166
|
|
|
|
|
|
|
|
167
|
0
|
0
|
|
|
|
0
|
print STDERR "# clearing path from $self->{from}->{name} to $self->{to}->{name}\n" if $self->{debug}; |
168
|
|
|
|
|
|
|
|
169
|
0
|
|
|
|
|
0
|
for my $key (@{$self->{cells}}) |
|
0
|
|
|
|
|
0
|
|
170
|
|
|
|
|
|
|
{ |
171
|
|
|
|
|
|
|
# XXX TODO: handle crossed edges differently (from CROSS => HOR or VER) |
172
|
|
|
|
|
|
|
# free in our cells area |
173
|
0
|
|
|
|
|
0
|
delete $cells->{$key}; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
0
|
|
|
|
|
0
|
$self->clear_cells(); |
177
|
|
|
|
|
|
|
|
178
|
0
|
|
|
|
|
0
|
$self; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
sub _distance |
182
|
|
|
|
|
|
|
{ |
183
|
|
|
|
|
|
|
# estimate the distance from SRC to DST node |
184
|
0
|
|
|
0
|
|
0
|
my ($self) = @_; |
185
|
|
|
|
|
|
|
|
186
|
0
|
|
|
|
|
0
|
my $src = $self->{from}; |
187
|
0
|
|
|
|
|
0
|
my $dst = $self->{to}; |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
# one of them not yet placed? |
190
|
0
|
0
|
0
|
|
|
0
|
return 100000 unless defined $src->{x} && defined $dst->{x}; |
191
|
|
|
|
|
|
|
|
192
|
0
|
|
|
|
|
0
|
my $cells = $self->{graph}->{cells}; |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# get all the starting positions |
195
|
|
|
|
|
|
|
# distance = 1: slots, generate starting types, the direction is shifted |
196
|
|
|
|
|
|
|
# by 90° counter-clockwise |
197
|
|
|
|
|
|
|
|
198
|
0
|
|
|
|
|
0
|
my @start = $src->_near_places($cells, 1, undef, undef, $src->_shift(-90) ); |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# potential stop positions |
201
|
0
|
|
|
|
|
0
|
my @stop = $dst->_near_places($cells, 1); # distance = 1: slots |
202
|
|
|
|
|
|
|
|
203
|
0
|
|
|
|
|
0
|
my ($s_p,@ss_p) = $self->port('start'); |
204
|
0
|
|
|
|
|
0
|
my ($e_p,@ee_p) = $self->port('end'); |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# the edge has a port description, limiting the start places |
207
|
0
|
0
|
|
|
|
0
|
@start = $src->_allowed_places( \@start, $src->_allow( $s_p, @ss_p ), 3) |
208
|
|
|
|
|
|
|
if defined $s_p; |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# the edge has a port description, limiting the stop places |
211
|
0
|
0
|
|
|
|
0
|
@stop = $dst->_allowed_places( \@stop, $dst->_allow( $e_p, @ee_p ), 3) |
212
|
|
|
|
|
|
|
if defined $e_p; |
213
|
|
|
|
|
|
|
|
214
|
0
|
|
|
|
|
0
|
my $stop = scalar @stop; |
215
|
|
|
|
|
|
|
|
216
|
0
|
0
|
0
|
|
|
0
|
return 0 unless @stop > 0 && @start > 0; # no free slots on one node? |
217
|
|
|
|
|
|
|
|
218
|
0
|
|
|
|
|
0
|
my $lowest; |
219
|
|
|
|
|
|
|
|
220
|
0
|
|
|
|
|
0
|
my $i = 0; |
221
|
0
|
|
|
|
|
0
|
while ($i < scalar @start) |
222
|
|
|
|
|
|
|
{ |
223
|
0
|
|
|
|
|
0
|
my $sx = $start[$i]; my $sy = $start[$i+1]; $i += 2; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# for each start point, calculate the distance to each stop point, then use |
226
|
|
|
|
|
|
|
# the smallest as value |
227
|
|
|
|
|
|
|
|
228
|
0
|
|
|
|
|
0
|
for (my $u = 0; $u < $stop; $u += 2) |
229
|
|
|
|
|
|
|
{ |
230
|
0
|
|
|
|
|
0
|
my $dist = Graph::Easy::_astar_distance($sx,$sy, $stop[$u], $stop[$u+1]); |
231
|
0
|
0
|
0
|
|
|
0
|
$lowest = $dist if !defined $lowest || $dist < $lowest; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
0
|
|
|
|
|
0
|
$lowest; |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub _add_cell |
239
|
|
|
|
|
|
|
{ |
240
|
|
|
|
|
|
|
# add a cell to the list of cells this edge covers. If $after is a ref |
241
|
|
|
|
|
|
|
# to a cell, then the new cell will be inserted right after this cell. |
242
|
|
|
|
|
|
|
# if after is defined, but not a ref, the new cell will be inserted |
243
|
|
|
|
|
|
|
# at the specified position. |
244
|
2135
|
|
|
2135
|
|
6621
|
my ($self, $cell, $after, $before) = @_; |
245
|
|
|
|
|
|
|
|
246
|
2135
|
100
|
|
|
|
6224
|
$self->{cells} = [] unless defined $self->{cells}; |
247
|
2135
|
|
|
|
|
3634
|
my $cells = $self->{cells}; |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# if both are defined, but belong to different edges, just ignore $before: |
250
|
2135
|
100
|
100
|
|
|
6007
|
$before = undef if ref($before) && $before->{edge} != $self; |
251
|
2135
|
50
|
66
|
|
|
11474
|
$after = undef if ref($after) && $after->{edge} != $self; |
252
|
2135
|
50
|
66
|
|
|
9686
|
if (!defined $after && ref($before)) |
253
|
|
|
|
|
|
|
{ |
254
|
0
|
|
|
|
|
0
|
$after = $before; $before = undef; |
|
0
|
|
|
|
|
0
|
|
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
2135
|
100
|
|
|
|
4506
|
if (defined $after) |
258
|
|
|
|
|
|
|
{ |
259
|
|
|
|
|
|
|
# insert the new cell right after $after |
260
|
81
|
|
|
|
|
119
|
my $ofs = $after; |
261
|
81
|
100
|
100
|
|
|
540
|
if (ref($after) && !ref($before)) |
|
|
100
|
66
|
|
|
|
|
262
|
|
|
|
|
|
|
{ |
263
|
|
|
|
|
|
|
# insert after $after |
264
|
5
|
|
|
|
|
13
|
$ofs = 1; |
265
|
5
|
|
|
|
|
12
|
for my $cell (@$cells) |
266
|
|
|
|
|
|
|
{ |
267
|
9
|
100
|
|
|
|
28
|
last if $cell == $after; |
268
|
4
|
|
|
|
|
10
|
$ofs++; |
269
|
|
|
|
|
|
|
} |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
elsif (ref($after) && ref($before)) |
272
|
|
|
|
|
|
|
{ |
273
|
|
|
|
|
|
|
# insert between after and before (or before/after for "reversed edges) |
274
|
46
|
|
|
|
|
129
|
$ofs = 0; |
275
|
46
|
|
|
|
|
52
|
my $found = 0; |
276
|
46
|
|
|
|
|
155
|
while ($ofs < scalar @$cells - 1) # 0,1,2,3 => 0 .. 2 |
277
|
|
|
|
|
|
|
{ |
278
|
178
|
|
|
|
|
229
|
my $c1 = $cells->[$ofs]; |
279
|
178
|
|
|
|
|
264
|
my $c2 = $cells->[$ofs+1]; |
280
|
178
|
|
|
|
|
176
|
$ofs++; |
281
|
178
|
100
|
100
|
|
|
1331
|
$found++, last if (($c1 == $after && $c2 == $before) || |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
282
|
|
|
|
|
|
|
($c1 == $before && $c2 == $after)); |
283
|
|
|
|
|
|
|
} |
284
|
46
|
100
|
|
|
|
105
|
if (!$found) |
285
|
|
|
|
|
|
|
{ |
286
|
|
|
|
|
|
|
# XXX TODO: last effort |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
# insert after $after |
289
|
1
|
|
|
|
|
3
|
$ofs = 1; |
290
|
1
|
|
|
|
|
3
|
for my $cell (@$cells) |
291
|
|
|
|
|
|
|
{ |
292
|
3
|
100
|
|
|
|
10
|
last if $cell == $after; |
293
|
2
|
|
|
|
|
4
|
$ofs++; |
294
|
|
|
|
|
|
|
} |
295
|
1
|
|
|
|
|
3
|
$found++; |
296
|
|
|
|
|
|
|
} |
297
|
46
|
50
|
|
|
|
88
|
$self->_croak("Could not find $after and $before") unless $found; |
298
|
|
|
|
|
|
|
} |
299
|
81
|
|
|
|
|
200
|
splice (@$cells, $ofs, 0, $cell); |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
else |
302
|
|
|
|
|
|
|
{ |
303
|
|
|
|
|
|
|
# insert new cell at the end |
304
|
2054
|
|
|
|
|
4389
|
push @$cells, $cell; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
|
307
|
2135
|
|
|
|
|
7842
|
$cell->_update_boundaries(); |
308
|
|
|
|
|
|
|
|
309
|
2135
|
|
|
|
|
5441
|
$self; |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
############################################################################# |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub from |
315
|
|
|
|
|
|
|
{ |
316
|
2
|
|
|
2
|
1
|
5
|
my $self = shift; |
317
|
|
|
|
|
|
|
|
318
|
2
|
|
|
|
|
9
|
$self->{from}; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub to |
322
|
|
|
|
|
|
|
{ |
323
|
2
|
|
|
2
|
1
|
9
|
my $self = shift; |
324
|
|
|
|
|
|
|
|
325
|
2
|
|
|
|
|
9
|
$self->{to}; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
sub nodes |
329
|
|
|
|
|
|
|
{ |
330
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
331
|
|
|
|
|
|
|
|
332
|
0
|
|
|
|
|
0
|
($self->{from}, $self->{to}); |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
sub start_at |
336
|
|
|
|
|
|
|
{ |
337
|
|
|
|
|
|
|
# move the edge's start point from the current node to the given node |
338
|
11
|
|
|
11
|
1
|
19
|
my ($self, $node) = @_; |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# if not a node yet, or not part of this graph, make into one proper node |
341
|
11
|
|
|
|
|
49
|
$node = $self->{graph}->add_node($node); |
342
|
|
|
|
|
|
|
|
343
|
11
|
50
|
33
|
|
|
113
|
$self->_croak("start_at() needs a node object, but got $node") |
344
|
|
|
|
|
|
|
unless ref($node) && $node->isa('Graph::Easy::Node'); |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
# A => A => nothing to do |
347
|
11
|
50
|
|
|
|
39
|
return $node if $self->{from} == $node; |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
# delete self at A |
350
|
11
|
|
|
|
|
45
|
delete $self->{from}->{edges}->{ $self->{id} }; |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# set "from" to B |
353
|
11
|
|
|
|
|
27
|
$self->{from} = $node; |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
# add to B |
356
|
11
|
|
|
|
|
44
|
$self->{from}->{edges}->{ $self->{id} } = $self; |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# invalidate layout |
359
|
11
|
50
|
|
|
|
39
|
$self->{graph}->{score} = undef if ref($self->{graph}); |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# return new start point |
362
|
11
|
|
|
|
|
29
|
$node; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
sub end_at |
366
|
|
|
|
|
|
|
{ |
367
|
|
|
|
|
|
|
# move the edge's end point from the current node to the given node |
368
|
11
|
|
|
11
|
1
|
16
|
my ($self, $node) = @_; |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
# if not a node yet, or not part of this graph, make into one proper node |
371
|
11
|
|
|
|
|
42
|
$node = $self->{graph}->add_node($node); |
372
|
|
|
|
|
|
|
|
373
|
11
|
50
|
33
|
|
|
74
|
$self->_croak("start_at() needs a node object, but got $node") |
374
|
|
|
|
|
|
|
unless ref($node) && $node->isa('Graph::Easy::Node'); |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# A => A => nothing to do |
377
|
11
|
50
|
|
|
|
33
|
return $node if $self->{to} == $node; |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
# delete self at A |
380
|
11
|
|
|
|
|
35
|
delete $self->{to}->{edges}->{ $self->{id} }; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
# set "to" to B |
383
|
11
|
|
|
|
|
19
|
$self->{to} = $node; |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# add to node B |
386
|
11
|
|
|
|
|
39
|
$self->{to}->{edges}->{ $self->{id} } = $self; |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
# invalidate layout |
389
|
11
|
50
|
|
|
|
36
|
$self->{graph}->{score} = undef if ref($self->{graph}); |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# return new end point |
392
|
11
|
|
|
|
|
27
|
$node; |
393
|
|
|
|
|
|
|
} |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
sub edge_flow |
396
|
|
|
|
|
|
|
{ |
397
|
|
|
|
|
|
|
# return the flow at this edge or '' if the edge itself doesn't have a flow |
398
|
886
|
|
|
886
|
1
|
2442
|
my $self = shift; |
399
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
# our flow comes from ourselves |
401
|
886
|
|
|
|
|
1984
|
my $flow = $self->{att}->{flow}; |
402
|
886
|
100
|
|
|
|
4165
|
$flow = $self->raw_attribute('flow') unless defined $flow; |
403
|
|
|
|
|
|
|
|
404
|
886
|
|
|
|
|
3801
|
$flow; |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
sub flow |
408
|
|
|
|
|
|
|
{ |
409
|
|
|
|
|
|
|
# return the flow at this edge (including inheriting flow from node) |
410
|
1333
|
|
|
1333
|
1
|
2539
|
my ($self) = @_; |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# print STDERR "# flow from $self->{from}->{name} to $self->{to}->{name}\n"; |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
# our flow comes from ourselves |
415
|
1333
|
|
|
|
|
3704
|
my $flow = $self->{att}->{flow}; |
416
|
|
|
|
|
|
|
# or maybe our class |
417
|
1333
|
100
|
|
|
|
7649
|
$flow = $self->raw_attribute('flow') unless defined $flow; |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
# if the edge doesn't have a flow, maybe the node has a default out flow |
420
|
1333
|
100
|
|
|
|
10993
|
$flow = $self->{from}->{att}->{flow} if !defined $flow; |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# if that didn't work out either, use the parents flows |
423
|
1333
|
100
|
|
|
|
6147
|
$flow = $self->parent()->attribute('flow') if !defined $flow; |
424
|
|
|
|
|
|
|
# or finally, the default "east": |
425
|
1333
|
50
|
|
|
|
3946
|
$flow = 90 if !defined $flow; |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# absolute flow does not depend on the in-flow, so can return early |
428
|
1333
|
100
|
|
|
|
5139
|
return $flow if $flow =~ /^(0|90|180|270)\z/; |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
# in-flow comes from our "from" node |
431
|
1316
|
|
|
|
|
6018
|
my $in = $self->{from}->flow(); |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
# print STDERR "# in: $self->{from}->{name} = $in\n"; |
434
|
|
|
|
|
|
|
|
435
|
1316
|
|
|
|
|
12289
|
my $out = $self->{graph}->_flow_as_direction($in,$flow); |
436
|
1316
|
|
|
|
|
3891
|
$out; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
sub port |
440
|
|
|
|
|
|
|
{ |
441
|
4438
|
|
|
4438
|
1
|
8385
|
my ($self, $which) = @_; |
442
|
|
|
|
|
|
|
|
443
|
4438
|
50
|
|
|
|
26635
|
$self->_croak("'$which' must be one of 'start' or 'end' in port()") unless $which =~ /^(start|end)/; |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
# our flow comes from ourselves |
446
|
4438
|
|
|
|
|
13437
|
my $sp = $self->attribute($which); |
447
|
|
|
|
|
|
|
|
448
|
4438
|
100
|
66
|
|
|
33039
|
return (undef,undef) unless defined $sp && $sp ne ''; |
449
|
|
|
|
|
|
|
|
450
|
512
|
|
|
|
|
3358
|
my ($side, $port) = split /\s*,\s*/, $sp; |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
# if absolut direction, return as is |
453
|
512
|
|
|
|
|
2093
|
my $s = Graph::Easy->_direction_as_side($side); |
454
|
|
|
|
|
|
|
|
455
|
512
|
100
|
|
|
|
1436
|
if (defined $s) |
456
|
|
|
|
|
|
|
{ |
457
|
311
|
100
|
|
|
|
892
|
my @rc = ($s); push @rc, $port if defined $port; |
|
311
|
|
|
|
|
777
|
|
458
|
311
|
|
|
|
|
1620
|
return @rc; |
459
|
|
|
|
|
|
|
} |
460
|
|
|
|
|
|
|
|
461
|
|
|
|
|
|
|
# in_flow comes from our "from" node |
462
|
201
|
50
|
|
|
|
283
|
my $in = 90; $in = $self->{from}->flow() if ref($self->{from}); |
|
201
|
|
|
|
|
965
|
|
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
# turn left in "south" etc: |
465
|
201
|
|
|
|
|
809
|
$s = Graph::Easy->_flow_as_side($in,$side); |
466
|
|
|
|
|
|
|
|
467
|
201
|
100
|
|
|
|
380
|
my @rc = ($s); push @rc, $port if defined $port; |
|
201
|
|
|
|
|
503
|
|
468
|
201
|
|
|
|
|
797
|
@rc; |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
sub flip |
472
|
|
|
|
|
|
|
{ |
473
|
|
|
|
|
|
|
# swap from and to for this edge |
474
|
2
|
|
|
2
|
1
|
11
|
my ($self) = @_; |
475
|
|
|
|
|
|
|
|
476
|
2
|
|
|
|
|
10
|
($self->{from}, $self->{to}) = ($self->{to}, $self->{from}); |
477
|
|
|
|
|
|
|
|
478
|
|
|
|
|
|
|
# invalidate layout |
479
|
2
|
50
|
|
|
|
14
|
$self->{graph}->{score} = undef if ref($self->{graph}); |
480
|
|
|
|
|
|
|
|
481
|
2
|
|
|
|
|
7
|
$self; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
sub as_ascii |
485
|
|
|
|
|
|
|
{ |
486
|
1594
|
|
|
1594
|
1
|
4003
|
my ($self, $x,$y) = @_; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
# invisible nodes, or very small ones |
489
|
1594
|
100
|
100
|
|
|
13204
|
return '' if $self->{w} == 0 || $self->{h} == 0; |
490
|
|
|
|
|
|
|
|
491
|
1477
|
|
|
|
|
5915
|
my $fb = $self->_framebuffer($self->{w}, $self->{h}); |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
########################################################################### |
494
|
|
|
|
|
|
|
# "draw" the label into the framebuffer (e.g. the edge and the text) |
495
|
1477
|
|
|
|
|
5137
|
$self->_draw_label($fb, $x, $y, ''); |
496
|
|
|
|
|
|
|
|
497
|
1477
|
|
|
|
|
13521
|
join ("\n", @$fb); |
498
|
|
|
|
|
|
|
} |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
sub as_txt |
501
|
|
|
|
|
|
|
{ |
502
|
622
|
|
|
622
|
1
|
10059
|
require Graph::Easy::As_ascii; |
503
|
|
|
|
|
|
|
|
504
|
622
|
|
|
|
|
2650
|
_as_txt(@_); |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
1; |
508
|
|
|
|
|
|
|
__END__ |