File Coverage

blib/lib/Algorithm/QuadTree.pm
Criterion Covered Total %
statement 63 65 96.9
branch 13 18 72.2
condition 3 5 60.0
subroutine 13 13 100.0
pod 7 7 100.0
total 99 108 91.6


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