File Coverage

blib/lib/Algorithm/QuadTree.pm
Criterion Covered Total %
statement 70 75 93.3
branch 14 18 77.7
condition 3 5 60.0
subroutine 15 16 93.7
pod 7 7 100.0
total 109 121 90.0


line stmt bran cond sub pod time code
1             package Algorithm::QuadTree;
2             $Algorithm::QuadTree::VERSION = '0.9';
3 8     8   993169 use strict;
  8         15  
  8         290  
4 8     8   39 use warnings;
  8         17  
  8         512  
5 8     8   44 use Carp;
  8         14  
  8         1588  
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   4090 require Algorithm::QuadTree::PP;
26 8         28 $backend = 'Algorithm::QuadTree::PP';
27              
28 8         25 my $backend_requested = defined $ENV{ALGORITHM_QUADTREE_BACKEND};
29 8   100     45 my $check_backend = $ENV{ALGORITHM_QUADTREE_BACKEND} || 'Algorithm::QuadTree::XS';
30 8 100       529 if (eval "require $check_backend; 1;") {
    100          
31 6         15 $backend = $check_backend;
32             }
33             elsif ($backend_requested) {
34 1         33 die $@;
35             }
36              
37 7         714 $backend->import;
38             }
39              
40             # List::Util 1.45 added 'uniqstr'
41 7     7   95 use constant HAS_LIST_UTIL => eval { require List::Util; List::Util->VERSION('1.45'); 1 };
  7         19  
  7         15  
  7         30  
  7         111  
  7         596  
42 7 50   7   36 use constant BACKEND_UNIQUE_RESULTS => $backend->can('UNIQUE_RESULTS') ? $backend->UNIQUE_RESULTS : 0;
  7         13  
  7         6574  
43              
44             sub new
45             {
46 6     6 1 1064249 my $self = shift;
47 6   33     43 my $class = ref($self) || $self;
48 6         56 my %args = @_;
49              
50 6         17 my $obj = bless {}, $class;
51              
52 6         25 for my $arg (keys %args) {
53 31 50       76 if ($arg =~ s{^-}{}) {
54 31         98 $obj->{uc $arg} = $args{"-$arg"};
55             }
56             }
57              
58 6         16 for my $arg (qw/xmin ymin xmax ymax depth/) {
59 30 50       61 unless (exists $obj->{uc $arg}) {
60 0         0 carp "- must specify $arg";
61 0         0 return undef;
62             }
63             }
64              
65              
66 6         45 $obj->{ORIGIN} = [0, 0];
67 6         18 $obj->{SCALE} = 1;
68              
69 6         39 _AQT_init($obj);
70              
71 6         51 return $obj;
72             }
73              
74             sub DESTROY
75             {
76 6     6   17475 my ($self) = @_;
77              
78 6         29 _AQT_deinit($self);
79             }
80              
81             # modify coords according to window
82             sub _adjustCoords
83             {
84 6     6   12 my ($self, @coords) = @_;
85              
86 6 100       11 if (@coords == 4) {
    50          
87             # rectangle
88              
89             $_ = $self->{ORIGIN}[0] + $_ / $self->{SCALE}
90 4         15 for $coords[0], $coords[2];
91             $_ = $self->{ORIGIN}[1] + $_ / $self->{SCALE}
92 4         9 for $coords[1], $coords[3];
93             }
94             elsif (@coords == 3) {
95             # circle
96              
97 2         5 $coords[0] = $self->{ORIGIN}[0] + $coords[0] / $self->{SCALE};
98 2         4 $coords[1] = $self->{ORIGIN}[1] + $coords[1] / $self->{SCALE};
99 2         2 $coords[2] /= $self->{SCALE};
100             }
101              
102 6         16 return @coords;
103             }
104              
105             sub add
106             {
107 57     57 1 7319 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       160 unless $self->{SCALE} == 1;
114              
115 57         151 _AQT_addObject($self, $object, @coords);
116              
117 57         139 return;
118             }
119              
120             sub delete
121             {
122 1     1 1 1863 my ($self, $object) = @_;
123              
124 1         7 _AQT_delete($self, $object);
125              
126 1         3 return;
127             }
128              
129             sub clear
130             {
131 3     3 1 14149 my $self = shift;
132              
133 3         13 _AQT_clear($self);
134              
135 3         5 return;
136             }
137              
138             sub getEnclosedObjects
139             {
140 76     76 1 86257 my ($self, @coords) = @_;
141              
142             @coords = $self->_adjustCoords(@coords)
143 76 100       267 unless $self->{SCALE} == 1;
144              
145 76         124 if (BACKEND_UNIQUE_RESULTS) {
146 76         239 return _AQT_findObjects($self, @coords);
147             }
148             else {
149             return _uniq(_AQT_findObjects($self, @coords));
150             }
151             }
152              
153             sub setWindow
154             {
155 2     2 1 5380 my ($self, $sx, $sy, $s) = @_;
156              
157 2         5 $self->{ORIGIN}[0] += $sx / $self->{SCALE};
158 2         5 $self->{ORIGIN}[1] += $sy / $self->{SCALE};
159 2         5 $self->{SCALE} *= $s;
160             }
161              
162             sub resetWindow
163             {
164 1     1 1 2547 my $self = shift;
165              
166 1         4 $self->{ORIGIN}[$_] = 0 for 0 .. 1;
167 1         3 $self->{SCALE} = 1;
168             }
169              
170             # HELPERS
171             # not called in object context
172              
173             sub _uniq
174             {
175 0     0     if (HAS_LIST_UTIL) {
176 0           return [ List::Util::uniqstr(@{$_[0]}) ];
  0            
177             }
178             else {
179             my %temp = map { $_ => $_ } @{$_[0]};
180             return [ values %temp ];
181             }
182             }
183              
184             1;
185              
186             __END__