File Coverage

blib/lib/Algorithm/QuadTree.pm
Criterion Covered Total %
statement 65 67 97.0
branch 10 12 83.3
condition 3 5 60.0
subroutine 15 15 100.0
pod 7 7 100.0
total 100 106 94.3


line stmt bran cond sub pod time code
1             package Algorithm::QuadTree;
2             $Algorithm::QuadTree::VERSION = '0.6';
3 6     6   695700 use strict;
  6         12  
  6         223  
4 6     6   26 use warnings;
  6         20  
  6         365  
5 6     6   35 use Carp;
  6         16  
  6         983  
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   3430 require Algorithm::QuadTree::PP;
25 6         22 my $backend = 'Algorithm::QuadTree::PP';
26              
27 6   100     39 my $check_backend = $ENV{ALGORITHM_QUADTREE_BACKEND} || 'Algorithm::QuadTree::XS';
28 6 100       408 if (eval "require $check_backend; 1;") {
29 5         16 $backend = $check_backend;
30             }
31              
32 6         704 $backend->import;
33             }
34              
35             # List::Util 1.45 added 'uniqstr'
36 6     6   42 use constant HAS_LIST_UTIL => eval { require List::Util; List::Util->VERSION('1.45'); 1 };
  6         12  
  6         10  
  6         21  
  6         90  
  6         5717  
37              
38             sub new
39             {
40 5     5 1 931764 my $self = shift;
41 5   33     45 my $class = ref($self) || $self;
42 5         48 my %args = @_;
43              
44 5         25 my $obj = bless {}, $class;
45              
46 5         17 for my $arg (qw/xmin ymin xmax ymax depth/) {
47 25 50       80 unless (exists $args{"-$arg"}) {
48 0         0 carp "- must specify $arg";
49 0         0 return undef;
50             }
51              
52 25         90 $obj->{uc $arg} = $args{"-$arg"};
53             }
54              
55 5         24 $obj->{ORIGIN} = [0, 0];
56 5         22 $obj->{SCALE} = 1;
57              
58 5         49 _AQT_init($obj);
59              
60 5         28 return $obj;
61             }
62              
63             sub DESTROY
64             {
65 5     5   15100 my ($self) = @_;
66              
67 5         42 _AQT_deinit($self);
68             }
69              
70             # modify coords according to window
71             sub _adjustCoords
72             {
73 6     6   11 my ($self, @coords) = @_;
74              
75 6 100       20 if (@coords == 4) {
    50          
76             # rectangle
77              
78             $_ = $self->{ORIGIN}[0] + $_ / $self->{SCALE}
79 4         22 for $coords[0], $coords[2];
80             $_ = $self->{ORIGIN}[1] + $_ / $self->{SCALE}
81 4         14 for $coords[1], $coords[3];
82             }
83             elsif (@coords == 3) {
84             # circle
85              
86 2         6 $coords[0] = $self->{ORIGIN}[0] + $coords[0] / $self->{SCALE};
87 2         5 $coords[1] = $self->{ORIGIN}[1] + $coords[1] / $self->{SCALE};
88 2         5 $coords[2] /= $self->{SCALE};
89             }
90              
91 6         18 return @coords;
92             }
93              
94             sub add
95             {
96 55     55 1 8623 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       129 unless $self->{SCALE} == 1;
103              
104 55         144 _AQT_addObject($self, $object, @coords);
105              
106 55         147 return;
107             }
108              
109             sub delete
110             {
111 1     1 1 1485 my ($self, $object) = @_;
112              
113 1         16 _AQT_delete($self, $object);
114              
115 1         2 return;
116             }
117              
118             sub clear
119             {
120 3     3 1 15722 my $self = shift;
121              
122 3         17 _AQT_clear($self);
123              
124 3         10 return;
125             }
126              
127             sub getEnclosedObjects
128             {
129 70     70 1 57826 my ($self, @coords) = @_;
130              
131             @coords = $self->_adjustCoords(@coords)
132 70 100       196 unless $self->{SCALE} == 1;
133              
134 70         189 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 6194 my ($self, $sx, $sy, $s) = @_;
145              
146 2         7 $self->{ORIGIN}[0] += $sx / $self->{SCALE};
147 2         5 $self->{ORIGIN}[1] += $sy / $self->{SCALE};
148 2         5 $self->{SCALE} *= $s;
149             }
150              
151             sub resetWindow
152             {
153 1     1 1 3166 my $self = shift;
154              
155 1         7 $self->{ORIGIN}[$_] = 0 for 0 .. 1;
156 1         2 $self->{SCALE} = 1;
157             }
158              
159             # HELPERS
160             # not called in object context
161              
162             sub _uniq
163             {
164 70     70   118 if (HAS_LIST_UTIL) {
165 70         92 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__