line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
############################################################################# |
2
|
|
|
|
|
|
|
# Find paths from node to node in a Manhattan-style grid via A*. |
3
|
|
|
|
|
|
|
# |
4
|
|
|
|
|
|
|
# (c) by Tels - part of Graph::Easy |
5
|
|
|
|
|
|
|
############################################################################# |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
package Graph::Easy::Layout::Scout; |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
$VERSION = '0.75'; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
############################################################################# |
12
|
|
|
|
|
|
|
############################################################################# |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
package Graph::Easy; |
15
|
|
|
|
|
|
|
|
16
|
49
|
|
|
49
|
|
253
|
use strict; |
|
49
|
|
|
|
|
106
|
|
|
49
|
|
|
|
|
2182
|
|
17
|
49
|
|
|
49
|
|
247
|
use warnings; |
|
49
|
|
|
|
|
172
|
|
|
49
|
|
|
|
|
1831
|
|
18
|
49
|
|
|
49
|
|
268
|
use Graph::Easy::Node::Cell; |
|
49
|
|
|
|
|
100
|
|
|
49
|
|
|
|
|
4499
|
|
19
|
49
|
|
|
|
|
163668
|
use Graph::Easy::Edge::Cell qw/ |
20
|
|
|
|
|
|
|
EDGE_SHORT_E EDGE_SHORT_W EDGE_SHORT_N EDGE_SHORT_S |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
EDGE_SHORT_BD_EW EDGE_SHORT_BD_NS |
23
|
|
|
|
|
|
|
EDGE_SHORT_UN_EW EDGE_SHORT_UN_NS |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
EDGE_START_E EDGE_START_W EDGE_START_N EDGE_START_S |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
EDGE_END_E EDGE_END_W EDGE_END_N EDGE_END_S |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
EDGE_N_E EDGE_N_W EDGE_S_E EDGE_S_W |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
EDGE_N_W_S EDGE_S_W_N EDGE_E_S_W EDGE_W_S_E |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
EDGE_LOOP_NORTH EDGE_LOOP_SOUTH EDGE_LOOP_WEST EDGE_LOOP_EAST |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
EDGE_HOR EDGE_VER EDGE_HOLE |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
EDGE_S_E_W EDGE_N_E_W EDGE_E_N_S EDGE_W_N_S |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
EDGE_LABEL_CELL |
40
|
|
|
|
|
|
|
EDGE_TYPE_MASK |
41
|
|
|
|
|
|
|
EDGE_ARROW_MASK |
42
|
|
|
|
|
|
|
EDGE_FLAG_MASK |
43
|
|
|
|
|
|
|
EDGE_START_MASK |
44
|
|
|
|
|
|
|
EDGE_END_MASK |
45
|
|
|
|
|
|
|
EDGE_NO_M_MASK |
46
|
49
|
|
|
49
|
|
267
|
/; |
|
49
|
|
|
|
|
103
|
|
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
############################################################################# |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
# mapping edge type (HOR, VER, NW etc) and dx/dy to startpoint flag |
51
|
|
|
|
|
|
|
my $start_points = { |
52
|
|
|
|
|
|
|
# [ dx == 1, dx == -1, dy == 1, dy == -1 , |
53
|
|
|
|
|
|
|
# dx == 1, dx == -1, dy == 1, dy == -1 ] |
54
|
|
|
|
|
|
|
EDGE_HOR() => [ EDGE_START_W, EDGE_START_E, 0, 0 , |
55
|
|
|
|
|
|
|
EDGE_END_E, EDGE_END_W, 0, 0, ], |
56
|
|
|
|
|
|
|
EDGE_VER() => [ 0, 0, EDGE_START_N, EDGE_START_S , |
57
|
|
|
|
|
|
|
0, 0, EDGE_END_S, EDGE_END_N, ], |
58
|
|
|
|
|
|
|
EDGE_N_E() => [ 0, EDGE_START_E, EDGE_START_N, 0 , |
59
|
|
|
|
|
|
|
EDGE_END_E, 0, 0, EDGE_END_N, ], |
60
|
|
|
|
|
|
|
EDGE_N_W() => [ EDGE_START_W, 0, EDGE_START_N, 0 , |
61
|
|
|
|
|
|
|
0, EDGE_END_W, 0, EDGE_END_N, ], |
62
|
|
|
|
|
|
|
EDGE_S_E() => [ 0, EDGE_START_E, 0, EDGE_START_S , |
63
|
|
|
|
|
|
|
EDGE_END_E, 0, EDGE_END_S, 0, ], |
64
|
|
|
|
|
|
|
EDGE_S_W() => [ EDGE_START_W, 0, 0, EDGE_START_S , |
65
|
|
|
|
|
|
|
0, EDGE_END_W, EDGE_END_S, 0, ], |
66
|
|
|
|
|
|
|
}; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
my $start_to_end = { |
69
|
|
|
|
|
|
|
EDGE_START_W() => EDGE_END_W(), |
70
|
|
|
|
|
|
|
EDGE_START_E() => EDGE_END_E(), |
71
|
|
|
|
|
|
|
EDGE_START_S() => EDGE_END_S(), |
72
|
|
|
|
|
|
|
EDGE_START_N() => EDGE_END_N(), |
73
|
|
|
|
|
|
|
}; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub _end_points |
76
|
|
|
|
|
|
|
{ |
77
|
|
|
|
|
|
|
# modify last field of path to be the correct endpoint; and the first field |
78
|
|
|
|
|
|
|
# to be the correct startpoint: |
79
|
59
|
|
|
59
|
|
140
|
my ($self, $edge, $coords, $dx, $dy) = @_; |
80
|
|
|
|
|
|
|
|
81
|
59
|
100
|
|
|
|
237
|
return $coords if $edge->undirected(); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# there are two cases (for each dx and dy) |
84
|
57
|
|
|
|
|
101
|
my $i = 0; # index 0,1 |
85
|
57
|
|
|
|
|
80
|
my $co = 2; |
86
|
57
|
|
|
|
|
73
|
my $case; |
87
|
|
|
|
|
|
|
|
88
|
57
|
|
|
|
|
129
|
for my $d ($dx,$dy,$dx,$dy) |
89
|
|
|
|
|
|
|
{ |
90
|
228
|
100
|
|
|
|
1069
|
next if $d == 0; |
91
|
|
|
|
|
|
|
|
92
|
204
|
|
|
|
|
291
|
my $type = $coords->[$co] & EDGE_TYPE_MASK; |
93
|
|
|
|
|
|
|
|
94
|
204
|
100
|
|
|
|
267
|
$case = 0; $case = 1 if $d == -1; |
|
204
|
|
|
|
|
443
|
|
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# modify first/last cell |
97
|
204
|
|
|
|
|
543
|
my $t = $start_points->{ $type }->[ $case + $i ]; |
98
|
|
|
|
|
|
|
# on bidirectional edges, turn START_X into END_X |
99
|
204
|
100
|
100
|
|
|
511
|
$t = $start_to_end->{$t} || $t if $edge->{bidirectional}; |
100
|
|
|
|
|
|
|
|
101
|
204
|
|
|
|
|
311
|
$coords->[$co] += $t; |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
} continue { |
104
|
228
|
|
|
|
|
240
|
$i += 2; # index 2,3, 4,5 etc |
105
|
228
|
100
|
|
|
|
547
|
$co = -1 if $i == 4; # modify now last cell |
106
|
|
|
|
|
|
|
} |
107
|
57
|
|
|
|
|
296
|
$coords; |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub _find_path |
111
|
|
|
|
|
|
|
{ |
112
|
|
|
|
|
|
|
# Try to find a path between two nodes. $options contains direction |
113
|
|
|
|
|
|
|
# preferences. Returns a list of cells like: |
114
|
|
|
|
|
|
|
# [ $x,$y,$type, $x1,$y1,$type1, ...] |
115
|
905
|
|
|
905
|
|
7616
|
my ($self, $src, $dst, $edge) = @_; |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# one node pointing back to itself? |
118
|
905
|
100
|
|
|
|
2793
|
if ($src == $dst) |
119
|
|
|
|
|
|
|
{ |
120
|
31
|
|
|
|
|
145
|
my $rc = $self->_find_path_loop($src,$edge); |
121
|
31
|
50
|
|
|
|
157
|
return $rc unless scalar @$rc == 0; |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# If one of the two nodes is bigger than 1 cell, use _find_path_astar(), |
125
|
|
|
|
|
|
|
# because it automatically handles all the possibilities: |
126
|
874
|
100
|
100
|
|
|
4017
|
return $self->_find_path_astar($edge) |
|
|
|
100
|
|
|
|
|
127
|
|
|
|
|
|
|
if ($src->is_multicelled() || $dst->is_multicelled() || $edge->has_ports()); |
128
|
|
|
|
|
|
|
|
129
|
742
|
|
|
|
|
2654
|
my ($x0, $y0) = ($src->{x}, $src->{y}); |
130
|
742
|
|
|
|
|
2076
|
my ($x1, $y1) = ($dst->{x}, $dst->{y}); |
131
|
742
|
|
|
|
|
1480
|
my $dx = ($x1 - $x0) <=> 0; |
132
|
742
|
|
|
|
|
1213
|
my $dy = ($y1 - $y0) <=> 0; |
133
|
|
|
|
|
|
|
|
134
|
742
|
|
|
|
|
1336
|
my $cells = $self->{cells}; |
135
|
742
|
|
|
|
|
992
|
my @coords; |
136
|
742
|
|
|
|
|
1634
|
my ($x,$y) = ($x0,$y0); # starting pos |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
########################################################################### |
139
|
|
|
|
|
|
|
# below follow some shortcuts for easy things like straight paths: |
140
|
|
|
|
|
|
|
|
141
|
742
|
50
|
|
|
|
2042
|
print STDERR "# dx,dy: $dx,$dy\n" if $self->{debug}; |
142
|
|
|
|
|
|
|
|
143
|
742
|
100
|
100
|
|
|
3898
|
if ($dx == 0 || $dy == 0) |
144
|
|
|
|
|
|
|
{ |
145
|
|
|
|
|
|
|
# try straight path to target: |
146
|
|
|
|
|
|
|
|
147
|
679
|
50
|
|
|
|
8612
|
print STDERR "# $src->{x},$src->{y} => $dst->{x},$dst->{y} - trying short path\n" if $self->{debug}; |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# distance to node: |
150
|
679
|
|
|
|
|
1182
|
my $dx1 = ($x1 - $x0); |
151
|
679
|
|
|
|
|
1192
|
my $dy1 = ($y1 - $y0); |
152
|
679
|
|
|
|
|
1683
|
($x,$y) = ($x0+$dx,$y0+$dy); # starting pos |
153
|
|
|
|
|
|
|
|
154
|
679
|
100
|
100
|
|
|
2766
|
if ((abs($dx1) == 2) || (abs($dy1) == 2)) |
155
|
|
|
|
|
|
|
{ |
156
|
579
|
100
|
|
|
|
2865
|
if (!exists ($cells->{"$x,$y"})) |
157
|
|
|
|
|
|
|
{ |
158
|
|
|
|
|
|
|
# a single step for this edge: |
159
|
567
|
|
|
|
|
1270
|
my $type = EDGE_LABEL_CELL; |
160
|
|
|
|
|
|
|
# short path |
161
|
567
|
100
|
|
|
|
2708
|
if ($edge->bidirectional()) |
|
|
100
|
|
|
|
|
|
162
|
|
|
|
|
|
|
{ |
163
|
5
|
100
|
|
|
|
24
|
$type += EDGE_SHORT_BD_EW if $dy == 0; |
164
|
5
|
100
|
|
|
|
22
|
$type += EDGE_SHORT_BD_NS if $dx == 0; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
elsif ($edge->undirected()) |
167
|
|
|
|
|
|
|
{ |
168
|
13
|
100
|
|
|
|
46
|
$type += EDGE_SHORT_UN_EW if $dy == 0; |
169
|
13
|
100
|
|
|
|
43
|
$type += EDGE_SHORT_UN_NS if $dx == 0; |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
else |
172
|
|
|
|
|
|
|
{ |
173
|
549
|
100
|
66
|
|
|
2952
|
$type += EDGE_SHORT_E if ($dx == 1 && $dy == 0); |
174
|
549
|
100
|
100
|
|
|
2194
|
$type += EDGE_SHORT_S if ($dx == 0 && $dy == 1); |
175
|
549
|
100
|
66
|
|
|
2071
|
$type += EDGE_SHORT_W if ($dx == -1 && $dy == 0); |
176
|
549
|
100
|
100
|
|
|
2098
|
$type += EDGE_SHORT_N if ($dx == 0 && $dy == -1); |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
# if one of the end points of the edge is of shape 'edge' |
179
|
|
|
|
|
|
|
# remove end/start flag |
180
|
567
|
100
|
50
|
|
|
2749
|
if (($edge->{to}->attribute('shape') ||'') eq 'edge') |
181
|
|
|
|
|
|
|
{ |
182
|
|
|
|
|
|
|
# we only need to remove one start point, namely the one at the "end" |
183
|
4
|
100
|
|
|
|
17
|
if ($dx > 0) |
|
|
50
|
|
|
|
|
|
184
|
|
|
|
|
|
|
{ |
185
|
3
|
|
|
|
|
7
|
$type &= ~EDGE_START_E; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
elsif ($dx < 0) |
188
|
|
|
|
|
|
|
{ |
189
|
0
|
|
|
|
|
0
|
$type &= ~EDGE_START_W; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
} |
192
|
567
|
100
|
50
|
|
|
4339
|
if (($edge->{from}->attribute('shape') ||'') eq 'edge') |
193
|
|
|
|
|
|
|
{ |
194
|
3
|
|
|
|
|
5
|
$type &= ~EDGE_START_MASK; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
567
|
|
|
|
|
3456
|
return [ $x, $y, $type ]; # return a short EDGE |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
|
201
|
112
|
100
|
|
|
|
245
|
my $type = EDGE_HOR; $type = EDGE_VER if $dx == 0; # - or | |
|
112
|
|
|
|
|
380
|
|
202
|
112
|
|
|
|
|
197
|
my $done = 0; |
203
|
112
|
|
|
|
|
212
|
my $label_done = 0; |
204
|
112
|
|
|
|
|
167
|
while (3 < 5) # endless loop |
205
|
|
|
|
|
|
|
{ |
206
|
|
|
|
|
|
|
# Since we do not handle crossings here, A* will be tried if we hit an |
207
|
|
|
|
|
|
|
# edge in this test. |
208
|
282
|
100
|
|
|
|
1253
|
$done = 1, last if exists $cells->{"$x,$y"}; # cell already full |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# the first cell gets the label |
211
|
181
|
100
|
|
|
|
235
|
my $t = $type; $t += EDGE_LABEL_CELL if $label_done++ == 0; |
|
181
|
|
|
|
|
378
|
|
212
|
|
|
|
|
|
|
|
213
|
181
|
|
|
|
|
698
|
push @coords, $x, $y, $t; # good one, is free |
214
|
181
|
|
|
|
|
197
|
$x += $dx; $y += $dy; # next field |
|
181
|
|
|
|
|
195
|
|
215
|
181
|
100
|
100
|
|
|
679
|
last if ($x == $x1) && ($y == $y1); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
112
|
100
|
|
|
|
411
|
if ($done == 0) |
219
|
|
|
|
|
|
|
{ |
220
|
11
|
50
|
|
|
|
35
|
print STDERR "# success for ", scalar @coords / 3, " steps in path\n" if $self->{debug}; |
221
|
|
|
|
|
|
|
# return all fields of path |
222
|
11
|
|
|
|
|
65
|
return $self->_end_points($edge, \@coords, $dx, $dy); |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
} # end else straight path try |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
########################################################################### |
228
|
|
|
|
|
|
|
# Try paths with one bend: |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
# ($dx != 0 && $dy != 0) => path with one bend |
231
|
|
|
|
|
|
|
# XXX TODO: |
232
|
|
|
|
|
|
|
# This could be handled by A*, too, but it would be probably a bit slower. |
233
|
|
|
|
|
|
|
else |
234
|
|
|
|
|
|
|
{ |
235
|
|
|
|
|
|
|
# straight path not possible, since x0 != x1 AND y0 != y1 |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
# " |" "| " |
238
|
|
|
|
|
|
|
# try first "--+" (aka hor => ver), then "+---" (aka ver => hor) |
239
|
63
|
|
|
|
|
145
|
my $done = 0; |
240
|
|
|
|
|
|
|
|
241
|
63
|
50
|
|
|
|
212
|
print STDERR "# bend path from $x,$y\n" if $self->{debug}; |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
# try hor => ver |
244
|
63
|
|
|
|
|
128
|
my $type = EDGE_HOR; |
245
|
|
|
|
|
|
|
|
246
|
63
|
|
|
|
|
94
|
my $label = 0; # attach label? |
247
|
63
|
50
|
50
|
|
|
459
|
$label = 1 if ref($edge) && ($edge->label()||'') eq ''; # no label? |
|
|
|
33
|
|
|
|
|
248
|
63
|
|
|
|
|
112
|
$x += $dx; |
249
|
63
|
|
|
|
|
192
|
while ($x != $x1) |
250
|
|
|
|
|
|
|
{ |
251
|
67
|
100
|
|
|
|
306
|
$done++, last if exists $cells->{"$x,$y"}; # cell already full |
252
|
51
|
50
|
|
|
|
153
|
print STDERR "# at $x,$y\n" if $self->{debug}; |
253
|
51
|
50
|
|
|
|
85
|
my $t = $type; $t += EDGE_LABEL_CELL if $label++ == 0; |
|
51
|
|
|
|
|
143
|
|
254
|
51
|
|
|
|
|
138
|
push @coords, $x, $y, $t; # good one, is free |
255
|
51
|
|
|
|
|
128
|
$x += $dx; # next field |
256
|
|
|
|
|
|
|
}; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# check the bend itself |
259
|
63
|
100
|
|
|
|
246
|
$done++ if exists $cells->{"$x,$y"}; # cell already full |
260
|
|
|
|
|
|
|
|
261
|
63
|
100
|
|
|
|
186
|
if ($done == 0) |
262
|
|
|
|
|
|
|
{ |
263
|
27
|
|
|
|
|
113
|
my $type_bend = _astar_edge_type ($x-$dx,$y, $x,$y, $x,$y+$dy); |
264
|
|
|
|
|
|
|
|
265
|
27
|
|
|
|
|
109
|
push @coords, $x, $y, $type_bend; # put in bend |
266
|
27
|
50
|
|
|
|
86
|
print STDERR "# at $x,$y\n" if $self->{debug}; |
267
|
27
|
|
|
|
|
38
|
$y += $dy; |
268
|
27
|
|
|
|
|
34
|
$type = EDGE_VER; |
269
|
27
|
|
|
|
|
101
|
while ($y != $y1) |
270
|
|
|
|
|
|
|
{ |
271
|
19
|
50
|
|
|
|
70
|
$done++, last if exists $cells->{"$x,$y"}; # cell already full |
272
|
19
|
50
|
|
|
|
53
|
print STDERR "# at $x,$y\n" if $self->{debug}; |
273
|
19
|
|
|
|
|
77
|
push @coords, $x, $y, $type; # good one, is free |
274
|
19
|
|
|
|
|
56
|
$y += $dy; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
63
|
100
|
|
|
|
158
|
if ($done != 0) |
279
|
|
|
|
|
|
|
{ |
280
|
36
|
|
|
|
|
65
|
$done = 0; |
281
|
|
|
|
|
|
|
# try ver => hor |
282
|
36
|
50
|
|
|
|
118
|
print STDERR "# hm, now trying first vertical, then horizontal\n" if $self->{debug}; |
283
|
36
|
|
|
|
|
59
|
$type = EDGE_VER; |
284
|
|
|
|
|
|
|
|
285
|
36
|
|
|
|
|
72
|
@coords = (); # drop old version |
286
|
36
|
|
|
|
|
84
|
($x,$y) = ($x0, $y0 + $dy); # starting pos |
287
|
36
|
|
|
|
|
106
|
while ($y != $y1) |
288
|
|
|
|
|
|
|
{ |
289
|
67
|
100
|
|
|
|
275
|
$done++, last if exists $cells->{"$x,$y"}; # cell already full |
290
|
59
|
50
|
|
|
|
143
|
print STDERR "# at $x,$y\n" if $self->{debug}; |
291
|
59
|
|
|
|
|
140
|
push @coords, $x, $y, $type; # good one, is free |
292
|
59
|
|
|
|
|
137
|
$y += $dy; # next field |
293
|
|
|
|
|
|
|
}; |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
# check the bend itself |
296
|
36
|
100
|
|
|
|
141
|
$done++ if exists $cells->{"$x,$y"}; # cell already full |
297
|
|
|
|
|
|
|
|
298
|
36
|
100
|
|
|
|
123
|
if ($done == 0) |
299
|
|
|
|
|
|
|
{ |
300
|
25
|
|
|
|
|
120
|
my $type_bend = _astar_edge_type ($x,$y-$dy, $x,$y, $x+$dx,$y); |
301
|
|
|
|
|
|
|
|
302
|
25
|
|
|
|
|
75
|
push @coords, $x, $y, $type_bend; # put in bend |
303
|
25
|
50
|
|
|
|
83
|
print STDERR "# at $x,$y\n" if $self->{debug}; |
304
|
25
|
|
|
|
|
36
|
$x += $dx; |
305
|
25
|
|
|
|
|
45
|
my $label = 0; # attach label? |
306
|
25
|
50
|
|
|
|
94
|
$label = 1 if $edge->label() eq ''; # no label? |
307
|
25
|
|
|
|
|
49
|
$type = EDGE_HOR; |
308
|
25
|
|
|
|
|
69
|
while ($x != $x1) |
309
|
|
|
|
|
|
|
{ |
310
|
31
|
100
|
|
|
|
110
|
$done++, last if exists $cells->{"$x,$y"}; # cell already full |
311
|
27
|
50
|
|
|
|
87
|
print STDERR "# at $x,$y\n" if $self->{debug}; |
312
|
27
|
50
|
|
|
|
52
|
my $t = $type; $t += EDGE_LABEL_CELL if $label++ == 0; |
|
27
|
|
|
|
|
66
|
|
313
|
27
|
|
|
|
|
90
|
push @coords, $x, $y, $t; # good one, is free |
314
|
27
|
|
|
|
|
82
|
$x += $dx; |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
} |
318
|
|
|
|
|
|
|
|
319
|
63
|
100
|
|
|
|
163
|
if ($done == 0) |
320
|
|
|
|
|
|
|
{ |
321
|
48
|
50
|
|
|
|
131
|
print STDERR "# success for ", scalar @coords / 3, " steps in path\n" if $self->{debug}; |
322
|
|
|
|
|
|
|
# return all fields of path |
323
|
48
|
|
|
|
|
339
|
return $self->_end_points($edge, \@coords, $dx, $dy); |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
|
326
|
15
|
50
|
|
|
|
64
|
print STDERR "# no success\n" if $self->{debug}; |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
} # end path with $dx and $dy |
329
|
|
|
|
|
|
|
|
330
|
116
|
|
|
|
|
576
|
$self->_find_path_astar($edge); # try generic approach as last hope |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
sub _find_path_loop |
334
|
|
|
|
|
|
|
{ |
335
|
|
|
|
|
|
|
# find a path from one node back to itself |
336
|
31
|
|
|
31
|
|
58
|
my ($self, $src, $edge) = @_; |
337
|
|
|
|
|
|
|
|
338
|
31
|
50
|
|
|
|
119
|
print STDERR "# Finding looping path from $src->{name} to $src->{name}\n" if $self->{debug}; |
339
|
|
|
|
|
|
|
|
340
|
31
|
|
|
|
|
70
|
my ($n, $cells, $d, $type, $loose) = @_; |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# get a list of all places |
343
|
|
|
|
|
|
|
|
344
|
31
|
|
|
|
|
205
|
my @places = $src->_near_places( |
345
|
|
|
|
|
|
|
$self->{cells}, 1, [ |
346
|
|
|
|
|
|
|
EDGE_LOOP_EAST, |
347
|
|
|
|
|
|
|
EDGE_LOOP_SOUTH, |
348
|
|
|
|
|
|
|
EDGE_LOOP_WEST, |
349
|
|
|
|
|
|
|
EDGE_LOOP_NORTH, |
350
|
|
|
|
|
|
|
], 0, 90); |
351
|
|
|
|
|
|
|
|
352
|
31
|
|
|
|
|
153
|
my $flow = $src->flow(); |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# We cannot use _shuffle_dir() here, because self-loops |
355
|
|
|
|
|
|
|
# are tried in a different order: |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# the default (east) |
358
|
31
|
|
|
|
|
211
|
my $index = [ |
359
|
|
|
|
|
|
|
EDGE_LOOP_NORTH, |
360
|
|
|
|
|
|
|
EDGE_LOOP_SOUTH, |
361
|
|
|
|
|
|
|
EDGE_LOOP_WEST, |
362
|
|
|
|
|
|
|
EDGE_LOOP_EAST, |
363
|
|
|
|
|
|
|
]; |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# west |
366
|
31
|
100
|
|
|
|
111
|
$index = [ |
367
|
|
|
|
|
|
|
EDGE_LOOP_SOUTH, |
368
|
|
|
|
|
|
|
EDGE_LOOP_NORTH, |
369
|
|
|
|
|
|
|
EDGE_LOOP_EAST, |
370
|
|
|
|
|
|
|
EDGE_LOOP_WEST, |
371
|
|
|
|
|
|
|
] if $flow == 270; |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# north |
374
|
31
|
100
|
|
|
|
235
|
$index = [ |
375
|
|
|
|
|
|
|
EDGE_LOOP_WEST, |
376
|
|
|
|
|
|
|
EDGE_LOOP_EAST, |
377
|
|
|
|
|
|
|
EDGE_LOOP_SOUTH, |
378
|
|
|
|
|
|
|
EDGE_LOOP_NORTH, |
379
|
|
|
|
|
|
|
] if $flow == 0; |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# south |
382
|
31
|
100
|
|
|
|
105
|
$index = [ |
383
|
|
|
|
|
|
|
EDGE_LOOP_EAST, |
384
|
|
|
|
|
|
|
EDGE_LOOP_WEST, |
385
|
|
|
|
|
|
|
EDGE_LOOP_NORTH, |
386
|
|
|
|
|
|
|
EDGE_LOOP_SOUTH, |
387
|
|
|
|
|
|
|
] if $flow == 180; |
388
|
|
|
|
|
|
|
|
389
|
31
|
|
|
|
|
75
|
for my $this_try (@$index) |
390
|
|
|
|
|
|
|
{ |
391
|
59
|
|
|
|
|
80
|
my $idx = 0; |
392
|
59
|
|
|
|
|
180
|
while ($idx < @places) |
393
|
|
|
|
|
|
|
{ |
394
|
114
|
50
|
|
|
|
250
|
print STDERR "# Trying $places[$idx+0],$places[$idx+1]\n" if $self->{debug}; |
395
|
114
|
100
|
|
|
|
275
|
next unless $places[$idx+2] == $this_try; |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# build a path from the returned piece |
398
|
31
|
|
|
|
|
115
|
my @rc = ($places[$idx], $places[$idx+1], $places[$idx+2]); |
399
|
|
|
|
|
|
|
|
400
|
31
|
50
|
|
|
|
100
|
print STDERR "# Trying $rc[0],$rc[1]\n" if $self->{debug}; |
401
|
|
|
|
|
|
|
|
402
|
31
|
50
|
|
|
|
173
|
next unless $self->_path_is_clear(\@rc); |
403
|
|
|
|
|
|
|
|
404
|
31
|
50
|
|
|
|
107
|
print STDERR "# Found looping path\n" if $self->{debug}; |
405
|
31
|
|
|
|
|
150
|
return \@rc; |
406
|
83
|
|
|
|
|
222
|
} continue { $idx += 3; } |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
0
|
|
|
|
|
0
|
[]; # no path found |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
############################################################################# |
413
|
|
|
|
|
|
|
############################################################################# |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
# This package represents a simple/cheap/fast heap: |
416
|
|
|
|
|
|
|
package Graph::Easy::Heap; |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
require Graph::Easy::Base; |
419
|
|
|
|
|
|
|
our @ISA = qw/Graph::Easy::Base/; |
420
|
|
|
|
|
|
|
|
421
|
49
|
|
|
49
|
|
596
|
use strict; |
|
49
|
|
|
|
|
269
|
|
|
49
|
|
|
|
|
130602
|
|
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub _init |
424
|
|
|
|
|
|
|
{ |
425
|
745
|
|
|
745
|
|
2168
|
my ($self,$args) = @_; |
426
|
|
|
|
|
|
|
|
427
|
745
|
|
|
|
|
5709
|
$self->{_heap} = [ ]; |
428
|
|
|
|
|
|
|
|
429
|
745
|
|
|
|
|
2969
|
$self; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
sub add |
433
|
|
|
|
|
|
|
{ |
434
|
|
|
|
|
|
|
# add one element to the heap |
435
|
7154
|
|
|
7154
|
|
12090
|
my ($self,$elem) = @_; |
436
|
|
|
|
|
|
|
|
437
|
7154
|
|
|
|
|
11799
|
my $heap = $self->{_heap}; |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
# heap empty? |
440
|
7154
|
100
|
|
|
|
30312
|
if (@$heap == 0) |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
441
|
|
|
|
|
|
|
{ |
442
|
1262
|
|
|
|
|
3802
|
push @$heap, $elem; |
443
|
|
|
|
|
|
|
} |
444
|
|
|
|
|
|
|
# smaller than first elem? |
445
|
|
|
|
|
|
|
elsif ($elem->[0] < $heap->[0]->[0]) |
446
|
|
|
|
|
|
|
{ |
447
|
|
|
|
|
|
|
#print STDERR "# $elem->[0] is smaller then first elem $heap->[0]->[0] (with ", scalar @$heap," elems on heap)\n"; |
448
|
1522
|
|
|
|
|
3594
|
unshift @$heap, $elem; |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
# bigger than or equal to last elem? |
451
|
|
|
|
|
|
|
elsif ($elem->[0] > $heap->[-1]->[0]) |
452
|
|
|
|
|
|
|
{ |
453
|
|
|
|
|
|
|
#print STDERR "# $elem->[0] is bigger then last elem $heap->[-1]->[0] (with ", scalar @$heap," elems on heap)\n"; |
454
|
838
|
|
|
|
|
1806
|
push @$heap, $elem; |
455
|
|
|
|
|
|
|
} |
456
|
|
|
|
|
|
|
else |
457
|
|
|
|
|
|
|
{ |
458
|
|
|
|
|
|
|
# insert the elem at the right position |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
# if we have less than X elements, use linear search |
461
|
3532
|
|
|
|
|
5372
|
my $el = $elem->[0]; |
462
|
3532
|
100
|
|
|
|
7548
|
if (scalar @$heap < 10) |
463
|
|
|
|
|
|
|
{ |
464
|
1910
|
|
|
|
|
2895
|
my $i = 0; |
465
|
1910
|
|
|
|
|
16047
|
for my $e (@$heap) |
466
|
|
|
|
|
|
|
{ |
467
|
5808
|
100
|
|
|
|
13958
|
if ($e->[0] > $el) |
468
|
|
|
|
|
|
|
{ |
469
|
862
|
|
|
|
|
2965
|
splice (@$heap, $i, 0, $elem); # insert $elem |
470
|
862
|
|
|
|
|
2493
|
return undef; |
471
|
|
|
|
|
|
|
} |
472
|
4946
|
|
|
|
|
7874
|
$i++; |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
# else, append at the end |
475
|
1048
|
|
|
|
|
2829
|
push @$heap, $elem; |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
else |
478
|
|
|
|
|
|
|
{ |
479
|
|
|
|
|
|
|
# use binary search |
480
|
1622
|
|
|
|
|
2041
|
my $l = 0; my $r = scalar @$heap; |
|
1622
|
|
|
|
|
1974
|
|
481
|
1622
|
|
|
|
|
3264
|
while (($r - $l) > 2) |
482
|
|
|
|
|
|
|
{ |
483
|
7550
|
|
|
|
|
10363
|
my $m = int((($r - $l) / 2) + $l); |
484
|
|
|
|
|
|
|
# print "l=$l r=$r m=$m el=$el heap=$heap->[$m]->[0]\n"; |
485
|
7550
|
100
|
|
|
|
13143
|
if ($heap->[$m]->[0] <= $el) |
486
|
|
|
|
|
|
|
{ |
487
|
5040
|
|
|
|
|
9787
|
$l = $m; |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
else |
490
|
|
|
|
|
|
|
{ |
491
|
2510
|
|
|
|
|
4919
|
$r = $m; |
492
|
|
|
|
|
|
|
} |
493
|
|
|
|
|
|
|
} |
494
|
1622
|
|
|
|
|
3030
|
while ($l < @$heap) |
495
|
|
|
|
|
|
|
{ |
496
|
3951
|
100
|
|
|
|
7606
|
if ($heap->[$l]->[0] > $el) |
497
|
|
|
|
|
|
|
{ |
498
|
1438
|
|
|
|
|
2331
|
splice (@$heap, $l, 0, $elem); # insert $elem |
499
|
1438
|
|
|
|
|
2714
|
return undef; |
500
|
|
|
|
|
|
|
} |
501
|
2513
|
|
|
|
|
4566
|
$l++; |
502
|
|
|
|
|
|
|
} |
503
|
|
|
|
|
|
|
# else, append at the end |
504
|
184
|
|
|
|
|
342
|
push @$heap, $elem; |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
} |
507
|
4854
|
|
|
|
|
15681
|
undef; |
508
|
|
|
|
|
|
|
} |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
sub elements |
511
|
|
|
|
|
|
|
{ |
512
|
523
|
|
|
523
|
|
1794
|
scalar @{$_[0]->{_heap}}; |
|
523
|
|
|
|
|
3840
|
|
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
sub extract_top |
516
|
|
|
|
|
|
|
{ |
517
|
|
|
|
|
|
|
# remove and return the top elemt |
518
|
6131
|
|
|
6131
|
|
24012
|
shift @{$_[0]->{_heap}}; |
|
6131
|
|
|
|
|
26636
|
|
519
|
|
|
|
|
|
|
} |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
sub delete |
522
|
|
|
|
|
|
|
{ |
523
|
|
|
|
|
|
|
# Find an element by $x,$y and delete it |
524
|
0
|
|
|
0
|
|
0
|
my ($self, $x, $y) = @_; |
525
|
|
|
|
|
|
|
|
526
|
0
|
|
|
|
|
0
|
my $heap = $self->{_heap}; |
527
|
|
|
|
|
|
|
|
528
|
0
|
|
|
|
|
0
|
my $i = 0; |
529
|
0
|
|
|
|
|
0
|
for my $e (@$heap) |
530
|
|
|
|
|
|
|
{ |
531
|
0
|
0
|
0
|
|
|
0
|
if ($e->[1] == $x && $e->[2] == $y) |
532
|
|
|
|
|
|
|
{ |
533
|
0
|
|
|
|
|
0
|
splice (@$heap, $i, 1); |
534
|
0
|
|
|
|
|
0
|
return; |
535
|
|
|
|
|
|
|
} |
536
|
0
|
|
|
|
|
0
|
$i++; |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
0
|
|
|
|
|
0
|
$self; |
540
|
|
|
|
|
|
|
} |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
sub sort_sub |
543
|
|
|
|
|
|
|
{ |
544
|
502
|
|
|
502
|
|
15610
|
my ($self) = shift; |
545
|
|
|
|
|
|
|
|
546
|
502
|
|
|
|
|
1965
|
$self->{_sort} = shift; |
547
|
|
|
|
|
|
|
} |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
############################################################################# |
550
|
|
|
|
|
|
|
############################################################################# |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
package Graph::Easy; |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
# Generic pathfinding via the A* algorithm: |
555
|
|
|
|
|
|
|
# See http://bloodgate.com/perl/graph/astar.html for some background. |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
sub _astar_modifier |
558
|
|
|
|
|
|
|
{ |
559
|
|
|
|
|
|
|
# calculate the cost for the path at cell x1,y1 |
560
|
6564
|
|
|
6564
|
|
12314
|
my ($x1,$y1,$x,$y,$px,$py, $cells) = @_; |
561
|
|
|
|
|
|
|
|
562
|
6564
|
|
|
|
|
7649
|
my $add = 1; |
563
|
|
|
|
|
|
|
|
564
|
6564
|
50
|
|
|
|
13473
|
if (defined $x1) |
565
|
|
|
|
|
|
|
{ |
566
|
6564
|
|
|
|
|
10232
|
my $xy = "$x1,$y1"; |
567
|
|
|
|
|
|
|
# add a harsh penalty for crossing an edge, meaning we can travel many |
568
|
|
|
|
|
|
|
# fields to go around. |
569
|
6564
|
100
|
100
|
|
|
27739
|
$add += 30 if ref($cells->{$xy}) && $cells->{$xy}->isa('Graph::Easy::Edge'); |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
6564
|
100
|
|
|
|
14841
|
if (defined $px) |
573
|
|
|
|
|
|
|
{ |
574
|
|
|
|
|
|
|
# see whether the new position $x1,$y1 is a continuation from $px,$py => $x,$y |
575
|
|
|
|
|
|
|
# e.g. if from we go down from $px,$py to $x,$y, then anything else then $x,$y+1 will |
576
|
|
|
|
|
|
|
# get a penalty |
577
|
6563
|
|
|
|
|
9802
|
my $dx1 = ($px-$x) <=> 0; |
578
|
6563
|
|
|
|
|
8732
|
my $dy1 = ($py-$y) <=> 0; |
579
|
6563
|
|
|
|
|
7759
|
my $dx2 = ($x-$x1) <=> 0; |
580
|
6563
|
|
|
|
|
7615
|
my $dy2 = ($y-$y1) <=> 0; |
581
|
6563
|
100
|
100
|
|
|
27194
|
$add += 6 unless $dx1 == $dx2 || $dy1 == $dy2; |
582
|
|
|
|
|
|
|
} |
583
|
6564
|
|
|
|
|
13901
|
$add; |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
sub _astar_distance |
587
|
|
|
|
|
|
|
{ |
588
|
|
|
|
|
|
|
# calculate the manhattan distance between x1,y1 and x2,y2 |
589
|
|
|
|
|
|
|
# my ($x1,$y1,$x2,$y2) = @_; |
590
|
|
|
|
|
|
|
|
591
|
20860
|
|
|
20860
|
|
28747
|
my $dx = abs($_[2] - $_[0]); |
592
|
20860
|
|
|
|
|
26345
|
my $dy = abs($_[3] - $_[1]); |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
# plus 1 because we need to go around one corner if $dx != 0 && $dx != 0 |
595
|
20860
|
100
|
100
|
|
|
83625
|
$dx++ if $dx != 0 && $dy != 0; |
596
|
|
|
|
|
|
|
|
597
|
20860
|
|
|
|
|
38074
|
$dx + $dy; |
598
|
|
|
|
|
|
|
} |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
my $edge_type = { |
601
|
|
|
|
|
|
|
'0,1,-1,0' => EDGE_N_W, |
602
|
|
|
|
|
|
|
'0,1,0,1' => EDGE_VER, |
603
|
|
|
|
|
|
|
'0,1,1,0' => EDGE_N_E, |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
'-1,0,0,-1' => EDGE_N_E, |
606
|
|
|
|
|
|
|
'-1,0,-1,0' => EDGE_HOR, |
607
|
|
|
|
|
|
|
'-1,0,0,1' => EDGE_S_E, |
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
'0,-1,-1,0' => EDGE_S_W, |
610
|
|
|
|
|
|
|
'0,-1,0,-1' => EDGE_VER, |
611
|
|
|
|
|
|
|
'0,-1,1,0' => EDGE_S_E, |
612
|
|
|
|
|
|
|
|
613
|
|
|
|
|
|
|
'1,0,0,-1' => EDGE_N_W, |
614
|
|
|
|
|
|
|
'1,0,1,0' => EDGE_HOR, |
615
|
|
|
|
|
|
|
'1,0,0,1' => EDGE_S_W, |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
# loops (left-right-left etc) |
618
|
|
|
|
|
|
|
'0,-1,0,1' => EDGE_N_W_S, |
619
|
|
|
|
|
|
|
'0,1,0,-1' => EDGE_S_W_N, |
620
|
|
|
|
|
|
|
'1,0,-1,0' => EDGE_E_S_W, |
621
|
|
|
|
|
|
|
'-1,0,1,0' => EDGE_W_S_E, |
622
|
|
|
|
|
|
|
}; |
623
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
sub _astar_edge_type |
625
|
|
|
|
|
|
|
{ |
626
|
|
|
|
|
|
|
# from three consecutive positions calculate the edge type (VER, HOR, N_W etc) |
627
|
1219
|
|
|
1219
|
|
2082
|
my ($x,$y, $x1,$y1, $x2, $y2) = @_; |
628
|
|
|
|
|
|
|
|
629
|
1219
|
|
|
|
|
1777
|
my $dx1 = ($x1 - $x) <=> 0; |
630
|
1219
|
|
|
|
|
2048
|
my $dy1 = ($y1 - $y) <=> 0; |
631
|
|
|
|
|
|
|
|
632
|
1219
|
|
|
|
|
1871
|
my $dx2 = ($x2 - $x1) <=> 0; |
633
|
1219
|
|
|
|
|
1555
|
my $dy2 = ($y2 - $y1) <=> 0; |
634
|
|
|
|
|
|
|
|
635
|
|
|
|
|
|
|
# in some cases we get (0,-1,0,0), so set the missing parts |
636
|
1219
|
100
|
100
|
|
|
3881
|
($dx2,$dy2) = ($dx1,$dy1) if $dx2 == 0 && $dy2 == 0; |
637
|
|
|
|
|
|
|
# can this case happen? |
638
|
1219
|
50
|
66
|
|
|
3837
|
($dx1,$dy1) = ($dx2,$dy2) if $dx1 == 0 && $dy1 == 0; |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
# return correct type depending on differences |
641
|
1219
|
50
|
|
|
|
6892
|
$edge_type->{"$dx1,$dy1,$dx2,$dy2"} || EDGE_HOR; |
642
|
|
|
|
|
|
|
} |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
sub _astar_near_nodes |
645
|
|
|
|
|
|
|
{ |
646
|
|
|
|
|
|
|
# return possible next nodes from $nx,$ny |
647
|
3202
|
|
|
3202
|
|
5841
|
my ($self, $nx, $ny, $cells, $closed, $min_x, $min_y, $max_x, $max_y) = @_; |
648
|
|
|
|
|
|
|
|
649
|
3202
|
|
|
|
|
5196
|
my @places = (); |
650
|
|
|
|
|
|
|
|
651
|
3202
|
|
|
|
|
12085
|
my @tries = ( # ordered E,S,W,N: |
652
|
|
|
|
|
|
|
$nx + 1, $ny, # right |
653
|
|
|
|
|
|
|
$nx, $ny + 1, # down |
654
|
|
|
|
|
|
|
$nx - 1, $ny, # left |
655
|
|
|
|
|
|
|
$nx, $ny - 1, # up |
656
|
|
|
|
|
|
|
); |
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
# on crossings, only allow one direction (NS or EW) |
659
|
3202
|
|
|
|
|
6190
|
my $type = EDGE_CROSS; |
660
|
|
|
|
|
|
|
# including flags, because only flagless edges may be crossed |
661
|
3202
|
100
|
|
|
|
8746
|
$type = $cells->{"$nx,$ny"}->{type} if exists $cells->{"$nx,$ny"}; |
662
|
3202
|
100
|
|
|
|
8418
|
if ($type == EDGE_HOR) |
|
|
100
|
|
|
|
|
|
663
|
|
|
|
|
|
|
{ |
664
|
258
|
|
|
|
|
990
|
@tries = ( |
665
|
|
|
|
|
|
|
$nx, $ny + 1, # down |
666
|
|
|
|
|
|
|
$nx, $ny - 1, # up |
667
|
|
|
|
|
|
|
); |
668
|
|
|
|
|
|
|
} |
669
|
|
|
|
|
|
|
elsif ($type == EDGE_VER) |
670
|
|
|
|
|
|
|
{ |
671
|
71
|
|
|
|
|
319
|
@tries = ( |
672
|
|
|
|
|
|
|
$nx + 1, $ny, # right |
673
|
|
|
|
|
|
|
$nx - 1, $ny, # left |
674
|
|
|
|
|
|
|
); |
675
|
|
|
|
|
|
|
} |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
# This loop does not check whether the position is already open or not, |
678
|
|
|
|
|
|
|
# the caller will later check if the already-open position needs to be |
679
|
|
|
|
|
|
|
# replaced by one with a lower cost. |
680
|
|
|
|
|
|
|
|
681
|
3202
|
|
|
|
|
4028
|
my $i = 0; |
682
|
3202
|
|
|
|
|
7001
|
while ($i < @tries) |
683
|
|
|
|
|
|
|
{ |
684
|
12150
|
|
|
|
|
20820
|
my ($x,$y) = ($tries[$i], $tries[$i+1]); |
685
|
|
|
|
|
|
|
|
686
|
12150
|
50
|
|
|
|
29093
|
print STDERR "# $min_x,$min_y => $max_x,$max_y\n" if $self->{debug} > 2; |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
# drop cells outside our working space: |
689
|
12150
|
100
|
100
|
|
|
97736
|
next if $x < $min_x || $x > $max_x || $y < $min_y || $y > $max_y; |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
690
|
|
|
|
|
|
|
|
691
|
10839
|
|
|
|
|
21558
|
my $p = "$x,$y"; |
692
|
10839
|
50
|
|
|
|
22015
|
print STDERR "# examining pos $p\n" if $self->{debug} > 2; |
693
|
|
|
|
|
|
|
|
694
|
10839
|
100
|
|
|
|
24780
|
next if exists $closed->{$p}; |
695
|
|
|
|
|
|
|
|
696
|
6957
|
100
|
66
|
|
|
47020
|
if (exists $cells->{$p} && ref($cells->{$p}) && $cells->{$p}->isa('Graph::Easy::Edge')) |
|
|
|
100
|
|
|
|
|
697
|
|
|
|
|
|
|
{ |
698
|
|
|
|
|
|
|
# If the existing cell is an VER/HOR edge, then we may cross it |
699
|
956
|
|
|
|
|
2422
|
my $type = $cells->{$p}->{type}; # including flags, because only flagless edges |
700
|
|
|
|
|
|
|
# may be crossed |
701
|
|
|
|
|
|
|
|
702
|
956
|
100
|
100
|
|
|
4529
|
push @places, $x, $y if ($type == EDGE_HOR) || ($type == EDGE_VER); |
703
|
956
|
|
|
|
|
1645
|
next; |
704
|
|
|
|
|
|
|
} |
705
|
6001
|
100
|
|
|
|
12628
|
next if exists $cells->{$p}; # uncrossable cell |
706
|
|
|
|
|
|
|
|
707
|
5249
|
|
|
|
|
11917
|
push @places, $x, $y; |
708
|
|
|
|
|
|
|
|
709
|
12150
|
|
|
|
|
30740
|
} continue { $i += 2; } |
710
|
|
|
|
|
|
|
|
711
|
3202
|
|
|
|
|
24446
|
@places; |
712
|
|
|
|
|
|
|
} |
713
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
sub _astar_boundaries |
715
|
|
|
|
|
|
|
{ |
716
|
|
|
|
|
|
|
# Calculate boundaries for area that A* should not leave. |
717
|
242
|
|
|
242
|
|
480
|
my $self = shift; |
718
|
|
|
|
|
|
|
|
719
|
242
|
|
|
|
|
495
|
my $cache = $self->{cache}; |
720
|
|
|
|
|
|
|
|
721
|
242
|
100
|
|
|
|
1788
|
return ( $cache->{min_x}-1, $cache->{min_y}-1, |
722
|
|
|
|
|
|
|
$cache->{max_x}+1, $cache->{max_y}+1 ) if defined $cache->{min_x}; |
723
|
|
|
|
|
|
|
|
724
|
2
|
|
|
|
|
4
|
my ($min_x, $min_y, $max_x, $max_y); |
725
|
|
|
|
|
|
|
|
726
|
2
|
|
|
|
|
18
|
my $cells = $self->{cells}; |
727
|
|
|
|
|
|
|
|
728
|
2
|
|
|
|
|
7
|
$min_x = 10000000; |
729
|
2
|
|
|
|
|
5
|
$min_y = 10000000; |
730
|
2
|
|
|
|
|
2
|
$max_x = -10000000; |
731
|
2
|
|
|
|
|
4
|
$max_y = -10000000; |
732
|
|
|
|
|
|
|
|
733
|
2
|
|
|
|
|
12
|
for my $c (sort keys %$cells) |
734
|
|
|
|
|
|
|
{ |
735
|
4
|
|
|
|
|
13
|
my ($x,$y) = split /,/, $c; |
736
|
4
|
100
|
|
|
|
15
|
$min_x = $x if $x < $min_x; |
737
|
4
|
100
|
|
|
|
17
|
$min_y = $y if $y < $min_y; |
738
|
4
|
50
|
|
|
|
13
|
$max_x = $x if $x > $max_x; |
739
|
4
|
100
|
|
|
|
13
|
$max_y = $y if $y > $max_y; |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
|
742
|
2
|
50
|
|
|
|
7
|
print STDERR "# A* working space boundaries: $min_x, $min_y, $max_x, $max_y\n" if $self->{debug}; |
743
|
|
|
|
|
|
|
|
744
|
2
|
|
|
|
|
14
|
( $cache->{min_x}, $cache->{min_y}, $cache->{max_x}, $cache->{max_y} ) = |
745
|
|
|
|
|
|
|
($min_x, $min_y, $max_x, $max_y); |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
# make the area one bigger in each direction |
748
|
2
|
|
|
|
|
5
|
$min_x --; $min_y --; $max_x ++; $max_y ++; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
3
|
|
749
|
2
|
|
|
|
|
6
|
($min_x, $min_y, $max_x, $max_y); |
750
|
|
|
|
|
|
|
} |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
# on edge pieces, select start fields (left/right of a VER, above/below of a HOR etc) |
753
|
|
|
|
|
|
|
# contains also for each starting position the joint-type |
754
|
|
|
|
|
|
|
my $next_fields = |
755
|
|
|
|
|
|
|
{ |
756
|
|
|
|
|
|
|
EDGE_VER() => [ -1,0, EDGE_W_N_S, +1,0, EDGE_E_N_S ], |
757
|
|
|
|
|
|
|
EDGE_HOR() => [ 0,-1, EDGE_N_E_W, 0,+1, EDGE_S_E_W ], |
758
|
|
|
|
|
|
|
EDGE_N_E() => [ 0,+1, EDGE_E_N_S, -1,0, EDGE_N_E_W ], # |_ |
759
|
|
|
|
|
|
|
EDGE_N_W() => [ 0,+1, EDGE_W_N_S, +1,0, EDGE_N_E_W ], # _| |
760
|
|
|
|
|
|
|
EDGE_S_E() => [ 0,-1, EDGE_E_N_S, -1,0, EDGE_S_E_W ], |
761
|
|
|
|
|
|
|
EDGE_S_W() => [ 0,-1, EDGE_W_N_S, +1,0, EDGE_S_E_W ], |
762
|
|
|
|
|
|
|
}; |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
# on edge pieces, select end fields (left/right of a VER, above/below of a HOR etc) |
765
|
|
|
|
|
|
|
# contains also for each end position the joint-type |
766
|
|
|
|
|
|
|
my $prev_fields = |
767
|
|
|
|
|
|
|
{ |
768
|
|
|
|
|
|
|
EDGE_VER() => [ -1,0, EDGE_W_N_S, +1,0, EDGE_E_N_S ], |
769
|
|
|
|
|
|
|
EDGE_HOR() => [ 0,-1, EDGE_N_E_W, 0,+1, EDGE_S_E_W ], |
770
|
|
|
|
|
|
|
EDGE_N_E() => [ 0,+1, EDGE_E_N_S, -1,0, EDGE_N_E_W ], # |_ |
771
|
|
|
|
|
|
|
EDGE_N_W() => [ 0,+1, EDGE_W_N_S, +1,0, EDGE_N_E_W ], # _| |
772
|
|
|
|
|
|
|
EDGE_S_E() => [ 0,-1, EDGE_E_N_S, -1,0, EDGE_S_E_W ], |
773
|
|
|
|
|
|
|
EDGE_S_W() => [ 0,-1, EDGE_W_N_S, +1,0, EDGE_S_E_W ], |
774
|
|
|
|
|
|
|
}; |
775
|
|
|
|
|
|
|
|
776
|
49
|
|
|
49
|
|
458
|
use Graph::Easy::Util qw(ord_values); |
|
49
|
|
|
|
|
137
|
|
|
49
|
|
|
|
|
371197
|
|
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
sub _get_joints |
779
|
|
|
|
|
|
|
{ |
780
|
|
|
|
|
|
|
# from a list of shared, already placed edges, get possible start/end fields |
781
|
25
|
|
|
25
|
|
63
|
my ($self, $shared, $mask, $types, $cells, $next_fields) = @_; |
782
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
# XXX TODO: do not do this for edges with no free places for joints |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
# take each cell from all edges shared, already placed edges as start-point |
786
|
25
|
|
|
|
|
48
|
for my $e (@$shared) |
787
|
|
|
|
|
|
|
{ |
788
|
41
|
|
|
|
|
66
|
for my $c (@{$e->{cells}}) |
|
41
|
|
|
|
|
103
|
|
789
|
|
|
|
|
|
|
{ |
790
|
110
|
|
|
|
|
214
|
my $type = $c->{type} & EDGE_TYPE_MASK; |
791
|
|
|
|
|
|
|
|
792
|
110
|
100
|
|
|
|
295
|
next unless exists $next_fields->{ $type }; |
793
|
|
|
|
|
|
|
|
794
|
|
|
|
|
|
|
# don't consider end/start (depending on $mask) cells |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
# do not join EDGE_HOR or EDGE_VER, but join corner pieces |
797
|
94
|
100
|
100
|
|
|
586
|
next if ( ($type == EDGE_HOR()) || |
|
|
|
100
|
|
|
|
|
798
|
|
|
|
|
|
|
($type == EDGE_VER()) ) && |
799
|
|
|
|
|
|
|
($c->{type} & $mask); |
800
|
|
|
|
|
|
|
|
801
|
61
|
|
|
|
|
111
|
my $fields = $next_fields->{$type}; |
802
|
|
|
|
|
|
|
|
803
|
61
|
|
|
|
|
164
|
my ($px,$py) = ($c->{x},$c->{y}); |
804
|
61
|
|
|
|
|
80
|
my $i = 0; |
805
|
61
|
|
|
|
|
134
|
while ($i < @$fields) |
806
|
|
|
|
|
|
|
{ |
807
|
122
|
|
|
|
|
285
|
my ($sx,$sy, $jt) = ($fields->[$i], $fields->[$i+1], $fields->[$i+2]); |
808
|
122
|
|
|
|
|
220
|
$sx += $px; $sy += $py; $i += 3; |
|
122
|
|
|
|
|
192
|
|
|
122
|
|
|
|
|
143
|
|
809
|
122
|
|
|
|
|
220
|
my $sxsy = "$sx,$sy"; |
810
|
|
|
|
|
|
|
# don't add the field twice |
811
|
122
|
100
|
|
|
|
346
|
next if exists $cells->{$sxsy}; |
812
|
116
|
|
|
|
|
485
|
$cells->{$sxsy} = [ $sx, $sy, undef, $px, $py ]; |
813
|
|
|
|
|
|
|
# keep eventually set start/end points on the original cell |
814
|
116
|
|
|
|
|
487
|
$types->{$sxsy} = $jt + ($c->{type} & EDGE_FLAG_MASK); |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
} |
818
|
|
|
|
|
|
|
|
819
|
25
|
|
|
|
|
50
|
my @R; |
820
|
|
|
|
|
|
|
# convert hash to array |
821
|
25
|
|
|
|
|
141
|
for my $s (ord_values ( $cells )) |
822
|
|
|
|
|
|
|
{ |
823
|
116
|
|
|
|
|
416
|
push @R, @$s; |
824
|
|
|
|
|
|
|
} |
825
|
25
|
|
|
|
|
335
|
@R; |
826
|
|
|
|
|
|
|
} |
827
|
|
|
|
|
|
|
|
828
|
|
|
|
|
|
|
sub _join_edge |
829
|
|
|
|
|
|
|
{ |
830
|
|
|
|
|
|
|
# Find out whether an edge sharing an ending point with the source edge |
831
|
|
|
|
|
|
|
# runs alongside the source node, if so, convert it to a joint: |
832
|
31
|
|
|
31
|
|
76
|
my ($self, $node, $edge, $shared, $end) = @_; |
833
|
|
|
|
|
|
|
|
834
|
|
|
|
|
|
|
# we check the sides B,C,D and E for HOR and VER edge pices: |
835
|
|
|
|
|
|
|
# --D-- |
836
|
|
|
|
|
|
|
# | +---+ | |
837
|
|
|
|
|
|
|
# E | A | B |
838
|
|
|
|
|
|
|
# | +---+ | |
839
|
|
|
|
|
|
|
# --C-- |
840
|
|
|
|
|
|
|
|
841
|
31
|
|
|
|
|
96
|
my $flags = |
842
|
|
|
|
|
|
|
[ |
843
|
|
|
|
|
|
|
EDGE_W_N_S + EDGE_START_W, |
844
|
|
|
|
|
|
|
EDGE_N_E_W + EDGE_START_N, |
845
|
|
|
|
|
|
|
EDGE_E_N_S + EDGE_START_E, |
846
|
|
|
|
|
|
|
EDGE_S_E_W + EDGE_START_S, |
847
|
|
|
|
|
|
|
]; |
848
|
31
|
100
|
100
|
|
|
231
|
$flags = |
849
|
|
|
|
|
|
|
[ |
850
|
|
|
|
|
|
|
EDGE_W_N_S + EDGE_END_W, |
851
|
|
|
|
|
|
|
EDGE_N_E_W + EDGE_END_N, |
852
|
|
|
|
|
|
|
EDGE_E_N_S + EDGE_END_E, |
853
|
|
|
|
|
|
|
EDGE_S_E_W + EDGE_END_S, |
854
|
|
|
|
|
|
|
] if $end || $edge->{bidirectional}; |
855
|
|
|
|
|
|
|
|
856
|
31
|
|
|
|
|
74
|
my $cells = $self->{cells}; |
857
|
31
|
|
|
|
|
156
|
my @places = $node->_near_places($cells, 1, # distance 1 |
858
|
|
|
|
|
|
|
$flags, 'loose'); |
859
|
|
|
|
|
|
|
|
860
|
31
|
|
|
|
|
156
|
my $i = 0; |
861
|
31
|
|
|
|
|
171
|
while ($i < @places) |
862
|
|
|
|
|
|
|
{ |
863
|
108
|
|
|
|
|
196
|
my ($x,$y) = ($places[$i], $places[$i+1]); $i += 3; |
|
108
|
|
|
|
|
119
|
|
864
|
|
|
|
|
|
|
|
865
|
108
|
100
|
|
|
|
402
|
next unless exists $cells->{"$x,$y"}; # empty space? |
866
|
|
|
|
|
|
|
# found some cell, check that it is a EDGE_HOR or EDGE_VER |
867
|
13
|
|
|
|
|
43
|
my $cell = $cells->{"$x,$y"}; |
868
|
13
|
100
|
|
|
|
98
|
next unless $cell->isa('Graph::Easy::Edge::Cell'); |
869
|
|
|
|
|
|
|
|
870
|
8
|
|
|
|
|
20
|
my $cell_type = $cell->{type} & EDGE_TYPE_MASK; |
871
|
|
|
|
|
|
|
|
872
|
8
|
50
|
66
|
|
|
37
|
next unless $cell_type == EDGE_HOR || $cell_type == EDGE_VER; |
873
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
# the cell must belong to one of the shared edges |
875
|
8
|
|
|
|
|
13
|
my $e = $cell->{edge}; local $_; |
|
8
|
|
|
|
|
14
|
|
876
|
8
|
100
|
|
|
|
16
|
next unless scalar grep { $e == $_ } @$shared; |
|
10
|
|
|
|
|
50
|
|
877
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
# make the cell at the current pos a joint |
879
|
6
|
|
|
|
|
90
|
$cell->_make_joint($edge,$places[$i-1]); |
880
|
|
|
|
|
|
|
|
881
|
|
|
|
|
|
|
# The layouter will check that each edge has a cell, so add a dummy one to |
882
|
|
|
|
|
|
|
# $edge to make it happy: |
883
|
6
|
|
|
|
|
33
|
Graph::Easy::Edge::Cell->new( type => EDGE_HOLE, edge => $edge, x => $x, y => $y ); |
884
|
|
|
|
|
|
|
|
885
|
6
|
|
|
|
|
36
|
return []; # path is empty |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
25
|
|
|
|
|
91
|
undef; # did not find an edge cell that can be used as joint |
889
|
|
|
|
|
|
|
} |
890
|
|
|
|
|
|
|
|
891
|
|
|
|
|
|
|
sub _find_path_astar |
892
|
|
|
|
|
|
|
{ |
893
|
|
|
|
|
|
|
# Find a path with the A* algorithm for the given edge (from node A to B) |
894
|
248
|
|
|
248
|
|
498
|
my ($self,$edge) = @_; |
895
|
|
|
|
|
|
|
|
896
|
248
|
|
|
|
|
496
|
my $cells = $self->{cells}; |
897
|
248
|
|
|
|
|
571
|
my $src = $edge->{from}; |
898
|
248
|
|
|
|
|
529
|
my $dst = $edge->{to}; |
899
|
|
|
|
|
|
|
|
900
|
248
|
50
|
|
|
|
659
|
print STDERR "# A* from $src->{x},$src->{y} to $dst->{x},$dst->{y}\n" if $self->{debug}; |
901
|
|
|
|
|
|
|
|
902
|
248
|
|
|
|
|
1062
|
my $start_flags = [ |
903
|
|
|
|
|
|
|
EDGE_START_W, |
904
|
|
|
|
|
|
|
EDGE_START_N, |
905
|
|
|
|
|
|
|
EDGE_START_E, |
906
|
|
|
|
|
|
|
EDGE_START_S, |
907
|
|
|
|
|
|
|
]; |
908
|
|
|
|
|
|
|
|
909
|
248
|
|
|
|
|
1555
|
my $end_flags = [ |
910
|
|
|
|
|
|
|
EDGE_END_W, |
911
|
|
|
|
|
|
|
EDGE_END_N, |
912
|
|
|
|
|
|
|
EDGE_END_E, |
913
|
|
|
|
|
|
|
EDGE_END_S, |
914
|
|
|
|
|
|
|
]; |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
# if the target/source node is of shape "edge", remove the endpoint |
917
|
248
|
50
|
|
|
|
1137
|
if ( ($edge->{to}->attribute('shape')) eq 'edge') |
918
|
|
|
|
|
|
|
{ |
919
|
0
|
|
|
|
|
0
|
$end_flags = [ 0,0,0,0 ]; |
920
|
|
|
|
|
|
|
} |
921
|
248
|
50
|
|
|
|
1003
|
if ( ($edge->{from}->attribute('shape')) eq 'edge') |
922
|
|
|
|
|
|
|
{ |
923
|
0
|
|
|
|
|
0
|
$start_flags = [ 0,0,0,0 ]; |
924
|
|
|
|
|
|
|
} |
925
|
|
|
|
|
|
|
|
926
|
248
|
|
|
|
|
1167
|
my ($s_p,@ss_p) = $edge->port('start'); |
927
|
248
|
|
|
|
|
933
|
my ($e_p,@ee_p) = $edge->port('end'); |
928
|
248
|
|
|
|
|
443
|
my (@A, @B); # Start/Stop positions |
929
|
0
|
|
|
|
|
0
|
my @shared_start; |
930
|
0
|
|
|
|
|
0
|
my @shared_end; |
931
|
|
|
|
|
|
|
|
932
|
248
|
|
|
|
|
517
|
my $joint_type = {}; |
933
|
248
|
|
|
|
|
445
|
my $joint_type_end = {}; |
934
|
|
|
|
|
|
|
|
935
|
248
|
|
|
|
|
562
|
my $start_cells = {}; |
936
|
248
|
|
|
|
|
484
|
my $end_cells = {}; |
937
|
|
|
|
|
|
|
|
938
|
|
|
|
|
|
|
########################################################################### |
939
|
|
|
|
|
|
|
# end fields first (because maybe an edge runs alongside the node) |
940
|
|
|
|
|
|
|
|
941
|
|
|
|
|
|
|
# has a end point restriction |
942
|
248
|
100
|
100
|
|
|
1095
|
@shared_end = $edge->{to}->edges_at_port('end', $e_p, $ee_p[0]) if defined $e_p && @ee_p == 1; |
943
|
|
|
|
|
|
|
|
944
|
248
|
|
|
|
|
457
|
my @shared = (); |
945
|
|
|
|
|
|
|
# filter out all non-placed edges (this will also filter out $edge) |
946
|
248
|
|
|
|
|
562
|
for my $s (@shared_end) |
947
|
|
|
|
|
|
|
{ |
948
|
84
|
100
|
|
|
|
97
|
push @shared, $s if @{$s->{cells}} > 0; |
|
84
|
|
|
|
|
311
|
|
949
|
|
|
|
|
|
|
} |
950
|
|
|
|
|
|
|
|
951
|
248
|
|
|
|
|
517
|
my $per_field = 5; # for shared: x,y,undef, px,py |
952
|
248
|
100
|
|
|
|
653
|
if (@shared > 0) |
953
|
|
|
|
|
|
|
{ |
954
|
|
|
|
|
|
|
# more than one edge share the same end port, and one of the others was |
955
|
|
|
|
|
|
|
# already placed |
956
|
|
|
|
|
|
|
|
957
|
18
|
50
|
|
|
|
61
|
print STDERR "# edge from '$edge->{from}->{name}' to '$edge->{to}->{name}' shares end port with ", |
958
|
|
|
|
|
|
|
scalar @shared, " other edge(s)\n" if $self->{debug}; |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
# if there is one of the already-placed edges running alongside the src |
961
|
|
|
|
|
|
|
# node, we can just convert the field to a joint and be done |
962
|
18
|
|
|
|
|
91
|
my $path = $self->_join_edge($src,$edge,\@shared); |
963
|
18
|
100
|
|
|
|
98
|
return $path if $path; # already done? |
964
|
|
|
|
|
|
|
|
965
|
12
|
|
|
|
|
64
|
@B = $self->_get_joints(\@shared, EDGE_START_MASK, $joint_type_end, $end_cells, $prev_fields); |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
else |
968
|
|
|
|
|
|
|
{ |
969
|
|
|
|
|
|
|
# potential stop positions |
970
|
230
|
|
|
|
|
1227
|
@B = $dst->_near_places($cells, 1, $end_flags, 1); # distance = 1: slots |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
# the edge has a port description, limiting the end places |
973
|
230
|
100
|
|
|
|
863
|
@B = $dst->_allowed_places( \@B, $dst->_allow( $e_p, @ee_p ), 3) |
974
|
|
|
|
|
|
|
if defined $e_p; |
975
|
|
|
|
|
|
|
|
976
|
230
|
|
|
|
|
414
|
$per_field = 3; # x,y,type |
977
|
|
|
|
|
|
|
} |
978
|
|
|
|
|
|
|
|
979
|
242
|
50
|
|
|
|
622
|
return unless scalar @B > 0; # no free slots on target node? |
980
|
|
|
|
|
|
|
|
981
|
|
|
|
|
|
|
########################################################################### |
982
|
|
|
|
|
|
|
# start fields |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
# has a starting point restriction: |
985
|
242
|
100
|
100
|
|
|
1077
|
@shared_start = $edge->{from}->edges_at_port('start', $s_p, $ss_p[0]) if defined $s_p && @ss_p == 1; |
986
|
|
|
|
|
|
|
|
987
|
242
|
|
|
|
|
519
|
@shared = (); |
988
|
|
|
|
|
|
|
# filter out all non-placed edges (this will also filter out $edge) |
989
|
242
|
|
|
|
|
639
|
for my $s (@shared_start) |
990
|
|
|
|
|
|
|
{ |
991
|
62
|
100
|
|
|
|
76
|
push @shared, $s if @{$s->{cells}} > 0; |
|
62
|
|
|
|
|
215
|
|
992
|
|
|
|
|
|
|
} |
993
|
|
|
|
|
|
|
|
994
|
242
|
100
|
|
|
|
629
|
if (@shared > 0) |
995
|
|
|
|
|
|
|
{ |
996
|
|
|
|
|
|
|
# More than one edge share the same start port, and one of the others was |
997
|
|
|
|
|
|
|
# already placed, so we just run along until we catch it up with a joint: |
998
|
|
|
|
|
|
|
|
999
|
13
|
50
|
|
|
|
45
|
print STDERR "# edge from '$edge->{from}->{name}' to '$edge->{to}->{name}' shares start port with ", |
1000
|
|
|
|
|
|
|
scalar @shared, " other edge(s)\n" if $self->{debug}; |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
# if there is one of the already-placed edges running alongside the src |
1003
|
|
|
|
|
|
|
# node, we can just convert the field to a joint and be done |
1004
|
13
|
|
|
|
|
71
|
my $path = $self->_join_edge($dst, $edge, \@shared, 'end'); |
1005
|
13
|
50
|
|
|
|
40
|
return $path if $path; # already done? |
1006
|
|
|
|
|
|
|
|
1007
|
13
|
|
|
|
|
73
|
@A = $self->_get_joints(\@shared, EDGE_END_MASK, $joint_type, $start_cells, $next_fields); |
1008
|
|
|
|
|
|
|
} |
1009
|
|
|
|
|
|
|
else |
1010
|
|
|
|
|
|
|
{ |
1011
|
|
|
|
|
|
|
# from SRC to DST |
1012
|
|
|
|
|
|
|
|
1013
|
|
|
|
|
|
|
# get all the starting positions |
1014
|
|
|
|
|
|
|
# distance = 1: slots, generate starting types, the direction is shifted |
1015
|
|
|
|
|
|
|
# by 90° counter-clockwise |
1016
|
|
|
|
|
|
|
|
1017
|
229
|
100
|
|
|
|
390
|
my $s = $start_flags; $s = $end_flags if $edge->{bidirectional}; |
|
229
|
|
|
|
|
692
|
|
1018
|
229
|
|
|
|
|
1217
|
my @start = $src->_near_places($cells, 1, $s, 1, $src->_shift(-90) ); |
1019
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
# the edge has a port description, limiting the start places |
1021
|
229
|
100
|
|
|
|
1059
|
@start = $src->_allowed_places( \@start, $src->_allow( $s_p, @ss_p ), 3) |
1022
|
|
|
|
|
|
|
if defined $s_p; |
1023
|
|
|
|
|
|
|
|
1024
|
229
|
50
|
|
|
|
862
|
return unless @start > 0; # no free slots on start node? |
1025
|
|
|
|
|
|
|
|
1026
|
229
|
|
|
|
|
379
|
my $i = 0; |
1027
|
229
|
|
|
|
|
645
|
while ($i < scalar @start) |
1028
|
|
|
|
|
|
|
{ |
1029
|
1112
|
|
|
|
|
1511
|
my $sx = $start[$i]; my $sy = $start[$i+1]; my $type = $start[$i+2]; $i += 3; |
|
1112
|
|
|
|
|
1462
|
|
|
1112
|
|
|
|
|
1506
|
|
|
1112
|
|
|
|
|
1186
|
|
1030
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
# compute the field inside the node from where $sx,$sy is reached: |
1032
|
1112
|
|
|
|
|
1202
|
my $px = $sx; my $py = $sy; |
|
1112
|
|
|
|
|
1180
|
|
1033
|
1112
|
100
|
100
|
|
|
5440
|
if ($sy < $src->{y} || $sy >= $src->{y} + $src->{cy}) |
1034
|
|
|
|
|
|
|
{ |
1035
|
602
|
100
|
|
|
|
1373
|
$py = $sy + 1 if $sy < $src->{y}; # above |
1036
|
602
|
100
|
|
|
|
1369
|
$py = $sy - 1 if $sy > $src->{y}; # below |
1037
|
|
|
|
|
|
|
} |
1038
|
|
|
|
|
|
|
else |
1039
|
|
|
|
|
|
|
{ |
1040
|
510
|
100
|
|
|
|
1159
|
$px = $sx + 1 if $sx < $src->{x}; # right |
1041
|
510
|
100
|
|
|
|
1374
|
$px = $sx - 1 if $sx > $src->{x}; # left |
1042
|
|
|
|
|
|
|
} |
1043
|
|
|
|
|
|
|
|
1044
|
1112
|
|
|
|
|
5030
|
push @A, ($sx, $sy, $type, $px, $py); |
1045
|
|
|
|
|
|
|
} |
1046
|
|
|
|
|
|
|
} |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
########################################################################### |
1049
|
|
|
|
|
|
|
# use A* to finally find the path: |
1050
|
|
|
|
|
|
|
|
1051
|
242
|
|
|
|
|
1405
|
my $path = $self->_astar(\@A,\@B,$edge, $per_field); |
1052
|
|
|
|
|
|
|
|
1053
|
242
|
100
|
100
|
|
|
1946
|
if (@$path > 0 && keys %$start_cells > 0) |
1054
|
|
|
|
|
|
|
{ |
1055
|
|
|
|
|
|
|
# convert the edge piece of the starting edge-cell to a joint |
1056
|
13
|
|
|
|
|
51
|
my ($x, $y) = ($path->[0],$path->[1]); |
1057
|
13
|
|
|
|
|
35
|
my $xy = "$x,$y"; |
1058
|
13
|
|
|
|
|
30
|
my ($sx,$sy,$t,$px,$py) = @{$start_cells->{$xy}}; |
|
13
|
|
|
|
|
72
|
|
1059
|
|
|
|
|
|
|
|
1060
|
13
|
|
|
|
|
39
|
my $jt = $joint_type->{"$sx,$sy"}; |
1061
|
13
|
|
|
|
|
101
|
$cells->{"$px,$py"}->_make_joint($edge,$jt); |
1062
|
|
|
|
|
|
|
} |
1063
|
|
|
|
|
|
|
|
1064
|
242
|
100
|
100
|
|
|
1775
|
if (@$path > 0 && keys %$end_cells > 0) |
1065
|
|
|
|
|
|
|
{ |
1066
|
|
|
|
|
|
|
# convert the edge piece of the starting edge-cell to a joint |
1067
|
12
|
|
|
|
|
44
|
my ($x, $y) = ($path->[-3],$path->[-2]); |
1068
|
12
|
|
|
|
|
35
|
my $xy = "$x,$y"; |
1069
|
12
|
|
|
|
|
21
|
my ($sx,$sy,$t,$px,$py) = @{$end_cells->{$xy}}; |
|
12
|
|
|
|
|
45
|
|
1070
|
|
|
|
|
|
|
|
1071
|
12
|
|
|
|
|
38
|
my $jt = $joint_type_end->{"$sx,$sy"}; |
1072
|
12
|
|
|
|
|
98
|
$cells->{"$px,$py"}->_make_joint($edge,$jt); |
1073
|
|
|
|
|
|
|
} |
1074
|
|
|
|
|
|
|
|
1075
|
242
|
|
|
|
|
4081
|
$path; |
1076
|
|
|
|
|
|
|
} |
1077
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
sub _astar |
1079
|
|
|
|
|
|
|
{ |
1080
|
|
|
|
|
|
|
# The core A* algorithm, finds a path from a given list of start |
1081
|
|
|
|
|
|
|
# positions @A to and of the given stop positions @B. |
1082
|
242
|
|
|
242
|
|
484
|
my ($self, $A, $B, $edge, $per_field) = @_; |
1083
|
|
|
|
|
|
|
|
1084
|
242
|
|
|
|
|
1510
|
my @start = @$A; |
1085
|
242
|
|
|
|
|
1567
|
my @stop = @$B; |
1086
|
242
|
|
|
|
|
409
|
my $stop = scalar @stop; |
1087
|
|
|
|
|
|
|
|
1088
|
242
|
|
|
|
|
518
|
my $src = $edge->{from}; |
1089
|
242
|
|
|
|
|
427
|
my $dst = $edge->{to}; |
1090
|
242
|
|
|
|
|
530
|
my $cells = $self->{cells}; |
1091
|
|
|
|
|
|
|
|
1092
|
242
|
|
|
|
|
1541
|
my $open = Graph::Easy::Heap->new(); # to find smallest elem fast |
1093
|
242
|
|
|
|
|
882
|
my $open_by_pos = {}; # to find open nodes by pos |
1094
|
242
|
|
|
|
|
545
|
my $closed = {}; # to find closed nodes by pos |
1095
|
|
|
|
|
|
|
|
1096
|
242
|
|
|
|
|
424
|
my $elem; |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
# The boundaries of objects in $cell, e.g. the area that the algorithm shall |
1099
|
|
|
|
|
|
|
# never leave. |
1100
|
242
|
|
|
|
|
945
|
my ($min_x, $min_y, $max_x, $max_y) = $self->_astar_boundaries(); |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
# Max. steps to prevent endless searching in case of bugs like endless loops. |
1103
|
242
|
|
|
|
|
530
|
my $tries = 0; my $max_tries = 2000000; |
|
242
|
|
|
|
|
424
|
|
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
# count how many times we did A* |
1106
|
242
|
|
|
|
|
807
|
$self->{stats}->{astar}++; |
1107
|
|
|
|
|
|
|
|
1108
|
|
|
|
|
|
|
########################################################################### |
1109
|
|
|
|
|
|
|
########################################################################### |
1110
|
|
|
|
|
|
|
# put the start positions into OPEN |
1111
|
|
|
|
|
|
|
|
1112
|
242
|
|
|
|
|
467
|
my $i = 0; my $bias = 0; |
|
242
|
|
|
|
|
470
|
|
1113
|
242
|
|
|
|
|
801
|
while ($i < scalar @start) |
1114
|
|
|
|
|
|
|
{ |
1115
|
1172
|
|
|
|
|
3226
|
my ($sx,$sy,$type,$px,$py) = |
1116
|
|
|
|
|
|
|
($start[$i],$start[$i+1],$start[$i+2],$start[$i+3],$start[$i+4]); |
1117
|
1172
|
|
|
|
|
1423
|
$i += 5; |
1118
|
|
|
|
|
|
|
|
1119
|
1172
|
|
|
|
|
2353
|
my $cell = $cells->{"$sx,$sy"}; my $rcell = ref($cell); |
|
1172
|
|
|
|
|
1699
|
|
1120
|
1172
|
100
|
100
|
|
|
4232
|
next if $rcell && $rcell !~ /::Edge/; |
1121
|
|
|
|
|
|
|
|
1122
|
1161
|
100
|
|
|
|
1656
|
my $t = 0; $t = $cell->{type} & EDGE_NO_M_MASK if $rcell =~ /::Edge/; |
|
1161
|
|
|
|
|
3227
|
|
1123
|
1161
|
100
|
100
|
|
|
4524
|
next if $t != 0 && $t != EDGE_HOR && $t != EDGE_VER; |
|
|
|
100
|
|
|
|
|
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
# For each start point, calculate the distance to each stop point, then use |
1126
|
|
|
|
|
|
|
# the smallest as value: |
1127
|
895
|
|
|
|
|
1106
|
my $lowest_x = $stop[0]; my $lowest_y = $stop[1]; |
|
895
|
|
|
|
|
1315
|
|
1128
|
895
|
|
|
|
|
2204
|
my $lowest = _astar_distance($sx,$sy, $stop[0], $stop[1]); |
1129
|
895
|
|
|
|
|
2465
|
for (my $u = $per_field; $u < $stop; $u += $per_field) |
1130
|
|
|
|
|
|
|
{ |
1131
|
3218
|
|
|
|
|
6791
|
my $dist = _astar_distance($sx,$sy, $stop[$u], $stop[$u+1]); |
1132
|
3218
|
100
|
|
|
|
8480
|
($lowest_x, $lowest_y) = ($stop[$u],$stop[$u+1]) if $dist < $lowest; |
1133
|
3218
|
100
|
|
|
|
10381
|
$lowest = $dist if $dist < $lowest; |
1134
|
|
|
|
|
|
|
} |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
# add a penalty for crossings |
1138
|
895
|
100
|
|
|
|
1317
|
my $malus = 0; $malus = 30 if $t != 0; |
|
895
|
|
|
|
|
1823
|
|
1139
|
895
|
|
|
|
|
2021
|
$malus += _astar_modifier($px,$py, $sx, $sy, $sx, $sy); |
1140
|
895
|
|
|
|
|
4903
|
$open->add( [ $lowest, $sx, $sy, $px, $py, $type, 1 ] ); |
1141
|
|
|
|
|
|
|
|
1142
|
895
|
|
|
|
|
2263
|
my $o = $malus + $bias + $lowest; |
1143
|
895
|
50
|
|
|
|
2331
|
print STDERR "# adding open pos $sx,$sy ($o = $malus + $bias + $lowest) at ($lowest_x,$lowest_y)\n" |
1144
|
|
|
|
|
|
|
if $self->{debug} > 1; |
1145
|
|
|
|
|
|
|
|
1146
|
|
|
|
|
|
|
# The cost to reach the starting node is obviously 0. That means that there is |
1147
|
|
|
|
|
|
|
# a tie between going down/up if both possibilities are equal likely. We insert |
1148
|
|
|
|
|
|
|
# a small bias here that makes the prefered order east/south/west/north. Instead |
1149
|
|
|
|
|
|
|
# the algorithmn exploring both way and terminating arbitrarily on the one that |
1150
|
|
|
|
|
|
|
# first hits the target, it will explore only one. |
1151
|
895
|
|
|
|
|
2674
|
$open_by_pos->{"$sx,$sy"} = $o; |
1152
|
|
|
|
|
|
|
|
1153
|
895
|
|
50
|
|
|
3854
|
$bias += $self->{_astar_bias} || 0; |
1154
|
|
|
|
|
|
|
} |
1155
|
|
|
|
|
|
|
|
1156
|
|
|
|
|
|
|
########################################################################### |
1157
|
|
|
|
|
|
|
########################################################################### |
1158
|
|
|
|
|
|
|
# main A* loop |
1159
|
|
|
|
|
|
|
|
1160
|
242
|
|
|
|
|
673
|
my $stats = $self->{stats}; |
1161
|
|
|
|
|
|
|
|
1162
|
|
|
|
|
|
|
STEP: |
1163
|
242
|
|
|
|
|
842
|
while( defined( $elem = $open->extract_top() ) ) |
1164
|
|
|
|
|
|
|
{ |
1165
|
3442
|
50
|
|
|
|
11343
|
$stats->{astar_steps}++ if $self->{debug}; |
1166
|
|
|
|
|
|
|
|
1167
|
|
|
|
|
|
|
# hard limit on number of steps todo |
1168
|
3442
|
50
|
|
|
|
7637
|
if ($tries++ > $max_tries) |
1169
|
|
|
|
|
|
|
{ |
1170
|
0
|
|
|
|
|
0
|
$self->warn("A* reached maximum number of tries ($max_tries), giving up."); |
1171
|
0
|
|
|
|
|
0
|
return []; |
1172
|
|
|
|
|
|
|
} |
1173
|
|
|
|
|
|
|
|
1174
|
3442
|
50
|
|
|
|
7036
|
print STDERR "# Smallest elem from ", $open->elements(), |
1175
|
|
|
|
|
|
|
" elems is: weight=", $elem->[0], " at $elem->[1],$elem->[2]\n" if $self->{debug} > 1; |
1176
|
3442
|
|
|
|
|
7591
|
my ($val, $x,$y, $px,$py, $type, $do_stop) = @$elem; |
1177
|
|
|
|
|
|
|
|
1178
|
3442
|
|
|
|
|
6146
|
my $key = "$x,$y"; |
1179
|
|
|
|
|
|
|
# move node into CLOSE and remove from OPEN |
1180
|
3442
|
|
50
|
|
|
8702
|
my $g = $open_by_pos->{$key} || 0; |
1181
|
3442
|
|
|
|
|
13123
|
$closed->{$key} = [ $px, $py, $val - $g, $g, $type, $do_stop ]; |
1182
|
3442
|
|
|
|
|
6372
|
delete $open_by_pos->{$key}; |
1183
|
|
|
|
|
|
|
|
1184
|
|
|
|
|
|
|
# we are done when we hit one of the potential stop positions |
1185
|
3442
|
|
|
|
|
9678
|
for (my $i = 0; $i < $stop; $i += $per_field) |
1186
|
|
|
|
|
|
|
{ |
1187
|
|
|
|
|
|
|
# reached one stop position? |
1188
|
13830
|
100
|
100
|
|
|
49377
|
if ($x == $stop[$i] && $y == $stop[$i+1]) |
1189
|
|
|
|
|
|
|
{ |
1190
|
240
|
100
|
|
|
|
1746
|
$closed->{$key}->[4] += $stop[$i+2] if defined $stop[$i+2]; |
1191
|
|
|
|
|
|
|
# store the reached stop position if it is known |
1192
|
240
|
100
|
|
|
|
962
|
if ($per_field > 3) |
|
|
50
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
{ |
1194
|
12
|
|
|
|
|
52
|
$closed->{$key}->[6] = $stop[$i+3]; |
1195
|
12
|
|
|
|
|
32
|
$closed->{$key}->[7] = $stop[$i+4]; |
1196
|
12
|
50
|
|
|
|
42
|
print STDERR "# Reached stop position $x,$y (lx,ly $stop[$i+3], $stop[$i+4])\n" if $self->{debug} > 1; |
1197
|
|
|
|
|
|
|
} |
1198
|
|
|
|
|
|
|
elsif ($self->{debug} > 1) { |
1199
|
0
|
|
|
|
|
0
|
print STDERR "# Reached stop position $x,$y\n"; |
1200
|
|
|
|
|
|
|
} |
1201
|
240
|
|
|
|
|
938
|
last STEP; |
1202
|
|
|
|
|
|
|
} |
1203
|
|
|
|
|
|
|
} # end test for stop postion(s) |
1204
|
|
|
|
|
|
|
|
1205
|
3202
|
50
|
33
|
|
|
14256
|
$self->_croak("On of '$x,$y' is not defined") |
1206
|
|
|
|
|
|
|
unless defined $x && defined $y; |
1207
|
|
|
|
|
|
|
|
1208
|
|
|
|
|
|
|
# get list of potential positions we need to explore from the current one |
1209
|
3202
|
|
|
|
|
8760
|
my @p = $self->_astar_near_nodes($x,$y, $cells, $closed, $min_x, $min_y, $max_x, $max_y); |
1210
|
|
|
|
|
|
|
|
1211
|
3202
|
|
|
|
|
4643
|
my $n = 0; |
1212
|
3202
|
|
|
|
|
7117
|
while ($n < scalar @p) |
1213
|
|
|
|
|
|
|
{ |
1214
|
5666
|
|
|
|
|
7804
|
my $nx = $p[$n]; my $ny = $p[$n+1]; $n += 2; |
|
5666
|
|
|
|
|
7551
|
|
|
5666
|
|
|
|
|
6234
|
|
1215
|
|
|
|
|
|
|
|
1216
|
5666
|
50
|
33
|
|
|
18869
|
if (!defined $nx || !defined $ny) |
1217
|
|
|
|
|
|
|
{ |
1218
|
0
|
|
|
|
|
0
|
require Carp; |
1219
|
0
|
|
|
|
|
0
|
Carp::confess("On of '$nx,$ny' is not defined"); |
1220
|
|
|
|
|
|
|
} |
1221
|
5666
|
|
|
|
|
7275
|
my $lg = $g; |
1222
|
5666
|
50
|
33
|
|
|
28044
|
$lg += _astar_modifier($px,$py,$x,$y,$nx,$ny,$cells) if defined $px && defined $py; |
1223
|
|
|
|
|
|
|
|
1224
|
5666
|
|
|
|
|
9526
|
my $n = "$nx,$ny"; |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
# was already open? |
1227
|
5666
|
100
|
|
|
|
17021
|
next if (exists $open_by_pos->{$n}); |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
# print STDERR "# Already open pos $nx,$ny with $open_by_pos->{$n} (would be $lg)\n" |
1230
|
|
|
|
|
|
|
# if $self->{debug} && exists $open_by_pos->{$n}; |
1231
|
|
|
|
|
|
|
# |
1232
|
|
|
|
|
|
|
# next if exists $open_by_pos->{$n} && $open_by_pos->{$n} <= $lg; |
1233
|
|
|
|
|
|
|
# |
1234
|
|
|
|
|
|
|
# if (exists $open_by_pos->{$n}) |
1235
|
|
|
|
|
|
|
# { |
1236
|
|
|
|
|
|
|
# $open->delete($nx, $ny); |
1237
|
|
|
|
|
|
|
# } |
1238
|
|
|
|
|
|
|
|
1239
|
|
|
|
|
|
|
# calculate distance to each possible stop position, and |
1240
|
|
|
|
|
|
|
# use the lowest one |
1241
|
4100
|
|
|
|
|
8981
|
my $lowest_distance = _astar_distance($nx, $ny, $stop[0], $stop[1]); |
1242
|
4100
|
|
|
|
|
9239
|
for (my $i = $per_field; $i < $stop; $i += $per_field) |
1243
|
|
|
|
|
|
|
{ |
1244
|
12644
|
|
|
|
|
25154
|
my $d = _astar_distance($nx, $ny, $stop[$i], $stop[$i+1]); |
1245
|
12644
|
100
|
|
|
|
40399
|
$lowest_distance = $d if $d < $lowest_distance; |
1246
|
|
|
|
|
|
|
} |
1247
|
|
|
|
|
|
|
|
1248
|
4100
|
50
|
|
|
|
10357
|
print STDERR "# Opening pos $nx,$ny ($lowest_distance + $lg)\n" if $self->{debug} > 1; |
1249
|
|
|
|
|
|
|
|
1250
|
|
|
|
|
|
|
# open new position into OPEN |
1251
|
4100
|
|
|
|
|
21068
|
$open->add( [ $lowest_distance + $lg, $nx, $ny, $x, $y, undef ] ); |
1252
|
4100
|
|
|
|
|
22644
|
$open_by_pos->{$n} = $lg; |
1253
|
|
|
|
|
|
|
} |
1254
|
|
|
|
|
|
|
} |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
########################################################################### |
1257
|
|
|
|
|
|
|
# A* is done, now build a path from the information we computed above: |
1258
|
|
|
|
|
|
|
|
1259
|
|
|
|
|
|
|
# count how many steps we did in A* |
1260
|
242
|
|
|
|
|
682
|
$self->{stats}->{astar_steps} += $tries; |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
# no more nodes to follow, so we couldn't find a path |
1263
|
242
|
100
|
|
|
|
702
|
if (!defined $elem) |
1264
|
|
|
|
|
|
|
{ |
1265
|
2
|
50
|
|
|
|
11
|
print STDERR "# A* couldn't find a path after $max_tries steps.\n" if $self->{debug}; |
1266
|
2
|
|
|
|
|
265
|
return []; |
1267
|
|
|
|
|
|
|
} |
1268
|
|
|
|
|
|
|
|
1269
|
240
|
|
|
|
|
529
|
my $path = []; |
1270
|
240
|
|
|
|
|
768
|
my ($cx,$cy) = ($elem->[1],$elem->[2]); |
1271
|
|
|
|
|
|
|
# the "last" cell in the path. Since we follow it backwards, it |
1272
|
|
|
|
|
|
|
# becomes actually the next cell |
1273
|
240
|
|
|
|
|
583
|
my ($lx,$ly); |
1274
|
0
|
|
|
|
|
0
|
my $type; |
1275
|
|
|
|
|
|
|
|
1276
|
240
|
|
|
|
|
310
|
my $label_cell = 0; # found a cell to attach the label to? |
1277
|
|
|
|
|
|
|
|
1278
|
240
|
|
|
|
|
336
|
my @bends; # record all bends in the path to straighten it out |
1279
|
|
|
|
|
|
|
|
1280
|
240
|
|
|
|
|
391
|
my $idx = 0; |
1281
|
|
|
|
|
|
|
# follow $elem back to the source to find the path |
1282
|
240
|
|
|
|
|
667
|
while (defined $cx) |
1283
|
|
|
|
|
|
|
{ |
1284
|
1155
|
50
|
|
|
|
2930
|
last unless exists $closed->{"$cx,$cy"}; |
1285
|
1155
|
|
|
|
|
1688
|
my $xy = "$cx,$cy"; |
1286
|
|
|
|
|
|
|
|
1287
|
1155
|
|
|
|
|
2007
|
$type = $closed->{$xy}->[ 4 ]; |
1288
|
|
|
|
|
|
|
|
1289
|
1155
|
|
|
|
|
1277
|
my ($px,$py) = @{ $closed->{$xy} }; # get X,Y of parent cell |
|
1155
|
|
|
|
|
2518
|
|
1290
|
|
|
|
|
|
|
|
1291
|
1155
|
|
100
|
|
|
3860
|
my $edge_type = ($type||0) & EDGE_TYPE_MASK; |
1292
|
1155
|
50
|
|
|
|
2455
|
if ($edge_type == 0) |
1293
|
|
|
|
|
|
|
{ |
1294
|
1155
|
|
100
|
|
|
3235
|
my $edge_flags = ($type||0) & EDGE_FLAG_MASK; |
1295
|
|
|
|
|
|
|
|
1296
|
|
|
|
|
|
|
# either a start or a stop cell |
1297
|
1155
|
50
|
|
|
|
2394
|
if (!defined $px) |
1298
|
|
|
|
|
|
|
{ |
1299
|
|
|
|
|
|
|
# We can figure it out from the flag of the position of cx,cy |
1300
|
|
|
|
|
|
|
# ................ |
1301
|
|
|
|
|
|
|
# : EDGE_START_S : |
1302
|
|
|
|
|
|
|
# ....................................... |
1303
|
|
|
|
|
|
|
# START_E : px,py : EDGE_START_W : |
1304
|
|
|
|
|
|
|
# ....................................... |
1305
|
|
|
|
|
|
|
# : EDGE_START_N : |
1306
|
|
|
|
|
|
|
# ................ |
1307
|
0
|
|
|
|
|
0
|
($px,$py) = ($cx, $cy); # start with same cell |
1308
|
0
|
0
|
|
|
|
0
|
$py ++ if ($edge_flags & EDGE_START_S) != 0; |
1309
|
0
|
0
|
|
|
|
0
|
$py -- if ($edge_flags & EDGE_START_N) != 0; |
1310
|
|
|
|
|
|
|
|
1311
|
0
|
0
|
|
|
|
0
|
$px ++ if ($edge_flags & EDGE_START_E) != 0; |
1312
|
0
|
0
|
|
|
|
0
|
$px -- if ($edge_flags & EDGE_START_W) != 0; |
1313
|
|
|
|
|
|
|
} |
1314
|
|
|
|
|
|
|
|
1315
|
|
|
|
|
|
|
# if lx, ly is undefined because px,py is a joint, get it via the stored |
1316
|
|
|
|
|
|
|
# x,y pos of the very last cell in the path |
1317
|
1155
|
100
|
|
|
|
2299
|
if (!defined $lx) |
1318
|
|
|
|
|
|
|
{ |
1319
|
240
|
|
|
|
|
530
|
$lx = $closed->{$xy}->[6]; |
1320
|
240
|
|
|
|
|
472
|
$ly = $closed->{$xy}->[7]; |
1321
|
|
|
|
|
|
|
} |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
# still not known? |
1324
|
1155
|
100
|
|
|
|
2205
|
if (!defined $lx) |
1325
|
|
|
|
|
|
|
{ |
1326
|
|
|
|
|
|
|
|
1327
|
|
|
|
|
|
|
# If lx,ly is undefined because we are at the end of the path, |
1328
|
|
|
|
|
|
|
# we can figure out from the flag of the position of cx,cy. |
1329
|
|
|
|
|
|
|
# .............. |
1330
|
|
|
|
|
|
|
# : EDGE_END_S : |
1331
|
|
|
|
|
|
|
# ................................. |
1332
|
|
|
|
|
|
|
# END_E : lx,ly : EDGE_END_W : |
1333
|
|
|
|
|
|
|
# ................................. |
1334
|
|
|
|
|
|
|
# : EDGE_END_N : |
1335
|
|
|
|
|
|
|
# .............. |
1336
|
228
|
|
|
|
|
376
|
($lx,$ly) = ($cx, $cy); # start with same cell |
1337
|
|
|
|
|
|
|
|
1338
|
228
|
100
|
|
|
|
642
|
$ly ++ if ($edge_flags & EDGE_END_S) != 0; |
1339
|
228
|
100
|
|
|
|
599
|
$ly -- if ($edge_flags & EDGE_END_N) != 0; |
1340
|
|
|
|
|
|
|
|
1341
|
228
|
100
|
|
|
|
589
|
$lx ++ if ($edge_flags & EDGE_END_E) != 0; |
1342
|
228
|
100
|
|
|
|
560
|
$lx -- if ($edge_flags & EDGE_END_W) != 0; |
1343
|
|
|
|
|
|
|
} |
1344
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
# now figure out correct type for this cell from positions of |
1346
|
|
|
|
|
|
|
# parent/following cell |
1347
|
1155
|
|
|
|
|
2943
|
$type += _astar_edge_type($px, $py, $cx, $cy, $lx,$ly); |
1348
|
|
|
|
|
|
|
} |
1349
|
|
|
|
|
|
|
|
1350
|
1155
|
50
|
|
|
|
3489
|
print STDERR "# Following back from $lx,$ly over $cx,$cy to $px,$py\n" if $self->{debug} > 1; |
1351
|
|
|
|
|
|
|
|
1352
|
1155
|
0
|
66
|
|
|
3378
|
if ($px == $lx && $py == $ly && ($cx != $lx || $cy != $ly)) |
|
|
|
0
|
|
|
|
|
|
|
|
33
|
|
|
|
|
1353
|
|
|
|
|
|
|
{ |
1354
|
0
|
0
|
|
|
|
0
|
print STDERR |
1355
|
|
|
|
|
|
|
"# Warning: A* detected loop in path-backtracking at $px,$py, $cx,$cy, $lx,$ly\n" |
1356
|
|
|
|
|
|
|
if $self->{debug}; |
1357
|
0
|
|
|
|
|
0
|
last; |
1358
|
|
|
|
|
|
|
} |
1359
|
|
|
|
|
|
|
|
1360
|
1155
|
50
|
|
|
|
2563
|
$type = EDGE_HOR if ($type & EDGE_TYPE_MASK) == 0; # last resort |
1361
|
|
|
|
|
|
|
|
1362
|
|
|
|
|
|
|
# if this is the first hor edge, attach the label to it |
1363
|
|
|
|
|
|
|
# XXX TODO: This clearly is not optimal. Look for left-most HOR CELL |
1364
|
1155
|
|
|
|
|
1398
|
my $t = $type & EDGE_TYPE_MASK; |
1365
|
|
|
|
|
|
|
|
1366
|
|
|
|
|
|
|
# Do not put the label on crossings: |
1367
|
1155
|
100
|
66
|
|
|
5130
|
if ($label_cell == 0 && (!exists $cells->{"$cx,$cy"}) && ($t == EDGE_HOR || $t == EDGE_VER)) |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
1368
|
|
|
|
|
|
|
{ |
1369
|
239
|
|
|
|
|
342
|
$label_cell++; |
1370
|
239
|
|
|
|
|
3987
|
$type += EDGE_LABEL_CELL; |
1371
|
|
|
|
|
|
|
} |
1372
|
|
|
|
|
|
|
|
1373
|
1155
|
100
|
100
|
|
|
15521
|
push @bends, [ $type, $cx, $cy, -$idx ] |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
1374
|
|
|
|
|
|
|
if ($type == EDGE_S_E || $t == EDGE_S_W || $t == EDGE_N_E || $t == EDGE_N_W); |
1375
|
|
|
|
|
|
|
|
1376
|
1155
|
|
|
|
|
3242
|
unshift @$path, $cx, $cy, $type; # unshift to reverse the path |
1377
|
|
|
|
|
|
|
|
1378
|
1155
|
100
|
|
|
|
4978
|
last if $closed->{"$cx,$cy"}->[ 5 ]; # stop here? |
1379
|
|
|
|
|
|
|
|
1380
|
915
|
|
|
|
|
1568
|
($lx,$ly) = ($cx,$cy); |
1381
|
915
|
|
|
|
|
1128
|
($cx,$cy) = @{ $closed->{"$cx,$cy"} }; # get X,Y of next cell |
|
915
|
|
|
|
|
4773
|
|
1382
|
|
|
|
|
|
|
|
1383
|
915
|
|
|
|
|
2611
|
$idx += 3; # index into $path (for bends) |
1384
|
|
|
|
|
|
|
} |
1385
|
|
|
|
|
|
|
|
1386
|
240
|
50
|
66
|
|
|
1916
|
print STDERR "# Trying to straighten path\n" if @bends >= 3 && $self->{debug}; |
1387
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
# try to straighten unnec. inward bends |
1389
|
240
|
100
|
|
|
|
617
|
$self->_straighten_path($path, \@bends, $edge) if @bends >= 3; |
1390
|
|
|
|
|
|
|
|
1391
|
240
|
50
|
|
|
|
1012
|
return ($path,$closed,$open_by_pos) if wantarray; |
1392
|
240
|
|
|
|
|
6989
|
$path; |
1393
|
|
|
|
|
|
|
} |
1394
|
|
|
|
|
|
|
|
1395
|
|
|
|
|
|
|
# 1: |
1396
|
|
|
|
|
|
|
# | | |
1397
|
|
|
|
|
|
|
# +----+ => | |
1398
|
|
|
|
|
|
|
# | | |
1399
|
|
|
|
|
|
|
# ----+ ------+ |
1400
|
|
|
|
|
|
|
|
1401
|
|
|
|
|
|
|
# 2: |
1402
|
|
|
|
|
|
|
# +--- +------ |
1403
|
|
|
|
|
|
|
# | | |
1404
|
|
|
|
|
|
|
# +---+ => | |
1405
|
|
|
|
|
|
|
# | | |
1406
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
# 3: |
1408
|
|
|
|
|
|
|
# ----+ ------+ |
1409
|
|
|
|
|
|
|
# | => | |
1410
|
|
|
|
|
|
|
# +----+ | |
1411
|
|
|
|
|
|
|
# | | |
1412
|
|
|
|
|
|
|
|
1413
|
|
|
|
|
|
|
# 4: |
1414
|
|
|
|
|
|
|
# | | |
1415
|
|
|
|
|
|
|
# +---+ | |
1416
|
|
|
|
|
|
|
# | => | |
1417
|
|
|
|
|
|
|
# +----+ +------ |
1418
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
my $bend_patterns = [ |
1420
|
|
|
|
|
|
|
|
1421
|
|
|
|
|
|
|
# The patterns are duplicated to catch both directions of the path: |
1422
|
|
|
|
|
|
|
|
1423
|
|
|
|
|
|
|
# First five entries must match |
1424
|
|
|
|
|
|
|
# dx, dy, |
1425
|
|
|
|
|
|
|
# coordinates for new edge |
1426
|
|
|
|
|
|
|
# (2 == y, 1 == x, first is |
1427
|
|
|
|
|
|
|
# taken from A, second from B) |
1428
|
|
|
|
|
|
|
# these replace the first & last bend |
1429
|
|
|
|
|
|
|
# 1: |
1430
|
|
|
|
|
|
|
[ EDGE_N_W, EDGE_S_E, EDGE_N_W, 0, -1, 2, 1, EDGE_HOR, EDGE_VER, 1,0, 0,-1 ], # 0 |
1431
|
|
|
|
|
|
|
[ EDGE_N_W, EDGE_S_E, EDGE_N_W, -1, 0, 1, 2, EDGE_VER, EDGE_HOR, 0,1, -1,0 ], # 1 |
1432
|
|
|
|
|
|
|
|
1433
|
|
|
|
|
|
|
# 2: |
1434
|
|
|
|
|
|
|
[ EDGE_S_E, EDGE_N_W, EDGE_S_E, 0, -1, 1, 2, EDGE_VER, EDGE_HOR, 0,-1, 1,0 ], # 2 |
1435
|
|
|
|
|
|
|
[ EDGE_S_E, EDGE_N_W, EDGE_S_E, -1, 0, 2, 1, EDGE_HOR, EDGE_VER, -1,0, 0,1 ], # 3 |
1436
|
|
|
|
|
|
|
|
1437
|
|
|
|
|
|
|
# 3: |
1438
|
|
|
|
|
|
|
[ EDGE_S_W, EDGE_N_E, EDGE_S_W, 0, 1, 2, 1, EDGE_HOR, EDGE_VER, 1,0, 0,1 ], # 4 |
1439
|
|
|
|
|
|
|
[ EDGE_S_W, EDGE_N_E, EDGE_S_W, -1, 0, 1, 2, EDGE_VER, EDGE_HOR, 0,-1, -1,0 ], # 5 |
1440
|
|
|
|
|
|
|
|
1441
|
|
|
|
|
|
|
# 4: |
1442
|
|
|
|
|
|
|
[ EDGE_N_E, EDGE_S_W, EDGE_N_E, 1, 0, 1, 2, EDGE_VER, EDGE_HOR, 0,1, 1,0 ], # 6 |
1443
|
|
|
|
|
|
|
[ EDGE_N_E, EDGE_S_W, EDGE_N_E, 0, -1, 2, 1, EDGE_HOR, EDGE_VER, -1,0, 0,-1 ], # 7 |
1444
|
|
|
|
|
|
|
|
1445
|
|
|
|
|
|
|
]; |
1446
|
|
|
|
|
|
|
|
1447
|
|
|
|
|
|
|
sub _straighten_path |
1448
|
|
|
|
|
|
|
{ |
1449
|
8
|
|
|
8
|
|
26
|
my ($self, $path, $bends, $edge) = @_; |
1450
|
|
|
|
|
|
|
|
1451
|
|
|
|
|
|
|
# XXX TODO: |
1452
|
|
|
|
|
|
|
# in case of multiple bends, removes only one of them due to overlap |
1453
|
|
|
|
|
|
|
|
1454
|
8
|
|
|
|
|
24
|
my $cells = $self->{cells}; |
1455
|
|
|
|
|
|
|
|
1456
|
8
|
|
|
|
|
17
|
my $i = 0; |
1457
|
|
|
|
|
|
|
BEND: |
1458
|
8
|
|
|
|
|
38
|
while ($i < (scalar @$bends - 2)) |
1459
|
|
|
|
|
|
|
{ |
1460
|
|
|
|
|
|
|
# for each bend, check it and the next two bends |
1461
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
# print STDERR "Checking bend $i at $bends->[$i], $bends->[$i+1], $bends->[$i+2]\n"; |
1463
|
|
|
|
|
|
|
|
1464
|
10
|
|
|
|
|
35
|
my ($a,$b,$c) = ($bends->[$i], |
1465
|
|
|
|
|
|
|
$bends->[$i+1], |
1466
|
|
|
|
|
|
|
$bends->[$i+2]); |
1467
|
|
|
|
|
|
|
|
1468
|
10
|
|
|
|
|
27
|
my $dx = ($b->[1] - $a->[1]); |
1469
|
10
|
|
|
|
|
21
|
my $dy = ($b->[2] - $a->[2]); |
1470
|
|
|
|
|
|
|
|
1471
|
10
|
|
|
|
|
17
|
my $p = 0; |
1472
|
10
|
|
|
|
|
30
|
for my $pattern (@$bend_patterns) |
1473
|
|
|
|
|
|
|
{ |
1474
|
80
|
|
|
|
|
82
|
$p++; |
1475
|
80
|
0
|
100
|
|
|
309
|
next if ($a->[0] != $pattern->[0]) || |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
1476
|
|
|
|
|
|
|
($b->[0] != $pattern->[1]) || |
1477
|
|
|
|
|
|
|
($c->[0] != $pattern->[2]) || |
1478
|
|
|
|
|
|
|
($dx != $pattern->[3]) || |
1479
|
|
|
|
|
|
|
($dy != $pattern->[4]); |
1480
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
# pattern matched |
1482
|
|
|
|
|
|
|
# print STDERR "# Got bends for pattern ", $p-1," (@$pattern):\n"; |
1483
|
|
|
|
|
|
|
# print STDERR "# type x,y,\n# @$a\n# @$b\n# @$c\n"; |
1484
|
|
|
|
|
|
|
|
1485
|
|
|
|
|
|
|
# check that the alternative path is empty |
1486
|
|
|
|
|
|
|
|
1487
|
|
|
|
|
|
|
# new corner: |
1488
|
0
|
|
|
|
|
0
|
my $cx = $a->[$pattern->[5]]; |
1489
|
0
|
|
|
|
|
0
|
my $cy = $c->[$pattern->[6]]; |
1490
|
0
|
0
|
|
|
|
0
|
($cx,$cy) = ($cy,$cx) if $pattern->[5] == 2; # need to swap? |
1491
|
|
|
|
|
|
|
|
1492
|
0
|
0
|
|
|
|
0
|
next BEND if exists $cells->{"$cx,$cy"}; |
1493
|
|
|
|
|
|
|
|
1494
|
|
|
|
|
|
|
# print STDERR "# new corner at $cx,$cy (swap: $pattern->[5])\n"; |
1495
|
|
|
|
|
|
|
|
1496
|
|
|
|
|
|
|
# check from A to new corner |
1497
|
0
|
|
|
|
|
0
|
my $x = $a->[1]; |
1498
|
0
|
|
|
|
|
0
|
my $y = $a->[2]; |
1499
|
|
|
|
|
|
|
|
1500
|
0
|
|
|
|
|
0
|
my @replace = (); |
1501
|
0
|
0
|
0
|
|
|
0
|
push @replace, $cx, $cy, $pattern->[0] if ($x == $cx && $y == $cy); |
1502
|
|
|
|
|
|
|
|
1503
|
0
|
|
|
|
|
0
|
my $ddx = $pattern->[9]; |
1504
|
0
|
|
|
|
|
0
|
my $ddy = $pattern->[10]; |
1505
|
|
|
|
|
|
|
# print STDERR "# dx,dy: $ddx,$ddy\n"; |
1506
|
0
|
|
0
|
|
|
0
|
while ($x != $cx || $y != $cy) |
1507
|
|
|
|
|
|
|
{ |
1508
|
0
|
0
|
|
|
|
0
|
next BEND if exists $cells->{"$x,$y"}; |
1509
|
|
|
|
|
|
|
# print STDERR "# at $x $y (go to $cx,$cy)\n"; sleep(1); |
1510
|
0
|
|
|
|
|
0
|
push @replace, $x, $y, $pattern->[7]; |
1511
|
0
|
|
|
|
|
0
|
$x += $ddx; |
1512
|
0
|
|
|
|
|
0
|
$y += $ddy; |
1513
|
|
|
|
|
|
|
} |
1514
|
|
|
|
|
|
|
|
1515
|
0
|
|
|
|
|
0
|
$x = $cx; $y = $cy; |
|
0
|
|
|
|
|
0
|
|
1516
|
|
|
|
|
|
|
|
1517
|
|
|
|
|
|
|
# check from new corner to C |
1518
|
0
|
|
|
|
|
0
|
$ddx = $pattern->[11]; |
1519
|
0
|
|
|
|
|
0
|
$ddy = $pattern->[12]; |
1520
|
0
|
|
0
|
|
|
0
|
while ($x != $c->[1] || $y != $c->[2]) |
1521
|
|
|
|
|
|
|
{ |
1522
|
0
|
0
|
|
|
|
0
|
next BEND if exists $cells->{"$x,$y"}; |
1523
|
|
|
|
|
|
|
# print STDERR "# at $x $y (go to $cx,$cy)\n"; sleep(1); |
1524
|
0
|
|
|
|
|
0
|
push @replace, $x, $y, $pattern->[8]; |
1525
|
|
|
|
|
|
|
|
1526
|
|
|
|
|
|
|
# set the correct type on the corner |
1527
|
0
|
0
|
0
|
|
|
0
|
$replace[-1] = $pattern->[0] if ($x == $cx && $y == $cy); |
1528
|
0
|
|
|
|
|
0
|
$x += $ddx; |
1529
|
0
|
|
|
|
|
0
|
$y += $ddy; |
1530
|
|
|
|
|
|
|
} |
1531
|
|
|
|
|
|
|
# insert Corner |
1532
|
0
|
|
|
|
|
0
|
push @replace, $x, $y, $pattern->[8]; |
1533
|
|
|
|
|
|
|
|
1534
|
|
|
|
|
|
|
# use Data::Dumper; print STDERR Dumper(@replace); |
1535
|
|
|
|
|
|
|
# print STDERR "# generated ", scalar @replace, " entries\n"; |
1536
|
|
|
|
|
|
|
# print STDERR "# idx A $a->[3] C $c->[3]\n"; |
1537
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
# the path is clear, so replace the inward bend with the new one |
1539
|
0
|
0
|
|
|
|
0
|
my $diff = $a->[3] - $c->[3] ? -3 : 3; |
1540
|
|
|
|
|
|
|
|
1541
|
0
|
|
|
|
|
0
|
my $idx = 0; my $p_idx = $a->[3] + $diff; |
|
0
|
|
|
|
|
0
|
|
1542
|
0
|
|
|
|
|
0
|
while ($idx < @replace) |
1543
|
|
|
|
|
|
|
{ |
1544
|
|
|
|
|
|
|
# print STDERR "# replace $p_idx .. $p_idx + 2\n"; |
1545
|
|
|
|
|
|
|
# print STDERR "# replace $path->[$p_idx] with $replace[$idx]\n"; |
1546
|
|
|
|
|
|
|
# print STDERR "# replace $path->[$p_idx+1] with $replace[$idx+1]\n"; |
1547
|
|
|
|
|
|
|
# print STDERR "# replace $path->[$p_idx+2] with $replace[$idx+2]\n"; |
1548
|
|
|
|
|
|
|
|
1549
|
0
|
|
|
|
|
0
|
$path->[$p_idx] = $replace[$idx]; |
1550
|
0
|
|
|
|
|
0
|
$path->[$p_idx+1] = $replace[$idx+1]; |
1551
|
0
|
|
|
|
|
0
|
$path->[$p_idx+2] = $replace[$idx+2]; |
1552
|
0
|
|
|
|
|
0
|
$p_idx += $diff; |
1553
|
0
|
|
|
|
|
0
|
$idx += 3; |
1554
|
|
|
|
|
|
|
} |
1555
|
|
|
|
|
|
|
} # end for this pattern |
1556
|
|
|
|
|
|
|
|
1557
|
10
|
|
|
|
|
44
|
} continue { $i++; }; |
1558
|
|
|
|
|
|
|
} |
1559
|
|
|
|
|
|
|
|
1560
|
|
|
|
|
|
|
sub _map_as_html |
1561
|
|
|
|
|
|
|
{ |
1562
|
0
|
|
|
0
|
|
|
my ($self, $cells, $p, $closed, $open, $w, $h) = @_; |
1563
|
|
|
|
|
|
|
|
1564
|
0
|
|
0
|
|
|
|
$w ||= 20; |
1565
|
0
|
|
0
|
|
|
|
$h ||= 20; |
1566
|
|
|
|
|
|
|
|
1567
|
0
|
|
|
|
|
|
my $html = <
|
1568
|
|
|
|
|
|
|
|
1569
|
|
|
|
|
|
|
|
1570
|
|
|
|
|
|
|
|
1571
|
|
|
|
|
|
|
|
1591
|
|
|
|
|
|
|
|
1592
|
|
|
|
|
|
|
|
1593
|
|
|
|
|
|
|
|
1594
|
|
|
|
|
|
|
A* Map |
1595
|
|
|
|
|
|
|
|
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
Nodes examined: ##closed## |
1598
|
|
|
|
|
|
|
Nodes still to do (open): ##open## |
1599
|
|
|
|
|
|
|
Nodes in path: ##path## |
1600
|
|
|
|
|
|
|
|
1601
|
|
|
|
|
|
|
EOF |
1602
|
|
|
|
|
|
|
; |
1603
|
|
|
|
|
|
|
|
1604
|
0
|
|
|
|
|
|
$html =~ s/##closed##/keys %$closed /eg; |
|
0
|
|
|
|
|
|
|
1605
|
0
|
|
|
|
|
|
$html =~ s/##open##/keys %$open /eg; |
|
0
|
|
|
|
|
|
|
1606
|
0
|
|
|
|
|
|
my $path = {}; |
1607
|
0
|
|
|
|
|
|
while (@$p) |
1608
|
|
|
|
|
|
|
{ |
1609
|
0
|
|
|
|
|
|
my $x = shift @$p; |
1610
|
0
|
|
|
|
|
|
my $y = shift @$p; |
1611
|
0
|
|
|
|
|
|
my $t = shift @$p; |
1612
|
0
|
|
|
|
|
|
$path->{"$x,$y"} = undef; |
1613
|
|
|
|
|
|
|
} |
1614
|
0
|
|
|
|
|
|
$html =~ s/##path##/keys %$path /eg; |
|
0
|
|
|
|
|
|
|
1615
|
0
|
|
|
|
|
|
$html .= '' . "\n";
1616
|
|
|
|
|
|
|
|
1617
|
0
|
|
|
|
|
|
for my $y (0..$h) |
1618
|
|
|
|
|
|
|
{ |
1619
|
0
|
|
|
|
|
|
$html .= " | \n";
1620
|
0
|
|
|
|
|
|
for my $x (0..$w) |
1621
|
|
|
|
|
|
|
{ |
1622
|
0
|
|
|
|
|
|
my $xy = "$x,$y"; |
1623
|
0
|
|
|
|
|
|
my $c = ' ' x 4; |
1624
|
0
|
0
|
0
|
|
|
|
$html .= " | $c | \n" and next if
|
|
|
0
|
|
|
|
|
1625
|
|
|
|
|
|
|
exists $cells->{$xy} and ref($cells->{$xy}) =~ /Node/; |
1626
|
0
|
0
|
0
|
|
|
|
$html .= " | $c | \n" and next if
|
|
|
0
|
|
|
|
|
1627
|
|
|
|
|
|
|
exists $cells->{$xy} && !exists $path->{$xy}; |
1628
|
|
|
|
|
|
|
|
1629
|
0
|
0
|
0
|
|
|
|
$html .= " | $c | \n" and next unless
|
|
|
0
|
|
|
|
|
1630
|
|
|
|
|
|
|
exists $closed->{$xy} || |
1631
|
|
|
|
|
|
|
exists $open->{$xy}; |
1632
|
|
|
|
|
|
|
|
1633
|
0
|
|
|
|
|
|
my $clr = '#a0a0a0'; |
1634
|
0
|
0
|
|
|
|
|
if (exists $closed->{$xy}) |
|
|
0
|
|
|
|
|
|
1635
|
|
|
|
|
|
|
{ |
1636
|
0
|
|
0
|
|
|
|
$c = ($closed->{$xy}->[3] || '0') . '+' . ($closed->{$xy}->[2] || '0'); |
|
|
|
0
|
|
|
|
|
1637
|
0
|
|
0
|
|
|
|
my $color = 0x10 + 8 * (($closed->{$xy}->[2] || 0)); |
1638
|
0
|
|
0
|
|
|
|
my $color2 = 0x10 + 8 * (($closed->{$xy}->[3] || 0)); |
1639
|
0
|
|
|
|
|
|
$clr = sprintf("%02x%02x",$color,$color2) . 'a0'; |
1640
|
|
|
|
|
|
|
} |
1641
|
|
|
|
|
|
|
elsif (exists $open->{$xy}) |
1642
|
|
|
|
|
|
|
{ |
1643
|
0
|
|
0
|
|
|
|
$c = ' ' . $open->{$xy} || '0'; |
1644
|
0
|
|
0
|
|
|
|
my $color = 0xff - 8 * ($open->{$xy} || 0); |
1645
|
0
|
|
|
|
|
|
$clr = 'a0' . sprintf("%02x",$color) . '00'; |
1646
|
|
|
|
|
|
|
} |
1647
|
0
|
|
|
|
|
|
my $b = ''; |
1648
|
0
|
0
|
|
|
|
|
$b = 'border: 2px white solid;' if exists $path->{$xy}; |
1649
|
0
|
|
|
|
|
|
$html .= " | $c | \n";
1650
|
|
|
|
|
|
|
} |
1651
|
0
|
|
|
|
|
|
$html .= " | \n";
1652
|
|
|
|
|
|
|
} |
1653
|
|
|
|
|
|
|
|
1654
|
0
|
|
|
|
|
|
$html .= "\n | \n"; |
1655
|
|
|
|
|
|
|
|
1656
|
0
|
|
|
|
|
|
$html; |
1657
|
|
|
|
|
|
|
} |
1658
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
1; |
1660
|
|
|
|
|
|
|
__END__ |