File Coverage

blib/lib/Algorithm/QuadTree.pm
Criterion Covered Total %
statement 65 67 97.0
branch 9 12 75.0
condition 3 5 60.0
subroutine 15 15 100.0
pod 7 7 100.0
total 99 106 93.4


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