1# You may distribute under the terms of either the GNU General Public License 2# or the Artistic License (the same terms as Perl itself) 3# 4# (C) Paul Evans, 2011-2016 -- leonerd@leonerd.org.uk 5 6package Tickit::Rect 0.72; 7 8use v5.14; 9use warnings; 10 11use Carp; 12 13# Load the XS code 14require Tickit; 15 16=head1 NAME 17 18C<Tickit::Rect> - a lightweight data structure representing a rectangle 19 20=head1 SYNOPSIS 21 22 use Tickit::Rect; 23 24 my $rect = Tickit::Rect->new( 25 top => 0, left => 5, lines => 3, cols => 10 26 ); 27 28=head1 DESCRIPTION 29 30Objects in this class represent a rectangle, by storing the top left corner 31coordinate and the size in lines and columns. This data structure is purely 32abstract and not tied to a particular window or coordinate system. It exists 33simply as a convenient data store containing some useful utility methods. 34 35=cut 36 37=head1 CONSTRUCTORS 38 39=cut 40 41=head2 new 42 43 $rect = Tickit::Rect->new( %args ) 44 45Construct a new rectangle of the given geometry, given by C<top>, C<left> and 46either C<lines> and C<cols>, or C<bottom> and C<right>. 47 48 $rect = Tickit::Rect->new( $str ) 49 50If given a single string, this will be parsed in the form 51 52 (left,top)..(right,bottom) 53 54=cut 55 56sub new 57{ 58 my $class = shift; 59 my %args; 60 if( @_ == 1 ) { 61 @args{qw(left top right bottom)} = 62 $_[0] =~ m/^\((\d+),(\d+)\)..\((\d+),(\d+)\)$/ or croak "Unrecognised Tickit::Rect string '$_[0]'"; 63 } 64 else { 65 %args = @_; 66 } 67 68 defined $args{lines} or $args{lines} = $args{bottom} - $args{top}; 69 defined $args{cols} or $args{cols} = $args{right} - $args{left}; 70 71 return $class->_new( @args{qw( top left lines cols )} ); 72} 73 74=head2 intersect 75 76 $rect = $existing_rect->intersect( $other_rect ) 77 78If there is an intersection between the given rectangles, return it. If not, 79return C<undef>. 80 81=cut 82 83=head2 translate 84 85 $rect = $existing_rect->translate( $downward, $rightward ) 86 87Returns a new rectangle of the same size as the given one, moved down and to 88the right by the given argmuents (which may be negative) 89 90=cut 91 92=head1 ACCESSORS 93 94=cut 95 96=head2 top 97 98=head2 left 99 100=head2 bottom 101 102=head2 right 103 104 $top = $rect->top 105 106 $left = $rect->left 107 108 $bottom = $rect->bottom 109 110 $right = $rect->right 111 112Return the edge boundaries of the rectangle. 113 114=head2 lines 115 116=head2 cols 117 118 $lines = $rect->lines 119 120 $cols = $rect->cols 121 122Return the size of the rectangle. 123 124=cut 125 126=head2 linerange 127 128 @lines = $rect->linerange( $min, $max ) 129 130A convenient shortcut to generate the list of lines covered that are within 131the given bounds (either bound may be given as C<undef>). Without bounds, 132equivalent to: 133 134 $rect->top .. $rect->bottom - 1 135 136=cut 137 138sub linerange 139{ 140 my $self = shift; 141 my ( $min, $max ) = @_; 142 143 my $start = $self->top; 144 $start = $min if defined $min and $min > $start; 145 146 my $stop = $self->bottom - 1; 147 $stop = $max if defined $max and $max < $stop; 148 149 return $start .. $stop; 150} 151 152=head1 METHODS 153 154=cut 155 156=head2 equals 157 158 $bool = $rect->equals( $other ) 159 160 $bool = ( $rect == $other ) 161 162Returns true if C<$other> represents the same area as C<$rect>. This method 163overloads the numerical equality operator (C<==>). 164 165=cut 166 167use overload '==' => "equals", eq => "equals"; 168 169=head2 contains 170 171 $bool = $rect->contains( $other ) 172 173Returns true if C<$other> is entirely contained within the bounds of C<$rect>. 174 175=cut 176 177=head2 intersects 178 179 $bool = $rect->intersects( $other ) 180 181Returns true if C<$other> and C<$rect> intersect at all, even if they overlap. 182 183=cut 184 185sub sprintf 186{ 187 my $self = shift; 188 return sprintf "[(%d,%d)..(%d,%d)]", $self->left, $self->top, $self->right, $self->bottom; 189} 190 191use overload 192 '""' => sub { 193 my $self = shift; 194 return ref($self) . $self->sprintf; 195 }, 196 bool => sub { 1 }; 197 198=head2 add 199 200 @r = $rect->add( $other ) 201 202Returns a list of the non-overlapping regions covered by either C<$rect> or 203C<$other>. 204 205In the trivial case that the two given rectangles do not touch, the result 206will simply be a list of the two initial rectangles. Otherwise a list of 207newly-constructed rectangles will be returned that covers the same area as 208the original two. This list will contain anywhere between 1 and 3 rectangles. 209 210=cut 211 212=head2 subtract 213 214 @r = $rect->subtract( $other ) 215 216Returns a list of the non-overlapping regions covered by C<$rect> but not by 217C<$other>. 218 219In the trivial case that C<$other> completely covers C<$rect> then the empty 220list is returned. In the trivial case that C<$other> and C<$rect> do not 221intersect then a list containing C<$rect> is returned. Otherwise, a list of 222newly-constructed rectangles will be returned that covers the required area. 223This list will contain anywhere between 1 and 4 rectangles. 224 225=cut 226 227=head1 AUTHOR 228 229Paul Evans <leonerd@leonerd.org.uk> 230 231=cut 232 2330x55AA; 234