File Coverage

blib/lib/Algorithm/QuadTree.pm
Criterion Covered Total %
statement 77 82 93.9
branch 15 20 75.0
condition 3 5 60.0
subroutine 17 18 94.4
pod 9 9 100.0
total 121 134 90.3


line stmt bran cond sub pod time code
1             package Algorithm::QuadTree;
2             $Algorithm::QuadTree::VERSION = '1.0';
3 8     8   922524 use strict;
  8         12  
  8         231  
4 8     8   24 use warnings;
  8         17  
  8         417  
5 8     8   39 use Carp;
  8         11  
  8         1172  
6              
7             ###############################
8             #
9             # Creating a new QuadTree objects automatically
10             # segments the given area into quadtrees of the
11             # specified depth.
12             #
13             # Arguments are a hash:
14             #
15             # -xmin => minimum x value
16             # -xmax => maximum x value
17             # -ymin => minimum y value
18             # -ymax => maximum y value
19             # -depth => depth of tree
20             #
21             ###############################
22              
23             my $backend;
24             BEGIN {
25 8     8   3190 require Algorithm::QuadTree::PP;
26 8         23 $backend = 'Algorithm::QuadTree::PP';
27              
28 8         23 my $backend_requested = defined $ENV{ALGORITHM_QUADTREE_BACKEND};
29 8   100     38 my $check_backend = $ENV{ALGORITHM_QUADTREE_BACKEND} || 'Algorithm::QuadTree::XS';
30 8 100       429 if (eval "require $check_backend; 1;") {
    100          
31 6         13 $backend = $check_backend;
32             }
33             elsif ($backend_requested) {
34 1         32 die $@;
35             }
36              
37 7         621 $backend->import;
38             }
39              
40             # List::Util 1.45 added 'uniqstr'
41 7     7   40 use constant HAS_LIST_UTIL => eval { require List::Util; List::Util->VERSION('1.45'); 1 };
  7         13  
  7         14  
  7         20  
  7         91  
  7         490  
42 7 50   7   29 use constant BACKEND_UNIQUE_RESULTS => $backend->can('UNIQUE_RESULTS') ? $backend->UNIQUE_RESULTS : 0;
  7         9  
  7         5693  
43              
44             sub new
45             {
46 6     6 1 876686 my $self = shift;
47 6   33     59 my $class = ref($self) || $self;
48 6         33 my %args = @_;
49              
50 6         14 my $obj = bless {}, $class;
51              
52 6         27 for my $arg (keys %args) {
53 30 50       76 if ($arg =~ s{^-}{}) {
54 30         89 $obj->{uc $arg} = $args{"-$arg"};
55             }
56             }
57              
58 6         18 for my $arg (qw/xmin ymin xmax ymax depth/) {
59 30 50       73 unless (exists $obj->{uc $arg}) {
60 0         0 carp "- must specify $arg";
61 0         0 return undef;
62             }
63             }
64              
65              
66 6         19 $obj->{ORIGIN} = [0, 0];
67 6         18 $obj->{SCALE} = 1;
68              
69 6         56 _AQT_init($obj);
70              
71 6         24 return $obj;
72             }
73              
74             sub DESTROY
75             {
76 6     6   16214 my ($self) = @_;
77              
78 6         28 _AQT_deinit($self);
79             }
80              
81             # modify coords according to window
82             sub _adjustCoords
83             {
84 6     6   9 my ($self, @coords) = @_;
85              
86 6 100       13 if (@coords == 4) {
    50          
87             # rectangle
88              
89             $_ = $self->{ORIGIN}[0] + $_ / $self->{SCALE}
90 4         16 for $coords[0], $coords[2];
91             $_ = $self->{ORIGIN}[1] + $_ / $self->{SCALE}
92 4         30 for $coords[1], $coords[3];
93             }
94             elsif (@coords == 3) {
95             # circle
96              
97 2         24 $coords[0] = $self->{ORIGIN}[0] + $coords[0] / $self->{SCALE};
98 2         6 $coords[1] = $self->{ORIGIN}[1] + $coords[1] / $self->{SCALE};
99 2         3 $coords[2] /= $self->{SCALE};
100             }
101              
102 6         15 return @coords;
103             }
104              
105             sub add
106             {
107 57     57 1 6783 my ($self, $object, @coords) = @_;
108              
109             # assume that $object is unique.
110             # assume coords are (xmin, ymix, xmax, ymax) or (centerx, centery, radius)
111              
112             @coords = $self->_adjustCoords(@coords)
113 57 100       127 unless $self->{SCALE} == 1;
114              
115 57         151 _AQT_addObject($self, $object, @coords);
116              
117 57         100 return;
118             }
119              
120             sub delete
121             {
122 1     1 1 1190 my ($self, $object) = @_;
123              
124 1         6 _AQT_delete($self, $object);
125              
126 1         2 return;
127             }
128              
129             sub clear
130             {
131 3     3 1 11017 my $self = shift;
132              
133 3         17 _AQT_clear($self);
134              
135 3         8 return;
136             }
137              
138             sub get
139             {
140 6     6 1 8744 my ($self, @coords) = @_;
141              
142             @coords = $self->_adjustCoords(@coords)
143 6 50       31 unless $self->{SCALE} == 1;
144              
145 6         10 $self->{CHECK} = 1;
146 6         8 if (BACKEND_UNIQUE_RESULTS) {
147 6         20 return _AQT_findObjects($self, @coords);
148             }
149             else {
150             return _uniq(_AQT_findObjects($self, @coords));
151             }
152             }
153              
154             sub getEnclosedObjects
155             {
156 74     74 1 51751 my ($self, @coords) = @_;
157              
158             @coords = $self->_adjustCoords(@coords)
159 74 100       188 unless $self->{SCALE} == 1;
160              
161 74         113 $self->{CHECK} = 0;
162 74         109 if (BACKEND_UNIQUE_RESULTS) {
163 74         205 return _AQT_findObjects($self, @coords);
164             }
165             else {
166             return _uniq(_AQT_findObjects($self, @coords));
167             }
168             }
169              
170             sub getApprox
171             {
172 2     2 1 2878 goto \&getEnclosedObjects;
173             }
174              
175             sub setWindow
176             {
177 2     2 1 5576 my ($self, $sx, $sy, $s) = @_;
178              
179 2         8 $self->{ORIGIN}[0] += $sx / $self->{SCALE};
180 2         3 $self->{ORIGIN}[1] += $sy / $self->{SCALE};
181 2         4 $self->{SCALE} *= $s;
182             }
183              
184             sub resetWindow
185             {
186 1     1 1 2790 my $self = shift;
187              
188 1         6 $self->{ORIGIN}[$_] = 0 for 0 .. 1;
189 1         2 $self->{SCALE} = 1;
190             }
191              
192             # HELPERS
193             # not called in object context
194              
195             sub _uniq
196             {
197 0     0     if (HAS_LIST_UTIL) {
198 0           return [ List::Util::uniqstr(@{$_[0]}) ];
  0            
199             }
200             else {
201             my %temp = map { $_ => $_ } @{$_[0]};
202             return [ values %temp ];
203             }
204             }
205              
206             1;
207              
208             __END__