File Coverage

blib/lib/Algorithm/QuadTree.pm
Criterion Covered Total %
statement 68 73 93.1
branch 13 16 81.2
condition 3 5 60.0
subroutine 15 16 93.7
pod 7 7 100.0
total 106 117 90.6


line stmt bran cond sub pod time code
1             package Algorithm::QuadTree;
2             $Algorithm::QuadTree::VERSION = '0.8';
3 7     7   994416 use strict;
  7         11  
  7         222  
4 7     7   25 use warnings;
  7         14  
  7         461  
5 7     7   38 use Carp;
  7         10  
  7         1147  
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 7     7   3335 require Algorithm::QuadTree::PP;
26 7         22 $backend = 'Algorithm::QuadTree::PP';
27              
28 7         26 my $backend_requested = defined $ENV{ALGORITHM_QUADTREE_BACKEND};
29 7   100     58 my $check_backend = $ENV{ALGORITHM_QUADTREE_BACKEND} || 'Algorithm::QuadTree::XS';
30 7 100       458 if (eval "require $check_backend; 1;") {
    100          
31 5         12 $backend = $check_backend;
32             }
33             elsif ($backend_requested) {
34 1         41 die $@;
35             }
36              
37 6         726 $backend->import;
38             }
39              
40             # List::Util 1.45 added 'uniqstr'
41 6     6   37 use constant HAS_LIST_UTIL => eval { require List::Util; List::Util->VERSION('1.45'); 1 };
  6         11  
  6         9  
  6         18  
  6         81  
  6         438  
42 6 50   6   40 use constant BACKEND_UNIQUE_RESULTS => $backend->can('UNIQUE_RESULTS') ? $backend->UNIQUE_RESULTS : 0;
  6         15  
  6         5537  
43              
44             sub new
45             {
46 5     5 1 993216 my $self = shift;
47 5   33     73 my $class = ref($self) || $self;
48 5         34 my %args = @_;
49              
50 5         25 my $obj = bless {}, $class;
51              
52 5         19 for my $arg (qw/xmin ymin xmax ymax depth/) {
53 25 50       70 unless (exists $args{"-$arg"}) {
54 0         0 carp "- must specify $arg";
55 0         0 return undef;
56             }
57              
58 25         130 $obj->{uc $arg} = $args{"-$arg"};
59             }
60              
61 5         22 $obj->{ORIGIN} = [0, 0];
62 5         16 $obj->{SCALE} = 1;
63              
64 5         33 _AQT_init($obj);
65              
66 5         27 return $obj;
67             }
68              
69             sub DESTROY
70             {
71 5     5   15733 my ($self) = @_;
72              
73 5         25 _AQT_deinit($self);
74             }
75              
76             # modify coords according to window
77             sub _adjustCoords
78             {
79 6     6   18 my ($self, @coords) = @_;
80              
81 6 100       19 if (@coords == 4) {
    50          
82             # rectangle
83              
84             $_ = $self->{ORIGIN}[0] + $_ / $self->{SCALE}
85 4         23 for $coords[0], $coords[2];
86             $_ = $self->{ORIGIN}[1] + $_ / $self->{SCALE}
87 4         14 for $coords[1], $coords[3];
88             }
89             elsif (@coords == 3) {
90             # circle
91              
92 2         8 $coords[0] = $self->{ORIGIN}[0] + $coords[0] / $self->{SCALE};
93 2         6 $coords[1] = $self->{ORIGIN}[1] + $coords[1] / $self->{SCALE};
94 2         4 $coords[2] /= $self->{SCALE};
95             }
96              
97 6         21 return @coords;
98             }
99              
100             sub add
101             {
102 55     55 1 10112 my ($self, $object, @coords) = @_;
103              
104             # assume that $object is unique.
105             # assume coords are (xmin, ymix, xmax, ymax) or (centerx, centery, radius)
106              
107             @coords = $self->_adjustCoords(@coords)
108 55 100       167 unless $self->{SCALE} == 1;
109              
110 55         188 _AQT_addObject($self, $object, @coords);
111              
112 55         189 return;
113             }
114              
115             sub delete
116             {
117 1     1 1 1919 my ($self, $object) = @_;
118              
119 1         6 _AQT_delete($self, $object);
120              
121 1         4 return;
122             }
123              
124             sub clear
125             {
126 3     3 1 13555 my $self = shift;
127              
128 3         18 _AQT_clear($self);
129              
130 3         10 return;
131             }
132              
133             sub getEnclosedObjects
134             {
135 70     70 1 67535 my ($self, @coords) = @_;
136              
137             @coords = $self->_adjustCoords(@coords)
138 70 100       206 unless $self->{SCALE} == 1;
139              
140 70         103 if (BACKEND_UNIQUE_RESULTS) {
141 70         207 return _AQT_findObjects($self, @coords);
142             }
143             else {
144             return _uniq(_AQT_findObjects($self, @coords));
145             }
146              
147             # PS. I don't check explicitly if those objects
148             # are enclosed in the given area. They are just
149             # part of the segments that are enclosed in the
150             # given area. TBD.
151             }
152              
153             sub setWindow
154             {
155 2     2 1 7002 my ($self, $sx, $sy, $s) = @_;
156              
157 2         9 $self->{ORIGIN}[0] += $sx / $self->{SCALE};
158 2         6 $self->{ORIGIN}[1] += $sy / $self->{SCALE};
159 2         5 $self->{SCALE} *= $s;
160             }
161              
162             sub resetWindow
163             {
164 1     1 1 4478 my $self = shift;
165              
166 1         8 $self->{ORIGIN}[$_] = 0 for 0 .. 1;
167 1         5 $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__