File Coverage

blib/lib/Math/Shape/OrientedRectangle.pm
Criterion Covered Total %
statement 116 118 98.3
branch 35 44 79.5
condition n/a
subroutine 17 17 100.0
pod 7 7 100.0
total 175 186 94.0


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