File Coverage

blib/lib/Math/Shape/OrientedRectangle.pm
Criterion Covered Total %
statement 117 119 98.3
branch 35 44 79.5
condition n/a
subroutine 17 17 100.0
pod 7 7 100.0
total 176 187 94.1


line stmt bran cond sub pod time code
1 6     6   20155 use strict;
  6         10  
  6         181  
2 6     6   27 use warnings;
  6         11  
  6         242  
3             package Math::Shape::OrientedRectangle;
4             $Math::Shape::OrientedRectangle::VERSION = '0.13';
5 6     6   108 use 5.008;
  6         17  
  6         180  
6 6     6   22 use Carp;
  6         10  
  6         362  
7 6     6   380 use Math::Shape::Vector;
  6         9  
  6         115  
8 6     6   22 use Math::Shape::Utils;
  6         7  
  6         357  
9 6     6   412 use Math::Shape::Line;
  6         7  
  6         125  
10 6     6   305 use Math::Shape::LineSegment;
  6         6  
  6         125  
11 6     6   1283 use Math::Shape::Rectangle;
  6         12  
  6         152  
12 6     6   1563 use Math::Shape::Circle;
  6         11  
  6         5868  
13              
14             # ABSTRACT: a 2d oriented rectangle
15              
16              
17             sub new {
18 14 50   14 1 72 croak 'incorrect number of args' unless @_ == 6;
19 14         20 my ($class, $x_1, $y_1, $x_2, $y_2, $rotation) = @_;
20 14         43 bless { center => Math::Shape::Vector->new($x_1, $y_1),
21             half_extend => Math::Shape::Vector->new($x_2, $y_2),
22             rotation => $rotation,
23             }, $class;
24             }
25              
26              
27             sub get_edge {
28 63 50   63 1 102 croak 'incorrect number of args' unless @_ == 2;
29 63         56 my ($self, $edge_number) = @_;
30              
31 63         158 my $a = Math::Shape::Vector->new(
32             $self->{half_extend}->{x},
33             $self->{half_extend}->{y});
34              
35 63         135 my $b = Math::Shape::Vector->new(
36             $self->{half_extend}->{x},
37             $self->{half_extend}->{y});
38              
39 63         64 my $mod = $edge_number % 4;
40              
41 63 100       122 if ($mod == 0)
    100          
    100          
42             {
43 31         42 $a->{x} = - $a->{x};
44             }
45             elsif ($mod == 1)
46             {
47 13         21 $b->{y} = - $b->{y};
48             }
49             elsif ($mod == 2)
50             {
51 18         21 $a->{y} = - $a->{y};
52 18         35 $b = $b->negate;
53             }
54             else
55             {
56 1         2 $a->negate;
57 1         2 $b->{x} = - $b->{x};
58             }
59              
60 63         120 $a = $a->rotate($self->{rotation});
61 63         156 $a = $a->add_vector($self->{center});
62 63         164 $b = $b->rotate($self->{rotation});
63 63         131 $b = $b->add_vector($self->{center});
64              
65 63         180 Math::Shape::LineSegment->new($a->{x}, $a->{y}, $b->{x}, $b->{y});
66             }
67              
68              
69             sub axis_is_separating
70             {
71 17 50   17 1 43 croak 'collides must be called with a Math::Shape::LineSegment object'
72             unless $_[1]->isa('Math::Shape::LineSegment');
73 17         16 my ($self, $axis) = @_;
74              
75 17         22 my $edge_0 = $self->get_edge(0);
76 17         26 my $edge_2 = $self->get_edge(2);
77              
78 17         40 my $n_vector = $axis->{start}->subtract_vector($axis->{end});
79              
80 17         35 my $axis_range = $axis->project($n_vector);
81 17         31 my $range_0 = $edge_0->project($n_vector);
82 17         35 my $range_2 = $edge_2->project($n_vector);
83 17         31 my $projection = $range_0->hull($range_2);
84              
85 17 100       33 $axis_range->is_overlapping($projection) ? 0 : 1;
86             }
87              
88              
89             sub corner
90             {
91 24 50   24 1 33 croak 'incorrect number of args' unless @_ == 2;
92 24         26 my ($self, $nr) = @_;
93              
94 24         29 my $mod = $nr % 4;
95 24         17 my $v;
96              
97 24 100       66 if ($mod == 0)
    100          
    100          
    50          
98             {
99 6         30 $v = Math::Shape::Vector->new(
100             - $self->{half_extend}{x},
101             $self->{half_extend}{y},
102             );
103             }
104             elsif ($mod == 1)
105             {
106 6         19 $v = Math::Shape::Vector->new(
107             $self->{half_extend}{x},
108             $self->{half_extend}{y},
109             );
110             }
111             elsif ($mod == 2)
112             {
113 6         20 $v = Math::Shape::Vector->new(
114             $self->{half_extend}{x},
115             - $self->{half_extend}{y},
116             );
117             }
118             elsif ($mod == 3)
119             {
120 6         21 $v = Math::Shape::Vector->new(
121             $self->{half_extend}{x},
122             $self->{half_extend}{y},
123             );
124 6         15 $v = $v->negate;
125             }
126             else
127             {
128 0         0 croak 'corner() should be called with a number between 0-3';
129             }
130 24         100 my $c = $v->rotate($self->{rotation});
131 24         136 $c->add_vector($self->{center});
132             }
133              
134              
135              
136             sub hull
137             {
138 6     6 1 7 my $self = shift;
139              
140             # create a rectangle at the same center point as $self
141 6         45 my $h = Math::Shape::Rectangle->new(
142             $self->{center}{x},
143             $self->{center}{y},
144             0,
145             0,
146             );
147              
148             # enlarge the rectangle by every corner vector of $self
149 6         18 for (0..3)
150             {
151 24         41 my $corner = $self->corner($_);
152 24         47 $h = $h->enlarge($corner);
153             }
154              
155             # return the hull
156 6         12 $h;
157             }
158              
159              
160             sub circle_hull
161             {
162 1     1 1 2 my $self = shift;
163              
164 1         5 Math::Shape::Circle->new(
165             $self->{center}->{x},
166             $self->{center}->{y},
167             $self->{half_extend}->length,
168             );
169             }
170              
171              
172             sub collides {
173 27     27 1 46 my ($self, $other_obj) = @_;
174              
175 27 100       269 if ($other_obj->isa('Math::Shape::OrientedRectangle'))
    100          
    100          
    100          
    100          
    50          
176             {
177 5         14 my $edge = $self->get_edge(0);
178 5 100       16 return 0 if $other_obj->axis_is_separating($edge);
179              
180 4         12 $edge = $self->get_edge(1);
181 4 50       13 return 0 if $other_obj->axis_is_separating($edge);
182              
183 4         9 $edge = $other_obj->get_edge(0);
184 4 50       11 return 0 if $self->axis_is_separating($edge);
185              
186 4         9 $edge = $other_obj->get_edge(1);
187 4 50       11 return 0 if $self->axis_is_separating($edge);
188              
189 4         19 1;
190             }
191             elsif ($other_obj->isa('Math::Shape::Vector'))
192             {
193             # convert into rectangle and use rectangle's collides() method
194 6         26 my $size = $self->{half_extend}->multiply(2);
195 6         30 my $lr = Math::Shape::Rectangle->new(
196             0,
197             0,
198             $size->{x},
199             $size->{y},
200             );
201              
202 6         18 my $lp = $other_obj->subtract_vector($self->{center});
203 6         22 $lp = $lp->rotate(- $self->{rotation});
204 6         25 $lp = $lp->add_vector($self->{half_extend});
205 6         20 $lr->collides($lp);
206             }
207             elsif ($other_obj->isa('Math::Shape::Line'))
208             {
209 6         25 my $size = $self->{center}->multiply(2);
210 6         31 my $lr = Math::Shape::Rectangle->new(
211             0,
212             0,
213             $size->{x},
214             $size->{y},
215             );
216              
217 6         19 my $base = $other_obj->{base}->subtract_vector($self->{center});
218 6         47 $base = $base->rotate(- $self->{rotation});
219 6         31 $base = $base->add_vector($self->{half_extend});
220 6         22 my $direction = $other_obj->{direction}->rotate(- $self->{rotation});
221              
222 6         24 my $ll = Math::Shape::Line->new(
223             $base->{x},
224             $base->{y},
225             $direction->{x},
226             $direction->{y},
227             );
228              
229 6         18 $lr->collides($ll);
230             }
231             elsif ($other_obj->isa('Math::Shape::LineSegment'))
232             {
233 6         23 my $size = $self->{half_extend}->multiply(2);
234 6         32 my $lr = Math::Shape::Rectangle->new(
235             0,
236             0,
237             $size->{x},
238             $size->{y},
239             );
240              
241 6         21 my $ls_p1 = $other_obj->{start}->subtract_vector($self->{center});
242 6         20 $ls_p1 = $ls_p1->rotate(- $self->{rotation});
243 6         21 $ls_p1 = $ls_p1->add_vector($self->{half_extend});
244              
245 6         22 my $ls_p2 = $other_obj->{end}->subtract_vector($self->{center});
246 6         16 $ls_p2 = $ls_p2->rotate(- $self->{rotation});
247 6         29 $ls_p2 = $ls_p2->add_vector($self->{half_extend});
248              
249 6         35 my $ls = Math::Shape::LineSegment->new(
250             $ls_p1->{x},
251             $ls_p1->{y},
252             $ls_p2->{x},
253             $ls_p2->{y},
254             );
255              
256 6         17 $lr->collides($ls);
257             }
258             # call the other objects collides() method
259             elsif ($other_obj->isa('Math::Shape::Rectangle'))
260             {
261 2         7 $other_obj->collides($self);
262             }
263             elsif ($other_obj->isa('Math::Shape::Circle'))
264             {
265 2         8 $other_obj->collides($self);
266             }
267             else
268             {
269 0           croak 'collides must be called with a Math::Shape::Vector library object';
270             }
271             }
272              
273             1;
274              
275             __END__