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