package SDL::Game::Rect;
use strict;
use warnings;
-
-use Class::XSAccessor::Array
- accessors => {
- x => 0,
- left => 0,
- y => 1,
- top => 1,
- width => 2,
- w => 2,
- height => 3,
- h => 3,
- };
-
+use Carp;
+use base 'SDL::Rect';
our $VERSION = '0.01';
sub new {
my $class = shift;
- my ($x, $y, $w, $h) = (@_);
- return bless [$x || 0, $y || 0, $w || 0, $h || 0], ref($class) || $class;
+ my $x = shift || 0;
+ my $y = shift || 0;
+ my $w = shift || 0;
+ my $h = shift || 0;
+
+ my $self = $class->SUPER::new($x, $y, $w, $h);
+ unless ($$self) {
+ #require Carp;
+ croak SDL::GetError();
+ }
+ bless $self, $class;
+ return $self;
}
#############################
sub bottom {
my ($self, $val) = (@_);
if (defined $val) {
- $self->[1] = $val - $self->[3]; # y = val - height
+ $self->top($val - $self->height); # y = val - height
}
- return $self->[1] + $self->[3]; # y + height
+ return $self->top + $self->height; # y + height
}
sub right {
my ($self, $val) = (@_);
if (defined $val) {
- $self->[0] = $val - $self->[2]; # x = val - width
+ $self->left($val - $self->width); # x = val - width
}
- return $self->[0] + $self->[2]; # x + width
+ return $self->left + $self->width; # x + width
}
sub centerx {
my ($self, $val) = (@_);
if (defined $val) {
- $self->[0] = $val - ($self->[2] >> 1); # x = val - (width/2)
+ $self->left($val - ($self->width >> 1)); # x = val - (width/2)
}
- return $self->[0] + ($self->[2] >> 1); # x + (width/2)
+ return $self->left + ($self->width >> 1); # x + (width/2)
}
sub centery {
my ($self, $val) = (@_);
if (defined $val) {
- $self->[1] = $val - ($self->[3] >> 1); # y = val - (height/2)
+ $self->top($val - ($self->height >> 1)); # y = val - (height/2)
}
- return $self->[1] + ($self->[3] >> 1); # y + (height/2)
+ return $self->top + ($self->height >> 1); # y + (height/2)
}
sub size {
my ($self, $w, $h) = (@_);
- return ($self->[2], $self->[3]) # (width, height)
+ return ($self->width, $self->height) # (width, height)
unless (defined $w or defined $h);
if (defined $w) {
- $self->[2] = $w; # width
+ $self->width($w); # width
}
if (defined $h) {
- $self->[3] = $h; # height
+ $self->height($h); # height
}
}
sub topleft {
my ($self, $y, $x) = (@_);
- return ($self->[1], $self->[0]) # (top, left)
+ return ($self->top, $self->left) # (top, left)
unless (defined $y or defined $x);
if (defined $x) {
- $self->[0] = $x; # left
+ $self->left($x); # left
}
if (defined $y) {
- $self->[1] = $y; # top
+ $self->top($y); # top
}
return;
}
sub midleft {
my ($self, $centery, $x) = (@_);
- return ($self->[1] + ($self->[3] >> 1), $self->[0]) # (centery, left)
+ return ($self->top + ($self->height >> 1), $self->left) # (centery, left)
unless (defined $centery or defined $x);
if (defined $x) {
- $self->[0] = $x; # left
+ $self->left($x); # left
}
if (defined $centery) {
- $self->[1] = $centery - ($self->[3] >> 1); # y = centery - (height/2)
+ $self->top($centery - ($self->height >> 1)); # y = centery - (height/2)
}
return;
}
sub bottomleft {
my ($self, $bottom, $x) = (@_);
- return ($self->[1] + $self->[3], $self->[0]) # (bottom, left)
+ return ($self->top + $self->height, $self->left) # (bottom, left)
unless (defined $bottom or defined $x);
if (defined $x) {
- $self->[0] = $x; # left
+ $self->left($x); # left
}
if (defined $bottom) {
- $self->[1] = $bottom - $self->[3]; # y = bottom - height
+ $self->top($bottom - $self->height); # y = bottom - height
}
return;
}
sub center {
my ($self, $centerx, $centery) = (@_);
- return ($self->[0] + ($self->[2] >> 1), $self->[1] + ($self->[3] >> 1))
+ return ($self->left + ($self->width >> 1), $self->top + ($self->height >> 1))
unless (defined $centerx or defined $centery);
if (defined $centerx) {
- $self->[0] = $centerx - ($self->[2] >> 1); # x = centerx - (width/2)
+ $self->left($centerx - ($self->width >> 1)); # x = centerx - (width/2)
}
if (defined $centery) {
- $self->[1] = $centery - ($self->[3] >> 1); # y = centery - (height/2)
+ $self->top($centery - ($self->height >> 1)); # y = centery - (height/2)
}
return;
}
sub topright {
my ($self, $y, $right) = (@_);
- return ($self->[1], $self->[0] + $self->[2]) # (top, right)
+ return ($self->top, $self->left + $self->width) # (top, right)
unless (defined $y or defined $right);
if (defined $right) {
- $self->[0] = $right - $self->[2]; # x = right - width
+ $self->left($right - $self->width); # x = right - width
}
if (defined $y) {
- $self->[1] = $y; # top
+ $self->top($y); # top
}
return;
}
sub midright {
my ($self, $centery, $right) = (@_);
- return ($self->[1] + ($self->[3] >> 1), $self->[0] + $self->[2]) # (centery, right)
+ return ($self->top + ($self->height >> 1), $self->left + $self->width) # (centery, right)
unless (defined $centery or defined $right);
if (defined $right) {
- $self->[0] = $right - $self->[2]; # x = right - width
+ $self->left($right - $self->width); # x = right - width
}
if (defined $centery) {
- $self->[1] = $centery - ($self->[3] >> 1); # y = centery - (height/2)
+ $self->top($centery - ($self->height >> 1)); # y = centery - (height/2)
}
return;
}
sub bottomright {
my ($self, $bottom, $right) = (@_);
- return ($self->[1] + $self->[3], $self->[0] + $self->[2]) # (bottom, right)
+ return ($self->top + $self->height, $self->left + $self->width) # (bottom, right)
unless (defined $bottom or defined $right);
if (defined $right) {
- $self->[0] = $right - $self->[2]; # x = right - width
+ $self->left($right - $self->width); # x = right - width
}
if (defined $bottom) {
- $self->[1] = $bottom - $self->[3]; # y = bottom - height
+ $self->top($bottom - $self->height); # y = bottom - height
}
return;
}
sub midtop {
my ($self, $centerx, $y) = (@_);
- return ($self->[0] + ($self->[2] >> 1), $self->[1]) # (centerx, top)
+ return ($self->left + ($self->width >> 1), $self->top) # (centerx, top)
unless (defined $centerx or defined $y);
if (defined $y) {
- $self->[1] = $y; # top
+ $self->top($y); # top
}
if (defined $centerx) {
- $self->[0] = $centerx - ($self->[2] >> 1); # x = centerx - (width/2)
+ $self->left($centerx - ($self->width >> 1)); # x = centerx - (width/2)
}
return;
}
sub midbottom {
my ($self, $centerx, $bottom) = (@_);
- return ($self->[0] + ($self->[2] >> 1), $self->[1] + $self->[3]) # (centerx, bottom)
+ return ($self->left + ($self->width >> 1), $self->top + $self->height) # (centerx, bottom)
unless (defined $centerx or defined $bottom);
if (defined $bottom) {
- $self->[1] = $bottom - $self->[3]; # y = bottom - height
+ $self->top($bottom - $self->height); # y = bottom - height
}
if (defined $centerx) {
- $self->[0] = $centerx - ($self->[2] >> 1); # x = centerx - (width/2)
+ $self->left($centerx - ($self->width >> 1)); # x = centerx - (width/2)
}
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;
+}
+
+sub _check_fit {
+ my ($self, $rect) = (@_);
+
+ my $x_ratio = $self->w / $rect->w;
+ my $y_ratio = $self->h / $rect->h;
+ my $max_ratio = ($x_ratio > $y_ratio) ? $x_ratio : $y_ratio;
+
+ my $w = int ($self->w / $max_ratio);
+ my $h = int ($self->h / $max_ratio);
+
+ my $x = $rect->x + int (($rect->w - $w) / 2);
+ my $y = $rect->y + int (($rect->h - $h) / 2);
+
+ return ($x, $y, $w, $h);
+}
+
+sub fit {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my ($x, $y, $w, $h) = _check_fit($self, $rect);
+
+ return $self->new ($x, $y, $w, $h);
+}
+
+sub fit_ip {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my ($x, $y, $w, $h) = _check_fit($self, $rect);
+
+ $self->x($x);
+ $self->y($y);
+ $self->w($w);
+ $self->h($h);
+
+ return;
+}
+
+sub normalize {
+ my $self = shift;
+
+ if ($self->w < 0) {
+ $self->x($self->x + $self->w);
+ $self->w(-$self->w);
+ }
+
+ if ($self->h < 0) {
+ $self->y( $self->y + $self->h);
+ $self->h(-$self->h);
+ }
+ return;
+}
+
+sub contains {
+ my ($self, $rect) = (@_);
+
+ unless ($rect->isa('SDL::Rect')) {
+ croak "must receive an SDL::Rect-based object";
+ }
+
+ my $contained = ($self->x <= $rect->x)
+ && ($self->y <= $rect->y)
+ && ($self->x + $self->w >= $rect->x + $rect->w)
+ && ($self->y + $self->h >= $rect->y + $rect->h)
+ && ($self->x + $self->w > $rect->x)
+ && ($self->y + $self->h > $rect->y)
+ ;
+
+ return $contained;
+}
+
+
+sub collidepoint {
+ my ($self, $x, $y) = (@_);
+
+ unless (defined $x and defined $y) {
+ croak "must receive (x,y) as arguments";
+ }
+
+ my $inside = $x >= $self->x
+ && $x < $self->x + $self->w
+ && $y >= $self->y
+ && $y < $self->y + $self->h
+ ;
+
+ return $inside;
+}
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)