File Coverage

blib/lib/Algorithm/RectanglesContainingDot.pm
Criterion Covered Total %
statement 102 103 99.0
branch 28 32 87.5
condition 28 33 84.8
subroutine 11 12 91.6
pod n/a
total 169 180 93.8


line stmt bran cond sub pod time code
1             package Algorithm::RectanglesContainingDot;
2              
3 1     1   24858 use strict;
  1         3  
  1         39  
4 1     1   5 use warnings;
  1         3  
  1         1411  
5              
6             our $VERSION = '0.02';
7              
8             package
9             Algorithm::RectanglesContainingDot::Perl;
10              
11             our $MIN_DIV = 8;
12              
13             sub new {
14 2     2   24 my $class = shift;
15 2         7 my $self = { rects => [],
16             names => [] };
17 2         9 bless $self, $class;
18             }
19              
20 0     0   0 sub _reset { delete shift->{div} }
21              
22             sub add_rectangle {
23 20000     20000   96153 my ($self, $name, $x0, $y0, $x1, $y1) = @_;
24              
25 20000 50       37217 ($x0, $x1) = ($x1, $x0) if $x0 > $x1;
26 20000 50       33355 ($y0, $y1) = ($y1, $y0) if $y0 > $y1;
27              
28 20000         18630 push @{$self->{rects}}, ($x0, $y0, $x1, $y1);
  20000         44695  
29 20000         20793 push @{$self->{names}}, $name;
  20000         49815  
30 20000         44250 delete $self->{div};
31             }
32              
33             sub rectangles_containing_dot {
34 2000     2000   1897373 my $self = shift;
35 2000   66     11872 my $div = $self->{div} || $self->_init_div;
36 2000         11149 @{$self->{names}}[_rectangles_containing_dot($div, $self->{rects}, @_)];
  2000         41840  
37             }
38              
39             sub _rectangles_containing_dot_ref {
40 2000     2000   14472 my ($self, $x, $y) = @_;
41 2000         4546 my $names = $self->{names};
42 2000         4333 my $rects = $self->{rects};
43 2000         2598 my @ret;
44 2000         6905 for (0..$#$names) {
45 20000000         18581439 my $i0 = $_ * 4;
46 20000000 100 100     53297819 if ($rects->[$i0] <= $x and
      100        
      100        
47             $rects->[$i0+1] <= $y and
48             $rects->[$i0+2] >= $x and
49             $rects->[$i0+3] >= $y) {
50 37926         77487 push @ret, $names->[$_];
51             }
52             }
53 2000         62806 @ret;
54             }
55              
56             # div is:
57             # x/y, right_div, left_div, point, all
58              
59             sub _init_div {
60 1     1   5 my $self = shift;
61 1         3 $self->{div} = [undef, undef, undef, undef, [0..$#{$self->{names}}]]
  1         2254  
62             }
63              
64             sub _rectangles_containing_dot {
65 2000     2000   4765 my ($div, $rects, $x, $y) = @_;
66             # print ".";
67 2000         3678 while (1) {
68 27750   66     73827 my $dir = $div->[0] || _divide_rects($div, $rects);
69              
70 27750 100       61324 if ($dir eq 'n') {
71 2000         3322 my @ret;
72 2000         3535 for (@{$div->[4]}) {
  2000         6998  
73 83175         130590 my ($x0, $y0, $x1, $y1) = @{$rects}[4*$_ .. 4*$_+3];
  83175         158466  
74 83175 100 100     456887 push @ret, $_
      100        
      66        
75             if ($x >= $x0 and $x <= $x1 and $y >= $y0 && $y <= $y1);
76             }
77 2000         11059 return @ret;
78             }
79              
80 25750 100       78170 $div = $div->[(($dir eq 'x') ? ($x <= $div->[3]) : ($y <= $div->[3])) ? 1 : 2];
    100          
81             }
82             }
83              
84             sub _find_best_div {
85 2072     2072   3086 my ($dr, $rects, $off) = @_;
86              
87 2072         6141 my @v0 = map { @{$rects}[$_*4+$off] } @$dr;
  486478         504468  
  486478         960104  
88 2072         23031 my @v1 = map { @{$rects}[$_*4+2+$off] } @$dr;
  486478         515262  
  486478         740885  
89 2072         25341 @v0 = sort { $a <=> $b } @v0;
  3561999         3273639  
90 2072         9657 @v1 = sort { $a <=> $b } @v1;
  3591172         3195535  
91              
92 2072         4906 my $med = 0.5 * @$dr;
93 2072         3862 my $op = 0;
94 2072         2436 my $cl = 0;
95 2072         5272 my $best = @$dr * @$dr;
96 2072         3038 my $bestv;
97             # my ($bestop, $bestcl);
98 2072   66     13026 while (@v0 and @v1) {
99 41563 100       79962 my $v = ($v0[0] <= $v1[0]) ? $v0[0] : $v1[0];
100 41563   100     153776 while (@v0 and $v0[0] == $v) {
101 486478         446388 $op++;
102 486478         1803866 shift @v0;
103             }
104 41563   66     169121 while (@v1 and $v1[0] == $v) {
105 238343         223789 $cl++;
106 238343         866526 shift @v1;
107             }
108              
109 41563         56012 my $l = $op - $med;
110 41563         55108 my $r = @$dr - $cl - $med;
111 41563         57175 my $good = $l * $l + $r * $r;
112              
113             #{ no warnings; print STDERR "med: $med, op: $op, cl: $cl, good: $good, best: $best, bestv: $bestv\n"; }
114              
115 41563 100       115564 if ($good < $best) {
116 29536         28981 $best = $good;
117 29536         127082 $bestv = $v;
118             # $bestop = $op;
119             # $bestcl = $cl;
120             }
121             }
122             # print "off: $off, best: $best, bestv: $bestv, bestop: $bestop, bestcl: $bestcl, size-bestcl: ".(@$dr-$bestcl)."\n";
123 2072         14692 return ($best, $bestv);
124             }
125              
126             sub _divide_rects {
127 1036     1036   2156 my ($div, $rects) = @_;
128 1036         1610 my $dr = $div->[4];
129 1036 50       2924 return $div->[0] = 'n' if (@$dr <= $MIN_DIV);
130 1036         2363 my $bestreq = 0.24 * @$dr * @$dr;
131 1036         2803 my ($bestx, $bestxx) = _find_best_div($dr, $rects, 0);
132 1036 50       4716 my ($besty, $bestyy) = ($bestx == 0) ? 1 : _find_best_div($dr, $rects, 1);
133             # print "bestx: $bestx, bestxx: $bestxx, besty: $besty, bestyy: $bestyy, bestreq: $bestreq\n";
134 1036 100       4005 if ($bestx < $besty) {
135 509 100       2020 if ($bestx < $bestreq) {
136 365         1891 @{$div}[1,2] = _part_rects($dr, $rects, $bestxx, 0);
  365         1238  
137 365         775 $div->[3] = $bestxx;
138 365         548 pop @$div;
139 365         10749 return $div->[0] = 'x';
140             }
141             }
142             else {
143 527 100       2645 if ($besty < $bestreq) {
144 327         1486 @{$div}[1,2] = _part_rects($dr, $rects, $bestyy, 1);
  327         1261  
145 327         755 $div->[3] = $bestyy;
146 327         424 pop @$div;
147 327         5307 return $div->[0] = 'y';
148             }
149             }
150 344         2382 return $div->[0] = 'n';
151             }
152              
153             sub _part_rects {
154 692     692   1743 my ($dr, $rects, $bestv, $off) = @_;
155 692         1186 my (@l, @r);
156 692         2267 for (@$dr) {
157 202863 100       484496 push @l, $_ if ($bestv >= $rects->[$_ * 4 + $off]);
158 202863 100       463676 push @r, $_ if ($bestv < $rects->[$_ * 4 + $off + 2]);
159             }
160             # print "off: $off, left: ".scalar(@l).", right: ".scalar(@r)."\n";
161 692         6013 return ([undef, undef, undef, undef, \@l],
162             [undef, undef, undef, undef, \@r])
163             }
164              
165             package Algorithm::RectanglesContainingDot;
166              
167             our @ISA;
168             if (eval "require Algorithm::RectanglesContainingDot_XS") {
169             @ISA = qw(Algorithm::RectanglesContainingDot_XS);
170             }
171             else {
172             @ISA = qw(Algorithm::RectanglesContainingDot::Perl);
173             }
174              
175             1;
176             __END__