File Coverage

blib/lib/Curses/Toolkit/Object/Coordinates.pm
Criterion Covered Total %
statement 15 147 10.2
branch 0 64 0.0
condition 0 21 0.0
subroutine 5 34 14.7
pod 19 24 79.1
total 39 290 13.4


line stmt bran cond sub pod time code
1             #
2             # This file is part of Curses-Toolkit
3             #
4             # This software is copyright (c) 2011 by Damien "dams" Krotkine.
5             #
6             # This is free software; you can redistribute it and/or modify it under
7             # the same terms as the Perl 5 programming language system itself.
8             #
9 1     1   103180 use warnings;
  1         3  
  1         45  
10 1     1   207 use strict;
  1         3  
  1         109  
11              
12             package Curses::Toolkit::Object::Coordinates;
13             {
14             $Curses::Toolkit::Object::Coordinates::VERSION = '0.211';
15             }
16             # ABSTRACT: simple coordinates class
17              
18 1     1   1159 use Params::Validate qw(SCALAR ARRAYREF HASHREF CODEREF GLOB GLOBREF SCALARREF HANDLE BOOLEAN UNDEF validate validate_pos);
  1         13324  
  1         174  
19              
20 1     1   11 use parent qw(Curses::Toolkit::Object);
  1         2  
  1         11  
21              
22             use overload
23 1         9 '+' => '_clone_add',
24             '-' => '_clone_subtract',
25             '""' => '_stringify',
26 1     1   69 '==' => '_equals';
  1         2  
27              
28              
29             # -- attributes
30              
31              
32             # -- constructor, builder & initializer
33              
34              
35             # called before object is built, to normalize the arguments.
36             sub new {
37 0     0 1   my $class = shift;
38              
39 0           my %params;
40              
41 0 0 0       if ( ref($_[0]) && $_[0]->isa(__PACKAGE__) ) {
42             # case: Coordinates->new( $clone );
43 0           my $c = $_[0];
44 0           %params = (
45             x1 => $c->{x1}, y1 => $c->{y1},
46             x2 => $c->{x2}, y2 => $c->{y2},
47             normalize => $c->{normalize},
48             );
49             } else {
50 0           %params = @_;
51 0 0 0       if ( exists $params{width} || exists $params{height} ) {
52             # case: width and height arguments
53 0           %params = validate( @_,
54             { x1 => { type => SCALAR }, y1 => { type => SCALAR },
55             width => { type => SCALAR }, height => { type => SCALAR },
56             normalize => { type => BOOLEAN, default => 1 },
57             }
58             );
59 0           $params{x2} = $params{x1} + $params{width};
60 0           $params{y2} = $params{y1} + $params{height};
61             } else {
62 0           %params = validate( @_,
63             { x1 => { type => SCALAR|CODEREF }, y1 => { type => SCALAR|CODEREF },
64             x2 => { type => SCALAR|CODEREF }, y2 => { type => SCALAR|CODEREF },
65             normalize => { type => BOOLEAN, default => 1 },
66             }
67             );
68             }
69              
70 0           foreach (qw( x1 x2 y1 y2 )) {
71 0 0         ref $params{$_} eq 'CODE'
72             or $params{$_} = int($params{$_});
73             }
74             }
75 0           my $self = bless \%params, $class;
76 0           $self->_normalize();
77 0           return $self;
78             }
79              
80              
81             sub new_zero {
82 0     0 1   my ($class) = @_;
83 0           return $class->new(
84             x1 => 0, y1 => 0,
85             x2 => 0, y2 => 0
86             );
87             }
88              
89              
90              
91             sub clone {
92 0     0 1   my ($self) = @_;
93 0           return ref($self)->new($self);
94             }
95              
96 0 0   0 0   sub get_x1 { my $v = shift->{x1}; ref $v eq 'CODE' ? $v->() : $v }
  0            
97 0 0   0 0   sub get_y1 { my $v = shift->{y1}; ref $v eq 'CODE' ? $v->() : $v }
  0            
98 0 0   0 0   sub get_x2 { my $v = shift->{x2}; ref $v eq 'CODE' ? $v->() : $v }
  0            
99 0 0   0 0   sub get_y2 { my $v = shift->{y2}; ref $v eq 'CODE' ? $v->() : $v }
  0            
100 0     0 0   sub get_normalize { shift->{normalize} }
101              
102             # -- public methods
103              
104              
105             sub set {
106 0     0 1   my $self = shift;
107              
108             # checks on params
109 0           my %params = validate(
110             @_,
111             { x1 => { type => SCALAR | CODEREF, optional => 1 }, y1 => { type => SCALAR | CODEREF, optional => 1 },
112             x2 => { type => SCALAR | CODEREF, optional => 1 }, y2 => { type => SCALAR | CODEREF, optional => 1 },
113             normalize => { type => BOOLEAN, default => 1 },
114             }
115             );
116 0 0         keys %params or die "One of (x1, y1, x2, y2, normalize) argument must be passed";
117              
118             # set the new coords
119 0           foreach my $k ( keys %params ) {
120 0           $self->{$k} = $params{$k};
121             }
122 0           $self->_normalize();
123 0           return $self;
124             }
125              
126              
127              
128             sub width {
129 0     0 1   my ($self) = @_;
130 0           return $self->get_x2() - $self->get_x1();
131             }
132              
133              
134              
135             sub height {
136 0     0 1   my ($self) = @_;
137 0           return $self->get_y2() - $self->get_y1();
138             }
139              
140              
141              
142             sub add {
143 0     0 1   my ( $self, $c ) = @_;
144              
145             # FIXME: callbacks loose their coderef status
146              
147 0 0         if ( !ref $c ) {
    0          
    0          
148             # argument is a constant
149 0           @{$self}{qw(x1 y1 x2 y2)} = (
  0            
150             $self->get_x1 + $c, $self->get_y1 + $c,
151             $self->get_x2 + $c, $self->get_y2 + $c,
152             );
153              
154             } elsif ( ref $c eq __PACKAGE__ ) {
155             # argument is a coordinate object
156 0           @{$self}{qw(x1 x2 y1 y2)} = (
  0            
157             $self->get_x1 + $c->get_x1, $self->get_x2 + $c->get_x2,
158             $self->get_y1 + $c->get_y1, $self->get_y2 + $c->get_y2,
159             );
160              
161             } elsif ( ref $c eq 'HASH' ) {
162             # argument is a hash
163 0           while ( my ( $k, $v ) = each %$c ) {
164 0           my $meth = "get_$k";
165 0           $self->{$k} = $self->$meth + $v;
166             }
167              
168             } else {
169 0           die "Argument type ('" . ref($c) . "') is not supported in Coordinate addition";
170             }
171 0           $self->_normalize();
172 0           return $self;
173             }
174              
175              
176              
177             sub subtract {
178 0     0 1   my ( $self, $c ) = @_;
179              
180             # FIXME: callbacks loose their coderef status
181              
182 0 0         if ( !ref $c ) {
    0          
    0          
183             # argument is a constant
184 0           @{$self}{qw(x1 y1 x2 y2)} = (
  0            
185             $self->get_x1 - $c, $self->get_y1 - $c,
186             $self->get_x2 - $c, $self->get_y2 - $c,
187             );
188              
189             } elsif ( ref $c eq __PACKAGE__ ) {
190             # argument is a coordinate object
191 0           @{$self}{qw(x1 x2 y1 y2)} = (
  0            
192             $self->get_x1 - $c->get_x1, $self->get_x2 - $c->get_x2,
193             $self->get_y1 - $c->get_y1, $self->get_y2 - $c->get_y2,
194             );
195              
196             } elsif ( ref $c eq 'HASH' ) {
197              
198             # argument is a hash
199 0           while ( my ( $k, $v ) = each %$c ) {
200 0           my $meth = "get_$k";
201 0           $self->{$k} = $self->$meth - $v;
202             }
203              
204             } else {
205 0           die "Argument type ('" . ref($c) . "') is not supported in Coordinate addition";
206             }
207 0           $self->_normalize();
208 0           return $self;
209             }
210              
211              
212              
213             sub restrict_to {
214 0     0 1   my $self = shift;
215 0           my ($c) = validate_pos( @_, { isa => 'Curses::Toolkit::Object::Coordinates' } );
216              
217 0 0         $self->get_x1 < $c->get_x1 and $self->{x1} = $c->{x1};
218 0 0         $self->get_x1 > $c->get_x2 and $self->{x1} = $c->{x2};
219              
220 0 0         $self->get_x2 > $c->get_x2 and $self->{x2} = $c->{x2};
221 0 0         $self->get_x2 < $c->get_x1 and $self->{x2} = $c->{x1};
222              
223 0 0         $self->get_y1 < $c->get_y1 and $self->{y1} = $c->{y1};
224 0 0         $self->get_y1 > $c->get_y2 and $self->{y1} = $c->{y2};
225              
226 0 0         $self->get_y2 > $c->get_y2 and $self->{y2} = $c->{y2};
227 0 0         $self->get_y2 < $c->get_y1 and $self->{y2} = $c->{y1};
228              
229 0           $self->_normalize();
230              
231 0           return $self;
232             }
233              
234              
235              
236             sub grow_to {
237 0     0 1   my $self = shift;
238 0           my ($c) = validate_pos( @_, { isa => 'Curses::Toolkit::Object::Coordinates' } );
239              
240 0 0         $self->get_x1 > $c->get_x1 and $self->{x1} = $c->{x1};
241 0 0         $self->get_x2 < $c->get_x2 and $self->{x2} = $c->{x2};
242              
243 0 0         $self->get_y1 > $c->get_y1 and $self->{y1} = $c->{y1};
244 0 0         $self->get_y2 < $c->get_y2 and $self->{y2} = $c->{y2};
245              
246 0           $self->_normalize();
247              
248 0           return $self;
249             }
250              
251              
252              
253             sub translate {
254 0     0 1   my $self = shift;
255              
256             # FIXME: callbacks loose their coderef status
257              
258 0           my %params = validate(
259             @_,
260             { x => { type => SCALAR, optional => 1 },
261             y => { type => SCALAR, optional => 1 },
262             }
263             );
264 0 0 0       defined $params{x} || $params{y}
265             or die "needs at least one of 'x' or 'y'";
266              
267 0 0         if ( defined $params{x} ) {
268 0           $self->{x1} += $params{x};
269 0           $self->{x2} += $params{x};
270             }
271 0 0         if ( defined $params{y} ) {
272 0           $self->{y1} += $params{y};
273 0           $self->{y2} += $params{y};
274             }
275              
276 0           $self->_normalize();
277              
278 0           return $self;
279             }
280              
281              
282              
283             sub translate_up {
284 0     0 1   my ( $self, $value ) = @_;
285 0           return $self->translate( y => -abs $value );
286             }
287              
288              
289              
290             sub translate_down {
291 0     0 1   my ( $self, $value ) = @_;
292 0           return $self->translate( y => abs $value );
293             }
294              
295              
296              
297             sub translate_left {
298 0     0 1   my ( $self, $value ) = @_;
299 0           return $self->translate( x => -abs $value );
300             }
301              
302              
303              
304             sub translate_right {
305 0     0 1   my ( $self, $value ) = @_;
306 0           return $self->translate( x => abs $value );
307             }
308              
309              
310              
311             sub contains {
312 0     0 1   my $self = shift;
313 0           my ($c) = validate_pos( @_, { isa => 'Curses::Toolkit::Object::Coordinates' } );
314             return
315 0   0       $self->get_x1() <= $c->get_x1()
316             && $self->get_y1() <= $c->get_y1()
317             && $self->get_x2() >= $c->get_x2()
318             && $self->get_y2() >= $c->get_y2();
319             }
320              
321              
322              
323             sub is_inside {
324 0     0 1   my $self = shift;
325 0           my ($c) = validate_pos( @_, { isa => 'Curses::Toolkit::Object::Coordinates' } );
326 0           return $c->contains($self);
327             }
328              
329              
330              
331             sub is_in_widget {
332 0     0 1   my ( $self, $widget ) = @_;
333 0           my $w_coord = $widget->get_coordinates();
334             return
335 0   0       $w_coord->get_x1 <= $self->get_x1
336             && $w_coord->get_x2 >= $self->get_x2
337             && $w_coord->get_y1 <= $self->get_y1
338             && $w_coord->get_y2 >= $self->get_y2;
339             }
340              
341              
342             sub is_in_widget_visible_shape {
343 0     0 1   my ( $self, $widget ) = @_;
344 0           my $w_coord = $widget->get_visible_shape();
345             return
346 0   0       $w_coord->get_x1 <= $self->get_x1
347             && $w_coord->get_x2 >= $self->get_x2
348             && $w_coord->get_y1 <= $self->get_y1
349             && $w_coord->get_y2 >= $self->get_y2;
350             }
351              
352              
353             # -- private methods
354              
355             #
356             # my $c3 = $c1->_clone_add( $c2 );
357             # my $c3 = $c1 + $c2; # overloaded
358             #
359             # clone a coord and add another to the new object.
360             #
361             sub _clone_add {
362 0     0     my $self = shift;
363 0           my $clone = $self->clone();
364 0           $clone->add(@_);
365 0           return $clone;
366             }
367              
368              
369             #
370             # my $c3 = $c1->_clone_subtract( $c2 );
371             # my $c3 = $c1 - $c2; # overloaded
372             #
373             # clone a coord and subtract another to the new object.
374             #
375             sub _clone_subtract {
376 0     0     my $self = shift;
377 0           my $clone = $self->clone();
378 0           $clone->subtract(@_);
379 0           return $clone;
380             }
381              
382              
383             #
384             # my $bool = $c1->_equals( $c2 );
385             # my $bool = $c1 == $c2; # overloaded
386             #
387             # return true if both $c1 and $c2 point to the same coords. they can
388             # point to different objects, though.
389             #
390             sub _equals {
391 0     0     my ( $c1, $c2 ) = @_;
392             return
393 0   0       $c1->get_x1 == $c2->get_x1
394             && $c1->get_y1 == $c2->get_y1
395             && $c1->get_x2 == $c2->get_x2
396             && $c1->get_y2 == $c2->get_y2;
397             }
398              
399              
400             #
401             # my $str = $self->_stringify;
402             # my $str = "$self"; # overloaded
403             #
404             # return the string 'WxH+XxY' with:
405             # W = width
406             # H = height,
407             # X = top left x coord
408             # Y = top left y coord
409             #
410             sub _stringify {
411 0     0     my ($self) = @_;
412 0           return $self->width . 'x' . $self->height . '+' . $self->get_x1 . 'x' . $self->get_y1;
413             }
414              
415              
416             #
417             # $self->_normalize;
418             #
419             # make sure the coordinate is positive. in effect:
420             # - swap x1 and x2 to make sure x1 <= x2
421             # - swap y1 and y2 to make sure y1 <= y2
422             #
423             sub _normalize {
424 0     0     my ($self) = @_;
425 0 0         $self->get_normalize() or return;
426 0 0         $self->get_x1() <= $self->get_x2() or ( $self->{x1}, $self->{x2} ) = ( $self->{x2}, $self->{x1} );
427 0 0         $self->get_y1() <= $self->get_y2() or ( $self->{y1}, $self->{y2} ) = ( $self->{y2}, $self->{y1} );
428 0           return;
429             }
430              
431             1;
432              
433             __END__