line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Algorithm::SpatialIndex::Strategy::QuadTree; |
2
|
7
|
|
|
7
|
|
14173
|
use 5.008001; |
|
7
|
|
|
|
|
26
|
|
|
7
|
|
|
|
|
261
|
|
3
|
7
|
|
|
7
|
|
37
|
use strict; |
|
7
|
|
|
|
|
14
|
|
|
7
|
|
|
|
|
236
|
|
4
|
7
|
|
|
7
|
|
38
|
use warnings; |
|
7
|
|
|
|
|
148
|
|
|
7
|
|
|
|
|
200
|
|
5
|
7
|
|
|
7
|
|
41
|
use Carp qw(croak); |
|
7
|
|
|
|
|
11
|
|
|
7
|
|
|
|
|
426
|
|
6
|
|
|
|
|
|
|
|
7
|
7
|
|
|
7
|
|
2092
|
use parent 'Algorithm::SpatialIndex::Strategy::2D'; |
|
7
|
|
|
|
|
1308
|
|
|
7
|
|
|
|
|
34
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# Note that the subnode indexes are as follows: |
10
|
|
|
|
|
|
|
# (like quadrants in planar geometry) |
11
|
|
|
|
|
|
|
# |
12
|
|
|
|
|
|
|
# /---\ |
13
|
|
|
|
|
|
|
# |1|0| |
14
|
|
|
|
|
|
|
# |-+-| |
15
|
|
|
|
|
|
|
# |2+3| |
16
|
|
|
|
|
|
|
# \---/ |
17
|
|
|
|
|
|
|
# |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
use constant { |
20
|
7
|
|
|
|
|
1178
|
XI => 1, # item X coord index |
21
|
|
|
|
|
|
|
YI => 2, # item Y coord index |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
XLOW => 0, # for access to node coords |
24
|
|
|
|
|
|
|
YLOW => 1, |
25
|
|
|
|
|
|
|
XUP => 2, |
26
|
|
|
|
|
|
|
YUP => 3, |
27
|
|
|
|
|
|
|
XSPLIT => 4, |
28
|
|
|
|
|
|
|
YSPLIT => 5, |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
UPPER_RIGHT_NODE => 0, |
31
|
|
|
|
|
|
|
UPPER_LEFT_NODE => 1, |
32
|
|
|
|
|
|
|
LOWER_LEFT_NODE => 2, |
33
|
|
|
|
|
|
|
LOWER_RIGHT_NODE => 3, |
34
|
7
|
|
|
7
|
|
595
|
}; |
|
7
|
|
|
|
|
12
|
|
35
|
|
|
|
|
|
|
|
36
|
7
|
|
|
7
|
|
40
|
use Exporter 'import'; |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
703
|
|
37
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
38
|
|
|
|
|
|
|
XI |
39
|
|
|
|
|
|
|
YI |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
XLOW |
42
|
|
|
|
|
|
|
YLOW |
43
|
|
|
|
|
|
|
XUP |
44
|
|
|
|
|
|
|
YUP |
45
|
|
|
|
|
|
|
XSPLIT |
46
|
|
|
|
|
|
|
YSPLIT |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
UPPER_RIGHT_NODE |
49
|
|
|
|
|
|
|
UPPER_LEFT_NODE |
50
|
|
|
|
|
|
|
LOWER_LEFT_NODE |
51
|
|
|
|
|
|
|
LOWER_RIGHT_NODE |
52
|
|
|
|
|
|
|
); |
53
|
|
|
|
|
|
|
our %EXPORT_TAGS = ('all' => \@EXPORT_OK); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
use Class::XSAccessor { |
56
|
7
|
|
|
|
|
80
|
getters => [qw( |
57
|
|
|
|
|
|
|
top_node_id |
58
|
|
|
|
|
|
|
bucket_size |
59
|
|
|
|
|
|
|
max_depth |
60
|
|
|
|
|
|
|
total_width |
61
|
|
|
|
|
|
|
)], |
62
|
7
|
|
|
7
|
|
36
|
}; |
|
7
|
|
|
|
|
13
|
|
63
|
|
|
|
|
|
|
|
64
|
1
|
|
|
1
|
1
|
12
|
sub coord_types { qw(double double double double double double) } |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
sub init { |
67
|
2
|
|
|
2
|
1
|
7
|
my $self = shift; |
68
|
|
|
|
|
|
|
} |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub init_storage { |
71
|
2
|
|
|
2
|
1
|
4
|
my $self = shift; |
72
|
2
|
|
|
|
|
16
|
my $index = $self->index; |
73
|
2
|
|
|
|
|
13
|
my $storage = $self->storage; |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# stored bucket_size/max_depth for persistent indexes |
76
|
2
|
|
|
|
|
9
|
$self->{bucket_size} = $storage->get_option('bucket_size'); |
77
|
2
|
|
|
|
|
8
|
$self->{max_depth} = $storage->get_option('max_depth'); |
78
|
|
|
|
|
|
|
# or use configured ones |
79
|
2
|
50
|
|
|
|
16
|
$self->{bucket_size} = $index->bucket_size if not defined $self->bucket_size; |
80
|
2
|
50
|
|
|
|
15
|
$self->{max_depth} = $index->max_depth if not defined $self->max_depth; |
81
|
|
|
|
|
|
|
|
82
|
2
|
|
|
|
|
7
|
$self->{top_node_id} = $storage->get_option('top_node_id'); |
83
|
2
|
50
|
|
|
|
10
|
if (not defined $self->top_node_id) { |
84
|
|
|
|
|
|
|
# create a new top node and its bucket |
85
|
2
|
|
|
|
|
34
|
my $node = Algorithm::SpatialIndex::Node->new( |
86
|
|
|
|
|
|
|
coords => [ |
87
|
|
|
|
|
|
|
$index->limit_x_low, $index->limit_y_low, |
88
|
|
|
|
|
|
|
$index->limit_x_up, $index->limit_y_up, |
89
|
|
|
|
|
|
|
undef, undef, |
90
|
|
|
|
|
|
|
], |
91
|
|
|
|
|
|
|
subnode_ids => [], |
92
|
|
|
|
|
|
|
); |
93
|
2
|
|
|
|
|
9
|
$self->{top_node_id} = $storage->store_node($node); |
94
|
2
|
|
|
|
|
9
|
$self->_make_bucket_for_node($node, $storage); |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
2
|
|
|
|
|
21
|
$self->{total_width} = $index->limit_x_up - $index->limit_x_low; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub insert { |
101
|
143
|
|
|
143
|
1
|
220
|
my ($self, $id, $x, $y) = @_; |
102
|
143
|
|
|
|
|
188
|
my $storage = $self->{storage}; # hash access due to hot path |
103
|
143
|
|
|
|
|
392
|
my $top_node = $storage->fetch_node($self->{top_node_id}); # hash access due to hot path |
104
|
143
|
|
|
|
|
297
|
return $self->_insert($id, $x, $y, $top_node, $storage); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
|
107
|
7
|
|
|
|
|
10089
|
SCOPE: { |
108
|
7
|
|
|
7
|
|
4993
|
no warnings 'recursion'; |
|
7
|
|
|
|
|
17
|
|
109
|
|
|
|
|
|
|
sub _insert { |
110
|
454
|
|
|
454
|
|
615
|
my ($self, $id, $x, $y, $node, $storage) = @_; |
111
|
454
|
|
|
|
|
617
|
my $nxy = $node->coords; |
112
|
454
|
|
|
|
|
584
|
my $subnodes = $node->subnode_ids; |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# If we have a bucket, we are the last level of nodes |
115
|
454
|
|
|
|
|
1331
|
SCOPE: { |
116
|
454
|
|
|
|
|
474
|
my $bucket = $storage->fetch_bucket($node->id); |
117
|
454
|
100
|
|
|
|
915
|
if (defined $bucket) { |
118
|
162
|
100
|
66
|
|
|
408
|
if ($bucket->nitems < $self->{bucket_size}) { |
|
|
100
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# sufficient space in bucket. Insert and return |
120
|
135
|
|
|
|
|
439
|
$bucket->add_items([$id, $x, $y]); |
121
|
135
|
|
|
|
|
338
|
$storage->store_bucket($bucket); |
122
|
135
|
|
|
|
|
575
|
return(); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
# check whether we've reached the maximum depth of the tree |
125
|
|
|
|
|
|
|
# and ignore bucket size if necessary |
126
|
|
|
|
|
|
|
# ( total width / local width ) = 2^( depth ) |
127
|
|
|
|
|
|
|
elsif ($nxy->[XUP] - $nxy->[XLOW] <= 0. |
128
|
|
|
|
|
|
|
or log($self->total_width / ($nxy->[XUP]-$nxy->[XLOW])) / log(2) >= $self->max_depth) |
129
|
|
|
|
|
|
|
{ |
130
|
|
|
|
|
|
|
# bucket at the maximum depth. Insert and return |
131
|
8
|
|
|
|
|
27
|
$bucket->add_items([$id, $x, $y]); |
132
|
8
|
|
|
|
|
54
|
$storage->store_bucket($bucket); |
133
|
8
|
|
|
|
|
34
|
return(); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
else { |
136
|
|
|
|
|
|
|
# bucket full, need to add new layer of nodes and split the bucket |
137
|
19
|
|
|
|
|
49
|
$self->_split_node($node, $bucket); |
138
|
|
|
|
|
|
|
# refresh data that will have changed: |
139
|
19
|
|
|
|
|
60
|
$node = $storage->fetch_node($node->id); # has updated subnode ids |
140
|
19
|
|
|
|
|
83
|
$subnodes = $node->subnode_ids; |
141
|
|
|
|
|
|
|
# Now we just continue with the normal subnode checking below: |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
} # end scope |
145
|
|
|
|
|
|
|
|
146
|
311
|
|
|
|
|
327
|
my $subnode_index; |
147
|
311
|
100
|
|
|
|
543
|
if ($x <= $nxy->[XSPLIT]) { |
148
|
124
|
100
|
|
|
|
194
|
if ($y <= $nxy->[YSPLIT]) { $subnode_index = LOWER_LEFT_NODE } |
|
60
|
|
|
|
|
76
|
|
149
|
64
|
|
|
|
|
89
|
else { $subnode_index = UPPER_LEFT_NODE } |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
else { |
152
|
187
|
100
|
|
|
|
318
|
if ($y <= $nxy->[YSPLIT]) { $subnode_index = LOWER_RIGHT_NODE } |
|
90
|
|
|
|
|
106
|
|
153
|
97
|
|
|
|
|
125
|
else { $subnode_index = UPPER_RIGHT_NODE } |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
311
|
50
|
|
|
|
537
|
if (not defined $subnodes->[$subnode_index]) { |
157
|
0
|
|
|
|
|
0
|
die("Cannot find subnode $subnode_index if node id=".$node->id); |
158
|
|
|
|
|
|
|
} |
159
|
|
|
|
|
|
|
else { |
160
|
311
|
|
|
|
|
757
|
my $subnode = $storage->fetch_node($subnodes->[$subnode_index]); |
161
|
311
|
50
|
|
|
|
596
|
die("Need node '" .$subnodes->[$subnode_index] . '", but it is not in storage!') |
162
|
|
|
|
|
|
|
if not defined $subnode; |
163
|
311
|
|
|
|
|
596
|
return $self->_insert($id, $x, $y, $subnode, $storage); |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
} # end SCOPE |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub _node_split_coords { |
169
|
|
|
|
|
|
|
# args: $self, $node, $bucket, $coords |
170
|
20
|
|
|
20
|
|
30
|
my $c = $_[3]; |
171
|
20
|
|
|
|
|
64
|
return( ($c->[0]+$c->[2])/2, ($c->[1]+$c->[3])/2 ); |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
# Splits the given node into four new nodes of equal |
176
|
|
|
|
|
|
|
# size and assigns the items |
177
|
|
|
|
|
|
|
sub _split_node { |
178
|
19
|
|
|
19
|
|
23
|
my $self = shift; |
179
|
19
|
|
|
|
|
37
|
my $parent_node = shift; |
180
|
19
|
|
|
|
|
22
|
my $bucket = shift; # just for speed, can be taken from parent_node |
181
|
|
|
|
|
|
|
|
182
|
19
|
|
|
|
|
44
|
my $storage = $self->storage; |
183
|
19
|
|
|
|
|
30
|
my $parent_node_id = $parent_node->id; |
184
|
19
|
50
|
|
|
|
37
|
$bucket = $storage->fetch_bucket($parent_node_id) if not defined $bucket; |
185
|
|
|
|
|
|
|
|
186
|
19
|
|
|
|
|
27
|
my $coords = $parent_node->coords; |
187
|
19
|
|
|
|
|
39
|
my ($splitx, $splity) = $self->_node_split_coords($parent_node, $bucket, $coords); |
188
|
19
|
|
|
|
|
58
|
@$coords[XSPLIT, YSPLIT] = ($splitx, $splity); # stored below |
189
|
19
|
|
|
|
|
27
|
my @child_nodes; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# UPPER_RIGHT_NODE => 0 |
192
|
19
|
|
|
|
|
122
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
193
|
|
|
|
|
|
|
coords => [$splitx, $splity, $coords->[XUP], $coords->[YUP], undef, undef], |
194
|
|
|
|
|
|
|
subnode_ids => [], |
195
|
|
|
|
|
|
|
); |
196
|
|
|
|
|
|
|
# UPPER_LEFT_NODE => 1 |
197
|
19
|
|
|
|
|
136
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
198
|
|
|
|
|
|
|
coords => [$coords->[XLOW], $splity, $splitx, $coords->[YUP], undef, undef], |
199
|
|
|
|
|
|
|
subnode_ids => [], |
200
|
|
|
|
|
|
|
); |
201
|
|
|
|
|
|
|
# LOWER_LEFT_NODE => 2 |
202
|
19
|
|
|
|
|
140
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
203
|
|
|
|
|
|
|
coords => [$coords->[XLOW], $coords->[YLOW], $splitx, $splity, undef, undef], |
204
|
|
|
|
|
|
|
subnode_ids => [], |
205
|
|
|
|
|
|
|
); |
206
|
|
|
|
|
|
|
# LOWER_RIGHT_NODE => 3 |
207
|
19
|
|
|
|
|
114
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
208
|
|
|
|
|
|
|
coords => [$splitx, $coords->[YLOW], $coords->[XUP], $splity, undef, undef], |
209
|
|
|
|
|
|
|
subnode_ids => [], |
210
|
|
|
|
|
|
|
); |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# save nodes |
213
|
19
|
|
|
|
|
41
|
my $snode_ids = $parent_node->subnode_ids; |
214
|
19
|
|
|
|
|
30
|
foreach my $cnode (@child_nodes) { |
215
|
76
|
|
|
|
|
88
|
push @{$snode_ids}, $storage->store_node($cnode); |
|
76
|
|
|
|
|
220
|
|
216
|
|
|
|
|
|
|
} |
217
|
19
|
|
|
|
|
57
|
$storage->store_node($parent_node); |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# split bucket |
220
|
19
|
|
|
|
|
36
|
my $items = $bucket->items; |
221
|
19
|
|
|
|
|
58
|
my @child_items = ([], [], [], []); |
222
|
19
|
|
|
|
|
34
|
foreach my $item (@$items) { |
223
|
89
|
100
|
|
|
|
167
|
if ($item->[XI] <= $splitx) { |
224
|
86
|
100
|
|
|
|
161
|
if ($item->[YI] <= $splity) { push @{$child_items[LOWER_LEFT_NODE]}, $item } |
|
54
|
|
|
|
|
54
|
|
|
54
|
|
|
|
|
120
|
|
225
|
32
|
|
|
|
|
35
|
else { push @{$child_items[UPPER_LEFT_NODE]}, $item } |
|
32
|
|
|
|
|
74
|
|
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
else { |
228
|
3
|
50
|
|
|
|
9
|
if ($item->[YI] <= $splity) { push @{$child_items[LOWER_RIGHT_NODE]}, $item } |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
9
|
|
229
|
0
|
|
|
|
|
0
|
else { push @{$child_items[UPPER_RIGHT_NODE]}, $item } |
|
0
|
|
|
|
|
0
|
|
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# generate buckets |
234
|
19
|
|
|
|
|
35
|
foreach my $subnode_idx (0..3) { |
235
|
76
|
|
|
|
|
177
|
$self->_make_bucket_for_node( |
236
|
|
|
|
|
|
|
$child_nodes[$subnode_idx], |
237
|
|
|
|
|
|
|
$storage, |
238
|
|
|
|
|
|
|
$child_items[$subnode_idx] |
239
|
|
|
|
|
|
|
); |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
# remove the parent node's bucket |
243
|
19
|
|
|
|
|
56
|
$storage->delete_bucket($bucket); |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
sub _make_bucket_for_node { |
247
|
78
|
|
|
78
|
|
102
|
my $self = shift; |
248
|
78
|
|
|
|
|
85
|
my $node_id = shift; |
249
|
78
|
|
33
|
|
|
147
|
my $storage = shift || $self->storage; |
250
|
78
|
|
100
|
|
|
156
|
my $items = shift || []; |
251
|
78
|
50
|
|
|
|
211
|
$node_id = $node_id->id if ref $node_id; |
252
|
|
|
|
|
|
|
|
253
|
78
|
|
|
|
|
424
|
my $b = $storage->bucket_class->new( |
254
|
|
|
|
|
|
|
node_id => $node_id, |
255
|
|
|
|
|
|
|
items => $items, |
256
|
|
|
|
|
|
|
); |
257
|
78
|
|
|
|
|
208
|
$storage->store_bucket($b); |
258
|
|
|
|
|
|
|
} |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub find_node_for { |
262
|
10
|
|
|
10
|
1
|
6282
|
my ($self, $x, $y) = @_; |
263
|
10
|
|
|
|
|
27
|
my $storage = $self->storage; |
264
|
10
|
|
|
|
|
44
|
my $topnode = $storage->fetch_node($self->top_node_id); |
265
|
10
|
|
|
|
|
29
|
my $coords = $topnode->coords; |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
# boundary check |
268
|
10
|
50
|
100
|
|
|
118
|
if ($x < $coords->[XLOW] |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
269
|
|
|
|
|
|
|
or $x > $coords->[XUP] |
270
|
|
|
|
|
|
|
or $y < $coords->[YLOW] |
271
|
|
|
|
|
|
|
or $y > $coords->[YUP]) { |
272
|
3
|
|
|
|
|
14
|
return undef; |
273
|
|
|
|
|
|
|
} |
274
|
|
|
|
|
|
|
|
275
|
7
|
|
|
|
|
22
|
return $self->_find_node_for($x, $y, $storage, $topnode); |
276
|
|
|
|
|
|
|
} |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
# TODO: This is almost trivial to rewrite in non-recursive form |
279
|
7
|
|
|
|
|
5094
|
SCOPE: { |
280
|
7
|
|
|
7
|
|
42
|
no warnings 'recursion'; |
|
7
|
|
|
|
|
13
|
|
281
|
|
|
|
|
|
|
sub _find_node_for { |
282
|
27
|
|
|
27
|
|
146
|
my ($self, $x, $y, $storage, $node) = @_; |
283
|
|
|
|
|
|
|
|
284
|
27
|
|
|
|
|
57
|
my $snode_ids = $node->subnode_ids; |
285
|
27
|
100
|
|
|
|
87
|
return $node if not @$snode_ids; |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# find the right sub node |
288
|
20
|
|
|
|
|
25
|
my ($splitx, $splity) = @{$node->coords}[XSPLIT, YSPLIT]; |
|
20
|
|
|
|
|
53
|
|
289
|
20
|
|
|
|
|
22
|
my $subnode_id; |
290
|
20
|
100
|
|
|
|
41
|
if ($x <= $splitx) { |
291
|
11
|
100
|
|
|
|
24
|
if ($y <= $splity) { $subnode_id = $snode_ids->[LOWER_LEFT_NODE] } |
|
7
|
|
|
|
|
24
|
|
292
|
4
|
|
|
|
|
7
|
else { $subnode_id = $snode_ids->[UPPER_LEFT_NODE] } |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
else { |
295
|
9
|
100
|
|
|
|
22
|
if ($y <= $splity) { $subnode_id = $snode_ids->[LOWER_RIGHT_NODE] } |
|
5
|
|
|
|
|
10
|
|
296
|
4
|
|
|
|
|
7
|
else { $subnode_id = $snode_ids->[UPPER_RIGHT_NODE] } |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
|
299
|
20
|
|
|
|
|
52
|
my $snode = $storage->fetch_node($subnode_id); |
300
|
20
|
|
|
|
|
51
|
return $self->_find_node_for($x, $y, $storage, $snode); |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
} # end SCOPE |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
sub find_nodes_for { |
306
|
6
|
|
|
6
|
1
|
7990
|
my ($self, $x1, $y1, $x2, $y2) = @_; |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# normalize coords |
309
|
6
|
50
|
|
|
|
22
|
my ($xl, $xu) = $x1 < $x2 ? ($x1, $x2) : ($x2, $x1); |
310
|
6
|
50
|
|
|
|
17
|
my ($yl, $yu) = $y1 < $y2 ? ($y1, $y2) : ($y2, $y1); |
311
|
|
|
|
|
|
|
|
312
|
6
|
|
|
|
|
19
|
my $storage = $self->storage; |
313
|
6
|
|
|
|
|
28
|
my $topnode = $storage->fetch_node($self->top_node_id); |
314
|
6
|
|
|
|
|
18
|
my $coords = $topnode->coords; |
315
|
|
|
|
|
|
|
|
316
|
6
|
|
|
|
|
10
|
my $rv = []; |
317
|
6
|
|
|
|
|
17
|
_find_nodes_for($self, $xl, $yl, $xu, $yu, $storage, $topnode, $rv); |
318
|
6
|
|
|
|
|
76
|
return @$rv; |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
sub _find_nodes_for { |
322
|
338
|
|
|
338
|
|
505
|
my ($self, $xl, $yl, $xu, $yu, $storage, $node, $rv) = @_; |
323
|
|
|
|
|
|
|
|
324
|
338
|
|
|
|
|
481
|
my $coords = $node->coords; |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# boundary check |
327
|
338
|
100
|
100
|
|
|
2881
|
if ( $xu < $coords->[XLOW] |
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
328
|
|
|
|
|
|
|
or $xl > $coords->[XUP] |
329
|
|
|
|
|
|
|
or $yu < $coords->[YLOW] |
330
|
|
|
|
|
|
|
or $yl > $coords->[YUP]) |
331
|
|
|
|
|
|
|
{ |
332
|
21
|
|
|
|
|
53
|
return; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
|
335
|
317
|
|
|
|
|
707
|
my $snode_ids = $node->subnode_ids; |
336
|
317
|
100
|
|
|
|
583
|
if (not @$snode_ids) { |
337
|
|
|
|
|
|
|
# leaf |
338
|
234
|
|
|
|
|
299
|
push @$rv, $node; |
339
|
234
|
|
|
|
|
525
|
return; |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# not a leaf |
343
|
83
|
|
|
|
|
114
|
foreach my $id (@$snode_ids) { |
344
|
332
|
|
|
|
|
1466
|
$self->_find_nodes_for( |
345
|
|
|
|
|
|
|
$xl, $yl, $xu, $yu, $storage, |
346
|
|
|
|
|
|
|
$storage->fetch_node($id), |
347
|
|
|
|
|
|
|
$rv |
348
|
|
|
|
|
|
|
); |
349
|
|
|
|
|
|
|
} |
350
|
|
|
|
|
|
|
} |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# Returns the leaves for the given node |
353
|
|
|
|
|
|
|
sub _get_all_leaf_nodes { |
354
|
0
|
|
|
0
|
|
|
my $self = shift; |
355
|
0
|
|
|
|
|
|
my $node = shift; |
356
|
0
|
|
|
|
|
|
my $storage = $self->storage; |
357
|
|
|
|
|
|
|
|
358
|
0
|
|
|
|
|
|
my @leaves; |
359
|
0
|
|
|
|
|
|
my @nodes = ($node); |
360
|
0
|
|
|
|
|
|
while (@nodes) { |
361
|
0
|
|
|
|
|
|
$node = shift @nodes; |
362
|
0
|
|
|
|
|
|
my $snode_ids = $node->subnode_ids; |
363
|
0
|
0
|
|
|
|
|
if (@$snode_ids) { |
364
|
0
|
|
|
|
|
|
push @nodes, map $storage->fetch_node($_), @$snode_ids; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
else { |
367
|
0
|
|
|
|
|
|
push @leaves, $node; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
} |
370
|
|
|
|
|
|
|
|
371
|
0
|
|
|
|
|
|
return @leaves; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
1; |
375
|
|
|
|
|
|
|
__END__ |