| 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__ |