package SDL::Game::Rect;
use strict;
use warnings;
-
+use Carp;
use base 'SDL::Rect';
our $VERSION = '0.01';
my $self = $class->SUPER::new($x, $y, $w, $h);
unless ($$self) {
- require Carp;
- Carp::croak SDL::GetError();
+ #require Carp;
+ croak SDL::GetError();
}
bless $self, $class;
return $self;
return;
}
+###############################
+## methods ##
+###############################
+
+sub duplicate {
+}
+
+sub copy {
+ my $self = shift;
+ return $self->new(
+ -top => $self->top,
+ -left => $self->left,
+ -width => $self->width,
+ -height => $self->height,
+ );
+}
+
+sub move {
+ my ($self, $x, $y) = (@_);
+ if (not defined $x or not defined $y) {
+ #require Carp;
+ croak "must receive x and y positions as argument";
+ }
+ return $self->new(
+ -top => $self->top + $y,
+ -left => $self->left + $x,
+ -width => $self->width,
+ -height => $self->height,
+ );
+}
+
+sub move_ip {
+ my ($self, $x, $y) = (@_);
+ if (not defined $x or not defined $y) {
+ #require Carp;
+ croak "must receive x and y positions as argument";
+ }
+ $self->x($self->x + $x);
+ $self->y($self->y + $y);
+
+ return;
+}
+
+sub inflate {
+ my ($self, $x, $y) = (@_);
+ if (not defined $x or not defined $y) {
+ #require Carp;
+ croak "must receive x and y positions as argument";
+ }
+
+ return $self->new(
+ -left => $self->left - ($x / 2),
+ -top => $self->top - ($y / 2),
+ -width => $self->width + $x,
+ -height => $self->height + $y,
+ );
+}
+
+sub inflate_ip {
+ my ($self, $x, $y) = (@_);
+ if (not defined $x or not defined $y) {
+ #require Carp;
+ croak "must receive x and y positions as argument";
+ }
+
+ $self->x( $self->x - ($x / 2) );
+ $self->y( $self->y - ($y / 2) );
+
+ $self->w( $self->w + $x );
+ $self->h( $self->h + $y );
+}
+
+sub _get_clamp_coordinates {
+ my ($self_pos, $self_len, $rect_pos, $rect_len) = (@_);
+
+ if ($self_len >= $rect_len) {
+ return $rect_pos + ($rect_len / 2) - ($self_len / 2);
+ }
+ elsif ($self_pos < $rect_pos) {
+ return $rect_pos;
+ }
+ elsif ( ($self_pos + $self_len) > ($rect_pos + $rect_len) ) {
+ return $rect_pos + $rect_len - $self_len;
+ }
+ else {
+ return $self_pos;
+ }
+}
+
+sub clamp {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my $x = _get_clamp_coordinates($self->x, $self->w, $rect->x, $rect->w);
+ my $y = _get_clamp_coordinates($self->y, $self->h, $rect->y, $rect->h);
+
+ return $self->new($x, $y, $self->w, $self->h);
+}
+
+sub clamp_ip {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my $x = _get_clamp_coordinates($self->x, $self->w, $rect->x, $rect->w);
+ my $y = _get_clamp_coordinates($self->y, $self->h, $rect->y, $rect->h);
+
+ $self->x($x);
+ $self->y($y);
+
+ return;
+}
+
+sub _get_intersection_coordinates {
+ my ($self, $rect) = (@_);
+ my ($x, $y, $w, $h);
+
+INTERSECTION:
+ {
+ ### Left
+ if (($self->x >= $rect->x) && ($self->x < ($rect->x + $rect->w))) {
+ $x = $self->x;
+ }
+ elsif (($rect->x >= $self->x) && ($rect->x < ($self->x + $self->w))) {
+ $x = $rect->x;
+ }
+ else {
+ last INTERSECTION;
+ }
+
+ ## Right
+ if ((($self->x + $self->w) > $rect->x) && (($self->x + $self->w) <= ($rect->x + $rect->w))) {
+ $w = ($self->x + $self->w) - $x;
+ }
+ elsif ((($rect->x + $rect->w) > $self->x) && (($rect->x + $rect->w) <= ($self->x + $self->w))) {
+ $w = ($rect->x + $rect->w) - $x;
+ }
+ else {
+ last INTERSECTION;
+ }
+
+ ## Top
+ if (($self->y >= $rect->y) && ($self->y < ($rect->y + $rect->h))) {
+ $y = $self->y;
+ }
+ elsif (($rect->y >= $self->y) && ($rect->y < ($self->y + $self->h))) {
+ $y = $rect->y;
+ }
+ else {
+ last INTERSECTION;
+ }
+
+ ## Bottom
+ if ((($self->y + $self->h) > $rect->y) && (($self->y + $self->h) <= ($rect->y + $rect->h))) {
+ $h = ($self->y + $self->h) - $y;
+ }
+ elsif ((($rect->y + $rect->h) > $self->y) && (($rect->y + $rect->h) <= ($self->y + $self->h))) {
+ $h = ($rect->y + $rect->h) - $y;
+ }
+ else {
+ last INTERSECTION;
+ }
+
+ return ($x, $y, $w, $h);
+ }
+
+ # if we got here, the two rects do not intersect
+ return ($self->x, $self->y, 0, 0);
+
+}
+
+sub clip {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my ($x, $y, $w, $h) = _get_intersection_coordinates($self, $rect);
+
+ return $self->new($x, $y, $w, $h);
+}
+
+sub clip_ip {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my ($x, $y, $w, $h) = _get_intersection_coordinates($self, $rect);
+
+ $self->x($x);
+ $self->y($y);
+ $self->w($w);
+ $self->h($h);
+
+ return;
+}
+
+
+sub _test_union {
+ my ($self, $rect) = (@_);
+ my ($x, $y, $w, $h);
+
+ $x = $self->x < $rect->x ? $self->x : $rect->x; # MIN
+ $y = $self->y < $rect->y ? $self->y : $rect->y; # MIN
+
+ $w = ($self->x + $self->w) > ($rect->x + $rect->w)
+ ? ($self->x + $self->w) - $x
+ : ($rect->x + $rect->w) - $x
+ ; # MAX
+
+ $h = ($self->y + $self->h) > ($rect->y + $rect->h)
+ ? ($self->y + $self->h) - $y
+ : ($rect->y + $rect->h) - $y
+ ; # MAX
+
+ return ($x, $y, $w, $h);
+}
+
+sub union {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my ($x, $y, $w, $h) = _test_union($self, $rect);
+ return $self->new($x, $y, $w, $h);
+}
+
+sub union_ip {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my ($x, $y, $w, $h) = _test_union($self, $rect);
+
+ $self->x($x);
+ $self->y($y);
+ $self->w($w);
+ $self->y($h);
+
+ return;
+}
+
+sub _test_unionall {
+ my ($self, $rects) = (@_);
+
+ # initial values for union rect
+ my $left = $self->x;
+ my $top = $self->y;
+ my $right = $self->x + $self->w;
+ my $bottom = $self->y + $self->h;
+
+ foreach my $rect (@{$rects}) {
+ unless ($rect->isa('SDL::Rect')) {
+ # TODO: better error message, maybe saying which item
+ # is the bad one (by list position)
+ croak "must receive an array reference of SDL::Rect-based objects";
+ }
+
+ $left = $rect->x if $rect->x < $left; # MIN
+ $top = $rect->y if $rect->y < $top; # MIN
+ $right = ($rect->x + $rect->w) if ($rect->x + $rect->w) > $right; # MAX
+ $bottom = ($rect->y + $rect->h) if ($rect->y + $rect->h) > $bottom; # MAX
+ }
+
+ return ($left, $top, $right - $left, $bottom - $top);
+}
+
+sub unionall {
+ my ($self, $rects) = (@_);
+
+ croak "must receive an array reference of SDL::Rect-based objects"
+ unless defined $rects and ref $rects eq 'ARRAY';
+
+ my ($x, $y, $w, $h) = _test_unionall($self, $rects);
+
+ return $self->new($x, $y, $w, $h);
+}
+
+sub unionall_ip {
+ my ($self, $rects) = (@_);
+
+ croak "must receive an array reference of SDL::Rect-based objects"
+ unless defined $rects and ref $rects eq 'ARRAY';
+
+ my ($x, $y, $w, $h) = _test_unionall($self, $rects);
+
+ $self->x($x);
+ $self->y($y);
+ $self->w($w);
+ $self->h($h);
+
+ return;
+}
+
42;
__END__
=head3 clip($rect)
-Returns a new Rect with the intersection between the two Rect objects, that is, returns a new Rect cropped to be completely inside the Rect object passed as an argument. If the two rectangles do not overlap to begin with, a Rect with 0 size is returned.
+Returns a new Rect with the intersection between the two Rect objects, that is, returns a new Rect cropped to be completely inside the Rect object passed as an argument. If the two rectangles do not overlap to begin with, a Rect with 0 size is returned, in the original Rect's (x,y) coordinates.
=head3 clip_ip($rect)
-Same as C<<clip>> above, but crops the current Rect in place and returns nothing. As the original method, the Rect becomes zero-sized if the two rectangles do not overlap to begin with.
+Same as C<<clip>> above, but crops the current Rect in place and returns nothing. As the original method, the Rect becomes zero-sized if the two rectangles do not overlap to begin with, retaining its (x, y) coordinates.
=head3 union($rect)