line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Algorithm::SpatialIndex::Strategy::OctTree; |
2
|
7
|
|
|
7
|
|
12799
|
use 5.008001; |
|
7
|
|
|
|
|
23
|
|
|
7
|
|
|
|
|
290
|
|
3
|
7
|
|
|
7
|
|
37
|
use strict; |
|
7
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
189
|
|
4
|
7
|
|
|
7
|
|
34
|
use warnings; |
|
7
|
|
|
|
|
61
|
|
|
7
|
|
|
|
|
208
|
|
5
|
7
|
|
|
7
|
|
37
|
use Carp qw(croak); |
|
7
|
|
|
|
|
10
|
|
|
7
|
|
|
|
|
433
|
|
6
|
|
|
|
|
|
|
|
7
|
7
|
|
|
7
|
|
1755
|
use parent 'Algorithm::SpatialIndex::Strategy::3D'; |
|
7
|
|
|
|
|
643
|
|
|
7
|
|
|
|
|
35
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
# Note that the subnode indexes are as follows: |
10
|
|
|
|
|
|
|
# (like octants, http://en.wikipedia.org/wiki/Octant) |
11
|
|
|
|
|
|
|
# After wikipedia: |
12
|
|
|
|
|
|
|
# |
13
|
|
|
|
|
|
|
# 0) first octant (+, +, +) |
14
|
|
|
|
|
|
|
# 1) top-back-right (−, +, +) |
15
|
|
|
|
|
|
|
# 2) top-back-left (−, −, +) |
16
|
|
|
|
|
|
|
# 3) top-front-left (+, −, +) |
17
|
|
|
|
|
|
|
# 4) bottom-front-left (+, −, −) |
18
|
|
|
|
|
|
|
# 5) bottom-back-left (−, −, −) |
19
|
|
|
|
|
|
|
# 6) bottom-back-right (−, +, −) |
20
|
|
|
|
|
|
|
# 7) bottom-front-right (+, +, −) |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
use constant { |
24
|
7
|
|
|
|
|
1808
|
XI => 1, # item X coord index |
25
|
|
|
|
|
|
|
YI => 2, # item Y coord index |
26
|
|
|
|
|
|
|
ZI => 3, # item Z coord index |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
XLOW => 0, # for access to node coords |
29
|
|
|
|
|
|
|
YLOW => 1, |
30
|
|
|
|
|
|
|
ZLOW => 2, |
31
|
|
|
|
|
|
|
XUP => 3, |
32
|
|
|
|
|
|
|
YUP => 4, |
33
|
|
|
|
|
|
|
ZUP => 5, |
34
|
|
|
|
|
|
|
XSPLIT => 6, |
35
|
|
|
|
|
|
|
YSPLIT => 7, |
36
|
|
|
|
|
|
|
ZSPLIT => 8, |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
PPP_NODE => 0, |
39
|
|
|
|
|
|
|
MPP_NODE => 1, |
40
|
|
|
|
|
|
|
MMP_NODE => 2, |
41
|
|
|
|
|
|
|
PMP_NODE => 3, |
42
|
|
|
|
|
|
|
PMM_NODE => 4, |
43
|
|
|
|
|
|
|
MMM_NODE => 5, |
44
|
|
|
|
|
|
|
MPM_NODE => 6, |
45
|
|
|
|
|
|
|
PPM_NODE => 7, |
46
|
7
|
|
|
7
|
|
629
|
}; |
|
7
|
|
|
|
|
18
|
|
47
|
|
|
|
|
|
|
|
48
|
7
|
|
|
7
|
|
40
|
use Exporter 'import'; |
|
7
|
|
|
|
|
17
|
|
|
7
|
|
|
|
|
678
|
|
49
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
50
|
|
|
|
|
|
|
XI |
51
|
|
|
|
|
|
|
YI |
52
|
|
|
|
|
|
|
ZI |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
XLOW |
55
|
|
|
|
|
|
|
YLOW |
56
|
|
|
|
|
|
|
ZLOW |
57
|
|
|
|
|
|
|
XUP |
58
|
|
|
|
|
|
|
YUP |
59
|
|
|
|
|
|
|
ZUP |
60
|
|
|
|
|
|
|
XSPLIT |
61
|
|
|
|
|
|
|
YSPLIT |
62
|
|
|
|
|
|
|
ZSPLIT |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
PPP_NODE |
65
|
|
|
|
|
|
|
MPP_NODE |
66
|
|
|
|
|
|
|
MMP_NODE |
67
|
|
|
|
|
|
|
PMP_NODE |
68
|
|
|
|
|
|
|
PMM_NODE |
69
|
|
|
|
|
|
|
MMM_NODE |
70
|
|
|
|
|
|
|
MPM_NODE |
71
|
|
|
|
|
|
|
PPM_NODE |
72
|
|
|
|
|
|
|
); |
73
|
|
|
|
|
|
|
our %EXPORT_TAGS = ('all' => \@EXPORT_OK); |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
use Class::XSAccessor { |
76
|
7
|
|
|
|
|
79
|
getters => [qw( |
77
|
|
|
|
|
|
|
top_node_id |
78
|
|
|
|
|
|
|
bucket_size |
79
|
|
|
|
|
|
|
max_depth |
80
|
|
|
|
|
|
|
total_width |
81
|
|
|
|
|
|
|
)], |
82
|
7
|
|
|
7
|
|
38
|
}; |
|
7
|
|
|
|
|
12
|
|
83
|
|
|
|
|
|
|
|
84
|
1
|
|
|
1
|
1
|
14
|
sub coord_types { qw(double double double double double double double double double) } # 9 doubles |
85
|
|
|
|
|
|
|
|
86
|
1
|
|
|
1
|
1
|
3
|
sub init {} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub init_storage { |
89
|
1
|
|
|
1
|
1
|
3
|
my $self = shift; |
90
|
1
|
|
|
|
|
8
|
my $index = $self->index; |
91
|
1
|
|
|
|
|
8
|
my $storage = $self->storage; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# stored bucket_size for persistent indexes |
94
|
1
|
|
|
|
|
3
|
$self->{bucket_size} = $storage->get_option('bucket_size'); |
95
|
1
|
|
|
|
|
4
|
$self->{max_depth} = $storage->get_option('max_depth'); |
96
|
|
|
|
|
|
|
# or use configured one |
97
|
1
|
50
|
|
|
|
9
|
$self->{bucket_size} = $index->bucket_size if not defined $self->bucket_size; |
98
|
1
|
50
|
|
|
|
8
|
$self->{max_depth} = $index->max_depth if not defined $self->max_depth; |
99
|
|
|
|
|
|
|
|
100
|
1
|
|
|
|
|
4
|
$self->{top_node_id} = $storage->get_option('top_node_id'); |
101
|
1
|
50
|
|
|
|
6
|
if (not defined $self->top_node_id) { |
102
|
|
|
|
|
|
|
# create a new top node and its bucket |
103
|
1
|
|
|
|
|
22
|
my $node = Algorithm::SpatialIndex::Node->new( |
104
|
|
|
|
|
|
|
coords => [ |
105
|
|
|
|
|
|
|
$index->limit_x_low, $index->limit_y_low, $index->limit_z_low, |
106
|
|
|
|
|
|
|
$index->limit_x_up, $index->limit_y_up, $index->limit_z_up, |
107
|
|
|
|
|
|
|
undef, undef, undef, |
108
|
|
|
|
|
|
|
], |
109
|
|
|
|
|
|
|
subnode_ids => [], |
110
|
|
|
|
|
|
|
); |
111
|
1
|
|
|
|
|
5
|
$self->{top_node_id} = $storage->store_node($node); |
112
|
1
|
|
|
|
|
6
|
$self->_make_bucket_for_node($node, $storage); |
113
|
|
|
|
|
|
|
} |
114
|
|
|
|
|
|
|
|
115
|
1
|
|
|
|
|
8
|
$self->{total_width} = $index->limit_x_up - $index->limit_x_low; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub insert { |
119
|
240
|
|
|
240
|
1
|
332
|
my ($self, $id, $x, $y, $z) = @_; |
120
|
240
|
|
|
|
|
332
|
my $storage = $self->{storage}; # hash access due to hot path |
121
|
240
|
|
|
|
|
664
|
my $top_node = $storage->fetch_node($self->{top_node_id}); # hash access due to hot path |
122
|
240
|
|
|
|
|
541
|
return $self->_insert($id, $x, $y, $z, $top_node, $storage); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
|
125
|
7
|
|
|
|
|
13807
|
SCOPE: { |
126
|
7
|
|
|
7
|
|
4798
|
no warnings 'recursion'; |
|
7
|
|
|
|
|
26
|
|
127
|
|
|
|
|
|
|
sub _insert { |
128
|
691
|
|
|
691
|
|
1100
|
my ($self, $id, $x, $y, $z, $node, $storage) = @_; |
129
|
691
|
|
|
|
|
966
|
my $nxyz = $node->coords; |
130
|
691
|
|
|
|
|
838
|
my $subnodes = $node->subnode_ids; |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# If we have a bucket, we are the last level of nodes |
133
|
691
|
|
|
|
|
1852
|
SCOPE: { |
134
|
691
|
|
|
|
|
713
|
my $bucket = $storage->fetch_bucket($node->id); |
135
|
691
|
100
|
|
|
|
1475
|
if (defined $bucket) { |
136
|
265
|
|
|
|
|
411
|
my $items = $bucket->items; |
137
|
265
|
100
|
33
|
|
|
733
|
if (@$items < $self->{bucket_size}) { |
|
|
50
|
|
|
|
|
|
138
|
|
|
|
|
|
|
# sufficient space in bucket. Insert and return |
139
|
240
|
|
|
|
|
238
|
push @{$items}, [$id, $x, $y, $z]; |
|
240
|
|
|
|
|
655
|
|
140
|
240
|
|
|
|
|
662
|
$storage->store_bucket($bucket); |
141
|
240
|
|
|
|
|
1042
|
return(); |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
# check whether we've reached the maximum depth of the tree |
144
|
|
|
|
|
|
|
# and ignore bucket size if necessary |
145
|
|
|
|
|
|
|
# ( total width / local width ) = 2^( depth ) |
146
|
|
|
|
|
|
|
elsif ($nxyz->[XUP] - $nxyz->[XLOW] <= 0. |
147
|
|
|
|
|
|
|
or log($self->total_width / ($nxyz->[XUP]-$nxyz->[XLOW])) / log(2) >= $self->max_depth) |
148
|
|
|
|
|
|
|
{ |
149
|
|
|
|
|
|
|
# bucket at the maximum depth. Insert and return |
150
|
0
|
|
|
|
|
0
|
push @{$items}, [$id, $x, $y]; |
|
0
|
|
|
|
|
0
|
|
151
|
0
|
|
|
|
|
0
|
$storage->store_bucket($bucket); |
152
|
0
|
|
|
|
|
0
|
return(); |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
else { |
155
|
|
|
|
|
|
|
# bucket full, need to add new layer of nodes and split the bucket |
156
|
25
|
|
|
|
|
60
|
$self->_split_node($node, $bucket); |
157
|
|
|
|
|
|
|
# refresh data that will have changed: |
158
|
25
|
|
|
|
|
86
|
$node = $storage->fetch_node($node->id); # has updated subnode ids |
159
|
25
|
|
|
|
|
107
|
$subnodes = $node->subnode_ids; |
160
|
|
|
|
|
|
|
# Now we just continue with the normal subnode checking below: |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
} # end scope |
164
|
|
|
|
|
|
|
|
165
|
451
|
|
|
|
|
453
|
my $subnode_index; |
166
|
451
|
100
|
|
|
|
787
|
if ($x <= $nxyz->[XSPLIT]) { |
167
|
203
|
100
|
|
|
|
316
|
if ($y <= $nxyz->[YSPLIT]) { |
168
|
75
|
100
|
|
|
|
112
|
if ($z <= $nxyz->[ZSPLIT]) { $subnode_index = MMM_NODE } |
|
33
|
|
|
|
|
42
|
|
169
|
42
|
|
|
|
|
55
|
else { $subnode_index = MMP_NODE } |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
else { # $y > ysplit |
172
|
128
|
100
|
|
|
|
191
|
if ($z <= $nxyz->[ZSPLIT]) { $subnode_index = MPM_NODE } |
|
60
|
|
|
|
|
89
|
|
173
|
68
|
|
|
|
|
89
|
else { $subnode_index = MPP_NODE } |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
else { # $x > xsplit |
177
|
248
|
100
|
|
|
|
423
|
if ($y <= $nxyz->[YSPLIT]) { |
178
|
120
|
100
|
|
|
|
183
|
if ($z <= $nxyz->[ZSPLIT]) { $subnode_index = PMM_NODE } |
|
60
|
|
|
|
|
80
|
|
179
|
60
|
|
|
|
|
198
|
else { $subnode_index = PMP_NODE } |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
else { # $y > ysplit |
182
|
128
|
100
|
|
|
|
209
|
if ($z <= $nxyz->[ZSPLIT]) { $subnode_index = PPM_NODE } |
|
60
|
|
|
|
|
83
|
|
183
|
68
|
|
|
|
|
94
|
else { $subnode_index = PPP_NODE } |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
451
|
50
|
|
|
|
724
|
if (not defined $subnodes->[$subnode_index]) { |
188
|
0
|
|
|
|
|
0
|
die("Cannot find subnode $subnode_index if node id=".$node->id); |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
else { |
191
|
451
|
|
|
|
|
1174
|
my $subnode = $storage->fetch_node($subnodes->[$subnode_index]); |
192
|
451
|
50
|
|
|
|
927
|
die("Need node '" .$subnodes->[$subnode_index] . '", but it is not in storage!') |
193
|
|
|
|
|
|
|
if not defined $subnode; |
194
|
451
|
|
|
|
|
968
|
return $self->_insert($id, $x, $y, $z, $subnode, $storage); |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
} # end SCOPE |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub _node_split_coords { |
200
|
|
|
|
|
|
|
# args: $self, $node, $bucket, $coords |
201
|
26
|
|
|
26
|
|
33
|
my $c = $_[3]; |
202
|
|
|
|
|
|
|
return( |
203
|
26
|
|
|
|
|
110
|
($c->[XLOW]+$c->[XUP])/2, |
204
|
|
|
|
|
|
|
($c->[YLOW]+$c->[YUP])/2, |
205
|
|
|
|
|
|
|
($c->[ZLOW]+$c->[ZUP])/2, |
206
|
|
|
|
|
|
|
); |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# Splits the given node into four new nodes of equal |
211
|
|
|
|
|
|
|
# size and assigns the items |
212
|
|
|
|
|
|
|
sub _split_node { |
213
|
25
|
|
|
25
|
|
33
|
my $self = shift; |
214
|
25
|
|
|
|
|
25
|
my $parent_node = shift; |
215
|
25
|
|
|
|
|
30
|
my $bucket = shift; # just for speed, can be taken from parent_node |
216
|
|
|
|
|
|
|
|
217
|
25
|
|
|
|
|
41
|
my $storage = $self->storage; |
218
|
25
|
|
|
|
|
40
|
my $parent_node_id = $parent_node->id; |
219
|
25
|
50
|
|
|
|
45
|
$bucket = $storage->fetch_bucket($parent_node_id) if not defined $bucket; |
220
|
|
|
|
|
|
|
|
221
|
25
|
|
|
|
|
41
|
my $coords = $parent_node->coords; |
222
|
25
|
|
|
|
|
53
|
my ($splitx, $splity, $splitz) = $self->_node_split_coords($parent_node, $bucket, $coords); |
223
|
25
|
|
|
|
|
68
|
@$coords[XSPLIT, YSPLIT, ZSPLIT] = ($splitx, $splity, $splitz); # stored below |
224
|
25
|
|
|
|
|
39
|
my @child_nodes; |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
# PPP_NODE |
227
|
25
|
|
|
|
|
198
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
228
|
|
|
|
|
|
|
coords => [$splitx, $splity, $splitz, |
229
|
|
|
|
|
|
|
$coords->[XUP], $coords->[YUP], $coords->[ZUP], |
230
|
|
|
|
|
|
|
undef, undef, undef], |
231
|
|
|
|
|
|
|
subnode_ids => [], |
232
|
|
|
|
|
|
|
); |
233
|
|
|
|
|
|
|
# MPP_NODE |
234
|
25
|
|
|
|
|
198
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
235
|
|
|
|
|
|
|
coords => [$coords->[XLOW], $splity, $splitz, |
236
|
|
|
|
|
|
|
$splitx, $coords->[YUP], $coords->[ZUP], |
237
|
|
|
|
|
|
|
undef, undef, undef], |
238
|
|
|
|
|
|
|
subnode_ids => [], |
239
|
|
|
|
|
|
|
); |
240
|
|
|
|
|
|
|
# MMP_NODE |
241
|
25
|
|
|
|
|
186
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
242
|
|
|
|
|
|
|
coords => [$coords->[XLOW], $coords->[YLOW], $splitz, |
243
|
|
|
|
|
|
|
$splitx, $splity, $coords->[ZUP], |
244
|
|
|
|
|
|
|
undef, undef, undef], |
245
|
|
|
|
|
|
|
subnode_ids => [], |
246
|
|
|
|
|
|
|
); |
247
|
|
|
|
|
|
|
# PMP_NODE |
248
|
25
|
|
|
|
|
182
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
249
|
|
|
|
|
|
|
coords => [$splitx, $coords->[YLOW], $splitz, |
250
|
|
|
|
|
|
|
$coords->[XUP], $splity, $coords->[ZUP], |
251
|
|
|
|
|
|
|
undef, undef, undef], |
252
|
|
|
|
|
|
|
subnode_ids => [], |
253
|
|
|
|
|
|
|
); |
254
|
|
|
|
|
|
|
# PMM_NODE |
255
|
25
|
|
|
|
|
201
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
256
|
|
|
|
|
|
|
coords => [$splitx, $coords->[YLOW], $coords->[ZLOW], |
257
|
|
|
|
|
|
|
$coords->[XUP], $splity, $splitz, |
258
|
|
|
|
|
|
|
undef, undef, undef], |
259
|
|
|
|
|
|
|
subnode_ids => [], |
260
|
|
|
|
|
|
|
); |
261
|
|
|
|
|
|
|
# MMM_NODE |
262
|
25
|
|
|
|
|
186
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
263
|
|
|
|
|
|
|
coords => [$coords->[XLOW], $coords->[YLOW], $coords->[ZLOW], |
264
|
|
|
|
|
|
|
$splitx, $splity, $splitz, |
265
|
|
|
|
|
|
|
undef, undef, undef], |
266
|
|
|
|
|
|
|
subnode_ids => [], |
267
|
|
|
|
|
|
|
); |
268
|
|
|
|
|
|
|
# MPM_NODE |
269
|
25
|
|
|
|
|
171
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
270
|
|
|
|
|
|
|
coords => [$coords->[XLOW], $splity, $coords->[ZLOW], |
271
|
|
|
|
|
|
|
$splitx, $coords->[YUP], $splitz, |
272
|
|
|
|
|
|
|
undef, undef, undef], |
273
|
|
|
|
|
|
|
subnode_ids => [], |
274
|
|
|
|
|
|
|
); |
275
|
|
|
|
|
|
|
# PPM_NODE |
276
|
25
|
|
|
|
|
196
|
push @child_nodes, Algorithm::SpatialIndex::Node->new( |
277
|
|
|
|
|
|
|
coords => [$splitx, $splity, $coords->[ZLOW], |
278
|
|
|
|
|
|
|
$coords->[XUP], $coords->[YUP], $splitz, |
279
|
|
|
|
|
|
|
undef, undef, undef], |
280
|
|
|
|
|
|
|
subnode_ids => [], |
281
|
|
|
|
|
|
|
); |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# save nodes |
284
|
25
|
|
|
|
|
48
|
my $snode_ids = $parent_node->subnode_ids; |
285
|
25
|
|
|
|
|
41
|
foreach my $cnode (@child_nodes) { |
286
|
200
|
|
|
|
|
208
|
push @{$snode_ids}, $storage->store_node($cnode); |
|
200
|
|
|
|
|
569
|
|
287
|
|
|
|
|
|
|
} |
288
|
25
|
|
|
|
|
81
|
$storage->store_node($parent_node); |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
# split bucket |
291
|
25
|
|
|
|
|
70
|
my $items = $bucket->items; |
292
|
25
|
|
|
|
|
170
|
my @child_items = ( map [], @child_nodes ); |
293
|
25
|
|
|
|
|
52
|
foreach my $item (@$items) { |
294
|
125
|
100
|
|
|
|
228
|
if ($item->[XI] <= $splitx) { |
295
|
85
|
100
|
|
|
|
122
|
if ($item->[YI] <= $splity) { |
296
|
69
|
100
|
|
|
|
113
|
if ($item->[ZI] <= $splitz) { push @{$child_items[MMM_NODE]}, $item } |
|
39
|
|
|
|
|
39
|
|
|
39
|
|
|
|
|
83
|
|
297
|
30
|
|
|
|
|
32
|
else { push @{$child_items[MMP_NODE]}, $item } |
|
30
|
|
|
|
|
70
|
|
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
else { # $item->[YI] > ysplit |
300
|
16
|
100
|
|
|
|
25
|
if ($item->[ZI] <= $splitz) { push @{$child_items[MPM_NODE]}, $item } |
|
12
|
|
|
|
|
13
|
|
|
12
|
|
|
|
|
27
|
|
301
|
4
|
|
|
|
|
5
|
else { push @{$child_items[MPP_NODE]}, $item } |
|
4
|
|
|
|
|
9
|
|
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
} |
304
|
|
|
|
|
|
|
else { # $item->[XI] > xsplit |
305
|
40
|
100
|
|
|
|
72
|
if ($item->[YI] <= $splity) { |
306
|
24
|
100
|
|
|
|
40
|
if ($item->[ZI] <= $splitz) { push @{$child_items[PMM_NODE]}, $item } |
|
12
|
|
|
|
|
11
|
|
|
12
|
|
|
|
|
37
|
|
307
|
12
|
|
|
|
|
11
|
else { push @{$child_items[PMP_NODE]}, $item } |
|
12
|
|
|
|
|
27
|
|
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
else { # $item->[YI] > ysplit |
310
|
16
|
100
|
|
|
|
31
|
if ($item->[ZI] <= $splitz) { push @{$child_items[PPM_NODE]}, $item } |
|
12
|
|
|
|
|
14
|
|
|
12
|
|
|
|
|
44
|
|
311
|
4
|
|
|
|
|
4
|
else { push @{$child_items[PPP_NODE]}, $item } |
|
4
|
|
|
|
|
13
|
|
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
} |
314
|
|
|
|
|
|
|
} |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# generate buckets |
317
|
25
|
|
|
|
|
60
|
foreach my $subnode_idx (0..$#child_nodes) { |
318
|
200
|
|
|
|
|
464
|
$self->_make_bucket_for_node( |
319
|
|
|
|
|
|
|
$child_nodes[$subnode_idx], |
320
|
|
|
|
|
|
|
$storage, |
321
|
|
|
|
|
|
|
$child_items[$subnode_idx] |
322
|
|
|
|
|
|
|
); |
323
|
|
|
|
|
|
|
} |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# remove the parent node's bucket |
326
|
25
|
|
|
|
|
83
|
$storage->delete_bucket($bucket); |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
sub _make_bucket_for_node { |
330
|
201
|
|
|
201
|
|
229
|
my $self = shift; |
331
|
201
|
|
|
|
|
209
|
my $node_id = shift; |
332
|
201
|
|
33
|
|
|
382
|
my $storage = shift || $self->storage; |
333
|
201
|
|
100
|
|
|
358
|
my $items = shift || []; |
334
|
201
|
50
|
|
|
|
477
|
$node_id = $node_id->id if ref $node_id; |
335
|
|
|
|
|
|
|
|
336
|
201
|
|
|
|
|
1056
|
my $b = $storage->bucket_class->new( |
337
|
|
|
|
|
|
|
node_id => $node_id, |
338
|
|
|
|
|
|
|
items => $items, |
339
|
|
|
|
|
|
|
); |
340
|
201
|
|
|
|
|
572
|
$storage->store_bucket($b); |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
sub find_node_for { |
345
|
10
|
|
|
10
|
1
|
4392
|
my ($self, $x, $y, $z) = @_; |
346
|
10
|
|
|
|
|
28
|
my $storage = $self->storage; |
347
|
10
|
|
|
|
|
43
|
my $topnode = $storage->fetch_node($self->top_node_id); |
348
|
10
|
|
|
|
|
23
|
my $coords = $topnode->coords; |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
# boundary check |
351
|
10
|
50
|
100
|
|
|
138
|
if ($x < $coords->[XLOW] |
|
|
|
66
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
33
|
|
|
|
|
352
|
|
|
|
|
|
|
or $x > $coords->[XUP] |
353
|
|
|
|
|
|
|
or $y < $coords->[YLOW] |
354
|
|
|
|
|
|
|
or $y > $coords->[YUP] |
355
|
|
|
|
|
|
|
or $z < $coords->[ZLOW] |
356
|
|
|
|
|
|
|
or $z > $coords->[ZUP]) |
357
|
|
|
|
|
|
|
{ |
358
|
3
|
|
|
|
|
18
|
return undef; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
7
|
|
|
|
|
26
|
return $self->_find_node_for($x, $y, $z, $storage, $topnode); |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
# TODO: This is almost trivial to rewrite in non-recursive form |
365
|
7
|
|
|
|
|
7166
|
SCOPE: { |
366
|
7
|
|
|
7
|
|
54
|
no warnings 'recursion'; |
|
7
|
|
|
|
|
21
|
|
367
|
|
|
|
|
|
|
sub _find_node_for { |
368
|
25
|
|
|
25
|
|
68
|
my ($self, $x, $y, $z, $storage, $node) = @_; |
369
|
|
|
|
|
|
|
|
370
|
25
|
|
|
|
|
59
|
my $snode_ids = $node->subnode_ids; |
371
|
25
|
100
|
|
|
|
85
|
return $node if not @$snode_ids; |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
# find the right sub node |
374
|
18
|
|
|
|
|
23
|
my ($xsplit, $ysplit, $zsplit) = @{$node->coords}[XSPLIT, YSPLIT, ZSPLIT]; |
|
18
|
|
|
|
|
60
|
|
375
|
18
|
|
|
|
|
23
|
my $subnode_id; |
376
|
18
|
100
|
|
|
|
39
|
if ($x <= $xsplit) { |
377
|
10
|
100
|
|
|
|
22
|
if ($y <= $ysplit) { |
378
|
7
|
100
|
|
|
|
16
|
if ($z <= $zsplit) { $subnode_id = $snode_ids->[MMM_NODE] } |
|
1
|
|
|
|
|
4
|
|
379
|
6
|
|
|
|
|
14
|
else { $subnode_id = $snode_ids->[MMP_NODE] } |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
else { # $y > ysplit |
382
|
3
|
50
|
|
|
|
8
|
if ($z <= $zsplit) { $subnode_id = $snode_ids->[MPM_NODE] } |
|
0
|
|
|
|
|
0
|
|
383
|
3
|
|
|
|
|
7
|
else { $subnode_id = $snode_ids->[MPP_NODE] } |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
else { # $x > xsplit |
387
|
8
|
100
|
|
|
|
20
|
if ($y <= $ysplit) { |
388
|
5
|
100
|
|
|
|
12
|
if ($z <= $zsplit) { $subnode_id = $snode_ids->[PMM_NODE] } |
|
4
|
|
|
|
|
9
|
|
389
|
1
|
|
|
|
|
4
|
else { $subnode_id = $snode_ids->[PMP_NODE] } |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
else { # $y > ysplit |
392
|
3
|
100
|
|
|
|
8
|
if ($z <= $zsplit) { $subnode_id = $snode_ids->[PPM_NODE] } |
|
1
|
|
|
|
|
4
|
|
393
|
2
|
|
|
|
|
5
|
else { $subnode_id = $snode_ids->[PPP_NODE] } |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
18
|
|
|
|
|
58
|
my $snode = $storage->fetch_node($subnode_id); |
398
|
18
|
|
|
|
|
66
|
return $self->_find_node_for($x, $y, $z, $storage, $snode); |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
} # end SCOPE |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub find_nodes_for { |
404
|
6
|
|
|
6
|
1
|
1953
|
my ($self, $x1, $y1, $z1, $x2, $y2, $z2) = @_; |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
# normalize coords |
407
|
6
|
50
|
|
|
|
21
|
my ($xl, $xu) = $x1 < $x2 ? ($x1, $x2) : ($x2, $x1); |
408
|
6
|
50
|
|
|
|
19
|
my ($yl, $yu) = $y1 < $y2 ? ($y1, $y2) : ($y2, $y1); |
409
|
6
|
100
|
|
|
|
20
|
my ($zl, $zu) = $z1 < $z2 ? ($z1, $z2) : ($z2, $z1); |
410
|
|
|
|
|
|
|
|
411
|
6
|
|
|
|
|
19
|
my $storage = $self->storage; |
412
|
6
|
|
|
|
|
26
|
my $topnode = $storage->fetch_node($self->top_node_id); |
413
|
6
|
|
|
|
|
16
|
my $coords = $topnode->coords; |
414
|
|
|
|
|
|
|
|
415
|
6
|
|
|
|
|
12
|
my $rv = []; |
416
|
6
|
|
|
|
|
17
|
_find_nodes_for($self, $xl, $yl, $zl, $xu, $yu, $zu, $storage, $topnode, $rv); |
417
|
6
|
|
|
|
|
152
|
return @$rv; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
sub _find_nodes_for { |
421
|
806
|
|
|
806
|
|
1602
|
my ($self, $xl, $yl, $zl, $xu, $yu, $zu, $storage, $node, $rv) = @_; |
422
|
|
|
|
|
|
|
|
423
|
806
|
|
|
|
|
1421
|
my $coords = $node->coords; |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
# boundary check |
426
|
806
|
100
|
100
|
|
|
9821
|
if ( $xu < $coords->[XLOW] |
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
427
|
|
|
|
|
|
|
or $xl > $coords->[XUP] |
428
|
|
|
|
|
|
|
or $yu < $coords->[YLOW] |
429
|
|
|
|
|
|
|
or $yl > $coords->[YUP] |
430
|
|
|
|
|
|
|
or $zu < $coords->[ZLOW] |
431
|
|
|
|
|
|
|
or $zl > $coords->[ZUP]) |
432
|
|
|
|
|
|
|
{ |
433
|
62
|
|
|
|
|
161
|
return; |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
744
|
|
|
|
|
1227
|
my $snode_ids = $node->subnode_ids; |
437
|
744
|
100
|
|
|
|
1516
|
if (not @$snode_ids) { |
438
|
|
|
|
|
|
|
# leaf |
439
|
644
|
|
|
|
|
963
|
push @$rv, $node; |
440
|
644
|
|
|
|
|
1753
|
return; |
441
|
|
|
|
|
|
|
} |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
# not a leaf |
444
|
100
|
|
|
|
|
187
|
foreach my $id (@$snode_ids) { |
445
|
800
|
|
|
|
|
2434
|
$self->_find_nodes_for( |
446
|
|
|
|
|
|
|
$xl, $yl, $zl, $xu, $yu, $zu, $storage, |
447
|
|
|
|
|
|
|
$storage->fetch_node($id), |
448
|
|
|
|
|
|
|
$rv |
449
|
|
|
|
|
|
|
); |
450
|
|
|
|
|
|
|
} |
451
|
|
|
|
|
|
|
} |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
# Returns the leaves for the given node |
454
|
|
|
|
|
|
|
sub _get_all_leaf_nodes { |
455
|
0
|
|
|
0
|
|
|
my $self = shift; |
456
|
0
|
|
|
|
|
|
my $node = shift; |
457
|
0
|
|
|
|
|
|
my $storage = $self->storage; |
458
|
|
|
|
|
|
|
|
459
|
0
|
|
|
|
|
|
my @leaves; |
460
|
0
|
|
|
|
|
|
my @nodes = ($node); |
461
|
0
|
|
|
|
|
|
while (@nodes) { |
462
|
0
|
|
|
|
|
|
$node = shift @nodes; |
463
|
0
|
|
|
|
|
|
my $snode_ids = $node->subnode_ids; |
464
|
0
|
0
|
|
|
|
|
if (@$snode_ids) { |
465
|
0
|
|
|
|
|
|
push @nodes, map $storage->fetch_node($_), @$snode_ids; |
466
|
|
|
|
|
|
|
} |
467
|
|
|
|
|
|
|
else { |
468
|
0
|
|
|
|
|
|
push @leaves, $node; |
469
|
|
|
|
|
|
|
} |
470
|
|
|
|
|
|
|
} |
471
|
|
|
|
|
|
|
|
472
|
0
|
|
|
|
|
|
return @leaves; |
473
|
|
|
|
|
|
|
} |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
1; |
476
|
|
|
|
|
|
|
__END__ |